@charset "UTF-8";
/* =============================================================================
   New Standard Ag storefront sheet — assembled by scripts/deploy-css.mjs.
   DO NOT EDIT ON THE HOST. Edit design/blocks/*.css and re-run the deploy; this
   file is regenerated wholesale and any host-side edit is reported as drift.
   Blocks: NSA-TOKENS, NSA-HEADER, NSA-HOME, NSA-PLP, NSA-LAYOUT, NSA-PDP-TABS
   ========================================================================== */

/* NSA-TOKENS-BEGIN */
/* -----------------------------------------------------------------------------
   NSA TOKENS — brand primitives for New Standard Ag.

   PALETTE PROVENANCE. Every value below was MEASURED off www.newstandardag.com by
   scripts/c4-brand.mjs (a computed-style census of their live pages), not chosen.
   Recording one correction: the brief and plan.md both describe the primary as
   "their green". It is not. New Standard Ag is a NAVY + MEDIUM BLUE brand:
     rgb(0,64,104)   #004068  38 uses  — wordmark, headings           -> primary
     rgb(44,130,196) #2C82C4   9 uses  — the CONTACT button fill      -> accent
     rgb(192,43,10)  #C02B0A  16 uses  — a rust used sparingly        -> alert
     rgb(112,112,112)#707070 294 uses  — body copy
   Their type is Barlow / Barlow Condensed (self-hosted below, latin subset).

   THE TWO-TOKEN ACCENT (§B2). --nsa-accent is a FILL. White on #2C82C4 is 3.79:1,
   under the 4.5 AA floor for normal text, so it never carries a label and never
   becomes `color:` on a light surface. --nsa-accent-ink #2470A8 (5.30:1 on white,
   5.03:1 on #F5F7FA) is the text-on-light variant. Buttons therefore paint from
   the navy, which carries white at 10.86:1 — decided here, up front, not patched
   later. The ratios are asserted by scripts/c6-contrast.mjs, not asserted by hand.
   -------------------------------------------------------------------------- */

@font-face{font-family:'Barlow';font-style:normal;font-weight:400;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-latin-400-normal.woff2') format('woff2')}
@font-face{font-family:'Barlow';font-style:normal;font-weight:500;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-latin-500-normal.woff2') format('woff2')}
@font-face{font-family:'Barlow';font-style:normal;font-weight:600;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-latin-600-normal.woff2') format('woff2')}
@font-face{font-family:'Barlow';font-style:normal;font-weight:700;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-latin-700-normal.woff2') format('woff2')}
@font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:600;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-condensed-latin-600-normal.woff2') format('woff2')}
@font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:700;font-display:swap;
  src:url('/Files/Templates/Designs/NewStandardAg/fonts/barlow-condensed-latin-700-normal.woff2') format('woff2')}

:root{
  --nsa-primary:#004068;
  --nsa-primary-dark:#00293F;
  --nsa-primary-soft:#E6EDF3;
  --nsa-accent:#2C82C4;          /* FILL only — never a text colour on light */
  --nsa-accent-ink:#2470A8;      /* the text-on-light variant, 5.30:1 on white */
  --nsa-accent-soft:#DCEAF6;
  --nsa-alert:#C02B0A;           /* their rust: discontinued / hazard only     */

  --nsa-ink:#1B2A36;
  --nsa-body:#4A5966;
  /* #6B7A87 measured 4.41:1 on white at 412x823 — just under AA — on the
     "Sign in to see your contract price" note. #64717F is 4.99:1. */
  --nsa-muted:#64717F;
  --nsa-line:#DDE4EA;
  --nsa-surface:#F4F7F9;
  --nsa-surface-2:#E9EFF4;

  --nsa-font:'Barlow',system-ui,-apple-system,'Segoe UI',Helvetica,Arial,sans-serif;
  --nsa-font-display:'Barlow Condensed','Barlow',system-ui,sans-serif;

  /* Header clearance. The real value is set in 10-header.css, keyed on the SERVED
     DOM rather than on a media query (§B5) — this is only the desktop default. */
  --nsa-bar-clearance:0px;
}

/* Marker rule — the gate's CSSOM leg asserts this selector is really in
   document.styleSheets. A re-scope of this string fails the gate on purpose. */
.nsa-marker-tokens{--nsa-block:"tokens"}

/* --- type ------------------------------------------------------------------
   Swift's Typography sheet declares --dw-font-family:Inter on `body` and on the
   heading group. Same specificity, later sheet, so these win without !important. */
body,.preview{--dw-font-family:var(--nsa-font);--dw-base-font-size:16px;--dw-line-height:1.55}
h1,h2,h3,h4,h5,h6,.dw-h1,.dw-h2,.dw-h3,.dw-h4,.dw-h5,.dw-h6{
  --dw-font-family:var(--nsa-font);--dw-font-weight:700;--dw-letter-spacing:-0.01em;--dw-line-height:1.12}
[data-dw-button]{--dw-font-family:var(--nsa-font);--dw-font-weight:600;--dw-letter-spacing:.01em}

body{color:var(--nsa-body)}
/* Headings must DEFER to a section's colour scheme. A flat `color:var(--nsa-ink)` here
   broke inheritance and painted the hero's h1 navy-on-navy — invisible, and caught only
   by reading the screenshot back. --dw-color-foreground is declared exclusively inside
   the .dw-colorscheme-* rules, so it is undefined on an unscheme'd section and the
   fallback applies there and only there. */
main h1,main h2,main h3,main h4,main h5,main h6{color:var(--dw-color-foreground,var(--nsa-ink))}

/* The display face is reserved for the hero and section eyebrows — a condensed
   face set as body copy is unreadable at 15px. */
.nsa-display{font-family:var(--nsa-font-display);font-weight:700;letter-spacing:.005em;line-height:1.04}
.nsa-eyebrow{font-family:var(--nsa-font-display);font-weight:600;letter-spacing:.13em;
  text-transform:uppercase;font-size:.86rem;color:var(--nsa-accent-ink)}

/* --- accents ---------------------------------------------------------------- */
main a:not(.btn):not(.nav-link):not(.dropdown-item){color:var(--nsa-accent-ink)}
main a:not(.btn):not(.nav-link):not(.dropdown-item):hover{color:var(--nsa-primary)}
::selection{background:var(--nsa-accent-soft);color:var(--nsa-primary-dark)}
:focus-visible{outline:3px solid var(--nsa-accent);outline-offset:2px}

/* A hairline rule in the brand blue, used by the band headings below.
   NB `border` resets `border-inline-start`, so the shorthand must never come
   after this rule for anything that uses it (§B13). */
.nsa-rule{border:0;border-top:3px solid var(--nsa-accent);width:56px;opacity:1}
/* NSA-TOKENS-END 2026-07-31 */

/* NSA-HEADER-BEGIN */
/* -----------------------------------------------------------------------------
   NSA HEADER — the branded chrome. The 220px overflow was fixed in the CONTENT
   MODEL (scripts/c8-header.mjs moved the search field out of the nav row and the
   seven category labels were shortened to the plan's budget); nothing here masks
   geometry and there is no `overflow-x:hidden` anywhere in this sheet.

   A DELIBERATE DEPARTURE FROM §B5, recorded rather than silently taken: marine's
   recipe is a transparent/floating header over a full-bleed hero, with clearance
   carried by a `main` padding token. New Standard Ag's own site uses a solid white
   header, and for a B2B supplies distributor a sober, always-legible bar beats a
   floating pill — the header carries a search field and an account menu that must
   never sit on top of photography. Keeping Swift's in-flow `.sticky-top` also means
   content starts immediately below the header, so the gate's header-clearance assert
   measures a ~0px gap by construction and there is no hand-fitted token to drift.
   --nsa-bar-clearance stays declared in the tokens block at 0 for that reason.
   -------------------------------------------------------------------------- */

.nsa-marker-header{--nsa-block:"header"}

header.sticky-top{
  background:#fff;
  border-bottom:1px solid var(--nsa-line);
  box-shadow:0 1px 0 rgba(0,64,104,.04),0 6px 18px -14px rgba(0,64,104,.35);
}

/* A hairline of brand blue along the very top — their wordmark's speed-lines,
   reduced to one rule. Painted on the impersonation row, which is 0px tall in a
   normal session, so it costs no vertical space. */
header.sticky-top > section:first-child{
  border-top:3px solid var(--nsa-primary);
}

/* --- row 1: wordmark | search | account | favourites | cart ------------------
   The logo column is `flex-fill` and would otherwise eat all the slack, leaving
   the search pinned against the account menu. Cap it at its own content width and
   let the search take a sensible share instead. */
header [data-dw-itemtype="swift-v2_logo"]{flex:0 0 auto !important}
header [data-dw-itemtype="swift-v2_logo"] img{display:block;height:auto}

header [data-dw-itemtype="swift-v2_searchfield"]{flex:1 1 420px;max-width:520px;margin-inline:24px}
header [data-dw-itemtype="swift-v2_searchfield"] input[type="search"],
header [data-dw-itemtype="swift-v2_searchfield"] input[type="text"]{
  border:1px solid var(--nsa-line);
  background:var(--nsa-surface);
  border-radius:8px;
  color:var(--nsa-ink);
}
header [data-dw-itemtype="swift-v2_searchfield"] input::placeholder{color:var(--nsa-muted);opacity:1}
header [data-dw-itemtype="swift-v2_searchfield"] input:focus{
  background:#fff;border-color:var(--nsa-accent);box-shadow:0 0 0 3px rgba(44,130,196,.18);outline:0}

/* Account / favourites / cart: brand-blue icons, dark labels. */
header [data-dw-itemtype="swift-v2_myaccount"] .nav-link,
header [data-dw-itemtype="swift-v2_favorites"] .nav-link,
header [data-dw-itemtype="swift-v2_minicart"] .nav-link{color:var(--nsa-ink)}
header [data-dw-itemtype="swift-v2_favorites"] .icon-3,
header [data-dw-itemtype="swift-v2_minicart"] .icon-3{color:var(--nsa-primary)}
/* The cart badge is a FILL carrying a label, so it takes the primary, not the accent:
   white on --nsa-accent measured 4.12:1, white on --nsa-primary is 10.86:1. This is the
   two-token accent rule again — the accent never carries text. */
header .mini-cart-quantity{background:var(--nsa-primary);color:#fff}

/* --- row 2: the category rail -----------------------------------------------
   Measured budget (scripts/c9-overflow.mjs) after the label shortening:
     1440 rail 1180px | 1400 1160 | 1366 1075 | 1200 1000 | 992 953
   one row at every one of them. The rules below spend no extra width: the
   padding they set is the padding Swift already had. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item > .nav-link{
  color:var(--nsa-primary);
  font-weight:600;
  /* .94rem, not .97 — see the caret note below: the carets cost width and the rail is
     spent to within a few px at 992, so the type pays for them. */
  font-size:.94rem;
  letter-spacing:.005em;
  border-bottom:3px solid transparent;
  border-radius:0;
  padding-inline:.375rem !important;   /* was Bootstrap p-2 (.5rem); buys back 28px */
  transition:border-color .12s ease,color .12s ease;
}

/* --- the dropdown caret ------------------------------------------------------
   Swift's mega-menu parents carry data-bs-toggle="dropdown" but NOT the
   .dropdown-toggle class, so Bootstrap never draws its caret and nothing on screen
   distinguishes a category that opens a mega-menu from a plain link. The gate's
   nav-affordance leg failed on exactly this (7x caret-dom + open-caret), and it is a
   real affordance gap, not a probe artefact — so the fix is the caret, not the config.

   Shape is Bootstrap's own: a border-drawn triangle on ::after, display:inline-block and
   position:static (the leg asserts static specifically — an absolutely-positioned caret
   is the pattern that detaches from the label and drifts).

   WIDTH BUDGET (§B5 — re-measure after ANY padding or label change). Seven carets at
   ~9px plus their margins would have pushed the 950px rail at 992 past its ~960px
   container, so the padding and type above pay for them. Measured after this change by
   scripts/c9-overflow.mjs at 1440/1400/1366/1200/992 — one row at every width. */
/* Keyed on data-bs-toggle, not on the parent's .dropdown class: when the panel opens
   Swift mutates the classes on both the item and the link, and a class-keyed rule stopped
   applying at exactly that moment. The attribute is what the affordance leg itself reads
   and it does not change. Border widths are PX, not em — with em they resolved to 0 in
   the open state. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]::after{
  content:"";
  display:inline-block;
  position:static;
  margin-left:5px;
  vertical-align:2px;
  /* !important: some rule in swift.css zeroes this pseudo-element's border widths in the
     hover/open state — measured 5px/4px closed, 0px/0px open, while the transform from my
     own .show rule WAS applied, so the rule matched and only the borders were being
     overridden. The competing declaration was not findable by grep, so the caret asserts
     its own geometry rather than depending on winning a cascade race with a vendor sheet
     that demonstrably re-declares it. */
  border-top:5px solid currentColor !important;
  border-right:4px solid transparent !important;
  border-bottom:0 !important;
  border-left:4px solid transparent !important;
  opacity:.7;
  transition:transform .12s ease,opacity .12s ease;
}
/* When the menu opens Swift adds `.show` to the LINK, and something in swift.css zeroes
   the pseudo-element's borders — measured: borderTopWidth/borderRightWidth go 4px -> 0px
   the moment `.show` lands, so the caret disappears exactly when it should be pointing
   up. Re-declare the borders here rather than only rotating. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]:hover::after,
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"].show::after{
  opacity:1;
  transform:rotate(180deg);
}

/* --- the hover bridge --------------------------------------------------------
   The mega-menu panel opens 16px below the link (measured: link.bottom 148, menu.top
   164), and nothing occupies that band — document.elementFromPoint at the midpoint
   returns the SECTION behind the header, so a pointer travelling from the label to the
   panel leaves the .nav-item and the menu closes under the cursor. A transparent bridge
   anchored on the link (which is made the positioning context for it, and only while
   open, so it never intercepts pointer events on a closed menu) keeps the hit test
   inside the item for the whole journey. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]{
  position:relative;
}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"].show::before,
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]:hover::before{
  content:"";
  position:absolute;
  left:0;right:0;top:100%;
  height:22px;
  background:transparent;
}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item > .nav-link:hover,
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item.show > .nav-link{
  color:var(--nsa-primary-dark);
  border-bottom-color:var(--nsa-accent);
  text-decoration:none;
}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu{
  border-top:3px solid var(--nsa-accent) !important;
  box-shadow:0 18px 30px -22px rgba(0,41,63,.55);
}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu a{color:var(--nsa-body)}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu a:hover{color:var(--nsa-accent-ink)}

/* Below the desktop breakpoint the rail is 953px in a ~960px container — spent to
   the pixel. Let it scroll horizontally INSIDE its own box rather than push the
   document: a scroll container does not extend body.scrollWidth, so this cannot
   reintroduce the A.1 defect. Never `overflow-x:hidden`, which would silently clip
   categories instead of letting a user reach them. */
@media (max-width:1199.98px){
  header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu-wrapper{
    overflow-x:auto;overflow-y:visible;scrollbar-width:thin;
  }
  header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav{flex-wrap:nowrap}
  header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item > .nav-link{white-space:nowrap}
}

/* ---- B6 T3: calm the mega-menu motion (owner: "loses the flipping black bar") ----
   Swift's hover-bridge rides the SAME ::after as this caret:
   `.dropdown:has(.dropdown-menu:where(.mouseover))>[data-bs-toggle=dropdown]::after`
   sets width:100% and height:var(--swift-dynamic-offset). The NSA caret above wins
   display/position on specificity but declares no box, so Swift's leak through and the
   caret inflates to a ~98x16px dark trapezoid that shoves the link 42px -> 67px.
   Pin the caret's own box; keep the blue 3px underline, which is the brand affordance. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]::after{
  width:auto !important; height:auto !important;
}
/* no flip */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"]:hover::after,
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav .nav-link[data-bs-toggle="dropdown"].show::after{
  transform:none !important;
}
/* Swift's .text-decoration-underline-hover adds a 2nd, ink-coloured underline; the blue
   3px border is the brand affordance. */
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item > .nav-link:hover,
header [data-dw-itemtype="swift-v2_menurelatedcontent"] nav > .nav-item.show > .nav-link{
  text-decoration:none !important;
}
/* in-place fade for the panel (display:none->block means a transition can't run) */
@keyframes nsa-menu-in{from{opacity:0}to{opacity:1}}
header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu.show{animation:nsa-menu-in .15s ease-out both}
@media (prefers-reduced-motion:reduce){
  header [data-dw-itemtype="swift-v2_menurelatedcontent"] .megamenu.show{animation:none}
}
/* NSA-HEADER-END 2026-07-31 */

/* NSA-HOME-BEGIN */
/* -----------------------------------------------------------------------------
   NSA HOME — the hero, the category rail, the USP band and the stats band.

   SCOPING (§B4). Everything here is keyed on the home page id via
   `body[data-dw-page-id="8538"] main`, EXCEPT the two component rules that are
   deliberately shop-wide (the poster scrim and the slider card), which are keyed on
   their own data-dw-itemtype. `body[data-dw-itemtype="swift-v2_page"]` is never used
   as "content pages" — it also matches home, cart, customer center, about and contact.
   -------------------------------------------------------------------------- */

.nsa-marker-home{--nsa-block:"home"}

/* --- the hero ---------------------------------------------------------------
   The shipped poster rendered 1440x381 with object-fit:cover over a 1680x945
   master — a 3.78:1 box holding a 1.78:1 image, i.e. two thirds of the picture
   thrown away, and a different two thirds at every viewport width.

   §B6 says size the poster to the image's own aspect ratio so cover == zero crop.
   Taken literally that is `aspect-ratio:1680/945`, which at 1440 wide is an 810px
   band — over the gate's own towering-image-band cap (0.85 * 900 = 765px) and far
   enough down the page that nothing else is above the fold. So the poster is given
   a FIXED 21:9 ratio instead: still deterministic and viewport-independent (the
   defect §B6 is actually about), still declared in one place, but a considered
   cinematic crop rather than an accidental one. object-position frames the dryer
   and the sky rather than letting the browser centre it. */
[data-dw-itemtype="swift-v2_poster"]{position:relative}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] > div{
  aspect-ratio:21/9;
  min-height:340px;
  max-height:640px;
}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] img.object-fit-cover{
  object-position:50% 38%;
}
/* A scrim, not a darker image: the copy sits over sky on the left and steel on the
   right, so legibility cannot rely on the photograph. Painted on the figure so it
   covers the image and sits under the copy block (which is already .z-1). */
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] figure::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(100deg,
    rgba(0,32,52,.90) 0%,
    rgba(0,32,52,.78) 34%,
    rgba(0,32,52,.30) 62%,
    rgba(0,32,52,.10) 100%);
}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] .position-relative.z-1{
  max-width:760px;
}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] h1{
  font-family:var(--nsa-font-display);
  font-size:clamp(2.6rem,5.2vw,4.4rem);
  line-height:1.02;
  letter-spacing:.004em;
  margin-bottom:.5rem;
}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] p{
  font-size:1.06rem;line-height:1.62;max-width:62ch;
}
/* --nsa-accent-ink is the accent's TEXT-ON-LIGHT variant (5.30:1 on white). Over the
   hero scrim it is the wrong half of the two-token pair — measured 2.1:1 and unreadable
   in the shipped frame. On dark the eyebrow takes the light tint instead: 8.7:1 on the
   scrim's #002034. This is the two-role/two-token rule doing its job, not an exception. */
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] .nsa-eyebrow{
  color:#8FC1E3;
}

/* Under 640px a full-bleed 21:9 poster is 274px of picture with the copy on top of
   it — unreadable. Stack: picture band, then the copy on the brand navy (§B6). */
@media (max-width:639.98px){
  body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] > div{
    aspect-ratio:auto;min-height:0;max-height:none;display:block;
  }
  body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] figure{
    position:relative !important;inset:auto !important;aspect-ratio:16/10;display:block;
  }
  body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] figure::after{background:none}
  body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_poster"] .position-relative.z-1{
    padding:1.75rem 1.25rem 2rem;max-width:none;
  }
}

/* --- the category rail -------------------------------------------------------
   The cards carry no image (the seven categories have no artwork of their own), and
   Swift's imageless card renders white text on a white card. Repaint them as sober
   bordered tiles: brand hairline on top, navy heading, readable body, real button. */
/* Swift emits the card's darkening as an INLINE style —
     <div class="card-img-overlay" style="background-image:linear-gradient(to top,black,transparent)">
   on a card that also carries inline `--bs-body-color:white` and `min-height:25rem`.
   Inline wins over any external rule on the same property, so these three need
   !important; nothing else in this sheet does. The gradient exists to make white text
   legible over a card IMAGE — with no image it is just a black smear over black text. */
[data-dw-itemtype="swift-v2_slider"] .card-img-overlay{
  background-image:none !important;
  position:relative !important;
  padding:0 !important;
}
[data-dw-itemtype="swift-v2_slider"] .card{min-height:0 !important}
[data-dw-itemtype="swift-v2_slider"] .card .card-text{margin-top:0 !important}

[data-dw-itemtype="swift-v2_slider"] .card{
  background:#fff !important;
  color:var(--nsa-body) !important;
  border:1px solid var(--nsa-line);
  border-top:4px solid var(--nsa-accent);
  border-radius:6px;
  padding:1.5rem 1.5rem 1.25rem;
  transition:border-color .14s ease,box-shadow .14s ease,transform .14s ease;
}
[data-dw-itemtype="swift-v2_slider"] .card:hover{
  border-color:var(--nsa-accent);
  box-shadow:0 14px 28px -20px rgba(0,64,104,.55);
  transform:translateY(-2px);
}
[data-dw-itemtype="swift-v2_slider"] .card :is(h1,h2,h3,h4,h5,h6){
  color:var(--nsa-primary) !important;margin-bottom:.5rem;
}
[data-dw-itemtype="swift-v2_slider"] .card p{color:var(--nsa-body) !important}
/* Four tiles across instead of two-and-a-bit in a 904px container. */
@media (min-width:992px){
  [data-dw-itemtype="swift-v2_slider"] .slider-container{grid-auto-columns:calc(25% - .75rem)}
}
@media (min-width:576px) and (max-width:991.98px){
  [data-dw-itemtype="swift-v2_slider"] .slider-container{grid-auto-columns:calc(50% - .5rem)}
}
[data-dw-itemtype="swift-v2_slider"] .slider-indicators li{background:var(--nsa-line)}
[data-dw-itemtype="swift-v2_slider"] .slider-indicators li.active{background:var(--nsa-accent)}

/* --- USP band ----------------------------------------------------------------- */
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_feature"] figure svg{
  color:var(--nsa-accent-ink);
}
body[data-dw-page-id="8538"] main [data-dw-itemtype="swift-v2_feature"] h3{
  color:var(--nsa-primary);margin-bottom:.4rem;
}

/* --- stats band --------------------------------------------------------------- */
body[data-dw-page-id="8538"] main .dw-h3{color:var(--nsa-primary)}
/* NSA-HOME-END 2026-07-31 */

/* NSA-PLP-BEGIN */
/* -----------------------------------------------------------------------------
   NSA PLP — the B2B line view. The most-looked-at screen in a supplies demo.

   SCOPING (§B4). Everything is under `body[data-dw-itemtype="swift-v2_shop"] main`,
   which is the shop root AND every PLP AND every PDP — they all render under the shop
   page id. Where a rule must apply to the LIST but not the DETAIL page, the exact
   complement of the PDP discriminator is used:
       main:not(:has([data-dw-itemtype="swift-v2_productcomponentselector"]))
   Both branches are proven by match-count in scripts/c18-scope-audit.mjs before this
   sheet is trusted — a `:not(:has())` selector throws in an `el.matches()` audit and
   the exception is swallowed, so that audit is done with CDP instead.
   -------------------------------------------------------------------------- */

.nsa-marker-plp{--nsa-block:"plp"}

/* --- the dead left rail (punch-list A.2) -------------------------------------
   Measured: #22323 `swift-v2_productlistnavigation` is a g-col-lg-3 column, 340px wide
   and 3473px tall, containing an empty <ul class="navbar-nav"> and one
   `div.border-bottom` — which is the "bare divider line" the punch list saw. It has no
   links because group navigation is switched off on the facets item, and its configured
   start/stop levels (pages 8452/8460) are Customer-Center pages left over from the Swift
   bike demo, so switching it on would render the wrong tree.

   The facets were tried in this rail (Layout=vertical) and it is the wrong home for
   them: `ExpandFacetGroups=all` renders every option of every group expanded inline,
   which pushed the first product ~1,000px down the page. They stay as the compact
   horizontal bar above the list, and the empty column is collapsed so the line items
   get the full 1408px — which is what a B2B line view wants anyway.

   The :not(:has(a)) guard means this rule stops applying the moment the column has real
   links in it, so enabling group navigation later needs no CSS change. */
/* `!important` is needed here and only here: the column's display comes from a
   [data-swift-gridcolumn] utility that already carries !important. */
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productlistnavigation"]:not(:has(a)){
  display:none !important;
}
/* …and the list column takes the width the rail gave up. `1 / -1` rather than
   `span 12` so it does not depend on the grid's column count. */
@media (min-width:992px){
  body[data-dw-itemtype="swift-v2_shop"] main .grid:has(> [data-dw-itemtype="swift-v2_productlistnavigation"]:not(:has(a))) > [class*="g-col-lg-9"]{
    grid-column:1 / -1 !important;
  }
}

/* --- the facet bar ----------------------------------------------------------- */
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productlistfacets"]{
  --nsa-facet-gap:1.25rem;
}
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productlistfacets"] .dropdown-toggle{
  color:var(--nsa-primary);font-weight:600;
}
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productlistfacets"] .dropdown-menu{
  border:1px solid var(--nsa-line);border-top:3px solid var(--nsa-accent);
  box-shadow:0 16px 28px -22px rgba(0,41,63,.5);
}

/* --- the line row ------------------------------------------------------------
   MEASURED SHAPE (scripts/p-probe.mjs against the live PLP): each product is an
       <article class="… d-flex flex-column">
   holding FOUR full-width <section data-dw-itemtype="swift-v2_rowflex"> in a vertical
   stack — image (¶22307), title (¶22308), price (¶22310), add-to-cart (¶22311) — each
   1052px wide. That is why the list read as one-per-row cards in a narrow column with a
   dead right half: it is not a line view at all, it is a stack.

   The fix flips the article to a row at the desktop breakpoint and gives each section a
   width. §B7's rule then applies: every non-title column is fixed and flex-shrink:0, so
   the title is the only shrinkable child and would collapse to width:0 and wrap one word
   per line — hence `flex:0 1 380px; min-width:180px` plus a 2-line clamp on it, and
   `min-width:0` on every flex text child.

   Desktop only. The stacked mobile card is the correct shape at 390 and is left alone. */
@media (min-width:992px){
  body[data-dw-itemtype="swift-v2_shop"] main article.d-flex.flex-column:has([data-dw-itemtype="swift-v2_productdefaultimage"]){
    flex-direction:row !important;
    align-items:center;
    gap:1.25rem;
    padding:.85rem 1rem;
    border-bottom:1px solid var(--nsa-line);
  }
  body[data-dw-itemtype="swift-v2_shop"] main article.d-flex.flex-column:has([data-dw-itemtype="swift-v2_productdefaultimage"]):hover{
    background:var(--nsa-surface);
  }
  /* every section is a column of the line; none of them may impose a min-content floor */
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section,
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section > div,
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section > div > div{
    min-width:0;
  }
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section:has([data-dw-itemtype="swift-v2_productdefaultimage"]){
    flex:0 0 104px;              /* a FIXED track, not a ratio (§B7) */
  }
  /* …and cap its HEIGHT too. The image section renders 136px tall by default, which
     makes a 12-row list ~2,300px of mostly whitespace. A line row wants a thumbnail. */
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) [data-dw-itemtype="swift-v2_productdefaultimage"]{
    max-height:88px;
  }
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) [data-dw-itemtype="swift-v2_productdefaultimage"] :is(img,.ratio,figure){
    max-height:88px;object-fit:contain;
  }
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section:has([data-dw-itemtype="swift-v2_productheader"]){
    flex:1 1 380px;min-width:180px;
  }
  body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productheader"] :is(h2,h3,h4,a,span){
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  }
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section:has([data-dw-itemtype="swift-v2_productprice"]){
    flex:0 0 260px;
  }
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > section:has([data-dw-itemtype="swift-v2_productaddtocart"]){
    flex:0 0 auto;
  }
  /* The stretched-link overlay is 0px tall in a row layout and would swallow clicks on
     the price CTA; it belongs to the stacked card, not the line. */
  body[data-dw-itemtype="swift-v2_shop"] main article:has([data-dw-itemtype="swift-v2_productdefaultimage"]) > a.stretched-link{
    position:static;
  }
  body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productnumber"]{
    flex:0 0 auto;min-width:0;color:var(--nsa-muted);font-variant-numeric:tabular-nums;
  }
}

/* --- product tiles ----------------------------------------------------------- */
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productheader"] a{
  color:var(--nsa-ink);font-weight:600;text-decoration:none;
}
body[data-dw-itemtype="swift-v2_shop"] main [data-dw-itemtype="swift-v2_productheader"] a:hover{
  color:var(--nsa-accent-ink);text-decoration:underline;
}

/* --- the anonymous pricing CTA (added to Swift-v2_ProductPrice.cshtml in T6) --- */
.nsa-price-signin-note{
  display:block;font-size:.86rem;color:var(--nsa-muted);line-height:1.3;
}
.nsa-price-signin .btn{white-space:nowrap}

/* --- the nopic fallback (§B9) -------------------------------------------------
   nopic.png itself is ACL-locked, so the placeholder is branded in CSS. This turns a
   wall of identical grey squares into something deliberate: a faint brand tint and the
   NSA mark, so an un-photographed line reads as "no photo yet", not as a broken image. */
body[data-dw-itemtype="swift-v2_shop"] main .ratio:has(img[src*="nopic"]),
body[data-dw-itemtype="swift-v2_shop"] main figure:has(img[src*="nopic"]){
  background:
    linear-gradient(180deg,var(--nsa-surface) 0%,var(--nsa-surface-2) 100%);
  border:1px solid var(--nsa-line);
  border-radius:4px;
  position:relative;
}
body[data-dw-itemtype="swift-v2_shop"] main .ratio:has(img[src*="nopic"])::after,
body[data-dw-itemtype="swift-v2_shop"] main figure:has(img[src*="nopic"])::after{
  content:"";position:absolute;inset:0;
  background:url("/Files/Images/NSA/nsa-mark.png") center/38% no-repeat;
  opacity:.13;
}
body[data-dw-itemtype="swift-v2_shop"] main img[src*="nopic"]{opacity:0}

/* On the PDP the placeholder inherits the full product-gallery box — 700x520 of tinted
   grey with a huge watermark, which reads as the page's subject rather than as "no photo
   yet". Cap it and centre it. The PDP branch is the exact complement of the list branch
   (both proven by match count in scripts/c18-audit.mjs: PDP discriminator 1 / list 0 on a
   product page, 0 / 1 on a PLP), so this cannot leak onto the list. */
body[data-dw-itemtype="swift-v2_shop"] main:has([data-dw-itemtype="swift-v2_productcomponentselector"])
  .ratio:has(img[src*="nopic"]){
  max-width:380px;
  margin-inline:auto;
}
body[data-dw-itemtype="swift-v2_shop"] main:has([data-dw-itemtype="swift-v2_productcomponentselector"])
  .ratio:has(img[src*="nopic"])::after{
  background-size:34%;
}
/* NSA-PLP-END 2026-07-31 */

/* NSA-LAYOUT-BEGIN */
/* ---- B9 T1: full width is a SETTING, not a stylesheet -------------------------
   B6 T4 widened the boxed rows with a CSS override here, because save_grid_rows does not
   carry containerWidth (#510). B9 replaced that with the native mechanism the owner asked
   for: each grid row's "Content width" property (GridRowViewModel.ContainerWidth = 4,
   "Full width"), written through the admin UI's own request —
       POST /Admin/Api/GridRowSave?Query.Type=GridRowById
       {"QueryData":{"Id":<rowId>},"model":{ …12 members, values as strings… }}
   — on all 59 rows of home / shop / PLP / PDP / about / header / footer AND the three
   component pages (8489/8490/8491) whose rows render as NESTED containers inside them.
   A nested container still honours its own width cap, so leaving those at 3 would have
   re-boxed the catalogue the moment this block was deleted.

   Stock swift.css then does the rest, and this is why no CSS is needed:
       [data-dw-container-width="4"] { --dw-container-width:100% !important;
                                       --dw-container-gutter:0rem }
       :is(header,footer,[data-dw-itemtype=swift-v2_header],[…_footer])
         [data-dw-container-width="4"] { --dw-container-gutter: calc(2rem) }
   i.e. main goes edge to edge and header/footer keep a 2rem gutter, by design.
   Prose is still capped by swift's :where([data-swift-text]){max-inline-size:80ch}.

   DO NOT reintroduce a --dw-container-width override here. If a row reads boxed, its
   Content width property is wrong — fix the row (scripts/b9-t1-apply.mjs). */
.nsa-marker-layout{--nsa-block:"layout"}

/* ---- B9 T1 DEVIATION (measured, deliberate, owner-visible) --------------------
   The brief asked for "no width CSS" and this block honours that: nothing here touches
   --dw-container-width. But stock Swift's width-4 also sets --dw-container-gutter:0rem in
   MAIN (it keeps 2rem only under header/footer), and with the B6 override gone that put
   every content row hard against the viewport edge. Measured on the live host, not
   guessed — scripts/out/b9-measure-t1.json:
     · /en-ca/about @1440 — "Your Trusted Partner in Commerce" and its body copy start at
       x=0; the three feature cards bleed off both edges.
     · every page @390 — breadcrumb, H1, product titles and the "Sign in for pricing"
       button all start at x=0. Before B9 they were inset 16px.
   So the gutter — and ONLY the gutter — is restored, to stock Swift's own value for
   full-width rows (calc(2rem), the literal value swift.css gives header/footer), which
   also makes main, header and footer share one inset for the first time.

   Two exclusions, both load-bearing:
   · :not(:where([data-swift-container] *)) — NESTED containers keep gutter 0. Swift zeroes
     them at (0,2,0); this rule is (0,2,2) and would otherwise double-inset the product
     list app inside the PLP row. :where() contributes 0 specificity, so the exclusion is
     free.
   · the poster/image :has() exclusion — rows 19022 (hero) and 19031 (image band) were
     already width 4 before B9 and are deliberately full-bleed; a poster supplies its own
     --swift-poster-padding. Giving them a gutter would paint a 16px white band around the
     hero, which is a regression, not a fix. */
main [data-swift-container][data-dw-container-width="4"]:not(:where([data-swift-container] *)):not(:has(> [data-swift-gridcolumn][data-dw-itemtype="swift-v2_poster"], > [data-swift-gridcolumn][data-dw-itemtype="swift-v2_image"])) {
  --dw-container-gutter: calc(2rem);
}

/* PDP hero: the stock 6rem bottom pad leaves a dead band between the buy column and
   Overview now that the pack-size selector sits under the price. 96px -> 32px. */
main [data-dw-itemtype="swift-v2_row"]:has([data-dw-itemtype="swift-v2_productmedia"]) {
  padding-bottom: 2rem !important;
}
/* NSA-LAYOUT-END 2026-07-31 */

/* NSA-PDP-TABS-BEGIN */
/* ---- B9 T2: PDP specifications as tabs + panels -------------------------------
   Owner, 2026-08-05: "The PDP needs to not use an accordion for specs but panels and a
   tabs navigation like the marine-demo."

   What ships: the STOCK Swift-v2_ProductFieldDisplayGroups paragraph (¶22303) with its
   Layout item field set to "tabs", which routes its RenderPartial at
   Components/Specifications/Tabs.cshtml — a new sixth layout beside Swift's five. Real
   Bootstrap tabs (role=tablist/tab/tabpanel, data-bs-toggle="tab"), driven by the
   bootstrap.bundle already on every page. No custom item type was needed and none was made.

   Everything here is scoped to `main .nsa-spectabs` (§B4: every unscoped structural rule is
   a sitewide landmine — a B4-era PLP rail rule once hid the first column of every 4Columns
   row site-wide). .nsa-spectabs exists on exactly one paragraph. */
.nsa-marker-pdp-tabs{--nsa-block:"pdp-tabs"}

/* --- the strip -------------------------------------------------------------
   Swift's PDP column is narrow and four ag group names ("What it is", "Safety &
   compliance", "Usage", "Pack & logistics") do not fit on one line at 390. The strip
   therefore SCROLLS rather than wraps — a wrapped tab strip stops reading as tabs, and
   the template already sets flex-nowrap. Scrollbar chrome is hidden because the tabs are
   the affordance; the strip is still keyboard- and swipe-reachable. */
main .nsa-spectabs-nav{
  overflow-x:auto;
  overflow-y:hidden;
  scrollbar-width:none;
  border-bottom:1px solid var(--nsa-line);
  margin-bottom:0;
  flex-wrap:nowrap;
}
main .nsa-spectabs-nav::-webkit-scrollbar{display:none}

main .nsa-spectabs-nav .nav-item{flex:0 0 auto}

main .nsa-spectabs-nav .nav-link{
  border:0;
  border-bottom:3px solid transparent;
  border-radius:0;
  padding:.55rem .9rem;
  font-family:var(--nsa-font);
  font-weight:600;
  font-size:.9375rem;
  line-height:1.3;
  white-space:nowrap;
  color:var(--nsa-body);
  background:none;
}
main .nsa-spectabs-nav .nav-link:hover,
main .nsa-spectabs-nav .nav-link:focus-visible{
  color:var(--nsa-primary);
  border-bottom-color:var(--nsa-line);
}
/* --nsa-accent is the FILL token and fails contrast as text on white (§B2); the strip's
   active label uses --nsa-accent-ink (5.30:1) and only the 3px rule uses the fill. */
main .nsa-spectabs-nav .nav-link.active{
  color:var(--nsa-accent-ink);
  border-bottom-color:var(--nsa-accent);
  background:none;
}

/* --- the panels ------------------------------------------------------------
   One panel is visible at a time (Bootstrap keeps the rest .fade without .show).
   The accordion used to supply the inset via .accordion-body; a tab-pane has none. */
main .nsa-spectabs-content .tab-pane{padding:1rem 0 .25rem}
main .nsa-spectabs-content dt{
  color:var(--nsa-muted);
  font-weight:500;
}
main .nsa-spectabs-content dd{color:var(--nsa-ink)}
/* Rows read as rows, not as a wall — a hairline between pairs, none under the last.
   `border` would reset border-inline-start on anything that carries one (§B13), so only
   the block-end edge is touched. */
main .nsa-spectabs-content dd{
  padding-bottom:.4rem;
  border-block-end:1px solid var(--nsa-line);
}
main .nsa-spectabs-content dt{padding-bottom:.4rem}
main .nsa-spectabs-content dd:last-of-type{border-block-end:0}
/* NSA-PDP-TABS-END 2026-07-31 */
