/* ==========================================================================
   All Points Fasteners — responsive, motion and mobile type
   --------------------------------------------------------------------------
   Consolidated 2026-09-03 from 15 Elementor Custom Code snippets (10629-10648)
   into one stylesheet shipped by the aps-responsive mu-plugin.

   Blocks appear in the EXACT order they occupied in the live document, because
   several rules resolved conflicts by source order rather than specificity.
   Do not reorder without re-verifying.

   Loaded by aps-responsive.php as a <link> printed on wp_head at priority 9999
   -- late enough to sit after every other aps-* mu-plugin's inline <style>
   (aps-loop-groups-css, aps-shop-landing-css, aps-facetwp-css all print in the
   head), which is what several of these rules depend on, while still being in
   the head so there is no flash of desktop layout.
   ========================================================================== */



/* ===== aps-responsive-pass ============================================= */
/* APF responsive pass (Luminos 2026-09-03). Every rule is backed by a
   headless-Chrome measurement at 360/390/768/1024/1025px across 19 URLs.
   Elementor breakpoints are stock: mobile <=767, tablet <=1024.
   Delete this snippet to revert the whole pass. */

/* 1. Media must not exceed its column. /good-stuff/fastener-faq/ ships a raw
   1920x1080 <video> with no width constraint: +1570px overflow at 360, and
   still +1008px at 1025. Hello Elementor constrains img but not video. */
body video, body embed, body object { max-width: 100%; height: auto; }
.page-content iframe, .elementor-widget-container > iframe, .wp-block-embed iframe { max-width: 100%; }

/* 2. Long words in headings -- the biggest source of phantom horizontal
   scroll. A block heading keeps its container width so its rect never exceeds
   the viewport, but its TEXT does, growing documentElement.scrollWidth only.
   /industries/ h1 needed 384px in 320px; PDP h2 "PRODUCT SPECIFICATIONS"
   396px in 360px; .aps-section__title 404px in 360px. */
body h1, body h2, body h3, body h4, body h5, body h6,
body .elementor-heading-title, body .aps-section__title,
body .aps-group__title, body .aps-shop-hero__title {
	overflow-wrap: break-word;
	hyphens: auto;
}
/* but never hyphenate a part number */
body .aps-group__table td:first-child, body .aps-group__table code, body .sku {
	hyphens: none;
	overflow-wrap: normal;
}

/* 3. HappyFox chat iframe: its 250px frame sat past the right edge, adding
   +64px/+35px of document scroll at 360/390 on category pages. */
#hfc-frame { max-width: 100vw !important; }

/* 4. /contact-us/: inner container is 748px but its content measures 835px --
   two 374px (50%) columns plus the gap. +77px at 768, +13px at 1024. */
@media (max-width: 1024px) {
	.elementor-element-7d3eeb3 > .e-con-inner { flex-wrap: wrap; }
	.elementor-element-7d3eeb3 > .e-con-inner > .e-con { flex-basis: 100%; max-width: 100%; }
}

/* 5. Contact fields compute to 15px; iOS Safari zooms the page on focus below
   16px and does not zoom back out. */
@media (max-width: 767px) {
	body input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
	body select, body textarea { font-size: 16px; }
}

/* 6. Tap targets. Measured: hamburger 33x33; all 19 footer links 26px tall;
   category-nav rows 31-32px; breadcrumbs 20px. Touch widths only. */
.elementor-menu-toggle {
	min-width: 44px; min-height: 44px;
	display: flex; align-items: center; justify-content: center;
}
@media (max-width: 1024px) {
	footer .elementor-icon-list-item > a,
	header .elementor-icon-list-item > a { min-height: 44px; display: flex; align-items: center; }
	.aps-cat-item .aps-cat-link { min-height: 44px; padding-block: .55rem; }
	/* `body` prefix added 2026-09-08: without it this rule lost the cascade to
	   Elementor's breadcrumb widget CSS and never applied. Re-measured on the
	   PDP it was still display:inline at 20px, at both 390 and 768. */
	body .woocommerce-breadcrumb a { display: inline-block; padding-block: .5rem; }
	.elementor-social-icon { width: 44px; height: 44px; }
	/* Deliberate exception: there can be 1,100+ View buttons on one archive, so
	   44px would add ~18,000px of page height. 40px clears the 24px WCAG 2.5.8
	   floor and improves on the measured 28px without wrecking density. */
	.aps-loop-groups .aps-group__view {
		min-height: 40px; display: inline-flex; align-items: center; padding: .4rem .75rem;
	}
}

/* 6b. Tap targets, second pass (pre-launch audit 2026-09-08). Section 6 covered
   the chrome; these are content-level targets it did not reach, re-measured in
   a real browser at 390 and 768.

   DELIBERATELY NOT TOUCHED: links inside prose paragraphs — the fastener-guide
   body copy (21 of them), .aps-shop-longform__body, .aps-cat-prose and the FAQ
   answers, all 19-23px. WCAG 2.5.8 exempts a target "in a sentence or whose
   size is otherwise constrained by the line-height of non-target text", so
   these already conform. Padding them would spill each link's hit box into the
   lines above and below, creating overlapping targets in running text and
   buying nothing against the criterion. Leave them at 19px on purpose. */
@media (max-width: 1024px) {
	/* Fastener-guide table of contents: 20 links at 19px, one per line. A list
	   of navigation targets rather than prose, so the inline exception does not
	   apply to these. */
	body .page-content > ul > li > a { display: inline-block; padding-block: .3rem; }

	/* Category chips on the PDP are linked h4 headings, measured 22px. */
	body h4.elementor-heading-title > a { display: inline-block; padding-block: .25rem; }

	/* Footer help panel, phone and email, 20px, one per row. */
	body .aps-help__contact > li > a { display: inline-block; padding-block: .4rem; }

	/* Standalone links that are the whole content of their own block rather
	   than words inside a sentence, so the 2.5.8 inline exemption does not
	   reach them: the "Request a Quote!" CTA sitting alone in a <div> in the
	   longform body (23px), and the "Read the full shipping/return policy"
	   links, each the sole child of its own <p> (19px). */
	body .aps-shop-longform__body > div > a,
	body .aps-faq__more > a,
	body .aps-faq__a > a { display: inline-block; padding-block: .35rem; }
}

/* 7. Text below 12px on phones: FacetWP labels 11.2px, facet heads and counts
   11.84px, hero eyebrows 11.5px. */
@media (max-width: 767px) {
	.facetwp-checkbox, .facetwp-facet .facetwp-display-value,
	.aps-facets-head, .aps-facets-reset, .aps-cat-count,
	.aps-group__pill, .aps-hero__eyebrow, .aps-policy-hero__eyebrow { font-size: 12px; }
}

/* 8. Facet labels were clipped, not wrapped: at 768 the drive_type facet hid
   68px of its own options behind the accordion's overflow:hidden. */
.facetwp-checkbox, .aps-facet-body-inner .facetwp-facet label {
	white-space: normal; overflow-wrap: break-word;
}

/* 9. Spec tables scroll but gave no sign of it -- a 7-column 578px table in a
   348px viewport reads as truncated. Edge fade shows only while scrollable. */
.aps-loop-groups .aps-group__table-wrap {
	background:
		linear-gradient(to right, #fff 30%, rgba(255,255,255,0)) left center / 40px 100% no-repeat,
		linear-gradient(to left, #fff 30%, rgba(255,255,255,0)) right center / 40px 100% no-repeat,
		radial-gradient(farthest-side at 0 50%, rgba(16,41,76,.16), transparent) left center / 14px 100% no-repeat,
		radial-gradient(farthest-side at 100% 50%, rgba(16,41,76,.16), transparent) right center / 14px 100% no-repeat;
	background-attachment: local, local, scroll, scroll;
	overscroll-behavior-x: contain;
}

/* 10. Elementor container wrapping a single <script>: paints nothing but holds
   40px at the bottom of every page. The inline script already ran at parse. */
#aps-quote-noreload { display: none; }

/* 11. Mobile header measured 266px vs 197px on desktop -- taller on the small
   screen, eating 34% of a 780px viewport. Top utility bar is 88px of it. */
@media (max-width: 767px) {
	.elementor-element-dff424e { --padding-block-start: 6px; --padding-block-end: 6px; }
	.elementor-element-dff424e .elementor-icon-list-item { margin-block: 0; }
}


/* ===== aps-responsive-pass-2 =========================================== */
/* APF responsive pass, part 2. Corrections to snippet "APS Responsive Pass"
   after re-measuring: that pass took total document overflow from 6,876px to
   192px across 95 runs; these five rules address what was left, plus two
   breakpoints that were set too narrow. Keep both snippets together. */

/* 1. HappyFox chat frame. Part 1 used max-width:100vw, which was the wrong
   lever -- the frame is only 250px wide, so max-width never binds. It is
   position:fixed with left:174px, putting its right edge at 424px in a 360px
   viewport: exactly the +64px (and +35px at 390) of remaining scroll.
   Anchor it to the right edge instead of a computed left offset. */
@media (max-width: 1024px) {
	#hfc-frame {
		left: auto !important;
		right: 0 !important;
	}
}

/* 2. /contact-us/ column blowout.
   Part 1 set flex-wrap on .e-con-inner, but Elementor's own
   `.e-con > .e-con-inner` rule has the same specificity (0,2,0) and loads
   later, so it won and the computed value stayed `nowrap`.
   The underlying cause is not wrapping anyway: flex items default to
   min-width:auto, so each column refuses to shrink below its min-content
   width and the pair demands 824px inside a 748px row. Letting them shrink
   fixes it while keeping the two-column design intact.
   The extra class raises specificity to (0,3,0) so cascade order stops
   mattering. */
.elementor-6648 .elementor-element-7d3eeb3 > .e-con-inner > .e-con,
.elementor-6648 .elementor-element-7d3eeb3 > .e-con-inner > .e-con > .e-con-inner {
	min-width: 0;
}
.elementor-6648 .elementor-element-7d3eeb3 .elementor-widget,
.elementor-6648 .elementor-element-7d3eeb3 .elementor-widget-container {
	min-width: 0;
	max-width: 100%;
}

/* 3. Buttons wider than the slot they sit in.
   PDP "DOWNLOAD SPECS" measures 195px inside a 163px parent (20px Oswald plus
   25px side padding puts its min-content width above the column), pushing the
   document 12px wide at 768. Constraining the button to its parent and letting
   the label wrap fixes it without shrinking the type. */
.elementor-button,
.elementor-button-link {
	max-width: 100%;
	overflow-wrap: break-word;
}

/* 4. Form fields: iPad zooms on focus too.
   Part 1 stopped at 767px, so the six Elementor form fields on /contact-us/
   were still 15px at 768 and 1024 -- iPad Safari zooms the page in on focus
   below 16px exactly as iPhone does. */
@media (max-width: 1024px) {
	body input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
	body select,
	body textarea {
		font-size: 16px;
	}
}

/* 5. Sub-12px text on tablet, same reason: part 1 stopped at 767px, so the
   FacetWP labels (11.2px) and facet headings/counts (11.84px) stayed tiny at
   768 and 1024, which is where the faceted sidebar is most used. */
@media (max-width: 1024px) {
	.facetwp-checkbox,
	.facetwp-facet .facetwp-display-value,
	.aps-facets-head,
	.aps-facets-reset,
	.aps-cat-count,
	.aps-group__pill,
	.aps-hero__eyebrow,
	.aps-policy-hero__eyebrow {
		font-size: 12px;
	}
}


/* ===== aps-responsive-pass-3 =========================================== */
/* APF responsive pass, part 3 -- the last two overflow sources, both caused by
   the same misunderstanding worth recording:

   `overflow-wrap: break-word` does NOT reduce an element's min-content width.
   It only breaks a word once the box is already sized. So it cannot help when
   the BOX is the thing sizing itself to the unbreakable word. Part 1 set
   break-word on headings and the overflow did not move. Two different fixes
   are needed depending on why the box grew. */

/* 1. Category section titles are display:inline-block, so they shrink-wrap to
   their content and no amount of overflow-wrap will constrain them --
   max-width is what binds an inline-block. Measured on
   /product-category/screws/ @360: a 404px heading inside a 320px column
   (+64px of document scroll), and a second at 373px (+33px).

   NOTE FOR CONTENT: 2 of that page's 139 section headings are raw slugs with
   no spaces -- "Waferheadselfdrillingscrews" and "Panheadselfdrillingscrews".
   This rule stops them breaking the layout, but they still read as run-on
   uppercase. The real fix is the chart_groups h4 values behind them. */
.aps-section__title,
.aps-group__title {
	max-width: 100%;
	overflow-wrap: break-word;
}

/* 2. /contact-us/ -- an unbreakable 28-character email address
   (sales@allpointsfasteners.com) inside a 106px icon-box description. It set a
   min-content floor of 243px on a 106px box, pushing the right-hand column to
   demand 471px of a 748px row: the +76px that survived parts 1 and 2. This is
   why min-width:0 on the columns did nothing -- the columns could shrink, the
   TEXT could not.

   `anywhere` rather than `break-word` specifically because only `anywhere`
   feeds back into intrinsic min-content sizing. Scoped to icon-box
   descriptions (where the contact details live) so it cannot break words in
   the footer navigation. */
.elementor-icon-box-description {
	overflow-wrap: anywhere;
}


/* ===== aps-motion-tokens =============================================== */
/* ==========================================================================
   All Points Fasteners — motion pass (Luminos, 2026-09-03)
   --------------------------------------------------------------------------
   The site had three unrelated motion systems and no shared vocabulary:

     1. apf-loader.php    first-visit intro overlay, 1.45s, localStorage-gated
     2. aps-gsap-reveals  GSAP + ScrollTrigger scroll reveals, but ONLY on
                          shop / product_cat / product_tag / pages containing
                          [aps_loop_groups]. Home, About, Contact, Industries,
                          Blog, the policy pages and the Good Stuff guides had
                          no entrance motion at all.
     3. per-mu-plugin CSS transitions written ad hoc: durations of .12s, .15s,
        .18s, .2s, .3s and .32s, with easings `ease`, cubic-bezier(.4,0,.2,1)
        and cubic-bezier(.22,1,.36,1) mixed across files.

   This unifies the vocabulary and extends the reveal to the pages that had
   none. The easing matches GSAP's power3.out (easeOutQuart) so the CSS here
   and the GSAP already running on category pages read as one system.
   ========================================================================== */

:root {
	/* power3.out == easeOutQuart; keeps CSS identical to the GSAP grammar */
	--aps-ease: cubic-bezier(.165, .84, .44, 1);
	--aps-ease-inout: cubic-bezier(.4, 0, .2, 1);
	--aps-dur-fast: 180ms;
	--aps-dur: 320ms;
	--aps-dur-slow: 900ms;
	--aps-rise: 24px;
}

/* Interactive states share one duration and one curve. Only properties that
   were ALREADY being transitioned are listed -- this retimes existing motion
   rather than introducing new motion. */
.aps-cat-card > a,
.aps-cat-link,
.aps-cat-dot,
.aps-facet-title,
.aps-facets-reset,
.aps-group__view,
.elementor-social-icon,
.elementor-button,
.elementor-icon-list-item > a {
	transition-duration: var(--aps-dur);
	transition-timing-function: var(--aps-ease);
}

.aps-facet-chevron {
	transition: transform var(--aps-dur) var(--aps-ease-inout),
	            border-color var(--aps-dur-fast) var(--aps-ease);
}

.aps-facet-body {
	transition: grid-template-rows var(--aps-dur) var(--aps-ease-inout);
}

/* Elementor's "float" hover (Home, Contact Us, Industries, Free Samples and
   the product template) is a -8px translate on hover carrying its own .3s
   ease-out. Keep the movement, retime it onto the shared curve. */
.elementor-animation-float {
	transition-duration: var(--aps-dur);
	transition-timing-function: var(--aps-ease);
}

/* Scroll reveal for the pages GSAP does not cover. The hidden state is applied
   by JavaScript (companion snippet), NEVER by this stylesheet -- so if the
   script fails, or IntersectionObserver is missing, nothing is left invisible.
   Only opacity and transform animate, so this cannot cause layout shift. */
.aps-reveal {
	opacity: 0;
	transform: translate3d(0, var(--aps-rise), 0);
	will-change: opacity, transform;
}

.aps-reveal-in {
	opacity: 1;
	transform: none;
	transition: opacity var(--aps-dur-slow) var(--aps-ease),
	            transform var(--aps-dur-slow) var(--aps-ease);
	will-change: auto;
}

/* One global reduced-motion guard. The intro loader and the GSAP reveals
   already honoured this; the ad-hoc CSS transitions and Elementor's hover
   animations did not. */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: .001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .001ms !important;
		scroll-behavior: auto !important;
	}

	.aps-reveal,
	.aps-reveal-in {
		opacity: 1 !important;
		transform: none !important;
	}
}


/* ===== aps-responsive-pass-4 =========================================== */
/* APF responsive pass, part 4. Fixes one regression this pass introduced and
   picks up the controls parts 1-3 missed. Re-measured after every change. */

/* --------------------------------------------------------------------------
   1. REGRESSION FIX (introduced by part 1).
   Part 1 gave every header AND footer icon-list link a 44px min-height. In the
   footer that is right -- those are navigation. In the header they are the top
   utility bar's tagline ("Trusted Fastener Supplier Since 1986") and phone
   line, which are not primary tap targets. The rule pushed that bar from 88px
   to 114px, taking the whole mobile header from 266px to 292px -- more of the
   first screen lost to chrome on every single page, which is the opposite of
   what this pass is for.

   Release the header bar; keep the phone number comfortably tappable, since
   that one genuinely is a tap target on a phone.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	header .elementor-icon-list-item > a {
		min-height: 0;
		display: flex;
		align-items: center;
	}
	header .elementor-icon-list-item > a[href^="tel:"] {
		min-height: 40px;
	}
}

/* --------------------------------------------------------------------------
   2. Controls parts 1-3 did not reach.
   Measured after part 3, at 360/390: header Quote and Cart buttons 30px,
   facet accordion toggles 38px, "View all products" 20px, "Reset filters"
   19px. All are real, frequently used controls.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.aps-ha__btn,
	.aps-facet-toggle,
	.aps-cat-all,
	.aps-facets-reset {
		min-height: 44px;
		display: inline-flex;
		align-items: center;
	}
	.aps-facet-toggle {
		display: flex;
	}
}

/* --------------------------------------------------------------------------
   3. Social icons stayed at 40px.
   Part 1 set width/height on .elementor-social-icon, but Elementor sizes these
   from --icon-size / font-size and padding, so the declaration did not bind.
   Drive the variable it actually reads.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.elementor-social-icons-wrapper .elementor-social-icon {
		width: 44px;
		height: 44px;
		--icon-size: 44px;
	}
}

/* --------------------------------------------------------------------------
   4. Sub-12px text that survived part 1.
   .aps-group__pill and the FacetWP counters were still 11.2-11.84px on mobile.
   The pill rule lost on specificity -- aps-loop-groups.php ships
   `.aps-loop-groups .aps-group__pill` at (0,2,0) against part 1's (0,1,0) --
   and .facetwp-counter was simply never listed.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.aps-loop-groups .aps-group__pill,
	.facetwp-facet .facetwp-counter,
	.facetwp-counter {
		font-size: 12px;
	}
}


/* ===== aps-mobile-hero-height ========================================== */
/* APF — page heroes reserve desktop height on a phone.

   Measured identical at 390 and 1440:

     /contact-us/   first section --min-height: 400px (fixed), content ~150px
     /industries/   first section --min-height: 45vh   -> 405px, content ~192px

   With the header at 183px that is ~583px of an 844px phone screen gone before
   a single line of real content. The hero is not "big by design" at that size,
   it is reserving a desktop measurement.

   min-height is only a FLOOR, so lowering it cannot clip anything -- a hero
   whose content is taller simply stays taller. 220px is kept rather than 0 so
   that a hero carrying only a background image (no text to give it height)
   does not collapse to nothing.

   Scoped to the FIRST section of page content, which is the hero on Home,
   About, Industries and Contact. Later sections are left alone.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
	body .page-content > .elementor > .e-con:first-child {
		--min-height: 220px;
		min-height: 220px;
	}
}


/* ===== aps-mobile-archive-type ========================================= */
/* APF — the type + width pass missed the blog archive and single-post templates.

   Two scoping mistakes, both found on /blog/ by looking at a screenshot:

   1. The type rules matched `.elementor-widget-text-editor p`. The blog intro
      renders its text DIRECTLY in the widget div, with no <p> wrapper, so it
      kept 26px/39px italic -- eight lines of oversized italic on a phone.
      Target the widget itself, not only its paragraph.

   2. Everything was scoped to `.page-content` (and `.elementor-location-single`
      for a couple of rules). The blog archive lives in
      `.elementor-location-archive`, so the widget-width reset and the icon-list
      size never applied there: that intro was also still 224px wide inside a
      370px column, and the eyebrow still 20px.

   This brings archive and single templates up to the same scale as page
   content. The three scopes are listed explicitly rather than dropped, so a
   rule can never silently reach the header or footer.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {

	/* Size the text-editor WIDGET, covering text with no <p> wrapper. */
	body .page-content .elementor-widget-text-editor,
	body .elementor-location-single .elementor-widget-text-editor,
	body .elementor-location-archive .elementor-widget-text-editor {
		font-size: clamp(15.5px, 4.2vw, 17px) !important;
		line-height: 1.62 !important;
	}

	/* Desktop percentage widths, in the archive template too. */
	body .elementor-location-archive .elementor-widget.elementor-widget-text-editor,
	body .elementor-location-archive .elementor-widget.elementor-widget-heading,
	body .elementor-location-archive .elementor-widget.elementor-widget-icon-list,
	body .elementor-location-single .elementor-widget.elementor-widget-icon-list {
		--container-widget-width: 100%;
		--container-widget-flex-grow: 1;
		width: 100%;
		max-width: 100%;
	}

	/* Icon-list text, in the archive and single templates too. */
	body .elementor-location-archive .elementor-widget-icon-list .elementor-icon-list-text,
	body .elementor-location-single .elementor-widget-icon-list .elementor-icon-list-text {
		font-size: clamp(15px, 3.9vw, 17px) !important;
		line-height: 1.45 !important;
	}
}


/* ===== aps-mobile-buttons ============================================== */
/* APF — button scale on mobile.

   Measured at 390px before this rule -> after:

     PDP  "DOWNLOAD FULL SPECIFICATIONS"  390x84 (2 lines) 20px -> 367x49 one line
     Home "Browse Products"               300x66           25px -> 197x49
     Home "Get A Free Quote"              259x66           25px -> 166x49
     Home "VIEW ALL PRODUCTS"             291x60           20px -> 225x49
     Contact "CALL 800-483-6354"          272x60           20px -> 210x49
     Header "Contact Us"                  166x57           18px -> 136x49

   A 25px label in a 66px-tall button on a 390px screen is a desktop CTA.

   🔴 The type snippet already carried a button rule and it never applied --
   `body .elementor-widget-button .elementor-button` is (0,2,1) and Elementor
   page-scopes to `.elementor-87 .elementor-element.elementor-element-X
   .elementor-button` at (0,4,0). Using !important here rather than growing the
   selector further: there are dozens of per-element rules and matching each
   one is not maintainable.

   ⚠ TWO DELIBERATE EXCLUSIONS -- a clamp with a 14px floor would INFLATE
   buttons that are already correctly small:
     .aps-group__view       12px, and there are up to 1,179 on one archive;
                            enlarging them would add thousands of px of height.
     .wp-block-button__link the cart's "Read more" at 97x38 / 12px is fine.

   ⚠ v1 of this snippet also carried a header-specific rule at
   `[data-elementor-type="header"] .elementor-button` (0,2,0), intending a 13px
   header CTA. It never applied, because `:not(.aps-group__view)` below pushes
   the generic rule to (0,2,1) -- my own rule outranked my own rule. Removed
   rather than "fixed": the header CTA at 136x49 matches the content CTAs, and
   forcing it smaller would save 5px of a 183px header. A rule that claims to
   do something it does not is worse than no rule.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
	body .elementor-button:not(.aps-group__view),
	body .elementor-cta__button,
	body .add-to-quote-button,
	body .single_add_to_cart_button {
		font-size: clamp(14px, 3.9vw, 16px) !important;
		line-height: 1.25 !important;
		padding: 12px 18px !important;
		letter-spacing: normal !important;
	}

	/* Full-bleed CTAs stay full-bleed, they were only ever too TALL. */
	body .elementor-widget-button .elementor-button {
		min-height: 44px;
	}
}


/* ===== aps-mobile-spec-tables ========================================== */
/* APF — spec tables on mobile.

   Measured at 390px before writing anything, which changed the plan:

     /product-category/screws/   98 tables, 1,179 rows, 6,903 cells,
                                 page ALREADY 112,255px tall (~133 screens)
     column counts vary per table: {3:2, 4:2, 5:60, 6:17, 7:16, 8:1}
     table width 578px inside a 348px wrapper (7-col), but only 360px on the
     electrical page -- i.e. 12px over, not 230px
     row height 62px

   🔴 STACKED CARDS WERE THE WRONG ANSWER and were NOT built. The standard
   responsive-table pattern (one card per row, label: value pairs) costs roughly
   180px per row here. On 1,179 rows that turns a 112,000px page into about
   212,000px. Cards suit a 10-20 row table; this is a dense catalogue.

   What actually helps a dense table on a phone:

   1. STICKY PART-NUMBER COLUMN. The part number is the row's identity. While
      scrolling a 578px table sideways in a 348px window it used to scroll away,
      so you were reading "8,000" with no idea which screw it belonged to.
      Pinning column 1 keeps the identity on screen. Costs zero page height.

   2. TIGHTER ROWS. Cell padding drops from .85rem/1rem to .5rem/.55rem,
      taking rows from 62px to ~56px. Small per row -- about 7,000px off that
      112,000px page.

   ⚠ The "View" control stays at its 40px minimum. Shrinking it to 34px would
   save a further ~7,000px on that page, but 40px is already a compromise below
   the 44px guideline and buying page height with tap-target size is the wrong
   trade on the primary action.

   ⚠ Sticky cells must be opaque or the scrolling columns show through them,
   and the header cell needs a higher z-index than the body cells so it wins
   at the crossing point.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {

	.aps-loop-groups .aps-group__table th:first-child,
	.aps-loop-groups .aps-group__table td:first-child {
		position: sticky;
		left: 0;
		z-index: 1;
		background: #fff;
		box-shadow: 1px 0 0 rgba(16, 41, 76, .10);
	}

	.aps-loop-groups .aps-group__table thead th:first-child {
		background: #f0f2f4;
		z-index: 2;
	}

	/* Row hover repaints the row; keep the pinned cell opaque to match. */
	.aps-loop-groups .aps-group__table tbody tr:hover td:first-child {
		background: #f6f7f9;
	}

	/* Tighter rows: 62px -> ~56px, across up to 1,179 of them. */
	.aps-loop-groups .aps-group__table th,
	.aps-loop-groups .aps-group__table td {
		padding: .5rem .55rem;
	}
}


/* ===== aps-responsive-pass-5 =========================================== */
/* APF responsive pass, part 5 -- and the reason it exists is worth recording.

   Parts 1-4 are injected into <head> by Elementor Custom Code. Several mu-plugins
   print their own inline <style> LATER in the head (verified in the served
   markup: aps-shop-landing-css and aps-facetwp-css both appear after
   aps-responsive-pass-4). At equal specificity the later rule wins, so three
   fixes silently did nothing no matter how they were written.

   This block is emitted at body_end, after every stylesheet in the document,
   so ordering can no longer defeat it. Anything that keeps losing the cascade
   belongs here rather than in parts 1-4. */

/* --------------------------------------------------------------------------
   1. Top utility bar height.
   Making the Quote/Cart buttons and the phone link properly tappable (part 4)
   grew this bar from 88px to 114px, taking the mobile header to 292px against
   a 266px baseline. Those three ARE the header's primary actions, so the tap
   targets stay; the bar's own 15px block padding is what gives the height back.
   Recovers ~14px. Both the Elementor container variables and the resolved
   property are set, because the container reads the variable.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
	.elementor-element-ba49463 {
		--padding-block-start: 8px;
		--padding-block-end: 8px;
		padding-block-start: 8px;
		padding-block-end: 8px;
	}
}

/* --------------------------------------------------------------------------
   2. Social icons stayed 40px through parts 1 and 4.
   Elementor derives the box from --icon-size plus padding, and its own rule
   prints later, so width/height never bound.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.elementor-social-icons-wrapper .elementor-social-icon {
		--icon-size: 24px;
		width: 44px !important;
		height: 44px !important;
	}
}

/* --------------------------------------------------------------------------
   3. Sub-12px text that outranked parts 1 and 4.
   .aps-group__pill is set by aps-loop-groups.php at .7rem and .facetwp-counter
   by aps-facetwp-styles.php -- both print after the head snippets. Measured
   173 pills and 99 counters still under 12px on /product-category/screws/ @390.
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.aps-loop-groups .aps-group__pill,
	.facetwp-facet .facetwp-counter,
	.facetwp-counter,
	.aps-cat-item .aps-cat-count {
		font-size: 12px;
	}
}


/* ===== aps-mobile-header-2 ============================================= */
/* APF — mobile header, single row. Supersedes "APS Mobile Header" (10636),
   which was written at too low a specificity to bind.

   aps-header-actions.php ships:
       [data-elementor-type="header"] .elementor-element-06880cf{width:auto !important}
       [data-elementor-type="header"] .elementor-element-06880cf{min-width:166px}
   That is (0,2,0). Snippet 10636 used a bare .elementor-element-06880cf --
   (0,1,0) -- so it lost on specificity no matter how late it was emitted, and
   the logo column stayed pinned at a 166px minimum. 166 + 284 = 450 in a 390px
   row, so the nav column kept wrapping to a second line. At 430 the two
   happened to fit and it looked like the rule was working; it never was.

   🔴 That 166px min-width is itself a deliberate earlier fix -- without it the
   logo column tracks the viewport and the logo goes INVISIBLE on tablet. So it
   is lowered here to a value that still guarantees the mark renders, and only
   under 767px. The 768-1024 tablet range keeps the original 166px untouched.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {

	[data-elementor-type="header"] .elementor-element-5aff331 > .e-con-inner {
		flex-wrap: nowrap;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: 6px;
	}

	/* Logo column: floor it at the logo's own width, not 166px. */
	[data-elementor-type="header"] .elementor-element-06880cf {
		min-width: 96px;
		width: auto !important;
		flex: 0 0 auto;
		padding: 6px 0 6px 10px;
		margin-block: 0;
	}
	[data-elementor-type="header"] .elementor-element-06880cf img {
		max-width: 96px;
		height: auto;
	}

	/* Controls column: take what is left, stay right-aligned, allow shrink. */
	[data-elementor-type="header"] .elementor-element-f5e9a9b {
		min-width: 0;
		width: auto !important;
		flex: 1 1 auto;
		justify-content: flex-end;
		align-items: center;
		flex-wrap: nowrap;
		gap: 6px;
		padding: 6px 10px 6px 0;
	}
	[data-elementor-type="header"] .elementor-element-f5e9a9b > .elementor-element {
		margin: 0;
		min-width: 0;
	}

	/* "Contact Us" was 167px -- too wide to share a 360px line. */
	[data-elementor-type="header"] .elementor-element-f1742f5 .elementor-button {
		padding: 11px 12px;
		font-size: 13px;
		line-height: 1.15;
		white-space: nowrap;
	}

	/* Circular phone button: keep it a 44px tap target, drop the extra bulk. */
	[data-elementor-type="header"] .elementor-element-7eda541 .elementor-icon {
		font-size: 16px;
		padding: 11px;
	}
}


/* ===== aps-mobile-type ================================================= */
/* ==========================================================================
   APF — mobile type + spacing scale
   --------------------------------------------------------------------------
   Measured at 390 vs 1440 across Home, About, Industries and a category page:
   EVERY text element rendered at an identical size on both. The Elementor kit
   has no responsive typography configured at all, so the desktop scale is
   served verbatim to phones:

     hero h1   79px / 79px lh   ->  7 characters per line, 3 lines
     h2        60px             -> 10 cpl
     h2        50px / 40px / 32px
     body p    26px / 39px lh   -> 13 cpl, 6 lines for one paragraph
     body p    21px
     gutters   10px inline on every section

   Headings also carry line-height 1.0, which is fine for a one-line desktop
   headline and cramped for a three-line mobile one.

   This is a fluid scale, not fixed sizes: clamp() interpolates with viewport
   width so 360 and 767 both land sensibly instead of stepping at a breakpoint.

   ⚠ !important is unavoidable here -- Elementor writes per-element sizes at
   `.elementor-element-XXX .elementor-heading-title` (0,2,0), and there are too
   many elements to enumerate. The trade-off is that headings which were
   ALREADY small get nudged up slightly (an 18px h2 becomes ~21px). That is
   accepted deliberately: on a 390px screen six distinct h2 sizes carry no
   hierarchy anyway, and h1 > h2 > h3 > h4 > body is preserved.

   Scoped to .elementor-heading-title and page content so the aps-* components
   (spec tables, facets, policy pages), which were tuned separately, are left
   alone.
   ========================================================================== */

@media (max-width: 767px) {

	/* ---- headings ------------------------------------------------------ */
	body h1.elementor-heading-title,
	body .page-content h1:not([class*="aps-"]) {
		font-size: clamp(29px, 8.6vw, 42px) !important;
		line-height: 1.08 !important;
	}

	body h2.elementor-heading-title,
	body .page-content h2:not([class*="aps-"]) {
		font-size: clamp(21px, 5.6vw, 30px) !important;
		line-height: 1.16 !important;
	}

	body h3.elementor-heading-title,
	body .page-content h3:not([class*="aps-"]) {
		font-size: clamp(18px, 4.6vw, 24px) !important;
		line-height: 1.22 !important;
	}

	body h4.elementor-heading-title,
	body .page-content h4:not([class*="aps-"]) {
		font-size: clamp(16px, 4vw, 20px) !important;
		line-height: 1.28 !important;
	}

	/* ---- body copy ------------------------------------------------------
	   26px and 21px paragraphs are the "too big" complaint. 16-17px at 1.62
	   puts a 360-390px column at roughly 45-55 characters per line, which is
	   the comfortable range; it was running at 13. */
	body .elementor-widget-text-editor p,
	body .elementor-widget-text-editor li,
	body .page-content .elementor-widget-text-editor,
	body .page-content > .elementor p:not([class*="aps-"]):not(.elementor-heading-title) {
		font-size: clamp(15.5px, 4.2vw, 17px) !important;
		line-height: 1.62 !important;
	}

	/* ---- gutters --------------------------------------------------------
	   Sections carried 10px inline padding, so copy ran almost to the glass.
	   18px reads as a deliberate margin without meaningfully narrowing the
	   column now that the type is smaller. */
	body .page-content > .elementor > .e-con {
		--padding-inline-start: 18px;
		--padding-inline-end: 18px;
		padding-inline: 18px;
	}

	/* Buttons inherited the desktop scale too. */
	body .elementor-widget-button .elementor-button {
		font-size: clamp(14px, 3.8vw, 16px);
		line-height: 1.2;
	}
}


/* ===== aps-mobile-widget-widths ======================================== */
/* APF — text widgets keep their desktop percentage width on mobile.

   The hero paragraph on Home rendered 214px wide inside a 334px column at
   390px -- ~23 characters per line, wrapping every four words. Shrinking the
   type could not fix it; the column itself was the constraint:

       width: var(--container-widget-width, 64%);
       max-width: 64%;
       --container-widget-width: 64%;

   64% is a desktop layout decision and Elementor emits no mobile override.

   🔴 SPECIFICITY TRAP -- this cost two attempts. The served page shows the
   rule as a bare `.elementor-element-59ca37e{...}`, which reads as (0,1,0).
   The actual stylesheet selector is PAGE-SCOPED:

       .elementor-87 .elementor-element.elementor-element-59ca37e   -> (0,3,0)

   Confirmed with CDP CSS.getMatchedStylesForNode rather than by reading the
   CSS. An override at `body .page-content .elementor-widget-text-editor`
   (0,2,1) loses to it -- which is why max-width appeared to take (that value
   came from Elementor's own .elementor-widget__width-initial) while width and
   the custom property did not. Doubling the widget class takes this to
   (0,4,1), which wins without needing !important.

   Images and buttons are deliberately excluded: a half-width image or a
   shrink-to-fit button is often a real design choice.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
	body .page-content .elementor-widget.elementor-widget-text-editor,
	body .page-content .elementor-widget.elementor-widget-heading,
	body .page-content .elementor-widget.elementor-widget-icon-list,
	body .page-content .elementor-widget.elementor-widget-icon-box,
	body .elementor-location-single .elementor-widget.elementor-widget-text-editor,
	body .elementor-location-single .elementor-widget.elementor-widget-heading {
		--container-widget-width: 100%;
		--container-widget-flex-grow: 1;
		width: 100%;
		max-width: 100%;
	}
}


/* ===== aps-mobile-iconlist-type ======================================== */
/* APF — icon-list text on mobile.

   The hero eyebrow ("Your Trusted Fastener Partner") is not a heading widget,
   it is an icon-list: span.elementor-icon-list-text at 20px / 30px. The type
   scale snippet only reached headings and paragraphs, so it stayed at the
   desktop size and wrapped to two lines inside its pill (287px of text in a
   304px box). The same widget renders the hero's checkmark list.

   !important is used deliberately. Elementor page-scopes these selectors --
   `.elementor-87 .elementor-element.elementor-element-XXX .elementor-icon-list-text`
   reaches (0,5,0) -- and chasing that with an ever-longer selector is more
   fragile than one honest override on a leaf typography property.

   Scoped to .page-content so the header utility bar and the footer navigation
   (both icon-lists, both already sized deliberately) are untouched.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
	body .page-content .elementor-widget-icon-list .elementor-icon-list-text {
		font-size: clamp(15px, 3.9vw, 17px) !important;
		line-height: 1.45 !important;
	}
}


/* ===== aps-mobile-polish =============================================== */
/* APF — two mobile fixes found by looking at screenshots, not metrics.

   1. PDP BREADCRUMB LINES OVERLAPPED EACH OTHER.
      .woocommerce-breadcrumb carries a pre-existing line-height of 8px. Any
      breadcrumb that wraps therefore stacks its lines 8px apart while the text
      is ~16px tall, so they sit on top of one another. On a long product name
      ("10-16 X 3/4" Self-Drilling Screws #3 Pt 5/16" Tall Serr. IHWH Unsl
      Steel Zinc Plated") that is three overlapping lines.

      My own tap-target rule in part 1 made it worse: it set the links to
      display:inline-block with .5rem block padding, taking them to 24px inside
      an 8px line box. That rule was over-eager anyway -- breadcrumbs are inline
      prose links, which WCAG 2.5.8 explicitly exempts from the 44px minimum.
      Reverted here, and the real line-height bug fixed.

      No metric caught this. Overlapping text is not overflow, not a small tap
      target and not tiny type -- it is only visible in a screenshot. */
@media (max-width: 1024px) {
	.woocommerce-breadcrumb {
		line-height: 1.6 !important;
	}
	.woocommerce-breadcrumb a {
		display: inline;
		padding-block: 0;
	}
}


/* ===== aps-mobile-group-collapse ============================================
   /product-category/screws/ renders 98 groups / 1,179 rows / 6,903 cells in one
   page: 112,255px tall on a phone, about 133 screens.

   🔴 NOT server-side pagination. Paging changes URLs and needs a canonical /
   rel-next strategy on a catalogue that is about to launch -- real SEO risk.
   🔴 NOT stacked cards either (see aps-mobile-spec-tables): ~180px per row
   would take that page to ~212,000px.

   Collapsing each group behind the heading it already has costs nothing in
   markup, nothing in URLs, and leaves every row in the DOM for indexing.
   Scanning 98 group titles then opening one is also how the catalogue is
   actually browsed.

   ⚠ The collapsed state is applied by JS (reveal.js), never here. If the script
   does not run, every table renders exactly as it does today. This stylesheet
   only describes what "collapsed" looks like once JS has asked for it.
   ============================================================================ */
@media (max-width: 767px) {

	.aps-loop-groups .aps-group__head[data-aps-toggle] {
		cursor: pointer;
		position: relative;
		padding-right: 2.4rem;
		-webkit-tap-highlight-color: transparent;
	}

	/* Chevron: points down when collapsed, up when open. */
	.aps-loop-groups .aps-group__head[data-aps-toggle]::after {
		content: "";
		position: absolute;
		top: 50%;
		right: 1rem;
		width: 9px;
		height: 9px;
		margin-top: -6px;
		border-right: 2px solid rgba(255, 255, 255, .75);
		border-bottom: 2px solid rgba(255, 255, 255, .75);
		transform: rotate(45deg);
		transition: transform var(--aps-dur, 320ms) var(--aps-ease, ease);
	}

	.aps-loop-groups .aps-group__head[aria-expanded="true"]::after {
		transform: rotate(-135deg);
		margin-top: -2px;
	}

	.aps-loop-groups .aps-group.is-aps-collapsed .aps-group__table-wrap {
		display: none;
	}

	/* Row count, so a collapsed group still says how much is inside. */
	.aps-loop-groups .aps-group__count {
		display: inline-block;
		margin-top: .35rem;
		font-family: 'Open Sans', system-ui, sans-serif;
		font-size: 12px;
		font-weight: 600;
		letter-spacing: .04em;
		color: rgba(255, 255, 255, .72);
	}

	.aps-loop-groups .aps-group__head[data-aps-toggle]:focus-visible {
		outline: 2px solid #EC1313;
		outline-offset: -2px;
	}
}
