/*!*************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[2]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[14].oneOf[10].use[3]!./app/globals.css ***!
  \*************************************************************************************************************************************************************************************************************************************************************/
/*
 * Design tokens.
 *
 * Brand values come from the SPG COLOR TOKENS page of the Venom Pro design
 * hub (Figma bDE6KaQqNkFojkaKoeZNAK), which calls itself "the source of
 * truth for app + controller theming". Names match Kyle's exactly so the
 * three surfaces stay talking about the same colours.
 *
 * The audit's contrast decisions still govern which of those values is
 * allowed where, so the pairings that failed WCAG 1.4.3 on the Shopify theme
 * cannot come back by accident. Every number below is measured, not
 * estimated, and the axe gate re-measures on every change.
 */
:root {
  /*
   * ---- brand, from the Spider Grills Brand Guide r2 (2026-08-08) ----
   * Figma eCvGsAPayl4yL9F4UlTmoC, project "Spider Grills Brand Book".
   * Token names are the published style names; do not rename them - the app,
   * the controller firmware and this site all read the same names.
   */
  --red: #972219;           /* spg-red   · THE LEAD. CTA, active, alarm. 8.21:1 on white */
  --ink: #231f20;           /* spg-black · primary canvas. 16.30:1 on white */
  --charcoal: #303030;      /* spg-charcoal · raised surface, 13.20:1 on white */
  --stone: #b9b7b5;         /* spg-stone · mid neutral. 2.00:1 on white, 8.15:1 on ink */
  --paper: #e6e6e6;         /* spg-white · body text on dark. 13.06:1 on ink */

  /*
   * spg-gold moved in r2, from the heritage #ab8846 to #8d703a.
   *
   * The old value was 3.31:1 on white and could not carry text at all, which
   * is what made a gold-forward direction impossible on a light site. The new
   * value is the one the book settled on after establishing that NO single
   * gold clears 4.5:1 on both white and spg-black - darkening it to pass on
   * white necessarily breaks it on dark. So:
   *
   *   4.65:1 on white     - fine as body text on light surfaces
   *   4.23:1 on --surface - large text and graphics only; see the rebinding
   *                         further down for the on-surface variant
   *   3.50:1 on --ink     - clears the 3:1 non-text bar, so marks, hairlines
   *                         and 24px+ headings are fine, but it is NEVER body
   *                         copy on a dark field. Use --paper or --stone.
   *
   * Both mark artworks were recoloured to match, so public/brand/sg-seal.*
   * carries #8d703a now and must be re-exported if the token moves again.
   */
  --gold: #8d703a;

  /*
   * Derived. The book gives the palette; these are the pairings the site
   * needs that the book does not name, each measured rather than assumed.
   *
   * --on-gold is the one that changed meaning. It used to be ink, which was
   * 4.92:1 against the old gold; against the new gold ink is only 3.50:1 and
   * fails as small text. White is 4.65:1, so text on a gold fill is white.
   */
  --on-gold: #ffffff;       /* text ON gold - 4.65:1 */
  --gold-text: #8d703a;     /* same value as --gold now; kept so the surface
                               rebinding below has something to rebind */
  --on-red: #ffffff;        /* text ON red - 8.21:1 */

  --muted: #757575;         /* body grey on white - 4.61:1 */
  /*
   * The audit picked #757575 against white. On the tinted surface it drops
   * to 4.19:1 and fails, which axe caught the first time a surface panel
   * shipped. Rather than trusting everyone to remember, the surface classes
   * below rebind --muted to this darker grey - 4.85:1 on #f6f4ef - so
   * anything nested inside a panel inherits a passing value automatically.
   */
  --muted-on-surface: #6b6b6b;
  /* Same story for the gold text: 4.65:1 on white, 4.23:1 on the surface. */
  --gold-text-on-surface: #856834;

  --bg: #ffffff;
  --surface: #f6f4ef;
  --line: #e5e1da;          /* 1.30:1 - a hairline, decorative only */
  /*
   * A form control that is empty is identified by nothing except its
   * boundary, so that boundary is the "visual information required to
   * identify a user interface component" that 1.4.11 puts at 3:1. --line is
   * 1.30:1 and cannot carry it. This is the darker warm grey that can:
   * 3.60:1 on white and 3.27:1 on the tinted surface, both measured.
   *
   * Hairlines between things (card edges, dividers, table rules) stay on
   * --line. They separate content rather than identify a control, and 1.4.11
   * does not reach them.
   */
  --control-line: #8c8680;
  --focus: #972219;         /* red focus ring - 8.21:1 on white */
  --danger: #972219;        /* the brand red doubles as the error colour */
  --success: #1d6b3f;       /* 4.9:1 on white */

  /*
   * ---- type, all Google Fonts (OFL), self-hosted by next/font ----
   *
   * Brand Guide r2 roles. Note these are NOT what the July hub had: Saira
   * carried body and Anybody carried display there, and r2 swapped both.
   * Aleo is gone, so there is no serif token any more.
   */
  --font-display: var(--font-archivo-black), Impact, system-ui, sans-serif;
  --font-body: var(--font-anybody), system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Numerals: temperature and timers in the app, prices and counts here. */
  --font-numeral: var(--font-saira), var(--font-anybody), system-ui, sans-serif;

  /* ---- type scale, from Brand Guide page 05 ----
   *
   * The book publishes 24 text styles and the web reads the DISPLAY AND BODY
   * column of them. Its header ramp is 48 / 40 / 32 / 28 / 24 / 20, all
   * Archivo Black, all at 100% line height and all with no tracking. Those
   * are the clamp maxima below; a page title is allowed to shrink on a phone,
   * which a fixed 48px cannot do, but it never exceeds the published size.
   *
   * The book's body ramp has exactly two sizes: body/md at 16/22 and body/sm
   * at 14/20. There is no third, smaller one, and page 06 says why:
   *
   *     "Body copy sets at 16px and never below 14px."
   *
   * So there is no --step--2 any more. It was 12px and carried the eyebrows,
   * the footer headings, the article meta and the distribution rows - a lot
   * of the site sat under the floor the book sets. Everything that used it
   * now takes --step--1, which IS the floor. Keeping a token below 14px
   * around is how it comes back.
   */
  --step--1: .875rem;     /* 14px · body/sm - the floor, never go under */
  --step-0: 1rem;         /* 16px · body/md */
  --step-1: 1.0625rem;    /* 17px · lede; between body/md and header/h6 */
  --step-2: clamp(1.15rem, 1.06rem + .4vw, 1.25rem);    /* -> 20px, header/h6 */
  --step-3: clamp(1.5rem, 1.25rem + 1.1vw, 2rem);       /* -> 32px, header/h3 */
  --step-4: clamp(2rem, 1.5rem + 2.2vw, 3rem);          /* -> 48px, header/h1 */
  --step-5: clamp(2.35rem, 1.75rem + 2.6vw, 3.75rem);   /* hero display */

  /* body/md is 16/22 and body/sm is 14/20; both are ~1.375-1.43. */
  --leading-body: 1.375;
  --leading-small: 1.43;
  /* body/md carries 0.64px of tracking at 16px. */
  --tracking-body: .04em;

  /* ---- spacing ----
   *
   * One ramp for the same reason. --gap keeps its name as the grid gutter so
   * existing grids are unaffected.
   */
  --space-2xs: .25rem;
  --space-xs: .5rem;
  --space-s: .75rem;
  --space-m: 1rem;
  --space-l: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
  --space-3xl: 4.5rem;

  /*
   * Band rhythm. Sections were a flat 3rem top and bottom at every width, so
   * a phone got the same air as a 27" monitor and the page read as evenly
   * spaced mush at both. One fluid value means the bands stay in proportion
   * to the viewport, and every section referring to it means they stay in
   * proportion to each other.
   */
  --section-y: clamp(2.5rem, 1.6rem + 3.6vw, 4.5rem);

  --radius: 10px;
  --page: 1200px;
  --gap: 1.25rem;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  /* body/md: Anybody Regular, 16px, 22px line, 0.64px tracking. The 22px is
     one of only two values the book deliberately changed from April, so it
     is carried rather than second-guessed. */
  font: 16px/var(--leading-body) var(--font-body);
  letter-spacing: var(--tracking-body);
}

img { max-width: 100%; height: auto; }

/*
 * Visible focus for keyboard users on everything interactive.
 *
 * The ring is a non-text indicator of a component's state, so 1.4.11 puts it
 * at 3:1 against what it sits on - and unlike text, it moves. The same red
 * ring measures 8.21:1 on white, 1.99:1 on the dark footer and 1.00:1 on the
 * red announcement bar, where it is not a weak indicator but literally an
 * invisible one, on the first two controls of every page.
 *
 * So --focus rebinds per ground, the same structural trick --muted and
 * --gold-text already use: a control inherits a ring that passes without
 * having to know what it is sitting on. Do not flatten this back to one
 * global value - that is the bug.
 */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}

/*
 * Dark and red grounds. White measures 16.30:1 on spg-black and 8.21:1 on
 * spg-red; gold is 3.50:1 on black and 2.48:1 on red, so one value covers
 * both and there is no second rule to get wrong.
 *
 * There is a fourth dark ground, `.band-dark`, and it rebinds --focus in its
 * own block rather than here - along with --muted, --gold-text and --line,
 * which it also has to move. Keeping that set together is what makes it
 * reviewable; splitting one of the four out to this list is how the next one
 * gets forgotten.
 */
.site-footer,
.announcement,
.hero-copy {
  --focus: #ffffff;
}

/* Screen-reader-only text that stays reachable by assistive tech. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/*
 * A label that is hidden at narrow widths and shown at wide ones.
 *
 * `display: none` was doing this job for the cart's "Cart" and the search
 * link's "Search", and `display: none` takes an element out of the
 * accessibility tree as well as out of the layout. Below 620px the cart link
 * was an aria-hidden glyph and a count that renders nothing when the cart is
 * empty - which is to say, on a phone, with an empty cart, it was a link with
 * no accessible name at all. Every gate passed it, because axe scans at
 * desktop width and that is where the label exists.
 *
 * This hides it the way the site hides everything else it wants announced but
 * not seen, and the reveal restores the properties the hidden state changed
 * rather than only `display`.
 */
.responsive-label {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link: hidden until focused, then visible and in flow. */
.skip-link {
  position: absolute;
  left: 0; top: 0;
  padding: 0.75rem 1rem;
  background: var(--red);
  color: var(--on-red);
  font-weight: 700;
  transform: translateY(-120%);
  z-index: 100;
}
.skip-link:focus { transform: translateY(0); }

/* Every icon-only control clears the 2.5.8 target size. */
.icon-button {
  min-width: 44px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; cursor: pointer; color: inherit;
}

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

/* ------------------------------------------------------------- layout */

.wrap { max-width: var(--page); margin: 0 auto; padding-inline: 1rem; }
.wrap-narrow { max-width: 760px; margin: 0 auto; padding-inline: 1rem; }
.section { padding-block: var(--section-y); }
.section-tight { padding-block: calc(var(--section-y) * .55); }
/*
 * Two bands of the same colour meeting: one rhythm unit, not two.
 *
 * The collapse is only correct while both bands are the same white field and
 * the join is invisible. `.band` marks a section that paints its own ground,
 * and those need their padding back on both sides - otherwise the first thing
 * inside sits directly on the band's own top edge.
 *
 * That is exactly what happened when the claims band arrived: it follows the
 * grills strip, so its top padding was zeroed and "THE INSTRUMENT" was printed
 * almost on the border of its own box. The band after it inherited the same
 * problem, which is why "FROM THE SPIDER KITCHEN" was pinned to the rule above
 * it. One rule, two symptoms, three bands away from each other.
 */
.section + .section { -webkit-padding-before: 0; padding-block-start: 0; }
.band + .section,
.section + .band { -webkit-padding-before: var(--section-y); padding-block-start: var(--section-y); }
.stack > * + * { margin-top: var(--space-m); }

/*
 * Measure. Long-form copy set across the full 1200px column is 110-ish
 * characters a line, which is roughly twice what is comfortable to track
 * back from. This caps it without centring anything that was left-aligned.
 */
.measure { max-width: 62ch; }
.center .measure { margin-inline: auto; }

.grid { display: grid; grid-gap: var(--gap); gap: var(--gap); }
.grid-products {
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
/*
 * A strip with one or two items in it.
 *
 * `auto-fill` lays out as many tracks as the container will hold whether or
 * not anything goes in them, so a "You may also like" with a single related
 * product put one card in the first of four tracks and left three quarters of
 * the row empty. That does not read as a short list, it reads as a grid whose
 * contents failed to load - which is exactly what it was reported as.
 *
 * Naming the tracks for the count keeps the cards at their normal size and
 * lets the row simply be short, which is an honest thing for it to be.
 * `auto-fit` is not the fix: it collapses the empty tracks, so the single
 * card would stretch to the full page width instead.
 */
.grid-count-1 { grid-template-columns: minmax(0, 280px); }
.grid-count-2 { grid-template-columns: repeat(2, minmax(0, 280px)); }
.grid-2 { grid-template-columns: 1fr; }
@media (min-width: 800px) { .grid-2 { grid-template-columns: 1fr 1fr; } }

/* ------------------------------------------------------------ typography */

/*
 * Headlines take Anybody, the display accent. Saira carries everything else,
 * which is what "PRIMARY UI - hero numerals, buttons, body copy,
 * micro-labels" means on the type-token page.
 */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  /*
   * 900, which is Archivo's Black weight. The book's published styles
   * resolve to family "Archivo", style "Black" - not the separate "Archivo
   * Black" family, which looks the same and is a different font file. Every
   * display-face rule in this file holds to 900 for that reason; anything
   * else asks the browser to synthesise a weight the family already has.
   */
  /*
   * No tracking. The book's entire header ramp is "-" in the tracking
   * column. The negative tracking that was here came from a different face;
   * Archivo Black is drawn tight already and closing it further mushes the
   * counters at display sizes.
   *
   * Line height 1.05 rather than the book's flat 100%. A web-only
   * concession, and it only bites on a heading that wraps - which the app
   * screens the ramp was drawn for mostly do not. At exactly 100% two lines
   * of Archivo Black touch. It is the same argument the book itself used to
   * open body from 16/16 to 16/22.
   */
  margin: 0 0 var(--space-s); font-weight: 900; letter-spacing: 0;
  line-height: 1.05;
  text-wrap: balance;
}
h1 { font-size: var(--step-4); }                     /* -> header/h1, 48px */
h2 { font-size: var(--step-3); }                     /* -> header/h3, 32px */
h3 { font-size: var(--step-2); line-height: 1.15; }  /* -> header/h6, 20px */
h4 { font-size: var(--step-1); line-height: 1.2; }
p { margin: 0 0 var(--space-m); }

/*
 * The eyebrow announces a section before its heading, so it is set as a
 * micro-label: gold, tracked out, and small. --gold-text, never raw --gold,
 * because at 12px this is unambiguously small text and raw gold is 3.31:1.
 * On a tinted surface the rebinding lower down swaps in the darker variant.
 */
/* body/sm-semibold: 14px, 20px line, 1.4px tracking. */
.eyebrow {
  text-transform: uppercase; letter-spacing: .1em;
  font-size: var(--step--1); font-weight: 600; color: var(--gold-text);
  margin: 0 0 var(--space-xs); line-height: var(--leading-small);
}
.lede { font-size: var(--step-1); line-height: 1.5; color: var(--muted); }
.muted { color: var(--muted); }
.center { text-align: center; }

/*
 * A short gold rule under a section heading. Gold is 3.31:1 on white, which
 * is under the 4.5:1 text needs but over the 3:1 that 1.4.11 asks of a
 * graphic, so this is exactly the job the raw brand value is allowed to do.
 */
.rule::after {
  content: ""; display: block;
  width: 3rem; height: 3px; margin-top: var(--space-m);
  background: var(--gold); border-radius: 2px;
}
.center .rule::after { margin-inline: auto; }

/* Shopify rich text arrives as HTML we do not author - keep it contained. */
.rte { overflow-wrap: anywhere; }
.rte :where(h1, h2, h3, h4) { margin-top: 1.75rem; }
.rte img { border-radius: var(--radius); }
.rte a { color: var(--red); }
.rte ul, .rte ol { padding-left: 1.25rem; }
.rte table { width: 100%; border-collapse: collapse; display: block; overflow-x: auto; }
.rte th, .rte td { border: 1px solid var(--line); padding: .5rem .6rem; text-align: left; }
.rte iframe { max-width: 100%; border-radius: var(--radius); }

/* --------------------------------------------------------------- buttons */

/*
 * buttons/sm from the book's numeral column: Saira SemiBold, 14px, 1.4px of
 * tracking (0.1em). Buttons are Saira, not the body face - Saira inherited
 * both the numeral and the button roles from Ruston Basic, which is what
 * April used here.
 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 44px; padding: .7rem 1.4rem;
  border-radius: var(--radius); border: 2px solid transparent;
  font-family: var(--font-numeral);
  font-size: var(--step--1); font-weight: 600; letter-spacing: .1em;
  text-decoration: none; cursor: pointer;
}
.btn-primary { background: var(--red); color: var(--on-red); }
/* Darker on hover, so the label stays above 4.5:1 the whole way. */
.btn-primary:hover { background: #7d1c14; }
.btn-secondary { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-secondary:hover { background: var(--surface); }

/*
 * The two dark-ground buttons, and why red is not one of them.
 *
 * spg-red measures 1.99:1 against spg-black. A filled button is identified by
 * its fill, and 1.4.11 puts that at 3:1, so a red button on the dark band is
 * the same defect the audit found in the focus ring - the rule the footer
 * already carries is "nothing interactive in this block may use var(--red)".
 * White is what is left, and on this ground it is also the strongest
 * affordance available: 16.30:1 either way round.
 *
 * Red still leads everywhere the site is light, which is almost all of it.
 */
.btn-invert { background: #fff; color: var(--ink); border-color: #fff; }
.btn-invert:hover { background: var(--paper); }
/*
 * The hero's secondary. It sits on the scrim, whose worst case is #615e5e by
 * construction, so white text and a white 2px edge measure 6.42:1 - text and
 * boundary both clear. Filling it on hover puts ink on white at 16.30:1.
 */
.btn-ghost { background: transparent; color: #fff; border-color: #fff; }
.btn-ghost:hover { background: #fff; color: var(--ink); }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .55; cursor: not-allowed;
}
.btn-block { width: 100%; }

/* ----------------------------------------------------------------- forms */

.field { display: grid; grid-gap: .35rem; gap: .35rem; }
.field label { font-weight: 600; font-size: var(--step--1); letter-spacing: .01em; }
/*
 * The boundary is the whole affordance on an empty field, so it is drawn in
 * --control-line at 3.60:1 rather than the 1.30:1 hairline these carried
 * before. 1.4.11 asks 3:1 of anything needed to identify a control, and an
 * input with no value and a near-white edge is identifiable by nothing.
 */
.field input, .field select, .field textarea {
  min-height: 44px; padding: .5rem .7rem; font: inherit;
  border: 1px solid var(--control-line); border-radius: 8px;
  background: #fff; color: var(--ink);
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--ink); }

/* ----------------------------------------------------------------- cards */

.card {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: #fff; overflow: hidden;
  /* A column, so the body can push its last row to the floor of the card. */
  display: flex; flex-direction: column;
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* The whole card is one link, so it answers to hover as one object. */
.card:hover { border-color: var(--control-line); box-shadow: 0 8px 22px rgba(35, 31, 32, .09); }
.card:has(a:focus-visible) { border-color: var(--control-line); }

/*
 * spg-paper, not spg-surface.
 *
 * The brand strategy's art direction names two grounds and assigns each one a
 * place: "dark editorial" on spg-black for campaign and the top of the
 * storefront, and "light catalogue - spg-paper cutout, no invented shadow"
 * for "storefront product grid, spec pages, marketplaces and retail". This is
 * the storefront product grid, so it is paper.
 *
 * It was the tinted --surface, chosen before that page existed, to give the
 * media well an edge against the white card body. The hairline below does
 * that job on its own, and the tint was quietly putting every product cutout
 * on the one ground the deck says it never shoots: "the warm-grey studio
 * backdrop that is neither one nor the other. It is the default of every
 * catalogue in this category and it says nothing."
 */
.card-media {
  aspect-ratio: 1 / 1; background: var(--bg);
  display: grid; grid-template-columns: minmax(0, 1fr); place-items: center;
  overflow: hidden; position: relative;
  /* The cutouts are shot on white, so without a rule the media and the body
     read as one undifferentiated block and the card loses its shape. */
  border-bottom: 1px solid var(--line);
}
.card-media img { width: 100%; height: 100%; object-fit: contain; }
/* The stand-in when a product has no photograph. --control-line is 3.27:1 on
   the tinted media well, so the mark is visible without pretending to be
   content. See the note in ProductCard.tsx. */
.card-media-empty { display: grid; place-items: center; color: var(--control-line); }
/* Status flag on the media. No z-index - see the note in ProductCard.tsx. */
.card-flag { position: absolute; top: var(--space-xs); left: var(--space-xs); margin: 0; }

.card-body {
  padding: var(--space-m);
  display: flex; flex-direction: column; gap: var(--space-2xs);
  flex: 1 1 auto;
}
/*
 * Product names take the display face: the Brand Guide gives Archivo Black
 * "headings, product names", and a product card title is a product name.
 * Weight 400 because the face is already black, and the tracking comes back
 * a touch because Archivo Black is wide and these titles wrap to two lines
 * often enough that the extra width shows.
 */
.card-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: var(--step-0); margin: 0; line-height: 1.25; letter-spacing: -.01em;
}
.card-rating { line-height: 1; }
/* The one line the eye tracks along across a row of ragged cards. */
.card-price { margin: auto 0 0; padding-top: var(--space-xs); }
/*
 * One link per card wrapping the title, stretched over the whole card by a
 * pseudo-element. Keeps the card clickable without the duplicate-link noise
 * a screen reader reads out when the image is a second link.
 */
.card-title a { color: var(--ink); text-decoration: none; }
.card-title a::after { content: ""; position: absolute; inset: 0; }
.card { position: relative; }

/*
 * Prices, quantities and counts take Saira. The Brand Guide gives Saira the
 * numeral role - temperature, timers, the controller LCD - and on a shop the
 * money is the same job: a figure to be read at a glance, in tabular digits
 * so a column of them lines up.
 */
.price { font-weight: 700; font-family: var(--font-numeral); font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }
.price-compare { color: var(--muted); text-decoration: line-through; font-weight: 400; margin-left: .4rem; }
.badge {
  display: inline-block; font-size: var(--step--1); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: .2rem .45rem; border-radius: 4px;
  background: var(--red); color: var(--on-red);
}
.badge-out { background: var(--surface); color: var(--ink); border: 1px solid var(--line); }

/* --------------------------------------------------------------- ratings */

/*
 * Stars are one decorative graphic plus one text label. The theme nested
 * interactive elements inside the stars, which is audit finding 5; here
 * nothing inside the graphic is focusable and the value is plain text.
 */
.stars { display: inline-flex; align-items: center; gap: .3rem; }
.stars svg { display: block; }

/* -------------------------------------------------------------- accordion */

.accordion { border-top: 1px solid var(--line); }
.accordion details { border-bottom: 1px solid var(--line); }
.accordion summary {
  cursor: pointer; padding: 1rem .25rem; font-weight: 700;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 44px; list-style: none;
}
.accordion summary::-webkit-details-marker { display: none; }
/*
 * `content: "x" / ""` gives generated content an empty alternative text, so
 * it is drawn but not announced. NVDA was reading the plus as part of the
 * summary's name - "Shipping Policy, heading, level 3, plus, button" - and
 * the disclosure triangle as a literal question mark. The open/closed state
 * is already carried by details/summary; the glyph is decoration.
 */
.accordion summary::after {
  content: "+" / ""; font-size: 1.4rem; line-height: 1; color: var(--red);
}
.accordion details[open] summary::after { content: "\2212" / ""; }
.accordion .accordion-body { padding: 0 .25rem 1rem; }

/* ------------------------------------------------------------------ misc */

/*
 * Everything that paints --surface as its background rebinds the two text
 * tokens here. Custom properties inherit, so every descendant that says
 * var(--muted) or var(--gold-text) gets the value that passes on that
 * background without having to know it is on one. axe caught both of these
 * the first time a surface panel shipped, which is the argument for making
 * it structural rather than a rule people have to remember.
 */
.on-surface,
.notice,
.review-summary,
.cart-summary,
.buy-status,
.feature-strip,
.press,
.slide-body,
.filter-panel,
.empty-state,
.claims,
.chip {
  --muted: var(--muted-on-surface);
  --gold-text: var(--gold-text-on-surface);
}

.notice {
  border: 1px solid var(--line); border-left: 4px solid var(--red);
  background: var(--surface); border-radius: var(--radius);
  padding: var(--space-m) var(--space-l);
}
.notice-error { border-left-color: var(--danger); }

/*
 * Empty states: the cart with nothing in it, a 404, a filter combination
 * with no matches. All three were bare markup on white, which reads as a
 * page that failed rather than a page with nothing to show. One composed
 * block, so an absence still looks designed and still offers a way onward.
 */
.empty-state {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-3xl) var(--space-l);
  display: grid; justify-items: center; text-align: center;
  grid-gap: var(--space-m);
  gap: var(--space-m);
}
.empty-state > * { margin: 0; }
.empty-state-mark { width: 72px; height: 72px; object-fit: contain; }
.empty-state-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-s); justify-content: center;
  margin-top: var(--space-xs);
}
.empty-state-aside { font-size: var(--step--1); color: var(--muted); }
.empty-state-aside a { color: var(--red); }

.divider { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }

.breadcrumbs { font-size: var(--step--1); color: var(--muted); padding-block: var(--space-m) var(--space-2xs); }
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2xs); margin: 0; padding: 0; }
.breadcrumbs a { color: var(--red); }
/* --line at 1.30:1 made the separators invisible and the trail read as one
   run of words. --control-line is a legible mark without competing. */
.breadcrumbs li + li::before { content: "/"; margin-right: var(--space-2xs); color: var(--control-line); }

.tag-list { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; margin: 0; padding: 0; }

.strip-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--space-s); margin-bottom: var(--space-l);
  padding-bottom: var(--space-s); border-bottom: 1px solid var(--line);
}
.strip-head h2, .strip-head h3 { margin: 0; }
.strip-head .eyebrow { margin-bottom: var(--space-2xs); }
.strip-link { color: var(--red); font-weight: 700; font-size: var(--step--1); white-space: nowrap; }
/*
 * `content: "x" / ""` again - generated content is part of the accessible
 * name computation, so a bare arrow here would append "right arrow" to the
 * link's name. Same reason the accordion's plus and the nav caret carry an
 * empty alternative. The arrow is decoration; the link is named by its text.
 */
.strip-link::after { content: " \2192" / ""; }

/* ----------------------------------------------------------------- gallery */

/*
 * `minmax(0, 1fr)` here is load-bearing, not tidying.
 *
 * A grid container with no explicit columns gets one implicit `auto` track,
 * and an `auto` track's growth limit is max-content - so it grows past its
 * own container rather than clipping. The stage's max-content is driven by a
 * full-resolution product photograph, so this track was resolving to 1013px
 * inside a 288px column, and the square aspect-ratio made the stage 1013px
 * TALL to match. On the desktop PDP that painted the buy panel on top of the
 * photograph; at 320px it pushed the document 709px wider than the viewport.
 *
 * `min-width: 0` on the item does NOT fix this and was the first thing tried:
 * it lowers the item's floor, while the overflow comes from the track's
 * ceiling. Naming the column is what caps it.
 *
 * The 320px case is WCAG 1.4.10 Reflow, an AA failure on the most important
 * template on the site, invisible to axe and to a screen reader alike.
 * scripts/axe.mjs now measures document width per route so it cannot return.
 */
.gallery { display: grid; grid-template-columns: minmax(0, 1fr); grid-gap: .6rem; gap: .6rem; }
/* Paper, for the same reason as `.card-media`: the strategy's light-catalogue
   ground is spg-paper with no invented shadow, and this is the largest
   cutout on the site. The border keeps the stage a defined object. */
.gallery-stage {
  aspect-ratio: 1 / 1; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius);
  display: grid; grid-template-columns: minmax(0, 1fr); place-items: center;
  overflow: hidden;
}
.gallery-image { width: 100%; height: 100%; object-fit: contain; border: 0; }
.gallery-position { margin: 0; font-size: var(--step--1); color: var(--muted); text-align: center; }
.gallery-controls { display: flex; align-items: center; gap: .25rem; }
.gallery-arrow { flex: 0 0 auto; font-size: 1.6rem; color: var(--ink); border-radius: 8px; }
.gallery-arrow:hover { background: var(--surface); }
.gallery-thumbs {
  display: flex; gap: .4rem; overflow-x: auto; padding: .25rem;
  scrollbar-width: thin; flex: 1 1 auto;
}
.gallery-thumb {
  flex: 0 0 auto; width: 64px; height: 64px; padding: 2px;
  background: #fff; border: 2px solid var(--line); border-radius: 8px;
  cursor: pointer; overflow: hidden; position: relative;
}
.gallery-thumb.is-selected { border-color: var(--red); }
.gallery-thumb-img { width: 100%; height: 100%; object-fit: contain; }
.gallery-thumb-fallback { font-size: 1.2rem; color: var(--red); }

/* --------------------------------------------------------------------- pdp */

.pdp-top { display: grid; grid-gap: 2rem; gap: 2rem; padding-block: 1rem 2rem; }
/* Both columns must be allowed to shrink; see the note on .gallery above. */
.pdp-media, .pdp-buy { min-width: 0; }
@media (min-width: 900px) {
  .pdp-top { grid-template-columns: minmax(0, 1.05fr) minmax(320px, .95fr); gap: 3rem; align-items: start; }
  .pdp-buy { position: -webkit-sticky; position: sticky; top: 1rem; }
}
/*
 * The buy panel is a real object rather than a column of loose elements.
 *
 * It is the one part of the page that stays put while the gallery scrolls,
 * and until it had an edge it read as text that had come unstuck from
 * something. A white card on a white page, defined by its border, keeps the
 * left side's tinted media well as the only large area of colour.
 */
.pdp-buy {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--space-l);
  background: #fff;
  align-self: start;
}
@media (min-width: 900px) {
  .pdp-buy { padding: var(--space-xl); }
}

/* Identity above the rule, purchase below it. */
.pdp-identity {
  padding-bottom: var(--space-l);
  margin-bottom: var(--space-l);
  border-bottom: 1px solid var(--line);
}
.pdp-identity h1 { margin-bottom: var(--space-s); }
.pdp-rating { margin: 0 0 var(--space-m); }
.pdp-rating-link { color: inherit; text-decoration: none; }
.pdp-rating-link:hover { text-decoration: underline; }
.pdp-price { margin: 0; }
.pdp-price-value { font-size: var(--step-3); line-height: 1; letter-spacing: -.01em; }
.pdp-sku {
  margin: var(--space-m) 0 0; font-size: var(--step--1); color: var(--muted);
  letter-spacing: .02em;
}

.option-group { border: 0; padding: 0; margin: 0 0 var(--space-l); }
.option-legend { padding: 0; font-weight: 700; font-size: var(--step--1); margin-bottom: var(--space-xs); }
.option-chosen { font-weight: 400; color: var(--muted); }
.option-values { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
/*
 * The swatch outline is the whole affordance, so it is drawn at 3.60:1 in
 * --control-line rather than the 1.30:1 hairline it had. Same 1.4.11
 * argument as the form fields.
 */
.option-swatch {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: .5rem .9rem;
  border: 2px solid var(--control-line); border-radius: 8px;
  cursor: pointer; font-weight: 600; background: #fff;
  transition: border-color .12s ease, background-color .12s ease;
}
.option-swatch:hover { border-color: var(--ink); }
.option-swatch.is-selected { border-color: var(--ink); background: var(--ink); color: #fff; }
.option-swatch.is-unavailable { color: var(--muted); text-decoration: line-through; border-color: var(--line); }
/* The radio itself is visually hidden, so its focus ring has to land here. */
.option-swatch:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }

.buy-form { display: grid; grid-gap: var(--space-s); gap: var(--space-s); }
.buy-qty { max-width: 7rem; }
/*
 * Quantity is a two-character field and the button is the primary action, so
 * they share a row instead of the field sitting orphaned above a
 * full-bleed button. Below 480px they stack, because a 7rem field beside a
 * button leaves the button too narrow to hold its label.
 */
.buy-actions { display: grid; grid-gap: var(--space-s); gap: var(--space-s); }
@media (min-width: 480px) {
  .buy-actions { grid-template-columns: 7rem minmax(0, 1fr); align-items: end; }
  .buy-qty { max-width: none; }
}
.buy-status {
  margin: 0; padding: var(--space-s) var(--space-m); border-radius: 8px;
  background: var(--surface); border-left: 4px solid var(--red);
  font-size: var(--step--1);
}

/*
 * The three assurances under the buy button.
 *
 * A hairline above them rather than a tinted panel: the buy panel is already
 * a white card on a white page held together by one border, and a second
 * filled box inside it would read as a different component rather than as the
 * end of this one.
 *
 * The icons take --gold-text at 4.65:1 on white. They sit beside their own
 * label in every row, so nothing here depends on reading a glyph.
 */
.buy-assurances {
  list-style: none; margin: var(--space-l) 0 0; padding: var(--space-m) 0 0;
  border-top: 1px solid var(--line);
  display: grid; grid-gap: var(--space-s); gap: var(--space-s);
  font-size: var(--step--1); line-height: var(--leading-small);
}
.buy-assurances li { display: flex; align-items: center; gap: var(--space-s); }
.buy-assurances svg { flex: 0 0 auto; color: var(--gold-text); }
.buy-assurances strong { font-weight: 700; }
.buy-assurances a { color: var(--red); }

/* ----------------------------------------------------------------- reviews */

/*
 * Summary on the left, reviews on the right.
 *
 * The list used to run the full 1168px column, which is about 150 characters
 * a line - roughly twice a comfortable measure - and with ten reviews a page
 * it made this section more than half the PDP. Narrowing the list is the
 * whole point of the two-column split; the summary gets the space it frees.
 */
.reviews-layout { display: grid; grid-gap: var(--space-xl); gap: var(--space-xl); }
@media (min-width: 1000px) {
  .reviews-layout {
    grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
    gap: var(--space-2xl); align-items: start;
  }
  /* It is a fixed reference while you read down the list. */
  .review-aside { position: -webkit-sticky; position: sticky; top: var(--space-m); }
}
.review-main { min-width: 0; }

.review-summary {
  display: grid; grid-gap: var(--space-l); gap: var(--space-l);
  padding: var(--space-l); border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}
.review-average { display: flex; align-items: center; gap: var(--space-m); }
/* A rating is a numeral, so it takes Saira - the same role it has on the
   controller LCD. */
.review-average-value {
  margin: 0; font-family: var(--font-numeral);
  font-size: var(--step-4); font-weight: 700; line-height: 1; letter-spacing: -.02em;
}
.review-average-count { margin: var(--space-2xs) 0 0; font-size: var(--step--1); color: var(--muted); }

.review-distribution { list-style: none; margin: 0; padding: 0; display: grid; grid-gap: var(--space-2xs); gap: var(--space-2xs); }
.review-distribution li {
  display: grid; grid-template-columns: 3.6rem minmax(0, 1fr) 4.4rem;
  grid-gap: var(--space-xs);
  gap: var(--space-xs); align-items: center; font-size: var(--step--1);
}
.review-dist-track { background: #fff; border: 1px solid var(--control-line); border-radius: 999px; height: 8px; overflow: hidden; }
.review-dist-fill { display: block; height: 100%; background: var(--gold); }
.review-dist-count { text-align: right; }

.review-list { list-style: none; margin: 0; padding: 0; display: grid; grid-gap: var(--gap); gap: var(--gap); }
/*
 * Each review is its own object. A hairline between entries was not enough
 * separation once they were stacked ten deep: a long review and the two
 * short ones after it read as one block of text.
 */
.review {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--space-l); background: #fff;
}
.review-title { margin: var(--space-xs) 0 var(--space-s); font-size: var(--step-1); }
.review-meta {
  margin: var(--space-m) 0 0; padding-top: var(--space-s);
  border-top: 1px solid var(--line);
  font-size: var(--step--1); color: var(--muted);
}
.review-pager-position { margin: 0; font-size: var(--step--1); color: var(--muted); }
.review-verified { color: var(--success); font-weight: 700; }
/*
 * The two-column split brought the line from about 150 characters down to
 * 94, which is still well past comfortable. This caps it outright. The card
 * stays full width and the text stops short of its right edge, which reads
 * as generous padding rather than as a short column.
 */
.review-body {
  margin: 0; white-space: pre-line; overflow-wrap: anywhere;
  max-width: 68ch; line-height: 1.6;
}
.review-pictures { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem; margin: .75rem 0 0; padding: 0; }
.review-picture { border-radius: 8px; object-fit: cover; width: 96px; height: 96px; }
.review-pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: 1.5rem; flex-wrap: wrap;
}

/* ------------------------------------------------------------- dark footer */

/*
 * The one dark surface on the site.
 *
 * This block was built around the old gold. At #ab8846 gold measured 4.92:1
 * on spg-black, so the footer used it for headings, links-on-hover, the
 * tagline and the policy row - the dark band was the one place gold could
 * carry text. Brand Guide r2 moved gold to #8d703a, which is 3.50:1 here,
 * and the book is explicit that gold is "never body copy on a dark field".
 *
 * So gold's job in this footer is now structural only: the top rule and the
 * hairline under each column heading, both graphics at 3.50:1 against the
 * 3:1 that 1.4.11 asks. Every piece of text moved to --paper (13.06:1) or
 * --stone (8.15:1).
 *
 * That is a real loss of colour here, and it is the trade the book makes
 * knowingly: no single gold clears 4.5:1 on both white and spg-black, and it
 * chose the one that works on white, where the whole storefront lives.
 *
 * Red stays absent: 1.99:1 here. Nothing interactive in this block may use
 * var(--red) - including the focus ring, which is why --focus is rebound to
 * white at the top of this file.
 */
.site-footer {
  background: var(--ink);
  color: var(--paper);
  border-top: 3px solid var(--gold);
  --muted: var(--stone);          /* 8.15:1 on spg-black */
  --line: #3a3537;
}
.footer-top {
  display: grid; grid-gap: var(--space-2xl); gap: var(--space-2xl); padding-block: var(--section-y);
}
@media (min-width: 860px) {
  .footer-top { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--space-3xl); }
}
.footer-brand { display: grid; grid-gap: var(--space-m); gap: var(--space-m); justify-items: start; }
.footer-seal { width: 96px; height: 96px; object-fit: contain; }
.footer-social {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-m); font-size: var(--step--1);
}
.footer-social a { color: var(--paper); text-decoration: none; }
.footer-social a:hover { color: #fff; text-decoration: underline; }
/* Was Aleo in gold. Aleo is dropped and gold cannot carry text here, so it
   is the display face at a size where it can hold the corner on its own. */
.footer-tagline {
  font-family: var(--font-display); font-weight: 900;
  font-size: var(--step-2); line-height: 1.2; color: var(--paper);
  margin: 0; max-width: 18ch;
}
.footer-columns {
  display: grid; grid-gap: var(--space-xl); gap: var(--space-xl);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.footer-heading {
  font-family: var(--font-body);
  font-size: var(--step--1); text-transform: uppercase; letter-spacing: .12em;
  color: var(--paper);             /* 13.06:1 - gold would be 3.50:1 */
  margin: 0 0 var(--space-s); padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--gold);   /* graphic, 3.50:1 */
}
.footer-links { list-style: none; padding: 0; margin: 0; display: grid; grid-gap: var(--space-xs); gap: var(--space-xs); }
.footer-links a { color: var(--stone); text-decoration: none; font-size: var(--step--1); }
.footer-links a:hover { color: #fff; text-decoration: underline; }

/* The UA shrinks `small` to about 0.83em, which put the copyright line at
   11.7px - under the book's 14px floor. Named explicitly rather than left
   to the user agent. */
.footer-base-inner small { font-size: var(--step--1); }
.footer-base { border-top: 1px solid var(--line); }
.footer-base-inner {
  display: flex; flex-wrap: wrap; gap: var(--space-m);
  align-items: center; justify-content: space-between;
  padding-block: var(--space-m); font-size: var(--step--1); color: var(--muted);
}
.footer-policies { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-m); margin: 0; padding: 0; }
.footer-policies a { color: var(--stone); }
.footer-policies a:hover { color: #fff; }

/* -------------------------------------------------------------- newsletter */

/*
 * No margin-top. The band sits directly against whatever precedes it and
 * relies on that section's own bottom padding, because adding 4rem on top of
 * a section that already had 3rem left a 7rem hole above the newsletter on
 * every page - most visible on a short collection, where the gap read as a
 * rendering fault rather than as spacing.
 */
.newsletter {
  background: var(--surface); border-top: 1px solid var(--gold);
  padding-block: var(--section-y);
  --muted: var(--muted-on-surface);
  --gold-text: var(--gold-text-on-surface);
}
.newsletter h2 { font-size: var(--step-3); margin-bottom: var(--space-xs); }
.newsletter-form {
  display: flex; flex-wrap: wrap; gap: var(--space-s);
  align-items: end; justify-content: center; margin-top: var(--space-l);
}
.newsletter-form .field { text-align: left; min-width: 260px; }

/* -------------------------------------------------------------------- home */

.hero { position: relative; background: var(--ink); }
/*
 * The hero is now a framed height with the photograph covering it, rather
 * than an image at its own intrinsic size.
 *
 * `height: auto` meant the 1920x700 asset simply laid out at whatever height
 * its aspect ratio gave, and `object-fit: cover` had nothing to do because
 * the box was already the image's shape. On a phone that produced a 142px
 * strip of photograph above the copy - the single worst thing on the site at
 * 390px, and invisible on a desktop review. Measured, not estimated:
 * 390 x 700/1920 = 142.2px.
 *
 * Giving the box a height and letting the image cover it fixes both ends. The
 * crop is centred because the grill and the fire are centred in the frame.
 */
.hero-image {
  display: block; width: 100%;
  height: clamp(260px, 72vw, 380px);
  object-fit: cover; object-position: center;
}
/*
 * Taller than the copy strictly needs. The scrim is sized to the copy box, so
 * a short hero means the darkened band is most of the picture - at 640px the
 * copy and its fade took 52% of the frame and the photograph read as a grade
 * rather than as a photograph. At 720 it is 46%, and the fire above the fade
 * stays bright.
 */
@media (min-width: 701px) {
  .hero-image { height: clamp(460px, 52vw, 720px); }
}
.hero-copy {
  position: absolute; inset: auto 0 0 0; padding-block: var(--space-xl);
  color: #fff;
  /*
   * Above the scrim. Both are positioned with `z-index: auto`, so they would
   * otherwise paint in DOM order and ::after - generated as the last child -
   * would be drawn over the words it exists to make readable.
   */
  z-index: 1;
}
/*
 * Hierarchy inside the hero, in the order the eye is meant to take it:
 * headline, then the red button, then the sentence that explains, then the
 * proof. The headline is the only thing on the site at --step-5.
 */
.hero-copy h1 {
  color: #fff; margin-bottom: var(--space-s); max-width: 18ch;
  font-size: var(--step-5);
}
.hero-standfirst {
  color: #fff; max-width: 46ch;
  font-size: var(--step-1); line-height: 1.5;
  margin: 0 0 var(--space-l);
}
.hero-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-s);
  margin: 0 0 var(--space-m);
}
/*
 * The ranking, stated once at the top of the page and evidenced further down
 * by the band that carries Men's Journal's own badge. It is small and set
 * below the buttons on purpose: proof supports a decision, it does not make
 * one, and a hero with two competing claims has neither.
 */
.hero-proof {
  display: flex; align-items: center; gap: var(--space-xs);
  margin: 0; color: #fff;
  font-size: var(--step--1); line-height: var(--leading-small);
  letter-spacing: .02em;
}
.hero-proof svg { flex: 0 0 auto; }

/*
 * The scrim, and why it is these exact numbers.
 *
 * White text on a photograph is the one thing on this site whose contrast
 * cannot be asserted: it depends on the pixels, it changes when marketing
 * swaps the image, and axe reports it as "incomplete" rather than as a pass
 * or a fail. A text-shadow, which is what was here before, makes it look
 * handled without making it true.
 *
 * A scrim converts it into arithmetic. Compositing alpha A of --ink over an
 * unknown pixel P gives A*ink + (1-A)*P, and for white text the worst case
 * is the lightest possible P - a pure white photograph. So the floor is
 * white text against A*ink + (1-A)*white, which is a fixed colour that can
 * be measured like any other.
 *
 *   A = .62 -> #777475 -> 4.63:1   (just clears 4.5:1)
 *   A = .72 -> #615e5e -> 6.42:1   (chosen)
 *
 * The scrim is anchored to the COPY, not to a percentage of the hero, and
 * that is the whole trick. The first attempt held .72 across the bottom 42%
 * of the hero, which measured fine at 1440px and quietly failed everywhere
 * else: the copy block is a fixed height while the hero is a 60vh image, so
 * its top edge sat at 44% of the hero at 1440, 59% at 1024 and 71% at 820,
 * climbing straight out of the band. Sizing the gradient to the copy box
 * makes the guarantee independent of viewport, image aspect and how many
 * lines the headline wraps to.
 *
 * So: the element spans the copy box plus a 5rem fade above it, and the
 * gradient holds >= .72 for the whole copy box before ramping to nothing,
 * which leaves the photograph a photograph above the text.
 *
 * The h1 is large text and would only need 3:1. The band is built to the
 * 4.5:1 normal-text bar anyway, so a standfirst or a caption can be added
 * here later without reopening any of this.
 *
 * Only above 700px. Below it the copy is not over the image at all - it sits
 * on solid --ink at 16.30:1 - and a scrim there would tint the red CTA.
 *
 * .hero-copy must stay full-bleed for this to work; the centred column is a
 * `wrap` INSIDE it. While `wrap` was on this element the gradient stopped at
 * 1200px and left a visible vertical seam down each side of the hero where
 * the darkened strip met the bare photograph. An element screenshot did not
 * show it - looking at the real page in Chrome did.
 */
/*
 * The copy block grew in the craft pass - a standfirst, a second button and a
 * proof line joined the headline - so the band the scrim has to guarantee is
 * taller than it was. Nothing about the arithmetic changes: the gradient is
 * still sized to the copy box, so it still cannot drift when the headline
 * wraps to a different number of lines or the viewport changes shape.
 */
@media (min-width: 701px) {
  .hero-copy::before {
    content: ""; position: absolute; pointer-events: none;
    left: 0; right: 0; bottom: 0; top: -6rem;
    /* Behind the copy's own text, in front of the image: .hero-copy is
       positioned with z-index 1, so it owns a stacking context and -1 here
       lands between its background and its content. */
    z-index: -1;
    background: linear-gradient(
      to top,
      rgba(35, 31, 32, .84) 0%,
      rgba(35, 31, 32, .72) calc(100% - 6rem),
      rgba(35, 31, 32, 0) 100%
    );
  }
}
@media (max-width: 700px) {
  .hero-copy {
    position: static; background: var(--ink);
    padding-block: var(--space-xl) var(--space-2xl);
  }
}

.feature-strip {
  background: var(--surface);
  border-top: 1px solid var(--gold); border-bottom: 1px solid var(--gold);
}
/*
 * `margin-inline: auto`, not `margin: 0`.
 *
 * This element carries `.wrap` as well, and `.wrap` centres itself with
 * `margin: 0 auto`. A bare `margin: 0` here is the same specificity and
 * comes later in the file, so it silently won and killed the centring: the
 * strip laid out flush against the left edge of the viewport at 0 while
 * every other `.wrap` on the page started at 120px. Long-standing, and only
 * obvious once the dividers below gave the row a visible right edge.
 *
 * Reset the block margin only. Anything else combining `.wrap` with a list
 * or grid class has to do the same.
 */
.feature-list {
  list-style: none; margin-block: 0; margin-inline: auto;
  padding-block: calc(var(--section-y) * .6);
  display: grid; grid-gap: var(--space-l); gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/*
 * Four claims in a row with nothing between them read as one run-on
 * sentence. Gold hairlines divide them into four statements - and gold is
 * doing exactly the job it is allowed to do here, a graphic at 3.01:1 on the
 * tinted ground rather than text.
 */
@media (min-width: 900px) {
  .feature-list { grid-template-columns: repeat(4, 1fr); gap: 0; }
  .feature-list li { padding-inline: var(--space-xl); -webkit-border-start: 1px solid var(--gold); border-inline-start: 1px solid var(--gold); }
  .feature-list li:first-child { -webkit-padding-start: 0; padding-inline-start: 0; -webkit-border-start: 0; border-inline-start: 0; }
  .feature-list li:last-child { -webkit-padding-end: 0; padding-inline-end: 0; }
}
/*
 * The icon is what turns this row from four sentences into four claims. It is
 * gold - the heritage accent, doing the one job the book reserves it for on a
 * light field - and it takes --gold-text rather than raw --gold so the
 * surface rebinding gives it the darker variant on this tinted ground.
 *
 * Decorative by construction: every icon sits directly above the words it
 * illustrates, so nothing here depends on reading a glyph.
 */
.feature-icon { display: block; color: var(--gold-text); margin-bottom: var(--space-s); }
.feature-icon svg { display: block; }
.feature-list h3 {
  font-family: var(--font-body); font-size: var(--step-0); font-weight: 700;
  letter-spacing: 0; margin-bottom: var(--space-2xs);
}
.feature-list p { margin: 0; font-size: var(--step--1); color: var(--muted); line-height: 1.5; }

/*
 * The standard intro to a band: eyebrow, heading, one paragraph. It was
 * three different ad-hoc arrangements of `wrap-narrow center` with inline
 * `padding: 0` overrides; now the spacing below it is one number and every
 * band starts the same distance above its content.
 */
/*
 * Left-aligned in the page column by default, centred only when asked.
 *
 * It was `margin: 0 auto`, which centres the 640px block inside the 1168px
 * column. That is right when the text inside is also centred and wrong the
 * moment it is not: the craft pass dropped `.center` from the lineup and the
 * claims bands, so their eyebrow and heading started at 264px while every
 * card beneath them started at 0. It reads as a heading that has come loose
 * from its own section.
 *
 * The axe gate's container-alignment check does not catch this - it measures
 * `.wrap` elements, and this is one level inside a `.wrap`.
 */
.section-head { max-width: 640px; margin: 0 0 var(--space-xl); }
.section-head.center { margin-inline: auto; }
.section-head > :last-child { margin-bottom: 0; }

.multicolumn {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-gap: var(--gap); gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
/* The two supporting products under the feature card. A wider minimum than
   the three-up grid it replaced, so each card gets room for a real
   photograph rather than a thumbnail. */
.multicolumn-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.multicolumn-media { aspect-ratio: 4 / 3; }
.multicolumn-media img { object-fit: cover; }
.lineup-title { font-size: var(--step-2); font-family: var(--font-display); }
.lineup-copy { margin: 0; font-size: var(--step--1); color: var(--muted); line-height: 1.55; }
/*
 * A visible affordance so the card reads as somewhere to go. It is
 * aria-hidden because the card already has exactly one link - the title -
 * and the whole card is that link's hit area; announcing a second call to
 * action would be the duplicate-link noise audit finding 2 was about.
 */
.lineup-cta {
  margin: auto 0 0; padding-top: var(--space-s);
  font-size: var(--step--1); font-weight: 700; color: var(--red);
}
.lineup-cta::after { content: " \2192" / ""; }

/* ------------------------------------------------- lineup feature card */

/*
 * The flagship, given a band of its own.
 *
 * Three equal cards is a layout that says all three products matter the same
 * amount. They do not - the Huntsman is what the reviews, the award and most
 * of the traffic are about - and an even row left the eye with nowhere to
 * land first. The feature card is roughly twice the area of one of the two
 * cards below it, which is the difference that makes it read as the lead
 * rather than as the first of three.
 */
.feature-card {
  display: grid;
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: #fff;
  margin-bottom: var(--gap);
}
@media (min-width: 900px) {
  .feature-card { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); }
}
/*
 * `cover`, not `contain`. This is a photograph of the grill in a garden, not
 * a cutout on a white sweep, so it should fill its half of the card the way a
 * photograph does. The product cards elsewhere stay on `contain` because
 * their art is cutouts and cropping one would cut the product.
 */
/*
 * A named aspect ratio, because otherwise the photograph sets the height of
 * the whole card.
 *
 * The asset is square, so at a 613px column it laid out 613px tall, and the
 * copy beside it - which needs about 330px - was centred in that with 140px
 * of empty white above and below. The card read as a picture with a caption
 * lost in the middle of it. 4:3 crops the frame to 460px, which is close
 * enough to what the copy needs that the two halves balance.
 *
 * The image is taken out of flow to make this hold: an `img` with width and
 * height attributes contributes its own intrinsic size to grid sizing, so
 * `aspect-ratio` on the parent alone would not have capped anything.
 */
.feature-card-media { background: var(--surface); position: relative; aspect-ratio: 4 / 3; }
.feature-card-media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.feature-card-body {
  padding: var(--space-xl);
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--space-m);
}
@media (min-width: 900px) {
  .feature-card-body { padding: var(--space-2xl); }
}
.feature-card-title {
  font-family: var(--font-display); font-weight: 900;
  font-size: var(--step-3); line-height: 1.05; margin: 0;
}
/* Ink, not grey. The two supporting cards below this one set their copy in
   --muted; keeping the lead card's at full strength is part of what makes it
   read as the lead rather than as a larger version of the same thing. */
.feature-card-copy { margin: 0; color: var(--ink); line-height: 1.6; }
.feature-card-actions { margin: var(--space-xs) 0 0; }

/*
 * The three facts, in the numeral face because two of them are numbers.
 *
 * The separator is a TRAILING pseudo-element on every item but the last, not
 * a leading one on every item but the first. Both produce the same row when
 * it fits on one line; they differ the moment it wraps, and this row does
 * wrap at desktop. A leading dot puts an orphaned "·" at the start of the
 * second line, which reads as a bullet that lost its list. A trailing one
 * leaves the dot at the end of the line before, which is how a continued
 * enumeration is punctuated anyway.
 *
 * It carries the empty alternative for the same reason the accordion's plus
 * and the nav caret do: generated content counts toward the accessible name,
 * so a bare "·" is read out between every value.
 */
.spec-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--space-2xs) var(--space-s);
  font-family: var(--font-numeral); font-weight: 600;
  font-size: var(--step--1); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink);
}
.spec-list li:not(:last-child)::after {
  content: " ·" / ""; color: var(--gold-text); margin-left: var(--space-2xs);
}

/* ---------------------------------------------------------- dark bands */

/*
 * spg-black is the Brand Guide's primary canvas, and before this pass the
 * footer was the only place the storefront ever showed it. One dark band in
 * the middle of the page gives a very long white document a break and marks
 * the block on it as different in kind rather than merely next in order.
 *
 * Everything that has to move on a dark ground moves here rather than in the
 * blocks that use it, so a component dropped inside inherits values that pass
 * without knowing where it is - the same structural trick as the footer:
 *
 *   --focus  white, because red is 1.99:1 here and the ring would be absent
 *   --muted  stone at 8.15:1; #757575 would be 2.19:1 and unreadable
 *   --gold-text  stone as well. Gold is 3.50:1 on this ground, which clears
 *                the 3:1 for a graphic and fails the 4.5:1 for text, and the
 *                book is explicit that gold is never body copy on a dark
 *                field. The eyebrow class asks for --gold-text, so the token
 *                is what moves rather than the class being special-cased.
 *   --line   a dark hairline, since #e5e1da would be a bright stripe
 *
 * Gold's job here is structural only: the rules top and bottom, and the icon
 * in the eyebrow. Both are graphics.
 */
.band-dark {
  background: var(--ink);
  color: var(--paper);
  border-block: 1px solid var(--gold);
  padding-block: var(--section-y);
  --focus: #ffffff;
  --muted: var(--stone);
  --gold-text: var(--stone);
  --line: #3a3537;
}
.band-dark h2, .band-dark h3 { color: var(--paper); }

.award-inner { display: grid; grid-gap: var(--space-xl); gap: var(--space-xl); align-items: center; }
@media (min-width: 900px) {
  .award-inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: var(--space-3xl);
  }
}
.award-eyebrow { display: flex; align-items: center; gap: var(--space-xs); }
/* The one piece of gold in the band that is not a rule. It is a graphic at
   3.50:1 against spg-black, which is what 1.4.11 asks of one. */
.award-eyebrow svg { color: var(--gold); flex: 0 0 auto; }
/*
 * --step-4 is the top of the Brand Guide's published header ramp, and this is
 * the only h2 on the site that takes it. That is the point: the claim is the
 * loudest thing on the page after the hero, and it is loud because of scale
 * and ground rather than because of an exclamation mark.
 */
.award-heading { font-size: var(--step-4); margin-bottom: var(--space-m); }
.award-copy p { max-width: 54ch; }
.award-copy > :last-child { margin-bottom: 0; }
.award-actions { margin: var(--space-l) 0 0; }
.award-image {
  width: 100%; height: auto; border-radius: var(--radius);
  /* The photograph carries Men's Journal's own badge in its top corner; the
     border keeps it from bleeding into the black band at that edge. */
  border: 1px solid var(--line);
}

/* ----------------------------------------------------------- the claims */

/*
 * Three measured claims, set as numerals.
 *
 * This band replaced the founding-story band when the brand strategy landed.
 * The reasoning is in app/page.tsx and in lib/home-content.ts; what it means
 * here is that the band's job changed from being felt to being believed, and
 * a number set large in the numeral face is how this brand argues.
 *
 * The figure takes Saira in tabular figures, the same role it has on the
 * controller LCD, at --step-4. It is NOT red: the Brand Book gives red
 * "affordance, CTA, active states, alarm numerics", and a marketing figure is
 * none of those. Spending the lead colour on a statistic is how red stops
 * meaning "press this".
 *
 * The gold bar above each item is a graphic at 4.23:1 on the tinted ground,
 * which is what 1.4.11 asks of one, and it is the same job gold does in the
 * feature strip's dividers.
 */
.claims { background: var(--surface); border-block: 1px solid var(--line); }
.claim-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-gap: var(--space-xl); gap: var(--space-xl);
}
@media (min-width: 860px) {
  .claim-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2xl); }
}
.claim-list li { border-top: 3px solid var(--gold); padding-top: var(--space-m); }
.claim-figure {
  font-family: var(--font-numeral); font-feature-settings: "tnum"; font-variant-numeric: tabular-nums;
  font-size: var(--step-4); font-weight: 700; line-height: 1;
  letter-spacing: -.02em; margin: 0 0 var(--space-xs); color: var(--ink);
}
.claim-label {
  font-family: var(--font-body); font-size: var(--step--1); font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--gold-text); margin: 0 0 var(--space-s); line-height: var(--leading-small);
}
.claim-body { margin: 0; color: var(--muted); line-height: 1.55; font-size: var(--step--1); }

/*
 * The press band, directly under the hero.
 *
 * It is deliberately the quietest band on the page. Third-party marks arrive
 * as third-party art - a blue wordmark, an orange badge, a black serif
 * logotype - and nothing can make five of those into a system. What can be
 * controlled is how much attention they are asked to carry: a thin tinted
 * strip with a small label reads as a citation, which is what it is, rather
 * than as a section competing with the product.
 */
.press {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.press-inner {
  display: grid; grid-gap: var(--space-m); gap: var(--space-m);
  padding-block: var(--space-l);
  align-items: center;
}
@media (min-width: 900px) {
  .press-inner { grid-template-columns: auto minmax(0, 1fr); gap: var(--space-2xl); }
}
/* Set as a micro-label rather than as a heading: it labels the row, it is
   not a section title. --gold-text, never raw gold, because 14px is small
   text - the surface rebinding lower down swaps in the darker variant. */
.press-heading {
  margin: 0; font-family: var(--font-body);
  font-size: var(--step--1); font-weight: 600;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--gold-text); white-space: nowrap;
}
.logo-row {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-m) var(--space-xl);
  align-items: center; justify-content: center;
}
@media (min-width: 900px) {
  .logo-row { justify-content: flex-start; gap: var(--space-m) var(--space-2xl); }
}
/*
 * An identical box per mark. See the note in page.tsx: a shared height alone
 * is not optical balance when one logo is a wordmark and the next is a round
 * badge.
 */
.logo-cell { display: grid; place-items: center; width: 128px; height: 44px; }
.press-logo {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain;
}

/* -------------------------------------------------------------- slideshow */

.slideshow { margin-top: 0; }
.recipes-cta { margin-top: var(--space-xl); }
.slideshow-viewport {
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--surface);
}
.slide { display: grid; }
/*
 * `hidden` is a UA-stylesheet `display: none`, and ANY author `display` beats
 * the UA sheet regardless of specificity. So `.slide { display: grid }` was
 * quietly cancelling the `hidden` attribute the component sets on every
 * inactive slide: all four recipes painted, stacked down the page, and all
 * four "Get recipe" links sat in the tab order - the precise WCAG 2.1.1
 * property Slideshow.tsx documents itself as having.
 *
 * Nothing caught it. axe has no rule for "renders more than it should", and
 * the keyboard gate checked the counter and the pause button but never that
 * an inactive slide is gone. It now checks; see scripts/keyboard.mjs.
 */
.slide[hidden] { display: none; }
/* More of the slide is photograph than was. The band's argument is the food;
   the words beside it are a caption on that argument, not a rival to it. */
@media (min-width: 800px) { .slide { grid-template-columns: 1.35fr 1fr; align-items: stretch; } }
.slide-media { background: var(--surface); }
.slide-media img { width: 100%; height: 100%; object-fit: cover; display: block; max-height: 460px; }
/*
 * No `--muted` here. It was set on this rule AND in the on-surface rebinding
 * block at the top of the file, and the duplicate is what broke the dark
 * variant: a value set on `.slide-body` itself beats one inherited from
 * `.slideshow-viewport.is-dark`, so the copy stayed at #6b6b6b and measured
 * 3.05:1 on spg-black. The rebinding block already lists `.slide-body`; this
 * rule only needs layout. axe caught it, which is three times now for this
 * exact class of bug.
 */
.slide-body {
  padding: var(--space-xl);
  display: flex; flex-direction: column; justify-content: center;
}
.slide-body h3 { font-size: var(--step-2); }
.slide-body p { color: var(--muted); }
.slide-body > :last-child { margin-bottom: 0; }
/* `.btn` is inline-flex, but inside a column flex container it is a flex item
   and stretches to the full width by default - which turned "Get recipe" into
   a white bar the width of the panel, outweighing the photograph the band
   exists to show. It sizes to its label. */
.slide-body .btn { align-self: flex-start; }

/*
 * The dark slide.
 *
 * Every one of these photographs is lit dark and shot close, and against them
 * a warm off-white caption panel read as the half of the slide that had not
 * been art-directed. On spg-black the panel disappears into the photograph's
 * own shadows, the two halves become one object, and the food is the
 * brightest thing on the band - which is the whole point of the band.
 *
 * The tokens move for the same reason they move on `.band-dark`: --muted at
 * #6b6b6b is 2.85:1 here and unreadable, stone is 8.15:1. The CTA is chosen
 * in the component, because red at 1.99:1 is not available on this ground.
 */
.slideshow-viewport.is-dark {
  background: var(--ink); border-color: var(--gold);
  color: var(--paper);
  --focus: #ffffff;
}
.slideshow-viewport.is-dark .slide-media { background: var(--ink); }
/*
 * The rebinding has to land ON `.slide-body`, not on an ancestor, because the
 * on-surface block sets these two tokens on `.slide-body` directly and a
 * custom property set on the element itself always beats an inherited one
 * whatever the ancestor's specificity. Same shape as `.band-dark`, one level
 * lower down.
 */
.slideshow-viewport.is-dark .slide-body {
  --muted: var(--stone);        /* 8.15:1 on spg-black; #6b6b6b is 3.05:1 */
  --gold-text: var(--stone);    /* gold is 3.50:1 here - graphics only */
}
.slideshow-viewport.is-dark .slide-body h3 { color: var(--paper); }

.slideshow-controls {
  display: flex; align-items: center; justify-content: center;
  gap: .35rem; margin-top: .75rem;
}
.slideshow-button {
  border: 1px solid var(--line); border-radius: 8px;
  background: #fff; color: var(--ink); font-size: 1.15rem;
}
.slideshow-button:hover { background: var(--surface); }
.slideshow-position { margin: 0 .5rem; font-size: var(--step--1); color: var(--muted); min-width: 6ch; text-align: center; }

/* -------------------------------------------------------------------- blog */

.article-grid {
  list-style: none; margin: var(--space-xl) 0 0; padding: 0;
  display: grid; grid-gap: var(--gap); gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
/* Under a strip-head, which already carries the space. */
.article-grid-flush { margin-top: 0; }
.article-media { aspect-ratio: 16 / 10; }
.article-media img { object-fit: cover; }
.article-meta {
  margin: 0; font-size: var(--step--1); color: var(--muted);
  text-transform: uppercase; letter-spacing: .08em; font-weight: 600;
}
.article-card-title { font-size: var(--step-2); font-family: var(--font-display); }
.article-excerpt {
  margin: 0; font-size: var(--step--1); color: var(--muted); line-height: 1.55;
  /* Shopify excerpts run to wildly different lengths and a three-up grid
     built from them looked shredded. Four lines, then an ellipsis. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}
.article-hero { width: 100%; height: auto; border-radius: var(--radius); margin-block: 1rem 1.5rem; }

/*
 * The standfirst was Aleo. Brand Guide r2 drops the slab serif outright -
 * it "read as retail rather than as a grilling company" - so this is body
 * copy at a larger size instead, and the gold rule beside it does the work
 * of marking it as a standfirst.
 *
 * That rule is a graphic, not text, so gold at 3.50:1 on white clears the
 * 3:1 that 1.4.11 asks. The excerpt itself stays in body grey at 4.61:1.
 */
.article-standfirst {
  font-size: 1.15rem; line-height: 1.5;
  border-left: 3px solid var(--gold);
  padding-left: 1.1rem; margin-block: 1.25rem 1.75rem;
  color: var(--muted);
}
.article-tags { margin-top: 2.5rem; border-top: 1px solid var(--line); padding-top: 1.25rem; }

/* ------------------------------------------------------------- collection */

.collection-header { padding-block: .5rem 1.5rem; max-width: 70ch; }
.collection-layout { display: grid; grid-gap: 2rem; gap: 2rem; padding-bottom: 3rem; }
@media (min-width: 900px) {
  .collection-layout { grid-template-columns: 260px minmax(0, 1fr); gap: 2.5rem; align-items: start; }
  .collection-aside { position: -webkit-sticky; position: sticky; top: 1rem; }
}
.filters-heading {
  font-size: var(--step--1); text-transform: uppercase; letter-spacing: .12em;
  font-family: var(--font-body); font-weight: 700; color: var(--muted);
  margin-bottom: var(--space-s);
}

/*
 * The disclosure button, mobile only. See FilterDisclosure.tsx for why the
 * panel collapses at all - it is a measured problem, not a preference.
 */
.filter-toggle {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  gap: var(--space-m); min-height: 48px; padding: 0 var(--space-m);
  border: 1px solid var(--control-line); border-radius: var(--radius);
  background: #fff; color: var(--ink);
  font: inherit; font-weight: 700; cursor: pointer;
}
.filter-toggle:hover { border-color: var(--ink); }
.filter-toggle-glyph { font-size: 1.3rem; line-height: 1; color: var(--red); }
.filter-toggle-count {
  display: inline-grid; place-items: center; margin-left: var(--space-xs);
  min-width: 22px; height: 22px; padding: 0 .35rem;
  border-radius: 999px; background: var(--red); color: var(--on-red);
  font-size: var(--step--1); font-weight: 800;
}
.filter-panel { display: none; margin-top: var(--space-m); }
.filter-panel.is-open { display: block; }

/*
 * The panel is a tool, so it gets an edge and a tint that separate it from
 * the catalogue it acts on. It also rebinds the two on-surface text tokens,
 * because it paints --surface; the rebinding block further up lists it.
 */
.filter-panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--space-m);
}

@media (min-width: 900px) {
  /* From here the panel is always laid out, whatever the button's state. */
  .filter-toggle { display: none; }
  .filter-panel, .filter-panel.is-open { display: block; margin-top: 0; }
}

.filter-form { display: grid; grid-gap: var(--space-m); gap: var(--space-m); }
.filter-group { border: 0; padding: 0; margin: 0; }
.filter-group + .filter-group,
.filter-form .field + .filter-group {
  border-top: 1px solid var(--line); padding-top: var(--space-m);
}
.filter-group legend {
  font-weight: 700; font-size: var(--step--1); padding: 0 0 var(--space-2xs);
  text-transform: uppercase; letter-spacing: .07em;
}
.filter-values { list-style: none; margin: 0; padding: 0; display: grid; grid-gap: .1rem; gap: .1rem; max-height: 15rem; overflow-y: auto; }
.filter-check {
  display: flex; align-items: center; gap: var(--space-xs);
  min-height: 44px; cursor: pointer; font-size: var(--step-0);
  border-radius: 6px; padding-inline: var(--space-2xs);
}
.filter-check:hover { background: #fff; }
.filter-check input { width: 18px; height: 18px; flex: 0 0 auto; accent-color: var(--red); }
.filter-price { display: grid; grid-template-columns: 1fr 1fr; grid-gap: var(--space-xs); gap: var(--space-xs); }
.filter-sort { max-width: 100%; }
/*
 * Apply is the commit action for the tool, not the loudest thing on a page
 * whose job is selling grills. Inside the tinted panel it reads as the end of
 * a form; as a full-bleed red block on bare white it outweighed every product
 * in the grid beside it.
 */
.filter-form .btn-primary { margin-top: var(--space-2xs); }
.filter-count {
  font-size: var(--step--1); margin: 0; text-align: center; color: var(--muted);
}

.applied-filters { margin-bottom: 1.25rem; }
.applied-heading { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: var(--space-2xs); }
.chip {
  display: inline-flex; align-items: center; gap: .35rem;
  min-height: 36px; padding: .3rem .65rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink);
  font-size: var(--step--1); text-decoration: none;
}
.chip:hover { border-color: var(--gold-text); }
.chip-clear { background: none; color: var(--red); font-weight: 700; }
/*
 * The selected topic on the blog index. Ink fill at 16.30:1 for the label,
 * and `aria-current="page"` on the link carries the same state to anyone not
 * looking at it - 1.4.1 is satisfied by the attribute, not by the colour.
 */
.chip.is-active {
  background: var(--ink); color: #fff; border-color: var(--ink); font-weight: 600;
}
.chip.is-active:hover { background: var(--charcoal); border-color: var(--charcoal); }

.tag-filter { margin-block: var(--space-m) var(--space-xl); }
.tag-filter-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-xs);
}

.pager {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; margin-top: 2.5rem; flex-wrap: wrap;
}

/* ------------------------------------------------------ announcement bar */

/*
 * The one full-bleed red band. White on spg-red is 8.21:1, so everything in
 * here - text, links, arrows and the focus ring - is white and nothing has
 * to be checked twice.
 */
.announcement { background: var(--red); color: var(--on-red); }
.announcement-inner {
  display: flex; align-items: center; justify-content: center; gap: var(--space-xs);
  padding-block: var(--space-2xs);
}
.announcement-text {
  margin: 0; text-align: center;
  font-size: var(--step--1); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase;
}
.announcement-text a { color: var(--on-red); text-underline-offset: 3px; }
.announcement-arrow { color: var(--on-red); font-size: 1.4rem; line-height: 1; }

/* ------------------------------------------------------------ site header */

/*
 * One row instead of three. The old header stacked an announcement strip, a
 * utility strip of three small social links, and the header proper, which put
 * the first real content most of a viewport down the page. Social moved to
 * the footer; this is what is left.
 *
 * Hierarchy, largest to smallest: the seal anchors the left, the nav sits on
 * its own baseline with room to breathe, and the cart is a bordered
 * affordance so it reads as pressable rather than as one more link.
 */
.site-header {
  border-bottom: 2px solid var(--gold);
  position: relative; background: #fff;
}
.site-header-inner {
  display: flex; align-items: center; gap: 1.25rem;
  padding-block: 1rem;
}

/* ---- brand lockup ---- */

.brand {
  display: inline-flex; align-items: center; gap: .85rem;
  color: var(--ink); text-decoration: none; margin-right: auto;
  padding: .25rem 0;
}
.brand-mark {
  width: 64px; height: 64px; object-fit: contain; flex: 0 0 auto;
}
.brand-lockup { display: grid; grid-gap: .1rem; gap: .1rem; line-height: 1.05; }
/*
 * `nowrap` on both halves of the lockup, and it is load-bearing.
 *
 * A lockup that wraps is not the lockup any more. At 1100px - the width where
 * the desktop nav appears and the row is tightest - "SPIDER GRILLS" broke
 * across two lines and the tagline broke across two more, and the header went
 * from 96px tall to 115px with the mark floating beside a four-line block of
 * text. Every gate passed it: the row had not overflowed and no two siblings
 * were on different lines, because the wrapping was happening INSIDE one
 * child.
 *
 * Refusing to wrap turns that silent failure into a loud one. If the lockup
 * no longer fits, the row overflows instead, and overflow is what the axe
 * gate's header-fit check already measures.
 */
.brand-word {
  font-family: var(--font-display);
  font-weight: 900; font-size: 1.32rem; letter-spacing: .055em;
  white-space: nowrap;
}
/*
 * The microlabel is the one piece of small gold text in the header, so it
 * uses the derived --gold-text at 4.65:1 rather than raw gold at 3.31:1.
 *
 * It is set to finish flush with the wordmark above it, and
 * `text-align-last: justify` is what makes that land: it absorbs whatever
 * is left over, so the two edges agree even when font metrics shift between
 * platforms rather than depending on one hardcoded size being pixel-perfect
 * everywhere. The sizing note that used to sit here quoted widths measured
 * against Anybody, which stopped being the display face at Brand Guide r2;
 * the current numbers are in the rule below.
 *
 * No negative right margin here, though tracked text does put a gap after
 * every character including the last. When justifying, the browser already
 * discards that trailing space and lands the final glyph on the edge, so
 * correcting for it a second time pushed the "9" 1.87px PAST the "S" -
 * measured, which is the only way that would have been noticed.
 */
.brand-tag {
  display: block;
  /*
   * 14px, because page 06 sets that as the floor and nothing on the site is
   * allowed under it - this was .73rem / 11.7px.
   *
   * Tracking is .06em and that number is measured, not chosen. The wordmark
   * sets the lockup's width at 196.1px in Archivo Black; the tagline has to
   * come in UNDER that so `text-align-last: justify` has slack to stretch it
   * out to meet the edge. At 14px the options measured 218.3px at .16em and
   * 199.8px at .10em - both wider than the wordmark, which would invert the
   * lockup and make the tagline set the width. .06em gives 187.5px, and
   * justify distributes the remaining 8.6px across the gaps.
   */
  font-size: var(--step--1); letter-spacing: .06em; text-transform: uppercase;
  font-weight: 600; color: var(--gold-text);
  text-align: justify;
  text-align-last: justify;
  white-space: nowrap;
}

@media (max-width: 900px) {
  .brand-mark { width: 52px; height: 52px; }
  .brand-word { font-size: 1.1rem; }
}
/*
 * The tight band: wide enough for the desktop nav, not wide enough for six
 * entries, a search control, a cart and a full-size lockup all at once. The
 * lockup gives up its size here rather than the navigation giving up an
 * entry, because the mark still identifies the site at 52px - page 02 of the
 * book puts the seal's floor at 44 - and a menu with a hole in it does not
 * navigate.
 *
 * The tagline goes rather than shrinking. Below the wordmark's width it has
 * no slack for `text-align-last: justify` to distribute, so it would stop
 * finishing flush with the wordmark and the lockup would look misaligned
 * rather than smaller.
 */
@media (min-width: 1100px) and (max-width: 1279px) {
  .brand-mark { width: 52px; height: 52px; }
  .brand-word { font-size: 1.1rem; }
  .brand-tag { display: none; }
}
@media (max-width: 420px) {
  .brand-lockup { display: none; }
}

/* ---- navigation ---- */

.nav-toggle { order: 3; color: var(--ink); font-size: 1.25rem; }
.nav-panel { display: none; }
.nav-panel.is-open {
  display: block; position: absolute; left: 0; right: 0; top: 100%;
  background: #fff; border-bottom: 2px solid var(--gold); padding: .5rem 1rem 1rem;
  z-index: 30; box-shadow: 0 8px 20px rgba(0,0,0,.10);
}
.nav-list { list-style: none; margin: 0; padding: 0; display: grid; grid-gap: .25rem; gap: .25rem; }
.nav-link {
  color: var(--ink); text-decoration: none; font-size: .95rem; font-weight: 600;
  display: flex; align-items: center; gap: .35rem;
  min-height: 44px; padding: .25rem 0; cursor: pointer; list-style: none;
  position: relative;
}
.nav-link::-webkit-details-marker { display: none; }
.nav-dropdown > summary::after { content: "▾" / ""; color: var(--red); }
.nav-submenu { list-style: none; margin: 0 0 .5rem; padding: 0 0 0 .9rem; display: grid; }
.nav-sublink {
  color: var(--ink); text-decoration: none; font-size: .9rem;
  display: flex; align-items: center; min-height: 40px;
}
.nav-sublink[aria-current="page"], .nav-link[aria-current="page"] {
  color: var(--red); text-decoration: underline;
}

/*
 * 1100, not 900.
 *
 * The IA rebuild took the header from four entries to six - Venom and Parts
 * had no home before - and added a search control beside the cart. The row
 * now has to hold the brand lockup at 275px, six uppercase labels, search and
 * cart, which measures about 1005px of content. At the old 900px breakpoint
 * that overflows by roughly 40px and the nav wraps under the logo.
 *
 * So the disclosure covers a wider range than it used to. That is the right
 * trade: a tablet gets a menu button, which works, rather than a broken row,
 * which does not. The axe gate walks the widths from this breakpoint up and
 * fails if the header row wraps or overflows its column, so the next entry
 * added to the nav gets a red build rather than a broken header.
 */
@media (min-width: 1100px) {
  /*
    Above the breakpoint the disclosure is gone and the panel is always
    laid out, so a collapsed state can never hide the nav on a wide screen.
  */
  .nav-toggle { display: none; }
  .nav-panel, .nav-panel.is-open {
    display: block; position: static; padding: 0; border: 0; box-shadow: none;
  }
  .nav-list { grid-auto-flow: column; gap: 1.35rem; align-items: center; }
  .nav-link {
    font-size: .85rem; text-transform: uppercase; letter-spacing: .06em;
    font-weight: 700; min-height: 40px;
  }
  /*
   * A gold underline on hover and focus, drawn as a graphic at 3.31:1.
   *
   * It has to be ::before. The dropdown caret already owns ::after on the
   * same summary element, and putting the underline there silently replaced
   * the caret, so the three menus with children stopped advertising that
   * they had any.
   */
  .nav-link::before {
    content: ""; position: absolute; left: 0; right: 0; bottom: .1rem;
    height: 2px; background: var(--gold); transform: scaleX(0);
    transition: transform .15s ease;
  }
  .nav-link:hover::before,
  .nav-panel li:focus-within > .nav-link::before { transform: scaleX(1); }

  .nav-dropdown { position: relative; }
  .nav-submenu {
    position: absolute; top: 100%; left: 0; min-width: 240px;
    background: #fff; border: 1px solid var(--line);
    border-top: 2px solid var(--gold);
    border-radius: 0 0 var(--radius) var(--radius);
    margin: .4rem 0 0; padding: .35rem; z-index: 40;
    box-shadow: 0 10px 24px rgba(0,0,0,.12);
  }
  .nav-sublink { padding: .4rem .7rem; border-radius: 6px; text-transform: none; letter-spacing: 0; }
  .nav-sublink:hover { background: var(--surface); }
}

/* ---- search ---- */

/*
 * Quieter than the cart on purpose. Both are utilities in the same corner,
 * and if they carry the same weight the eye has to read both to find either.
 * The cart is a bordered affordance because it is the end of the funnel;
 * search is a text link with a glyph because it is a way in.
 */
.search-link {
  order: 4; display: inline-flex; align-items: center; gap: .4rem;
  min-height: 44px; padding: 0 .5rem;
  color: var(--ink); text-decoration: none;
  font-weight: 700; font-size: .9rem;
  text-transform: uppercase; letter-spacing: .06em;
  border-radius: var(--radius);
}
.search-link:hover { background: var(--surface); }
@media (min-width: 760px) {
  .search-label {
    position: static; width: auto; height: auto;
    margin: 0; overflow: visible; clip: auto; white-space: normal;
  }
}
/*
 * Back to icon-only in the tight band, where the desktop nav is showing but
 * the row has no slack. Same rule as the brand lockup shrinking there: the
 * navigation keeps all six entries and the utilities give up their words.
 *
 * This hides the label the responsive way, so both controls keep their
 * accessible names - "Search the Spider Grills catalogue" and "Cart" are
 * still what a screen reader hears. Later in the file than the rule above, at
 * equal specificity, so it wins inside the range.
 */
@media (min-width: 1100px) and (max-width: 1279px) {
  .search-label, .cart-label {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
}

.search-form {
  display: flex; flex-wrap: wrap; gap: var(--space-s);
  align-items: end; margin-block: var(--space-l) var(--space-m);
}
.search-form .field { flex: 1 1 22rem; min-width: 0; }
.search-count {
  margin: 0 0 var(--space-l); color: var(--muted); font-size: var(--step--1);
}
/* The count owns the space above the grid, so the grid does not add its own. */
.search-results { margin-top: 0; }

/* ---- cart ---- */

.cart-link {
  order: 5; display: inline-flex; align-items: center; gap: .5rem;
  min-height: 44px; padding: 0 .9rem;
  border: 2px solid var(--ink); border-radius: var(--radius);
  color: var(--ink); text-decoration: none; font-weight: 700; font-size: .9rem;
  text-transform: uppercase; letter-spacing: .06em;
}
.cart-link:hover { background: var(--ink); color: #fff; }
@media (min-width: 620px) {
  .cart-label {
    position: static; width: auto; height: auto;
    margin: 0; overflow: visible; clip: auto; white-space: normal;
  }
}
.cart-count {
  background: var(--red); color: var(--on-red);
  border-radius: 999px; min-width: 22px; height: 22px;
  display: inline-grid; place-items: center;
  font-size: .75rem; font-weight: 800; padding: 0 .35rem;
}
.cart-link:hover .cart-count { background: var(--gold); color: var(--on-gold); }

/* -------------------------------------------------------------------- cart */

.cart-layout { display: grid; grid-gap: var(--space-xl); gap: var(--space-xl); margin-top: var(--space-l); }
@media (min-width: 900px) { .cart-layout { grid-template-columns: minmax(0, 1fr) 340px; gap: var(--space-2xl); align-items: start; } }

.cart-lines { list-style: none; margin: 0; padding: 0; display: grid; grid-gap: var(--space-l); gap: var(--space-l); }
.cart-line {
  display: grid; grid-template-columns: 96px minmax(0, 1fr) auto; grid-gap: var(--space-m); gap: var(--space-m);
  border-bottom: 1px solid var(--line); padding-bottom: var(--space-l); align-items: start;
}
.cart-line:last-child { border-bottom: 0; padding-bottom: 0; }
.cart-line-media {
  width: 96px; aspect-ratio: 1; background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px; display: grid; place-items: center; overflow: hidden;
}
.cart-line-media img { width: 100%; height: 100%; object-fit: contain; }
.cart-line-title { font-size: var(--step-0); margin: 0 0 var(--space-2xs); line-height: 1.3; }
.cart-line-title a { color: var(--ink); text-decoration: none; }
.cart-line-title a:hover { text-decoration: underline; }
.cart-line-total { font-weight: 700; margin: 0; white-space: nowrap; font-size: var(--step-1); }
.cart-line-options { margin: 0; font-size: var(--step--1); color: var(--muted); }
.cart-line-unit { margin: var(--space-2xs) 0 0; font-weight: 600; color: var(--muted); font-size: var(--step--1); }

.cart-line-controls { display: flex; align-items: center; gap: var(--space-s); margin-top: var(--space-m); flex-wrap: wrap; }
.qty-group { display: inline-flex; margin: 0; }
/* Same 1.4.11 reasoning as the form fields: the box is the control. */
.qty-button { border: 1px solid var(--control-line); border-radius: 8px; background: #fff; font-size: 1.15rem; }
.qty-button:hover:not([disabled]) { background: var(--surface); border-color: var(--ink); }
.qty-value { margin: 0; min-width: 2.5ch; text-align: center; font-weight: 700; }
.link-button {
  background: none; border: 0; padding: .5rem; min-height: 44px;
  color: var(--red); font: inherit; font-size: .875rem;
  text-decoration: underline; cursor: pointer;
}

.cart-summary {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: var(--space-l); display: grid; grid-gap: var(--space-m); gap: var(--space-m);
  position: -webkit-sticky;
  position: sticky; top: var(--space-m);
}
.cart-summary h2 { font-size: var(--step-2); margin: 0; }
.cart-totals { margin: 0; display: grid; grid-gap: var(--space-xs); gap: var(--space-xs); }
.cart-totals > div { display: flex; justify-content: space-between; gap: var(--space-m); }
.cart-totals dt { margin: 0; }
.cart-totals dd { margin: 0; font-weight: 600; }
.cart-total-row {
  border-top: 1px solid var(--control-line); margin-top: var(--space-2xs);
  padding-top: var(--space-s); font-size: var(--step-1);
}
.cart-total-row dt, .cart-total-row dd { font-weight: 800; }
.cart-note { margin: 0; font-size: var(--step--1); color: var(--muted); text-align: center; }

/*
 * The design agent dock. Only ever rendered on sandbox.spidergrills.ai, and
 * styled so it cannot be mistaken for part of the shop: it sits above
 * everything, on the brand red, and says what it is.
 */
.agent-dock { position: fixed; right: 16px; bottom: 16px; z-index: 9999; }
.agent-dock-open {
  background: var(--red); color: var(--on-red); border: 0; cursor: pointer;
  border-radius: 999px; padding: 12px 20px; font: 600 13px/1 var(--font-body);
  letter-spacing: .04em; box-shadow: 0 6px 24px rgb(0 0 0 / 28%);
}
.agent-dock-panel {
  width: min(420px, calc(100vw - 32px)); max-height: min(560px, calc(100vh - 32px));
  display: flex; flex-direction: column; gap: 8px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--control-line); border-radius: var(--radius);
  box-shadow: 0 12px 40px rgb(0 0 0 / 30%); padding: 12px;
}
.agent-dock-head { display: flex; align-items: center; justify-content: space-between; }
.agent-dock-head button { background: none; border: 0; cursor: pointer; font-size: 15px; color: var(--muted); }
.agent-dock-note { font-size: var(--step--1); color: var(--muted); line-height: var(--leading-small); }
.agent-dock-note code { font-family: var(--font-numeral); font-size: .95em; }
.agent-dock-log { overflow-y: auto; display: grid; grid-gap: 8px; gap: 8px; padding: 4px 0; }
.agent-dock-empty, .agent-dock-msg { margin: 0; font-size: var(--step--1); line-height: var(--leading-small); }
.agent-dock-empty { color: var(--muted); }
.agent-dock-user { font-weight: 600; }
.agent-dock-assistant { color: var(--muted); white-space: pre-wrap; }
.agent-dock-error { margin: 0; font-size: var(--step--1); color: var(--danger); }
.agent-dock-form { display: flex; gap: 8px; }
.agent-dock-form input {
  flex: 1 1; min-width: 0; padding: 10px; font: inherit; font-size: var(--step--1);
  border: 1px solid var(--control-line); border-radius: 6px; background: var(--bg); color: var(--ink);
}
.agent-dock-form button {
  background: var(--red); color: var(--on-red); border: 0; border-radius: 6px;
  padding: 10px 16px; font: 600 13px/1 var(--font-body); cursor: pointer;
}
.agent-dock-form button[disabled] { opacity: .5; cursor: default; }

/*!****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[2].use[1]!./node_modules/next/dist/build/webpack/loaders/next-font-loader/index.js??ruleSet[1].rules[14].oneOf[2].use[2]!./node_modules/next/font/google/target.css?{"path":"app/layout.tsx","import":"Archivo","arguments":[{"subsets":["latin"],"display":"swap","variable":"--font-archivo-black","weight":["900"]}],"variableName":"archivo"} ***!
  \****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/* vietnamese */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/252fe63ba044b66a-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/fc41968e50424dc3-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 900;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/e6921b6a1b3694e0-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}@font-face {font-family: 'Archivo Fallback';src: local("Arial");ascent-override: 88.96%;descent-override: 21.28%;line-gap-override: 0.00%;size-adjust: 98.70%
}.__className_356554 {font-family: 'Archivo', 'Archivo Fallback';font-weight: 900;font-style: normal
}.__variable_356554 {--font-archivo-black: 'Archivo', 'Archivo Fallback'
}

/*!****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[2].use[1]!./node_modules/next/dist/build/webpack/loaders/next-font-loader/index.js??ruleSet[1].rules[14].oneOf[2].use[2]!./node_modules/next/font/google/target.css?{"path":"app/layout.tsx","import":"Anybody","arguments":[{"subsets":["latin"],"display":"swap","variable":"--font-anybody","weight":["400","500","600","700"]}],"variableName":"anybody"} ***!
  \****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/* vietnamese */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/35d6509c4f679996-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/45389aa4d9ffef4f-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a72d451f1e0337db-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/35d6509c4f679996-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/45389aa4d9ffef4f-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a72d451f1e0337db-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/35d6509c4f679996-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/45389aa4d9ffef4f-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a72d451f1e0337db-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/35d6509c4f679996-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/45389aa4d9ffef4f-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Anybody';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a72d451f1e0337db-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}@font-face {font-family: 'Anybody Fallback';src: local("Arial");ascent-override: 72.11%;descent-override: 21.77%;line-gap-override: 0.00%;size-adjust: 110.25%
}.__className_f1e6e5 {font-family: 'Anybody', 'Anybody Fallback';font-style: normal
}.__variable_f1e6e5 {--font-anybody: 'Anybody', 'Anybody Fallback'
}

/*!****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
  !*** css ./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[14].oneOf[2].use[1]!./node_modules/next/dist/build/webpack/loaders/next-font-loader/index.js??ruleSet[1].rules[14].oneOf[2].use[2]!./node_modules/next/font/google/target.css?{"path":"app/layout.tsx","import":"Saira","arguments":[{"subsets":["latin"],"display":"swap","variable":"--font-saira","weight":["500","600","700"]}],"variableName":"saira"} ***!
  \****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/* vietnamese */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a5ded5fed781d3c1-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/f28ff2d15b1e9bd8-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/9b01607e2f794050-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a5ded5fed781d3c1-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/f28ff2d15b1e9bd8-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/9b01607e2f794050-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/a5ded5fed781d3c1-s.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/f28ff2d15b1e9bd8-s.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Saira';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(/_next/static/media/9b01607e2f794050-s.p.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}@font-face {font-family: 'Saira Fallback';src: local("Arial");ascent-override: 109.52%;descent-override: 42.36%;line-gap-override: 0.00%;size-adjust: 103.63%
}.__className_5cb132 {font-family: 'Saira', 'Saira Fallback';font-style: normal
}.__variable_5cb132 {--font-saira: 'Saira', 'Saira Fallback'
}

