/* ======================
   TAF 2026 — "Warm Oven" design system: tokens + components.
   Class prefix .taf-* avoids Kadence collisions (.scallop keeps its upstream name).
   Does NOT style WPRM markup — that is wprm-taf2026.css.
   Mobile-first at 390px; desktop breakpoint 900px.
   ====================== */

/* ---- Fonts: self-hosted Poppins (--display) + Mulish (--sans) ----
   Poppins = all headings, eyebrows, buttons, nav, labels, numerals, chips.
   Mulish = all running body copy. Latin subset, static per-weight woff2,
   font-display:swap. Fallback is the platform UI sans (system-ui), which is
   metrically close to both faces — swap reflow stays low. */
/* ---- METRIC-OVERRIDE FALLBACKS (2026-07-14). Zero bytes; the real CLS fix.
   The comment below used to claim the platform sans is "metrically close to both
   faces — swap reflow stays low". That was FALSE, and measured so: rendering the
   same string at 100px, Poppins is 110.2% of Arial's width at weight 400 and
   106.2% at 700; Mulish is 104.6%. So every heading and every line of body copy
   REFLOWS when the webfont swaps in — and Lighthouse blames 100% of the layout
   shift on this site on `Web font loaded`, on a recipe template whose field CLS
   p75 is 0.10 against a 0.10 bar. Zero margin.

   These two faces are the SAME Arial the browser would fall back to anyway, but
   stretched so its metrics match the real face. The swap then causes no reflow.
   Ratios measured empirically in-browser, NOT from OS/2 xAvgCharWidth — fonts
   compute that field differently and it claimed Poppins was 156% of Arial, which
   is nonsense. ascent/descent are the real font's values (Poppins 1050/-350,
   Mulish 1005/-250 per 1000upm) divided by size-adjust, because the overrides are
   relative to the ADJUSTED em. (Both ascent/descent figures re-verified against
   the woff2 tables 2026-08-06: Poppins typoAscender 1050, typoDescender -350;
   Mulish 1005 / -250, both at 1000upm, both USE_TYPO_METRICS. They are right.) */

/* ---- CORRECTION, 2026-08-06 (audit 07 #5). One face served 400-800 and it was
   calibrated against the WRONG denominator, so the July mitigation never worked.
   Measured, not argued:

   `src: local('Arial')` NEVER RESOLVES TO ARIAL BOLD. Five faces were declared at
   400/500/600/700/800 with this identical src and all five measured IDENTICALLY,
   at Arial *Regular* widths — while a plain `font-family: Arial` at weight >= 600
   does pick up Arial Bold (pangram 1978.77 -> 2139.17px). Chrome applies no
   advance widening for synthetic bold either. The shipped 106.2% is Poppins-700 /
   Arial *BOLD* (re-measured: 106.12%) — a ratio against a face that never loads.
   Against the face that DOES load, every weight was short: -3.37% at 400 rising
   to -6.33% at 800, which is where the audit's flat-2296.1-vs-2420->2545 mismatch
   came from. H1/.taf-rhero__title/.taf-cat-title are --w-black (800), i.e. the
   display type driving CLS sat on the WORST-fitting number in the file.

   The five values below are measured against Arial Regular over the site's own
   608 post titles (the actual H1 content), fontTools advance sums cross-validated
   in-browser to 0.01px, then calibrated to zero corpus error. Payoff on real
   geometry (H1 30px/800/lh1.15, count of titles whose block height differs from
   real Poppins): 75/608 titles wrong at 342px, mean 4.26px -> 20/608, mean 1.13px.
   ~5.6x less swap reflow, zero bytes.

   line-gap-override stays 0% though real Poppins lineGap is 100/1000 = 10%: it is
   inert here because inc/enqueue.php pins `line-height: var(--lh-display)
   !important` on h1..h6, and `line-height: normal` appears once in the whole
   theme. Kept at parity rather than changed unmeasured.

   ANDROID CAVEAT, recorded not hidden: Android ships none of Arial / Helvetica
   Neue / Liberation Sans, so this family fails to resolve there and the stack
   falls through to system-ui (Roboto). These overrides do nothing on that slice —
   before this change and after it. A Roboto-targeted face is a separate piece of
   work, not a regression introduced here. */
@font-face {
  font-family: 'Poppins Fallback';
  font-weight: 400;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 110.42%;
  ascent-override: 95.09%;    /* 105%  / 1.1042 */
  descent-override: 31.70%;   /* 35%   / 1.1042 */
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Poppins Fallback';
  font-weight: 500;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 111.65%;
  ascent-override: 94.04%;    /* 105%  / 1.1165 */
  descent-override: 31.35%;   /* 35%   / 1.1165 */
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Poppins Fallback';
  font-weight: 600;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 112.58%;
  ascent-override: 93.27%;    /* 105%  / 1.1258 */
  descent-override: 31.09%;   /* 35%   / 1.1258 */
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Poppins Fallback';
  font-weight: 700;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 113.66%;
  ascent-override: 92.38%;    /* 105%  / 1.1366 */
  descent-override: 30.79%;   /* 35%   / 1.1366 */
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Poppins Fallback';
  font-weight: 800;
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 114.58%;
  ascent-override: 91.64%;    /* 105%  / 1.1458 */
  descent-override: 30.55%;   /* 35%   / 1.1458 */
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Mulish Fallback';
  src: local('Arial'), local('Helvetica Neue'), local('Liberation Sans');
  size-adjust: 104.6%;
  ascent-override: 96.08%;    /* 100.5% / 1.046 */
  descent-override: 23.90%;   /* 25%  / 1.046 */
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Poppins';
  src: url('../fonts/poppins-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('../fonts/poppins-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('../fonts/poppins-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('../fonts/poppins-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('../fonts/poppins-800.woff2') format('woff2');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Mulish';
  src: url('../fonts/mulish-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Mulish';
  src: url('../fonts/mulish-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Mulish';
  src: url('../fonts/mulish-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Mulish';
  src: url('../fonts/mulish-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---- Tokens ---- */
/* == GENERATED: tokens — do not edit by hand. Source: design/tokens.json (scripts/build-tokens.py) == */
:root {
  /* Palette — every neutral mixed from the brand pink */
  --milk:         #FEFBFA;
  --cream:        #FAEDF2;
  --paper:        #FFFFFF;
  --chocolate:    #33202B;
  --cocoa:        #6E5964;
  --rose:         #E11584;
  --rose-deep:    #B80E67;
  --rose-link:    #D01277;
  --rose-tint:    #FCE7F3;
  --saffron:      #E29B32;
  --saffron-deep: #B87716;
  --pistachio:    #6E7C4D;
  --line:         #F0DEE6;

  /* Semantic aliases */
  --bg-page:      var(--milk);
  --bg-surface:   var(--paper);
  --bg-tinted:    var(--cream);
  --bg-selected:  var(--rose-tint);
  --text-body:    var(--chocolate);
  --text-muted:   var(--cocoa);
  --text-link:    var(--rose-link);
  --accent:       var(--rose);
  --accent-hover: var(--rose-deep);
  --border-hairline: var(--line);

  /* Component fills */
  --tag-veg-bg:    #EEF2E3;
  --tag-season-bg: #FCF0DC;
  /* Avatar-initial inks on the tag tints (comment avatars, taf-comments.css).
     The base --pistachio / --saffron-deep measure 3.96:1 / 3.28:1 on their own
     tint grounds — an AA fail for the 17px-bold initial. These are the same hues
     darkened just past 4.5:1 (computed 2026-07-17: veg 4.56:1, season 4.69:1).
     Tint BACKGROUNDS stay untouched; rose-tint's ink (--rose-deep, 5.42:1) needs
     no companion token. */
  --tag-veg-ink:   #657246;
  --tag-season-ink: #96600F;
  /* Footer ground is LIGHT (deeper cream), not chocolate. Chocolate/cocoa ink on
     it clears AA (12.4:1 / 5.1:1) — keep new footer ink in that family.
     Rationale: inc/footer.php. */
  --footer-bg:     #F2E0E9;
  --footer-text:   var(--chocolate);
  --footer-label:  var(--cocoa);
  --footer-legal:  var(--cocoa);
  --footer-rule:   rgba(51, 32, 43, .12);

  /* Type — the two self-hosted faces above. --display = Poppins (headings,
     eyebrows, buttons, nav, labels, numerals, chips); --sans = Mulish (body). */
  --display: 'Poppins', 'Poppins Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --sans: 'Mulish', 'Mulish Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --ui: 'Poppins', 'Poppins Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --fs-hero: 34px;
  --fs-h1: 30px;
  --fs-h2: 24px;
  --fs-h3: 20px;
  --fs-h4: 18px;
  --fs-body: 18px;
  --fs-small: 15px;
  --fs-meta: 13px;
  --fs-eyebrow: 12px;

  /* Weight scale (T0, 2026-07-14). These five ARE the shipped woff2 files —
     poppins-400/500/600/700/800, mulish-400/500/600/700. There is no variable
     font, so any weight not in this list cannot be rendered as authored.
     ⛔ DO NOT reintroduce 650. It was used 39x across all seven stylesheets and
     it is not a real weight. Per CSS Fonts L4, a requested weight >500 searches
     upward first, so 650 already resolved to the 700 file. Measured on the live
     page to be sure, rather than trusting the spec:
         weight 400 -> 380.75px 600 -> 389.70px
         weight 650 -> 395.14px 700 -> 395.14px (byte-identical)
     So 650 -> --w-bold (700) moves ZERO pixels; 650 -> --w-semibold (600) would
     have visibly LIGHTENED all 39 (389.70 != 395.14). The tokenization audit
     recommended 600. It was wrong. */
  --w-regular:  400;
  --w-medium:   500;
  --w-semibold: 600;
  --w-bold:     700;
  --w-black:    800;

  /* ---- The "on-" pair tokens (T0, 2026-07-14). Read this before changing --rose.
     `color: #fff` was hardcoded 38 times. They are NOT one thing, and collapsing
     them into a single token (as the audit proposed) would be semantics-blind:
     each has a DIFFERENT ground, MEASURED with getComputedStyle, and
     they must be able to diverge.

     🔴 THE AA TRAP THIS FIXES: today a brand-color change silently breaks
     contrast on every button on the site. docs/02-design-system.md stakes the
     system on "white text on the brand pink = 4.5:1 AA". Lighten --rose and all
     27 on-accent uses drop below AA — with no error, no test, nothing. Now there
     is exactly ONE place to fix it. If you lighten --rose, you MUST re-check
     --color-on-accent against it (and it may have to become --chocolate).
     The scrim and dark texts are unaffected by --rose and must NOT follow it. */
  --color-on-accent: #fff;  /* 27x — text on --rose/--accent: buttons, submits, chips, badges, the #1 rank pill */
  --color-on-dark:   #fff;  /* 3x — text on --chocolate: the jump pill on hover, the search-row CTA */
  --color-on-scrim:  #fff;  /* 8x — text over a photo scrim: hero/mosaic/featband captions. Stays white regardless of brand. */

  --lh-body: 1.65;
  /* Display leading tuned for Poppins: 1.15 stays open at heading sizes without
     going loose on the hero, and matches the per-component display line-heights
     below, so the type system shares one value. */
  --lh-display: 1.15;
  --ls-display: -0.01em;
  --ls-eyebrow: 0.14em;

  /* Layout */
  --maxw: 728px;
  --maxw-wide: 1200px;
  --pad: 20px;
  --hdr-h: 60px;

  /* Corner radii — canonical, never hardcode roundness:
     surfaces (cards, tiles, images, banners, magnets, search container) = 8px;
     controls (buttons, chips, inputs, tags, small thumbs) = 6px;
     avatars + category circles = 50%.
     Pills are retired — --radius-pill aliases the 6px control radius so existing
     pill usages soften automatically. */
  --radius: 8px;
  --radius-sm: 6px;
  --radius-pill: var(--radius-sm); /* retired: controls are soft 6px, not pills */
  --radius-round: 50%;

  --shadow: 0 1px 2px rgba(51,32,43,.06), 0 6px 24px -8px rgba(51,32,43,.12);
  --shadow-hover: 0 2px 4px rgba(51,32,43,.08), 0 10px 28px -8px rgba(51,32,43,.18);
  --speed: .15s;

  /* Scrims: photo cards that carry white text over the image.
     The INK is tokenized (written once, here); the RAMP stays per-band.
     Collapsing all three onto one gradient was tried and reverted: it silently
     restyled the seasonal hero (home band 1, above the fold) and the feature
     band. Tokenizing must move zero pixels — so each band keeps its own stop,
     and only the duplicated rgba() ink is deduplicated.
     --ink-rgb is --chocolate (#33202B) as channels; rgba() cannot take a hex. */
  --ink-rgb: 51, 32, 43;
  --scrim-mosaic:   linear-gradient(transparent 38%, rgba(var(--ink-rgb), .82)); /* Trending (handoff spec) */
  --scrim-seahero:  linear-gradient(transparent 45%, rgba(var(--ink-rgb), .8));  /* parity — do not retune */
  --scrim-featband: linear-gradient(transparent 30%, rgba(var(--ink-rgb), .78)); /* parity — do not retune */
  --scrim-menutile: linear-gradient(transparent 35%, rgba(var(--ink-rgb), .78)); /* Mobile-menu browse tiles (handoff spec) */

  /* Spacing scale (T4, 2026-07-14). NAMED, NOT YET RATIONALIZED.
     Every distinct padding/margin/gap value already in the theme gets a token at
     its CURRENT value, so tokenizing moves zero pixels — css-parity proves it by
     construction. 34 tokens is not a scale, it is an inventory. The real scale is
     ~9 steps, and collapsing 34 into 9 is T6: it MOVES PIXELS and needs sign-off.

     T6 MUST NOT remap values inside a rule marked token-frozen. 17 rules build a
     44px WCAG tap target, an iOS 16px input floor, or the footer's sticky-ad
     clearance out of these very numbers. Snapping those silently breaks
     accessibility or ad revenue, and NO automated gate would catch it: in T6
     css-parity says 'value changed' and the geometry harness says 'box moved',
     both of which are true AND expected. The gates go quiet exactly there. */
  --space-2: 2px;
  --space-4: 4px;
  --space-6: 6px;
  --space-8: 8px;
  --space-10: 10px;
  --space-12: 12px;
  --space-14: 14px;
  --space-16: 16px;
  --space-18: 18px;
  --space-20: 20px;
  --space-24: 24px;
  --space-28: 28px;
  --space-32: 32px;
  --space-40: 40px;
  --space-44: 44px;
  --space-48: 48px;
  --space-64: 64px;
  --space-90: 90px;

  /* Font-size scale (T4). VALUE-NAMED ON PURPOSE — do not 'improve' this.
     The semantic tokens (--fs-h1 etc.) are RE-DECLARED at min-width:900px, so
     mapping a literal 30px onto --fs-h1 is parity on mobile and 42px on DESKTOP.
     token-inventory's 'free wins' list actively recommends exactly that trap for
     18 sites. Value-named tokens cannot spring it. Collapsing 25 sizes into ~8
     steps is T6. */
  --fs-10: 10px;
  --fs-12: 12px;
  --fs-13: 13px;
  --fs-14: 14px;
  --fs-15: 15px;
  --fs-16: 16px;
  --fs-16-5: 16.5px;
  --fs-17: 17px;
  --fs-18: 18px;
  --fs-19: 19px;
  --fs-20: 20px;
  --fs-22: 22px;
  --fs-24: 24px;
  --fs-26: 26px;
  --fs-30: 30px;
  --fs-32: 32px;
  --fs-36: 36px;
  --fs-38: 38px;
  --fs-42: 42px;
  --fs-54: 54px;

  /* Line-height scale (T4), value-named at current values. */
  --lh-108: 1.08;
  --lh-11: 1.1;
  --lh-112: 1.12;
  --lh-115: 1.15;
  --lh-12: 1.2;
  --lh-125: 1.25;
  --lh-13: 1.3;
  --lh-135: 1.35;
  --lh-14: 1.4;
  --lh-145: 1.45;
  --lh-15: 1.5;
  --lh-155: 1.55;
  --lh-16: 1.6;
  --lh-165: 1.65;

  /* Border widths (T4). */
  --bw-1: 1px;
  --bw-15: 1.5px;
  --bw-4: 4px;

  /* z-index (T4). The values are ad-hoc — 0,1,2,49,50,55,60,98,99 — and 1001
     in the dropdown was chosen to WIN, not to belong to a layer. Named at parity
     here; a real semantic scale (--z-header/--z-drawer/--z-modal) is T6. */
  --z-0: 0;
  --z-1: 1;
  --z-2: 2;
  --z-49: 49;
  --z-50: 50;
  --z-55: 55;
  --z-60: 60;
  --z-98: 98;
  --z-99: 99;

  /* Opacity (T4). */
  --o-6: .6;
  --o-7: .7;
  --o-75: .75;
  --o-8: .8;
  --o-85: .85;
  --o-9: .9;

  /* Motion (T4). --speed (.15s) already existed and was RETYPED as a literal
     4x in taf-dropdown.css. .35s is the photo-zoom, a real second duration —
     the value the comps specify (design/dist/screens/*.html:146). */
  /* Mobile-menu motion (2026-07-17, design_handoff_mobile_menu): .28s is the
     sheet's slide-up; .05/.12/.24/.3 are the section stagger delays (with the
     pre-existing .18/.35); --ease-out is the comp's decelerate curve. */
  --dur-05: .05s;
  --dur-12: .12s;
  --dur-15: .15s;
  --dur-18: .18s;
  --dur-2: .2s;
  --dur-24: .24s;
  --dur-28: .28s;
  --dur-3: .3s;
  --dur-35: .35s;
  /* .5s: the Kit success checkmark stroke-draw (delight B, 2026-07-17). */
  --dur-5: .5s;
  --ease: ease;
  --ease-out: cubic-bezier(.22, .9, .3, 1);

  /* Letter-spacing (T4). NOTE the notation duplicates this exposed: .14em and
     0.14em were BOTH in the code and are the same value; --ls-eyebrow (0.14em)
     already existed and was retyped 6x. Same for .04em / 0.04em. */
  --ls--001em: -0.01em;
  --ls-04em: 0.04em;
  --ls-08em: 0.08em;
  --ls-1em: 0.1em;
  --ls-14em: 0.14em;
  --ls-1px: 1px;

  /* Aspect ratios (T4). Notation was inconsistent (4/5 vs '4 / 5' vs 4 / 4.4).
     These are content shapes, not a scale — T6 should NOT collapse them. */
  --ar-1-1: 1 / 1;
  --ar-3-2: 3 / 2;
  --ar-4-3: 4 / 3;
  --ar-4-5: 4 / 5;
  --ar-5-4: 5 / 4;
  --ar-6-5: 6 / 5;
  --ar-16-9: 16 / 9;
  --ar-16-10: 16 / 10;
  --ar-4-34: 4 / 3.4;
  --ar-4-44: 4 / 4.4;
  --ar-24-11: 24 / 11;

  /* Pill radius (T4). main.css says 'pills are retired', yet 999px/99px survive
     in 5 places — and 2 of those carry a comment saying the HANDOFF sanctioned
     them. The token block's claim was simply out of date. One token now. */
  --radius-full: 999px;
  --radius-99: 99px;

  /* Motion easing (T4). 72 uses, exactly ONE value. */

  /* z-index, semantic (T4). --z-dropdown was 1001 — a value chosen to WIN,
     three orders of magnitude above the sticky header (99) and the scrim (98),
     rather than to belong to a layer. Named, at parity. A real layer scale is T6. */
  --z-dropdown: 1001;

  /* Elevation one-offs (T4). */
  --shadow-pill: 0 8px 24px -6px rgba(51, 32, 43, .45);
  --shadow-mm-cta: 0 -12px 24px -18px rgba(var(--ink-rgb), .35); /* mobile-menu pinned CTA: the comp's upward lift */

  /* Opacity, remaining (T4). */
  --o-1: 1;
  --o-7: 0.7;
  --o-8: 0.8;

  /* Line-height, remaining (T4). */
  --lh-1: 1;

  /* Aspect ratios (T4). Notation was inconsistent — 4/5, '4 / 5' and 4 / 4.4 all
     appeared. These are CONTENT SHAPES, not a scale: T6 must not collapse them. */
  --ar-1-1: 1 / 1;
  --ar-4-3: 4 / 3;
  --ar-4-5: 4 / 5;
  --ar-5-4: 5 / 4;
  --ar-6-5: 6 / 5;
  --ar-16-9: 16 / 9;
  --ar-16-10: 16 / 10;
  --ar-4-34: 4 / 3.4;
  --ar-4-44: 4 / 4.4;
  --ar-24-11: 24 / 11;

  /* Focus ring (T4). Every remaining border-width literal was `outline: 3px` —
     the a11y focus ring. It is one decision, in one place, now. */
  --focus-ring: 3px;
  /* Focus-ring COLOR (WCAG 1.4.11, measured 2026-07-17). Bare --saffron fails the
     3:1 minimum for state indicators on every light surface: 2.34:1 vs white,
     2.27:1 vs --milk. --saffron-deep (#B87716) measures 3.69:1 vs white — passes.
     Focus outlines use THIS token; rating stars, eyebrows and other decorative
     saffron keep the bright --saffron on purpose. Do not darken --saffron itself. */
  --focus-ring-color: var(--saffron-deep);

  /* Star system (D-series rework): saffron anatomy sitewide.
     FILLED = --saffron fill + --saffron-deep stroke (bare saffron on cream is
     ~2.3:1; the deep stroke carries the 3:1 non-text minimum). EMPTY (D1-C
     quiet) = --line fill AND stroke — no tint empties, no outline-only
     empties, no opacity tricks. Size ladder (D6-A), one tier per surface:
     meta 13 (comment-list stars, text-star runs), display 17 (comments
     aggregate), interactive 20 (post-meta row, card header row), band 26
     (review-band buttons), modal 28 (WPRM rating modal — the plugin inlines
     that widget's geometry with !important, so the token is the recorded
     tier; no CSS applies it yet). The comment-form widget is the one
     interactive surface not on the 20px token: WPRM inlines its sizing,
     so it stays on the scale() shim in taf-comments.css. */
  --star-meta: 13px;
  --star-display: 17px;
  --star-interactive: 20px;
  --star-band: 26px;
  --star-modal: 28px;

  /* Ad-placeholder hatching (T4). These 4 colors exist ONLY to draw the dashed
     reserved-ad boxes, and the whole block was COPY-PASTED — it appears twice in
     main.css (at :582 and :3215). Tokenizing collapses the duplicate to one truth. */
  --ad-ph-a: #F7ECF1;
  --ad-ph-b: #F3E5EC;
  --ad-ph-line: #DFC3D2;
  --ad-ph-text: #7D5E6E; /* 4.93:1 on --ad-ph-a, 4.66:1 on --ad-ph-b (was #9A7A8B: 3.29/3.12 — AA fail at 12px) */

  /* Translucent surfaces (T4). Every one re-derived --milk/--chocolate by hand
     as an rgba() literal, because rgba() cannot take a hex. --ink-rgb/--milk-rgb
     are the channel tokens that make them expressible. */
  --milk-rgb: 254, 251, 250;
  --veil-82: rgba(254, 251, 250, .82);
  --veil-80: rgba(254, 251, 250, 0.8);
  --veil-55: rgba(254, 251, 250, 0.55);
  --veil-0: rgba(254, 251, 250, 0);
  --scrim-search: rgba(43, 25, 37, .74);
  --scrim-dropdown: rgba(51, 32, 43, .18);
  --chocolate-hover: #462D3B;
  /* Star glow (delight A): the halo the rating-star thank-you pulse fades
     through. A full/zero-alpha PAIR, so the keyframes animate the halo away
     without a second hand-derived rgba of --saffron (#E29B32).
     SAFFRON since the 2026-08-01 star rework (the glow follows the star
     fill; supersedes the 2026-07-17 rose-glow decision, which rode the
     short-lived rose-star era). */
  --saffron-glow: rgba(226, 155, 50, .5);
  --saffron-glow-0: rgba(226, 155, 50, 0);

  /* Final T4 tail. */
  --lh-1: 1;
  --ar-1: 1;
  --shadow-underline: inset 0 -2px;

  /* CTA-box ink (T4). #F3E4EC is the LIGHT ink on the WPRM 'Tried this recipe?'
     box, which is the one chocolate surface left in the system.
     ⚠️ IT IS NOT --footer-text. The audit's free-win detector matched them by
     VALUE, but --footer-text is now var(--chocolate) — the footer was moved OFF
     chocolate onto light cream, so its ink was flipped to dark. Substituting
     --footer-text here would have rendered the CTA text CHOCOLATE ON CHOCOLATE:
     invisible, on 600 recipes. css-parity caught it. Value-equality is not
     meaning-equality. */
  --cta-ink: #F3E4EC;

  /* WPRM ingredient-fade gradient (T4). Eased stops, hand-authored. */
  --fade-0: rgba(255, 255, 255, 0);
  --fade-08: rgba(255, 255, 255, 0.08);
  --fade-30: rgba(255, 255, 255, 0.3);
  --fade-72: rgba(255, 255, 255, 0.72);
}
/* == END GENERATED == */

/* ---- Base typography (child sheet loads after Kadence, so it wins ties) ---- */
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-body);
  background: var(--bg-page);
}
img { max-width: 100%; height: auto; }
a { color: var(--text-link); text-underline-offset: 3px; }
a:hover { color: var(--accent-hover); }

h1, h2, h3, .taf-display {
  font-family: var(--display);
  font-weight: var(--w-regular);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
/* h4 must stay explicitly sized: unset, Kadence's 22px default outranks our h3 and
   renders h4 LARGER than h3 (audit). It now equals --fs-body by a review decision.
   That is NOT a flat heading: inside post content the winning weight rule is
   taf-postfooter.css's `.single-post .taf-post-content :is(h1..h6) { --w-semibold }`,
   which outranks the `h4, h5, h6 { --w-regular }` rule further down this sheet — so a
   body-copy h4 renders 18px Poppins 600 against 18px Mulish 400 and still reads as a
   heading by face + weight. Measured on /lentil-millet-summer-salad/, not assumed. */
h4 { font-size: var(--fs-h4); }
.taf-hero-title { font-size: var(--fs-hero); }

/* H1 = 800, H2 = 600 sitewide. The component title classes are listed explicitly
   so a class-specificity rule elsewhere cannot pull them back to a lighter weight.
   (.taf-magnetband__title is a deliberate 700 — not listed.) */

h1,
.taf-rhero__title,
.taf-cat-title { font-weight: var(--w-black); }
h2,
.taf-catband__title,
.taf-secthead h2,
.taf-section-head h2 { font-weight: var(--w-semibold); }

.taf-eyebrow {
  font-family: var(--display);
  font-size: var(--fs-eyebrow);
  font-weight: var(--w-bold);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-link);
}
.taf-muted { color: var(--text-muted); }
.taf-small { font-size: var(--fs-small); }
.taf-meta { font-size: var(--fs-meta); color: var(--text-muted); }

[hidden] { display: none !important; }

/* Icons are inline SVG in markup (no icon font). Size to the cap height. */
.taf-icon {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;
  fill: currentColor;
  flex: none;
}
.taf-icon--lg { width: 1.25em; height: 1.25em; }

/* ---- The paper-liner scallop is RETIRED SITEWIDE:
   "no scalloped seams across the site. NOWHERE." The .scallop component and
   every call site (6 patterns, post-footer band, subscribe page content) were
   removed together; the recipe card's scallop edge was already retired
   2026-07-09. Do not reintroduce. ---- */

/* ---- Buttons ---- */
.taf-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-8);
  font-family: var(--display); font-size: var(--fs-16); font-weight: var(--w-semibold);
  min-height: 48px; padding: var(--space-12) var(--space-20);
  border-radius: var(--radius-pill); border: none; cursor: pointer;
  text-decoration: none;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.taf-btn--primary { background: var(--accent); color: var(--color-on-accent); }
.taf-btn--primary:hover { background: var(--accent-hover); color: var(--color-on-accent); }
.taf-btn--secondary { background: transparent; color: var(--text-body); border: var(--bw-15) solid var(--chocolate); }
.taf-btn--secondary:hover { border-color: var(--accent); color: var(--accent); }
.taf-btn--ghost { background: transparent; color: var(--accent); min-height: 44px; padding: var(--space-8) var(--space-12); }
.taf-btn--sm { min-height: 40px; padding: var(--space-8) var(--space-16); font-size: var(--fs-15); }
.taf-btn--block { width: 100%; }

/* Chips (category pills) */
.taf-chip {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex; align-items: center; gap: var(--space-6);
  min-height: 44px; padding: var(--space-8) var(--space-16); /* 44px tap target */
  border-radius: var(--radius-pill); border: var(--bw-15) solid var(--border-hairline);
  background: var(--bg-surface); color: var(--text-body);
  font-family: var(--display); font-size: var(--fs-15); font-weight: var(--w-semibold); text-decoration: none; white-space: nowrap;
  transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.taf-chip:hover { border-color: var(--accent); color: var(--accent); }
.taf-chip.is-active { background: var(--chocolate); border-color: var(--chocolate); color: var(--milk); }

/* ---- Gutenberg wrapper bridges ----
   Pattern markup puts .taf-btn/.taf-chip/.taf-search on the core block WRAPPER
   (div.wp-block-button / form.wp-block-search); the element users see is the inner
   __link / __input. Bridge the visual onto the inner element. */
.wp-block-button.taf-btn, .wp-block-button.taf-chip { background: transparent; border: none; padding: 0; min-height: 0; }
.wp-block-button.taf-btn .wp-block-button__link,
.wp-block-button.taf-chip .wp-block-button__link {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-8);
  font-family: var(--display); font-weight: var(--w-semibold); text-decoration: none;
  border-radius: var(--radius-pill); cursor: pointer;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.wp-block-button.taf-btn .wp-block-button__link { font-size: var(--fs-16); min-height: 48px; padding: var(--space-12) var(--space-20); border: none; }
.wp-block-button.taf-btn--primary .wp-block-button__link { background: var(--accent); color: var(--color-on-accent); }
.wp-block-button.taf-btn--primary .wp-block-button__link:hover { background: var(--accent-hover); color: var(--color-on-accent); }
.wp-block-button.taf-btn--secondary .wp-block-button__link { background: transparent; color: var(--text-body); border: var(--bw-15) solid var(--chocolate); }
.wp-block-button.taf-btn--secondary .wp-block-button__link:hover { border-color: var(--accent); color: var(--accent); }
.wp-block-button.taf-chip .wp-block-button__link {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  font-size: var(--fs-15); font-weight: var(--w-semibold); min-height: 44px; padding: var(--space-8) var(--space-16); white-space: nowrap; /* 44px tap target */
  background: var(--bg-surface); color: var(--text-body); border: var(--bw-15) solid var(--border-hairline);
}
.wp-block-button.taf-chip .wp-block-button__link:hover { border-color: var(--accent); color: var(--accent); }
.taf-search .wp-block-search__input {
  font-size: var(--fs-16); min-height: 48px; padding: var(--space-8) var(--space-12);
  background: var(--bg-surface); color: var(--text-body);
  border: var(--bw-15) solid var(--border-hairline); border-radius: var(--radius-sm);
}
/* SEARCH focus rings are ROSE — the original searchbar
   focus intent was rose (the old inert border-color rule). Rose clears the
   3:1 non-text floor on every search ground: 4.5:1 paper, ~4.4:1 milk,
   3.98:1 cream. Search-scoped ONLY: the sitewide ring stays
   --focus-ring-color (deep saffron) because rose rings on rose buttons/chips
   would be invisible. All four search rules below follow this rule. */
.taf-search .wp-block-search__input:focus-visible { outline: var(--focus-ring) solid var(--rose); outline-offset: 1px; }
.taf-search .wp-block-search__button {
  font-size: var(--fs-16); font-weight: var(--w-bold); min-height: 48px; padding: var(--space-12) var(--space-20);
  background: var(--accent); color: var(--color-on-accent); border: none; border-radius: var(--radius-pill); cursor: pointer;
}
.taf-search .wp-block-search__button:hover { background: var(--accent-hover); }

/* Tags on tiles */
.taf-tag {
  display: inline-block; padding: var(--space-2) var(--space-8); border-radius: var(--radius-pill);
  font-family: var(--display); font-size: var(--fs-12); font-weight: var(--w-bold); letter-spacing: var(--ls-04em);
  background: var(--bg-selected); color: var(--rose-deep);
}
.taf-tag--veg { background: var(--tag-veg-bg); color: var(--pistachio); }
.taf-tag--season { background: var(--tag-season-bg); color: var(--saffron-deep); }

/* ---- Stars ----
   Compact text-★ form: SAFFRON (star rework — reverses the
   short-lived v0.33.16 rose sweep). Today its only consumers are the recipe
   post-meta fallback + count wrapper (inc/recipe-post.php); tile stars are
   retired sitewide (D5-C — the tile variants were pruned from this file).
   The full 5-star anatomy lives in the WPRM painter near the end of this
   file and in wprm-taf2026.css. */
.taf-stars { color: var(--saffron); font-size: var(--star-meta); letter-spacing: var(--ls-1px); white-space: nowrap; }
.taf-stars .n { color: var(--text-muted); font-family: var(--sans); margin-left: var(--space-4); letter-spacing: 0; }

/* ---- Recipe tile (grid card) ---- */
.taf-tile { text-decoration: none; color: inherit; display: block; }
.taf-tile .ph {
  border-radius: var(--radius); overflow: hidden; aspect-ratio: var(--ar-4-5);
  background: var(--bg-tinted); position: relative;
  box-shadow: none;
  transition: box-shadow var(--speed) var(--ease);
}
.taf-tile .ph img { width: 100%; height: 100%; object-fit: cover; }
.taf-tile .ph .taf-tag { position: absolute; top: 10px; left: 10px; z-index: var(--z-1); }
.taf-tile:hover .ph,
.taf-tile:focus-visible .ph { box-shadow: var(--shadow-hover); }
.taf-tile h3, .taf-tile .t {
  font-family: var(--display); font-weight: var(--w-regular); font-size: var(--fs-17);
  /* 1.15: every tile-title markup variant equalized to the majority display leading
     (was 1.3 here while .taf-tilecard__title/.taf-mosaic__title sat at 1.15). */
  line-height: var(--lh-115); letter-spacing: var(--ls-display); margin-top: var(--space-8);
}
.taf-tile:hover h3, .taf-tile:hover .t,
.taf-tile:focus-visible h3, .taf-tile:focus-visible .t {
  text-decoration: underline; text-underline-offset: 3px;
}

/* ---- Grids ---- */
.taf-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-16); }
.taf-grid-3, .taf-grid-4 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-16); }

/* ---- Layout scaffolding ---- */
.taf-container { max-width: var(--maxw-wide); margin: 0 auto; padding-left: var(--pad); padding-right: var(--pad); }
.taf-content { max-width: var(--maxw); margin: 0 auto; }
.taf-section { padding: var(--space-24) var(--pad); max-width: var(--maxw); margin: 0 auto; }
.taf-section--wide { max-width: var(--maxw-wide); }
.taf-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-12); margin-bottom: var(--space-16); }
.taf-section-head .more { font-size: var(--fs-15); font-weight: var(--w-bold); white-space: nowrap; }

/* ---- Ad slots: ALL REMOVED in v0.90.0. The theme owns no ad container ----
   Raptive asked us (email, 2026-08-07) to stop targeting theme-owned containers
   and to let their spacing logic place content ads dynamically. So the three
   recipe-index wrappers (post 42733 block content), the category-archive slot
   (inc/category-archive.php) and every rule that reserved, centred or collapsed
   them are gone. Their ad code self-reserves a 250px min-height, and that is
   what handles CLS on those templates now.

   It settles both of the questions that produced it. Nothing of ours ever
   forced a 300x250 — the slots set a min-height and no width at all — and the
   collapse rule's live `:has()` test really did shift 0 -> 250px at injection.
   Deleting the containers answers both by construction rather than by argument.

   ⛔ Do not re-add a container, a reserve or a collapse rule without agreeing it
   with Raptive first. Their placement logic is theirs to tune; a theme box only
   constrains what they can serve into it. The --ad-ph-* placeholder tokens stay
   in the generated region and nothing uses them now.

   Two earlier removals, kept here so their reasoning is not rediscovered:
   .taf-ad--sidebar went because Raptive builds its OWN sticky containers inside
   the sidebar (.rp-sticky-sidebar) and manages its own reserves — NOT because
   it never filled, which was a false headless measurement (live: 9 of 11 units
   filled, seven at 300x600). .taf-ad--video and .taf-ad--sticky went with the
   containers; nothing emitted either one.

   KEPT BELOW, AND LOAD-BEARING: the sticky-footer clearance. Raptive injects
   its own adhesion unit and inc/recipe-post.php stamps the body class, so this
   reserves the 64px that keeps content and the jump pill clear of it. */
body.taf-has-sticky-ad { padding-bottom: calc(var(--space-64) + env(safe-area-inset-bottom, 0px)); }

/* Recipe anchor (menu.js contract): scroll-margin clears the sticky footer ad
   (64px) + jump pill (48px + 8px), which also clears the 60/72px sticky header.
   Applies to the #recipe anchor and to WPRM's real card container. */
#recipe,
.wprm-recipe-container { scroll-margin-top: calc(var(--space-64) + var(--space-48)); }
/* FEAST-INDEPENDENCE PIN #2 (2026-08-06): anchor jumps on content headings clear
   the 72px sticky header. Today this is FEAST's inline `scroll-margin-top: 80px`
   on every content heading (the round-5 jump-to decline recorded that fact);
   deactivate Feast and every ToC/anchor jump on ~600 old posts lands under the
   header. 80px = the exact value Feast applies, so this is computed-identical
   while Feast is active and load-bearing the day it is not.
   calc(--space-64 + --space-16) = 80, matching the container idiom above. */
.entry-content :is(h1, h2, h3, h4, h5, h6)[id] { scroll-margin-top: calc(var(--space-64) + var(--space-16)); }


/* ---- Header ---- */
.taf-header {
  background: var(--bg-page);
  border-bottom: var(--bw-1) solid var(--border-hairline);
  padding: 0 var(--pad);
  height: var(--hdr-h);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-12);
  position: sticky; top: 0; z-index: var(--z-50);
}
.taf-header .taf-brand {
  font-family: var(--display); font-size: var(--fs-20); color: var(--text-body);
  text-decoration: none; letter-spacing: var(--ls-display);
}
.taf-header .taf-brand .amp { color: var(--accent); }
.taf-logo { height: 34px; width: auto; display: block; }
.taf-header-actions { display: flex; gap: var(--space-8); align-items: center; }

.taf-icon-btn {
  width: 44px; height: 44px; border-radius: var(--radius-round);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--text-body);
  cursor: pointer; font-size: var(--fs-20);
  transition: background var(--speed) var(--ease);
}
.taf-icon-btn:hover { background: var(--bg-tinted); }

/* Desktop nav (hidden on mobile) */
.taf-nav { display: none; }
.taf-nav a {
  text-decoration: none; font-weight: var(--w-bold); font-size: var(--fs-15);
  color: var(--text-body); transition: color var(--speed) var(--ease);
}
.taf-nav a:hover { color: var(--accent); }
.taf-nav a.is-seasonal { color: var(--accent); }

/* ---- Mobile menu drawer (menu.js contract: [data-taf-menu] + .is-open,
   toggle = [data-taf-menu-toggle], body gets .taf-menu-open) ---- */
[data-taf-menu] {
  display: none;
  position: fixed;
  top: var(--hdr-h); left: 0; right: 0; bottom: 0;
  z-index: var(--z-49);
  background: var(--bg-page);
  border-top: var(--bw-1) solid var(--border-hairline);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
[data-taf-menu].is-open { display: block; }
body.taf-menu-open { overflow: hidden; }

.taf-menu-search { padding: var(--space-16) var(--pad) var(--space-4); }
.taf-menu-list { padding: var(--space-8) var(--space-8) var(--space-8); }
.taf-menu-list a {
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--space-12) var(--space-12); text-decoration: none;
  color: var(--text-body); font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-17);
  border-bottom: var(--bw-1) solid var(--border-hairline);
}
.taf-menu-list a:last-child { border-bottom: none; }
.taf-menu-list a.is-seasonal { color: var(--accent); }
.taf-menu-list .taf-icon { color: var(--text-muted); }
.taf-menu-cta { padding: var(--space-12) var(--pad) var(--space-20); }

/* ---- Search field ---- */
.taf-search {
  display: flex; align-items: center; gap: var(--space-8);
  background: var(--bg-surface); border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-pill); padding: 0 var(--space-8) 0 var(--space-16); height: 52px;
  box-shadow: var(--shadow);
}
.taf-search--flat { box-shadow: none; }
.taf-search input {
  /* token-frozen: iOS Safari zooms an input whose font-size is under 16px
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  flex: 1; border: none; outline: none; background: transparent;
  font-family: var(--sans); font-size: var(--fs-16); /* 16px stops iOS zoom */
  color: var(--text-body); min-width: 0;
}
.taf-search input::placeholder { color: var(--text-muted); }
.taf-search .go {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  height: 44px; padding: 0 var(--space-18); border-radius: var(--radius-pill); border: none; /* 44px tap target */
  background: var(--accent); color: var(--color-on-accent); font-weight: var(--w-bold); font-size: var(--fs-15); cursor: pointer;
}
.taf-search .go:hover { background: var(--accent-hover); }

/* ---- Breadcrumbs (Yoast output, wrapped by inc/breadcrumbs.php) ---- */
.taf-breadcrumbs { font-size: var(--fs-meta); color: var(--text-muted); }
.taf-breadcrumbs a { color: var(--text-muted); text-decoration: none; }
.taf-breadcrumbs a:hover { color: var(--accent); }
.taf-breadcrumbs .sep { margin: 0 var(--space-4); color: var(--border-hairline); }

/* ---- Sticky jump pill (mobile thumb zone; menu.js: .taf-jump-pill) ---- */
.taf-jump-pill {
  position: fixed; bottom: 76px; left: 50%; transform: translateX(-50%);
  z-index: var(--z-60);
  background: var(--chocolate); color: var(--milk);
  border-radius: var(--radius-pill); padding: var(--space-12) var(--space-20);
  font-weight: var(--w-bold); font-size: var(--fs-15); text-decoration: none;
  box-shadow: var(--shadow-pill);
  display: inline-flex; gap: var(--space-8); align-items: center;
}
.taf-jump-pill:hover { color: var(--color-on-dark); }

/* ---- Callouts (hairline border, no left stripe) ---- */
.taf-callout {
  background: var(--bg-tinted);
  border: var(--bw-1) solid var(--border-hairline);
  border-radius: var(--radius);
  padding: var(--space-16) var(--space-20); margin: var(--space-20) 0;
}
.taf-callout .taf-eyebrow { color: var(--saffron-deep); }
.taf-callout p { margin-top: var(--space-4); font-size: var(--fs-16); }
.taf-callout--why .taf-eyebrow { color: var(--rose-deep); }
/* The "why" callout keeps the signature rose left-accent bar instead of a full
   box outline (cream bg + 8px radius from .taf-callout stay). */
.taf-callout--why { border: none; border-left: var(--bw-4) solid var(--rose); }

/* ---- Email lead magnet (Kit) ---- */
.taf-magnet {
  background: var(--rose-tint); /* rose-tint ground, matching the home magnet */
  border-radius: var(--radius);
  padding: var(--space-24) var(--space-20) var(--space-24);
  position: relative;
  margin: var(--space-24) 0 var(--space-8);
  text-align: center;
}
.taf-magnet .taf-eyebrow { color: var(--rose-deep); }
.taf-magnet h3 { font-size: var(--fs-24); margin: var(--space-8) 0 var(--space-4); font-weight: var(--w-semibold); }
.taf-magnet p { font-size: var(--fs-15); color: var(--text-muted); max-width: 42ch; margin: 0 auto; }
.taf-magnet form { display: flex; gap: var(--space-8); margin-top: var(--space-16); }
.taf-magnet input[type="email"] {
  /* token-frozen: iOS Safari zooms an input whose font-size is under 16px
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  flex: 1; height: 48px; border-radius: var(--radius-pill);
  border: var(--bw-15) solid var(--border-hairline);
  background: var(--bg-surface); padding: 0 var(--space-18);
  font-size: var(--fs-16); /* 16px stops iOS zoom */
  min-width: 0;
}
.taf-magnet .fine { font-size: var(--fs-12); color: var(--text-muted); margin-top: var(--space-8); }
/* Kit's ASYNC-INJECTED form (formkit) in the magnet band: Kit lays the
   Name/Email fields in a flex row and its own stacking never fires inside
   our container — measured 136px-wide side-by-side fields at 390
   (2026-08-11, /indian-dessert/). Stack them full-width on phones.
   Sidebar precedent (.taf-sb-kit): !important beats Kit's [data-uid]
   per-form sheet. */
@media (max-width: 699px) {
  .taf-magnet .formkit-fields { flex-direction: column !important; }
  .taf-magnet .formkit-field { flex: 1 1 100% !important; width: 100% !important; }
  .taf-magnet .formkit-input { width: 100% !important; }
}

/* ---- Author bio ---- */
.taf-bio {
  display: flex; gap: var(--space-16); align-items: flex-start;
  background: var(--bg-surface); border: var(--bw-1) solid var(--border-hairline);
  border-radius: var(--radius); padding: var(--space-16); margin: var(--space-24) 0;
}
.taf-bio img { width: 64px; height: 64px; border-radius: var(--radius-round); object-fit: cover; flex-shrink: 0; }
.taf-bio h4 { font-family: var(--display); font-size: var(--fs-17); font-weight: var(--w-regular); }
.taf-bio p { font-size: var(--fs-15); color: var(--text-muted); margin-top: var(--space-4); line-height: var(--lh-15); }
.taf-bio a { font-size: var(--fs-15); font-weight: var(--w-bold); }

/* ---- FAQ (details/summary) ---- */
.taf-faq details { border-bottom: var(--bw-1) solid var(--border-hairline); }
.taf-faq summary {
  padding: var(--space-12) 0; cursor: pointer;
  font-weight: var(--w-bold); font-size: var(--fs-16); list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-12);
}
.taf-faq summary::-webkit-details-marker { display: none; }
.taf-faq summary .taf-icon { color: var(--text-muted); transition: transform var(--speed) var(--ease); }
.taf-faq details[open] summary .taf-icon { transform: rotate(180deg); }
.taf-faq details > div { padding: 0 0 var(--space-16); font-size: var(--fs-16); color: var(--text-body); }

/* ---- Footer (light ground, chocolate text, 4-col grid: brand blurb +
   Recipes / Seasonal / About). ---- */
.taf-footer {
  /* token-frozen: clears the sticky ad — ad viewability is revenue
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  background: var(--footer-bg); color: var(--footer-text);
  padding: var(--space-44) var(--pad) var(--space-90); /* bottom clears the sticky ad */
  margin-top: var(--space-40);
}
.taf-footer__inner { max-width: var(--maxw-wide); margin: 0 auto; }
.taf-footer__grid { display: grid; grid-template-columns: 1fr; gap: var(--space-24); }
.taf-footer .taf-brand { font-family: var(--display); font-size: var(--fs-20); color: var(--footer-text); margin: 0; }
.taf-footer .taf-blurb { font-size: var(--fs-15); color: var(--footer-label); margin: var(--space-8) 0 0; max-width: 30ch; }
.taf-footer__logo { display: block; width: auto; height: auto; max-width: 190px; margin: 0 0 var(--space-12); }
/* Column headings are <h2> (heading order) styled as small uppercase labels. */
.taf-footer h2 {
  font-family: var(--display); font-size: var(--fs-12); font-weight: var(--w-bold);
  letter-spacing: var(--ls-14em); text-transform: uppercase;
  color: var(--footer-label); margin: 0 0 var(--space-12);
}
.taf-footer a { color: var(--footer-text); text-decoration: none; }
.taf-footer a:hover { color: var(--rose); text-decoration: underline; text-underline-offset: 3px; }
.taf-footer__col a { display: block; padding: var(--space-4) 0; font-size: var(--fs-15); } /* ~33px targets — DELIBERATE WAIVER: the 44px house floor is waived for footer links (the 10px padding read as too much air); WCAG AA 2.5.8 (24px) still clears. Do not "fix" back to 44 without asking. */
.taf-footer .legal { margin-top: var(--space-32); padding-top: var(--space-20); border-top: var(--bw-1) solid var(--footer-rule); font-size: var(--fs-12); color: var(--footer-legal); }
@media (min-width: 900px) {
  /* logo far left, the 3 nav columns clustered on the right (per reference) */
  .taf-footer__grid { grid-template-columns: 1fr auto auto auto; column-gap: var(--space-48); row-gap: var(--space-24); align-items: start; }
}

/* ---- Utilities ---- */
.taf-hr { border: none; border-top: var(--bw-1) solid var(--border-hairline); margin: var(--space-24) 0; }
.taf-center { text-align: center; }

/* Focus visibility: 3px deep-saffron ring everywhere (--focus-ring-color, WCAG 1.4.11) */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: var(--focus-ring) solid var(--focus-ring-color);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Desktop (900px) ---- */
@media (min-width: 900px) {
  /* == GENERATED: tokens — do not edit by hand. Source: design/tokens.json (scripts/build-tokens.py) == */
  :root {
    --fs-hero: 52px;
    --fs-h1: 42px;
    --fs-h2: 30px;
    --fs-h3: 22px;
    --pad: 32px;
    --hdr-h: 72px; /* 72px desktop header */
  }
  /* == END GENERATED == */
  .taf-section { padding: var(--space-40) var(--pad); }
  /* Adjacent plain sections stacked 40+40 = 80px voids (recipe index, measured
     2026-07-17) while ad-flanked pairs read 40 — collapse the second section's
     top so every boundary lands on the 40 beat. Both :not()s matter: the flush
     ad wrapper is itself a .taf-section--wide, and zeroing around it would
     crush the ad slot's breathing room. */
  .taf-section--wide:not(.taf-section--flush) + .taf-section--wide:not(.taf-section--flush) { padding-top: 0; }
  .taf-grid-3 { grid-template-columns: repeat(3, 1fr); gap: var(--space-20); }
  .taf-grid-4 { grid-template-columns: repeat(4, 1fr); gap: var(--space-20); }
  .taf-logo { height: 42px; }
  .taf-nav { display: flex; gap: var(--space-24); align-items: center; }
  [data-taf-menu-toggle] { display: none; }
}

/* ======================
   Recipe post template. Markup: inc/recipe-post.php.
   ====================== */

/* ---- Meta row (directly after the H1) ----
   The H1→rating-row gap is carried by .entry-header's margin-bottom in the
   scaffold block below, not here. */
.taf-post-meta { margin: 0 0 var(--space-16); }
/* (.taf-post-meta__rating row retired 2026-08-01 — the rating now lives
   inside the byline line; actions in .taf-post-meta__actions-row.) */
.taf-post-meta__stars { display: inline-flex; align-items: center; gap: var(--space-4); }
.taf-post-meta__stars .taf-stars { font-size: var(--fs-15); }
/* WPRM display stars inside the meta row: TEXT size only (the "4.7 · 9
   ratings" line — WPRM's star SVGs size by width/height attrs, not
   font-size). Star color/shape comes from the sitewide saffron painter
   near the end of this file; glyph geometry (20px interactive tier,
   tight tap boxes) lives in wprm-taf2026.css §16. */
.taf-post-meta__stars .wprm-recipe-rating { display: inline-flex; align-items: center; font-size: var(--fs-15); }
/* (.taf-post-meta__facts retired — Option A, no
   time/servings up top; the card's facts strip owns them.) */
/* Header layout, Option F (final): one row = avatar ·
   [name over date, stacked to the avatar's height] · buttons right-pinned
   (desktop-only; the pill covers phones). Stars on their own line below,
   sitting above the Grow share row; the rating div is server-omitted when
   unrated, so nothing reflows — the header is simply one row shorter. */
.taf-post-meta__byline { display: flex; align-items: center; gap: var(--space-10, 10px); margin: 0; }
.taf-post-meta__who { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
/* Byline type: 13 -> 15 (16 at >=900, in the desktop block below). User
   2026-08-04. Two things were wrong with 13px --fs-meta here:
     • it was the ONLY header element that did not step at desktop — H1 goes
       30 -> 42, h2 24 -> 30, and the rating TEXT beside it 15 -> 16, while the
       byline stayed 13, so the desktop header was proportionally the most
       imbalanced (42px H1 against 13px attribution);
     • it rendered SMALLER than "4.4 · 7 ratings" sitting directly beneath it in
       the same block — the author's name was smaller than her rating count.
   13px is --fs-meta, the tier for INCIDENTAL chrome (breadcrumbs, comment
   dates). Author attribution and the Updated date are primary trust signals on
   a recipe post, not chrome. These now ride the rating text's existing 15/16
   ladder rather than introducing a new value, so the whole meta block reads as
   one tier at each width. Name and date move together — their equal-size
   relationship is unchanged, only the tier. */
.taf-post-meta__name { font-size: var(--fs-15); line-height: var(--lh-15, 1.5); }
.taf-post-meta__date { font-size: var(--fs-15); line-height: var(--lh-14, 1.4); }
.taf-post-meta__actions { display: inline-flex; align-items: center; gap: var(--space-8); margin-left: auto; }
.taf-post-meta__rating { display: flex; align-items: center; margin-top: var(--space-8); }
/* Jump + Watch video show at EVERY width now — the fixed
   mobile pill is retired, so these two are the only jump affordance and must
   not be desktop-only. Below 900 they cannot share the byline's line (avatar +
   name/date + two pills overflows 390 badly), so the byline wraps and the
   action pair takes its own full-width row directly beneath — still "pinned to
   the header", just on the header's second line. */
.taf-post-meta__jump { display: inline-flex; }
@media (max-width: 899.98px) {
  .taf-post-meta__byline { flex-wrap: wrap; }
  .taf-post-meta__actions {
    width: 100%;
    margin-left: 0;
    margin-top: var(--space-10, 10px);
  }
  /* 44px floor: these were desktop-only pills at 41px tall, which was fine as a
     secondary affordance beside a 49px thumb-zone pill. With the pill retired
     they ARE the mobile jump control, so they have to clear WCAG 2.5.5 on their
     own. Measured 41 -> 44. */
  .taf-post-meta__actions { align-items: stretch; }
  .taf-post-meta__actions .taf-btn {
    flex: 1 1 0;
    justify-content: center;
    min-height: var(--space-44);
    /* nowrap is load-bearing, not tidiness: sharing the row two-up leaves each
       pill ~170px, and "Jump to recipe" + its arrow wrapped to two lines there
       — measured 66px tall against Watch video's 44, so the pair rendered
       visibly mismatched. Held on one line both land at 44. */
    white-space: nowrap;
    padding-left: var(--space-10, 10px);
    padding-right: var(--space-10, 10px);
  }
}
/* Avatar: 40px at EVERY width. Was 36 mobile / 40 desktop,
   and the mobile 36 became the odd one out when name+date went 13 -> 15/16
   just above: the byline text stack grew to 43.5px and a 36px circle no
   longer anchored it — the avatar read small against its own two lines.
   40 matches the stack and matches desktop, so there is now one avatar size.
   No upscaling risk: recipe-post.php already requests get_avatar(..., 64).
   The >=900 override that used to raise it is now redundant and is gone. */
.taf-post-meta__byline .taf-post-meta__avatar {
  width: 40px; height: 40px; border-radius: var(--radius-round);
  object-fit: cover; flex-shrink: 0;
}
.taf-post-meta__byline p { margin: 0; }
.taf-post-meta__byline a { font-weight: var(--w-bold); }
/* Equal-height meta-row buttons: the bordered secondary (Watch video) rendered 3px
   taller than the primary Jump button — same 8px padding PLUS its 1.5px borders.
   Compensate the vertical padding by the border width; the primary never moves. */
.taf-post-meta__actions .taf-btn--secondary.taf-btn--sm {
  padding-top: calc(var(--space-8) - var(--bw-15));
  padding-bottom: calc(var(--space-8) - var(--bw-15));
}

/* ---- Desktop rail (2026-08-01 sidebar handoff, inc/sidebar.php) ----
   Five stacked widgets: bio · trusted-site button · Kit magnet · popular ·
   seasonal. Kadence's primary sidebar, 320px grid column (see the single-post
   grid rule below). NOT sticky — the handoff specs a static rail (the old
   rail's `position: sticky; top: calc(var(--hdr-h) + 16px)` was removed here;
   restore that one line to revert). Hidden below 900px: Kadence stacks the
   sidebar UNDER the content there and the handoff renders no mobile rail. */
.taf-single-sidebar {
  display: none;
  flex-direction: column;
  gap: 36px; /* token-exempt: handoff rail rhythm — 36px widget gap sits between the 32/40 spacing steps */
}

/* Card shells: cream (bio, Kit magnet) and ruled paper (popular, seasonal). */
.taf-sb-card { border-radius: var(--radius); }
.taf-sb-card--cream { background: var(--bg-tinted); }
.taf-sb-card--paper { background: var(--bg-surface); border: var(--bw-1) solid var(--border-hairline); }

/* W1: author bio. */
.taf-sb-bio {
  text-align: center;
  padding: 26px var(--space-24) var(--space-24); /* token-exempt: comp pad — 26px top, no spacing step */
}
.taf-sb-bio__photo {
  display: block; width: 116px; height: 116px; margin: 0 auto; /* comp portrait — 116px circle */
  border-radius: var(--radius-round); object-fit: cover;
  border: 3px solid var(--paper); /* token-exempt: comp portrait ring — 3px sits between --bw-15 and --bw-4 */
  box-shadow: var(--shadow);
}
.taf-sb-bio__hi {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-20);
  line-height: var(--lh-115); color: var(--text-body);
  margin: var(--space-14) 0 var(--space-8);
}
.taf-sb-bio__copy {
  font-family: var(--sans); font-size: var(--fs-15); line-height: var(--lh-16);
  color: var(--text-muted); margin: 0;
}

/* Shared "more" link (bio + seasonal footer): 15/700 rose-link + 15px arrow.
   Rail-scoped: Kadence's content.min.css paints `.widget-area a:where(...)`
   at 0,1,1 (chocolate) and `:hover` at 0,2,1 — bare .taf-sb-* class rules
   LOSE (verified rendered 2026-08-01). The .taf-single-sidebar prefix wins. */
.taf-single-sidebar .taf-sb-more {
  display: inline-flex; align-items: center; gap: var(--space-6);
  font-family: var(--sans); font-size: var(--fs-15); font-weight: var(--w-bold);
  color: var(--rose-link); text-decoration: none;
}
.taf-sb-bio__more { margin-top: var(--space-12); }
.taf-single-sidebar .taf-sb-more:hover,
.taf-single-sidebar .taf-sb-more:focus-visible { color: var(--rose-deep); }
.taf-sb-more .taf-pf-arrow { width: 15px; height: 15px; flex: none; }

/* W2: trusted-site button — pixel-matches the recipe card's Pin secondary
   button (wprm-taf2026.css §7: paper fill, 1.5px rose border, rose-deep text,
   46px action height, 12/16 pad, 13/700 uppercase .08em, 17px icon, rose-tint
   hover). Wiring is production's exact mechanism: a static Google Preferred
   Sources link (Feast's feast_trusted_google_source block) — no JS. */
.taf-single-sidebar .taf-sb-trust {
  display: inline-flex; width: 100%; align-items: center; justify-content: center;
  gap: var(--space-8);
  min-height: 46px; /* token-exempt: V2 action height (matches the card's Print/Pin) */
  padding: var(--space-12) var(--space-16);
  border-radius: var(--radius-pill);
  border: var(--bw-15) solid var(--rose);
  background: var(--paper); color: var(--rose-deep);
  font-family: var(--display); font-size: var(--fs-13); font-weight: var(--w-bold);
  text-transform: uppercase; letter-spacing: var(--ls-08em);
  white-space: nowrap; text-decoration: none;
  transition: background var(--speed) var(--ease);
}
.taf-single-sidebar .taf-sb-trust:hover,
.taf-single-sidebar .taf-sb-trust:focus-visible { background: var(--rose-tint); color: var(--rose-deep); }
.taf-sb-trust .taf-sb-gicon { width: 17px; height: 17px; flex: none; }

/* W3: email magnet (Kit form 9655435 — same embed as the magnet bands; the
   .formkit-* selectors restyle Kit's async-injected markup exactly the way
   .taf-magnetband__form does, and may drift with Kit updates the same way). */
.taf-sb-kit {
  padding: var(--space-24) 22px 26px; /* token-exempt: comp pad — 22px sides / 26px bottom, no spacing steps */
}
.taf-sb-kit__eyebrow { margin: 0; color: var(--rose-link); }
.taf-sb-kit__title {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-22);
  line-height: var(--lh-12); color: var(--text-body);
  margin: var(--space-10) 0 var(--space-8);
}
.taf-sb-kit__pitch {
  font-family: var(--sans); font-size: var(--fs-15); line-height: var(--lh-16);
  color: var(--text-muted); margin: 0;
}
.taf-sb-kit__form { margin-top: var(--space-16); }
.taf-sb-kit__form .formkit-form,
.taf-sb-kit__form form { max-width: none; margin: 0; padding: 0; background: transparent; border: none; box-shadow: none; }
.taf-sb-kit__form .formkit-fields,
.taf-sb-kit__form [data-element="fields"],
.taf-sb-kit__form .seva-fields { display: flex; flex-direction: column; gap: var(--space-10); margin: 0; }
.taf-sb-kit__form .formkit-field { flex: 0 0 auto !important; min-width: 0 !important; margin: 0 !important; }
.taf-sb-kit__form .formkit-input,
.taf-sb-kit__form input[type="email"],
.taf-sb-kit__form input[type="text"] {
  width: 100%;
  /* token-frozen: iOS Safari zooms an input whose font-size is under 16px;
     44px is the WCAG tap-target floor. The handoff's 44px/15px maps onto the
     sitewide constraint pair — constraint-check asserts both. */
  height: var(--space-44); font-size: var(--fs-16);
  border-radius: var(--radius-sm) !important;
  border: var(--bw-1) solid #E5C8D6; /* token-exempt: sidebar input-border constant — mirrors the V2 #DFC3D2 rule treatment */
  background: var(--paper); padding: 0 var(--space-14); box-shadow: none;
}
/* Kit's embedded per-form stylesheet ships `.formkit-form[data-uid]
   .formkit-submit { display:inline-block }` (0,2,1) and padded spans — they
   beat plain descendant rules and wrapped the button to two lines (verified
   rendered 2026-08-01). display/padding carry !important like width does. */
.taf-sb-kit__form .formkit-submit,
.taf-sb-kit__form button[type="submit"],
.taf-sb-kit__form button {
  width: 100% !important; flex: 0 0 auto !important;
  display: flex !important; align-items: center; justify-content: center; gap: var(--space-8);
  min-height: 46px; /* token-exempt: V2 action height (matches the card's Print/Pin) */
  background: var(--rose); color: var(--color-on-accent);
  font-family: var(--display); font-size: var(--fs-13); font-weight: var(--w-bold);
  text-transform: uppercase; letter-spacing: var(--ls-08em);
  padding: var(--space-12) var(--space-16);
  border-radius: var(--radius-sm) !important; border: none; cursor: pointer;
  white-space: nowrap; transition: background var(--speed) var(--ease);
}
.taf-sb-kit__form .formkit-submit:hover,
.taf-sb-kit__form button:hover { background: var(--rose-deep); }
.taf-sb-kit__form .formkit-submit > div,
.taf-sb-kit__form .formkit-submit span { margin: 0 !important; padding: 0 !important; color: var(--color-on-accent); }
.taf-sb-kit__form .taf-sb-plane { width: 17px; height: 17px; flex: none; }
.taf-sb-kit__form .formkit-header,
.taf-sb-kit__form .formkit-subheader,
.taf-sb-kit__form .formkit-guarantee,
.taf-sb-kit__form [data-element="header"],
.taf-sb-kit__form [data-element="subheader"],
.taf-sb-kit__form [data-element="guarantee"],
.taf-sb-kit__form .formkit-powered-by-convertkit-container,
.taf-sb-kit__form .formkit-powered-by-convertkit { display: none !important; }
/* Kit ships a 30px margin under the <form> plus a 10px pad on its wrapper;
   both stacked on the card's own 26px padding and left the subscribe card
   visibly bottom-heavy (measured 56px below the button vs 24px above the
   eyebrow —). Zero Kit's two, keep ours. */
.taf-sb-kit__form .formkit-form { margin-bottom: 0 !important; }
/* Kit's inner wrapper ships 56px of TOP padding (a huge hole between the
   pitch and the first field) plus a horizontal inset that pushed the inputs
   out of line with the card's own text, and its submit button adds 15px
   below. Zero all three: fields align to the text column, the pitch→field
   gap becomes the wrapper's own 16px, and the card's 26px bottom padding
   matches its 24px top. (all measured.) */
.taf-sb-kit__form .formkit-form > div { padding: 0 !important; }
.taf-sb-kit__form .formkit-submit,
.taf-sb-kit__form button { margin-bottom: 0 !important; }
/* Kit's field row carries margin:-5px, so the inputs hung 5px outside the
   card's text column on each side. Zero it — fields align to the copy. */
.taf-sb-kit__form .formkit-fields { margin-left: 0 !important; margin-right: 0 !important; }

/* W4 + W5: ruled-heading tile cards (shared classes; only content differs). */
.taf-sb-list { padding: var(--space-20) var(--space-18); }
.taf-sb-list__h {
  display: flex; align-items: center; gap: var(--space-6);
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-20);
  line-height: var(--lh-115); letter-spacing: var(--ls--001em); color: var(--text-body);
  margin: 0 0 var(--space-16);
}
/* Trailing rule fills the heading line — the recipe card's section-heading
   pattern (wprm-taf2026.css §8). */
.taf-sb-list__h::after {
  content: "";
  flex: 1;
  border-top: var(--bw-1) solid #DFC3D2; /* token-exempt: V2 section-rule constant (mirrors wprm-taf2026.css) */
}
.taf-sb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-20) var(--space-14); }
.taf-sb-tile { display: block; color: var(--text-body); text-decoration: none; }
.taf-sb-tile__img {
  display: block; width: 100%; height: auto;
  aspect-ratio: var(--ar-1-1); object-fit: cover;
  border-radius: var(--radius-sm);
}
.taf-sb-tile__t {
  display: block; margin-top: var(--space-8);
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-15);
  line-height: var(--lh-13);
}
.taf-sb-tile:hover .taf-sb-tile__t,
.taf-sb-tile:focus-visible .taf-sb-tile__t { color: var(--rose-deep); }
.taf-sb-list .taf-sb-more { margin-top: var(--space-18); }

/* ---- Jump pill visibility (menu.js toggles .is-hidden while the card is on
   screen; base styles are in the components block above) ---- */
.taf-jump-pill { transition: opacity var(--speed) var(--ease), visibility var(--speed) var(--ease); }
.taf-jump-pill.is-hidden { opacity: 0; visibility: hidden; pointer-events: none; }

@media (min-width: 900px) {
  .taf-single-sidebar { display: flex; }
  .taf-post-meta__jump { display: inline-flex; }
  /* Desktop: byline→content 18px, 16px meta-row rating TEXT (the count line;
     the star glyphs themselves sit on the 20px interactive token in
     wprm-taf2026.css §16). The avatar override that lived here is GONE — the
     base rule is 40px at every width now, so this was a no-op restating it. */
  .taf-post-meta { margin-bottom: var(--space-16); }
  /* Byline steps with the rating text it sits above (see the base rule). */
  .taf-post-meta__name,
  .taf-post-meta__date { font-size: var(--fs-16); }
  .taf-post-meta__stars .taf-stars,
  .taf-post-meta__stars .wprm-recipe-rating { font-size: var(--fs-16); }
  /* Desktop replaces the pill with the meta-row button. */
  .taf-jump-pill { display: none; }
}

/* ======================
   Raptive ad plumbing. The reserved zones above hold their min-height with ads
   blocked, so injection causes zero CLS; body.taf-has-sticky-ad reserves the 64px
   adhesion clearance (class added by inc/recipe-post.php).
   STABLE RAPTIVE CONTENT SELECTOR — DO NOT RENAME: .taf-post-content, which
   inc/recipe-post.php wraps around the_content of every single post so Raptive's
   `content_selector` can target it with no markup change.
   ====================== */
.taf-post-content { display: block; }

/* In-content images take the 8px surface radius. Scoped to post-body images: WPRM
   card + instruction images must stay UNTOUCHED — their wprm-taf2026.css rules are
   higher specificity and !important on the radius, so this (0,1,1) rule never reaches
   them; :not([class*="wprm"]) is belt-and-braces. */
.taf-post-content img:not([class*="wprm"]) {
  border-radius: var(--radius);
  object-fit: cover;
}

/* Single-post desktop grid: 728px content + 48px gutter + 320px sidebar, centered
   (Kadence's default gives content the full remainder). Rail widened 300 → 320
   (2026-08-01 sidebar handoff). Content column widened 700 → 728 on 2026-08-06 at
   RAPTIVE'S RECOMMENDATION, so a 728x90 leaderboard fits the in-content slots
   exactly; the row is 728 + 48 + 320 = 1096, still inside the 1200px container.
   ⚠️ It MUST track --maxw (700 → 728 in the same release) — this track and the
   token are the same measure written twice, and taf-postfooter.css re-derives the
   article's left edge from --maxw. ⚠️ taf-postfooter.css's full-bleed correction
   constant is derived from the OTHER two values — (320 + 48) / 2 = 184px, sidebar
   and gutter only, so the width change does not move it — and MUST be recomputed
   if the sidebar or gutter ever changes. */
@media (min-width: 1024px) {
  body.single-post .content-container.site-container {
    grid-template-columns: minmax(0, 728px) 320px;
    justify-content: center;
    column-gap: var(--space-48);
  }
}

/* ======================
   Homepage — block-built from patterns/ so the owner can reorder bands.
   ====================== */

/* Flush section wrapper: horizontal band alignment without doubled vertical
   padding (for bands whose inner elements carry their own margins). */
.taf-section--flush { padding-top: 0; padding-bottom: 0; }

/* ---- Hero (patterns/hero-home.php) ----
   Hero copy lives in .taf-hero-copy beside .taf-hero-photo: split at 900px, photo
   hidden on mobile. */
.taf-hero { padding-top: var(--space-32); padding-bottom: 0; }
.taf-hero .taf-hero-title { max-width: 13ch; margin: var(--space-8) auto var(--space-12); }
.taf-hero .taf-hero-copy > p.taf-muted { max-width: 34ch; margin-left: auto; margin-right: auto; }
.taf-hero .taf-search { margin: var(--space-20) auto var(--space-12); }
.taf-hero .wp-block-buttons { row-gap: var(--space-8); }

/* wp:search bridge, hero-scoped: the core search block becomes the design's pill —
   one bordered container (.taf-search) around a borderless input + rose button. Core's
   inside-wrapper border/padding is neutralized so the container carries the border. */
.taf-hero .taf-search .wp-block-search__inside-wrapper {
  flex: 1; display: flex; align-items: center; gap: var(--space-8);
  border: none; padding: 0;
}
.taf-hero .taf-search .wp-block-search__input {
  /* token-frozen: iOS Safari zooms an input whose font-size is under 16px
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  flex: 1; min-width: 0; min-height: 0;
  border: none; background: transparent;
  padding: var(--space-10) 0;
  font-size: var(--fs-16); /* 16px stops iOS zoom */
}
.taf-hero .taf-search .wp-block-search__input:focus-visible { outline: none; }
.taf-hero .taf-search:focus-within {
  outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; /* search = rose, see .taf-search rule */
}
.taf-hero .taf-search .wp-block-search__button {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  min-height: 44px; margin: 0; padding: var(--space-8) var(--space-18); /* 44px tap target */
  font-size: var(--fs-15); font-weight: var(--w-bold);
}

/* Ghost button bridge; same wrapper-to-inner bridging pattern as the .taf-btn
   rules above. */
.wp-block-button.taf-btn--ghost { background: transparent; border: none; padding: 0; min-height: 0; }
.wp-block-button.taf-btn--ghost .wp-block-button__link {
  display: inline-flex; align-items: center; gap: var(--space-8);
  background: transparent; color: var(--accent); border: none;
  min-height: 44px; padding: var(--space-8) var(--space-12);
  font-family: var(--display); font-size: var(--fs-16); font-weight: var(--w-semibold);
  text-decoration: none; border-radius: var(--radius-pill);
  transition: color var(--speed) var(--ease);
}
.wp-block-button.taf-btn--ghost .wp-block-button__link:hover { color: var(--accent-hover); background: transparent; }

/* ---- About band (patterns/about-band.php) ---- */
.taf-about-band .wp-block-image { width: 96px; margin: 0 auto; }
.taf-about-band .wp-block-image img {
  width: 96px; height: 96px;
  border-radius: var(--radius-round); object-fit: cover;
}
.taf-about-band h2 { margin: var(--space-12) 0 var(--space-8); }
.taf-about-band p.taf-muted { max-width: 36ch; margin-left: auto; margin-right: auto; }

@media (min-width: 900px) {
  .taf-hero { padding-top: var(--space-48); }
  .taf-hero .taf-hero-title { max-width: 16ch; margin: var(--space-12) auto var(--space-16); }
  .taf-hero .taf-hero-copy > p.taf-muted { font-size: var(--fs-20); max-width: 44ch; }
  .taf-hero .taf-search { max-width: 440px; margin-top: var(--space-24); }
}

/* ======================
   Kadence cascade defence — relied on by every override below.

   Kadence prints its generated CSS AFTER this sheet, and its equal-specificity rules
   (fed by Feast's filtered Kadence defaults — palette3 #000000, palette4 #3a3a3a,
   palette5 #848484, palette8 #f3f3f3, heading font "inherit") win every tie against the
   base rules above. Those values come from a Feast code filter, not the DB: there is no
   theme-mod to change. Two layers:
     1) inc/enqueue.php enqueues child CSS at priority 20, after every Kadence sheet, so
        equal-specificity rules win ties again.
     2) The bridge below re-points Kadence's own custom properties at Warm Oven tokens,
        so Kadence's rules render on-system at ANY specificity — html:root (0,1,1) beats
        its :root (0,1,0) regardless of load order.
   ====================== */

/* ---- Kadence bridge: Kadence globals → Warm Oven tokens ---- */
html:root {
  --global-heading-font-family: var(--display); /* was "inherit" → system sans */
  --global-palette3: var(--chocolate); /* heading ink; was #000000 */
  --global-palette4: var(--chocolate); /* body/input ink; was #3a3a3a */
  /* Muted text — desktop nav links, comment float-labels and .comment-reply-link all
     draw palette5. #848484 failed AA (3.74:1 on white); cocoa = 6.41:1. */
  --global-palette5: var(--cocoa);
  --global-palette8: var(--milk);      /* page background; was #f3f3f3 */
  /* Kadence global.min.css a{} / a:hover{} read these — keep them on the
     Warm Oven link colors. */
  --global-palette-highlight: var(--text-link);
  --global-palette-highlight-alt: var(--accent-hover);
}

/* Kadence ships `a { transition: all .1s linear }` (global.min.css) — `all` animates
   layout and transform too, so any future positional change on a bare link would
   animate by accident. Scope anchors to paint properties at the house speed. Equal
   specificity (0,0,1); this sheet loads later, so it wins the tie. Components that
   declare their own transition list (.taf-cat's transform lift, .taf-headsocial__link,
   every .taf-* control) out-specify this and keep their lists — verified: no anchor
   relies on THIS rule for a transform transition (the tile photo zoom rides the img,
   not the anchor). */
a {
  transition-property: color, background-color, border-color, box-shadow;
  transition-duration: var(--speed);
}

/* Heading ink. h4–h6 join the display face explicitly; weight 400 pins the Poppins
   Regular instance so browsers don't faux-bold now that --global-heading-font-family
   resolves to Poppins. */
h1, h2, h3, h4, h5, h6 { color: var(--text-body); }
h4, h5, h6 { font-family: var(--display); font-weight: var(--w-regular); }

/* ---- Feast in-content "Jump to:" TOC (#feast-advanced-jump-to) — full restyle, CSS only ----
   Feast injects a <style> element INSIDE post content (after this sheet in document
   order). Measured on the rendered page /stollen-recipe/ 2026-07-17: ground #F9EEE4 (the banned
   beige, via var(--global-palette7)), a 4px solid #B1B1B1 left stripe (the banned
   side-stripe, via var(--global-palette6)), radius 0, opacity .97. The injected rules are
   a STYLE ELEMENT, not inline style="" attrs, so specificity is enough: details#… (1,0,1)
   beats the injected #feast-advanced-jump-to (1,0,0) at any print order — no !important.
   Copy and markup untouched; margins/z-index/max-height kept at Feast's values.

   ⚠ SUPERSEDES the exempted #A34A34 link fix that lived here: that literal existed
   only to clear AA through the opacity-.97 blend onto the old beige. Opacity is pinned
   to 1 below (no blend), the ground is --milk, and the house link tokens pass directly —
   the link math is only valid while opacity stays 1:
     --rose-link #D01277 on --milk #FEFBFA = 5.05:1 (AA ≥ 4.5 ✓)
     --rose-deep #B80E67 on --milk = 6.19:1 (hover ✓)
     --chocolate #33202B on --milk = 14.7:1 (summary/body ✓) */
details#feast-advanced-jump-to {
  background: var(--milk);
  border: var(--bw-1) solid var(--line); /* hairline all round replaces the 4px stripe (shorthand out-specifies Feast's border-left) */
  border-radius: var(--radius);
  opacity: var(--o-1); /* kills Feast's .97 blend — the AA math above depends on this */
  color: var(--chocolate);
}
details#feast-advanced-jump-to summary { color: var(--chocolate); }
details#feast-advanced-jump-to li a {
  color: var(--rose-link);
  /* 44px tap target: inline-block + block padding lifts each TOC row past the floor. */
  display: inline-block;
  padding-block: var(--space-10);
}
details#feast-advanced-jump-to li a:hover,
details#feast-advanced-jump-to li a:focus-visible { color: var(--rose-deep); }

/* The mobile jump pill is printed inside a <nav aria-label="Recipe shortcuts">
   landmark (taf2026_jump_pill(), inc/recipe-post.php). No styles needed: the pill is
   position:fixed, so the <nav> box is empty and zero-height. */

/* ---- Tile figure bridge ----
   WP wraps tile imgs in figure.wp-block-image between .ph and the img, so
   `.taf-tile .ph img{height:100%}` resolved against the auto-height figure and square
   photos sat short in the 4:5 box. */
.taf-tile .ph figure { height: 100%; }

/* ---- Homepage: counters + component deltas ---- */

/* ---- Feast homepage-styling counters ----
   Feast injects #feast-homepage-styling-<id> on the front page: it centers .entry-content
   h2 with a bottom hairline + padding, and hides .wp-block-search button/__label. Feast
   must stay active, so out-specify it: (0,2,1) beats its (0,1,1) at any print order. */
.entry-content .taf-section h2 {
  margin: var(--space-4) 0 var(--space-4);
  padding-bottom: 0;
  border-bottom: none;
  text-align: inherit;
}
.entry-content .taf-section-head h2 { margin: 0; } /* eyebrow→h2 abuts */
.entry-content .taf-about-band h2 { margin: var(--space-12) 0 var(--space-8); }
.entry-content .taf-eyebrow { margin: 0; }

/* Un-hide + re-seat the hero search's Go button (Feast hides .wp-block-search button
   on the homepage). The doubled class beats Kadence's (0,3,0) `:not(.has-background)`
   rule that painted the button white. */
.taf-search .wp-block-search__button.wp-block-search__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  visibility: visible;
  background: var(--accent);
  color: var(--color-on-accent);
}
.taf-search .wp-block-search__button.wp-block-search__button:hover {
  background: var(--accent-hover);
}
.taf-search .wp-block-search__input::placeholder { color: var(--text-muted); }

/* ---- Rose-milk band background fallback ----
   The theme.json preset CSS for this entry is not reliably emitted
   (--wp--preset--color--rose-milk resolves EMPTY), so the class painted transparent. Same
   value as theme.json, so WP's preset rule agrees if/when it lands. */
.has-rose-milk-background-color { background-color: var(--cream); }

/* ---- Tile title links: quiet ink, underline only on tile hover ----
   (The global link element styles painted them pink + always-underlined.) */
.taf-tile .t a,
.taf-tile h3 a {
  color: inherit;
  text-decoration: none;
}
.taf-tile .t a:hover,
.taf-tile .t a:focus-visible,
.taf-tile h3 a:hover { color: inherit; }
/* Figure margin safety: the tile photo must fill its 4:5 box even in contexts
   without Kadence's figure reset (editor iframe, harnesses). */
.taf-tile .ph figure { margin: 0; }

/* ---- Tile hover: shadow + image zoom (scale 1.04, .35s), cropping inside the
   overflow:hidden rounded .ph box. One hover language site-wide. ---- */
@media (hover: hover) and (pointer: fine) {
  .taf-tile .ph { transition: box-shadow var(--speed) var(--ease); }
  .taf-tile .ph img { transition: transform var(--dur-35) var(--ease); }
  .taf-tile:hover .ph img,
  .taf-tile:focus-visible .ph img { transform: scale(1.04); }
}

/* ---- Breakpoint utilities: mobile shows 2 tiles per band + a full-width browse
   button; desktop shows 4-up grids + a section-head "more" link instead ---- */
.taf-hide-mobile { display: none; }
.taf-tile--desktop { display: none; }

/* ---- Mobile chip row: one horizontal scroll lane, edge-bled to the viewport ---- */
@media (max-width: 899.98px) {
  .taf-hero .wp-block-buttons {
    display: flex;
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: var(--space-8);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin-left: calc(-1 * var(--pad));
    margin-right: calc(-1 * var(--pad));
    padding: 0 var(--pad) var(--space-8);
  }
}

/* ---- Hero photo: hidden on mobile (the 390 hero is typographic-only) ---- */
.taf-hero-photo { display: none; }

/* ---- Bread band rhythm: intro line capped at 40ch, grid 18px under it ---- */
.has-rose-milk-background-color > .taf-section { padding: var(--space-32) var(--pad) var(--space-32); }
.entry-content .taf-section .taf-muted.taf-small { margin: 0; max-width: 40ch; }
.entry-content .taf-section-head + .taf-muted.taf-small { margin: -8px 0 var(--space-16); } /* token-exempt: a negative pull-up, not a scale step. calc(var(--space-N) * -1) is equivalent but unreadable, and css-parity compares text not computed values, so it cannot prove the equivalence anyway. */
.has-rose-milk-background-color .taf-grid-4 { margin-top: var(--space-16); }

/* ---- Footer (rendered by inc/footer.php on kadence_footer) ---- */
.taf-footer__inner { max-width: var(--maxw-wide); margin: 0 auto; }
/* Legal-line links run inline inside the © sentence (the footer rule above makes
   column links display:block). */
.taf-footer .legal a {
  display: inline;
  padding: 0;
  color: var(--footer-legal);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.taf-footer .legal a:hover { color: var(--rose); }
/* Safety: if Kadence's own footer ever prints alongside ours (remove_action
   raced), keep the page from growing two footers. */
#colophon.site-footer { display: none; }

/* ---- Desktop: split hero + 4-up bands ---- */
@media (min-width: 900px) {
  .taf-hide-mobile { display: block; }
  .taf-tile--desktop { display: block; }
  .taf-hide-desktop,
  .wp-block-buttons.taf-hide-desktop { display: none; }

  /* Split hero: copy 1.1fr / photo 0.9fr, left-aligned.
     Scoped to the FRONT PAGE only — recipe index/about reuse .taf-hero and must stay
     stacked/centered (unscoped, this checkerboarded the recipe index). */
  .home .taf-hero {
    display: grid;
    grid-template-columns: 1.1fr 0.9fr;
    gap: var(--space-48);
    align-items: center;
    max-width: var(--maxw-wide);
    padding: var(--space-48) var(--pad) var(--space-20);
    text-align: left;
  }
  .home .taf-hero .taf-hero-title { max-width: 16ch; margin: var(--space-12) 0 var(--space-16); }
  .home .taf-hero .taf-hero-copy > p.taf-muted { margin-left: 0; margin-right: 0; }
  .home .taf-hero .taf-search { margin: var(--space-24) 0 0; }
  .home .taf-hero .wp-block-buttons { justify-content: flex-start; margin-top: var(--space-16); }

  .taf-hero .taf-hero-photo {
    display: block;
    position: relative;
    aspect-ratio: var(--ar-4-44);
    overflow: hidden;
    border-radius: var(--radius);
    background: var(--bg-tinted);
  }
  .taf-hero-photo figure { height: 100%; margin: 0; }
  .taf-hero-photo img { width: 100%; height: 100%; object-fit: cover; }
  .taf-hero-caption {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: var(--space-40) var(--space-20) var(--space-16);
    background: linear-gradient(transparent, var(--scrim-search));
    display: flex;
    align-items: center;
    gap: var(--space-12);
  }
  .taf-hero-caption p { margin: 0; }
  .taf-hero-caption .taf-tag { position: static; }
  .taf-hero-caption-title {
    font-family: var(--display);
    font-size: var(--fs-20);
    letter-spacing: var(--ls-display);
    color: var(--color-on-scrim);
  }

  .has-rose-milk-background-color > .taf-section { padding: var(--space-48) var(--pad); }

  .taf-footer { padding: var(--space-40) var(--pad); }
}

/* ======================
   Recipe index (/recipe-index/). Markup: page post_content +
   patterns/category-photo-grid.php + inc/recipe-index.php.
   ====================== */

/* ---- Band 1: search hero ----
   Reuses the .taf-hero scaffolding + the wp:search pill bridge above; only the
   page-specific deltas live here. */
.taf-ri-hero { padding-top: var(--space-32); padding-bottom: 0; }
.taf-ri-hero .taf-hero-title { font-size: var(--fs-30); max-width: 16ch; margin: var(--space-8) auto var(--space-12); }

/* Chip row: one horizontally scrollable line on mobile, edge-to-edge within the
   728px hero column. Overrides the core buttons flex layout at equal-or-higher
   specificity. */
.wp-block-buttons.taf-chip-row {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-8);
  justify-content: flex-start;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: var(--space-12) calc(-1 * var(--pad)) 0;
  padding: 0 var(--pad) var(--space-4);
}
.wp-block-buttons.taf-chip-row > .wp-block-button { margin: 0; flex: none; }

/* The wrapper→inner bridge for a SELECTED wp:button chip lived here. Removed
   2026-08-05 with the recipe index's "All" chip (user) — that pattern was its
   only caller, and no other pattern or post_content carries
   `taf-chip is-active` on a wp:button (checked against the DB, 0 rows).
   Category archives still render a selected chip, but as a plain <a>, so they
   are served by `.taf-chip.is-active` above. Restore this bridge if a selected
   chip is ever authored as a block button again. */

/* ---- Band 2: category photo grid ----
   4:3 tiles (recipe tiles stay 4:5); count chip in the tile title line. */
.taf-tile--cat .ph { aspect-ratio: var(--ar-4-3); }
.taf-tile .t .taf-meta { font-family: var(--display); }
/* Linked wp:image inside .ph: keep the fill chain figure → a → img intact. */
.taf-tile .ph figure > a { display: block; height: 100%; }

/* ---- Band 4: most-loved query loop ----
   core/post-template renders <ul class="wp-block-post-template taf-grid-4"> of
   <li class="wp-block-post"> — neutralize the list chrome so taf-grid-4 lays it out. */
.taf-most-loved ul.wp-block-post-template { list-style: none; margin: 0; padding: 0; }
.taf-most-loved li.wp-block-post { margin: 0; }

/* Featured-image block as the tile photo: the figure carries .ph (4:5, tinted,
   overflow hidden); the isLink anchor must fill it. */
.taf-tile .wp-block-post-featured-image.ph { margin: 0; }
.taf-tile .wp-block-post-featured-image.ph > a { display: block; height: 100%; }
.taf-tile .wp-block-post-featured-image.ph img { width: 100%; height: 100%; object-fit: cover; }

/* Post-title block as the tile title: the inner isLink anchor inherits ink and
   drops its own underline (the tile-hover underline still paints through). */
.taf-tile h3.t { margin: var(--space-8) 0 0; }
.taf-tile .t a { color: inherit; text-decoration: none; }

/* ---- Pagination: real links styled as chips (comp) ---- */
.taf-most-loved .wp-block-query-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  margin-top: var(--space-24);
}
.taf-most-loved .wp-block-query-pagination-numbers {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-8);
  margin: 0;
}
.taf-most-loved .page-numbers,
.taf-most-loved .wp-block-query-pagination-previous,
.taf-most-loved .wp-block-query-pagination-next {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  min-height: 44px; /* 44px tap target */
  padding: var(--space-8) var(--space-14);
  border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--text-body);
  font-size: var(--fs-15);
  font-weight: var(--w-semibold);
  text-decoration: none;
  white-space: nowrap;
  margin: 0;
  transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.taf-most-loved a.page-numbers:hover,
.taf-most-loved .wp-block-query-pagination-previous:hover,
.taf-most-loved .wp-block-query-pagination-next:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.taf-most-loved .page-numbers.current {
  background: var(--chocolate);
  border-color: var(--chocolate);
  color: var(--milk);
}
.taf-most-loved .page-numbers.dots {
  border: none;
  background: transparent;
  padding: var(--space-8) var(--space-4);
  min-height: 0;
  color: var(--text-muted);
}

/* ---- Desktop (900px) ---- */
@media (min-width: 900px) {
  .taf-ri-hero { padding-top: var(--space-48); }
  .taf-ri-hero .taf-hero-title { font-size: var(--fs-42); }
  .taf-ri-hero .taf-search { max-width: 480px; }
  /* Chips fit on one line at 1280 — wrap centered, no scroll. */
  .wp-block-buttons.taf-chip-row {
    flex-wrap: wrap;
    justify-content: center;
    overflow-x: visible;
    margin-left: auto;
    margin-right: auto;
    padding-bottom: 0;
  }
}

/* ======================
   Recipe post — scaffold + rhythm. Kadence's single-post chrome (featured-image hero,
   boxed content card, content margins) is stripped and the column rhythm re-set.
   ====================== */

/* ---- 1. Page scaffold: no featured-image hero, no white content card, no extra
   content-area margins — Kadence renders all three on single posts ("thumbnail behind
   title" + content-bg), which pushed the crumbs 155–394px down the page. ---- */
body.single-post .post-thumbnail { display: none; }
/* The doubled class out-specifies Feast's content.min.css rule
   `.content-style-boxed .content-bg:not(.loop-entry)` (0,3,0), which re-adds -1rem
   side margins under 720px. */
body.single-post article.single-entry.single-entry {
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  margin: 0; /* Kadence content-bg gives the article -16px side margins */
}
body.single-post .content-area { margin-top: 0; margin-bottom: 0; }
/* The container carries the article padding so the desktop sidebar column aligns with
   the content top. */
body.single-post .content-container.site-container { padding: var(--space-16) var(--space-20) var(--space-40); }
body.single-post .entry-content-wrap { padding: 0; }
/* Desktop padding: both grid columns start 28px below the header. Lives after the base
   padding rule so the cascade keeps it at ≥1024. */
@media (min-width: 1024px) {
  body.single-post .content-container.site-container { padding: var(--space-24) var(--space-32) var(--space-40); }
}

/* ---- 2. Header rhythm: crumbs → H1 → rating row. Kadence gave .entry-header 17px +
   .entry-content 32px (rendered gap 32px) and heading line-height 1.2. ---- */
body.single-post .entry-header { margin: var(--space-12) 0 var(--space-8); }
body.single-post .entry-content { margin-top: 0; }
body.single-post h1.entry-title { line-height: var(--lh-display); }

/* ---- 3. Content-column rhythm: paragraphs/figures pace at 18px mobile / 22px
   desktop; H2 sections 26px top, 8px bottom (Kadence had 32px / 36–45px). ---- */
.taf-post-content p { margin: 0 0 var(--space-16); }
.taf-post-content figure.wp-block-image { margin: var(--space-16) 0; }
.taf-post-content h2 {
  margin: var(--space-24) 0 var(--space-8);
  line-height: var(--lh-display);
}
/* Legacy jump links (locked element) sat flush against the first paragraph —
   give them the same 16px the byline row uses. */
.taf-post-content .wprm-recipe-snippet { margin: 0 0 var(--space-16); }

@media (min-width: 900px) {
  body.single-post .entry-header { margin-top: var(--space-12); }
  /* Recipe H1 is 40px on desktop (not the 42px --fs-h1 token). */
  body.single-post h1.entry-title { font-size: var(--fs-42); }
  .taf-post-content p { margin-bottom: var(--space-20); }
  .taf-post-content figure.wp-block-image { margin: var(--space-20) 0; }
  /* The desktop teaser paragraph used to be sized up here. Its comment claimed 18px
     but the rule read var(--fs-17) — 17px, identical to the then-17px body, so it had
     been visually inert for its whole life (there is no --fs-18 token; an undefined
     var() in font-size silently inherits, which is likely how it drifted to --fs-17).
     Once --fs-body went 17px -> 18px the rule would have INVERTED it — a 17px opening
     paragraph under 18px body copy — so it is removed rather than left to regress.
     To actually size the teaser up, give it its own token above --fs-body; do not
     reintroduce a bare --fs-18. */
}

/* ======================
   Category archive. Markup: category.php + inc/category-archive.php. Reuses the shared
   primitives (.taf-tile, .taf-chip, .taf-stars, .taf-magnet, .taf-ad--incontent).
   ====================== */

/* ---- Page scaffold ----
   728px content column on mobile; at ≥900px the container opens to --maxw-wide
   for the 4-up grid while the intro keeps a readable measure. */
.taf-cat-archive {
  max-width: var(--maxw-wide);
  margin: 0 auto;
  padding: var(--space-20) var(--pad) var(--space-24);
}
.taf-cat-archive .taf-breadcrumbs { display: block; }

/* ---- Head: H1 + intro (native term description) ---- */
.taf-cat-head { margin: var(--space-12) 0 var(--space-16); }
.taf-cat-title { font-size: var(--fs-h1); margin: 0 0 var(--space-8); }
/* The intro is the native term description — it arrives wpautop'd with inline
   links. Keep the muted body tone; links inherit the brand color. */
.taf-cat-intro { color: var(--text-muted); max-width: 42ch; }
.taf-cat-intro p { margin: 0 0 var(--space-8); }
.taf-cat-intro p:last-child { margin-bottom: 0; }

/* ---- Subcategory chips: one scroll lane on mobile, edge-bled within the page padding;
   wraps at ≥900px. Same lane idiom as the index chip row, repeated because this row is a
   plain <nav>. ---- */
.taf-cat-chips {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-8);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: var(--space-16) calc(-1 * var(--pad)) 0;
  padding: 0 var(--pad) var(--space-4);
}
.taf-cat-chips .taf-chip { flex: none; }
/* the is-active "All …" chip is already styled by .taf-chip.is-active */

/* ---- Tile grid ----
   .taf-grid-4 does the layout (2-up mobile / 4-up ≥900px); only loop-context
   deltas live here. */
.taf-cat-grid { margin-top: var(--space-16); }
.taf-cat-grid + .taf-cat-grid { margin-top: var(--space-16); }
.taf-cat-grid .taf-tile .t { margin: var(--space-8) 0 0; }
/* get_the_post_thumbnail() prints a bare <img> inside .ph; guard the
   no-thumbnail case (tinted box). */
.taf-cat-grid .taf-tile .ph img { width: 100%; height: 100%; object-fit: cover; }

/* ---- Magnet band after row 2 ----
   The wrapper keeps it aligned to the content column, not the wide grid. */
.taf-cat-magnet { max-width: var(--maxw); margin: var(--space-24) auto 0; }
.taf-cat-magnet .taf-magnet { margin: var(--space-8) 0 0; }

/* ---- Pagination: real core .page-numbers links styled as chips ---- */
.taf-cat-archive .navigation.pagination {
  display: flex;
  justify-content: center;
  margin-top: var(--space-24);
}
.taf-cat-archive .nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
}
.taf-cat-archive .nav-links .page-numbers {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  min-height: 44px; /* 44px tap target */
  padding: var(--space-8) var(--space-14);
  border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--text-body);
  font-size: var(--fs-15);
  font-weight: var(--w-semibold);
  text-decoration: none;
  white-space: nowrap;
  margin: 0;
  transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.taf-cat-archive .nav-links a.page-numbers:hover,
.taf-cat-archive .nav-links a.page-numbers:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}
.taf-cat-archive .nav-links .page-numbers.current {
  background: var(--chocolate);
  border-color: var(--chocolate);
  color: var(--milk);
}
.taf-cat-archive .nav-links .page-numbers.dots {
  border: none;
  background: transparent;
  padding: var(--space-8) var(--space-4);
  min-height: 0;
  color: var(--text-muted);
}

/* ---- Desktop (900px) ---- */
@media (min-width: 900px) {
  .taf-cat-archive { padding-top: var(--space-32); }
  .taf-cat-head { margin-bottom: var(--space-24); }
  .taf-cat-title { font-size: var(--fs-h1); } /* token scales to 42px ≥900 */
  .taf-cat-intro { max-width: 62ch; }
  /* Chips fit on one line at 1280 — wrap in place, no scroll lane. */
  .taf-cat-chips {
    flex-wrap: wrap;
    overflow-x: visible;
    margin-left: 0;
    margin-right: 0;
    padding: 0;
  }
  .taf-cat-grid { margin-top: var(--space-24); }
  .taf-cat-grid + .taf-cat-grid { margin-top: var(--space-20); }
  .taf-cat-magnet { margin-top: var(--space-32); }
}

/* ======================
   Posts index — "Latest recipes" (/latest-posts/). Markup: home.php +
   inc/latest-posts.php.

   ⚠️ THIS IS A RE-SKIN, NOT A NEW LAYOUT. Everything geometric below reproduces
   what Kadence's stock blog archive already rendered here, measured in a real
   browser at 1440 and 390: a 1152px content box, a 40px gutter, and three
   column counts — 3-up ≥1025, 2-up 576–1024, 1-up <576.

   ⚠️ THOSE BREAKPOINTS ARE KADENCE'S (grid-sm-col-2 / grid-lg-col-3), NOT the
   theme's own 900px step. They are written out here on purpose: snapping them
   to 900 would move the column count at 900–1023 and at 576–899, which is a
   layout change on a template whose whole brief is "the grid does not move".

   The band is full-bleed and the grid is contained, so this section owns no
   wrapper max-width — .taf-latest inherits the full width of Kadence's
   #inner-wrap, exactly as the section.entry-hero it replaces did.
   ====================== */

/* ---- Page-header band ----
   Was a FIXED 200px of #FCE7F3 at every width from 390 to 1440, holding one
   centred breadcrumb and an <h1> that measured 0×0 (the posts page carries an
   empty title). Now --cream, content-driven height, and it holds a real H1.
   Padding is 40/40 rather than the deck's 38/42 — same sum, same measured band
   height, and every value is a token. See inc/latest-posts.php. */
.taf-latest-hero {
  background: var(--bg-tinted);
  padding: var(--space-24) var(--pad) var(--space-32);
  text-align: center;
}
.taf-latest-hero-inner { max-width: var(--maxw-wide); margin: 0 auto; }

/* Breadcrumb, in-band. Yoast prints its separator as a BARE TEXT NODE between
   the crumb spans — there is no element to select — so it simply inherits this
   rule's --text-muted (5.64:1 on --cream) while the links take --rose-deep
   (5.60:1). Kadence's band shipped #D01277 on #FCE7F3 = 4.43:1, an AA failure
   at 18px and the site's worst remaining contrast defect. These three
   declarations out-specify the sitewide .taf-breadcrumbs rules above. */
.taf-latest-hero .taf-breadcrumbs {
  font-size: var(--fs-15);
  line-height: var(--lh-15);
  color: var(--text-muted);
  margin: 0 0 var(--space-12);
}
.taf-latest-hero .taf-breadcrumbs a { color: var(--rose-deep); text-decoration: none; }
.taf-latest-hero .taf-breadcrumbs .breadcrumb_last { color: var(--text-muted); }

/* No line-height here: inc/enqueue.php prints an !important
   h1..h6{line-height:var(--lh-display)} counter against Feast's !important
   heading rule, and --lh-display (1.15) is exactly the deck's H1 leading. */
.taf-latest-h1 {
  font-family: var(--display);
  font-weight: var(--w-black);
  font-size: var(--fs-32);
  letter-spacing: var(--ls-display);
  color: var(--text-body);
  margin: 0;
  text-wrap: balance;
}

/* ---- The grid ----
   --maxw-wide (1200px) minus 2×--space-24 = the 1152px content box this
   template has always rendered; --space-40 is the gutter it already had. */
.taf-latest-grid {
  max-width: var(--maxw-wide);
  margin: 0 auto;
  padding: var(--space-28) var(--space-24) 0;
  list-style: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* 40px at EVERY width, not just desktop: Kadence's .grid-cols sets
     column-gap/row-gap: 2.5rem unconditionally, so the 1-up stack was already
     40px apart. The design comp drew 32px on its mobile pane while its own
     "preserved exactly" list said 40px — the list wins, because a gutter change
     is not one of the approved re-skin deltas. */
  gap: var(--space-40);
}
.taf-latest-item { display: flex; margin: 0; }

/* ---- The card ----
   Radius 4px → --radius (8px); Kadence's flat drop shadow → the theme's own
   two-layer brand-ink --shadow; body padding 32px → 24px. The body is a flex
   column so "Read more" pins to the foot of the tallest card in the row
   instead of leaving dead white space under a short excerpt. */
.taf-latest-card {
  background: var(--bg-surface);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  width: 100%;
  box-shadow: var(--shadow);
  transition: box-shadow var(--dur-15) var(--ease-out), transform var(--dur-15) var(--ease-out);
}

/* 3:2 from a square 768×768 source, centre-cropped — the ratio this template
   has always rendered (Kadence's class says ratio-2-3; the measured box is 3:2). */
.taf-latest-photo {
  display: block;
  aspect-ratio: var(--ar-3-2);
  background: var(--bg-tinted);
  overflow: hidden;
}
.taf-latest-photo img { width: 100%; height: 100%; object-fit: cover; }

.taf-latest-body {
  padding: var(--space-24);
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}

/* Categories are PLAIN TEXT, not an eyebrow. Kadence rendered them Mulish 700 /
   12.6px / UPPERCASE / +0.63px tracking / #D01277, pipe-joined — a kicker
   competing with the recipe name directly under it. Same slot, same links,
   quiet: --cocoa is 6.41:1 on white. */
.taf-latest-cats {
  font-size: var(--fs-14);
  line-height: var(--lh-15);
  font-weight: var(--w-regular);
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-muted);
  margin: 0 0 var(--space-8);
}
.taf-latest-cats a { color: inherit; text-decoration: none; }

/* 30px/600 in a 293px measure ran ~9.8 characters a line, so most titles broke
   over three. 24px/700 in a 309px measure runs ~12.5. */
.taf-latest-title {
  font-family: var(--display);
  font-weight: var(--w-bold);
  font-size: var(--fs-24);
  letter-spacing: var(--ls-display);
  margin: 0 0 var(--space-8);
  text-wrap: balance;
}
.taf-latest-title a { color: var(--text-body); text-decoration: none; }

/* Date only. The byline is GONE: every entry on this site has the same author,
   so an author name repeated forty times a page carried no information and cost
   a line in every card. */
.taf-latest-meta {
  font-size: var(--fs-14);
  line-height: var(--lh-15);
  color: var(--text-muted);
  margin: 0 0 var(--space-14);
}
.taf-latest-meta time { white-space: nowrap; }
.taf-latest-meta .dot { margin: 0 var(--space-4); opacity: var(--o-6); }
/* The "Updated <Month Year>" stamp. It fires only past a 365-day
   publish-to-modified gap, which ZERO of page 1's forty entries clear — it is a
   deep-archive signal by design, not a rule that never runs. */
.taf-latest-meta .upd { color: var(--text-body); font-weight: var(--w-bold); white-space: nowrap; }

.taf-latest-ex {
  font-size: var(--fs-16-5);
  line-height: var(--lh-16);
  color: var(--text-body);
  margin: 0 0 var(--space-18);
  text-wrap: pretty;
}

.taf-latest-morewrap { margin: auto 0 0; }
/* "READ MORE →" (uppercase, +0.72px tracking, body ink) → sentence case in
   --rose-link, 5.2:1 on white. Same Kadence arrow glyph. */
.taf-latest-more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  font-family: var(--sans);
  font-size: var(--fs-15);
  font-weight: var(--w-bold);
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-link);
  text-decoration: none;
}
.taf-latest-arrow { transition: transform var(--dur-15) var(--ease-out); }

/* ---- Interaction: hover is pointer-gated, focus is not ---- */
@media (hover: hover) and (pointer: fine) {
  .taf-latest-hero .taf-breadcrumbs a:hover { text-decoration: underline; }
  .taf-latest-cats a:hover { color: var(--text-link); text-decoration: underline; }
  .taf-latest-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-hover); }
  .taf-latest-card:hover .taf-latest-title a { text-decoration: underline; text-underline-offset: var(--space-4); }
  .taf-latest-more:hover { color: var(--rose-deep); text-decoration: underline; text-underline-offset: var(--space-4); }
  .taf-latest-more:hover .taf-latest-arrow { transform: translateX(3px); /* token-exempt: a 3px nudge is the motion itself, not spacing — it has no counterpart anywhere in the layout to share a token with */ }
}
.taf-latest a:focus-visible {
  outline: var(--focus-ring) solid var(--focus-ring-color);
  outline-offset: var(--space-2);
  border-radius: var(--radius-sm);
}
@media (prefers-reduced-motion: reduce) {
  .taf-latest-card:hover { transform: none; }
  .taf-latest-more:hover .taf-latest-arrow { transform: none; }
}

/* ---- Pagination: real core .page-numbers links styled as chips ----
   The theme's own chip, the fourth surface to use it (most-loved, category
   archive, search). The previous pager left-aligned 45×44.5 boxes with a solid
   #B34F36 rust current chip — a colour in no token file. Centred here, matching
   the other three. */
.taf-latest .navigation.pagination {
  display: flex;
  justify-content: center;
  margin-top: var(--space-32);
  padding: 0 var(--space-24) var(--space-8);
}
.taf-latest .nav-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
}
.taf-latest .nav-links .page-numbers {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  min-height: 44px; /* 44px tap target */
  min-width: 44px; /* 44px tap target */
  padding: var(--space-8) var(--space-14);
  border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--text-body);
  font-size: var(--fs-15);
  font-weight: var(--w-semibold);
  text-decoration: none;
  white-space: nowrap;
  margin: 0;
  transition: border-color var(--dur-15) var(--ease-out), color var(--dur-15) var(--ease-out);
}
.taf-latest .nav-links a.page-numbers:hover,
.taf-latest .nav-links a.page-numbers:focus-visible {
  border-color: var(--text-link);
  color: var(--text-link);
}
.taf-latest .nav-links .page-numbers.current {
  background: var(--chocolate);
  border-color: var(--chocolate);
  color: var(--milk);
}
.taf-latest .nav-links .page-numbers.dots {
  border: none;
  background: transparent;
  padding: var(--space-8) var(--space-4);
  min-height: 0;
  min-width: 0;
  color: var(--text-muted);
}

/* ---- 2-up ----
   576px is Kadence's OWN small breakpoint, read out of its content.min.css
   (@media screen and (min-width:576px){.grid-sm-col-2{…}}) rather than guessed
   from the design comp. */
@media (min-width: 576px) {
  .taf-latest-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- Desktop band + 3-up (Kadence's large breakpoint) ---- */
@media (min-width: 900px) {
  .taf-latest-hero { padding: var(--space-40) var(--space-24); }
  .taf-latest-h1 { font-size: var(--fs-42); }
}
/* 1025px, NOT 1024: Kadence's large grid rule is
   @media screen and (min-width:1025px){.grid-lg-col-3{…}}, read out of
   content.min.css. Rounding it to 1024 would put three columns on a viewport
   that used to show two — a one-pixel-wide layout change, but a real one. */
@media (min-width: 1025px) {
  .taf-latest-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    padding-top: var(--space-44);
  }
}

/* ======================
   About page (/about/) — PRE-JULY REMNANTS. Markup: patterns/about-story.php +
   patterns/about-magnet.php, both KEPT-BUT-UNREFERENCED (post 21 stopped
   rendering them). The live About page is the ABOUT v3 block further down.

   ⚠️ WHAT IS LEFT HERE IS DELIBERATE, AND SO IS WHAT IS GONE.
   Deleted 2026-08-04, after proving each class dead TWO ways: (a) no consumer
   in any theme .php/.js, (b) zero live posts — a DB scan of wp_posts, excluding
   trash/auto-draft, matched only TWO rows for every one of them, and both are
   `revision` rows parented to post 21, which WordPress never renders on the
   front end. Deleted on that proof:
     .taf-about-hero / .taf-about-portrait / .taf-about-title (old centred hero)
     .taf-about-kitchen / .taf-about-cards / .taf-about-card* (old link cards)
     .taf-about-favs (superseded by
        .taf-abt-favs — the favorites pattern was reclassed this build)
   plus their @media (min-width:900px) counterparts.

   KEPT, because a real pattern file still references them even though post 21
   does not render it — deleting these would silently break that pattern the
   day someone re-adds it: .taf-about-story / .taf-about-stats / .taf-about-stat*
   / .taf-press* (about-story.php), .taf-about-close / .taf-about-outro
   (about-magnet.php), and .taf-faq (patterns/faq.php — the RECIPE-post FAQ,
   live on ~600 posts, and nothing to do with this page).
   ====================== */

/* ---- Band 2: story column ---- */
.taf-about-story { padding-top: var(--space-8); }
.taf-about-story > p { margin: 0 0 var(--space-12); }

/* Credentials strip: 2×2 stat card, 4-up at 900px. */
.taf-about-stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-12);
  background: var(--bg-surface);
  border: var(--bw-1) solid var(--border-hairline);
  border-radius: var(--radius);
  padding: var(--space-20); margin: var(--space-20) 0;
  text-align: center;
}
.taf-about-stat p { margin: 0; }
.taf-about-stat__num { font-family: var(--display); font-size: var(--fs-24); line-height: var(--lh-115); font-weight: var(--w-bold); }

/* Press row */
.taf-press { margin: var(--space-4) 0 0; }
.taf-press__links { font-size: var(--fs-small); margin: var(--space-4) 0 0; }
.taf-press__links a { white-space: nowrap; }

/* ---- Band 6: FAQ (component styles in main.css; page spacing only) ---- */
.taf-faq h2 { margin-bottom: var(--space-4); }

/* ---- Band 7: magnet + outro ---- */
.taf-about-close { padding-top: 0; }
/* Mirrors the bread-band intro rule (margin:0 + 40ch cap) that otherwise catches this
   centered outro and left-flushes its capped box. Auto side margins re-center it. */
.entry-content .taf-section .taf-about-outro.taf-muted.taf-small,
.taf-about-outro { margin: var(--space-20) auto var(--space-8); }

/* ---- Desktop (900px) ---- */
@media (min-width: 900px) {
  .taf-about-stats { grid-template-columns: repeat(4, 1fr); }
}

/* =========================================================================
   ABOUT v3 (2026-08-04) — design/about-2026/design_handoff_about_page.
   post 21 pattern order: hero · philosophy · what-you'll-find · inspiration ·
   journey · facts · press · faq · favorites.

   THE LAYOUT RULE, and it governs every rule below: ONE centred reading column
   on --milk. Every section is a border-top hairline plus generous top/bottom
   padding. NO cards, NO boxes, NO shadows — the portrait's shadow is the one
   exception and the comp draws it that way. ONE accent band, "My inspiration"
   on --cream, and it is the only background change inside the reading column;
   that is what makes it read as a pause. Do not add a second tint, and do not
   put these sections back into boxes. The July 2026 build was boxes, and this
   file replaces it wholesale.

   NO SCALLOPED SEAMS anywhere. Retired sitewide; the bands
   here meet the page on a plain hairline. Never reintroduce.

   NO ICON FONT, NO SERIF. The handoff ships Phosphor.woff2 and
   YoungSerif-Regular.woff2; we ship NEITHER. Type is Poppins (--display) plus
   Mulish (--sans); icons are house inline stroke SVG (.taf-abouticon). House
   rule since the 2026-07-15 About rebuild.

   ── SNAP TABLE ────────────────────────────────────────────────────────────
   NO NEW TOKENS were minted for this page. The comp
   needs several values the scale lacks; each is SNAPPED to the nearest existing
   step, and each delta is a recorded decision rather than drift:

     comp token value delta
     reading column 740px → --maxw 728px -12px
       (--maxw was 700px when this table was written, a -40px snap; it widened to
        728px on 2026-08-06 for Raptive's 728x90 leaderboard, so the delta from
        the comp's 740 is now -12px. The snap decision is unchanged, only closer.)
     section padding 60/62 → --space-64 64px +2/+4px (>=900px)
                             → --space-40 40px mobile — the handoff
                                itself drops this tier to "about 40px"
     h2 32px → --fs-30 30px -2px
     H1 52px → --fs-hero 34/52px 0px at >=900px
       --fs-hero is 34px base and 52px at >=900px, so the comp's 52px is EXACT
       on desktop and a sane mobile step comes free. This is a DELIBERATE
       override of the suggested --fs-54: a flat 54px would be +2px on desktop
       and would also put 54px type on a 390px phone. Checked against what the
       hero actually used (--fs-30/--fs-42), then measured.
     heading→content 26px → --space-24 24px -2px
     number column 52px → --space-48 48px -4px
     hero column gap 56px → --space-48 48px -8px
     favorites gap 30/28 → --space-32 32px +2/+4px
     body leading 1.75 → --lh-165 1.65 -0.10
       The line-height scale STOPS at 1.65. The comp's 1.75 body and 1.7 journey
       passages both land there; the body SIZE (18px) is exact.

   Every color, space, gap, radius, border, font-size, weight and leading below
   is a token. The ratchet (scripts/token-inventory.py) sits at 17 magic values
   with ZERO slack in font-size, spacing, line-height, letter-spacing,
   border-radius and border-width, so one raw px in this block fails the
   pre-commit gate. width / max-width / grid tracks are geometry and are not
   ratchet-tracked; they are still written as tokens wherever a token expresses
   the intent.
   ======================================================================= */

/* House stroke-icon: outline glyphs (fill:none, unlike .taf-icon) sized to
   their box by font-size. */
.taf-abouticon { display: block; width: 1.25em; height: 1.25em; fill: none; stroke: currentColor; flex: none; }

/* ---- The section system -------------------------------------------------
   .taf-section already supplies the centred --maxw column and the page gutter;
   .taf-abt only re-times the vertical rhythm and draws the hairline. Doubled
   (0,2,0) so it beats `@media (min-width:900px){.taf-section{padding:
   var(--space-40) var(--pad)}}` further up this file regardless of print order
   — that rule is why the July bands measured 40px bottom padding. ---- */
.entry-content .taf-section.taf-abt,
.taf-section.taf-abt { padding-top: var(--space-32); padding-bottom: var(--space-32); }

/* The hairline IS the section separator. The first section after the hero
   (philosophy) has no .taf-abt--rule: the cream band's own bottom edge already
   draws that line, and a hairline there would double it.

   IT IS A ::before, NOT A border-top ON THE SECTION, and that is deliberate.
   .taf-section is border-box with `padding: … var(--pad)`, so a border on the
   box spans the full 728px while the TEXT inside spans 664px — the rule then
   overhangs the copy by 32px at each end. The comp hangs its rule on the inner
   reading column, flush with the text. MEASURED before the fix at 1280: rules
   ran 290→990 against text at 322→958. Insetting by --pad puts them flush. */
.entry-content .taf-section.taf-abt--rule,
.taf-section.taf-abt--rule { border-top: 0; position: relative; }
.entry-content .taf-abt--rule::before,
.taf-abt--rule::before {
  content: ""; position: absolute; top: 0; left: var(--pad); right: var(--pad);
  border-top: var(--bw-1) solid var(--border-hairline);
}

/* ⚠️ NO UNDERLINE ON THE SECTION HEADINGS (on the rendered
   page). `.entry-content .wp-block-group h2` near the end of this file gives
   EVERY h2 inside a wp:group a 2px --rose-tint border-bottom plus 8px of
   padding. That is the blog-post section-title style and it is right there —
   but it is wrong here: the comp's About headings are plain type, and the rule
   read as an underline under all seven section headers.

   The override needs (0,3,1). That rule is (0,2,1) AND prints later in the
   file, so a matching `.entry-content .taf-abt h2` (0,2,1) loses the tie on
   order. Qualifying the group class wins on specificity instead, which holds
   regardless of print order. */
.entry-content .wp-block-group.taf-abt h2,
.entry-content .wp-block-group.taf-abt-inspowrap h2 { border-bottom: 0; padding-bottom: 0; }

/* Kadence hangs a 32px block-gap margin on every child of a wp:group. Zero it
   here so the spacing declared below is the spacing that renders. This is not
   defensive boilerplate — the same leak has been re-fixed on this page three
   times already (see the block-gap neutralizer regions further down the file).
   Verify any change by MEASURED inter-element gap, never by the declaration. */
.entry-content .taf-abt > *,
.taf-abt > * { margin-top: 0; margin-bottom: 0; }

/* Every section heading, whether it came from wp:heading or from wp:html. */
.entry-content .taf-abt h2,
.taf-abt h2 {
  font-family: var(--display); font-weight: var(--w-semibold);
  font-size: var(--fs-30); line-height: var(--lh-115);
  letter-spacing: var(--ls-display); color: var(--text-body);
  margin: 0 0 var(--space-24);
}

/* ---- Section 1: hero — full-bleed cream band, copy + portrait ------------
   alignfull genuinely breaks out of page 21's .entry-content (measured on the
   rendered page: left:0 width:1280 at a 1280 viewport, against entry-content's
   left:40 width:1200). The earlier docblock claiming otherwise was wrong. ---- */
.entry-content .taf-abt-herowrap,
.taf-abt-herowrap { background: var(--bg-tinted); padding: var(--space-40) var(--pad); }
.taf-abt-hero { max-width: var(--maxw-wide); margin: 0 auto; display: grid; gap: var(--space-32); align-items: center; }

/* !important is load-bearing: inc/enqueue.php prints
   `h1..h6{line-height:var(--lh-display)!important}` as a systemic counter, so
   the comp's tighter 1.05 hero leading (snapped to --lh-108) cannot land
   without matching that !important. Removing it silently returns the H1 to
   1.15 and re-flows the whole band. */
.entry-content .taf-abt-hero__title,
.taf-abt-hero__title {
  font-family: var(--display); font-weight: var(--w-black);
  font-size: var(--fs-hero); line-height: var(--lh-108) !important;
  letter-spacing: var(--ls-display); color: var(--text-body);
  margin: 0 0 var(--space-18);
}
.taf-abt-hero__name { color: var(--accent); }
.entry-content .taf-abt-hero__lede,
.taf-abt-hero__lede {
  font-size: var(--fs-19); line-height: var(--lh-165);
  color: var(--text-body); max-width: 52ch; margin: 0;
}
.entry-content .taf-abt-hero__ctarow,
.taf-abt-hero__ctarow { margin: var(--space-24) 0 0; }
.taf-abt-hero__cta { display: inline-flex; align-items: center; gap: var(--space-8); }
/* Guarantee the primary CTA's fill/ink inside entry-content, where a Kadence
   base `a` color could otherwise outrank .taf-btn--primary (0,1,0). */
.entry-content a.taf-abt-hero__cta.taf-btn--primary { color: var(--color-on-accent); background: var(--accent); }
.entry-content a.taf-abt-hero__cta.taf-btn--primary:hover,
.entry-content a.taf-abt-hero__cta.taf-btn--primary:focus-visible { color: var(--color-on-accent); background: var(--accent-hover); }

/* Portrait (LCP). order:-1 puts it above the copy on phones — the July build's
   decision, kept: it is the LCP element and hoisting it is worth more than the
   H1-first reading order at that width. aspect-ratio reserves the box so the
   eager image causes ZERO layout shift. */
.taf-abt-hero__media { order: -1; width: 100%; max-width: 500px; margin: 0 auto; }
.taf-abt-hero__portrait { margin: 0; width: 100%; }
.taf-abt-hero__portrait img {
  width: 100%; height: auto; aspect-ratio: var(--ar-4-5); object-fit: cover;
  border-radius: var(--radius); box-shadow: var(--shadow-hover); display: block;
}

/* ---- Section 2: my food philosophy -------------------------------------- */
.entry-content .taf-abt-phil p,
.taf-abt-phil p { font-size: var(--fs-18); line-height: var(--lh-165); color: var(--text-body); }
.entry-content .taf-abt-phil > p + p,
.taf-abt-phil > p + p { margin-top: var(--space-16); }

/* ---- Section 3: what you'll find — numbered 01–04 + camera note ----------
   A real <ol>; its own markers are suppressed because the visible 01–04 are
   styled spans (aria-hidden, so AT does not read "zero one" before each line).
   52px number column snaps to --space-48. ---- */
.entry-content .taf-abt-find__list,
.taf-abt-find__list { list-style: none; margin: 0; padding: 0; }
.taf-abt-find__row {
  display: grid; grid-template-columns: var(--space-48) 1fr; gap: var(--space-20);
  padding: var(--space-20) 0; border-bottom: var(--bw-1) solid var(--border-hairline);
  list-style: none;
}
.entry-content .taf-abt-find__num,
.taf-abt-find__num {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-14);
  color: var(--rose-deep); /* not --accent: #E11584 on --paper is 4.39:1, 0.11 short of AA. --rose-deep is 6.19:1. Same precedent as the Slice-1 gate fix. */
  letter-spacing: var(--ls-08em); padding-top: var(--space-4);
}
.entry-content .taf-abt-find__text,
.taf-abt-find__text { font-size: var(--fs-18); line-height: var(--lh-165); color: var(--text-body); margin: 0; }

/* The camera note: a --cream inset. This is a TINTED NOTE, not a card — no
   border, no shadow. It is the one boxed shape the layout system allows. */
.taf-abt-find__note {
  display: flex; align-items: flex-start; gap: var(--space-16);
  background: var(--bg-tinted); border-radius: var(--radius);
  padding: var(--space-24); margin-top: var(--space-24);
}
.taf-abt-find__note-icon { color: var(--accent); font-size: var(--fs-18); margin-top: var(--space-2); }
.entry-content .taf-abt-find__note-text,
.taf-abt-find__note-text { font-size: var(--fs-16); line-height: var(--lh-165); color: var(--text-muted); margin: 0; }

/* ---- Section 4: my inspiration — THE accent band ------------------------
   The page's single background change. Full-bleed cream, hairline top edge,
   nothing else. No scallop, no card, no shadow. ---- */
.entry-content .taf-abt-inspowrap,
.taf-abt-inspowrap {
  background: var(--bg-tinted); padding: var(--space-32) 0;
  border-top: var(--bw-1) solid var(--border-hairline);
}
/* ⚠️ THE GUTTER LIVES ON THE INNER BOX, NOT THE WRAPPER — this is an alignment
   bug that was caught on the rendered page, so do not "simplify" it back.
   .taf-section is `max-width:var(--maxw); padding: … var(--pad)` and it is
   border-box, so its 728px INCLUDES the 32px gutter and its text starts 32px
   inside the column. Putting --pad on this band's full-bleed WRAPPER instead
   gave the inner box a full 728px of text, which started 32px further out than
   every other section: MEASURED at 1280, the quote's left edge was 290 while
   all seven sibling sections' text was at 322. Matching .taf-section's own
   geometry here puts it back on 322. */
.taf-abt-inspo {
  max-width: var(--maxw); margin: 0 auto;
  padding-left: var(--pad); padding-right: var(--pad);
}
/* ⚠️ NO QUOTE GLYPH — the comp's ph-quotes mark was cut in review
   ("remove the weird glyph"). The rose typographic quote marks below do
   that job instead. Do not restore .taf-abt-inspo__mark. */
.entry-content .taf-abt-inspo__quote,
.taf-abt-inspo__quote {
  font-family: var(--display); font-weight: var(--w-semibold);
  font-size: var(--fs-24); line-height: var(--lh-15);
  letter-spacing: var(--ls-display); color: var(--text-body);
  margin: var(--space-24) 0 0; padding: 0; border: 0;
  text-wrap: pretty;
}
/* The “ ” take the QUOTE'S OWN colour (reversing the rose
   they were given earlier the same day: "make the quotes in the quote same as
   the rest of the content"). `inherit`, not a repeat of --text-body, so they
   can never drift from the blockquote they punctuate.
   The span stays: it is REAL TEXT and NOT aria-hidden, so the quotation still
   reads as quoted to assistive tech, and keeping the hook means re-colouring
   them later is one line rather than a markup change. */
.entry-content .taf-abt-inspo__mk,
.taf-abt-inspo__mk { color: inherit; }

/* ---- Section 5: my journey — three labelled passages -------------------- */
.taf-abt-journey__item + .taf-abt-journey__item {
  border-top: var(--bw-1) solid var(--border-hairline); margin-top: var(--space-24); padding-top: var(--space-24);
}
.entry-content .taf-abt-journey__label,
.taf-abt-journey__label {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-13);
  letter-spacing: var(--ls-14em); text-transform: uppercase; color: var(--text-link);
  margin: 0;
}
.entry-content .taf-abt-journey__body,
.taf-abt-journey__body { font-size: var(--fs-18); line-height: var(--lh-165); color: var(--text-body); margin: var(--space-10) 0 0; }

/* ---- Section 6: quick facts — five icon rows ---------------------------- */
.entry-content .taf-abt-facts__list,
.taf-abt-facts__list { list-style: none; margin: 0; padding: 0; }
.taf-abt-facts__row {
  display: flex; align-items: flex-start; gap: var(--space-16);
  padding: var(--space-16) 0; border-bottom: var(--bw-1) solid var(--border-hairline);
  list-style: none;
}
/* No rule under the LAST fact. The comp draws one, but on
   the real page it stacks against the next section's own top hairline with
   only the section padding between — two parallel rules reading as a mistake.
   Deliberate departure from the comp; do not "restore" it. */
.taf-abt-facts__row:last-child { border-bottom: 0; }
.taf-abt-facts__icon { color: var(--accent); font-size: var(--fs-16); margin-top: var(--space-4); }
.entry-content .taf-abt-facts__text,
.taf-abt-facts__text { font-size: var(--fs-17); line-height: var(--lh-16); color: var(--text-body); margin: 0; }

/* ---- Section 7: as seen on — names in type, no logos --------------------
   ONE LINE at desktop. All six names plus five middots have
   to fit the 664px reading column (636px before the 2026-08-06 widening, which
   only ever adds room here), which the comp's 18px/14px-gap setting does
   not — it wrapped "Cherry Bombe" onto a second line. The gap is tightened and
   the names step down a size; both are measured to fit, not guessed.
   flex-wrap STAYS `wrap`: at 390px six names cannot share a line and must be
   allowed to fall, so this is a desktop fit, not a nowrap lock. ---- */
.entry-content .taf-abt-press__row,
.taf-abt-press__row {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-8); margin: 0;
}
/* One rule for BOTH the linked names and the unlinked one (Cherry Bombe has no
   verified URL — see patterns/about-press.php), so the row reads evenly. */
.entry-content .taf-abt-press__name,
.taf-abt-press__name {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-16);
  color: var(--text-body); text-decoration: none; white-space: nowrap;
}
.entry-content a.taf-abt-press__name:hover,
.entry-content a.taf-abt-press__name:focus-visible { color: var(--accent-hover); text-decoration: underline; }
.taf-abt-press__dot { color: var(--accent); font-size: var(--fs-15); }

/* ---- Section 8: questions I get a lot — native <details>, NO JS ---------
   Scoped to .taf-abt-faq and NOT to .taf-faq on purpose: .taf-faq is the
   recipe-post FAQ component (patterns/faq.php) and is live on ~600 posts.
   Restyling it from here would be a sitewide change disguised as a page build.

   The accordion has no JavaScript and must keep it that way: native <details>
   is what lets in-page browser search reveal closed answers and what makes the
   page print correctly. The only motion is the + rotating 45° into an ×. ---- */
.taf-abt-faq__list { border-top: var(--bw-1) solid var(--border-hairline); }
.taf-abt-faq__item { border-bottom: var(--bw-1) solid var(--border-hairline); }
.taf-abt-faq__q {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  justify-content: space-between; gap: var(--space-20); padding: var(--space-20) 0;
}
.taf-abt-faq__q::-webkit-details-marker { display: none; }
.entry-content .taf-abt-faq__qt,
.taf-abt-faq__qt {
  font-family: var(--display); font-weight: var(--w-semibold);
  font-size: var(--fs-19); line-height: var(--lh-135); color: var(--text-body);
}
.taf-abt-faq__i {
  color: var(--accent); font-size: var(--fs-16);
  transition: transform var(--dur-2) var(--ease);
}
.taf-abt-faq__item[open] .taf-abt-faq__i { transform: rotate(45deg); }
.entry-content .taf-abt-faq__a,
.taf-abt-faq__a {
  font-size: var(--fs-16); line-height: var(--lh-165); color: var(--text-muted);
  margin: 0; padding: 0 var(--space-40) var(--space-24) 0;
}
.taf-abt-faq__q:focus-visible { outline: var(--focus-ring) solid var(--focus-ring-color); outline-offset: var(--space-2); }

/* ---- Section 9: the author's favorite recipes — STANDARD tile row + CTA -----
   [taf_recipe_tiles variant="tile"] prints the recipe index's .taf-grid-4 /
   .taf-tile markup, and this band uses that component's STANDARD behaviour:
   2-up on phones, 4-up at >=900px, with the component's own gaps.

   ⚠️ THIS IS A DELIBERATE DEPARTURE FROM THE COMP. The v3
   comp draws these four as a 2×2, and an earlier pass of this build forced
   that shape by re-tracking the grid at >=900px. Review asked for the
   standard tile row instead, so the override is GONE rather than merely
   loosened — .taf-grid-4 is left to do exactly what it does everywhere else
   on the site. Do not reinstate a 2×2 here from the comp alone.

   ⚠️ THIS IS THE ONE SECTION THAT LEAVES THE READING COLUMN, AND THAT IS THE
   PRICE OF "the standard tile row". The band is .taf-section--wide, so its
   heading, rule, tiles and CTA all share the WIDE left edge (72px at 1280),
   while sections 2–8 share the reading column's (322px). The house pattern
   for a tile row everywhere else on the site — home, recipe index — is
   exactly this: .taf-section--wide holding the section head and .taf-grid-4
   together, so the row is internally consistent even though it does not line
   up with the reading column above it.

   TWO ALTERNATIVES WERE BUILT AND REJECTED, so neither gets re-tried blind:
   1. Whole band in the 728px reading column. Four tiles then shared the 664px
      text measure at ~144px each and the titles hit their 2-line clamp and
      ELLIPSED — "No-Knead Cranberry…", "Chocolate Fudge With…". Seen on the
      rendered page.
   2. Wide grid, but heading/rule/CTA pinned back to the reading measure so
      they kept the 322px edge. Measured correct and looked WRONG: the heading
      sat indented from the tiles it labels, which start 250px to its left.

   TO PUT IT BACK IN THE READING COLUMN (comp fidelity over tile size): drop
   `taf-section--wide` from patterns/about-favorites.php and add
   `grid-template-columns: 1fr 1fr` for .taf-abt-favs .taf-grid-4 at >=900px.
   That restores the comp's 2×2 at ~302px and a single 322px left edge for the
   whole page. It is a two-line change, deliberately left undone: review
   asked for the standard row. ---- */
.taf-abt-favs .taf-tile .ph { aspect-ratio: var(--ar-4-3); border-radius: var(--radius); overflow: hidden; }
/* The tile title is an <h3>, so inc/enqueue.php's systemic
   `h1..h6{line-height:var(--lh-display)!important}` counter claims its leading
   and pins it to 1.15. MEASURED: without the !important below it computed
   19.55px on 17px type instead of the comp's 22.1px, tightening every wrapped
   two-line recipe title. The !important is the only way to win an !important. */
.entry-content .taf-abt-favs .taf-tile .t,
.taf-abt-favs .taf-tile .t {
  font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-17);
  line-height: var(--lh-13) !important; letter-spacing: normal; color: var(--text-body);
  margin: var(--space-12) 0 0;
}
.taf-abt-favs .taf-tile:hover .t,
.taf-abt-favs .taf-tile:focus-visible .t { color: var(--accent-hover); }
/* The CTA is the comp's OUTLINED rose button, CENTRED under the tile row
   (the comp left-aligns it; this is a deliberate override). */
.entry-content .taf-abt-favs__ctarow,
.taf-abt-favs__ctarow { display: flex; justify-content: center; margin: var(--space-32) 0 0; }
/* FILLED PINK AT REST ("should be colored pink always even
   when not selected"). It was an OUTLINE button that only filled on hover —
   which means on a phone, where there is no hover, it never filled at all and
   the page's one closing call to action read as the quietest thing on it.
   Hover/focus now deepens to --accent-hover rather than being the moment the
   button appears. */
.entry-content a.taf-abt-favs__cta,
a.taf-abt-favs__cta {
  display: inline-flex; align-items: center; gap: var(--space-8);
  background: var(--accent); color: var(--color-on-accent);
  border: var(--bw-15) solid var(--accent); border-radius: var(--radius-sm);
}
.entry-content a.taf-abt-favs__cta:hover,
.entry-content a.taf-abt-favs__cta:focus-visible {
  background: var(--accent-hover); border-color: var(--accent-hover); color: var(--color-on-accent);
}

/* ---- Desktop (>=900px) — the tier where the comp's numbers are literal ---- */
@media (min-width: 900px) {
  /* ⚠️ SECTION PADDING IS 48, NOT THE COMP'S 60/62 ("reduce padding"). The snap table below still records --space-64 as the
     comp's own value; this is a deliberate step further down from it, because
     64 top + 64 bottom put 128px between every pair of sections and the page
     read as too airy on the real screen. Mobile drops to --space-32 to match.
     This rule beats `.taf-section{padding:var(--space-40) var(--pad)}` in the
     900px block further up on specificity (0,2,0 vs 0,1,0), not on order. */
  .entry-content .taf-section.taf-abt,
  .taf-section.taf-abt { padding-top: var(--space-48); padding-bottom: var(--space-48); }

  .entry-content .taf-abt-herowrap,
  .taf-abt-herowrap { padding: var(--space-48) var(--pad); }
  .taf-abt-hero { grid-template-columns: 1.15fr 1fr; gap: var(--space-48); }
  .taf-abt-hero__media { order: 0; max-width: none; }

  /* Vertical only — the horizontal gutter belongs to .taf-abt-inspo (see the
     alignment note above), so re-adding --pad here would push the quote 32px
     out of line with every other section all over again. */
  .entry-content .taf-abt-inspowrap,
  /* Matches .taf-abt's reduced 48 so the accent band keeps the same rhythm as
     the sections around it. */
  .taf-abt-inspowrap { padding: var(--space-48) 0; }

  /* (No favorites grid override here on purpose — .taf-grid-4's own 4-up step
     is the standard tile row review asked for. See the note above.) */
}

/* ======================
   404 + search-results. Markup: inc/search-404.php over Kadence's NATIVE templates;
   requires the Feast "Modern 404" / "Modern Search Results" toggles OFF (see that file).
   Everything is scoped under body.error404 / body.search-results, so while Feast owns
   those templates none of these selectors match.
   ====================== */

/* ---- Shared: suggestion blocks (404 + empty search) ---- */
.taf-suggest-heading { margin: var(--space-32) 0 var(--space-12); }
.taf-suggest-grid { margin: 0 0 var(--space-8); }
.taf-suggest-grid .taf-tile p { margin: 0; }
.taf-suggest-grid .taf-tile p.t { margin-top: var(--space-8); }
/* the_post_thumbnail() puts the img straight inside .ph — cover-fill it (the
   figure/anchor variants are covered above). */
.taf-suggest-grid .taf-tile .ph > img { width: 100%; height: 100%; object-fit: cover; }

/* A real <ul> since 2026-07-17 (was role=list divs) — flatten the list chrome. */
.taf-chips { display: flex; flex-wrap: wrap; gap: var(--space-8); margin: 0 0 var(--space-24); padding: 0; list-style: none; }
.taf-chips li { margin: 0; }

/* ---- 404 (body.error404, renderer: taf2026_render_404_content) ---- */
.taf-404 { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-20) 0 var(--space-8); }
.taf-404__title {
  font-size: var(--fs-30); text-align: center;
  max-width: 16ch; margin: var(--space-8) auto 0;
}
.taf-404__lead {
  text-align: center; color: var(--text-muted);
  max-width: 46ch; margin: var(--space-12) auto 0;
}
.taf-404__search { max-width: 480px; margin: var(--space-20) auto var(--space-32); }
/* The pill container is 52px but the input's own box was only ~26px tall —
   grow the input so a tap anywhere near the text lands in the field. */
.taf-404__search input { height: var(--space-44); } /* 44px tap target */
/* Kadence's .content-wrap/.entry-content-wrap chrome around the 404 body: keep
   the canvas plain milk like every other band. */
body.error404 .content-wrap .taf-404,
body.error404 section.error.taf-404 { background: transparent; border: none; box-shadow: none; }

/* ---- Search results (Kadence native loop) ----
   Kadence renders <ul id="archive-container"> of li.entry-list-item > article.loop-entry.
   Re-shape it into the standard tile grid: 2-up mobile, 4-up ≥900px. */
body.search-results ul#archive-container {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-16) var(--space-16);
  list-style: none; margin: var(--space-24) 0 0; padding: 0;
}
body.search-results li.entry-list-item { margin: 0; }
body.search-results article.loop-entry {
  background: transparent; border: none; box-shadow: none;
  overflow: visible; height: auto;
}

/* A search result IS a `.taf-tile` (rendered whole by taf2026_search_tile), so it needs
   no photo/title/spacing/hover rules — the shared component owns them. Only the grid is
   search-specific. Do NOT add tile overrides here; see inc/search-404.php. */

/* Pagination: real links styled as chips (same treatment as the most-loved
   pagination, applied to Kadence's .nav-links markup). */
body.search-results .pagination .nav-links,
body.search-results nav.pagination .page-numbers { margin: 0; }
body.search-results nav.pagination { margin: var(--space-24) 0 0; }
body.search-results nav.pagination .nav-links {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: center; gap: var(--space-8);
}
body.search-results nav.pagination .page-numbers {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex; align-items: center; gap: var(--space-6);
  min-height: 44px; padding: var(--space-8) var(--space-14); /* 44px tap target */
  border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-pill);
  background: var(--bg-surface); color: var(--text-body);
  font-size: var(--fs-15); font-weight: var(--w-semibold); text-decoration: none;
  white-space: nowrap;
}
body.search-results nav.pagination a.page-numbers:hover,
body.search-results nav.pagination a.page-numbers:focus-visible {
  border-color: var(--accent); color: var(--accent);
}
body.search-results nav.pagination .page-numbers.current {
  background: var(--chocolate); border-color: var(--chocolate); color: var(--milk);
}
body.search-results nav.pagination .page-numbers.dots {
  border: none; background: transparent; padding: var(--space-8) var(--space-4); min-height: 0;
  color: var(--text-muted);
}

/* Pagination chips, all three surfaces (most-loved, category archive, search):
   single-digit links squared off to the 44px floor, numeral centered. ADDITIVE —
   the token-frozen chip styles above stay untouched. */
.taf-most-loved .page-numbers:not(.dots),
.taf-cat-archive .nav-links .page-numbers:not(.dots),
body.search-results nav.pagination .page-numbers:not(.dots) {
  min-width: var(--space-44); /* 44px tap target */
  justify-content: center;
}

/* Empty state (taf2026_search_empty_state) + Kadence's native "nothing matched"
   notice above it. */
/* (search-no-results added 2026-07-17: section.error only renders on the no-results
   body class, so the search-results-only selectors never matched it.) */
body.search-results section.error .page-content,
body.search-no-results section.error .page-content { background: transparent; padding: 0; }
body.search-results section.error form.search-form,
body.search-no-results section.error form.search-form { max-width: 480px; margin-top: var(--space-16); }
.taf-search-empty { margin-top: var(--space-24); }

/* WP default search form (Kadence 404/empty-state fallbacks and the native
   empty-search notice): style as the design-system pill.
   ⚠️ body.search-results and body.search-no-results are MUTUALLY EXCLUSIVE WP body
   classes — the empty-state retry form only ever renders under search-no-results,
   so every selector here must carry BOTH or the no-results form ships unstyled
   (it did, until the 2026-07-17 polish wave). */
body.error404 form.search-form,
body.search-results form.search-form,
body.search-no-results form.search-form {
  display: flex; align-items: center; gap: var(--space-8);
  background: var(--bg-surface);
  border: var(--bw-15) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  padding: var(--space-4) var(--space-8) var(--space-4) var(--space-16);
}
body.error404 form.search-form label,
body.search-results form.search-form label,
body.search-no-results form.search-form label { flex: 1; min-width: 0; }
body.error404 form.search-form .search-field,
body.search-results form.search-form .search-field,
body.search-no-results form.search-form .search-field {
  width: 100%; border: none; outline: none; background: transparent;
  font-family: var(--sans); font-size: var(--fs-16); color: var(--text-body);
  padding: var(--space-8) 0; min-width: 0;
}
body.error404 form.search-form .search-field::placeholder,
body.search-results form.search-form .search-field::placeholder,
body.search-no-results form.search-form .search-field::placeholder { color: var(--text-muted); opacity: var(--o-1); }
body.error404 form.search-form .search-submit,
body.search-results form.search-form .search-submit,
body.search-no-results form.search-form .search-submit {
  /* position/width override Kadence's fixed ~50px icon-button geometry, which
     clipped the label to "Sear" under the magnifier (verified 390, v0.30.1) */
  position: static; width: auto; min-width: 80px;
  height: 40px; padding: 0 var(--space-16); border: none;
  border-radius: var(--radius-sm);
  background: var(--accent); color: var(--color-on-accent);
  font-weight: var(--w-bold); font-size: var(--fs-15); cursor: pointer;
}
body.error404 form.search-form .kadence-search-icon-wrap,
body.search-results form.search-form .kadence-search-icon-wrap,
body.search-no-results form.search-form .kadence-search-icon-wrap { display: none; /* it overlaid the now-labeled submit */ }
body.error404 form.search-form .search-submit:hover,
body.search-results form.search-form .search-submit:hover,
body.search-no-results form.search-form .search-submit:hover { background: var(--accent-hover); }
/* Container-level focus ring: the field suppresses its own outline above, so the
   pill needs the search-scoped rose ring (same rationale as .taf-searchbar). */
body.search-results .search-form:focus-within,
body.search-no-results .search-form:focus-within {
  outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; /* search = rose, see .taf-search rule */
}

/* ---- Desktop (900px) ---- */
@media (min-width: 900px) {
  .taf-404 { padding-top: var(--space-32); }
  .taf-404__title { font-size: var(--fs-42); }
  body.search-results ul#archive-container { grid-template-columns: repeat(4, 1fr); gap: var(--space-20); }
}

/* ======================
   Homepage typography overrides. The heading counter lives in inc/enqueue.php's inline
   style (line-height !important); these carry the specificity + !important to beat it.
   ====================== */

/* The live desktop nav is Kadence's .main-navigation a (.taf-nav is not in the
   DOM). Equal specificity, later source order. */
.main-navigation a,
.header-navigation a { font-family: var(--display); font-size: var(--fs-15); font-weight: var(--w-semibold); color: var(--chocolate); }
.main-navigation a:hover,
.header-navigation a:hover { color: var(--accent); }

/* Kit embed: ConvertKit's inline formkit CSS (a .formkit-form[data-uid] rule at 0,3,0
   plus an element-level inline style) wins over the .taf-btn spec, so !important is
   required. */
.formkit-form .formkit-submit { font-size: var(--fs-16) !important; font-weight: var(--w-bold) !important; }
.formkit-form .formkit-input { font-size: var(--fs-16) !important; color: var(--chocolate) !important; }

/* The "Browse all bread recipes" wrapper carries taf-btn--secondary WITHOUT taf-btn, so
   the bridge (which keys on .taf-btn) never set its type. */
.wp-block-button.taf-btn--secondary .wp-block-button__link { font-size: var(--fs-16); font-weight: var(--w-bold); }

/* Tile titles take no display tracking (the link inherited -0.01em from
   .taf-tile .t). */
.taf-tile .t a { letter-spacing: normal; }

/* Footer legal links match the 12px legal line (they inherited the 15px column-
   link size). */
.taf-footer .legal a { font-size: var(--fs-12); }

/* Mobile hero H1. Scoped to .home so the recipe-index hero keeps the token size. */
.home .taf-hero-title { font-size: var(--fs-36); }

@media (min-width: 900px) {
  /* Desktop hero H1: line-height needs !important to beat the heading counter;
     .home .taf-hero .taf-hero-title (0,3,0) out-specifies bare h1. */
  .home .taf-hero .taf-hero-title { font-size: var(--fs-54); line-height: var(--lh-108) !important; }

  /* The hero caption title is a <p>, so the h1–h6 counter never touches it and it
     kept the 1.65 body line-height. */
  .taf-hero-caption-title { line-height: var(--lh-115); }

  /* Magnet heading is 20px on desktop; mobile keeps the 22px base. */
  .taf-magnet h3 { font-size: var(--fs-20); }
}

/* ======================
   Homepage + systemic cascade fixes. Selectors are pitched to out-specify the actual
   winning rule (Kadence generated CSS, ConvertKit inline CSS, the enqueue.php counter),
   so they hold regardless of print order.
   ====================== */

/* ---- Bands render FLUSH: each band's own padding separates it, not a blanket band
   margin. Kadence's `.single-content .wp-block-group{margin-bottom:32px}` (0,2,0) is the
   rule being beaten; 0,3,0 wins order-independently. ---- */
.entry-content.single-content > .wp-block-group { margin-bottom: 0; }

/* Hero → next-band seam: on mobile .taf-hero-photo is display:none, so .taf-hero-copy is
   the last in-flow child and its 32px block-gap margin collapses THROUGH the
   padding-bottom:0 hero. Harmless on desktop where the hero is a grid. */
.home .taf-hero .taf-hero-copy { margin-bottom: 0; }

/* Restore the magnet's own margins — Kadence's `.wp-block-group:last-child
   {margin-bottom:0}` (0,2,1) zeroed them. They collapse through the flush band to make
   its seams. */
.entry-content .taf-section > .taf-magnet { margin: var(--space-24) 0 var(--space-8); }

/* Mobile-only 28px page-bg gap above the full-bleed cream band (desktop bands are
   flush). 0,3,0 sets the band margin-top that Kadence pins to 0. */
@media (max-width: 899.98px) {
  .entry-content .wp-block-group.has-rose-milk-background-color { margin-top: var(--space-24); }
}

/* ---- Section-head → grid gap: `.taf-section-head{margin-bottom:16px}` (0,1,0) was
   overridden to 32px by the block-gap rule (0,2,0). Doubled class = 0,3,0. ---- */
.entry-content .taf-section-head.taf-section-head { margin-bottom: var(--space-16); }

/* ---- Top-level nav items: chocolate at rest, rose on hover. The winning rule is
   Kadence's inline `.main-navigation .primary-menu-container > ul > li.menu-item > a`
   (0,3,3); doubling `.menu-item` → 0,4,3 beats it. The `> ul > li` chain scopes this to
   top-level items, so submenu links are untouched. ---- */
.main-navigation .primary-menu-container > ul > li.menu-item.menu-item > a { color: var(--chocolate); }
.main-navigation .primary-menu-container > ul > li.menu-item.menu-item > a:hover,
.main-navigation .primary-menu-container > ul > li.menu-item.menu-item > a:focus-visible { color: var(--accent); }

/* ---- Footer labels take the BODY line-height. Beating the enqueue.php counter
   (`h1..h6{line-height:var(--lh-display)!important}`) needs specificity AND !important. ---- */
.taf-footer h2 { line-height: var(--lh-165) !important; }

/* ---- Hero H1 max-width: the `ch` cap resolves ~46px too wide on mobile (13ch = 274px
   vs 228px, collapsing 4 lines → 3). Pin the explicit box widths. ---- */
.home .taf-hero .taf-hero-title { max-width: 228px; }
@media (min-width: 900px) {
  .home .taf-hero .taf-hero-title { max-width: 432px; }
}

/* ---- Bread band inset: the full-bleed cream .alignfull wrapper was double-padded by
   Kadence `.wp-block-group.has-background{padding:20px 30px}` (0,3,0) ON TOP of the inner
   .taf-section's padding. Neutralize the wrapper (0,4,0); the inner section supplies the
   inset. The background stays full-bleed. ---- */
.entry-content .wp-block-group.alignfull.has-rose-milk-background-color { padding: 0; }

/* ---- Magnet email form INLINE on one row. ConvertKit's inline
   `.formkit-form[data-uid] .formkit-fields{flex-wrap:wrap}` (0,3,0) forced the field to
   100% width, stacking the button onto its own line. !important + the grow/basis reset
   put them back on one row. ---- */
.taf-magnet .formkit-form .formkit-fields { flex-wrap: nowrap !important; gap: var(--space-8); }
.taf-magnet .formkit-form .formkit-field  { flex: 1 1 auto !important; min-width: 0; }
.taf-magnet .formkit-form .formkit-submit { flex: 0 0 auto !important; width: auto !important; white-space: nowrap; }

/* ---- Hero search pill internal padding (mobile only — the inside-wrapper padding is
   zeroed above, so the text started flush to the border). 0,4,0 beats that 0,3,0. ---- */
@media (max-width: 899.98px) {
  .home .taf-hero .taf-search .wp-block-search__inside-wrapper {
    padding-left: var(--space-16);
    padding-right: var(--space-8);
  }
}

/* ======================
   About page (body.page-id-21) overrides.
   ====================== */

/* ---- Single 20px page gutter + no dead space above the hero. Kadence wraps the page in
   content-container(24px) + a boxed article(-16px) + entry-content-wrap(24px) — a net 32px
   inset ON TOP of each .taf-section's own 20px, plus 56px of dead space above the hero.
   Same idiom as the single-post scaffold reset. ---- */
body.page-id-21 .content-area { margin-top: 0; margin-bottom: 0; }
body.page-id-21 .content-container.site-container { padding-left: 0; padding-right: 0; }
body.page-id-21 .entry-content-wrap { padding: 0; }
/* Doubled class (0,3,1) out-specifies Feast's boxed -16px margin (0,3,0). */
body.page-id-21 article.single-entry.single-entry { margin-left: 0; margin-right: 0; }

/* ---- 22px above and below the "why" callout (about-story.php, unreferenced). ---- */
body.page-id-21 .taf-about-story .taf-callout--why {
  margin-top: var(--space-20); margin-bottom: var(--space-20);
}

/* ---- Stat numerals carry the display tracking (about-story.php, unreferenced). ---- */
body.page-id-21 .taf-about-stat__num { letter-spacing: var(--ls--001em); }

/* ---- About tightens the footer top margin to 28px (system default is 40px). ---- */
body.page-id-21 .taf-footer { margin-top: var(--space-24); }

/* Deleted here 2026-08-04 with the ABOUT v3 rebuild, each proven dead by "no
   theme .php/.js consumer" + "no live post in the DB, only post-21 revisions":
   the .taf-about-hero/.taf-about-title/.taf-about-portrait centring rules, the
   .taf-about-kitchen subhead rule, the .taf-about-card__title rule, and the
   .taf-about-favs tile-title rule (the favorites pattern is .taf-abt-favs now,
   and the v3 block sets its own letter-spacing).

   ALSO GONE: `body.page-id-21 .wp-block-details p`, which coloured and shrank
   FAQ answers. The v3 FAQ is hand-authored <details class="taf-abt-faq__item">,
   not core/details, so it never carried .wp-block-details and that rule matched
   nothing on this page any more.

   ---- Desktop (≥900px) ----
   The whole 900px block went with them: its two rules styled .taf-about-title
   (deleted) and the magnet h3 inside .taf-about-close (about-magnet.php, which
   post 21 no longer renders — and whose own h3 sizing is unchanged elsewhere).
   Restore that second rule if the magnet band ever returns to this page. */

/* ======================
   Recipe-index + category archive overrides.
   ====================== */

/* --- INDEX: section headings. --fs-h2 covers both breakpoints (24px mobile /
   30px ≥900px). Scoped to the recipe-index page so other section headings are
   untouched. */
.page-id-42733 .taf-section h2.wp-block-heading {
  font-size: var(--fs-h2);
  font-weight: var(--w-semibold);
}

/* --- INDEX + CATEGORY: recipe-tile titles and the count meta take no display
   tracking (they inherited -0.01em from --ls-display). */
.taf-tile .t,
.t.wp-block-post-title,
p.t,
.taf-meta {
  letter-spacing: normal;
}

/* --- INDEX: most-loved h3 tile titles. The query-loop post titles are NOT inside
   .taf-tile; pinned explicitly (with !important against the heading counter) so they
   track the equalized 1.15 tile-title leading wherever they render. */
.t.wp-block-post-title {
  line-height: var(--lh-115) !important;
}

/* --- INDEX: category-tile row gap. Kadence's block-gap (0,2,0) added a 32px tile
   margin-bottom ON TOP of the 16px grid gap. Needs > (0,2,0). */
.page-id-42733 .taf-tile.taf-tile--cat {
  margin-bottom: 0;
}

/* --- INDEX: hero H1 spans the full content box (it inherited the home hero's ch cap +
   auto side margins). .taf-ri-hero keeps the home hero untouched. */
.taf-ri-hero .taf-hero-title {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}

/* --- INDEX: hero H1 → search gap. */
.taf-ri-hero .taf-search {
  margin-top: 0;
}

/* --- INDEX: the browse band tucks under the hero. It is the .taf-section
   immediately after the hero. */
.taf-ri-hero + .taf-section {
  padding-top: var(--space-20);
}

/* --- CATEGORY: magnet eyebrow. max-width:none because .taf-magnet p's 42ch cap
   also hits the eyebrow and, with the auto-centering killed, the capped box
   left-flushes. A label needs no measure. (A font-size: var(--fs-eyebrow) used to
   live here too — dead: the equal-specificity 15px rule in the typography pass
   below wins by source order. Removed 2026-07-17.) */
.taf-magnet .taf-eyebrow {
  max-width: none;
}

/* --- CATEGORY: the magnet heading is a flat 22px at all widths (the ≥900px rule
   below steps it to 20px on home only). */
.taf-magnet h3 {
  font-size: var(--fs-24);
}

/* ======================
   Typography + geometry pass.
   ====================== */

/* Magnet eyebrow: `.taf-magnet p{font-size:15px}` overrides the 12px eyebrow token, so the
   computed eyebrow is 15px (its letter-spacing and leading then follow from the tokens).
   Home, category, about. */
.taf-magnet .taf-eyebrow,
.taf-about-close .taf-eyebrow { font-size: var(--fs-15); }

/* Home magnet h3 steps down to 20px on desktop; mobile keeps 22px. Scoped to .home so the
   about magnet (22px) is untouched. */
@media (min-width: 900px) {
  .home .taf-magnet h3 { font-size: var(--fs-20); }
}

/* Category archive H1: 30px mobile / 42px desktop. .taf-cat-title only exists on the
   category archive, so this is inherently scoped. */
.taf-cat-title { font-size: var(--fs-30); line-height: var(--lh-115); letter-spacing: var(--ls--001em); }
@media (min-width: 900px) {
  .taf-cat-title { font-size: var(--fs-42); }
}

/* About FAQ question headings (.taf-faq details > summary > h3). The inner <h3> inherits
   the display face at --fs-h3/400, overriding the summary's sans 16/650. font-family +
   line-height need !important to beat the enqueue.php heading counter; size/weight/tracking
   win on specificity alone. */
.taf-faq summary h3,
.taf-faq details summary h3 {
  font-family: var(--sans) !important;
  font-size: var(--fs-16);
  font-weight: var(--w-bold);
  line-height: var(--lh-165) !important;
  letter-spacing: normal;
}

/* Button + chip labels inherit the body line-height (1.65); the theme forces 1.6 on
   .wp-element-button links. The 2-class selector beats it. */
.wp-block-button .wp-block-button__link,
.taf-btn, .taf-chip { line-height: var(--lh-165); }

/* ---- The in-content ad reserve and its collapse-when-unfilled rules were
   REMOVED in v0.90.0 with the containers themselves. See the ad-slot note near
   the top of this sheet for the reasoning and for what must not be re-added.

   One piece of hard-won knowledge, kept because it will apply to any future
   `:has()` rule here: the collapse selector had to be written as the doubled
   `.taf-ad.taf-ad--incontent:not(:has(…))` (0,3,0) to beat
   `body.category .taf-ad--incontent` (0,2,1). Written singly it was 0,2,0 and
   LOST — measured, with the recipe-index slots collapsing while the category
   slot stayed 250px tall. Specificity, not source order, decided it. ---- */

/* Cream band grid is ALIGNED to the content column, not run wider, so its tiles line up
   vertically with the bands above. The BACKGROUND still runs full-bleed (alignfull); only
   the grid inside is column-aligned. */

/* The desktop rating-row jump control matches the mobile pill's 700 (it otherwise takes the
   shared .taf-btn 650). a.taf-post-meta__jump (0,0,1,1) beats .taf-btn (0,0,1,0) — scoped to
   the jump control, not all primary buttons. */
a.taf-post-meta__jump { font-weight: var(--w-bold); }

/* ======================
   Block-gap neutralizers — the canonical explanation; the blocks below refer back here.

   Kadence ships `.single-content .wp-block-group{margin-bottom:32px}` (0,2,0) plus
   analogous block-gap margins on figures/paragraphs. That 32px lands on EVERY nested group
   — the tile .ph photo frame, the unclassed eyebrow+H2 wrapper inside .taf-section-head —
   ON TOP of the intended element margin. Element margins can therefore read as correct
   while the VISIBLE gap is wrong: block-gap ADDS to them. Verify by measured inter-element
   gap (prev.bottom → next.top), never by margin.

   Each rule below zeroes/retunes the leaking nested margin. Selectors are pitched >(0,2,0)
   so they win regardless of print order.
   ====================== */

/* ---- Tile photo-frame → title: .taf-tile .ph is itself a .wp-block-group, so the 32px
   block-gap swallows the title's 10px margin-top. Zero the frame so the title's 10px IS the
   gap. Every tile, every grid. Doubled .ph = (0,3,0). */
.taf-tile .ph.ph { margin-bottom: 0; }

/* ---- Section-head heading wrapper: the eyebrow+H2 sit in an unclassed nested
   .wp-block-group carrying the 32px block-gap, injecting dead space between the visible H2
   and whatever follows. */
.taf-section-head > .wp-block-group.is-layout-flow { margin-bottom: 0; }

/* ---- Bread band: pull the subhead's top margin to -12 against the 16px section-head
   bottom (collapse → 4px). Mobile-only — the desktop bread band has no subhead. */
@media (max-width: 899.98px) {
  .entry-content .has-rose-milk-background-color .taf-small.taf-muted { margin-top: -12px; } /* token-exempt: a negative pull-up, not a scale step. calc(var(--space-N) * -1) is equivalent but unreadable, and css-parity compares text not computed values, so it cannot prove the equivalence anyway. */
}

/* ---- The avatar figure's 32px block-gap swallows the H2's 14px margin-top. */
.home .taf-about-band figure.wp-block-image { margin-bottom: 0; }

/* ---- The bio paragraph's 32px block-gap; the ghost CTA sits flush under it. */
.home .taf-about-band > p.taf-muted { margin-bottom: 0; }

/* ======================
   About page — the same 32px block-gap leak lands on the stat cells, the kitchen cards and
   the magnet form-wrapper, stacking on a grid gap or the fine print's own margin.
   ====================== */

/* Stat-cell row gap = the grid row-gap alone; each cell is a wp-block-group carrying the
   32px block-gap on top of it. */
body.page-id-21 .taf-about-stats .taf-about-stat.taf-about-stat { margin-bottom: 0; }

/* (The kitchen-card gap neutralizer was deleted 2026-08-04 with the v3 rebuild —
   .taf-about-cards / .taf-about-card have no consumer left.) */

/* The stats band's OWN bottom margin. */
body.page-id-21 .taf-about-stats.taf-about-stats { margin-bottom: var(--space-20); }

/* The email form sits in a nested wp-block-group whose 32px block-gap swallows the fine
   print's 10px margin-top. */
body.page-id-21 .taf-magnet .wp-block-group:has(+ .fine) { margin-bottom: 0; }

/* ======================
   Recipe index — section headings leak the wp-block-heading default 6px top margin.
   ====================== */
.page-id-42733 .taf-section-head h2.wp-block-heading { margin-top: 0; }

/* ======================
   More block-gap neutralizers — the same 32px leak (see above), on further nested elements:
   the tile title <p.t>, the tile group itself, the magnet form wrapper, the hero subhead,
   the bread grid.
   Selectors are TARGETED, not a blanket `.wp-block-group{margin-bottom:0}`, ON PURPOSE: a
   blanket 0,3,0 rule would clobber the retuned group margins above (e.g.
   .taf-about-stats.taf-about-stats{margin-bottom:22px}).
   ====================== */

/* ---- Tile title: the title <p.t> carries the 32px block-gap as bottom margin —
   zero it so the grid row-gap alone spaces rows (tile stars are retired, D5-C). */
.taf-tile > .t,
.taf-tile > h3.t { margin-bottom: 0; }

/* ---- Tile row-to-row = the grid row-gap only; each .taf-tile is a .wp-block-group carrying
   the 32px block-gap on top of it. The doubled class ties Kadence at (0,2,0) and wins by
   load order. */
.taf-tile.taf-tile { margin-bottom: 0; }

/* ---- Magnet (home + category). The ConvertKit form sits in a nested .wp-block-group with
   two leaks: its 32px margin-block-start pushes the form below the description (retune to
   16px), and its 32px margin-block-end swallows the fine print's 10px margin-top (zero it). */
.taf-magnet .wp-block-group:has(+ .fine) { margin-top: var(--space-16); margin-bottom: 0; }

/* ---- ConvertKit's own stylesheet puts margin-bottom:20px on form.seva-form; it collapses
   THROUGH the padding/border-less wrapper and re-inflates the fine-print gap. Its rule ties
   on specificity and loses only by order, so !important guarantees the win. */
.taf-magnet .seva-form { margin-bottom: 0 !important; }

/* ---- Hero subhead → search: the subhead's 32px block-gap dominates the search's 20px
   margin-top. */
.taf-hero p:has(+ .taf-search) { margin-bottom: 0; }

/* ---- Bread band: the grid carries the 32px block-gap before the mobile-only "see all" CTA.
   Zero it only when it directly precedes that CTA. (Desktop uses the section-head "more"
   link, so the CTA is absent there.) */
.taf-grid-4:has(+ .wp-block-buttons.taf-hide-desktop) { margin-bottom: 0; }
.wp-block-buttons.taf-hide-desktop { margin-top: var(--space-16); }

/* ---- The footer brand <p> picks up a 22px top margin; the footer's padding-top gives the
   space. */
.taf-footer .taf-brand { margin-top: 0; }

/* ---- The footer keeps its 90px bottom padding at all widths (sticky-ad clearance); the
   desktop override above dropped it to 44px. */
/* token-frozen: THE STICKY-AD CLEARANCE (desktop half).
   This is what keeps content from sliding UNDER a Raptive unit. Ad viewability is
   revenue on a ~$27K/yr line.
   T6 remapped this to 64px and ONLY constraint-check.py caught it (2026-07-14).
   The constraint spans TWO rules: the base .taf-footer, which carried the
   sticky-ad comment and so was frozen by T3, and THIS media override, which did
   not — so T3's comment-driven triage never saw it. A constraint can span rules;
   freezing only the annotated one leaks.
   T6 must NOT remap the value in this rule. */
@media (min-width: 900px) { .taf-footer { padding-bottom: var(--space-90); } }

/* ---- About FAQ: 18px horizontal inset on the summary and the answer so they align. (The
   design also wraps the FAQ in a bordered card; the theme renders it flush by choice.) ---- */
.taf-faq summary { padding-left: var(--space-16); padding-right: var(--space-16); }
.taf-faq details > div { padding-left: var(--space-16); padding-right: var(--space-16); }

/* (The .taf-about-card thumbnail flex-basis pin was deleted 2026-08-04 with the
   v3 rebuild — the card component it rescued has no consumer left.) */

/* ---- body font-family: `body{font-family:var(--sans)}` (0,0,1) loses to Kadence, so <body>
   computes to the Kadence stack. !important forces the token; only elements WITHOUT their own
   font-family inherit it (headings/buttons/inputs are unaffected). */
body { font-family: var(--sans) !important; }

/* ---- The search "Go" button intentionally stays on var(--sans) (it inherits the design sans
   via the theme's `button{font:inherit}` reset). Do not "fix" it to a UA default. ---- */

/* Magnet Kit submit → brand rose, pinned in theme CSS so it holds regardless of the
   ConvertKit form's own button color. Remove this block to defer to the Kit setting. */
.taf-magnet .formkit-submit,
.taf-magnet button[type="submit"] {
  background-color: var(--accent) !important;
  color: var(--color-on-accent) !important;
}
.taf-magnet .formkit-submit:hover,
.taf-magnet button[type="submit"]:hover {
  background-color: var(--accent-hover) !important;
}

/* Hero search input + "Go" button: line-height back to UA `normal` — the theme's
   1.6 nudges the placeholder's vertical centering. */
.taf-hero .wp-block-search__input,
.taf-hero .wp-block-search__button { line-height: normal; }

/* The long placeholder clipped mid-character at the field edge on narrow phones. Truncate
   with an ellipsis and keep an 8px gap before the Go button. */
.taf-hero .wp-block-search__input {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-right: var(--space-8);
}

/* ---- Category archive headline eyebrow ----
   Term meta 'taf2026_headline' renders as the eyebrow above the H1, reusing the .taf-eyebrow
   token; only the eyebrow→H1 rhythm is category-specific. Collapses cleanly when a term has
   no headline. See inc/category-archive.php. ---- */
.taf-cat-eyebrow {
  margin: 0 0 var(--space-8);
}

/* ======================
   HOME — section components. Desktop-base + a max-width: 899.98px mobile layer.
   Tokens for radius/color/shadow; explicit px for type/geometry.
   ====================== */

/* ---- shared home helpers ---- */
.taf-ph { background: var(--line); display: block; }        /* photo placeholder fill */
.taf-i { width: 1em; height: 1em; display: inline-block; vertical-align: middle; flex: none; }

/* shared cook/bake-time meta (used instead of star ratings on the home — clock
   icon + time; inherits its parent's color so it works on cream and dark scrims) */
.taf-time { display: inline-flex; align-items: center; gap: var(--space-4); font-family: var(--display); font-size: var(--fs-13); line-height: var(--lh-13); }
.taf-time .taf-i { width: 14px; height: 14px; opacity: var(--o-9); }

.taf-secthead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-16); margin-bottom: var(--space-20); }
.taf-secthead h2 { font-size: var(--fs-30); margin: 0; }
.taf-more { margin: 0; }
.taf-more a { font-family: var(--display); font-size: var(--fs-15); font-weight: var(--w-semibold); white-space: nowrap; }

.taf-tilegrid { display: grid; gap: var(--space-20); }
.taf-tilegrid--4 { grid-template-columns: repeat(4, 1fr); }
.taf-tilegrid--3 { grid-template-columns: repeat(3, 1fr); }

/* whole-tile card (baking + mithai side tiles) */
.taf-tilecard { text-decoration: none; color: inherit; display: block; }
.taf-tilecard__img { width: 100%; aspect-ratio: var(--ar-4-5); border-radius: var(--radius); overflow: hidden; object-fit: cover; transition: box-shadow var(--speed) var(--ease); }
.taf-tilecard__title { font-family: var(--display); font-size: var(--fs-17); line-height: var(--lh-115); margin: var(--space-8) 0 0; }
.taf-tilecard__meta { font-family: var(--display); font-size: var(--fs-13); color: var(--cocoa); margin: var(--space-4) 0 0; }
.taf-tilecard:hover .taf-tilecard__img { box-shadow: var(--shadow-hover); }
.taf-tilecard:hover .taf-tilecard__title { text-decoration: underline; text-underline-offset: 3px; }
/* Hover zoom: .taf-tilecard__img is the overflow:hidden rounded box; the photo inside scales,
   so it crops WITHIN the fixed bounds (no layout shift).
   TWO photo idioms share this box — a real <img srcset> (dynamic tiles) and a ::before
   background layer (static patterns). Keep BOTH: see inc/home-new-recipes.php. */
.taf-tilecard__img { position: relative; }
.taf-tilecard__img::before {
  content: ""; position: absolute; inset: 0;
  background-image: inherit; background-size: cover; background-position: center;
  transition: transform var(--dur-35) var(--ease);
}
.taf-tilecard__img > img {
  position: absolute; inset: 0; display: block;
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-35) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .taf-tilecard:hover .taf-tilecard__img::before,
  .taf-tilecard:hover .taf-tilecard__img > img { transform: scale(1.04); }
}

/* ---- Header helpers (the Kadence header carries these classes) ---- */
/* Header CTA: targets the build's .taf-hdr-cta AND Kadence's generated button selector
   (a.button.header-button.button-style-filled, 0,3,1) so it beats the theme's un-themed
   button. #fff on --accent = ~4.7:1 (AA). */
.taf-hdr-cta a, a.taf-hdr-cta,
a.button.header-button.button-style-filled {
  background: var(--accent); color: var(--color-on-accent); font-family: var(--display); font-size: var(--fs-15); font-weight: var(--w-semibold);
  padding: var(--space-12) var(--space-16); border-radius: var(--radius-sm); text-decoration: none;
  white-space: nowrap; display: inline-block; border: none;
  transition: background var(--speed) var(--ease);
}
.taf-hdr-cta a:hover, a.taf-hdr-cta:hover,
a.button.header-button.button-style-filled:hover {
  background: var(--accent-hover); color: var(--color-on-accent);
}
/* Search icon: 44px tap target, ~20px glyph, rose on hover. */
.taf-hdr-search { width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; color: var(--chocolate); background: transparent; border: none; cursor: pointer; transition: color var(--speed) var(--ease); }
.taf-hdr-search:hover { color: var(--accent); }
.taf-hdr-search .taf-i, .taf-hdr-search svg { width: 20px; height: 20px; }

/* Hide the redundant text site-title beside the wordmark logo — the logo's alt
   text carries the site name. */
.site-branding .site-title { display: none; }

/* Logo sizing is HEIGHT-based, not Kadence's width-based.
   Kadence sizes the logo by WIDTH (logo_width theme_mod); on the wide 2026 lockup (aspect
   2.24) a 100px width implies only ~45px of height, leaving the header's vertical room empty.
   Header rows carry no padding and a min-height (72px desktop / 60px mobile), so header
   height = max(min-height, tallest item) — 64/52 leave it unchanged.
   !important is load-bearing: Kadence prints logo_width as Customizer CSS inline AFTER this
   file, so it wins on order at any specificity. Left alone it clamps width to 78px while the
   height stays, which SQUASHES the logo (aspect 1.5 vs the file's true 2.24). Width must
   stay auto. */
.site-branding .custom-logo,
#main-header .custom-logo,
.site-mobile-header-wrap .custom-logo {
  height: 52px !important;      /* mobile: 60px row */
  width: auto !important;
  max-width: none !important;
}
@media (min-width: 900px) {
  .site-branding .custom-logo,
  #main-header .custom-logo {
    height: 64px !important;    /* desktop: 72px row */
  }
}

/* Sticky main header: #masthead is a single row here, so making the outer container sticky
   pins the whole bar. z-index 99 clears the sticky footer ad (55), jump pill (60) and mobile
   drawer (49). */
#masthead { position: sticky; top: 0; z-index: var(--z-99); }
/* translucent sticky header (overrides Kadence's solid #fff); no backdrop blur */
#masthead, #masthead .site-header-row-container-inner { background: var(--veil-80) !important; }
/* soft (non-blur) gradient fade at the sticky header's bottom edge, masked to
   fade out — a gentle separator with no blur */
#masthead::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -18px; height: 18px;
  pointer-events: none; z-index: var(--z-98);
  background: linear-gradient(to bottom, var(--veil-55), var(--veil-0));
  /* token-exempt: mask-image alpha stops — black/transparent are the MASK channel, not design colors */
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1), rgba(0,0,0,0));
  mask-image: linear-gradient(to bottom, rgba(0,0,0,1), rgba(0,0,0,0));
}
/* Header search drawer: styled in the KADENCE SEARCH DRAWER block at the end of
   this file. */

/* Header CTA right-edge alignment: the Kadence main-row container is full-width with a 32px
   gutter, so its content edge overshoots the 1200px page column. Cap the container to 1200 +
   two gutters and center it, so the header box tracks the page column at every width. */
#masthead .site-container { max-width: calc(var(--maxw-wide) + 2 * var(--pad)); margin-left: auto; margin-right: auto; }

/* Recipes nav dropdown (Kadence .sub-menu) → Warm-Oven panel. See inc/nav-dropdown.php. */
.main-navigation ul.sub-menu {
  background: var(--paper);
  border: var(--bw-1) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  padding: 0; overflow: hidden;
}
.main-navigation ul.sub-menu li.menu-item { border-bottom: var(--bw-1) solid var(--border-hairline); }
.main-navigation ul.sub-menu li.menu-item:last-child { border-bottom: none; }
.main-navigation ul.sub-menu li.menu-item > a {
  color: var(--chocolate); font-size: var(--fs-15); font-weight: var(--w-semibold);
  padding: var(--space-8) var(--space-16); background: transparent;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.main-navigation ul.sub-menu li.menu-item > a:hover,
.main-navigation ul.sub-menu li.menu-item > a:focus-visible {
  color: var(--accent); background: var(--cream);
}

/* ---- Section 2 hero: readers' #1 recipe ---- */
.taf-rhero { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-48) var(--pad) var(--space-48); display: grid; grid-template-columns: 1fr 1.05fr; gap: var(--space-48); align-items: center; }
.taf-rhero__title { font-size: var(--fs-54); line-height: var(--lh-108); margin: var(--space-12) 0 var(--space-16); max-width: 14ch; }
/* Signature pink on the hero's accent phrase; the rest of the H1 stays chocolate,
   and the trailing period sits outside the span. */
.taf-rhero__accent { color: var(--rose); }
.taf-rhero__rating { font-size: var(--fs-15); margin: 0 0 var(--space-12); }
.taf-rhero__rating b { font-weight: var(--w-bold); }
.taf-rhero__lede { font-size: var(--fs-17); max-width: 42ch; margin: 0 0 var(--space-24); }
.taf-rhero__actions { display: flex; align-items: center; gap: var(--space-16); margin: 0; flex-wrap: wrap; }
.taf-cta { background: var(--rose); color: var(--color-on-accent); font-size: var(--fs-16); font-weight: var(--w-bold); padding: var(--space-12) var(--space-24); border-radius: var(--radius-sm); text-decoration: none; display: inline-block; transition: background var(--speed) var(--ease); }
.taf-cta:hover { background: var(--rose-deep); color: var(--color-on-accent); }
.taf-textlink { font-size: var(--fs-15); font-weight: var(--w-bold); }
.taf-rhero__media { position: relative; margin: 0; }
.taf-rhero__photo { position: relative; margin: 0; aspect-ratio: var(--ar-5-4); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); background: var(--line); }
.taf-rhero__photo img { width: 100%; height: 100%; object-fit: cover; }
.taf-rhero__tag { position: absolute; top: 14px; left: 14px; margin: 0; background: var(--chocolate); color: var(--milk); font-size: var(--fs-12); font-weight: var(--w-bold); letter-spacing: var(--ls-04em); padding: var(--space-4) var(--space-12); border-radius: var(--radius-sm); z-index: var(--z-2); }
/* kill the WP block-image default 32px figure margin (it pushed the hero CTA far
   below the photo on mobile) */
.entry-content .taf-rhero__media .taf-rhero__photo { margin: 0 !important; }

/* ---- Hero responsive ----
   Tablet (700-899px): keep 2 columns, shrink the H1 and gap so the copy column doesn't cramp
   and the photo stays a side column. Phones (<700px): stack + center. */
@media (min-width: 700px) and (max-width: 899.98px) {
  .taf-rhero { padding: var(--space-32) var(--pad) var(--space-40); gap: var(--space-32); }
  .taf-rhero__title { font-size: var(--fs-36); }
  .entry-content .taf-rhero__title { margin: var(--space-4) 0 var(--space-12); }
  .entry-content .taf-rhero__rating { margin: 0 0 var(--space-12); }
  .taf-rhero__lede { display: none; }
}
@media (max-width: 699.98px) {
  .taf-rhero { display: flex; flex-direction: column; gap: var(--space-12); padding: var(--space-24) var(--pad) var(--space-16); text-align: center; }
  .taf-rhero__copy { display: contents; }
  .taf-rhero .taf-eyebrow { order: 1; margin: 0; }
  .taf-rhero__title { order: 2; font-size: var(--fs-36); max-width: none; }
  .entry-content .taf-rhero__title { margin: 0 auto; }
  .taf-rhero__rating { order: 3; margin: 0; }
  .entry-content .taf-rhero__rating { margin: 0; }
  .taf-rhero__lede { order: 4; max-width: none; margin: 0 auto; }
  .taf-rhero__media { order: 5; }
  .taf-rhero__actions { order: 6; justify-content: center; margin: 0; }
  .taf-rhero__rating .taf-time { justify-content: center; }
  .taf-cta { width: 100%; text-align: center; }
  .taf-rhero__photo { aspect-ratio: var(--ar-4-34); }
}

/* ---- Section 3 category band ---- */
.taf-catband { padding: var(--space-40) var(--pad) var(--space-48); }
.taf-catband__inner { max-width: var(--maxw-wide); margin: 0 auto; }
.taf-catband__title { font-size: var(--fs-30); text-align: center; margin: 0; }
/* Circles wrap into even, centered rows (flex-wrap + fixed-width items) — no horizontal
   scroller at any width. */
.taf-catrow { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-24) var(--space-16); margin-top: var(--space-24); }
.taf-cat { width: 150px; text-decoration: none; color: var(--chocolate); text-align: center; display: block; transition: transform var(--dur-2) var(--ease); }
/* Category circle photos carry no white ring (the paper border is deliberately
   removed). Keep radius/shadow. */
.taf-cat__img { width: 150px; height: 150px; border-radius: var(--radius-round); object-fit: cover; margin: 0 auto var(--space-8); box-shadow: var(--shadow); }

.taf-cat__img--all .taf-i { width: 26px; height: 26px; }
/* Tight leading so the category NAME sits right above the COUNT (no airy gap). */
.taf-cat__label { display: block; font-size: var(--fs-15); font-weight: var(--w-bold); line-height: var(--lh-12); margin-bottom: var(--space-2); }
/* Hover lift: the whole circle group rises 4px and the photo shadow deepens. */
.taf-cat:hover { transform: translateY(-4px); }
.taf-cat:hover .taf-cat__img { box-shadow: var(--shadow-hover); }
.taf-cat:hover .taf-cat__label { color: var(--rose-deep); }

/* Tablet + mobile (≤1024): the 6-up row collapses to two FLUID rows of 3 that
   resize with the viewport instead of a fixed size; the hero lede is dropped. */
/* ⚠️ BREAKPOINT OVERLAP — DELIBERATELY NOT "FIXED" (T5, 2026-07-14).
   This is `max-width: 1024px` while three other blocks are `min-width: 1024px`.
   At EXACTLY 1024px BOTH match — and 1024px is iPad landscape, a real, common
   viewport. They touch different properties (this hides the recipe lede; the
   others show the 320px sidebar), so the overlap may even be intended.

   Fixing it necessarily CHANGES RENDERING on iPad landscape, in one direction or
   the other, and the intent cannot be recovered from the code:
     - push this to 1023.98px -> the lede REAPPEARS at 1024px
     - push the others to 1025px -> the SIDEBAR DISAPPEARS at 1024px
   Neither is obviously right. Needs a design call, not a lint pass.

   NOTE the site already has a CORRECT fractional pair for this edge in
   taf-dropdown.css (max-width: 1024.98px / min-width: 1025px), so the wide edge
   currently has TWO conventions. The 899/699 edges were normalized (they had a
   real gap where NEITHER rule applied); this one was left alone on purpose. */
@media (max-width: 1024px) {
  .entry-content .taf-rhero__lede, .taf-rhero__lede { display: none; }
  .taf-catrow { gap: var(--space-20) var(--space-16); margin-top: var(--space-24); }
  .taf-cat { width: calc((100% - 34px) / 3); }
  .taf-cat__img, .taf-cat__img--all { width: 100%; height: auto; aspect-ratio: var(--ar-1-1); }
  /* Phones: the CTA drops under the search box and goes full-width. */
  .taf-searchrow { flex-direction: column; gap: var(--space-8); }
  .taf-searchrow__cta { width: 100%; justify-content: center; }
}
/* Search box + the "see all recipes" CTA sit side by side, the CTA OUTSIDE the box. The
   recipe index is the highest-yield link on the homepage, so it gets its own affordance — as
   a SECONDARY button, so it complements the rose Search rather than competing. */
.taf-searchwrap { width: 100%; max-width: 940px; margin: var(--space-32) auto 0; scroll-margin-top: 96px; }
.taf-searchrow { display: flex; align-items: stretch; justify-content: center; gap: var(--space-12); }
.taf-searchrow .taf-searchbar { flex: 1 1 auto; max-width: 680px; }
/* Chocolate fill, deliberately NOT rose: the rose Search button beside it stays the band's
   primary action. */
.taf-searchrow__cta {
  flex: 0 0 auto; height: 56px; padding: 0 var(--space-24); white-space: nowrap;
  display: inline-flex; align-items: center; gap: var(--space-8);
  background: var(--chocolate); color: var(--color-on-dark); border: none;
  transition: background var(--speed) var(--ease);
}
.taf-searchrow__cta:hover { background: var(--chocolate-hover); color: var(--color-on-dark); }
.taf-searchrow__cta svg { width: 18px; height: 18px; }
.taf-searchbar { display: flex; align-items: center; gap: var(--space-8); background: var(--paper); border: none; border-radius: var(--radius); height: 56px; padding: 0 var(--space-8) 0 var(--space-20); box-shadow: var(--shadow); }
/* The input suppresses its own ring (below); the CONTAINER is the focus
   affordance. The old `border-color: var(--rose)` was inert — the pill has
   border: none — so keyboard focus had no visible indicator here at all
   (verified rendered 2026-07-17). Sitewide ring language instead. */
.taf-searchbar:focus-within { outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; } /* search = rose, see .taf-search rule */
.taf-searchbar__icon { width: 18px; height: 18px; color: var(--cocoa); }
.taf-searchbar__input { flex: 1; min-width: 0; border: none !important; outline: none; background: transparent !important; box-shadow: none !important; font-size: var(--fs-16); color: var(--chocolate); }
.taf-searchbar__input::placeholder { color: var(--cocoa); }
.taf-searchbar__btn { background: var(--rose); color: var(--color-on-accent); font-size: var(--fs-15); font-weight: var(--w-bold); padding: var(--space-12) var(--space-20); border-radius: var(--radius-sm); border: none; cursor: pointer; white-space: nowrap; transition: background var(--speed) var(--ease); }
.taf-searchbar__btn:hover { background: var(--rose-deep); }
.taf-popular { margin: var(--space-8) 0 0; text-align: center; font-size: var(--fs-13); color: var(--cocoa); }
.taf-popular a { font-weight: var(--w-bold); }

/* ---- Autocomplete panel (taf-search.js; spec docs/plan/search-autocomplete/).
   Injected as the active search form's child; the form gets .taf-ac-anchor.
   position:absolute under the input → zero CLS; z-index above the drawer (49)
   and jump pill (60). Rows keep the 44px touch floor; thumbs are fixed 48px
   boxes so lazy loads can't shift layout. ---- */
.taf-ac-anchor { position: relative; }
.taf-ac {
  position: absolute;
  top: calc(100% + var(--space-8));
  left: 0;
  right: 0;
  /* token-exempt: layered above the drawer (49) and jump pill (60); the theme has no z-scale tokens */
  z-index: 70;
  background: var(--paper);
  border-radius: var(--radius);
  box-shadow: var(--shadow-hover);  /* the elevated tier — the panel floats over content */
  max-height: min(70vh, 520px);
  overflow-y: auto;
  padding: var(--space-8);
  /* The panel is a floating MENU, not hero copy — it must read left wherever it
     is injected. On /recipe-index/ the host form sits inside .taf-center
     (.taf-section taf-center taf-hero taf-ri-hero), so `text-align: center`
     inherited all the way down (measured: computed `center` on .taf-ac,
     .taf-ac__body, .taf-ac__title and .taf-ac__meta alike). The symptom read as
     "only the stars are centred" because .taf-ac__body is a shrink-to-fit
     column flexbox: both spans stretch to the WIDEST child's width (the title),
     so a single-line title exactly fills its box and its centring is invisible,
     while the short `★ 4.3 · 12` floats mid-box. Wrapping titles centred too
     (measured: line 1 of a 2-line title started 9px in). The drawer instance
     inherits `start` and never showed it. Fix the inheritance at the panel, not
     the one element with the visible symptom. */
  text-align: left;
}
/* .taf-ac ul + li qualifiers: a global list rule lands 32px margin-bottom on
   a bare .taf-ac__list (measured — it reads as phantom bottom padding on
   the panel). Trap 4: Kadence content defaults. */
.taf-ac ul.taf-ac__list { list-style: none; margin: 0; padding: 0; }
.taf-ac ul.taf-ac__list > li { margin: 0; }
.taf-ac__row a {
  display: flex; align-items: center; gap: var(--space-12);
  padding: var(--space-8); min-height: 56px; border-radius: var(--radius-sm);
  text-decoration: none; color: var(--chocolate);
}
/* aria-selected sits on the <a> since the a11y re-run (2026-08-06) moved
   role="option" off the <li> — see assets/js/taf-search.js renderResults(). The
   painted result is identical; the attribute just moved one level down. */
.taf-ac__row a:hover, .taf-ac__row a[aria-selected="true"] { background: var(--rose-tint); }
.taf-ac__thumb { width: 48px; height: 48px; border-radius: var(--radius-sm); object-fit: cover; flex: none; background: var(--rose-tint); }
.taf-ac__body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.taf-ac__title { font-size: var(--fs-15); font-weight: var(--w-semibold); line-height: var(--lh-13); }
.taf-ac__title mark {
  background: var(--rose-tint); color: var(--rose-deep);
  border-radius: 2px; padding: 0 1px; /* token-exempt: hairline mark-highlight metrics, sub-token scale */
}
.taf-ac__meta { font-size: var(--fs-12); color: var(--cocoa); }
.taf-ac__all a { color: var(--rose); font-weight: var(--w-bold); text-decoration: none; padding: var(--space-12) var(--space-8); font-size: var(--fs-14); min-height: 44px; display: flex; align-items: center; border-radius: var(--radius-sm); }
/* [aria-selected="true"] joined this rule when the see-all row became a real
   role="option" in the arrow cycle (2026-08-06). Without it a keyboard user
   arrowing onto the 6th option got NO visible indicator — measured: the only
   thing that changed on activation was the attribute itself, across 12 computed
   properties. WCAG 2.4.7. The value matches :hover so the painted result is the
   one readers already know. */
.taf-ac__all a:hover, .taf-ac__all a:focus-visible,
.taf-ac__all a[aria-selected="true"] { background: var(--rose-tint); }

/* ---- Recently viewed (taf-recipe.js D3) — its own band above the comments,
   built in the post-footer band idiom (eyebrow · display title · 4:5 tile
   grid, 2-up phone / 4-up desktop). The DESKTOP SIDEBAR copy reuses the
   sidebar's own .taf-sb-* classes and needs no styles here. ---- */
/* NO HAIRLINE above this band. It sat directly under the full-bleed rose
   band's bottom edge, so the page showed two dividers a few pixels apart —
   the band's own edge does the work.
   The 24px padding-top went WITH the line (it only existed to sit the title
   under it), and the 40px margin came down to 32: 64px of clear cream under a
   full-bleed tint read as a dropped section rather than the next one. */
.taf-recent {
  margin: var(--space-32) 0 var(--space-32);
}
/* The eyebrow ("Pick up where you left off") is GONE —. Its
   rule went with it, and the title's 8px top margin (which only existed to
   sit it under the eyebrow) is now 0 so the band opens on the title. */
.taf-recent__title {
  font-family: var(--display); font-size: var(--fs-24); font-weight: var(--w-semibold);
  letter-spacing: var(--ls-display); color: var(--chocolate);
  margin: 0 0 var(--space-20);
  padding: 0; border: 0; /* not a content h2 — no section hairline */
}
.taf-recent__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-20) var(--space-16); }
.taf-recent__tile { display: block; color: var(--chocolate); text-decoration: none; min-width: 0; }
.taf-recent__imgwrap {
  display: block; aspect-ratio: var(--ar-4-5);
  border-radius: var(--radius); overflow: hidden; background: var(--rose-tint);
  transition: box-shadow var(--speed) var(--ease);
}
.taf-recent__img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: var(--radius); }
.taf-recent__t {
  display: block; margin-top: var(--space-8);
  font-family: var(--display); font-size: var(--fs-16); font-weight: var(--w-regular);
  line-height: var(--lh-13);
}
.taf-recent__tile:hover .taf-recent__t,
.taf-recent__tile:focus-visible .taf-recent__t { text-decoration: underline; text-underline-offset: 3px; }
.taf-recent__tile:hover .taf-recent__imgwrap { box-shadow: var(--shadow-hover); }
@media (min-width: 900px) {
  .taf-recent__grid { grid-template-columns: repeat(4, 1fr); }
  .taf-recent__title { font-size: var(--fs-30); }
}

/* ---- Home utility links (section-head "more", popular queries, intro-card link):
   the house rose-deep on hover/focus. Covers .taf-seahero__cta too (it carries
   .taf-more).

   THE ROW LINK-OUTS ARE UNDERLINED ("the 'all <category>'
   link-outs on each row need an arrow or an underline — right now they don't
   read as links that lead somewhere"). This is a measured problem, not a
   cosmetic one: only 1.4% of the clicks leaving this page go to any category
   link at all (180 in a year), against 82% straight into a recipe
   (reports/homepage-plan.html).

   The old rule said "quiet at rest — the surrounding band carries the
   affordance". It does not. Verified rendered on the rendered page before this change:
   `All bread recipes` painted #D01277 with text-decoration:none, sitting on the
   baseline of a plain H2, distinguished from the page's ink by COLOUR ALONE —
   which is also a WCAG 1.4.1 (Use of Colour) failure, not only a conversion
   one.

   Underline, not an arrow, of the two options considered: `.taf-pf-band__all` —
   the "All 57 no-bake recipes" link on the related-recipes band at the foot of
   every recipe post — already solves this exact role with exactly this
   treatment (taf-postfooter.css: rose-link, underline, 3px offset). Matching it
   makes one house convention instead of two. 3px offset is the site-wide
   default set on the bare `a` at :519; it is restated nowhere here.

   .taf-popular is deliberately NOT included: those are four search queries run
   together inside one "Popular: a · b · c · d" line, and underlining all four
   turns a quiet line into a fence. Its "Popular:" lead-in and inline-list shape
   already read as links. ---- */
.taf-more a,
.taf-introcard__link a { text-decoration: underline; }
.taf-popular a { text-decoration: none; }
.taf-more a:hover, .taf-more a:focus-visible,
.taf-popular a:hover, .taf-popular a:focus-visible,
.taf-introcard__link a:hover, .taf-introcard__link a:focus-visible { color: var(--rose-deep); }
.taf-popular a:hover, .taf-popular a:focus-visible { text-decoration: underline; }

/* ---- 44px tap targets for the inline utility links (2026-07-17 polish wave).
   Visual size unchanged: an invisible centered ::after extends each link's hit
   area to the 44px floor — the same idiom as the search empty state
   (.taf-empty-chip). Adjacent links' zones may overlap slightly on dense rows
   (accepted there too).
   ⚠️ `.taf-popular a` in this list is ALSO the mobile drawer's Popular line
   (it shares the class since 2026-08-09, see #mobile-drawer .taf-mm-popular).
   The drawer is the phone; do not narrow this selector list. ---- */
.taf-more a,
.taf-popular a,
.taf-introcard__link a,
.taf-breadcrumbs a,
.taf-post-meta__byline a { position: relative; }
.taf-more a::after,
.taf-popular a::after,
.taf-introcard__link a::after,
.taf-breadcrumbs a::after,
.taf-post-meta__byline a::after {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: var(--space-44); transform: translateY(-50%); /* 44px tap target */
}

/* ---- Section 4 baking right now ---- */
.taf-baking { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-32) var(--pad) var(--space-8); } /* top 48->32: the short curated bands read gappy at 56px internal rhythm; 32 lands them on the page's 40px beat */

/* ---- Section 5 author intro card ---- */
.taf-introcard { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-40) var(--pad) var(--space-8); }
.taf-introcard__card { display: flex; gap: var(--space-24); align-items: center; background: var(--paper); border: var(--bw-1) solid var(--line); border-radius: var(--radius); padding: var(--space-24) var(--space-32); }
.taf-introcard__avatar { flex-shrink: 0; margin: 0; width: 132px; height: 132px; }
.taf-introcard__avatar img { width: 132px; height: 132px; border-radius: var(--radius-round); object-fit: cover; background: var(--line); }
.taf-introcard__body { flex: 1; }
.taf-introcard__body h2 { font-size: var(--fs-24); margin: 0 0 var(--space-8); }
.taf-introcard__body p { font-size: var(--fs-16); max-width: 64ch; margin: 0; }
.taf-introcard__link { margin: 0; white-space: nowrap; }
.taf-introcard__link a { font-size: var(--fs-15); font-weight: var(--w-bold); }

/* ---- Section 6 "Trending this month" (legacy slug: most-loved) ----
   An editorial mosaic: ONE tile component, five instances. 3 cols x 2 rows —
   the #1 hero owns column 1 across both rows, tiles 2-5 fill the right two
   columns. No card chrome: the photo IS the surface, scrim + rank chip +
   caption sit on top of it.

   NOTE: .taf-scrimhero__* below is a SEPARATE component and is not used here.
   It is the scrim hero card, owned by the seasonal hero
   (patterns/summer-recipes.php, which composes it with .taf-scard tiles) and by
   inc/home-new-recipes.php. It was called .taf-loved__* until 2026-07-14 —
   named after Trending, which had stopped using it. Renamed so the class says
   what it is. The old .taf-lovedrow* paper rows died with the mosaic.
   ---- */
/* Band width (second decision): trending sits in the SAME
   maxw-wide box as the other home bands (content 2*pad narrower than the
   seahero's alignfull+inner shell), but keeps the seahero's PROPORTIONS —
   the composed .taf-scrimhero__grid's 1.15fr/1fr columns and the nested
   2-col cards scale to the box, so hero:cards ratio matches the seahero
   at every width. (A brief v0.28.5 experiment widened this box to full
   seahero parity; reverted in favor of band alignment.) */
/* Bottom padding = the ENTIRE visible gap to the next band: .taf-catband's
   rose-milk edge butts this element at 0 margin (measured 2026-08-11), so a
   small value here reads as no padding at all against the colour edge.
   Symmetric with the band's own top padding at each width. */
.taf-mosaic { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-40) var(--pad) var(--space-40); }

/* The mosaic. aspect-ratio (NOT a fixed height) sets the module's box: it is
   defined before any image loads, so the band reserves its space at parse time
   and cannot shift — and it scales instead of breaking.
   The desktop inner width is 1136px (the 1200px --maxw-wide cap minus 2x --pad,
   which is 32px from 900px up — NOT the 20px it is on phones). 24/11 of 1136 =
   520.6px tall: the intended ~520px, measured, without a hardcoded height. */
/* .taf-mosaic__grid (the flat 2fr/1fr/1fr 24:11 grid) retired 2026-07-17: trending now composes the seahero's own layout modules —
   .taf-scrimhero__grid (1.15fr/1fr, 32px gap) + .taf-seahero__cards — so the
   two bands' hero widths and card sizing can never drift apart again.
   .taf-mosaic__layout marks the trending instance; only hero min-heights
   (mirroring the seahero's per-breakpoint values) live on it. */
.taf-mosaic__layout .taf-mosaic__tile--hero { min-height: 540px; }
/* One component. --hero only changes placement + type scale. */
.taf-mosaic__tile { position: relative; display: block; border-radius: var(--radius); overflow: hidden; text-decoration: none; color: inherit; }
/* The tile is the clipping box; the photo is an absolutely-filled <img> so it can
   never influence the grid tracks (and so object-fit can crop it to any cell). */
.taf-mosaic__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-35) var(--ease); }
/* Hover zoom: the theme's one photo-zoom (1.04 / .35s), same as the mithai hero,
   the tilecards and the seasonal hero. */
@media (hover: hover) and (pointer: fine) {
  .taf-mosaic__tile:hover .taf-mosaic__img { transform: scale(1.04); }
}
/* The global reduce block (above) kills the transition but not the transform, so
   the zoom would still SNAP. Kill the transform too. */
@media (prefers-reduced-motion: reduce) {
  .taf-mosaic__tile:hover .taf-mosaic__img { transform: none; }
}
.taf-mosaic__scrim { position: absolute; inset: 0; background: var(--scrim-mosaic); }
/* Rank chip. --radius-pill, not a 999px literal: the design system RETIRED pills
   (see the token block) and aliases --radius-pill to the 6px control radius, so
   this chip reads as the same soft chip as every other control. */
.taf-mosaic__rank { position: absolute; top: 14px; left: 14px; z-index: var(--z-2); background: var(--rose); color: var(--color-on-accent); font-family: var(--display); font-size: var(--fs-15); font-weight: var(--w-bold); line-height: var(--lh-1); padding: var(--space-8) var(--space-12); border-radius: var(--radius-pill); box-shadow: var(--shadow); }
/* The "#" is ornament, not content: it belongs to the chip, so the markup carries
   the bare numeral and CSS prepends the glyph at 75%. */
.taf-mosaic__rank::before { content: "#"; opacity: var(--o-75); }
.taf-mosaic__rank--sm { top: 10px; left: 10px; font-size: var(--fs-12); padding: var(--space-4) var(--space-8); }
.taf-mosaic__cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: var(--z-2); padding: var(--space-16); }
.taf-mosaic__title { display: block; font-family: var(--display); font-size: var(--fs-17); line-height: var(--lh-115); color: var(--color-on-scrim); }
/* .taf-time inherits colour, so the clock icon + time go white on the scrim. */
.taf-mosaic__meta { display: block; margin-top: var(--space-4); font-size: var(--fs-13); color: var(--color-on-scrim); opacity: var(--o-9); }
.taf-mosaic__tile--hero .taf-mosaic__cap { padding: var(--space-24); }
.taf-mosaic__tile--hero .taf-mosaic__title { font-size: var(--fs-24); }
.taf-mosaic__tile--hero .taf-mosaic__meta { margin-top: var(--space-4); }

/* Trending 2x2 goes card-style: tiles 2-5 use the seahero
   satellite treatment — clean photo, title in its own whitespace below, no
   scrim, no time. The rank pill stays on the image (it is the band's
   ranked-list signal, not a text overlay). Only the #1 hero tile keeps the
   scrim cap, mirroring the seahero's big tile. Markup composes .taf-scard;
   these rules only adapt it to the mosaic's fixed-aspect grid. */
/* Cards are plain .taf-scard (natural 4:3 photo + two-line title) exactly like
   the seahero satellites; position:relative anchors the rank pill. */
.taf-mosaic__card { position: relative; }

/* ---- Seasonal-hero feature card (was shared with Trending; now seahero-only) ---- */
.taf-scrimhero__grid { display: grid; grid-template-columns: 1.15fr 1fr; gap: var(--space-32); align-items: stretch; }
.taf-scrimhero__hero { position: relative; display: block; min-height: 420px; border-radius: var(--radius); overflow: hidden; text-decoration: none; color: inherit; }
/* A real <img>: object-fit:cover reproduces the background-size:cover crop it
   replaced, on the same absolute box, so nothing moves. */
.taf-scrimhero__heroimg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-35) var(--ease); }
/* Hover zoom: same treatment as the mithai hero / tilecards. */
@media (hover: hover) and (pointer: fine) {
  .taf-scrimhero__hero:hover .taf-scrimhero__heroimg { transform: scale(1.04); }
}
.taf-scrimhero__scrim { position: absolute; inset: 0; background: var(--scrim-seahero); }
/* .taf-badge / .taf-badge--soft removed: both dead. --soft had no consumer before
   the mosaic; the base lost its last one (the "#1" chip) when .taf-mosaic__rank took
   over. Its design rule survives here because it is still true and still binding:
   A LABELLED (not ranked) feature must never wear the rose "#1" chip — a label
   ground is --rose-tint + chocolate ink. Re-implement on .taf-mosaic__rank if a
   labelled variant is ever needed; do not resurrect a second badge component. */
.taf-scrimhero__herocap { position: absolute; left: 0; right: 0; bottom: 0; z-index: var(--z-2); padding: var(--space-24); }
.taf-scrimhero__herotitle { display: block; font-family: var(--display); font-size: var(--fs-30); line-height: var(--lh-115); color: var(--color-on-scrim); }
.taf-scrimhero__herometa { display: block; margin-top: var(--space-4); font-size: var(--fs-15); color: var(--color-on-scrim); opacity: var(--o-9); }

/* ---- Section 7 collections ---- */
.taf-collections { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-32) var(--pad) var(--space-48); }
.taf-coll { display: block; background: var(--cream); border-radius: var(--radius); padding: var(--space-16); text-decoration: none; color: inherit; transition: box-shadow var(--speed) var(--ease); }
.taf-coll:hover { box-shadow: var(--shadow-hover); }
.taf-coll__montage { display: grid; grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; gap: var(--space-8); aspect-ratio: var(--ar-16-10); }
/* The montage cell is the clipping box; the photo inside is a real <img> (the cell is 187x206
   on desktop and an 88px square thumb on phones — one background-image file cannot serve
   both). Absolute fill, so the img cannot influence the grid tracks. */
.taf-coll__m { position: relative; border-radius: var(--radius-sm); overflow: hidden; }
.taf-coll__m > img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
.taf-coll__m--tall { grid-row: span 2; }
/* The "N recipes" count eyebrow was removed from both collection patterns
   2026-08-05, so `.taf-coll__eyebrow` no longer exists in any pattern or in
   live post_content (only in four old recipe-index REVISIONS, which never
   render). Its base rule and its mobile grid placement are gone with it. The
   counts were accurate, not stale — they were dropped as a design call. */
/* An <h3> (subordinate to the section's <h2>); the .entry-content scope + these
   explicit values beat Kadence's .entry-content h3 defaults (size/weight/margin). */
.entry-content .taf-coll__title { display: block; font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-20); line-height: var(--lh-115); margin: var(--space-4) 0 0; }
/* ONE line on desktop, hard-clamped. The copy is written to fit (longest is 46
   characters, ~56 fit on a line in the 348px card), so the clamp is a guard
   against future copy, not the mechanism — an ellipsis mid-sentence is a bug,
   not a design. Keep new descriptions under ~50 characters. */
.taf-coll__meta {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; line-clamp: 1;
  overflow: hidden; font-size: var(--fs-13); color: var(--cocoa); margin-top: var(--space-4);
}
.taf-coll__cta { display: none; } /* desktop collage cards show the description; the CTA is the mobile row affordance */

/* ---- Section 8 email magnet band (Kit form restyle) ---- */
/* Direct token bg — do NOT rely on the `has-rose-tint-background-color` preset:
   --wp--preset--color--rose-tint is not reliably emitted (page-cache dependent). */
/* 2-column, LEFT-ALIGNED e-book magnet on the rose-tint ground: left column =
   eyebrow + headline + pitch; right column = the Kit form + fine print. Stacks to
   one column on mobile. */
.taf-magnetband { padding: var(--space-24) var(--pad); text-align: left; background-color: var(--rose-tint); }
.taf-magnetband__inner { max-width: 1080px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-48); align-items: center; }
/* Kadence hangs a 32px block-gap margin on every child of a wp:group, and this
   inner IS a wp:group — so its first grid item carried margin-bottom:32px into
   a grid that already spaces itself with `gap`. Centred alignment then pushed
   that 32px below the tallest column, which read as the band having more
   bottom padding than top: MEASURED 24px above the content and 69.8px below it.
   It only became visible when the "No spam, ever" line was removed on
   2026-08-04 — that paragraph had been filling the dead space.
   The band's own padding is symmetric and always was; this is the leak, not the
   padding. Same neutraliser used elsewhere in this file for the same cause.

   BOTH selectors are required and the unscoped one alone does NOT work: the
   first attempt was `.taf-magnetband__inner > *` on its own and it measured no
   change, because Kadence's rule is `.entry-content`-scoped at (0,2,0). Same
   doubled form the .taf-abt neutraliser uses further up this file.

   !important is load-bearing: the leak arrives as the `margin-block`
   SHORTHAND (computed `0px 32px`), which is why a plain `margin-bottom: 0`
   lost and why a rule-by-rule scan for `margin-bottom` found nothing matching
   the element at all. */
.entry-content .taf-magnetband__inner > *,
.taf-magnetband__inner > *,
.entry-content .taf-introcard__card > *,
.taf-introcard__card > * { margin-top: 0 !important; margin-bottom: 0 !important; }
.taf-magnetband__text, .taf-magnetband__aside { align-self: center; } /* balance the two columns vertically */
.taf-magnetband__text > :first-child { margin-top: 0; }
.taf-magnetband__aside > :last-child, .taf-magnetband__form :last-child { margin-bottom: 0; }
.taf-magnetband .taf-eyebrow { color: var(--rose-deep); }
.taf-magnetband__title { font-family: var(--display); font-weight: var(--w-bold); font-size: var(--fs-36); line-height: var(--lh-112); color: var(--chocolate); max-width: 22ch; margin: var(--space-8) 0 var(--space-12); text-align: left; }
.taf-magnetband__pitch { font-family: var(--sans); font-size: var(--fs-17); max-width: 46ch; margin: 0; color: var(--cocoa); text-align: left; }
.taf-magnetband__aside { display: block; }
.taf-magnetband__form { max-width: none; margin: 0; }
/* ConvertKit inline-form markup → design look */
.taf-magnetband__form .formkit-form,
.taf-magnetband__form form { max-width: none; margin: 0; padding: 0; background: transparent; border: none; box-shadow: none; }
/* Fields wrap: name + email share the first row; the submit (a sibling of the fields inside
   .formkit-fields) is forced onto its own full-width row via flex-basis:100%. */
.taf-magnetband__form .formkit-fields,
.taf-magnetband__form [data-element="fields"],
.taf-magnetband__form .seva-fields { display: flex; gap: var(--space-8); flex-wrap: wrap; margin: 0; }
/* name + email share ONE row (50/50); !important beats Kit's own full-width field CSS */
.taf-magnetband__form .formkit-field { flex: 1 1 calc(50% - 5px) !important; min-width: 0 !important; margin: 0 !important; }
/* radius uses !important: Kit prints its own inline <style> (in-body, so it wins
   the cascade tie) forcing 4px on the input + button. */
.taf-magnetband__form .formkit-input,
.taf-magnetband__form input[type="email"],
.taf-magnetband__form input[type="text"] { width: 100%; height: 52px; border-radius: var(--radius-sm) !important; border: var(--bw-15) solid var(--line); background: var(--paper); padding: 0 var(--space-16); font-size: var(--fs-16); box-shadow: none; }
.taf-magnetband__form .formkit-submit,
.taf-magnetband__form button[type="submit"],
.taf-magnetband__form button { flex: 1 1 100% !important; width: 100% !important; height: 50px; background: var(--rose); color: var(--color-on-accent); font-size: var(--fs-16); font-weight: var(--w-bold); padding: 0 var(--space-20); border-radius: var(--radius-sm) !important; border: none; cursor: pointer; white-space: nowrap; transition: background var(--speed) var(--ease); }
.taf-magnetband__form .formkit-submit:hover,
.taf-magnetband__form button:hover { background: var(--rose-deep); }
.taf-magnetband__form .formkit-submit > div,
.taf-magnetband__form .formkit-submit span { margin: 0; color: var(--color-on-accent); }
.taf-magnetband__form .formkit-header,
.taf-magnetband__form .formkit-subheader,
.taf-magnetband__form .formkit-guarantee,
.taf-magnetband__form [data-element="header"],
.taf-magnetband__form [data-element="subheader"],
.taf-magnetband__form [data-element="guarantee"],
.taf-magnetband__form .formkit-powered-by-convertkit-container,
.taf-magnetband__form .formkit-powered-by-convertkit { display: none !important; }

/* ======================
   SEASONAL HERO — full-width cream band: 1 feature + 4 recipes.
   REUSES the .taf-mosaic feature+rows module; every size change is scoped to .taf-seahero so
   the Trending module (same classes, further down) is untouched. No new tokens.
   Rotating it per season = swap the pattern's copy, badge, CTA and 5 recipes; layout, band
   colour and type scale stay fixed.
   ====================== */
.taf-seahero { border-bottom: var(--bw-1) solid var(--line); }
/* The alignfull background wrapper is zero-padded by the .has-background reset
   above; this restores the band's own padding at the same specificity. */
.entry-content .wp-block-group.alignfull.has-background.taf-seahero { padding: var(--space-48) var(--pad) var(--space-48); }
.taf-seahero__inner { max-width: var(--maxw-wide); margin: 0 auto; }
/* The head is NOT a single stacked column: the headline spans, then the intro sits
   LEFT and the CTA RIGHT on one line. Stacking them all left leaves the right half
   of the band empty. */
.taf-seahero__head { margin: 0 0 var(--space-24); }
.taf-seahero__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-32); flex-wrap: wrap;
}
.taf-seahero__row .taf-seahero__cta { flex: none; }
/* .taf-seahero__eyebrow is RETIRED ("remove the breadcrumb from
   the hero" — patterns/summer-recipes.php). Rules kept, unused, because the
   seasonal slot is a rotating template and a future Diwali/Christmas copy of
   the pattern must NOT quietly re-inherit a styleless eyebrow if someone puts
   one back; deleting them would hide that decision. Do not reintroduce the
   element. */
.taf-seahero__eyebrow { display: flex; align-items: center; gap: var(--space-8); margin: 0; }
.taf-seahero__eyebrow svg { width: 16px; height: 16px; flex-shrink: 0; }
/* border-bottom/padding are zeroed deliberately: Kadence decorates content
   headings with a 1px #EEE underline, which drew a stray hairline between this
   headline and its intro line. */
.entry-content .taf-seahero__title,
.taf-seahero__title {
  font-family: var(--display); font-size: var(--fs-hero); font-weight: var(--w-bold);
  line-height: var(--lh-108); letter-spacing: var(--ls-display);
  /* Top margin was --space-8, which only ever existed to sit the headline under
     the eyebrow. The eyebrow is gone (2026-08-04), so the headline now opens
     the band and the band's own 48px padding is the whole gap. */
  margin: 0 0 var(--space-12); text-align: left;
  border-bottom: none; padding-bottom: 0;
}
.taf-seahero__title .taf-accent { color: var(--accent); }
/* .entry-content wins the margin: Kadence gives content <p> a 32px bottom margin, and flexbox
   centres the MARGIN box — so that phantom 32px shoved the CTA below the intro's optical
   centre. */
/* No max-width: the subtitle is written to run on ONE line beside the link (a ch
   cap is what forced it to wrap, not the copy length). */
.entry-content .taf-seahero__intro,
.taf-seahero__intro { font-size: var(--fs-20); line-height: var(--lh-155); color: var(--text-muted); margin: 0; }
/* The CTA is the same rose underlined link every 4-recipe band uses at its
   top-right (.taf-more), not a button — the hero photos carry the section. */
.entry-content .taf-seahero__cta,
.taf-seahero__cta { flex: 0 0 auto; margin: 0; }

/* Feature card: taller, bigger overlay title, lifted. */
.taf-seahero .taf-scrimhero__hero { min-height: 540px; box-shadow: var(--shadow); }
.taf-seahero .taf-scrimhero__herotitle { font-size: var(--fs-42); line-height: var(--lh-11); }
.taf-seahero .taf-scrimhero__herometa { font-size: var(--fs-15); }

/* The four supporting recipes are a 2x2 grid of PHOTO CARDS. Titles are held to a two-line box
   so all four cards line up whatever the recipe is called. */
.taf-seahero__cards { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-20); }
.taf-scard {
  display: flex; flex-direction: column;
  background: var(--paper); border: var(--bw-1) solid var(--line); border-radius: var(--radius);
  overflow: hidden; text-decoration: none; color: inherit;
  transition: box-shadow var(--speed) var(--ease);
}
.taf-scard:hover { box-shadow: var(--shadow-hover); }
/* real <img>; object-fit:cover reproduces the background-size:cover crop */
.taf-scard__img { display: block; width: 100%; aspect-ratio: var(--ar-4-3); object-fit: cover; flex: none; }
.taf-scard__body { padding: var(--space-12) var(--space-16) var(--space-16); }
.taf-scard__title {
  display: block; font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-17);
  line-height: var(--lh-125); color: var(--text-body);
  /* two-line box: the four cards align even when a title runs short */
  min-height: calc(2 * 1.25em);
}
/* .taf-scard__meta (the time row) retired 2026-07-17: the photo gets
   the breathing space. Title keeps its two-line box so cards still align. */

@media (max-width: 899.98px) {
  .entry-content .wp-block-group.alignfull.has-background.taf-seahero { padding: var(--space-32) var(--pad) var(--space-32); }
  .taf-seahero__head { margin-bottom: var(--space-20); }
  .taf-seahero__row { flex-direction: column; align-items: flex-start; gap: var(--space-16); }
  .taf-seahero__intro { font-size: var(--fs-17); }
  .taf-seahero .taf-scrimhero__hero { min-height: 320px; }
  .taf-seahero .taf-scrimhero__herotitle { font-size: var(--fs-30); }
  .taf-seahero__cards { gap: var(--space-12); }
  .taf-scard__body { padding: var(--space-12) var(--space-12) var(--space-12); }
  .taf-scard__title { font-size: var(--fs-15); }
}

/* ======================
   SEASONAL SPOTLIGHT — split card: cream copy cell + full-bleed photo. Hand-curated: the
   owner swaps the pattern per season. Colours come from the token layer.
   ====================== */
.taf-spotlight-sec { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-48) var(--pad) var(--space-8); }
.taf-spotlight {
  display: grid; grid-template-columns: 0.92fr 1.08fr;
  border: var(--bw-1) solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden; background: var(--paper);
}
.taf-spotlight__copy {
  background: var(--cream); padding: var(--space-48) var(--space-40);
  display: flex; flex-direction: column; justify-content: center; align-items: flex-start;
}
.taf-spotlight__copy .taf-eyebrow { color: var(--rose-deep); }
/* Two-class selector is deliberate: a global content-heading rule centres h2s, which left this
   title centred while its eyebrow and blurb sat left. A single class loses to it. */
.taf-spotlight__copy .taf-spotlight__title,
.entry-content .taf-spotlight__title { text-align: left; }
.taf-spotlight__title { font-size: var(--fs-42); line-height: var(--lh-115); margin: var(--space-8) 0 var(--space-12); }
.taf-spotlight__blurb { font-size: var(--fs-17); color: var(--text-muted); margin: 0 0 var(--space-20); max-width: 44ch; }
.taf-spotlight__pills { display: flex; flex-wrap: wrap; gap: var(--space-8); margin: 0 0 var(--space-24); padding: 0; list-style: none; }
.taf-spotlight__pill {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex; align-items: center; min-height: var(--space-44); /* 44px tap target (was 36px — sanctioned bump, 2026-07-17) */
  background: var(--paper); border: var(--bw-1) solid var(--line); border-radius: var(--radius-99);
  padding: var(--space-8) var(--space-14); font-family: var(--display); font-size: var(--fs-15);
  color: var(--text-body); text-decoration: none;
  transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.taf-spotlight__pill:hover { border-color: var(--accent); color: var(--accent); }
.taf-spotlight__media { position: relative; min-height: 420px; }
.taf-spotlight__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- Section 9 mithai ---- */
/* ---- Feature band: one big scrim hero card + two standard tiles. The shape is
   generic and shared by the mithai and mango patterns — the topic is not. ---- */
.taf-featband { max-width: var(--maxw-wide); margin: 0 auto; padding: var(--space-48) var(--pad) var(--space-48); }
.taf-featband__grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--space-20); align-items: stretch; }
.taf-featband__hero { position: relative; display: block; min-height: 0; border-radius: var(--radius); overflow: hidden; text-decoration: none; color: inherit; }
.taf-featband__heroimg { position: absolute; inset: 0; width: 100%; height: 100%; transition: transform var(--dur-35) var(--ease); }
/* Hover zoom: .taf-featband__hero is the overflow:hidden rounded box; the img
   layer scales inside it and crops within bounds (scrim/caption stay put). */
@media (hover: hover) and (pointer: fine) {
  .taf-featband__hero:hover .taf-featband__heroimg { transform: scale(1.04); }
}
.taf-featband__scrim { position: absolute; inset: 0; background: var(--scrim-featband); }
.taf-featband__herocap { position: absolute; left: 0; right: 0; bottom: 0; z-index: var(--z-2); padding: var(--space-24); }
.taf-featband__eyebrow { display: block; color: var(--color-on-scrim); font-size: var(--fs-12); font-weight: var(--w-bold); letter-spacing: var(--ls-14em); text-transform: uppercase; opacity: var(--o-85); }
.taf-featband__herotitle { display: block; font-family: var(--display); font-size: var(--fs-24); line-height: var(--lh-115); color: var(--color-on-scrim); margin-top: var(--space-4); }
.taf-featband__herometa { display: block; margin-top: var(--space-4); font-size: var(--fs-15); color: var(--color-on-scrim); opacity: var(--o-85); }

/* ---- Section 10 footer ---- */
.taf-sitefooter { padding: var(--space-40) var(--pad); }
.taf-sitefooter__inner { max-width: var(--maxw-wide); margin: 0 auto; display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--space-40); }
.taf-sitefooter__wordmark { font-family: var(--display); font-size: var(--fs-20); color: var(--milk); margin: 0; }
.taf-sitefooter__blurb { font-size: var(--fs-15); color: var(--milk); opacity: var(--o-75); max-width: 30ch; margin: var(--space-8) 0 0; }
.taf-sitefooter__col { display: grid; gap: var(--space-8); align-content: start; }
.taf-sitefooter__label { font-size: var(--fs-12); font-weight: var(--w-bold); letter-spacing: var(--ls-14em); text-transform: uppercase; color: var(--milk); opacity: var(--o-6); margin: 0 0 var(--space-4); }
.taf-sitefooter__col a { color: var(--milk); font-size: var(--fs-15); text-decoration: none; }
.taf-sitefooter__col a:hover { color: var(--milk); text-decoration: underline; }

/* ======================
   HOME — WP / Feast / Kadence counter neutralizers.
   The home is core/Kadence blocks inside .entry-content.single-content, so four global
   rule-sets out-specify the (0,1,0) component rules above:
     RC1 .entry-content .wp-block-group.has-background{padding:20px 30px} (0,3,0)
     RC2 Feast #feast-homepage-styling-* .entry-content h2 (0,1,1): hairline + 17px
          padding-bottom + centered + its own margin
     RC3 Kadence block spacing (0,1,1)/(0,2,0): margin-top:0, margin-bottom:32px
     RC4 the inc/enqueue.php counter: h1..h6{line-height:var(--lh-display)!important}
   Each rule below restores the intended value at just-enough specificity (!important only
   where an !important counter demands it).
   ====================== */

/* RC1 — band padding (base = desktop; <900 variants in the mobile layer below).
   0,5,0 / 0,4,0 beat WP has-background (0,3,0) and the rose-milk reset (0,4,0). */
.entry-content .wp-block-group.alignfull.has-background.taf-catband { padding: var(--space-40) var(--pad) var(--space-48); }
.entry-content .wp-block-group.has-background.taf-magnetband { padding: var(--space-24) var(--pad); }
/* Mango row (patterns/mango-row.php) — a normal 4-up tile row wearing the
   related-recipes band's tint. The tint is full-bleed so the band takes NO
   horizontal padding; the gutter belongs to the inner, exactly as it does in
   .taf-baking. 40px top matches .taf-catband's opening; 40px bottom because a
   tinted band has to close itself (the untinted .taf-baking rows can get away
   with 8px, since the next band's own top padding does the work and there is no
   visible edge). The tile grid carries no bottom margin here — measured, 0px —
   so this padding IS the whole gap under the tiles. */
.entry-content .wp-block-group.alignfull.has-background.taf-mangoband { padding: var(--space-40) 0 var(--space-40); }
/* The inner supplies the shared measure AND the page gutter. Both values are
   the ones .taf-baking uses (--maxw-wide + --pad), which is the point: verified
   rendered at 1280, this row's tile grid starts at x=72 and ends at x=1208 —
   the same pixels as the No-bake, Savoury, Breads and Cakes grids around it.
   Before the gutter moved off the band and onto the inner it started at x=40,
   a visible 32px overhang against every neighbouring row. */
.taf-mangoband__inner { max-width: var(--maxw-wide); margin: 0 auto; padding: 0 var(--pad); }
.entry-content .wp-block-group.taf-mangoband__inner { margin-top: 0; margin-bottom: 0; }
/* The section-heading hairline is --rose-tint #FCE7F3 (the h2 rule at ~2122),
   which is chosen to read against the milk page ground. On this band's --cream
   #FAEDF2 ground it is the same colour and disappears — the border MEASURES
   present (2px solid rgb(252,231,243), verified in the browser) while painting
   nothing, so "Mango season" lost the rule its immediate neighbour "No-bake
   treats" still shows, four inches down the same page. --line is the house
   hairline and is visible on cream at the same weight of subtlety the rose-tint
   has on milk. Scoped to this band only; the category band has the same
   invisible-hairline condition and predates this, so it is left alone rather
   than changed under cover of a different task. */
.entry-content .taf-mangoband .taf-secthead h2 { border-bottom-color: var(--line); }

/* RC2 — Feast h2 counter: drop the hairline + padding, restore margin/text-align. */
.entry-content .taf-secthead h2,
.entry-content .taf-catband__title,
.entry-content .taf-introcard__body h2,
.entry-content .taf-magnetband__title { padding-bottom: 0; border-bottom: none; }
.entry-content .taf-secthead h2 { margin: 0; text-align: left; }
.entry-content .taf-catband__title { margin: 0; text-align: center; }
.entry-content .taf-introcard__body h2 { margin: 0 0 var(--space-8); text-align: left; }
.entry-content .taf-magnetband__title { margin: var(--space-8) 0 var(--space-12); text-align: left; }

/* RC3 — restore the margins the Kadence block spacing zeroed/inflated. */
.entry-content .taf-tilecard__title { margin: var(--space-8) 0 0; }
.entry-content .taf-tilecard__meta  { margin: var(--space-4) 0 0; }
.entry-content .taf-popular { margin: var(--space-8) 0 0; }
/* Kill the 32px block-spacing margin Kadence puts under the search FORM — it was
   the real source of the wide search→"Popular:" gap (the popular row's own 10px top
   margin now governs). */
.entry-content .taf-searchwrap form.taf-searchbar { margin-bottom: 0; }
.entry-content .taf-magnetband__pitch { margin: 0; }
.entry-content .wp-block-group.taf-magnetband__form { margin: 0; }
.entry-content .wp-block-group.taf-secthead { margin-bottom: var(--space-20); }

/* ======================
   HOME — mobile (< 900px)
   ====================== */
@media (max-width: 899.98px) {
  /* RC1 mobile band padding (out-specify the same WP block rules). */
  .entry-content .wp-block-group.alignfull.has-background.taf-catband { padding: var(--space-16) var(--pad) var(--space-16); margin-top: 0; } /* also kills the legacy rose-milk 28px margin-top */
  .entry-content .wp-block-group.has-background.taf-magnetband { padding: var(--space-24) var(--pad); }

  .taf-secthead h2 { font-size: var(--fs-24); }
  /* stack the section heading over its "more" link so a long link no longer squeezes
     the H2 into a 2-line wrap. */
  .taf-secthead { flex-direction: column; align-items: flex-start; gap: var(--space-4); }
  .taf-catband__title { font-size: var(--fs-24); } /* match the sibling H2s */
  /* drive the heading→circles gap from the title's margin (the catrow's own
     margin-top is zeroed by a WP/Kadence rule on mobile) */
  .entry-content .taf-catband__title { margin-bottom: var(--space-24); }

  /* Hero responsive rules live in the dedicated hero media blocks near the
     .taf-rhero base rules, so the hero can stay 2-column on tablet and stack only
     below 700px. */

  /* category band: circles wrap into centered rows (no horizontal scroller) */
  .taf-catband { padding: var(--space-16) var(--pad) var(--space-16); } /* effective value set by the .has-background rule above */
  .taf-cat__label { font-size: var(--fs-13); }
  .taf-searchwrap { margin-top: var(--space-24); }
  .taf-searchbar { height: 52px; }

  /* tile grids */
  .taf-baking { padding: var(--space-16) var(--pad) var(--space-4); }
  .entry-content .wp-block-group.alignfull.has-background.taf-mangoband { padding: var(--space-24) 0 var(--space-24); }
  .taf-tilegrid--4 { grid-template-columns: 1fr 1fr; gap: var(--space-16); }
  .taf-tilegrid--3 { grid-template-columns: 1fr; gap: var(--space-16); }

  /* intro card: compact */
  .taf-introcard { padding: var(--space-24) var(--pad) var(--space-4); }
  .taf-introcard__card { gap: var(--space-16); padding: var(--space-16) var(--space-20); flex-wrap: wrap; }
  .taf-introcard__avatar, .taf-introcard__avatar img { width: 76px; height: 76px; }
  .taf-introcard__body h2 { font-size: var(--fs-20); }
  .taf-introcard__link { flex-basis: 100%; }

  /* Trending: the mosaic collapses. Same five tiles, same template — the grid
     just re-places them: the #1 hero takes a full-width row of its own, tiles
     2-5 sit under it as a 2x2. The container's aspect-ratio is released and each
     tile locks its OWN ratio instead, so the band still reserves its height
     before the photos load (no CLS). The 3-col mosaic is restored from 700px up
     in the tablet block below. */
  .taf-mosaic { padding: var(--space-24) var(--pad) var(--space-24); }
  /* Stacking comes from .taf-scrimhero__grid's own 1fr mobile rule (shared with
     the seahero); the hero's height mirrors the seahero's mobile 320px. */
  .taf-mosaic__layout .taf-mosaic__tile--hero { min-height: 320px; }
  /* 6/5 ≈ 292px tall on a 390px phone — the height the old hero card carried. */
  .taf-mosaic__tile--hero .taf-mosaic__cap { padding: var(--space-16); }
  .taf-mosaic__tile--hero .taf-mosaic__title { font-size: var(--fs-24); }
  .taf-mosaic__cap { padding: var(--space-12); }
  .taf-mosaic__title { font-size: var(--fs-15); }

  /* most loved / seasonal hero feature card: single column */
  .taf-scrimhero__grid { grid-template-columns: 1fr; gap: var(--space-16); }

  /* Seasonal spotlight: stack, photo first (a 420px-tall photo under a wall of copy
     reads as an afterthought on a phone). */
  .taf-spotlight-sec { padding: var(--space-24) var(--pad) var(--space-4); }
  .taf-spotlight { grid-template-columns: 1fr; }
  .taf-spotlight__media { min-height: 260px; order: -1; }
  .taf-spotlight__copy { padding: var(--space-24) var(--space-20) var(--space-24); }
  .taf-spotlight__title { font-size: var(--fs-30); }
  .taf-scrimhero__hero { min-height: 300px; }

  /* collections: stacked cards */
  .taf-collections { padding: var(--space-24) var(--pad) var(--space-16); }
  /* collections collapse to compact horizontal rows on mobile: a single square thumb
     (collage reduced to its lead image) + eyebrow / title / CTA, replacing the tall
     collage cards. */
  .taf-coll { display: grid; grid-template-columns: 88px 1fr; column-gap: var(--space-12); row-gap: var(--space-2); align-content: center; padding: var(--space-12); }
  .taf-coll__montage { grid-column: 1; grid-row: 1 / span 3; align-self: center; display: block; width: 88px; height: 88px; aspect-ratio: var(--ar-1); }
  .taf-coll__montage .taf-coll__m { display: none; }
  .taf-coll__montage .taf-coll__m--tall { display: block; width: 100%; height: 100%; border-radius: var(--radius-sm); }
  /* Two rows, not three: the count eyebrow was removed from BOTH collection
     patterns, so the card is thumb + title + CTA. Rows are assigned explicitly,
     so a stale `grid-row: 1` here would reserve an empty row and its row-gap. */
  .entry-content .taf-coll__title { grid-column: 2; grid-row: 1; font-weight: var(--w-semibold); font-size: var(--fs-17); margin: 0; }
  /* The description used to be display:none here — the phone card showed a
     title and a CTA and nothing that said what the collection actually was.
     It is back, clamped to TWO lines: the text column is ~266px wide beside
     the 88px thumb, so ~43 characters fit per line and the current copy needs
     one or two. Row 2 also closes the empty grid row left when the count
     eyebrow was removed (title 1 / meta 2 / CTA 3, montage spanning all three). */
  .taf-coll__meta {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
    overflow: hidden; grid-column: 2; grid-row: 2; margin: var(--space-2) 0 0;
    font-size: var(--fs-13); line-height: var(--lh-14); color: var(--cocoa);
  }
  /* --rose-deep, not --rose: this 14px CTA sits on the cream card, where --rose
     measures 3.98:1 — under the 4.5:1 AA floor for text this size. It only shows on
     phones, so desktop-only contrast checks never catch it. */
  .taf-coll__cta { display: block; grid-column: 2; grid-row: 3; margin: var(--space-2) 0 0; font-family: var(--display); font-weight: var(--w-semibold); font-size: var(--fs-15); color: var(--rose-deep); }

  /* magnet: stacked field + full-width button */
  .taf-magnetband { padding: var(--space-24) var(--pad); }
  /* single column, top-aligned (no vertical-centering gap), tight gap; and kill the
     ~56px of dead space the Kit form reserves above its fields. */
  .taf-magnetband__inner { grid-template-columns: 1fr; gap: var(--space-16); align-items: start; }
  .taf-magnetband__text, .taf-magnetband__aside { align-self: start; }
  .taf-magnetband__text > :last-child { margin-bottom: 0; }
  .taf-magnetband__form .formkit-form,
  .taf-magnetband__form .formkit-form > div { padding-top: 0 !important; }
  .taf-magnetband__title { font-size: var(--fs-24); font-weight: var(--w-semibold); } /* the only 700 heading → 24/600 */
  .taf-magnetband__form .formkit-fields,
  .taf-magnetband__form [data-element="fields"],
  .taf-magnetband__form .seva-fields { flex-direction: column; }
  .taf-magnetband__form .formkit-submit,
  .taf-magnetband__form button { width: 100%; }

  /* mithai: hero full width, two tiles below */
  .taf-featband { padding: var(--space-20) var(--pad) var(--space-24); }
  .taf-footer { padding-top: var(--space-24); padding-bottom: var(--space-48); } /* trim the 90px sticky-ad bottom clearance on mobile */
  /* compact mobile footer: brand full-width, link columns in a 2-col grid instead of
     one tall stack. */
  .taf-footer__grid { grid-template-columns: 1fr 1fr; gap: var(--space-24) var(--space-20); }
  .taf-footer__brand { grid-column: 1 / -1; }
  .taf-footer__col a { padding: var(--space-2) 0; } /* DELIBERATE WAIVER: 44px floor waived for footer links (see the base rule); AA 24px clears via line + padding */
  .taf-featband__grid { grid-template-columns: 1fr 1fr; }
  .taf-featband__hero { grid-column: 1 / -1; min-height: 280px; }

  /* footer: compact */
  .taf-sitefooter__inner { grid-template-columns: 1fr 1fr; gap: var(--space-24); }
  .taf-sitefooter__brand { grid-column: 1 / -1; }
}

/* ---- Tablet (700-899px): most-loved, collections and footer behave like the
   desktop layout rather than the phone layout. This block FOLLOWS the ≤899px block
   so it wins in the tablet range. ---- */
@media (min-width: 700px) and (max-width: 899.98px) {
  /* Trending: the 3-col mosaic fits from 700px up — restore it (same move the
     mithai featband makes below). Type steps down: a 1fr tile is only ~159px
     wide at 700px, where a 17px title wraps to three lines. */
  /* Tablet: .taf-scrimhero__grid's own 1.15fr/1fr tablet rule places the band;
     hero height mirrors the seahero's tablet 360px. */
  .taf-mosaic__layout .taf-mosaic__tile--hero { min-height: 360px; }
  .taf-mosaic__cap { padding: var(--space-12); }
  .taf-mosaic__title { font-size: var(--fs-15); }
  .taf-mosaic__meta { font-size: var(--fs-12); }
  .taf-mosaic__tile--hero .taf-mosaic__cap { padding: var(--space-16); }
  .taf-mosaic__tile--hero .taf-mosaic__title { font-size: var(--fs-24); }

  /* seasonal hero feature card (desktop 2-column) */
  .taf-scrimhero__grid { grid-template-columns: 1.15fr 1fr; gap: var(--space-24); }
  .taf-scrimhero__hero { min-height: 360px; }

  /* collections: desktop 3-up collage cards (revert the phone horizontal rows) */
  .taf-tilegrid--3 { grid-template-columns: repeat(3, 1fr); gap: var(--space-20); }
  .taf-coll { display: block; padding: var(--space-16); }
  .taf-coll__montage { display: grid; grid-template-columns: 1.4fr 1fr; grid-template-rows: 1fr 1fr; gap: var(--space-8); aspect-ratio: var(--ar-16-10); width: auto; height: auto; }
  .taf-coll__montage .taf-coll__m { display: block; }
  .entry-content .taf-coll__title { display: block; grid-column: auto; grid-row: auto; font-size: var(--fs-17); margin: var(--space-8) 0 0; }
  /* Tablet restores the collage card, so the description goes back to its own
     row rather than the phone grid slot — but it gets TWO clamped lines, not
     the desktop one. This band's card is the narrowest place the description
     ever renders (measured: 174.7px of text box at 700, vs 365px at 1280), so
     a single line silently truncated mid-sentence. Measured at 760: "Eleven
     ways with the little…", "Crackers, muffins and small…"; three recipe-index
     cards (Pantry essentials / Weekend artisan breads / Mithai for beginners)
     clipped across the WHOLE band and had done since 0.71.0. Two lines clears
     all of them and matches what <700 already does.
     ⚠️ `scrollHeight === clientHeight` DOES NOT DETECT THIS. With -webkit-box
     + line-clamp, scrollHeight collapses to the clamped height and reports no
     overflow while text is visibly cut. To test, clone the node with the clamp
     removed and compare heights. Two verification passes read "clean" before
     this was caught. */
  .taf-coll__meta { display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; grid-column: auto; grid-row: auto; margin-top: var(--space-4); }
  .taf-coll__cta { display: none; }

  /* baking-right-now: single row of 4 cards (desktop) */
  .taf-tilegrid--4 { grid-template-columns: repeat(4, 1fr); gap: var(--space-16); }
  /* recipe tiles (recipe-index most-loved, about favorite bakes, bread band):
     desktop 4-up rows — matches the category circles' rhythm */
  .taf-grid-4 { grid-template-columns: repeat(4, 1fr); gap: var(--space-16); }
  /* mithai: single row — featured hero + two tiles (desktop) */
  .taf-featband__grid { grid-template-columns: 2fr 1fr 1fr; }
  .taf-featband__hero { grid-column: auto; min-height: 0; }

  /* email magnet: desktop 2-column (copy left, form right; name+email in a row) */
  .taf-magnetband { padding: var(--space-24) var(--pad); }
  .entry-content .wp-block-group.has-background.taf-magnetband { padding: var(--space-24) var(--pad); }
  .taf-magnetband__inner { grid-template-columns: 1fr 1fr; gap: var(--space-40); align-items: center; }
  .taf-magnetband__text, .taf-magnetband__aside { align-self: center; }
  .taf-magnetband__title { font-size: var(--fs-36); font-weight: var(--w-bold); }
  .taf-magnetband__form .formkit-fields,
  .taf-magnetband__form [data-element="fields"],
  .taf-magnetband__form .seva-fields { flex-direction: row; }

  /* category circles: desktop single row of 6 (sized to fit the tablet width) */
  .taf-catrow { flex-wrap: nowrap; gap: var(--space-16); }
  .taf-cat { width: calc((100% - 84px) / 6); }

  /* footer: logo left, 3 nav columns clustered right (per reference) */
  .taf-footer__grid { grid-template-columns: 1fr auto auto auto; column-gap: var(--space-24); row-gap: var(--space-20); align-items: start; }
  .taf-footer__brand { grid-column: 1; }
}

/* ======================
   HOME — desktop-only counter neutralizers (≥900px). Scoped to desktop because the mobile
   hero re-lays these elements as a centered flex column with its own margins/order — these
   restorations would clobber that.
   ====================== */
@media (min-width: 900px) {
  .entry-content .taf-rhero__rating { margin: 0 0 var(--space-12); }
  .entry-content .taf-rhero__lede   { margin: 0 0 var(--space-24); }
  /* Hero H1: margin needs 0,2,0 to beat block heading spacing; line-height needs
     !important to beat the enqueue.php heading counter. */
  .entry-content .taf-rhero__title { margin: var(--space-12) 0 var(--space-16); line-height: var(--lh-108) !important; }
  .entry-content .taf-catrow { margin-top: var(--space-24); }
}

/* ---- Form inputs → rose hairline + 6px radius ----
   (a) Recipe-post comment form (Kadence renders grey #cbd5e0 / 3px). */
.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"],
.comment-form textarea {
  border: var(--bw-15) solid var(--line) !important;
  border-radius: var(--radius-sm) !important;
}
/* (b) Kit / ConvertKit embeds. Kit's embed CSS forces a grey #e3e3e3 border + 4px
   radius inline, so !important is required. Scoped to formkit so WPRM inputs and
   the home searchbar are untouched. */
.formkit-form input,
.formkit-input,
.taf-magnet .formkit-form input,
.taf-magnet .formkit-input {
  border: var(--bw-15) solid var(--line) !important;
  border-radius: var(--radius-sm) !important;
}
/* Kit submit button → the display face + 6px radius. */
.formkit-form .formkit-submit,
.formkit-submit,
.taf-magnet .formkit-submit {
  font-family: var(--display) !important;
  border-radius: var(--radius-sm) !important;
}

/* ---- Mobile header icon buttons → 44x44 tap target ----
   Kadence's own toggles are padding-only. Keep the visual icon size; the min box
   just enlarges the hit area. */
.menu-toggle-open,
.search-toggle-open {
  min-width: 44px;
  min-height: 44px;
}
/* The hamburger's "Menu" text (Kadence's own .menu-toggle-label, fed by the
   theme-mod filter in inc/mobile-menu.php) is tablet-and-up furniture: phones
   keep the icon-only square. Shown + styled in the MOBILE MENU TABLET LAYER. */
#mobile-toggle .menu-toggle-label { display: none; }

/* (The About desktop card-thumbnail overrides — three !important rules on
   .taf-about-card figure.taf-about-card__thumb — were deleted 2026-08-04 with
   the v3 rebuild. They existed only to rescue the retired link-card component,
   which now has no consumer in any pattern or live post.) */

/* ======================
   RECIPE INDEX — "Browse by category" circles: 4 per row on desktop/tablet, 2 on phones.
   Fluid, capped at 235px. `.taf-catgrid .taf-cat` out-specifies the global ≤1024 .taf-cat
   width override that sizes the HOME band's 3-up rows.
   ====================== */
/* Group heading (Baking / Recipes by Course / …). An <h2> since the band's own
   heading was removed — see the pattern docblock; <h3> here would skip a level.
   Deliberately quieter than the other bands' 30px H2s: structural level and
   visual weight are separate decisions.
   Scoped with .entry-content because Kadence's content defaults otherwise win
   on size, weight and margin (a single-class rule LOSES to `.entry-content h2`
   — the trap that has bitten every new element inside .entry-content here).

   ⚠️ THE MARGINS ARE THE POINT, not decoration. Measured before this pass, the
   gap above the label and the gap below it were BOTH 16px, so it sat
   equidistant between the heading above and the circles below and bound to
   neither. 40/12 makes it belong to its own grid. Do not "tidy" these to a
   symmetric pair. */
/* ⚠️ THE SELECTOR IS FOUR CLASSES ON PURPOSE — do not shorten it. Two rules
   written when every h2 inside .taf-section was a BAND heading now also match
   these group headings, and both outrank a plain `.entry-content .taf-catgroup`
   (0,2,0):
     .page-id-42733 .taf-section h2.wp-block-heading  (0,3,1) -> font-size 30px
     .entry-content .taf-section h2                   (0,2,1) -> margin 4px 0
   Measured, not guessed: with the short selector these headings rendered 30px
   with 4px above and below. (0,4,1) beats both outright rather than relying on
   source order, which a future file reorganisation would silently break. */
.entry-content .taf-section h2.wp-block-heading.taf-catgroup {
  font-family: var(--display); font-size: var(--fs-20); font-weight: var(--w-semibold);
  line-height: var(--lh-125); margin: var(--space-40) 0 var(--space-12);
}
.entry-content .taf-section h2.wp-block-heading.taf-catgroup:first-of-type { margin-top: 0; }
@media (min-width: 900px) {
  .entry-content .taf-section h2.wp-block-heading.taf-catgroup { font-size: var(--fs-22); }
}

/* ---- Guides strip (round 8, G3b). Sits under the Baking group. Three
   reference ARTICLES, not recipes, so the shape is deliberately not a circle.
   ⚠️ Separation is carried by RULES, never a fill: an empty Raptive slot is
   1.044:1 from white and --cream is 1.105:1, so a tinted panel here would sit
   in the same contrast register as ad inventory and get skipped. See the
   pattern docblock. The link colour is --rose-deep, not --rose: at 14px on
   --milk, bare --rose measures under the 4.5:1 AA floor. ---- */
/* Label and links both sit LEFT, reading as one line — not label-left /
   links-right. Pushing the links to the far edge made them read as a separate
   utility row rather than as three things belonging to the label. */
.taf-guides {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-10) var(--space-24);
  margin: var(--space-20) 0 0;
  padding: var(--space-14) 0;
  border-top: var(--bw-1) solid var(--line);
  border-bottom: var(--bw-1) solid var(--line);
}
.taf-guides__k {
  font-family: var(--display); font-size: var(--fs-12); font-weight: var(--w-bold);
  letter-spacing: var(--ls-14em); text-transform: uppercase; color: var(--chocolate);
}
.taf-guides__links { display: flex; flex-wrap: wrap; gap: var(--space-8) var(--space-24); }
.taf-guides__links a {
  font-family: var(--display); font-size: var(--fs-14); font-weight: var(--w-semibold);
  color: var(--rose-deep); text-decoration: none;
  display: inline-flex; align-items: center; gap: var(--space-6);
}
.taf-guides__links a:hover, .taf-guides__links a:focus-visible { text-decoration: underline; }
.taf-guides__links em { font-style: normal; font-size: var(--fs-13); }
@media (max-width: 699.98px) {
  /* Stack: the label sits above its own list rather than competing for the row. */
  .taf-guides { flex-direction: column; align-items: flex-start; gap: var(--space-10); }
  .taf-guides__links { flex-direction: column; gap: var(--space-4); }
  /* token-frozen: 44px is the WCAG tap target, built here from min-height because
     the link is a 23px line of text. Stacked text links are the easiest place on a
     page to ship an under-sized target — constraint-check.py does not know this
     selector, so nothing would have caught it. Do not snap this onto a scale. */
  .taf-guides__links a { min-height: 44px; }
}

.taf-catgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-24) var(--space-24); justify-items: center; }
.taf-catgrid .taf-cat { width: 100%; max-width: 235px; }
.taf-catgrid .taf-cat__img { width: 100%; height: auto; aspect-ratio: var(--ar-1-1); }
@media (min-width: 900px) {
  /* labels scale with the bigger (235px vs 150px home) circles */
  .taf-catgrid .taf-cat__label { font-size: var(--fs-17); }
}
@media (max-width: 699.98px) {
  .taf-catgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-24) var(--space-16); }
}

/* ======================
   KADENCE SEARCH DRAWER → the site's search pill. Kadence's markup, CSS-only, scoped under
   #search-drawer.
   ====================== */
/* Translucent veil, not an opaque panel: the page stays
   faintly visible behind the search. Kadence's .drawer-overlay is a 40% BLACK
   scrim UNDERNEATH this — left in place it shows through the cream and reads
   muddy, so it is cleared and the cream is the only veil. The slight blur stops
   page text competing with the field; drop the backdrop-filter for a plain
   translucency. */
#search-drawer .drawer-overlay { background: transparent !important; }
#search-drawer .drawer-inner {
  background: var(--veil-82) !important;                          /* fallback */
  background: color-mix(in srgb, var(--bg-page) 82%, transparent) !important;
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
}
/* Kadence lays drawer-content out as a centered flex ROW (header.min.css) — go
   column so the heading stacks above the pill, and let the form take the full
   column. */
#search-drawer .drawer-inner .drawer-content { display: flex; flex-direction: column; align-items: stretch; justify-content: center; max-width: 680px; margin: 0 auto; padding: 0 var(--pad); }
#search-drawer .drawer-content::before {
  content: "What are we baking today?";
  display: block; text-align: center;
  font-family: var(--display); font-size: var(--fs-h2); font-weight: var(--w-semibold);
  line-height: var(--lh-display); letter-spacing: var(--ls-display);
  color: var(--text-body); margin-bottom: var(--space-16);
}
#search-drawer .search-form { position: relative; display: flex; align-items: center; gap: var(--space-8); width: 100%; background: var(--bg-surface); border: none; border-radius: var(--radius); height: 56px; padding: 0 var(--space-8) 0 var(--space-48); box-shadow: var(--shadow); }
/* Shadow-deepen alone is not a visible-focus indicator (WCAG 2.4.7) when the
   field suppresses its own ring — add the sitewide ring on the container. */
#search-drawer .search-form:focus-within { box-shadow: var(--shadow-hover); outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; } /* search = rose, see .taf-search rule */
#search-drawer .search-form label { flex: 1; min-width: 0; margin: 0; }
/* selector mirrors Kadence's `#search-drawer .drawer-inner input.search-field`
   (header.min.css) — equal specificity, we load later */
#search-drawer .drawer-inner input.search-field { width: 100%; height: auto; padding: 0; border: none !important; outline: none; background: transparent !important; box-shadow: none !important; font-family: var(--sans); font-size: var(--fs-17); color: var(--text-body); }
#search-drawer .drawer-inner input.search-field::placeholder { color: var(--text-muted); opacity: var(--o-1); }
/* The clear (×) button. Chrome/Safari draw a NATIVE blue glyph on every
   input[type=search] — off-palette in all three places we use one (the header
   drawer, the on-page search bar, the 404 box). Re-skin rather than hide: the
   affordance is worth keeping. -webkit-appearance:none strips the native glyph,
   then a mask paints our own in the palette so it can take a hover colour.
   Firefox draws no such button, so there is nothing to override there. */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
          appearance: none;
  width: 16px; height: 16px; margin-left: var(--space-8);
  cursor: pointer;
  background-color: var(--text-muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128 50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M205.66 194.34a8 8 0 0 1-11.32 11.32L128 139.31l-66.34 66.35a8 8 0 0 1-11.32-11.32L116.69 128 50.34 61.66a8 8 0 0 1 11.32-11.32L128 116.69l66.34-66.35a8 8 0 0 1 11.32 11.32L139.31 128Z'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: background-color var(--speed) var(--ease);
}
input[type="search"]::-webkit-search-cancel-button:hover { background-color: var(--rose-deep); }

/* :focus MUST be overridden too. Kadence ships
   `#search-drawer .drawer-inner input.search-field:focus { color: var(--color-on-accent) }` for its
   DARK drawer; ours is light, and the drawer auto-focuses the field on open — so
   without this the text and caret are white on white: the field looks empty even
   when it carries the query, and typing shows no caret. caret-color is set
   explicitly because it otherwise inherits `color`. */
#search-drawer .drawer-inner input.search-field,
#search-drawer .drawer-inner input.search-field:focus {
  color: var(--text-body);
  caret-color: var(--text-body);
  box-shadow: none !important;
}
/* magnifier: decorative, pinned inside the pill's left padding (the stock rules
   float it over the submit text) */
#search-drawer .drawer-inner .kadence-search-icon-wrap { position: absolute; left: 20px; right: auto; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--text-muted); pointer-events: none; }
#search-drawer .drawer-inner .kadence-search-icon-wrap svg { display: block; width: 18px; height: 18px; }
#search-drawer .drawer-inner .search-form .search-submit { position: static; width: auto; height: auto; background: var(--accent); color: var(--color-on-accent); font-family: var(--display); font-size: var(--fs-small); font-weight: var(--w-bold); padding: var(--space-12) var(--space-20); border: none; border-radius: var(--radius-sm); cursor: pointer; opacity: var(--o-1); transition: background var(--speed) var(--ease); }
#search-drawer .drawer-inner .search-form .search-submit:hover,
#search-drawer .drawer-inner .search-form .search-submit:focus-visible { background: var(--accent-hover); }
/* Kadence turns the magnifier white when the submit is hovered (sibling rule) —
   with the icon far left that flash is just noise; hold it steady */
#search-drawer .drawer-inner .search-submit[type="submit"]:hover ~ .kadence-search-icon-wrap { color: var(--text-muted); }
#search-drawer .search-toggle-close { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: var(--radius-round); color: var(--text-body); background: transparent; transition: color var(--speed) var(--ease), background var(--speed) var(--ease); }
#search-drawer .search-toggle-close:hover,
#search-drawer .search-toggle-close:focus-visible { color: var(--accent-hover); background: var(--rose-tint); }

/* ======================
   KADENCE MOBILE DRAWER (#mobile-drawer) → the full-screen Warm Oven menu sheet. Content = inc/mobile-menu.php
   via the kadence_before/after_mobile_navigation_popup hooks; Kadence's own nav
   between them is the "Site links" section, restyled below.
   Kadence's markup, CSS-only takeover. Measured on the rendered page /breads/ @390px,
   2026-07-17 (pre-restyle): panel #090C10 with #fff ink (header.min.css
   `.popup-drawer .drawer-inner` literals), rgba(255,255,255,.1) hairlines
   (kadence-global-inline-css), and nav links reading var(--global-palette8) —
   which the html:root bridge above remaps to --milk, i.e. near-invisible the
   moment the ground goes light. Every dark value here is a Kadence STYLESHEET
   rule, not an inline style, so the #mobile-drawer id (this sheet also loads
   after Kadence's) wins everywhere — no !important in this block.
   Ground is --milk (the comp's sheet; was --cream in the interim restyle).
   Contrast on --milk #FEFBFA (computed 2026-07-17): --chocolate 14.75:1,
   --rose-deep 6.19:1, --cocoa 6.23:1, --rose-link 5.05:1 — all AA.
   The .drawer-overlay KEEPS Kadence's 40% black scrim on purpose: the sheet is
   full-screen, but the overlay still covers overscroll rubber-band reveals.
   ====================== */
/* The open sheet must cover the sticky #masthead (z 99) — Kadence's own drawer
   z measured 49 on the rendered page, fine for a sidepanel that opens under the header,
   wrong for a full-screen sheet. --z-dropdown is the house chosen-to-win tier. */
#mobile-drawer.popup-drawer { z-index: var(--z-dropdown); }
/* Sheet plumbing: .drawer-inner becomes a full-width flex column and
   .drawer-content (NOT .drawer-inner, which Kadence scrolls) is the scroller —
   so the top bar and close button hold still and the CTA can pin to the bottom
   edge with position:sticky. transform:none neutralizes Kadence's sidepanel
   slide-in; the open motion is the taf-mm keyframes at the end of this block. */
#mobile-drawer .drawer-inner {
  position: relative;
  width: 100%;
  max-width: none;
  /* Pin the sheet to the drawer's viewport box. Without this the flex column
     GROWS to its content (measured 1178px in an 844px viewport, v0.29.0) and
     .drawer-content never overflows — so nothing scrolls, the sticky CTA never
     pins, and the bottom ~334px (socials + CTA) is unreachable. #mobile-drawer
     itself is viewport-sized, so 100% is the constraint. */
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: none;
  padding: 0;
  background: var(--milk);
  color: var(--chocolate); /* close bars + sub-toggles are color:inherit — visible ink flows from here */
}
#mobile-drawer .drawer-content {
  flex: 1 1 auto;
  min-height: 0;
  display: block; /* Kadence's content-valign flex would center-stack the sheet */
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-color: var(--cocoa) var(--milk); /* on-system rail for the sheet's own scroll */
  /* Kadence's own 25.5px sides → the comp's 18px gutter. Full-bleed children
     (top bar, pinned CTA) pull back out with matching -18px margins. */
  padding: 0 var(--space-18);
}

/* ---- Top bar: comp mirrors the closed header (logo left, 44px close right).
   The logo row is before-hook markup, sticky at the scroller's top; the close
   button stays Kadence's own .drawer-header/.drawer-toggle (its JS wiring and
   data-set-focus must not be rebuilt), lifted out of the flex flow and pinned
   over the bar's right end. ---- */
/* THE DRAWER'S THREE-LAYER STACK (read with the .taf-mm-search rule near the
   fade-up keyframes). All three live in ONE stacking context — .drawer-inner,
   which `animation: taf-mm-in … both` makes permanent — so their z-indexes are
   directly comparable and ties break on TREE ORDER:
     close button  .drawer-header  49  — must out-paint the opaque top bar, and
                                        .drawer-header is FIRST in .drawer-inner,
                                        so at a tie the later top bar would win
                                        and hide the X. Hence 49, not 2.
     sticky bar    .taf-mm-topbar   2  — must out-paint the scrolling content
                                        that slides under it, .taf-mm-search
                                        included (at 1 they tied and the search
                                        section, being later, covered the logo).
     search block  .taf-mm-search   1  — must out-paint its LATER siblings; see
                                        the long note at that rule. Cannot drop
                                        to 0: the siblings are stacking contexts
                                        at z-index auto, painted in tree order.
   Any edit here must keep 49 > 2 > 1 with .taf-mm-search still above 0. */
#mobile-drawer .drawer-header {
  position: absolute; top: 0; right: 0; z-index: var(--z-49);
  width: auto;
  padding: var(--space-12) var(--space-18);
}
#mobile-drawer .taf-mm-topbar {
  position: sticky; top: 0; z-index: var(--z-2);
  margin: 0 calc(-1 * var(--space-18));
  padding: var(--space-12) var(--space-64) var(--space-12) var(--space-18); /* right pad clears the pinned 44px close button */
  background: var(--milk);
  border-bottom: var(--bw-1) solid var(--line);
}
#mobile-drawer .taf-mm-topbar__home { display: inline-flex; align-items: center; min-height: var(--space-44); } /* 44px tap target (logo is 42px) */
#mobile-drawer .taf-mm-logo { display: block; width: auto; height: 42px; } /* token-exempt: comp — the top bar mirrors the closed mobile header's logo height */
/* Close button. Measured open (pre-restyle): 31.2x52.8 (Kadence pads .6em/.15em
   around two position:absolute bars) — 13px under the floor on the x-axis. */
#mobile-drawer .drawer-header .drawer-toggle {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  min-width: 44px; min-height: 44px;      /* tap target; the X bars stay .75em (18px) */
  align-items: center; justify-content: center;
  color: var(--chocolate); /* explicit, not just inherited: visible ink is a requirement here */
}
#mobile-drawer .drawer-header .drawer-toggle:hover,
#mobile-drawer .drawer-header .drawer-toggle:focus-visible { color: var(--rose-deep); }

/* ---- Search: the sheet leads with it (inc/mobile-menu.php's form keeps
   drawer-search.php's contract: GET to home_url('/'), name="s", sr-only label).
   Comp: paper box, rose border + soft rose glow, borderless input, 44px rose
   arrow submit. ---- */
#mobile-drawer .taf-mm-search { padding: var(--space-18) 0 var(--space-6); }
#mobile-drawer .taf-mm-search__box {
  display: flex; align-items: center; gap: var(--space-10);
  background: var(--paper);
  border: var(--bw-15) solid var(--rose);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-6) var(--space-4) var(--space-16);
  box-shadow: 0 6px 18px -8px rgba(225, 21, 132, .25); /* token-exempt: comp's rose glow — --rose (#E11584) as channels; no --rose-rgb channel token exists and one use doesn't earn one */
}
/* SEARCH focus rings are ROSE — see the .taf-search rule. */
#mobile-drawer .taf-mm-search__box:focus-within { outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; }
#mobile-drawer .taf-mm-search__box > .taf-mm-icon { color: var(--rose); flex-shrink: 0; }
#mobile-drawer .taf-mm-search__input {
  /* token-frozen: iOS Safari zooms an input whose font-size is under 16px
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  flex: 1; min-width: 0;
  border: none; outline: none; background: transparent; box-shadow: none;
  font-family: var(--sans); font-size: var(--fs-16); /* 16px stops iOS zoom */
  color: var(--text-body); caret-color: var(--text-body);
  padding: var(--space-10) 0;
}
#mobile-drawer .taf-mm-search__input::placeholder { color: var(--text-muted); }
#mobile-drawer .taf-mm-search__go {
  /* token-frozen: WCAG 2.5.5 — the 44px HEIGHT is the tap floor and must stay.
     T6 must NOT remap these values to a scale — tokenize at parity only.
     Width is `auto` since 2026-08-04: the button carries the word "Search" at
     every width now, so it sizes to its label rather than to a 44px square. */
  min-width: 44px; height: 44px; flex-shrink: 0; /* tap target */
  padding: 0 var(--space-20);
  display: inline-flex; align-items: center; justify-content: center;
  border: none; cursor: pointer;
  border-radius: var(--radius-sm);
  background: var(--rose); color: var(--color-on-accent);
  transition: background var(--speed) var(--ease);
}
#mobile-drawer .taf-mm-search__go:hover,
#mobile-drawer .taf-mm-search__go:focus-visible { background: var(--accent-hover); }
/* THE SUBMIT IS LABELLED AT EVERY WIDTH ("the search arrow in
   the hamburger still sits in a button instead of being its own label").
   It used to be an arrow-in-a-square on phones and the word "Search" only from
   700px up — so the width where the control matters most, and where there is no
   hover to explain it, was the one that got the least explicit affordance. The
   arrow is no longer rendered at all (see inc/mobile-menu.php); this is now the
   only treatment, not the tablet half of a pair. */
#mobile-drawer .taf-mm-search__go-label {
  display: inline;
  font-family: var(--display);
  font-size: var(--fs-15);
  font-weight: var(--w-bold);
}
/* ---- Popular one-tap queries. NO PILL: this line takes the same treatment as
   the home page's Popular line, by design.
   The element carries `.taf-popular` itself, so every treatment rule — fs-13,
   cocoa lead-in, bold links, no underline, rose-deep+underline on hover, and
   the invisible 44px tap-target extension at :3872 — arrives from the home
   page's own rules and cannot drift out of step. What was here before was a
   rose-tint pill: a second convention for a job the site already had one for.
   ⚠️ The tap target is NOT optional on this surface — it is the phone. It is
   inherited, not restated; do not "tidy up" `.taf-popular a::after` without
   checking the drawer.
   Only the drawer's own geometry is restated below: `.taf-popular` centres
   itself for the home band, and this line sits under a left-aligned field. ---- */
#mobile-drawer .taf-mm-popular { margin-top: var(--space-12); text-align: left; }

/* ---- Browse recipes: eyebrow + "All recipes" on the head row, 2x2 photo
   tiles (5:4, chocolate scrim, display-face label). ---- */
#mobile-drawer .taf-mm-browse { padding: var(--space-24) 0 var(--space-4); }
#mobile-drawer .taf-mm-browse__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-12); margin-bottom: var(--space-12); }
#mobile-drawer .taf-mm-browse__head .taf-eyebrow { margin: 0; }
#mobile-drawer .taf-mm-all {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--space-4);
  font-size: var(--fs-13); font-weight: var(--w-bold);
  color: var(--rose-link); text-decoration: none;
}
#mobile-drawer .taf-mm-all::before {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: 44px; transform: translateY(-50%); /* tap target */
}
#mobile-drawer .taf-mm-all:hover,
#mobile-drawer .taf-mm-all:focus-visible { color: var(--rose-deep); }
#mobile-drawer .taf-mm-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-12); }
#mobile-drawer .taf-mm-tile {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: var(--ar-5-4);
  transition: box-shadow var(--speed) var(--ease);
}
/* Tile hover language (shadow only — no photo zoom beyond the sanctioned idiom;
   the label stays white-on-scrim). */
#mobile-drawer .taf-mm-tile:hover,
#mobile-drawer .taf-mm-tile:focus-visible { box-shadow: var(--shadow-hover); }
#mobile-drawer .taf-mm-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Scrim ink is the house --ink-rgb composition; the 35% stop is this band's own
   ramp (per the scrim-token rule: ink shared, ramp per band). */
#mobile-drawer .taf-mm-tile::after { content: ""; position: absolute; inset: 0; background: var(--scrim-menutile); }
#mobile-drawer .taf-mm-tile__label {
  position: absolute; left: var(--space-12); right: var(--space-12); bottom: var(--space-10); z-index: var(--z-1);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-8);
  /* 600 is a deliberate legibility exception (in-flow tile titles are 400): white-on-scrim needs the weight. */
  font-family: var(--display); font-size: var(--fs-16); font-weight: var(--w-semibold);
  color: var(--color-on-scrim);
}
#mobile-drawer .taf-mm-tile__label .taf-mm-icon { flex-shrink: 0; opacity: var(--o-85); }

/* ---- Trending this week: ONE deliberate cream card. ---- */
#mobile-drawer .taf-mm-trend { padding: var(--space-24) 0 var(--space-4); }
#mobile-drawer .taf-mm-trend__card {
  display: flex; align-items: center; gap: var(--space-14);
  background: var(--cream);
  border: var(--bw-1) solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-12);
  text-decoration: none;
}
#mobile-drawer .taf-mm-trend__card img {
  width: 72px; height: 72px; /* token-exempt: comp's 72px thumb box — a content shape, not a spacing step */
  object-fit: cover; border-radius: var(--radius-sm); flex-shrink: 0;
}
#mobile-drawer .taf-mm-trend__text { min-width: 0; }
/* Comp says saffron-deep text — measured ~3.4:1 on cream, an AA fail at 12px.
   Text takes cocoa (≥5.1:1 on cream, the footer-verified pair); the flame icon
   alone keeps the saffron heat (decorative, exempt from text contrast). */
#mobile-drawer .taf-mm-trend__eyebrow { display: flex; align-items: center; gap: var(--space-6); color: var(--text-muted); }
#mobile-drawer .taf-mm-trend__eyebrow .taf-mm-icon { color: var(--saffron-deep); }
#mobile-drawer .taf-mm-trend__title {
  display: block; margin-top: var(--space-4);
  font-family: var(--display); font-size: var(--fs-17); font-weight: var(--w-semibold);
  line-height: var(--lh-125); color: var(--chocolate);
}
#mobile-drawer .taf-mm-trend__card > .taf-mm-icon { margin-left: auto; color: var(--rose); flex-shrink: 0; }
#mobile-drawer .taf-mm-trend__card:hover .taf-mm-trend__title,
#mobile-drawer .taf-mm-trend__card:focus-visible .taf-mm-trend__title { color: var(--rose-deep); }

/* ---- "Site links": Kadence's own nav restyled to slim icon-led rows.
   Nav links + parent-row wrap: Kadence paints BOTH from palette8 (its
   dark-drawer white); selectors mirror Kadence's, id-scoped. Parent <a>s sit
   inside .drawer-nav-drop-wrap (li > div > a, color:inherit), so `ul li > a`
   alone would miss them — the drop-wrap rules carry them. The leading rose
   icon + trailing caret are injected into the title by inc/mobile-menu.php's
   nav_menu_item_title filter (mobile location only; unknown items get no
   icon). ---- */
#mobile-drawer .mobile-navigation { padding: var(--space-24) 0 var(--space-8); }
#mobile-drawer .mobile-navigation ul li > a,
#mobile-drawer .mobile-navigation ul li.menu-item-has-children > .drawer-nav-drop-wrap { color: var(--chocolate); }
#mobile-drawer .mobile-navigation ul li > a,
#mobile-drawer .mobile-navigation .drawer-nav-drop-wrap > a {
  display: flex; align-items: center; gap: var(--space-12);
  padding: var(--space-14) var(--space-2); /* rows measure 17px type + 28px pad ≈ 48px ≥ the 44px floor */
  font-size: var(--fs-17); font-weight: var(--w-bold); line-height: var(--lh-13);
  text-decoration: none;
}
#mobile-drawer .mobile-navigation ul li > a:hover,
#mobile-drawer .mobile-navigation ul li > a:focus-visible,
#mobile-drawer .drawer-nav-drop-wrap a:hover,
#mobile-drawer .drawer-nav-drop-wrap a:focus-visible { color: var(--rose-deep); }
/* Current page: Kadence's palette-highlight resolves to --rose-link (5.05:1 on
   milk — passes, but thin); deepen to --rose-deep = the house active/hover ink. */
#mobile-drawer .mobile-navigation ul li.current-menu-item > a,
#mobile-drawer .mobile-navigation ul li.current-menu-item.menu-item-has-children > .drawer-nav-drop-wrap { color: var(--rose-deep); }
/* Hairlines: white-on-dark rgba(255,255,255,.1) → --line. Color only; Kadence
   keeps the 1px structure. Selectors mirror kadence-global-inline-css exactly,
   id-scoped. The comp also rules the TOP of the first row — Kadence only
   bottoms rows, so the first-child rule adds it. */
#mobile-drawer .mobile-navigation ul li.menu-item-has-children .drawer-nav-drop-wrap,
#mobile-drawer .mobile-navigation ul li:not(.menu-item-has-children) a { border-bottom-color: var(--line); }
#mobile-drawer .mobile-navigation:not(.drawer-navigation-parent-toggle-true) ul li.menu-item-has-children .drawer-nav-drop-wrap button { border-left-color: var(--line); }
#mobile-drawer .mobile-navigation ul.menu > li:first-child > a,
#mobile-drawer .mobile-navigation ul.menu > li:first-child > .drawer-nav-drop-wrap { border-top: var(--bw-1) solid var(--line); }
/* Row furniture from the title filter. */
#mobile-drawer .mobile-navigation a > .taf-mm-icon { color: var(--rose); flex-shrink: 0; }
#mobile-drawer .taf-mm-navlabel { min-width: 0; }
#mobile-drawer .taf-mm-caret { margin-left: auto; display: inline-flex; color: var(--cocoa); }
/* Sub-menu toggle (44.2x51.1 measured — already over the floor): inherit gives
   it chocolate; state ink matches links. */
#mobile-drawer .drawer-sub-toggle:hover,
#mobile-drawer .drawer-sub-toggle:focus-visible { color: var(--rose-deep); }

/* ---- Socials + location. 44px circles (comp says 40 — WCAG bump, glyphs stay
   20px like the footer row's). ---- */
#mobile-drawer .taf-mm-social { display: flex; align-items: center; gap: var(--space-12); padding: var(--space-6) 0 var(--space-24); }
#mobile-drawer .taf-mm-social__link {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  width: 44px; height: 44px;              /* tap target; the glyph is 20px */
  display: inline-flex; align-items: center; justify-content: center;
  border: var(--bw-15) solid var(--line);
  border-radius: var(--radius-round);
  color: var(--chocolate);
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
#mobile-drawer .taf-mm-social__link svg { width: 20px; height: 20px; display: block; }
#mobile-drawer .taf-mm-social__link:hover,
#mobile-drawer .taf-mm-social__link:focus-visible { color: var(--rose-deep); border-color: var(--rose); }
#mobile-drawer .taf-mm-social__meta { margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-4); font-size: var(--fs-meta); color: var(--text-muted); }
#mobile-drawer .taf-mm-social__meta .taf-mm-icon { color: var(--rose); }

/* ---- The drawer ad slot that shipped in v0.87.0 (#mobile-drawer
   .taf-ad--drawer — a reserved 300×250 below the socials, `isolation:isolate`
   to keep a filled iframe out of .taf-mm-search's z-stack, collapse-when-:empty)
   was REMOVED 2026-08-06 in v0.88.0. Raptive rejected the placement: ads in a
   collapsible/overlay container are against their policy, so it would never
   have filled. Its ≥700px grid rule went with it — see the drawer grid block
   below, whose row count came back down with it. ---- */

/* ---- The pinned e-book CTA's rules lived here (sticky-to-the-sheet-bottom
   panel + rose button + tablet-only supporting line). Removed 2026-08-05 with
   the markup in inc/mobile-menu.php — the header bar's own "Free e-book"
   button is visible at mobile widths and points at the same target, so the
   drawer copy was a second CTA for one destination.
   ⚠️ `--shadow-mm-cta` was this rule's ONLY consumer and is now an unused
   token. It was deliberately NOT deleted: `:root` is generated from
   design/tokens.json, and `build-tokens.py --check` already FAILS at HEAD
   (tokens.json still holds the pre-scrub comment text, so `--write` would
   revert part of the comment-leak scrub). Re-sync tokens.json's comments
   first, then drop the token.
   Restore from git v0.69.0 if the header button ever stops rendering. ---- */

/* ---- Open motion (comp: sheet slides up 14px + fades; sections stagger in).
   Keyed on .show-drawer — Kadence's open-state class on the drawer itself
   (menu.js's Esc handler matches the same class) — so it replays per open.
   `both` holds the pre-animation frame during each delay. ---- */
@keyframes taf-mm-in { from { opacity: 0; transform: translateY(14px); } to { opacity: var(--o-1); transform: none; } }
@keyframes taf-mm-fadeup { from { opacity: 0; transform: translateY(10px); } to { opacity: var(--o-1); transform: none; } }
#mobile-drawer.show-drawer .drawer-inner { animation: taf-mm-in var(--dur-28) var(--ease-out) both; }
#mobile-drawer.show-drawer .taf-mm-search { animation: taf-mm-fadeup var(--dur-35) var(--ease-out) var(--dur-05) both; }
#mobile-drawer.show-drawer .taf-mm-browse { animation: taf-mm-fadeup var(--dur-35) var(--ease-out) var(--dur-12) both; }
#mobile-drawer.show-drawer .taf-mm-trend { animation: taf-mm-fadeup var(--dur-35) var(--ease-out) var(--dur-18) both; }
#mobile-drawer.show-drawer .mobile-navigation { animation: taf-mm-fadeup var(--dur-35) var(--ease-out) var(--dur-24) both; }
#mobile-drawer.show-drawer .taf-mm-social { animation: taf-mm-fadeup var(--dur-35) var(--ease-out) var(--dur-3) both; }
/* READ THIS WITH THE FIVE RULES ABOVE — it exists only because of them.
   `animation-fill-mode: both` makes every one of those sections a PERMANENT
   stacking context (the fill holds past the delay and past the end). So the
   autocomplete panel's z-index: 70 is trapped inside .taf-mm-search and cannot
   out-paint a later sibling at any value: .taf-mm-trend, .mobile-navigation and
   .taf-mm-social simply come later in tree order. Measured at 390px before this
   rule: 4 of the panel's 6 targets (suggestions 3-5 + the "See all results"
   footer) hit-tested to drawer sections, i.e. they were untappable. Proof of
   mechanism: under prefers-reduced-motion the animations are `none` above, the
   sections stop being stacking contexts, and all 6 targets hit-test clean.
   So lift the SECTION, not the panel inside it.
   z-index: 1 and no higher — this section must stay UNDER the sticky logo bar
   (.taf-mm-topbar, z-index: 2) and the close button (.drawer-header, z-index:
   49); all three share .drawer-inner's stacking context. Shipping this at 1
   while the top bar was also at 1 tied them, and this section — later in tree
   order — painted over the wordmark once the sheet was scrolled (measured at
   390x664, scrollTop 140: the chips row hit-tested inside the bar's box, x 0-390
   y 0-77). The bar was raised to 2 and the close button to 49 to break it; see
   the three-layer note on .drawer-header. Do not raise this above 1. */
#mobile-drawer .taf-mm-search { position: relative; z-index: var(--z-1); }
@media (prefers-reduced-motion: reduce) {
  #mobile-drawer.show-drawer .drawer-inner,
  #mobile-drawer.show-drawer .taf-mm-search,
  #mobile-drawer.show-drawer .taf-mm-browse,
  #mobile-drawer.show-drawer .taf-mm-trend,
  #mobile-drawer.show-drawer .mobile-navigation,
  #mobile-drawer.show-drawer .taf-mm-social { animation: none; }
}

/* ======================
   MOBILE MENU — TABLET LAYER (v2 handoff, >=700px = the theme's existing
   boundary; the comp says "~768", fluid between). Same DOM as the phone
   sheet, CSS-only reflow: .drawer-content becomes a 1.35fr/1fr grid — top bar
   and search span both columns, the 2x2 tiles take the left, trending ->
   site links -> socials stack right, and the pinned CTA becomes a horizontal
   bar. No upper bound on the media query: the drawer only OPENS below
   Kadence's mobile-header switch (its toggle is hidden on the desktop
   header), and every rule stays #mobile-drawer / #mobile-toggle scoped, so
   nothing can leak into the desktop layout even where the hidden DOM exists.
   Kadence renders its nav between the hooks wrapped in
   .site-header-item-mobile-navigation (verified: kadence 1.5.1
   template-parts/header/mobile-navigation.php) — that wrapper is the grid
   item, the inner .mobile-navigation keeps the phone restyle. Any OTHER
   popup element the Customizer might add is also a .site-header-item and
   rides the right column via auto row placement.
   ====================== */
@media (min-width: 700px) {
  #mobile-drawer .drawer-content {
    display: grid;
    grid-template-columns: 1.35fr 1fr;
    /* FIVE rows, and the last one takes the slack: at tablet sizes the content
       UNDERFLOWS the sheet, so a 1fr track absorbs the leftover height instead
       of leaving a void (349px measured @834x1112, v0.31.0). The tracks are
       1 topbar · 2 search · 3-5 browse (spans 3) with trend on 3, nav on 4 and
       socials on 5. When content overflows (long menus) the rows collapse to
       auto and the phone-style sticky pinning takes over unchanged.
       ⚠️ History, so the two live-fire lessons are not re-learned: v0.87.0 had
       to move the absorber 5 → 6 and declare a SIXTH row for the drawer ad
       slot, because minmax(0,1fr) on row 5 let the socials' track shrink below
       its 44px content and a filled ad slid 30px up UNDER them. That slot was
       removed in v0.88.0 (Raptive rejected it), so there is nothing below the
       socials any more and the absorber is back on the last real row. Adding a
       sixth row without also moving the absorber re-opens the underflow void;
       putting content on row 5 under a 1fr row 6 re-opens the overlap. */
    grid-template-rows: auto auto auto auto minmax(0, 1fr);
    column-gap: var(--space-32);
    row-gap: var(--space-24); /* comp: 22-26px section rhythm — one 24 beat */
    align-content: start;
    padding: 0 var(--space-32); /* comp gutter 28 -> the 32 token (24 sits too tight against the 32 column gap) */
  }
  /* Full-width rows. position:sticky keeps working on grid items, so the top
     bar and CTA pin exactly as on phone. */
  #mobile-drawer .taf-mm-topbar {
    grid-column: 1 / -1;
    margin: 0 calc(-1 * var(--space-32));
    padding: var(--space-14) var(--space-32); /* the logo is left-anchored and ~100px wide — it cannot reach the close pill, so no protective right pad */
  }
  #mobile-drawer .drawer-header { padding: var(--space-14) var(--space-32); }
  /* Close control becomes a labeled pill (comp "✕ Close"). Kadence's button
     and its X bars are KEPT — JS wiring untouched; the bars re-anchor into a
     left icon zone and a CSS-generated "Close" fills the text zone. The label
     is decorative: the accessible name stays Kadence's aria-label
     ("Close menu" contains "Close" — WCAG 2.5.3 label-in-name holds). */
  #mobile-drawer .drawer-header .drawer-toggle {
    width: auto; height: 44px; box-sizing: border-box; /* keeps the 44px floor as a pill */
    padding: 0 var(--space-16) 0 var(--space-40);
    border: var(--bw-15) solid var(--line);
    background: var(--paper);
    border-radius: var(--radius-sm);
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
  }
  #mobile-drawer .drawer-header .drawer-toggle:hover,
  #mobile-drawer .drawer-header .drawer-toggle:focus-visible { border-color: var(--rose); }
  /* Kadence anchors the X bars at left:50% of the button with a -.375em
     (-9px @ its 24px font) centering margin (header.min.css .toggle-close-bar)
     — re-anchor onto the icon zone's center; the margin still centers the
     18px bar on `left`, both open and closed states. */
  #mobile-drawer .drawer-header .drawer-toggle .toggle-close-bar { left: var(--space-24); }
  #mobile-drawer .drawer-header .drawer-toggle::after {
    content: "Close"; /* not translatable from CSS — accepted, site ships English-only (see inc/mobile-menu.php) */
    font-family: var(--sans); font-size: var(--fs-15); font-weight: var(--w-bold); line-height: var(--lh-1);
  }
  /* Header hamburger becomes the matching "☰ Menu" pill. The label span is
     Kadence's own (.menu-toggle-label, fed by inc/mobile-menu.php's theme-mod
     filter; hidden on phones by the base rule above). Kadence prints label
     BEFORE icon — flex order flips to match the comp. */
  #mobile-toggle.menu-toggle-open {
    display: inline-flex; align-items: center; gap: var(--space-8);
    height: 44px; padding: 0 var(--space-16);
    border: var(--bw-15) solid var(--line);
    background: var(--paper);
    border-radius: var(--radius-sm);
  }
  #mobile-toggle .menu-toggle-label {
    display: inline; order: 2; padding-right: 0;
    font-family: var(--sans); font-size: var(--fs-15); font-weight: var(--w-bold);
  }
  #mobile-toggle .menu-toggle-icon { order: 1; display: inline-flex; }

  /* Search hero: field + labeled submit, chips inline right, wrapping below
     on narrow tablets (comp). */
  #mobile-drawer .taf-mm-search {
    grid-column: 1 / -1;
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--space-16);
    padding: 0;
  }
  #mobile-drawer .taf-mm-search__box {
    flex: 1 1 380px; /* token-exempt: comp's wrap threshold — the field holds ~half the row before the chips drop below it */
    padding-left: var(--space-18);
  }
  #mobile-drawer .taf-mm-search__input { font-size: var(--fs-17); padding: var(--space-12) 0; }
  /* The three "Search" submit overrides that lived here are GONE — the base
     rule now labels the button at every width, so restating it from 700px up
     was a no-op pretending to be a breakpoint decision. */
  #mobile-drawer .taf-mm-popular { margin-top: 0; }
  /* `.taf-mm-chip--phone` is retired too, and so is the pill it modified: the
     Popular line is a flat three at every width (inc/mobile-menu.php) and
     carries the home page's `.taf-popular` treatment since 2026-08-09. */

  /* Two-column body. Explicit rows: browse spans the right column's three. */
  #mobile-drawer .taf-mm-browse { grid-column: 1; grid-row: 3 / span 3; padding: 0; }
  #mobile-drawer .taf-mm-browse__head { margin-bottom: var(--space-14); }
  #mobile-drawer .taf-mm-tiles { gap: var(--space-14); }
  /* 19px labels (comp). These used to read --fs-h3 because it happened to be 19px and
     did not step at 900px. That coupling was a trap: h3 now steps 20 -> 22px, which
     would have silently moved the drawer. Pinned to --fs-19 instead. */
  #mobile-drawer .taf-mm-tile__label { left: var(--space-14); right: var(--space-14); bottom: var(--space-12); font-size: var(--fs-19); }
  #mobile-drawer .taf-mm-trend { grid-column: 2; grid-row: 3; padding: 0; }
  #mobile-drawer .taf-mm-trend__card { padding: var(--space-14); }
  #mobile-drawer .drawer-content > .site-header-item { grid-column: 2; }
  #mobile-drawer .drawer-content > .site-header-item-mobile-navigation { grid-row: 4; }
  #mobile-drawer .mobile-navigation { padding: 0; }
  #mobile-drawer .mobile-navigation ul li > a,
  #mobile-drawer .mobile-navigation .drawer-nav-drop-wrap > a { font-size: var(--fs-16); }
  #mobile-drawer .taf-mm-social { grid-column: 2; grid-row: 5; align-self: start; padding: 0; }
  /* NOTHING owns a row 6 here, and the grid declares only five tracks.
     Two former occupants are gone: the tablet e-book CTA bar (removed
     2026-08-05) and the drawer ad slot (v0.87.0's full-width `grid-column:
     1 / -1; grid-row: 6`, removed 2026-08-06 — Raptive rejected the
     placement). The socials on row 5 are the last item in the grid.
     ⚠️ Anything added below the socials needs BOTH a sixth track and the
     slack absorber moved onto it (see grid-template-rows at the top of this
     block) — v0.87.0 measured a filled row-6 item sliding 30px up under the
     socials when the absorber was left on row 5. */
}

/* ======================
   COMPACT MAGNET (.taf-magnet) Kit form, phones.
   The "INLINE on one row" rule above forces flex-wrap:nowrap!important on ALL .taf-magnet Kit
   forms — right for the 1-field home form, but it crushed the 2-field form (Name + Email +
   button) to ~40px input stubs below 700px. :has() scopes this fix to 2-field forms: name +
   email share a 50/50 row, the submit drops to its own full-width row. ≥700 keeps one row.
   ====================== */
@media (max-width: 699.98px) {
  .taf-magnet .formkit-form .formkit-fields:has(> .formkit-field + .formkit-field) { flex-wrap: wrap !important; }
  .taf-magnet .formkit-form .formkit-fields:has(> .formkit-field + .formkit-field) .formkit-field { flex: 1 1 calc(50% - 4px) !important; min-width: 0 !important; margin: 0 !important; }
  .taf-magnet .formkit-form .formkit-fields:has(> .formkit-field + .formkit-field) .formkit-submit { flex: 1 1 100% !important; width: 100% !important; margin-top: 0; }
}

/* ---- Raptive ad units: GEOMETRY ONLY, desktop/tablet.
   What stood here until v0.89.1: the reserved-slot chrome (stripes + dashed
   border + "Advertisement" ::before) on every .adthrive-ad at >=700px.
   Removed by decision 2026-08-07 — the theme draws no ad visuals; Raptive's
   script owns presentation. (The immediate trigger: v0.88.0 removed the card
   rails whose !important rules had stripped this chrome from in-card units,
   so the blanket look started reaching Raptive's injected recipe ads.)
   min-height:90px stays — the documented perf-U2 KEEP decision (measured
   inert both ways; Raptive reserves via its own inline heights). ---- */
@media (min-width: 700px) {
  .adthrive-ad { min-height: 90px; }
  /* floated units inside instruction/ingredient lists: clear the text */
  .wprm-recipe-instruction .adthrive-ad,
  .wprm-recipe-ingredient .adthrive-ad,
  .wprm-recipe .adthrive-ad { margin: 0 0 var(--space-12) var(--space-16); }
}

/* ---- One star anatomy site-wide: SAFFRON (star rework; reverses
   the v0.33.16 rose sweep). Covers every WPRM span.wprm-rating-star widget —
   shortcode display stars, the post-meta row, the card header row (wprm-taf2026.css
   repeats the anatomy at card specificity).
   FILLED = --saffron fill + --saffron-deep stroke (bare saffron on cream is ~2.3:1;
             the deep stroke carries the 3:1 non-text contrast).
   EMPTY = --line fill AND stroke (D1-C quiet grey — no tint fills, no outline-only
             empties, no opacity tricks).
   PARTIAL = WPRM's per-widget linearGradient (4 stops in two opacity pairs: head
             opacity-1 pair = the filled span, tail opacity-0 pair = the rest). Head
             stops go saffron; tail stops go --line and are forced visible so the
             unfilled span reads grey, not transparent. The base fill rule must skip
             partial stars (its !important would flatten the url(#gradient) fill), so
             the exclusion rides a zero-specificity :where().
   HOVER = fill-to-cursor (D3-B): WPRM's JS swaps -selecting-filled/-selecting-empty
             classes and writes inline fills; the class rules below out-!important the
             base painter so the preview actually splits at the cursor (the old painter
             flattened all five to one solid state).
   !important throughout: WPRM prints per-widget #id styles from its data-color, plus
   inline fills during hover. fill-opacity pins WPRM's not-voted 30% wash — the
   empty/full anatomy carries that affordance now. ---- */
.wprm-rating-star:where(:not(.wprm-rating-star-33):not(.wprm-rating-star-50):not(.wprm-rating-star-66)) svg polygon,
.wprm-rating-star:where(:not(.wprm-rating-star-33):not(.wprm-rating-star-50):not(.wprm-rating-star-66)) svg path {
  fill: var(--line) !important;
}
.wprm-rating-star svg polygon,
.wprm-rating-star svg path {
  stroke: var(--line) !important;
  fill-opacity: 1 !important;
}
/* filled + fractional stars carry the deep-saffron outline */
.wprm-rating-star.wprm-rating-star-33 svg polygon,
.wprm-rating-star.wprm-rating-star-33 svg path,
.wprm-rating-star.wprm-rating-star-50 svg polygon,
.wprm-rating-star.wprm-rating-star-50 svg path,
.wprm-rating-star.wprm-rating-star-66 svg polygon,
.wprm-rating-star.wprm-rating-star-66 svg path { stroke: var(--saffron-deep) !important; }
.wprm-rating-star.wprm-rating-star-full svg polygon,
.wprm-rating-star.wprm-rating-star-full svg path {
  fill: var(--saffron) !important;
  stroke: var(--saffron-deep) !important;
}
/* D3-B fill-to-cursor (these must FOLLOW the state rules above: selecting-empty
   ties the partial-stroke rule on specificity and wins by order) */
.wprm-rating-star.wprm-rating-star-selecting-filled svg polygon,
.wprm-rating-star.wprm-rating-star-selecting-filled svg path {
  fill: var(--saffron) !important;
  stroke: var(--saffron-deep) !important;
}
.wprm-rating-star.wprm-rating-star-selecting-empty svg polygon,
.wprm-rating-star.wprm-rating-star-selecting-empty svg path {
  fill: var(--line) !important;
  stroke: var(--line) !important;
}
/* D2-A fractional fill. The gradient defs sit in a separate width-0 svg BESIDE the
   star spans (not inside them), so the selector anchors on WPRM's id prefix. WPRM
   uses linearGradient solely for partial stars. !important beats the per-widget
   linearGradient#id stop rules. */
svg linearGradient[id^="wprm-"] stop:nth-of-type(-n+2) { stop-color: var(--saffron) !important; }
svg linearGradient[id^="wprm-"] stop:nth-of-type(n+3) {
  stop-color: var(--line) !important;
  stop-opacity: 1 !important;  /* the markup ships these at 0 — grey, not see-through */
}

/* ---- WPRM in-content jump shortcodes ("Jump to Recipe / Jump to Video"): strip
   the icon SVGs and the inline black color; read as brand links. !important beats
   the inline style attr. ---- */
.wprm-jump-to-recipe-shortcode,
.wprm-jump-to-video-shortcode {
  color: var(--rose-link) !important;
  font-family: var(--display); font-weight: var(--w-bold);
  text-decoration: underline; text-underline-offset: 3px;
  /* 44px tap target: 17px × 1.65 line (28px) + 2×8px padding. inline-block so the
     vertical padding actually grows the hit box (it paints-only on plain inline). */
  display: inline-block;
  padding: var(--space-8) 0;
}
.wprm-jump-to-recipe-shortcode:hover,
.wprm-jump-to-video-shortcode:hover { color: var(--rose-deep) !important; }
.wprm-jump-to-recipe-shortcode .wprm-recipe-icon,
.wprm-jump-to-video-shortcode .wprm-recipe-icon { display: none; }

/* ---- Jump pill: flip the arrow when the recipe card is ABOVE the viewport
   (menu.js toggles the class at the same observer crossing that shows/hides the
   pill). ---- */
.taf-jump-pill svg { transition: transform var(--speed) var(--ease); }
.taf-jump-pill--up svg { transform: rotate(180deg); }

/* ---- Grow Social (dpsp) share buttons: one rose family, both placements (inline
   rows + floating rail). The raw network colors clashed with the palette.
   !important beats the plugin's per-network rules. ---- */
.dpsp-network-btn { background-color: var(--rose) !important; }
.dpsp-network-btn .dpsp-network-icon { background-color: transparent !important; } /* X ships its own black bg */
.dpsp-network-btn:hover,
.dpsp-network-btn:focus-visible { background-color: var(--rose-deep) !important; }
/* rail: hidden (menu.js observer) once the footer scrolls in, so it never overlaps
   the footer logo/legal */
#dpsp-floating-sidebar { transition: opacity var(--speed) var(--ease), visibility var(--speed) var(--ease); }
#dpsp-floating-sidebar.taf-rail-hidden { opacity: 0; visibility: hidden; pointer-events: none; }
/* NOTE: the rail's mobile hiding is Grow's own UA sniff, not a viewport
   rule — verified live (a desktop UA shows it at ANY width once the scroll
   trigger fires; .dpsp-hide-on-mobile has no CSS effect). A viewport
   override was built, deployed, then REVERTED in review: plugin behavior
   stands on tablets. Don't re-add without a fresh ask. */

/* ---- dpsp keeps network-colored border layers on pseudo-elements (icon-animation ring) too.
   Rose on every layer, every state. ---- */
.dpsp-network-btn,
.dpsp-network-btn::before,
.dpsp-network-btn::after,
.dpsp-network-btn .dpsp-network-icon,
.dpsp-network-btn .dpsp-network-icon::before,
.dpsp-network-btn .dpsp-network-icon::after { border-color: var(--rose) !important; }
.dpsp-network-btn:hover,
.dpsp-network-btn:hover::before,
.dpsp-network-btn:hover::after,
.dpsp-network-btn:focus-visible { border-color: var(--rose-deep) !important; }

/* ---- "Watch video" secondary button beside the Jump button (rendered only when
   the recipe has a video); lands just under the sticky header. Visible at EVERY
   width since 2026-08-04 — it used to be desktop-only, which left mobile with no
   route to the video at all once the jump pill was retired. The two share the
   action row's full width below 900 (see the meta-row block above). ---- */
.taf-post-meta__video { display: inline-flex; }
#recipe-video { scroll-margin-top: var(--space-64); }

/* ---- Rating prompt: the card's user-rating stars invite a tap — gentle grow on
   hover (they open WPRM's rate modal on click). 1.15 per the 2026-08-01 star
   rework (was 1.2); desktop pointers only, and reduced-motion kills both the
   transition and the scale itself. ---- */
@media (hover: hover) and (pointer: fine) {
  .wprm-user-rating .wprm-rating-star svg { transition: transform var(--speed) var(--ease); }
  .wprm-user-rating .wprm-rating-star:hover svg { transform: scale(1.15); }
}
@media (prefers-reduced-motion: reduce) {
  .wprm-user-rating .wprm-rating-star:hover svg { transform: none; }
}
.wprm-user-rating .wprm-rating-star { cursor: pointer; }

/* ======================
   FOOTER — social icon row + split legal bar. The divider lives on the bar, so the two legal
   lines share one rule.
   ====================== */
/* Box model (measured on the rendered page 2026-07-17): WAS 18x18 anchors — glyph-only, zero
   padding — on a 16px gap: under the 44px WCAG floor. NOW each anchor is a 44x44 flex box
   (the header-social pattern below) around a 20px glyph — 18px looked starved in the
   bigger box; 20px matches .taf-hdr-search. Each box carries (44-20)/2 = 12px of built-in
   air per side, so gap:0 yields a 24px visual glyph gap — the closest to the old 16px
   that non-overlapping 44px targets allow (a true 16px would need the boxes to overlap).
   The negative margins re-seat the row: left pulls the first glyph back onto the column
   edge; top absorbs the box's 12px of head-air so the glyph row keeps its old position.
   The 12px of extra air BELOW is kept (no negative bottom): pulling the legalbar up would
   lay it over the live bottom of the tap boxes. */
.taf-footer__social {
  display: flex;
  gap: 0; /* the 44px boxes carry the spacing — see box-model note above */
  margin-top: calc(var(--space-16) - var(--space-12));
  margin-left: calc(-1 * var(--space-12));
}
.taf-footer__social a {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;              /* tap target; the glyph is 20px */
  color: var(--footer-label);
  transition: color var(--speed) var(--ease);
}
.taf-footer__social a:hover,
.taf-footer__social a:focus-visible { color: var(--rose); }
.taf-footer__social svg { width: 20px; height: 20px; }
.taf-footer .taf-footer__legalbar { margin-top: var(--space-32); padding-top: var(--space-20); border-top: var(--bw-1) solid var(--footer-rule); display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-8) var(--space-20); flex-wrap: wrap; }
.taf-footer .taf-footer__legalbar .legal { margin: 0; padding: 0; border: none; }

/* Recipe-index collections band anchor (footer "Collections" target). */
#collections { scroll-margin-top: var(--space-64); }
/* The recipe-card band's stars jump here. The sticky .site-header is 72px
   (measured), so a bare jump buries the "Leave a Reply" head and the rating
   field under it. Same scroll-margin-top convention the ToC pills and
   #recipe-video use — scrollIntoView honors it natively, and taf-recipe.js
   subtracts it explicitly on the instant path. */
#respond { scroll-margin-top: calc(var(--space-64) + var(--space-16)); }

/* ---- Header social icons ----
   URLs + glyphs come from the footer's taf2026_footer_socials() so the two can never drift —
   see inc/header-social.php. 44px tap target. ---- */
.taf-headsocial { display: flex; align-items: center; gap: var(--space-2); }
.taf-headsocial__link {
  /* token-frozen: WCAG 2.5.5 — the 44px target is built from padding+size+border in this rule
     T6 must NOT remap these values to a scale — tokenize at parity only. */
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;              /* tap target; the glyph is 18px */
  color: var(--text-muted);
  transition: color var(--dur-18) var(--ease), transform var(--dur-18) var(--ease);
}
.taf-headsocial__link svg { width: 18px; height: 18px; display: block; }
.taf-headsocial__link:hover,
.taf-headsocial__link:focus-visible { color: var(--rose-deep); transform: translateY(-1px); }

/* Phones: the header row is already logo + CTA + search + hamburger — four more
   icons would crowd it out. Social stays desktop/tablet only; the footer row carries
   it on phones. */
@media (max-width: 767px) { .taf-headsocial { display: none; } }

/* ============================================================
   Delight micro-interactions (2026-07-17). All motion is
   transform/opacity (plus SVG stroke paint) — zero layout shift.
   The global prefers-reduced-motion block above (transition:none /
   animation:none on *) kills every animation here; each keyframe's
   base state is the FINAL state, so reduced-motion users simply see
   the finished UI. Explicit transform:none kills are added where a
   transform would otherwise SNAP in (the mosaic precedent).
   ============================================================ */

/* ---- Kit signup success (delight B) ----
   ck.5.js swaps the form fields for
   `<div class="formkit-alert formkit-alert-success">` on a successful
   submit (verified in the ck.5.js source: className = "formkit-alert
   formkit-alert-success", replaceChild on [data-element="fields"]).
   kit-form.js prepends an inline .taf-kit-check svg to that alert.
   The success LOOK is already owned by taf-subscribe.css (quiet
   rose-deep confirmation line on the cream magnet/subscribe panels —
   do NOT re-panel it here; that fight was checked, not missed). This
   block adds only the MOTION: a fade-up entrance on the alert (it
   occupies the replaced fields' slot — no new space, no CLS) and a
   rose checkmark whose stroke draws in. */
@keyframes taf-kit-success-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: var(--o-1); transform: none; }
}
@keyframes taf-kit-check-draw {
  from { stroke-dashoffset: 24; }
  to   { stroke-dashoffset: 0; }
}
.convertkit-form .formkit-alert.formkit-alert-success {
  animation: taf-kit-success-in var(--dur-35) var(--ease-out) both;
}
.convertkit-form .taf-kit-check {
  display: inline-block;
  width: var(--space-18);
  height: var(--space-18);
  margin-right: var(--space-8);
  vertical-align: calc(-1 * var(--space-4));
  fill: none;
  stroke: var(--rose);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.convertkit-form .taf-kit-check path {
  /* path length ≈23; 24 covers it. The draw starts as the panel lands
     (entrance .35s, draw .5s after a .12s delay — total under 1s). */
  stroke-dasharray: 24;
  animation: taf-kit-check-draw var(--dur-5) var(--ease-out) var(--dur-12) both;
}
@media (prefers-reduced-motion: reduce) {
  .convertkit-form .formkit-alert.formkit-alert-success { transform: none; opacity: var(--o-1); }
  .convertkit-form .taf-kit-check path { stroke-dashoffset: 0; }
}

/* ---- Search empty-state chips (delight D; inc/search-404.php) ----
   This WAS the mobile drawer's pill, unscoped from #mobile-drawer so the
   search empty state could use it. ⚠️ THE DRAWER NO LONGER HAS A PILL — it
   took the home page's `.taf-popular` treatment, and the class it mirrored is
   deleted, so the old "keep the two rules in step" instruction has nothing
   left to step with. Leaving the empty state as a pill is a deliberate,
   separately scoped decision — matching it is a design call, not a tidy-up.
   🗒 Pills are NOT extinct sitewide: `.taf-pf-tag` (Filed under, radius-full),
   `.taf-spotlight__pill`, and rules in taf-contact.css and taf-comments.css
   all still use one. Grep `radius-full|radius-99` before claiming otherwise. */
/* Kadence's stock apology line ("Sorry, but nothing matched…") would stack
   above our lead — two no-results messages. Its template isn't hookable (unlike
   the 404), so hide ONLY the <p>; Kadence's prefilled retry FORM in the same
   .page-content stays — it is the page's only retry field. */
body.search-no-results section.error .page-content > p { display: none; }
.taf-empty-chips {
  /* A real <ul> since 2026-07-17 (was role=list divs) — flatten the list chrome. */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin: var(--space-12) 0 var(--space-8);
  padding: 0;
  list-style: none;
}
.taf-empty-chips li { margin: 0; }
.taf-empty-chip {
  position: relative;
  font-size: var(--fs-13);
  font-weight: var(--w-bold);
  color: var(--rose-deep);
  text-decoration: none;
  background: var(--rose-tint);
  border-radius: var(--radius-99);
  padding: var(--space-8) var(--space-14);
}
.taf-empty-chip::before {
  /* Invisible 44px tap-target extension — same pattern (and same WCAG
     2.5.5 rationale) as `.taf-popular a::after`, which is what the drawer's
     Popular line uses now. */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: var(--space-44);
  transform: translateY(-50%);
}
.taf-empty-chip:hover,
.taf-empty-chip:focus-visible { color: var(--rose-deep); background: var(--line); }
.taf-search-empty__lead {
  margin: var(--space-8) 0 0;
  color: var(--text-muted);
}

/* ---- Button press (delight F) ----
   Sitewide primary buttons dip 1px while held. Grouped here, once —
   NOT copied per component. Membership: the .taf-btn family (jump /
   video / pattern CTAs; block-bridged buttons press their INNER
   __link — the wrapper is a stripped shell), both search "go" buttons,
   the Kit submit, the comment submit and the WPRM Print/Pin pair.
   EXCLUDED, deliberately: .taf-jump-pill (position:fixed with
   transform:translateX(-50%) — translateY would overwrite it and
   throw the pill off-center) and .taf-chip / .taf-empty-chip
   (navigation pills, not press buttons).
   The transition lives on :active only: it eases the press in without
   clobbering each button's own base transition list (background/color/
   border); release snaps back via the base rule, which is the correct
   feel for a key-up. */
.taf-btn:active,
.wp-block-button.taf-btn .wp-block-button__link:active,
.taf-search .go:active,
#mobile-drawer .taf-mm-search__go:active,
.formkit-form .formkit-submit:active,
.taf-comments #respond input[type="submit"]:active,
.wprm-recipe-template-taf-2026 .wprm-template-chic-buttons .wprm-recipe-link:active {
  transform: translateY(1px);
  transition: transform var(--dur-05) var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .taf-btn:active,
  .wp-block-button.taf-btn .wp-block-button__link:active,
  .taf-search .go:active,
  #mobile-drawer .taf-mm-search__go:active,
  .formkit-form .formkit-submit:active,
  .taf-comments #respond input[type="submit"]:active,
  .wprm-recipe-template-taf-2026 .wprm-template-chic-buttons .wprm-recipe-link:active { transform: none; }
}

/* ======================
   Tile-title two-line clamp: every grid tile title
   holds at most 2 lines so rows stay level whatever the recipe is called.
   The 2px padding-bottom + equal negative margin is descender room: Poppins'
   deepest descender (g, measured -270/1000upm from the shipped woff2) reaches
   ~0.045em past a 1.15 line box's bottom edge, and the clamp's overflow:hidden
   would shave it. The padding keeps the ink; the negative margin keeps the
   title→stars rhythm at its current value. Blink/WebKit line-clamp truncates
   lines past the clamp (they do not paint into the padding). The .entry-content
   and h3.t variants ride along so the higher-specificity margin rules elsewhere
   cannot re-open the 2px. ====================== */
.entry-content .taf-tilecard__title,
.taf-tilecard__title,
.taf-tile .t,
.taf-tile h3.t {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; /* token-exempt: a line COUNT (2 lines), not a scale value */
  overflow: hidden;
  padding-bottom: var(--space-2);
  margin-bottom: calc(-1 * var(--space-2)); /* token-exempt: negative compensation for the descender padding above, not a scale step */
}

/* ==== Blog-post body polish (inc/post-polish.php; comp artifact d1a89cac).
   Scoped to .entry-content on single posts —
   finalized components (header, card, comments) are untouched. ==== */

/* ---- Jump To — option 1C "Quiet keyline" (handoff jumpto-1c, 2026-08-04;
   supersedes the 2026-08-02 JumpTo.dc.html build entirely).
   A single hairline box: square corners, no fill, no shadow, no rose top
   rule, NO ICONS ANYWHERE, plain-text Hide/Show, single-column numbered
   list with no dividers, hover changes text colour only.
   Copy is deliberately NOT the handoff's: it specified the label
   "In this post" and "Jump to the recipe card", but the settled call is
   that the wording stays as the site already has it. Visual spec from
   the handoff, words from the site.
   Markup stays <details>/<summary> rather than the
   handoff's nav+ol+button — native disclosure needs no JS and already
   exposes expanded state; the visible numbers are aria-hidden decoration
   either way, so <ul> is kept too.
   The handoff's half-pixel type (titles/CTA 15.5px, toggle 13.5px, numbers
   12.5px) is NOT reproduced. Those read as artefacts of
   a browser-rendered comp rather than intent, they cannot be tokens on a
   whole-pixel scale, and at this size the difference is sub-perceptual. Snapped
   to --fs-16 / --fs-13 / --fs-12, which also retired three magic values.
   Remaining comp-exact one-offs carry per-line exemptions (wave-8 precedent). ---- */
.taf-toc {
  margin: 28px 0;                     /* token-exempt: 28px comp margin — 1C exact */
  background: none;
  border: 1px solid #EFE2E8;          /* token-exempt: 1px #EFE2E8 comp keyline — 1C exact */
  border-radius: 0;                   /* square corners are the point of 1C */
  box-shadow: none;
  padding: var(--space-16) var(--space-18) var(--space-18);
}
.taf-toc__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-12);
  list-style: none; cursor: pointer;
  padding: 0 0 var(--space-14);
}
.taf-toc__bar::-webkit-details-marker { display: none; }
.taf-toc__bar:focus-visible { outline: var(--focus-ring) solid var(--focus-ring-color); outline-offset: -3px; }
.taf-toc__label {
  /* 1C spec says 11px. Review sized it up to 13px, tried 18px, and reverted:
     18 was too large against the toggle beside it. 13px is the settled value. */
  font-family: var(--ui); font-size: var(--fs-13); font-weight: var(--w-bold);
  letter-spacing: .16em; /* token-exempt: .16em comp label tracking */
  text-transform: uppercase; color: var(--cocoa);
}
/* Toggle: plain underlined text, no chevron. Mobile gets the 44px target. */
.taf-toc__state {
  display: inline-flex; align-items: center;
  min-height: var(--space-44);
}
.taf-toc__state em {
  display: none; font-style: normal;
  font-family: var(--sans); font-size: var(--fs-13); font-weight: var(--w-semibold);
  color: var(--cocoa); text-decoration: underline; text-underline-offset: 3px;
}
.taf-toc[open] .taf-toc__hide { display: inline; }
.taf-toc:not([open]) .taf-toc__show { display: inline; }
.taf-toc__bar:hover .taf-toc__state em { color: var(--rose-deep); }
.taf-toc ul.taf-toc__list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-2);
}
.taf-toc ul.taf-toc__list > li { margin: 0; }
.taf-toc__list a {
  display: grid; grid-template-columns: 22px 1fr; align-items: center; gap: var(--space-8);  /* token-exempt: 22px comp number column */
  padding: 7px 0; min-height: var(--space-44);  /* token-exempt: 7px comp row padding */
  text-decoration: none; color: var(--chocolate);
  transition: color var(--speed) var(--ease);
}
/* Hover/active recolours the row text and NOTHING else — no fill, no move. */
.taf-toc__list a:hover, .taf-toc__list a:focus-visible { color: var(--rose-deep); }
.taf-toc__num {
  font-family: var(--sans); font-size: var(--fs-12); font-weight: var(--w-semibold); color: var(--cocoa);
  /* Was the literal #B8A2AC ("comp number ink — quieter than the titles"). At 12px on --paper
     that measured 2.32:1 against a 4.5:1 requirement — a 2x miss, on EVERY recipe post carrying
     a Jump-To TOC (axe-core 4.10.2, 390 and 1440, 2026-08-05). --cocoa is 6.23:1 and is already
     this system's "quieter secondary ink" role token, so the design intent survives and the
     magic-value count drops by one. The numerals are aria-hidden, so WCAG's decorative exception
     is arguable — but at 12px they were hard for anyone to see. Fixed rather than argued. */
}
.taf-toc__list a:hover .taf-toc__num,
.taf-toc__list a:focus-visible .taf-toc__num { color: var(--rose-deep); }
.taf-toc__t {
  font-family: var(--sans); font-size: var(--fs-16); font-weight: var(--w-medium);
  line-height: var(--lh-14); text-wrap: pretty;
}
/* Recipe row: same grid so it aligns to the titles, empty number cell. */
.taf-toc__recipe a {
  display: grid; grid-template-columns: 22px 1fr; align-items: center; gap: var(--space-8);  /* token-exempt: 22px comp number column */
  padding: 7px 0; min-height: var(--space-44);  /* token-exempt: 7px comp row padding */
  background: none; border-top: 0; text-decoration: none;
  font-family: var(--sans); font-size: var(--fs-16); font-weight: var(--w-bold);
  color: var(--rose-link);
  transition: color var(--speed) var(--ease);
}
.taf-toc__recipe a > span { grid-column: 2; }
.taf-toc__recipe a:hover, .taf-toc__recipe a:focus-visible { background: none; color: var(--rose-deep); }
@media (min-width: 900px) {
  .taf-toc { padding: var(--space-20) var(--space-24) 22px; /* token-exempt: 22px desktop comp padding */ }
  .taf-toc__bar { align-items: baseline; }
  .taf-toc__state { min-height: 0; }
  .taf-toc__list a, .taf-toc__recipe a { align-items: baseline; padding: 6px 0; min-height: 0; /* token-exempt: 6px comp row padding */ }
}

/* ---- Sign-off band — rev 3: a real call to action.
   Rose top rule + tinted panel; LEFT-aligned copy; a solid "Leave a comment"
   button; glyph-only white tiles for the socials (labels move to sr-text).
   Desktop splits into text | actions with a hairline divider; mobile stacks
   with the divider above the "or reach out on" label. Glyphs are still the
   footer's simple-icons paths — one icon source sitewide. ---- */
.taf-signoff {
  /* --cream #FAEDF2, not --rose-tint #FCE7F3: the lighter, less saturated panel
     tint, and the one the recipe-card panels already use.
     NOTE THE NAMING TRAP: --cream IS the pink panel tint; --milk is the page
     ground. Putting --milk here would make the band vanish into the page. */
  background: var(--cream);
  /* No top rule. It existed only to "match the Jump-To card's cap", and 1C
     removed that cap — so the rule was matching nothing. Removed in review;
     a 3px accent across a rounded panel also fought the corners it ran
     into. The tint alone carries the band now. */
  border-radius: var(--radius);
  padding: var(--space-24) var(--space-20);
  margin: var(--space-32) 0 var(--space-24);
  /* Phone centres the whole band; the desktop split
     restores left alignment, where the copy sits beside the actions. */
  text-align: center;
}
.taf-signoff p.taf-signoff__head {
  font-family: var(--display); font-weight: var(--w-semibold);
  /* token-exempt: 24px/1.2 band head — per comp */
  font-size: 24px; line-height: 1.2; letter-spacing: var(--ls--001em);
  color: var(--chocolate); margin: 0;
  text-wrap: balance;
}
.taf-signoff p.taf-signoff__body {
  font-family: var(--sans); font-size: var(--fs-15); line-height: var(--lh-16);
  color: var(--cocoa); margin: var(--space-8) 0 0;
  text-wrap: pretty;
}
.taf-signoff__cta {
  display: flex; align-items: center; justify-content: center; gap: var(--space-10);
  background: var(--rose); color: var(--color-on-accent);
  font-family: var(--ui); font-size: var(--fs-16); font-weight: var(--w-bold);
  border-radius: var(--radius-sm); padding: var(--space-14, 14px) var(--space-20);
  min-height: 52px; margin-top: var(--space-16); text-decoration: none;
  transition: background var(--speed) var(--ease);
}
.taf-signoff__cta:hover, .taf-signoff__cta:focus-visible { background: var(--rose-deep); color: var(--color-on-accent); }
.taf-signoff__cta svg { width: 20px; height: 20px; flex: none; }
.taf-signoff p.taf-signoff__or {
  font-family: var(--ui); font-size: var(--fs-12); font-weight: var(--w-bold);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase; color: var(--cocoa);
  margin: var(--space-16) 0 var(--space-10);
  padding-top: var(--space-16); border-top: var(--bw-1) solid var(--line);
}
.taf-signoff__socials { display: flex; flex-wrap: wrap; gap: var(--space-10); justify-content: center; }
.taf-signoff__tile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; flex: none;
  background: var(--paper); color: var(--rose);
  border-radius: var(--radius-sm); text-decoration: none;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.taf-signoff__tile svg { width: 22px; height: 22px; }
.taf-signoff__tile:hover, .taf-signoff__tile:focus-visible { background: var(--rose); color: var(--color-on-accent); }
@media (min-width: 900px) {
  .taf-signoff {
    display: flex; align-items: center; gap: var(--space-32);
    padding: var(--space-32) var(--space-32);
    text-align: left;
  }
  .taf-signoff__text { flex: 1 1 auto; min-width: 0; }
  .taf-signoff p.taf-signoff__head { font-size: 28px; /* token-exempt: comp desktop head */ }
  .taf-signoff__actions {
    flex: 0 0 auto; padding-left: var(--space-32);
    border-left: var(--bw-1) solid var(--line);
  }
  .taf-signoff__socials { justify-content: flex-start; }

  /* Actions column compressed to the copy's height.
     Measured before: text 123px vs actions 156px — a 33px overhang that
     drove the band to 223px. Every value below still clears the site's
     44px touch minimum; the saving comes from the dead space between the
     three stacked rows, not from shrinking the targets past standard.
     After: actions 124px, band 187px. */
  .taf-signoff__cta {
    margin-top: 0;
    min-height: var(--space-44);
    padding: var(--space-8) var(--space-20);
  }
  .taf-signoff p.taf-signoff__or {
    border-top: 0; padding-top: 0;
    margin: var(--space-8) 0;
  }
  .taf-signoff__tile { width: var(--space-44); height: var(--space-44); }
}

/* ---- Roundup items: the WPRM
   Roundup Item / List blocks used by 7 listicle posts (~157 items).
   Untouched they render WPRM's factory look, which is the last plugin-default
   surface on the site — MEASURED before this landed: #EDEDED ground, 5px
   radius, Mulish 16.8px/700 name in #333, and a BLACK pill link. Everything
   below is CSS only; no stored content is touched, so it is fully reversible
   and carries no content risk.

   Selectors are scoped to .entry-content so the recipe card's own template
   (wprm-taf2026.css, DB-pushed separately) is never in scope. !important is
   needed on the ground/name/link colours because WPRM emits its own template
   CSS for these after ours.

   SHAPE, v0.95.0: a white card, the photograph FLUSH TO ITS LEFT EDGE and
   cropped to the card's height, the copy in a padded well on the right, and
   the rose arrow link the rest of the site uses.

   🔴 WHAT THIS REPLACED, AND WHY — it was fixing a defect, not just restyling.
   The 2026-08-07 roundup-template clone asked WPRM for a 500x500 crop instead
   of 100x100, which cured a 2.5x upscale on phones. But WPRM's own template
   CSS gives .wprm-recipe-image `flex-shrink: 0` and NOTHING sized the <img>,
   so on desktop the photo took its intrinsic 520px of a 696px content box and
   left the copy 160px — an eight-line description down a gutter, and a 552px
   tall item. Measured on the rendered page before the change. EVERY rule below
   that constrains the photo is load-bearing: delete `flex: 0 0 40%` or the
   img's `height: 100%` and the defect returns.

   The photo sits LEFT via `order: -1`, because WPRM emits the summary
   container FIRST in the DOM. Do not "fix" the order by reordering markup —
   that is a plugin template, and the reading order (name, summary, link, then
   photo) is the right one for a screen reader. ---- */
.entry-content .wprm-recipe-roundup-item {
  background: var(--paper) !important;
  border: var(--bw-1) solid var(--line) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow);
  /* The photo bleeds to the card edge, so the card clips it and carries no
     padding of its own — the padding moved onto the text column. */
  overflow: hidden;
  padding: 0 !important;
  gap: 0;
  margin: 0 0 var(--space-20) !important;
  align-items: stretch;
  /* ⚠️ BOTH OF THESE UNDO A WPRM RULE, AND IT IS INVISIBLE ON DESKTOP.
     The template's own CSS — inline in the page, from the
     `wprm_template_taf-roundup-summary` option, NOT a file you can grep in
     this repo — carries:
        @media only screen and (max-width: 640px) {
          .wprm-recipe-template-taf-roundup-summary {
            flex-wrap: wrap-reverse; text-align: center; }
        }
     so under 640px the name, the summary and the link all centred while the
     card stayed left-aligned. It bit BOTH the stacked phone layout AND the
     560-640px band, where the two-column layout is still in play, and it is
     invisible above 640. Removing these brings it straight back. */
  text-align: left;
  flex-wrap: nowrap;
  /* token-exempt: a crop floor, not a spacing step. Summaries run 87-151
     characters, so without it a short one collapses the card and the photo
     beside it crops to a letterbox. 212px is the tallest the copy gets at
     728px (24+28 padding, a 22px name, three 15px lines and the link). */
  min-height: 212px;
}
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-summary-container {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-12);
  padding: var(--space-24) var(--space-28) !important;
}
.entry-content .wprm-recipe-roundup-item .wprm-recipe-name {
  font-family: var(--display) !important;
  font-size: var(--fs-22) !important;
  font-weight: var(--w-bold) !important;
  line-height: var(--lh-125) !important;
  color: var(--chocolate) !important;
  text-wrap: balance;
}
.entry-content .wprm-recipe-roundup-item .wprm-recipe-summary,
.entry-content .wprm-recipe-roundup-item .wprm-recipe-summary span {
  font-family: var(--sans) !important;
  font-size: var(--fs-15) !important;
  line-height: var(--lh-155) !important;
  color: var(--cocoa) !important;
}
/* Three lines maximum. The longest live summary is 151 characters, which is
   exactly three lines at this width — the clamp is the guard for the next one
   somebody writes, not a crop of anything on the site today. */
.entry-content .wprm-recipe-roundup-item .wprm-recipe-summary {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The black pill becomes a rose text link with the site's arrow affordance.
   The LABEL is per-item stored data ("Recipe Here!") — deliberately not
   rewritten here; renaming it is a content edit, not a restyle. */
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-link {
  background: none !important;
  padding: 0 !important;
  border-radius: 0 !important;
  /* WPRM's inline-button style carries `border: 1px solid #000`. Zeroing the
     background/padding/radius WITHOUT this left a black hairline box hugging
     the text — caught in a screenshot AFTER this component was "verified"
     by reading a hand-picked set of computed properties, which of course
     never included border. Look at the thing. */
  border: 0 !important;
  margin: 0 !important;
  color: var(--rose-deep) !important;
  font-family: var(--ui) !important;
  font-size: var(--fs-13) !important;
  font-weight: var(--w-bold) !important;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  text-decoration: none !important;
  box-shadow: var(--shadow-underline) var(--cream);
  position: relative;
}
/* The text itself is 21.4px tall, under the 24px WCAG 2.2 target minimum (the
   14px version it replaced was 23.1px — also short). This grows the HIT AREA
   to 44px without moving a pixel of the type or the underline. It extends
   11.3px above the link, and the gap above it is 12px, so it never covers the
   summary text. Same technique as the drawer's Popular link. */
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-link::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: var(--space-44);
}
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-link::after { content: " \2192"; }
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-link:hover,
.entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-link:focus-visible { text-decoration: underline !important; }
/* The photo column. `align-self: stretch` makes it as tall as the card, and
   the image fills it absolutely — a 40%-wide SQUARE image would otherwise set
   the card's height itself and the crop would never happen. */
.entry-content .wprm-recipe-roundup-item .wprm-recipe-image {
  order: -1;
  flex: 0 0 40%;
  align-self: stretch;
  position: relative;
  overflow: hidden;
  padding: 0 !important;
}
/* Social Pug wraps the <img> so its Pin button can sit top-left. Stretching
   the WRAPPER (not just the img) keeps that button inside the photo. The
   `> img` rule is the fallback for any item WPRM renders without a wrapper.

   ⚠️ `position` AND `display` NEED !important, AND THE REASON IS MEASURED:
   Social Pug ships `.dpsp-pin-it-wrapper { display: inline-table; position:
   relative !important; }` in style-frontend-pro.css. Without the override the
   wrapper stayed in flow, the 500px square image set the photo column's own
   height, and the column then set the CARD's height — every item came out
   300px tall with no crop at all. It looked plausible, which is why it has to
   be measured rather than eyeballed. */
.entry-content .wprm-recipe-roundup-item .wprm-recipe-image .dpsp-pin-it-wrapper,
.entry-content .wprm-recipe-roundup-item .wprm-recipe-image > img {
  display: block !important;
  position: absolute !important;
  inset: 0;
  width: 100% !important;
  max-width: 100% !important;
  height: 100% !important;
}
.entry-content .wprm-recipe-roundup-item .wprm-recipe-image img {
  display: block;
  width: 100% !important;
  height: 100% !important;
  object-fit: cover;
  /* WPRM writes `border-radius: 5px` as an INLINE style on the img, so this
     needs !important to lose the corner the card now owns. */
  border-radius: 0 !important;
}
.entry-content .wprm-recipe-roundup-item .wprm-spacer { display: none !important; }

@media (max-width: 559px) {
  /* Phone: stack, photo first — a thumb beside three lines of summary leaves
     the text column too narrow to read. The card keeps its clip, so the photo
     still reaches both edges. */
  .entry-content .wprm-recipe-roundup-item {
    flex-direction: column;
    align-items: stretch;
    min-height: 0;
  }
  .entry-content .wprm-recipe-roundup-item .wprm-recipe-image {
    flex: 0 0 auto;
    width: 100% !important;
    max-width: 100% !important;
    aspect-ratio: 3 / 2;  /* token-exempt: photo crop ratio, not a spacing/size scale value */
  }
  .entry-content .wprm-recipe-roundup-item .wprm-recipe-roundup-summary-container {
    justify-content: flex-start;
    padding: var(--space-18) var(--space-18) var(--space-20) !important;
  }
  .entry-content .wprm-recipe-roundup-item .wprm-recipe-name { font-size: var(--fs-19) !important; }
}

/* ---- Seasonal PAGES (/summer/, /fall/, /holidays/).
   These three are regular WP pages, not category archives, so they get
   Kadence's page hero rather than our archive header. Three complaints, three
   measured causes:
     1. "blue tint" → the band is Kadence --global-palette7 #F9EEE4, a warm
        sand that reads cold beside the Warm Oven pinks. Retinted to the
        theme's own rose panel tint.
     2. "title band too tall" → Kadence inline CSS sets
        `.entry-hero.page-hero-section .entry-header { min-height: 200px }`
        for a 48px title. Halved.
     3. "padding between the title and the tile row too large" → measured 112px
        of dead space under the band: .content-area margin-top 80 +
        .entry-content-wrap padding-top 32. Both trimmed on these pages only.
   Scoped to .page-hero-section, which ONLY these three pages carry (verified:
   /about/, /recipe-index/ and /contact/ render no hero at all). ---- */
.entry-hero-container-inner { background: var(--rose-tint) !important; }
.entry-hero.page-hero-section .entry-header { min-height: var(--space-90) !important; }
.page-hero-section + .content-area,
.entry-hero.page-hero-section ~ .content-area { margin-top: var(--space-24) !important; }
.page-hero-section ~ .content-area .entry-content-wrap { padding-top: var(--space-16); }

/* ---- Section-title rhythm ----
   THE PINK UNDERLINE IS GONE, SITEWIDE ("remove the pink
   underline from all h2s across the site"). It was a 2px --rose-tint
   border-bottom under every h2 in .entry-content — ~600 recipe posts plus every
   page. Headings now carry their own weight on size and face alone, which is
   what the rest of the article vocabulary already does.
   The 8px padding-bottom STAYS: it was the gap between the heading and its
   rule, and it is now simply the gap before the content. Removing it too would
   pull every h2 8px closer to its first paragraph on every page on the site —
   a second, unasked-for change riding along with this one. */
.entry-content > h2, .entry-content .wp-block-group h2 {
  padding-bottom: var(--space-8);
}

/* ---- Callout unification: the hard-coded #eef3f4 tip/reader-love/quick-pick
   groups adopt the Warm Oven milk ground (inline style needs !important) ---- */
/* Any tinted element, not just groups: the author's 2026 "review pitch" is a
   PARAGRAPH carrying the same #eef3f4 inline background (4+ posts), so the
   group-only selector left it raw blue-grey mid-article. */
.entry-content .has-background[style*="eef3f4"] {
  /* --cream (#FAEDF2) is the Warm Oven PANEL tint; --milk is the page ground.
     Shipping --milk here painted the box invisible and Kadence's 30px group
     padding read as random indentation. */
  background-color: var(--cream) !important;
  border-radius: var(--radius);
  /* ALIGNMENT: Kadence's group padding insets the callout's
     TEXT ~30px from the body column. Bleed the panel out by its own inline
     padding so the text lands on the body text's left edge and only the tint
     extends past it. */
  padding-inline: var(--space-12) !important;
  margin-inline: calc(-1 * var(--space-12));
}
/* A tinted PARAGRAPH has no group padding of its own — give it the panel's. */
.entry-content p.has-background[style*="eef3f4"] {
  padding: var(--space-16) var(--space-16) !important;
  margin-inline: 0;
}

/* ---- Why-you'll-love list (server-side transform adds the class) ----
   The class is now STRUCTURAL ONLY. It used to paint a saffron ✦ (U+2726) via
   li::before with list-style:none and a 24px indent; a review decision was
   plain bullets, here and anywhere else the theme invented a marker (this was the
   only one in prose — the recipe card's Notes ⓘ is a separate, approved component).
   Deliberately NO rules: with the override gone the list inherits the same cascade
   as any other prose list — Kadence's `ul { disc }` plus `.single-content ul
   { padding-left: 2em }` (36px at the 18px body size). Re-declaring those values
   here would only let them drift out of sync. post-polish.php still emits the class,
   because the paragraphs-to-list transform needs a hook and a name.
   Verified rendered: identical marker/indent/spacing to a sibling ul.wp-block-list. */

/* ---- "See the recipe card" pointer: quiet, already-italic line ---- */
.entry-content .taf-card-pointer { font-size: var(--fs-15); color: var(--cocoa); }

/* ---- Authoring slot markers (owner-editing C4 post kit) -------------------
   BLOCKING PAIR: this rule and patterns/section-*.php ship in the same deploy.
   Three of the post patterns stamp a `taf-slot` paragraph — the video slot, the
   recipe-card slot and the "this section is optional" line in Variations. They
   are instructions addressed to the author, and a reader must never see one.
   Without this rule a post published before she deletes them prints
   "RECIPE CARD SLOT — insert the WP Recipe Maker block here" to the world.

   FRONT END ONLY, on purpose: the editor loads assets/css/editor.css instead,
   so the slot lines stay visible while she writes, which is their entire job.

   ZERO EFFECT ON EXISTING CONTENT — re-verified 2026-08-06 against the live
   database: `taf-slot` occurs in 0 published posts and 0 pages, and nowhere
   else in this stylesheet. Only content stamped from the C4 patterns can match.
   Scoped to .entry-content so it can never reach an admin screen. */
.entry-content .taf-slot { display: none; }

/* (Pin frame retired before ship — post-card pin images
   stay plain. Only breathing room above them remains.) */
.entry-content .wprm-recipe-container ~ figure.wp-block-image,
.entry-content .wprm-recipe-container ~ .wp-block-image {
  margin-top: var(--space-24);
}

/* ---- Cruft guards: empty paragraphs/list items ship in ~100 posts ---- */
.entry-content p:empty, .entry-content li:empty { display: none; }

/* ---- FSRI tile titles: match the house tile line-height ---- */
.entry-content .fsri-title { line-height: var(--lh-13); }

/* ---- FAQ accordion: long questions keep the arrow beside the text ---- */
.entry-content .schema-faq-section summary { flex-wrap: nowrap; }

/* ---- Theme-owned FAQ cards (post-polish faq transform; replaces Feast's
   injected accordion). Native <details>, no JS. ---- */
.taf-faq { margin: 0 0 var(--space-16); }
.taf-faq__item {
  background: var(--paper);
  border: var(--bw-1) solid var(--line);
  border-radius: var(--radius-sm);
  margin: 0 0 var(--space-8);
}
.taf-faq__q {
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-8);
  list-style: none; cursor: pointer;
  font-weight: var(--w-semibold); font-size: var(--fs-16); color: var(--chocolate);
  padding: var(--space-12) var(--space-16); min-height: 44px;
}
.taf-faq__q::-webkit-details-marker { display: none; }
.taf-faq__q:focus-visible { outline: var(--focus-ring) solid var(--rose); outline-offset: 2px; border-radius: var(--radius-sm); }
.taf-faq__caret { color: var(--rose); flex: none; transition: transform var(--speed) var(--ease); }
.taf-faq__item[open] .taf-faq__caret { transform: rotate(180deg); }
.taf-faq__a { padding: 0 var(--space-16) var(--space-12); color: var(--cocoa); }
.taf-faq__a img { max-width: 100%; height: auto; border-radius: var(--radius-sm); }
.taf-faq__a a { color: var(--rose); }

/* Post-FAQ type: 16 -> 18. FAQ answers are body prose in the
   reading column and were the only prose on a recipe post still at 16 after the
   body moved to 18.

   ✅ CONFIRMED in review WITH its known cost: questions wrap more
   at 18 on a phone, so the cards run taller. That was put to her and accepted —
   taller cards are the price of FAQ prose matching body prose. Do not "fix" the
   extra wrapping by dropping back to 16.

   TWO CLASSES ON PURPOSE. The About-page FAQ rules further up this sheet —
   `.taf-faq summary` (0,1,1) and `.taf-faq details > div` (0,1,2) — also match
   this markup and were already OUT-SPECIFYING `.taf-faq__q` / `.taf-faq__a`
   (0,1,0). That is why `.taf-faq__a`'s own `font-size: var(--fs-15)` never
   painted (measured 16px, not 15px) and has now been deleted rather than left
   as a decorative lie. (0,2,0) here wins cleanly.

   ABOUT IS UNAFFECTED: `__q`/`__a` are emitted ONLY by inc/post-polish.php's
   FAQ transform, and both of its paths guarantee a `.taf-faq` ancestor (the
   attribute path wraps in one; the HTML fallback appends the class to the
   existing wrapper). About's pattern FAQ has neither class and stays 16. */
.taf-faq .taf-faq__q { font-size: var(--fs-18); }
.taf-faq .taf-faq__a { font-size: var(--fs-18); }

/* ---- Step columns render 2-up at ALL widths (ours stay side-by-side) ---- */
.entry-content .wp-block-columns > .wp-block-column {
  flex-basis: 0 !important; /* beats core's mobile flex-basis:100%!important — same specificity, later sheet */
  flex-grow: 1;
  min-width: 0;
}

/* ---- Review pitch (post-polish): the author's "leave a review" line, rendered
   as a component instead of a raw tinted paragraph — copy left, CTA right,
   stacking on phones. ---- */
.taf-reviewpitch {
  display: flex; align-items: center; gap: var(--space-16); flex-wrap: wrap;
  background: var(--cream);
  border: var(--bw-1) solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-16) var(--space-20);
  margin: var(--space-24) 0;
}
.taf-reviewpitch p.taf-reviewpitch__text {
  flex: 1 1 260px; margin: 0;
  font-family: var(--sans); font-size: var(--fs-15); line-height: var(--lh-15);
  color: var(--chocolate); text-wrap: pretty;
}
.taf-reviewpitch__cta {
  display: inline-flex; align-items: center; gap: var(--space-8);
  flex: none; min-height: 44px;
  font-family: var(--ui); font-size: var(--fs-15); font-weight: var(--w-bold);
  color: var(--rose); text-decoration: none; white-space: nowrap;
}
.taf-reviewpitch__cta svg { width: 15px; height: 15px; transition: transform var(--speed) var(--ease); }
.taf-reviewpitch__cta:hover, .taf-reviewpitch__cta:focus-visible { color: var(--rose-deep); text-decoration: underline; }
.taf-reviewpitch__cta:hover svg { transform: translateX(2px); }

/* ---- Right-edge overhangs ("top tips / reader love / share
   icon are all extending over the right edge of the content"). Two unrelated
   causes that produce the same symptom, both found by LOOKING — the box
   measurements said flush in one case and the text ink was outside the box. ---- */

/* 1. Social Pug's share total. The wrapper and both inner spans are pinned to a
   40px box with `text-align: center`, and the word "SHARES" is WIDER than 40px
   at its rendered size — so the glyphs overflow their own box symmetrically and
   paint ~6px past the content column on each side. Every bounding-box reading
   is 806 = flush, which is exactly why this survived a measurement pass: the
   BOX is honest, the INK is not. Letting the block size to its content puts the
   label back inside the column. */
.taf-post-content .dpsp-total-share-wrapper,
.taf-post-content .dpsp-total-share-wrapper .dpsp-total-share-count,
.taf-post-content .dpsp-total-share-wrapper > span { width: auto; }

/* 2. the author's hand-wrapped tinted groups — "Top tips", "Reader Love ❤️" and
   friends. These carry `margin: 0 -12px` with `padding: 20px 12px`, a bleed
   that keeps the TEXT on the body measure while the tint runs 12px wider on
   each side. That is a legitimate idiom, but on this template it reads as the
   panel breaking the column, because nothing else here bleeds. Zero the pull
   and keep the padding: the tint now ends on the column and the copy insets,
   which is the relationship every other panel on the page already has.
   Scoped to in-content groups so full-bleed band patterns are untouched. */
.taf-post-content > .wp-block-group.has-background { margin-left: 0; margin-right: 0; }

/* ---- About page: the four 2026-08-04 review items that are not one-liners ----
   Scoped to .page-id-21 throughout. These are ABOUT-page decisions, and every
   selector below would otherwise be a sitewide change wearing a page build's
   clothes — `.content-bg` in particular is on every post and page. ---- */

/* 1. NO WHITE CARD. Kadence boxes the content area: `.entry.content-bg` ships
   white with a 4px radius and a 15px drop shadow, so the page rendered as a
   floating white slab on the milk ground and every tinted band stopped 40px
   short of the viewport. The handoff is explicit — "one reading column on
   --milk for every section, no cards, no boxes, no shadows" — so the card is
   removed here rather than the bands being made to live inside it. */
.page-id-21 .entry.content-bg {
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}

/* 2 + 3. TINTED FULL-BLEED BANDS for the e-book magnet and "As seen on"
   ("make the ebook component full width colored background like the
   quote section", "as seen on gets the colored background section").
   Same treatment as .taf-abt-inspowrap, which is the band they are being asked
   to match; the gutter deliberately stays on the INNER .taf-section, because
   that box is border-box and already carries --pad — putting it on the
   full-bleed wrapper instead is the alignment bug this page has already had
   once. */
.page-id-21 .taf-abt-bandwrap {
  background: var(--bg-tinted);
  padding: var(--space-32) 0;
  border-top: var(--bw-1) solid var(--border-hairline);
}
/* The magnet card loses its own fill inside the band — a rose-tint card on a
   cream band is two tints doing one job, and the BAND is meant to carry the
   colour. Its own padding goes too; the band supplies it. */
.page-id-21 .taf-abt-bandwrap .taf-magnet {
  background: transparent;
  border: 0;
  padding: 0;
}
/* The band already draws the section's top hairline, so the section inside it
   must not draw a second one 32px lower. */
/* The section rule is a ::before, NOT a border-top (see the .taf-abt--rule
   block earlier in this file — it is a pseudo so the hairline can hang on the
   664px text measure instead of the 728px border-box). Zeroing `border-top`
   here therefore did nothing, and a hairline kept painting ~60px INSIDE the
   cream band, above "As seen on", which is what review reported.
   Worth recording how it survived a check: an element-by-element scan of every
   computed `border-top` in the band came back clean, because a pseudo-element
   is not in `querySelectorAll('*')`. Only the screenshot showed it. */
.page-id-21 .taf-abt-bandwrap .taf-abt--rule::before { content: none; }

/* Equal air top and bottom ("reduce top and bottom padding,
   both paddings should be equal"). Measured before: the magnet band ran 57px of
   space above its content and 80px below, because the inner .taf-section
   contributed 0 top / 40 bottom on top of the band's own 32/32. Both inner
   paddings are now zeroed and the band alone owns the vertical rhythm, so the
   two bands also match each other instead of each having its own arithmetic. */
.page-id-21 .taf-abt-bandwrap > .taf-section {
  padding-top: 0;
  padding-bottom: 0;
}
.page-id-21 .taf-abt-bandwrap { padding: var(--space-32) 0; }

/* The magnet's supporting line is one sentence and should read as one line on
   desktop. Its 42ch cap wrapped it at "straight to / your inbox." inside a
   664px column that had room for all of it. */
.page-id-21 .taf-abt-bandwrap .taf-magnet p { max-width: none; }

/* Zeroing the inner .taf-section got the press band to 33/32 but left the
   magnet at 57/40: the .taf-magnet card carries its own vertical margin, so the
   band's symmetric 32/32 was landing on an asymmetric child. Collapse the card's
   own margins and let the band own the rhythm for both. Measured, not assumed —
   the two bands are compared against each other after every change here. */
.page-id-21 .taf-abt-bandwrap .taf-magnet { margin-top: 0; margin-bottom: 0; }
.page-id-21 .taf-abt-bandwrap .taf-magnet > :first-child { margin-top: 0; }
.page-id-21 .taf-abt-bandwrap .taf-magnet > :last-child { margin-bottom: 0; }

/* ---- Header: one control below 900px ------------------
   The standalone search toggle is removed on phone and tablet and the menu
   toggle becomes a COMBINED menu+search glyph, so the single button honestly
   advertises both things behind it. This is not a reduction in function: the
   drawer already opens with a full search field focused at the top, so search
   was never lost — it was just being offered twice in a 44px-crowded header
   (brand, search, "Free e-book", menu, inside 390px).
   >=900 is untouched: the desktop header keeps its own search affordance. ---- */
@media (max-width: 899.98px) {
  .site-header .search-toggle-open { display: none; }

  /* Kadence renders its own hamburger inside .kadence-svg-iconset. Hide that
     and mask our combined glyph over the button instead of filtering Kadence's
     markup — the icon then inherits the button's colour and every hover/focus
     state Kadence already ships, and reverting is deleting this block.
     The SVG is a data URI for the same reason the facts-strip icons are. */
  .site-header .menu-toggle-open .kadence-svg-iconset { display: none; }
  .site-header .menu-toggle-open::before {
    content: "";
    display: block;
    /* 24 -> 28 ("should it not be a little bigger?"). It is
       now the ONLY control in the header besides the e-book button, so it was
       carrying a search affordance as well as a menu one at the size the plain
       hamburger used to be. The 44px tap target is unchanged — this grows the
       ink inside it, not the button. */
    width: var(--space-28, 28px);
    height: var(--space-28, 28px);
    background-color: currentColor;
    -webkit-mask: var(--taf-menusearch) center / contain no-repeat;
    mask: var(--taf-menusearch) center / contain no-repeat;
  }
}
/* Declared outside the query so the token reads as a theme asset rather than a
   breakpoint detail.
   REDRAWN 2026-08-04 ("the new search/burger glyph is kinda weird"). The first
   attempt kept three FULL rules and dropped a small lens on top of the third,
   so the lens read as a smudge over the hamburger rather than as a magnifier —
   the two ideas were fighting for the same pixels.
   Now the rules get out of its way: line 1 spans, lines 2 and 3 stop at 12 to
   open a well on the right, and the lens sits in that well at r=4.6 with a
   proper 3px handle running past the baseline. Stroke drops 2 -> 1.9 because
   at 28px a 2px stroke closed up the lens interior. */
:root {
  --taf-menusearch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Cpath d='M2.5 6h19M2.5 12h9.5M2.5 18h9.5'/%3E%3Ccircle cx='17' cy='14.6' r='4.6'/%3E%3Cpath d='M20.4 18 22.6 20.2'/%3E%3C/svg%3E");
}

/* ---- Drawer: recipe-index CTA, directly under the search block ----------
   Full-width so it reads as the drawer's second answer to "find me a recipe"
   (search being the first), not as another link in a list. Rose-tint ground
   rather than solid rose: the drawer already pins a solid rose e-book button
   at its foot, and two solid rose blocks in one panel would compete. ---- */
.taf-mm-index {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-12);
  margin-top: var(--space-14);
  padding: var(--space-14) var(--space-16);
  min-height: var(--space-44);
  background: var(--rose-tint);
  color: var(--rose-deep);
  border-radius: var(--radius-sm);
  font-family: var(--display);
  font-size: var(--fs-15);
  font-weight: var(--w-bold);
  text-decoration: none;
}
.taf-mm-index svg { flex: none; }
.taf-mm-index:hover,
.taf-mm-index:focus-visible { background: var(--rose); color: var(--color-on-accent); }

/* ---- Reader Love -> design option B3 "Display quote" ---------------------
   Approved from the component-design deck. Server side is
   inc/post-polish.php (taf2026_rl_render); this file only has to draw it.

   B3 is loud through SCALE, not colour: no panel, no fill, no border, no radius.
   That is the entire reason it survives on a page carrying Raptive ad units —
   a coloured panel in the article body reads as inventory. Everything the old
   treatment leaned on (the #eef3f4 cream group, the plugin's star PNG, the core
   block's near-black bar) is gone at the source, not overpainted.

   TOKEN SNAPS from the specimen (deck .r3). Recorded because they are
   deliberate, and small enough that the next reader would otherwise "fix" them:
     letter-spacing -0.015em -> var(--ls--001em) (-0.01em, nearest token)
     label tracking 0.16em -> var(--ls-14em) (0.14em, the eyebrow tracking)
     stars 17px SVG box -> var(--star-interactive), a 20px GLYPH. The site's
       star system is a text ★ (taf2026_star_glyphs, the 2026-08-01 rework), not
       an SVG. A ★ inks about 0.72em, so a 20px glyph draws ~14.4px of star
       against the 17px box's ~15px. Snapping to --star-display (17px) would have
       shipped stars visibly SMALLER than the design, not equal to it.
   The rest is on-token as drawn: 22/1.35/500 display quote, 12/700/uppercase
   rose-deep attribution. ---- */
.entry-content .taf-readerlove {
  /* 40, not 32. With the heading gone there is no label to announce the
     section, so the space has to do that job on its own — and 32px measured
     barely above the body's own paragraph rhythm (18px x 1.65 leading), which
     read as "another paragraph" rather than "a different object". Measured at
     390 and 1280 before and after. */
  margin: var(--space-40) 0;
  text-align: center;
}
/* NO LABEL AND NO HEADING ("remove the title 'reader love' and
   can it not appear in the ToC"). the author's <h2> is dropped server-side, which
   is also what the deck's B3 stage drew. Nothing announces the section — that
   is what keeps it reading as part of the article and not as a widget. The ToC
   follows for free: the heading is gone before the H2 scan runs. */

/* TWO QUOTES: lead + echo, stacked. Never side by side — see the rationale in
   post-polish.php. The echo keeps the same centred axis and face and only drops
   scale, so it reads as the same component speaking again rather than as a
   different one. */
/* Measure cap lives on the FIGURE, not on the quote. 30em here resolves against
   the inherited 18px body size (540px); on the quote itself em would resolve
   against the quote's OWN size, so the box would have got NARROWER exactly as
   the copy got longer and stepped down — backwards, and it shipped that way for
   one deploy (measured 660px at 22px before this moved). Does not bind at 390px. */
.entry-content .taf-readerlove__q { margin: 0 auto; padding: 0; max-width: 30em; }
.entry-content .taf-readerlove__q + .taf-readerlove__q {
  position: relative;
  margin-top: var(--space-24);
  padding-top: var(--space-24);
}
/* The beat between two voices: a SHORT centred hairline, not a full-measure
   rule — full width would read as a section break between two things.
   ⚠️ This is a ::before. It is the ONLY rule this component draws, and an audit
   that scans borders over querySelectorAll('*') will NOT see it (that exact
   miss happened on this site on 2026-08-04). It is here and it is intended. */
.entry-content .taf-readerlove__q + .taf-readerlove__q::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--space-40);
  margin-left: calc(-1 * var(--space-20));
  border-top: var(--bw-1) solid var(--border-hairline);
}

.entry-content .taf-readerlove__stars {
  display: block;
  color: var(--saffron);
  font-size: var(--star-interactive);
  line-height: var(--lh-1);
  letter-spacing: var(--ls-1px);
  margin-bottom: var(--space-14);
}
.entry-content .taf-readerlove__stars .taf-star-off { color: var(--line); }
.entry-content .taf-readerlove__q--echo .taf-readerlove__stars {
  font-size: var(--star-meta);
  margin-bottom: var(--space-10);
}

.entry-content .taf-readerlove__text {
  font-family: var(--display);
  font-weight: var(--w-medium);
  font-size: var(--fs-22);
  line-height: var(--lh-135);
  letter-spacing: var(--ls--001em);
  color: var(--chocolate);
  text-wrap: balance;
  margin: 0 auto;
  padding: 0;
  border: 0;      /* the core block's 4px #33202B bar, dead at the source */
  quotes: none;   /* the marks are real characters in the text, not generated */
}
/* Long copy steps DOWN in size rather than being trimmed. The deck's note said
   "long quotes must be trimmed to fit" — we do not edit a reader's words. The
   thresholds are the real corpus, measured: p50=141, p75=241, p90=340 chars,
   longest 521. 23 of the 62 quotes land on a step. */
.entry-content .taf-readerlove__q--lead.is-long  .taf-readerlove__text { font-size: var(--fs-20); }
/* ⚠️ KNOWN LIMIT, NOT AN OVERSIGHT. 7 of the 62 quotes are over 320 characters;
   the longest is 521 and renders as 15 CENTRED lines at 390px (measured: 398px
   tall). Centred type stops being a display gesture at that length and just
   makes the reader work. The fix is a left-aligned xlong variant OR trimming
   the comment — both are design/editorial calls not yet made, and 2 of
   those posts carry a second, short quote, so a per-quote left variant would
   mix two axes inside one component. Flagged rather than invented. */
.entry-content .taf-readerlove__q--lead.is-xlong .taf-readerlove__text { font-size: var(--fs-18); }
.entry-content .taf-readerlove__q--echo .taf-readerlove__text {
  font-size: var(--fs-17);
  line-height: var(--lh-14);
}
.entry-content .taf-readerlove__q--echo.is-long .taf-readerlove__text,
.entry-content .taf-readerlove__q--echo.is-xlong .taf-readerlove__text { font-size: var(--fs-16); }

.entry-content .taf-readerlove__who {
  font-family: var(--display);
  font-size: var(--fs-12);
  font-weight: var(--w-bold);
  letter-spacing: var(--ls-14em);
  text-transform: uppercase;
  color: var(--rose-deep);
  text-align: center;
  margin: var(--space-16) 0 0;
}
.entry-content .taf-readerlove__q--echo .taf-readerlove__who { margin-top: var(--space-12); }

/* ---- The core quote block's near-black bar, killed everywhere -------------
   Measured on the rendered page (/spicy-millet-stuffed-tomatoes/, before this component):
   `border-left: 4px solid rgb(51, 32, 43)`. #33202B is --chocolate, an INK, and
   it appears nowhere else on this site as a rule. Reader Love no longer uses the
   block at all, but 4 published posts carry an ordinary wp:quote elsewhere and
   the editor can always add another, so the bar dies at the source rather than
   only inside the component.
   Swapped for the blush hairline every other rule in the theme uses instead of
   being removed outright: the indent is what still says "this is quoted". ---- */
.entry-content blockquote.wp-block-quote {
  border-left: var(--bw-15) solid var(--border-hairline);
}

/* The pattern's typed ★ rating line. Deliberately NOT prefixed with
   .entry-content: setup.php calls add_editor_style('assets/css/main.css'), which
   rewrites every selector under .editor-styles-wrapper, and the editor canvas
   has no .entry-content — so an .entry-content rule is invisible in the editor.
   This is the one rule in the component that has to reach the author's DRAFT, so
   she can see that the stars are the rating and not decoration.
   On the front end the star line is consumed by the transform and never renders,
   EXCEPT when the transform conservatively aborts — in which case this makes the
   fallback look deliberate rather than like a stray line of asterisks. */
.taf-rl-stars {
  color: var(--saffron);
  font-size: var(--star-interactive);
  letter-spacing: var(--ls-1px);
  text-align: center;
}

/* ---- Intro card on phones ("this looks off on mobile, fix
   positioning and spacing and alignment"). Two faults, one cause and one
   layout:
   1. Its children carried WP flow-layout's 32px block-gap, so avatar -> body
      -> link were spaced 32px apart on top of the flex `gap`. Killed by the
      neutraliser above, which now covers this card too.
   2. `.taf-introcard__card` is `flex-direction: row` with three children. On a
      350px card that wraps, which is why the avatar sat alone on its own line
      with the heading stranded below it — a wrapped row, not a designed
      column. Below 700 it is now an explicit column, left-aligned, so the
      avatar, the copy and the link share one edge instead of being centred
      against a row axis that no longer exists. ---- */
@media (max-width: 699.98px) {
  /* AVATAR BESIDE THE NAME. The portrait and the "Hi, I'm
     the author" heading now share a row, with the bio and the link full width
     underneath — the introduction reads as one unit instead of three stacked
     blocks with a floating headshot on top.

     The two live in different parents (figure, then h2 inside __body), so the
     card becomes a grid and `__body` is `display: contents` — its heading and
     paragraph are promoted into the card's own grid and placed individually.
     That is what avoids a markup change; the pattern file is untouched.
     `display:contents` is safe on this element specifically: the old
     accessibility bug applied to elements with an implicit ARIA role, and this
     is a generic wp-block-group div with none. */
  .taf-introcard__card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--space-14);
    row-gap: var(--space-12);
    align-items: center;
  }
  .taf-introcard__body { display: contents; }
  .taf-introcard__avatar {
    grid-column: 1; grid-row: 1;
    width: max-content; margin: 0;   /* the figure is block and would take the whole track */
  }
  .taf-introcard__body > h2 { grid-column: 2; grid-row: 1; }
  .taf-introcard__body > p  { grid-column: 1 / -1; grid-row: 2; }
  .taf-introcard__link      { grid-column: 1 / -1; grid-row: 3; }
  /* The card's own bottom padding was 4px against 24px at the top — the link
     used to hang below the flex row and needed the room. It no longer does, so
     the box closes symmetrically. */
  .taf-introcard { padding-bottom: var(--space-24); }
}
