/*
Theme Name: Impress
Theme URI: https://www.impressprint.com/
Author: Impress Printing LLC
Author URI: https://www.impressprint.com/
Description: A native WordPress full-site-editing (block) theme for Impress, a family-run promotional-products and business-printing shop in Omaha, NE. Ported from the VoiceBox-design-system marketing site: bold uppercase display headlines, a red/yellow/black print-shop palette, marquee announcement bars, a mega-menu category navigation and WooCommerce-ready product patterns. Design tokens live in theme.json; this stylesheet only carries the handful of interaction patterns (mega menu, mobile nav, marquee ticker, CSS-only account tabs, conditional reseller fields, accordion) that block settings genuinely cannot express.
Version: 1.0.37
Requires at least: 6.4
Tested up to: 6.6
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: impress
Tags: full-site-editing, block-patterns, e-commerce, one-column, custom-colors, custom-menu

This theme assumes WooCommerce is active for shop/product functionality
(see functions.php for `add_theme_support('woocommerce')`) and assumes the
free Forminator plugin is installed for the contact form and the reseller
signup form. See the comment block at the top of functions.php for why.
*/

/* ==========================================================================
   Impress — supplementary theme CSS
   theme.json carries color, typography and spacing. This file only carries
   interaction patterns that block settings cannot express: the mega menu,
   mobile nav toggle, marquee ticker, CSS-only account-access tabs, the
   conditional reseller certificate panel, the accordion +/- icon, the
   before/after grid, and a small `.btn`/`.card`/`.prod`/`.tile` utility
   layer used inside hand-authored pattern markup. Section structure mirrors
   the original styles.css so it stays easy to diff against the source site.
   ========================================================================== */

/* ------------------------------ local tokens ----------------------------- */
/* Re-declared verbatim from the source design system so ported component CSS
   below (written against these exact custom-property names) keeps working
   without a find/replace pass. theme.json emits the WP-managed
   --wp--preset--* versions of the same values for use inside block settings. */
:root{
  --color-primary:#c8102e; --color-primary-hover:#a70d26; --color-primary-active:#8a0a1f; --color-primary-tint:#fdeef0; --color-on-primary:#ffffff;
  --color-accent:#ffd100; --color-accent-contrast:#222222; --color-accent-tint:#fff9db;
  --color-bg:#ffffff; --color-surface:#f6f6f6; --color-surface-inverse:#111111; --color-text:#111111; --color-text-inverse:#ffffff; --color-muted:#6b6b6b; --color-border:#e6e6e6; --color-border-strong:#111111;
  --color-success:#16a34a; --color-warning:#ca8a04; --color-error:#ef4444;
  --color-success-tint:#e9f8ef; --color-warning-tint:#fdf3d9; --color-error-tint:#fde8e8;
  --font-display:"Arial Black","Arial Bold",Impact,"Haettenschweiler","Segoe UI",system-ui,sans-serif;
  --font-ui:-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --font-mono:"Courier New",Consolas,ui-monospace,monospace;
  --text-display:48px; --text-headline:34px; --text-subhead:22px; --text-body-lg:19px; --text-body:16px; --text-small:14px; --text-label:12px;
  --weight-regular:400; --weight-medium:600; --weight-bold:700;
  --leading-display:0.98; --tracking-display:-0.02em; --tracking-label:0.09em;
  --space-1:4px; --space-2:8px; --space-3:16px; --space-4:24px; --space-5:32px; --space-6:48px; --space-7:64px; --space-8:96px;
  --section-mobile:48px; --section-tablet:64px; --section-desktop:96px;
  --radius-none:0px; --radius-sm:6px; --radius-md:10px; --radius-pill:999px;
  --border-hairline:1px; --border-strong:2px; --border-accent:4px;
  --container-max:1200px; --control-height:44px;
  --transition-fast:120ms ease; --transition-base:180ms ease;
  --shadow-sm:0 2px 8px rgba(17,17,17,.08); --shadow-md:0 8px 24px rgba(17,17,17,.12);
  --focus-ring-width:3px; --focus-ring-color:#8ab4ff; --focus-ring-offset:2px;
  --placeholder:#e9e9e9;
}

/* --------------------------------- layout -------------------------------- */
/* Ported from the source design system's base.css — dropped during the
   initial port, which let any padded 100%-width element (.wrap, forms, etc.)
   overflow the viewport by its own horizontal padding. box-sizing is the
   real fix; deliberately NOT setting overflow-x:hidden on html/body as a
   second layer of protection, because any overflow value other than
   `visible` on an ancestor breaks `position:sticky` on descendants (this is
   why the sticky header/mega-menu silently failed to stick) — box-sizing
   alone is sufficient once nothing is actually overflowing. */
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:auto}
html,body{max-width:100%}
img,video,canvas,svg,table{max-width:100%}
/* Restored — dropped in the initial port from the source design system's
   base.css (tokens/base.css: "h1,h2,h3,h4{margin:0} p{margin:0}"). Without
   this reset, every heading and paragraph fell back to the browser's UA
   default top+bottom margins, which every component rule and hand-authored
   pattern in this theme (.hero .lead, .sec-head, .card h3, the inline
   margin-top values throughout patterns/*.php and templates/*.html) was
   written assuming does NOT exist — so spacing above headings/paragraphs
   was previously the sum of an inline margin-top plus a leftover UA
   default margin, instead of the single token-based value the markup
   actually specifies. Found during the task-5 spacing/vertical-rhythm
   audit; this is the highest-impact single fix from that audit since it
   affects heading/paragraph spacing sitewide, not just one pattern. */
h1,h2,h3,h4{margin:0}
p{margin:0}
.wrap{width:100%;max-width:calc(var(--container-max) + 64px);margin:0 auto;padding:0 var(--space-4)}
@media(min-width:700px){.wrap{padding:0 var(--space-5)}}
/* Restored — dropped in the initial port. Controls the vertical rhythm of
   nearly every section on every page (mobile/tablet/desktop padding scale). */
.section{padding:var(--section-mobile) 0}
@media(min-width:700px){.section{padding:var(--section-tablet) 0}}
@media(min-width:1000px){.section{padding:var(--section-desktop) 0}}
.section--tight{padding-top:var(--space-6);padding-bottom:var(--space-6)}
.band--surface{background:var(--color-surface)}
.band--dark{background:var(--color-surface-inverse);color:var(--color-text-inverse)}
.band--dark h1,.band--dark h2,.band--dark h3{color:var(--color-text-inverse)}
.band--dark p{color:#d6d6d6}
.band--yellow{background:var(--color-accent);color:var(--color-accent-contrast)}
.band--yellow h2,.band--yellow p{color:var(--color-accent-contrast)}
/* Restored — dropped in the initial port. */
.stack{display:grid;gap:var(--space-3)}
.stack-4{display:grid;gap:var(--space-4)}
.stack-6{display:grid;gap:var(--space-6)}
.row{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:center}
.grid{display:grid;gap:var(--space-4)}
.g2{grid-template-columns:1fr}
.g3{grid-template-columns:1fr}
.g4{grid-template-columns:repeat(2,1fr)}
@media(min-width:700px){.g2{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.g3{grid-template-columns:repeat(3,1fr)}.g4{grid-template-columns:repeat(4,1fr)}}
.only-lg{display:none}
@media(min-width:1000px){.only-lg{display:block}}
.price{font-family:var(--font-mono);font-weight:var(--weight-bold)}

/* ------------------------------- typography ------------------------------ */
.display{font-family:var(--font-display);letter-spacing:var(--tracking-display);line-height:var(--leading-display);text-transform:uppercase}
.h-hero{font-size:36px}
@media(min-width:700px){.h-hero{font-size:var(--text-display)}}
@media(min-width:1000px){.h-hero{font-size:62px}}
.eyebrow{font-size:11px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-primary);display:inline-block}
.eyebrow--yellow{color:var(--color-accent)}
.eyebrow--muted{color:var(--color-muted)}
.lead{font-size:var(--text-body-lg);color:var(--color-muted);max-width:60ch}
.muted{color:var(--color-muted)}
.small{font-size:var(--text-small)}
.sku{font-family:var(--font-mono);font-size:var(--text-small);color:var(--color-muted)}
.measure{max-width:68ch}
.sec-head{border-top:var(--border-accent) solid var(--color-border-strong);padding-top:var(--space-3);margin-bottom:var(--space-5);display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--space-4)}
/* Flex items default to min-width:auto, floored at their own content's
   min-content size - same mechanism as the CSS Grid min-width:auto issue
   elsewhere in this file, just for .sec-head's flex children instead of a
   grid item. With only one child (no "see all" link to wrap around),
   flex-wrap:wrap above has nothing to wrap to, so that child's heading
   text was still flooring .sec-head's own width when .sec-head itself
   sits inside a narrow container (e.g. the car-wraps gallery's grid
   column) - confirmed live at 320px. min-width:0 here lets it actually
   shrink instead. */
.sec-head > *{min-width:0}
.sec-head--red{border-top-color:var(--color-primary)}
.band--dark .sec-head{border-top-color:var(--color-accent)}

/* -------------------------------- buttons -------------------------------- */
.btn,.wp-block-button__link{font-family:var(--font-ui);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:.06em;font-size:var(--text-small);padding:10px var(--space-4);border-radius:var(--radius-sm);border:var(--border-strong) solid var(--color-primary);background:var(--color-primary);color:var(--color-on-primary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);text-decoration:none;transition:background var(--transition-fast),color var(--transition-fast),border-color var(--transition-fast)}
.btn:hover,.wp-block-button__link:hover{background:var(--color-primary-hover);border-color:var(--color-primary-hover);color:var(--color-on-primary)}
.btn:active{background:var(--color-primary-active);border-color:var(--color-primary-active)}
.btn--secondary,.is-style-impress-secondary .wp-block-button__link{background:transparent;color:var(--color-text);border-color:var(--color-border)}
.btn--secondary:hover,.is-style-impress-secondary .wp-block-button__link:hover{background:var(--color-text);color:var(--color-text-inverse);border-color:var(--color-text)}
.btn--onDark,.is-style-impress-ondark .wp-block-button__link{background:transparent;color:var(--color-text-inverse);border-color:#5a5a5a}
.btn--onDark:hover,.is-style-impress-ondark .wp-block-button__link:hover{background:var(--color-text-inverse);color:var(--color-text);border-color:var(--color-text-inverse)}
.btn--sm{font-size:var(--text-label);padding:7px var(--space-3)}
.btn--lg{font-size:var(--text-body);padding:14px var(--space-5)}
.btn--block{width:100%}

/* --------------------------------- cards --------------------------------- */
.card{background:var(--color-surface);border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);padding:var(--space-4);transition:border-color var(--transition-base),box-shadow var(--transition-base)}
.card--editorial{background:var(--color-bg);border-radius:var(--radius-none)}
.card--featured{border-top:var(--border-accent) solid var(--color-primary)}
a.card:hover,.card--link:hover{border-color:var(--color-primary);box-shadow:var(--shadow-sm)}
.card h3{margin-bottom:var(--space-2)}

/* ------------------------------- real photos ------------------------------ */
.im{display:block;width:100%;height:auto;object-fit:cover;background:var(--placeholder);border-radius:var(--radius-sm)}
.im--43{aspect-ratio:4/3}
.im--11{aspect-ratio:1/1}
.im--169{aspect-ratio:16/9}
.im--tall{aspect-ratio:3/4}
.im--sharp{border-radius:0}
.prod-media .im,.tile .im,.post .im,.ba .im,.mega .im{border-radius:0}
.ba .im{height:100%}

/* ------------------------------ product card ----------------------------- */
.prod{display:flex;flex-direction:column;background:var(--color-surface);border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden;transition:border-color var(--transition-base),box-shadow var(--transition-base)}
.prod:hover{border-color:var(--color-primary);box-shadow:var(--shadow-sm)}
.prod--featured{border-top:var(--border-accent) solid var(--color-primary)}
.prod-media{position:relative}
.prod-media .im{display:block}
/* Real WooCommerce product images (shop loop, category archives, related
   products - every one of these renders the same content-product.php card,
   so this one rule covers all three) come from
   external_image_handler.php's get_basic_img_html() as a bare
   <img class="wp-post-image"> with no theme sizing class, unlike the
   hand-authored homepage cards above which opt into a fixed ratio via
   .im/.im--43. Every source photo has a different natural aspect ratio, so
   without this the shop/category grid rendered every card a different
   height. Fixed 4:3 + object-fit:cover crops to a consistent frame instead,
   matching .im--43 elsewhere on the site. */
.prod-media img.wp-post-image{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;background:var(--placeholder)}
.prod-flags{position:absolute;top:var(--space-2);left:var(--space-2);display:flex;flex-wrap:wrap;gap:6px}
.prod-body{padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-2);flex:1}
.prod-body h3{font-family:var(--font-ui);font-size:18px;font-weight:var(--weight-bold);line-height:1.25;letter-spacing:0;margin:0;text-transform:none}
.prod-body h3 a{color:var(--color-text);text-decoration:none}
.prod-body h3 a:hover{color:var(--color-primary);text-decoration:underline}
/* Real WooCommerce loop cards (category pages, Shop All) render the title as
   a plain <h2 class="woocommerce-loop-product__title"> with no link of its
   own — the whole card is one wrapping <a class="woocommerce-LoopProduct-
   link">. The hand-built "Best sellers" cards above use <h3><a>, so the
   :hover rule above never matched WooCommerce's real markup, leaving title
   hover-red inconsistent between the homepage and every real product grid.
   Shared here on .prod:hover so every card component gets the same hover
   state regardless of which markup shape produced it. */
.prod:hover .woocommerce-loop-product__title{color:var(--color-primary)}
.prod-price{margin-top:auto;padding-top:var(--space-3);display:flex;align-items:baseline;gap:var(--space-2)}
.prod-price .amt{font-family:var(--font-display);font-size:24px;letter-spacing:var(--tracking-display)}
/* "Starts at $X.XX" prefix — added via the woocommerce_get_price_html
   filter in functions.php, wherever a product price shows inside a grid/
   loop (never on the single-product page itself). */
.impress-starts-at{display:block;font-size:var(--text-small);font-weight:var(--weight-regular);color:var(--color-muted);text-transform:none;letter-spacing:0}
.prod .btn{margin-top:var(--space-2)}

/* --------------------------------- badges -------------------------------- */
.badge{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;white-space:nowrap;font-size:11px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;padding:4px 8px;border:var(--border-hairline) solid var(--color-accent);border-radius:var(--radius-sm);background:var(--color-accent);color:var(--color-accent-contrast)}
.badge--dark{background:var(--color-text);color:var(--color-text-inverse);border-color:var(--color-text)}
.badge--primary{background:var(--color-primary);color:var(--color-on-primary);border-color:var(--color-primary)}
.badge--outline{background:transparent;color:var(--color-text);border-color:var(--color-border)}

/* --------------------------------- forms --------------------------------- */
.field{display:grid;gap:6px;margin-bottom:var(--space-3)}
.field > label,.lbl{font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase}
.hint{font-size:var(--text-small);color:var(--color-muted)}
.req{color:var(--color-primary)}
input[type=text],input[type=email],input[type=tel],input[type=password],input[type=search],input[type=number],input[type=date],select,textarea{width:100%;min-height:var(--control-height);font-family:var(--font-ui);font-size:var(--text-body);color:var(--color-text);background:var(--color-bg);padding:9px 14px;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm);transition:border-color var(--transition-fast),box-shadow var(--transition-fast)}
textarea{min-height:120px;resize:vertical}
input:focus,select:focus,textarea:focus{border-color:var(--color-primary);outline:none;box-shadow:0 0 0 var(--focus-ring-width) rgba(200,16,46,.18)}
fieldset{border:0;margin:0 0 var(--space-4);padding:0;min-width:0}
legend{font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;padding:0;margin-bottom:var(--space-3)}
.opts{display:grid;gap:var(--space-2);grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.opt{position:relative;display:block;cursor:pointer;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-bg);padding:var(--space-3);transition:border-color var(--transition-fast),background var(--transition-fast)}
.opt:hover{border-color:var(--color-primary)}
.opt input{position:absolute;opacity:0;width:1px;height:1px}
.opt:has(input:checked){border-color:var(--color-primary);background:var(--color-primary-tint)}
.opt:has(input:focus-visible){outline:var(--focus-ring-width) solid var(--focus-ring-color);outline-offset:var(--focus-ring-offset)}
.opt-title{display:block;font-weight:var(--weight-bold)}
.opt-meta{display:block;font-size:var(--text-small);color:var(--color-muted)}
.opt--rec{border-top:var(--border-accent) solid var(--color-primary)}
/* The "Flat Rate Shipping" option on the product page (functions.php's
   #pdp-shipping panel) - same .opt card as Local Pickup beside it, but
   with a third line showing the actual price prominently, so the cost is
   visible before Add to Cart rather than only surfacing at checkout.
   assets/js/site.js#initRushShippingPriceSync doubles the number in
   [data-flat-rate-price] live when Rush turnaround is selected. */
.opt-price{display:block;margin-top:6px;font-family:var(--font-display);font-size:22px;letter-spacing:var(--tracking-display);color:var(--color-primary)}
.check{display:flex;gap:var(--space-2);align-items:flex-start;font-size:var(--text-body)}
.check input{width:20px;height:20px;min-height:0;flex:0 0 auto;margin-top:2px;accent-color:var(--color-primary)}
.check-body{display:grid;gap:2px}
.upload{border:var(--border-strong) dashed var(--color-border);border-radius:var(--radius-sm);background:var(--color-bg);padding:var(--space-4);display:grid;gap:var(--space-2)}
.upload:hover{border-color:var(--color-primary)}
input[type=file]{font-size:var(--text-small);padding:8px 0;border:0;min-height:0}
input[type=file]::file-selector-button{font-family:var(--font-ui);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:.06em;font-size:var(--text-label);padding:8px 14px;margin-right:12px;border:var(--border-strong) solid var(--color-border);background:transparent;border-radius:var(--radius-sm);cursor:pointer}
input[type=file]::file-selector-button:hover{background:var(--color-text);color:var(--color-text-inverse);border-color:var(--color-text)}
.form-grid{display:grid;gap:0 var(--space-3);grid-template-columns:1fr}
@media(min-width:700px){.form-grid{grid-template-columns:1fr 1fr}.form-grid .span2{grid-column:1/-1}}

/* --------------------------------- header -------------------------------- */
/* ---- marquee bars (announcement + trust strip) ----
   Static, wrapping, fully readable without JS. assets/js/site.js adds
   .mq--on, clones the group and drives one translate loop. Clones are
   aria-hidden so the copy is read once. */
.mq{overflow:hidden;position:relative;width:100%}
.mq__track{display:flex;flex-wrap:wrap;align-items:center}
.mq--on .mq__track{flex-wrap:nowrap;width:max-content;will-change:transform;animation:mq-scroll var(--mq-dur,30s) linear infinite}
.mq__group{display:flex;align-items:center;flex:0 0 auto}
.mq--on .mq__group{flex-wrap:nowrap;white-space:nowrap}
.mq--paused .mq__track,.mq--still .mq__track{animation-play-state:paused}
@keyframes mq-scroll{from{transform:translateX(0)}to{transform:translateX(calc(-1 * var(--mq-w,0px)))}}
@media(prefers-reduced-motion:reduce){.mq--on .mq__track{animation:none;transform:none;flex-wrap:wrap;width:auto}.mq__group--clone{display:none}}
.topbar .mq{min-height:34px}
.topbar .mq__group{gap:var(--space-2) var(--space-5);padding:8px var(--space-5) 8px 0}
.trust .mq{min-height:46px}
.trust .mq__group{gap:var(--space-2) var(--space-4);padding:var(--space-3) var(--space-4) var(--space-3) 0}
.topbar{background:var(--color-surface-inverse);color:#fff;font-size:var(--text-small)}
.topbar .wrap{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-4);justify-content:space-between;padding-top:8px;padding-bottom:8px}
.topbar a{color:#fff}
.topbar .sep{color:#8a8a8a}
/* position:sticky is defeated somewhere in the block-template-part wrapper
   chain WordPress renders around this template part (confirmed empirically:
   an inline `!important` override still fails to stick, while the identical
   element sticks fine the moment it's moved to be a direct child of body) —
   so the mega menu's "sticky at top of the site on scroll" requirement is
   implemented with a JS-toggled fixed position instead, which is immune to
   whatever containing-block quirk is defeating native sticky here. See
   assets/js/site.js `initStickyHeader()`. */
.site-header{position:relative;z-index:40;background:var(--color-bg);border-bottom:var(--border-strong) solid var(--color-border-strong)}
.site-header.is-pinned{position:fixed;top:0;left:0;right:0;width:100%;box-shadow:var(--shadow-md)}
body.has-pinned-header-space{transition:padding-top 0s}
/* ---- hamburger ---- */
/* Icon-only (no "Menu" label) to match the account/cart icon buttons next
   to it — same square control-height tap target, same border/hover
   treatment. "Menu" stays in the markup as visually-hidden .sr-only text
   for screen readers/accessibility, it just isn't shown. */
.navtoggle{display:inline-flex;align-items:center;justify-content:center;width:var(--control-height);height:var(--control-height);padding:0;font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-text);background:transparent;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer}
.navtoggle:hover{border-color:var(--color-primary);color:var(--color-primary)}
.navtoggle svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2}
.navtoggle .bar-x{display:none}
.navtoggle[aria-expanded="true"] .bar-x{display:block}
.navtoggle[aria-expanded="true"] .bar-open{display:none}
@media(min-width:1000px){.navtoggle{display:none}}
.header-row{display:flex;align-items:center;gap:var(--space-3);padding-top:var(--space-3);padding-bottom:var(--space-3);flex-wrap:wrap}
.wordmark{display:inline-flex;align-items:center;flex:0 0 auto;text-decoration:none}
.wordmark .logo-full{height:34px;width:auto;display:block}
@media(min-width:1000px){.wordmark .logo-full{height:42px}}
.site-search{display:flex;flex:1 1 220px;min-width:0}
.site-search input{border-right:0;border-radius:var(--radius-sm) 0 0 var(--radius-sm);font-size:var(--text-small)}
.site-search button{min-height:var(--control-height);border:var(--border-strong) solid var(--color-text);background:var(--color-text);color:#fff;border-radius:0 var(--radius-sm) var(--radius-sm) 0;padding:0 16px;font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--tracking-label);text-transform:uppercase;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.site-search button:hover{background:var(--color-primary);border-color:var(--color-primary)}
.util-nav{display:flex;align-items:center;gap:var(--space-3);margin-left:auto;font-size:var(--text-small);font-weight:var(--weight-bold)}
.util-nav a{color:var(--color-text);text-decoration:none}
.util-nav a:hover{color:var(--color-primary);text-decoration:underline}
/* Account and cart are icon-only on every viewport now (no "Log in or sign
   up" / "Cart" text at any breakpoint) — one square control-height button
   style, shared by both via .util-icon, same icon stroke weight (2px,
   currentColor) already used by search/hamburger elsewhere. "Dashboard"
   and "Reseller Program" stay real text links on desktop and move into the
   mobile slide-out nav below 1000px (see patterns/mega-menu.php's
   .mobile-only-link items) rather than becoming more icons nobody could
   identify. */
.util-nav .util-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--control-height);height:var(--control-height);border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm)}
.util-nav .util-icon:hover{background:var(--color-text);border-color:var(--color-text);text-decoration:none}
.util-nav .util-icon:hover .icon{stroke:#fff}
.mobile-only-link{display:none}
@media(max-width:999px){
  /* Same 1000px breakpoint the hamburger itself switches on (.navtoggle
     is hidden at min-width:1000px) — one consistent cutover for every part
     of the nav, instead of the account/cart icons switching at 700px while
     Dashboard/Reseller Program switched at a different width. Below it,
     "Dashboard"/"Reseller Program" move into the mobile slide-out
     (.mobile-only-link) instead of sitting as text in the util-nav row. */
  .only-desktop-text{display:none}
  .mobile-only-link{display:block}
}
.mainnav{border-top:var(--border-hairline) solid var(--color-border);position:relative}
.mainnav-list{list-style:none;margin:0;padding:0;display:flex;gap:2px;overflow-x:auto;-webkit-overflow-scrolling:touch}
.mainnav-list > li > a{display:block;white-space:nowrap;padding:12px 14px;font-size:var(--text-small);font-weight:var(--weight-bold);text-decoration:none;color:var(--color-text)}
.mainnav-list > li > a:hover,.mainnav-list > li > a[aria-current=page]{color:var(--color-primary);box-shadow:inset 0 -3px 0 var(--color-primary)}
.mega{display:none}
@media(min-width:1000px){
  .mainnav-list{overflow:visible}
  .mega{position:absolute;top:100%;left:0;right:0;background:var(--color-bg);border-top:var(--border-hairline) solid var(--color-border);box-shadow:var(--shadow-md);padding:var(--space-5) 0;z-index:50}
  .has-mega:hover .mega,.has-mega:focus-within .mega{display:block}
  .mega-in{display:grid;grid-template-columns:1fr 1fr 1fr 320px;gap:var(--space-6)}
  .mega h3{font-size:11px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-muted);margin-bottom:var(--space-3);font-family:var(--font-ui)}
  .mega ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-2)}
  .mega ul a{font-size:var(--text-small);color:var(--color-text);text-decoration:none}
  .mega ul a:hover{color:var(--color-primary);text-decoration:underline}
  .mega-promo{background:var(--color-surface);border-top:var(--border-accent) solid var(--color-primary);padding:var(--space-4)}
  .mega-promo p.t{font-family:var(--font-display);font-size:22px;line-height:1.05;letter-spacing:var(--tracking-display);margin:10px 0 6px}
}

/* ------------------------------ breadcrumbs ------------------------------ */
.crumbs{font-size:var(--text-small);color:var(--color-muted);padding:var(--space-3) 0;display:flex;flex-wrap:wrap;gap:8px}
.crumbs a{color:var(--color-muted)}
.crumbs [aria-current]{color:var(--color-text);font-weight:var(--weight-bold)}

/* --------------------------------- footer -------------------------------- */
/* .site-footer previously had zero vertical padding of its own — the wrap's
   content (wordmark, nav columns) sat flush against the top edge of the
   black background with no breathing room, unlike every .section elsewhere
   on the site. Matches .section's own responsive padding scale exactly. */
/* padding-top was var(--section-mobile/tablet/desktop) - the same scale a
   full page SECTION uses between major content blocks, which read as too
   much air above a footer specifically. One step down the same spacing
   scale (space-5/6/7 instead of section-mobile/tablet/desktop, i.e.
   space-6/7/8) keeps the same responsive proportions, just smaller. */
.site-footer{background:var(--color-surface-inverse);color:#fff;padding-top:var(--space-5)}
@media(min-width:700px){.site-footer{padding-top:var(--space-6)}}
@media(min-width:1000px){.site-footer{padding-top:var(--space-7)}}
/* Restored — dropped in the initial port, which collapsed the four-column
   footer into a single stacked column at every width. */
.foot-grid{display:grid;gap:var(--space-6);grid-template-columns:1fr}
@media(min-width:700px){.foot-grid{grid-template-columns:1fr 1fr}}
@media(min-width:1000px){.foot-grid{grid-template-columns:1.5fr 1fr 1fr 1fr}}
.site-footer h2,.site-footer h3{font-size:11px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-accent);margin-bottom:var(--space-3);font-family:var(--font-ui)}
.site-footer ul{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-2)}
.site-footer a{color:#e6e6e6;text-decoration:none;font-size:var(--text-small)}
.site-footer a:hover{color:#fff;text-decoration:underline}
.foot-brand p{color:#c9c9c9;font-size:var(--text-small);max-width:34ch}
.social{display:flex;gap:var(--space-2);flex-wrap:wrap;margin-top:var(--space-3)}
.social a{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border:var(--border-hairline) solid #3a3a3a;border-radius:var(--radius-sm)}
.social a:hover{border-color:var(--color-primary);background:var(--color-primary)}
.social svg{width:18px;height:18px;fill:none;stroke:#e6e6e6;stroke-width:1.8}
.social a:hover svg{stroke:#fff}
.foot-legal{margin-top:var(--space-5);padding-top:var(--space-4);border-top:var(--border-hairline) solid #3a3a3a;font-size:var(--text-small);color:#8a8a8a;display:flex;flex-wrap:wrap;gap:var(--space-3);justify-content:space-between}
.foot-legal a{color:#8a8a8a}

/* ---------------------------------- hero --------------------------------- */
/* Restored — dropped in the initial port, which left the hero as plain
   block flow (text stacked above the image collage at every width instead
   of a two-column side-by-side layout on desktop). */
.hero{border-bottom:var(--border-strong) solid var(--color-border-strong)}
/* On every page where a .trust marquee sits directly under the hero (home,
   the printing categories via shop-content.php, banners/apparel category
   pages), this border drew a heavy horizontal rule wedged between the two —
   confirmed as the line the marquee needed removed from above it. Left
   in place on pages where .hero has no marquee right after it (services,
   about, contact, car-wraps, websites), where it's still a real separator. */
.hero:has(+ .trust){border-bottom:none}
.hero-in{display:grid;gap:var(--space-5);align-items:center;padding-top:var(--space-6);padding-bottom:var(--space-6)}
@media(min-width:1000px){.hero-in{grid-template-columns:1.05fr .95fr;gap:var(--space-8);padding-top:var(--space-8);padding-bottom:var(--space-8)}}
.hero h1{max-width:16ch}
.hero .lead{margin-top:var(--space-4)}
.hero-in .hero-visual{align-self:start;width:100%}
.hero-cta{display:flex;flex-wrap:wrap;gap:var(--space-3);margin-top:var(--space-5)}
.hero-facts{display:flex;flex-wrap:wrap;gap:var(--space-4);margin-top:var(--space-6);padding-top:var(--space-4);border-top:var(--border-hairline) solid var(--color-border)}
.hero-fact{display:grid;gap:2px}
.hero-fact b{font-family:var(--font-display);font-size:26px;letter-spacing:var(--tracking-display)}
.hero-fact span{font-size:var(--text-small);color:var(--color-muted)}

/* ---- homepage hero visual: single rotating image (crossfade) ----
   .hero-visual gets the theme's own border-accent + primary-red line -
   the exact same "thin accent rule" token .sec-head--red already uses
   site-wide - as the "line accent" the redesign asked for, rather than
   inventing a new one-off treatment. .hero-rotator holds N absolutely-
   positioned images at opacity:0, only .is-active at opacity:1 - the
   FIRST image carries is-active directly in the markup (not added by
   JS), so a no-JS visitor still gets one complete, correctly-cropped
   photo, just without the crossfade. assets/js/site.js#initHeroRotator
   is the only thing that ever changes which one is active afterward. */
.hero-visual{position:relative}
@media(min-width:1000px){.hero-visual{border-left:var(--border-accent) solid var(--color-primary);padding-left:var(--space-6)}}
.hero-rotator{position:relative;overflow:hidden;border-radius:var(--radius-md);aspect-ratio:3/4;background:var(--color-surface)}
.hero-rotator__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity 900ms ease}
.hero-rotator__img.is-active{opacity:1}
.hero-rotator__meta{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);margin-top:var(--space-3)}
.hero-rotator__index{font-family:var(--font-display);letter-spacing:var(--tracking-display);font-size:13px;color:var(--color-muted);flex:0 0 auto}
.hero-rotator__index b{color:var(--color-primary)}
.hero-rotator__caption{font-family:var(--font-ui);font-size:var(--text-small);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:var(--tracking-label);color:var(--color-text);flex:1 1 auto;text-align:right}
.hero-rotator__dots{display:flex;gap:8px;flex:0 0 auto}
.hero-rotator__dot{width:8px;height:8px;padding:0;border:none;border-radius:var(--radius-pill);background:var(--color-border);cursor:pointer;transition:background var(--transition-fast),width var(--transition-fast)}
.hero-rotator__dot.is-active{background:var(--color-primary);width:22px}
@media(prefers-reduced-motion:reduce){.hero-rotator__img{transition:none}}
/* align-items:flex-start (not center) + no flex-wrap so the icon stays
   pinned top-left of its own line-height and the label sits beside it on
   the same row, wrapping its own text internally on narrow widths instead
   of the whole <span> dropping to a line below the icon (the old
   flex-wrap:wrap moved the two flex children — icon, span — onto separate
   rows whenever they didn't both fit on one line). */
.res-strip{display:inline-flex;align-items:flex-start;gap:var(--space-3);border:var(--border-strong) solid var(--color-border-strong);border-radius:var(--radius-sm);padding:12px var(--space-4);background:var(--color-bg)}
.res-strip svg{width:18px;height:18px;margin-top:2px;fill:none;stroke:var(--color-primary);stroke-width:2;flex:0 0 auto}
.res-strip .small{flex:1 1 auto;min-width:0}

/* ------------------------------ trust / stats ---------------------------- */
.trust{background:var(--color-surface-inverse);color:#fff}
.trust ul{list-style:none;margin:0;display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-4);font-size:var(--text-small);align-items:center}
.trust li{display:flex;gap:10px;align-items:center;color:#e6e6e6}
.trust li::before{content:"—";color:var(--color-accent)}
.stats{display:grid;gap:var(--space-4);grid-template-columns:repeat(2,1fr)}
@media(min-width:1000px){.stats{grid-template-columns:repeat(4,1fr)}}
.stat{border-top:var(--border-accent) solid var(--color-primary);padding-top:var(--space-3)}
.stat b{display:block;font-family:var(--font-display);font-size:40px;line-height:1;letter-spacing:var(--tracking-display)}
.band--dark .stat{border-top-color:var(--color-accent)}
.stat p{font-size:var(--text-small);color:var(--color-muted);margin-top:var(--space-2)}
.band--dark .stat p{color:#c9c9c9}

/* ------------------------------- categories ------------------------------ */
.cat-tiles{display:grid;gap:var(--space-3);grid-template-columns:repeat(2,1fr)}
@media(min-width:1000px){.cat-tiles{grid-template-columns:repeat(4,1fr);gap:var(--space-4)}}
.tile{display:grid;gap:var(--space-2);text-decoration:none;color:var(--color-text);border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden;background:var(--color-surface);transition:border-color var(--transition-base),box-shadow var(--transition-base)}
.tile:hover{border-color:var(--color-primary);box-shadow:var(--shadow-sm);color:var(--color-text)}
.tile-body{padding:var(--space-3) var(--space-3) var(--space-4)}
.tile b{display:block;font-size:16px}
.tile span{font-size:var(--text-small);color:var(--color-muted)}
.tile:hover b{color:var(--color-primary)}

/* -------------------------------- timeline ------------------------------- */
.steps{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-4);counter-reset:s}
@media(min-width:1000px){.steps{grid-template-columns:repeat(5,1fr)}}
.steps li{counter-increment:s;border-top:var(--border-strong) solid var(--color-border-strong);padding-top:var(--space-3)}
.steps li::before{content:"0" counter(s);display:block;font-family:var(--font-display);font-size:28px;letter-spacing:var(--tracking-display);color:var(--color-primary);margin-bottom:var(--space-2)}
.steps b{display:block;margin-bottom:6px}
.steps p{font-size:var(--text-small);color:var(--color-muted)}

/* ------------------------------ testimonials ----------------------------- */
.quote{background:var(--color-surface);border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-border-strong);border-radius:var(--radius-sm);padding:var(--space-4);display:grid;gap:var(--space-3)}
.quote blockquote{margin:0;font-size:var(--text-body-lg);line-height:1.4}
.quote cite{font-style:normal;font-size:var(--text-small);color:var(--color-muted)}
.quote cite b{display:block;color:var(--color-text);font-style:normal}
/* "Printing for" client-logo marquee: same .mq/.mq__track/.mq__group
   marquee mechanics + assets/js/site.js#initMarquee behaviour as the
   homepage's other marquee bars (hero trust strip, shop-content category
   strip) - full-bleed edge-to-edge like those two (no border-radius, and
   now a direct sibling of .wrap in testimonials.php rather than nested
   inside it, so it isn't capped at the page's max-width). .logo-mq__img
   normalizes every logo to a common 100px width (auto height, so each SVG
   keeps its own aspect ratio) regardless of the wildly different intrinsic
   sizes real client-supplied logo files come in. Real original logo colors
   (per merchant request) - no grayscale/invert filter, since some of these
   marks (the paint-brand-red Reyes/Alex Painting logos, the green Los
   Solesitos leaf) are recognizable specifically by their color. */
.logo-mq{background:var(--color-surface-inverse)}
.logo-mq .mq__group{gap:var(--space-6);padding:var(--space-4) var(--space-5)}
.logo-mq__img{width:100px;height:auto;flex:0 0 auto;display:block}

/* ------------------------------ before/after ----------------------------- */
.ba{display:grid;gap:2px;grid-template-columns:1fr 1fr;border:var(--border-strong) solid var(--color-border-strong);background:var(--color-border-strong)}
.ba figure{margin:0;background:var(--color-bg);display:grid}
.ba figcaption{padding:var(--space-3);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase}

/* ---------------------------------- shop --------------------------------- */
/* woocommerce/archive-product.php + woocommerce/content-product.php.
   Class names and layout copied verbatim from the source design's
   .shop-layout/.facets/.chips/.toolbar/.pager rules so the real WooCommerce
   shop/category archive matches the Claude Design shop mockup exactly. */
.shop-layout{display:grid;gap:var(--space-5)}
@media(min-width:1000px){.shop-layout{grid-template-columns:280px 1fr;gap:var(--space-6);align-items:start}}
.facets{border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-border-strong);border-radius:var(--radius-sm);padding:var(--space-4);background:var(--color-bg)}
.facets fieldset{margin-bottom:var(--space-4);padding-bottom:var(--space-4);border-bottom:var(--border-hairline) solid var(--color-border)}
.facets fieldset:last-of-type{border-bottom:0;padding-bottom:0}
.facets .check{margin-bottom:10px;font-size:var(--text-small)}
/* The <details class="facets"> element in the markup carries a hardcoded
   `open` attribute (confirmed necessary: a closed <details>'s children stay
   hidden regardless of any CSS display value you give them — author CSS
   cannot override that, only the `open` attribute or JS toggling it can), so
   the filter panel starts expanded on every device. Below 1000px the
   visible summary lets a user collapse/reopen it (native <details> click
   behavior still works normally even though it starts open); at 1000px+ the
   summary is hidden entirely so it just reads as an always-visible sidebar. */
.facets-summary{cursor:pointer;font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-small);letter-spacing:var(--tracking-label);text-transform:uppercase;padding:var(--space-3) var(--space-4);margin:calc(-1 * var(--space-4));margin-bottom:0;list-style:none}
.facets-summary::-webkit-details-marker{display:none}
.facets-summary::after{content:"+";float:right;font-family:var(--font-mono);color:var(--color-primary)}
.facets[open] .facets-summary::after{content:"\2013"}
.facets[open] .facets-summary{border-bottom:var(--border-hairline) solid var(--color-border);margin-bottom:var(--space-4)}
@media(min-width:1000px){
  .facets-summary{display:none}
  .facets > form{display:block !important}
}
.chips{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.chip{display:inline-flex;align-items:center;gap:8px;flex:0 0 auto;white-space:nowrap;font-size:var(--text-small);font-weight:var(--weight-bold);padding:7px 12px;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-bg);color:var(--color-text);text-decoration:none}
.chip:hover{border-color:var(--color-primary);color:var(--color-primary)}
.chip--on{background:var(--color-primary-tint);border-color:var(--color-primary);color:var(--color-primary)}
.toolbar{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:center;justify-content:space-between;border-bottom:var(--border-strong) solid var(--color-border-strong);padding-bottom:var(--space-3);margin-bottom:var(--space-4)}
.toolbar select{width:auto;min-width:200px}
/* Bridges WooCommerce's own real result-count/ordering markup (rendered
   in woocommerce_before_shop_loop, wrapped in .toolbar unchanged) to the
   source design's toolbar layout, without touching WooCommerce's output. */
.toolbar .woocommerce-result-count{margin:0;font-size:var(--text-small);color:var(--color-muted)}
.toolbar .woocommerce-ordering{display:flex;align-items:center;gap:var(--space-2);margin:0}
.toolbar .woocommerce-ordering label{font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase}
.toolbar .woocommerce-ordering select{width:auto;min-width:200px}
.pager,ul.page-numbers,nav.woocommerce-pagination ul{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center;justify-content:center;margin-top:var(--space-6);list-style:none;padding:0}
.pager a,.pager span,.page-numbers a,.page-numbers span,nav.woocommerce-pagination a,nav.woocommerce-pagination span{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;padding:0 12px;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-sm);text-decoration:none;color:var(--color-text);font-weight:var(--weight-bold);font-size:var(--text-small)}
.pager a:hover,.page-numbers a:hover,nav.woocommerce-pagination a:hover{border-color:var(--color-primary);color:var(--color-primary)}
.pager [aria-current=page],.page-numbers.current,nav.woocommerce-pagination .current{background:var(--color-text);border-color:var(--color-text);color:#fff}
/* Real WooCommerce shop-loop chrome bridged into the .prod card look
   (woocommerce/content-product.php keeps every core hook — this only
   restyles what those hooks print). */
.woocommerce-products-header__title{font-family:var(--font-display);letter-spacing:var(--tracking-display);line-height:var(--leading-display);text-transform:uppercase;font-size:32px}
.woocommerce-products-header .term-description,.woocommerce-products-header .page-description{font-size:var(--text-body-lg);color:var(--color-muted);max-width:60ch;margin-top:var(--space-3)}
ul.products{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-4);grid-template-columns:1fr}
@media(min-width:700px){ul.products{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){ul.products{grid-template-columns:repeat(3,1fr)}}
/* WooCommerce's own bundled woocommerce-layout.css / woocommerce-smallscreen.css
   set float+percentage-width rules directly on li.product (e.g.
   ".woocommerce ul.products[class*='columns-'] li.product{width:48%;float:left}"
   on small screens) — confirmed live, this was overriding the grid above and
   collapsing every product card to a squeezed half-width column with the
   title/SKU/button text wrapping badly. Per spec a grid container's children
   should ignore float automatically, but as with the same class of bug on
   the My Account page, the competing rule was still winning in practice, so
   neutralize it explicitly rather than rely on that. */
.woocommerce ul.products li.product,
.woocommerce-page ul.products li.product{float:none!important;width:auto!important;margin:0!important;clear:none!important}
/* WooCommerce's own core CSS also puts a float-era clearfix directly on
   ul.products itself (".products:before,.products:after{content:' ';
   display:table}"). Confirmed live: because our .products is a real grid
   container, that generated pseudo-element counts as an actual grid item —
   it silently claimed row 1 / column 1, pushing every real product one cell
   to the right on the grid's first row only (the empty-looking first slot,
   grid visually "starting" at the second product). Floats don't need
   clearing inside a grid, so the clearfix content serves no purpose here;
   removing it entirely is correct, not just a workaround. */
ul.products::before,ul.products::after{content:none!important;display:none!important}
.prod .onsale{position:absolute;top:var(--space-2);left:var(--space-2);z-index:1;display:inline-flex;align-items:center;font-size:11px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;padding:4px 8px;border:var(--border-hairline) solid var(--color-primary);border-radius:var(--radius-sm);background:var(--color-primary);color:var(--color-on-primary)}
.prod-body .woocommerce-loop-product__title{font-family:var(--font-ui);font-size:18px;font-weight:var(--weight-bold);line-height:1.25;letter-spacing:0;margin:0 0 var(--space-2);text-transform:none;color:var(--color-text)}
.prod-body .price{display:block;margin-top:auto;padding-top:var(--space-3);font-family:var(--font-display);font-size:24px;letter-spacing:var(--tracking-display)}
.prod-body .price ins{text-decoration:none;color:var(--color-primary)}
.prod-body .price del{color:var(--color-muted);font-size:16px;margin-right:6px}
.prod .button{font-family:var(--font-ui);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:.06em;font-size:var(--text-label);padding:7px var(--space-3);border-radius:var(--radius-sm);border:var(--border-strong) solid var(--color-border);background:transparent;color:var(--color-text);cursor:pointer;display:block;width:calc(100% - calc(2 * var(--space-4)));margin:0 var(--space-4) var(--space-4);text-align:center;text-decoration:none}
.prod .button:hover{background:var(--color-text);color:var(--color-text-inverse);border-color:var(--color-text)}
.prod .star-rating{margin-bottom:var(--space-2)}
.woocommerce-info,.woocommerce-message,.woocommerce-error{list-style:none;margin:0 0 var(--space-4);padding:var(--space-3) var(--space-4);border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-primary);background:var(--color-primary-tint);border-radius:var(--radius-sm);font-size:var(--text-small)}

/* -------------------------------- tables --------------------------------- */
.table-scroll{overflow-x:auto}
table.data{width:100%;border-collapse:collapse;font-size:var(--text-small);min-width:560px}
table.data caption{text-align:left;font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;padding-bottom:var(--space-3)}
table.data th,table.data td{text-align:left;padding:var(--space-3);border-bottom:var(--border-hairline) solid var(--color-border);vertical-align:top}
table.data thead th{border-bottom:var(--border-strong) solid var(--color-border-strong);font-size:var(--text-label);letter-spacing:var(--tracking-label);text-transform:uppercase}
table.data td.num,table.data th.num{text-align:right;font-family:var(--font-mono);white-space:nowrap}

/* ------------------------------- accordion ------------------------------- */
details.acc{border-bottom:var(--border-hairline) solid var(--color-border);background:var(--color-bg)}
details.acc summary{cursor:pointer;list-style:none;padding:var(--space-4) 0;display:flex;justify-content:space-between;gap:var(--space-4);font-weight:var(--weight-bold);font-size:var(--text-body-lg)}
details.acc summary::-webkit-details-marker{display:none}
details.acc summary::after{content:"+";font-family:var(--font-mono);font-size:22px;line-height:1;color:var(--color-primary)}
details.acc[open] summary::after{content:"\2013"}
details.acc summary:hover{color:var(--color-primary)}
details.acc > div{padding-bottom:var(--space-4);color:var(--color-muted);max-width:70ch}

/* ---------------------------------- blog --------------------------------- */
.post{display:grid;gap:var(--space-3);border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden;background:var(--color-bg)}
.post:hover{border-color:var(--color-primary);box-shadow:var(--shadow-sm)}
.post-body{padding:0 var(--space-4) var(--space-4);display:grid;gap:var(--space-2)}
.post h3{font-family:var(--font-ui);font-size:19px;letter-spacing:0;line-height:1.3;text-transform:none}
.post h3 a{color:var(--color-text);text-decoration:none}
.post h3 a:hover{color:var(--color-primary);text-decoration:underline}

/* -------------------------------- account -------------------------------- */
/* woocommerce/myaccount/dashboard.php + WooCommerce's own (unoverridden)
   myaccount/navigation.php. .acct-layout/.acct-nav/.designs are the source
   design's classes, applied here as a thin bridge over WooCommerce's real
   class names (.woocommerce-MyAccount-navigation, -content, -link, .is-active)
   rather than reproducing them — the endpoints themselves (Orders, Addresses,
   Account details, Logout) are untouched, only their chrome is restyled. */
/* CSS grid here fought WooCommerce's own bundled woocommerce-layout.css /
   woocommerce-smallscreen.css, which both declare float+width on these two
   elements (float:left/width:30% and float:none/width:100%, at matching
   specificity to a plain grid rule) — even after neutralizing float, the
   two elements kept landing in the wrong track/row against those files,
   deep enough into WooCommerce's own CSS that it wasn't worth fully
   reverse-engineering. Flexbox + explicit `order` is far more resistant to
   this kind of legacy-float interference than grid track placement is, and
   !important on the container settles which layout system actually wins. */
/* :has(.woocommerce-MyAccount-navigation) scopes this to the logged-in
   dashboard specifically — body.woocommerce-account is present in BOTH the
   logged-in and logged-out states of /my-account/, and without this guard
   the flex layout also caught the logged-out login page's single-child
   wrapper, shrinking it to its content's natural width (a flex item with
   no explicit flex-grow doesn't stretch to fill the row the way a normal
   block child would) instead of the real ~1200px container width. */
.woocommerce-account .woocommerce:has(.woocommerce-MyAccount-navigation){display:flex!important;flex-wrap:wrap;gap:var(--space-5)}
/* WooCommerce's own woocommerce-layout.css puts a float clearfix
   (content:" ";display:table) on .woocommerce::before/::after, meant only
   to contain floated children. Once this container is display:flex, the
   spec requires that generated content become a REAL flex item (unlike
   table layout, flex never suppresses ::before/::after) — confirmed live,
   it was rendering as an invisible ~48px-wide phantom item ahead of the
   nav column, pushing nav+content ~96px right of center while content
   stayed flush with the container's right edge (a 48px item + the 48px
   gap next to it = 96px of dead space at the start, none at the end).
   That's the actual "skewed to the right" the whole nav+content pair had
   — nothing to do with the outer .wrap/.woocommerce centering, which was
   already symmetric. Nothing floats in a flex layout, so the clearfix
   itself is dead weight here; removing the generated content drops it out
   of flex layout entirely. */
.woocommerce-account .woocommerce:has(.woocommerce-MyAccount-navigation)::before,
.woocommerce-account .woocommerce:has(.woocommerce-MyAccount-navigation)::after{content:none!important;display:none!important}
.woocommerce-account .woocommerce-MyAccount-navigation,
.woocommerce-account .woocommerce-MyAccount-content{float:none!important;width:100%!important;margin:0!important}
@media(min-width:1000px){
  /* Matches the base rule's :has() so this actually out-cascades it instead
     of losing to it on specificity (both now equally specific; this one
     wins because it comes later in the source, same as before :has() was
     added — confirmed live: without the matching :has() here, nav+content
     stayed wrapped/stacked at desktop widths instead of going side by side). */
  .woocommerce-account .woocommerce:has(.woocommerce-MyAccount-navigation){flex-wrap:nowrap;gap:var(--space-6);align-items:flex-start}
  .woocommerce-account .woocommerce-MyAccount-navigation{order:1;flex:0 0 240px!important;width:240px!important}
  .woocommerce-account .woocommerce-MyAccount-content{order:2;flex:1 1 auto!important;width:auto!important;min-width:0}
}
.woocommerce-MyAccount-navigation{list-style:none;margin:0;padding:0}
.woocommerce-MyAccount-navigation ul{list-style:none;margin:0;padding:0;display:grid;gap:2px;border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden}
.woocommerce-MyAccount-navigation-link a{display:block;padding:12px var(--space-3);font-size:var(--text-small);font-weight:var(--weight-bold);text-decoration:none;color:var(--color-text);background:var(--color-bg);border-bottom:var(--border-hairline) solid var(--color-border)}
.woocommerce-MyAccount-navigation-link:last-child a{border-bottom:0}
.woocommerce-MyAccount-navigation-link a:hover{background:var(--color-surface);color:var(--color-primary)}
.woocommerce-MyAccount-navigation-link.is-active a{background:var(--color-text);color:#fff}
.woocommerce-MyAccount-navigation-link--customer-logout a{color:var(--color-primary)}
.woocommerce-MyAccount-content{min-width:0}
.woocommerce-MyAccount-content > p:first-child{margin-top:0}
/* Real dashboard/order/address/account-details/downloads endpoint content —
   all still WooCommerce's own unoverridden markup, just given the same
   card/table/form language used everywhere else in the theme. */
.woocommerce-MyAccount-content table.shop_table,.woocommerce-MyAccount-content table.woocommerce-orders-table{width:100%;border-collapse:collapse;font-size:var(--text-small)}
.woocommerce-MyAccount-content table.shop_table th,.woocommerce-MyAccount-content table.shop_table td,.woocommerce-MyAccount-content table.woocommerce-orders-table th,.woocommerce-MyAccount-content table.woocommerce-orders-table td{text-align:left;padding:var(--space-3);border-bottom:var(--border-hairline) solid var(--color-border);vertical-align:top}
.woocommerce-MyAccount-content table.shop_table thead th,.woocommerce-MyAccount-content table.woocommerce-orders-table thead th{border-bottom:var(--border-strong) solid var(--color-border-strong);font-size:var(--text-label);letter-spacing:var(--tracking-label);text-transform:uppercase}
.woocommerce-MyAccount-content .woocommerce-Addresses{display:grid;gap:var(--space-4);grid-template-columns:1fr}
@media(min-width:700px){.woocommerce-MyAccount-content .woocommerce-Addresses{grid-template-columns:repeat(2,1fr)}}
.woocommerce-MyAccount-content .woocommerce-Address{border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);padding:var(--space-4)}
.woocommerce-MyAccount-content .woocommerce-Address-title{display:flex;justify-content:space-between;align-items:center;gap:var(--space-3);margin-bottom:var(--space-2)}
.acct-intro{margin-bottom:var(--space-5)}
.acct-cards .card > .sec-head:first-child{margin-top:0}
.designs{display:grid;gap:var(--space-3);grid-template-columns:repeat(2,1fr)}
@media(min-width:1000px){.designs{grid-template-columns:repeat(4,1fr)}}
.res-links{list-style:none;margin:0;padding:0;display:grid;gap:2px;border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden}
.res-links a{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);padding:12px var(--space-3);font-size:var(--text-small);font-weight:var(--weight-bold);text-decoration:none;color:var(--color-text);border-bottom:var(--border-hairline) solid var(--color-border);background:var(--color-bg)}
.res-links a:last-child{border-bottom:0}
.res-links a:hover{background:var(--color-surface);color:var(--color-primary)}
.res-links svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;flex:0 0 auto}

/* -------------------------------- auth pages ----------------------------- */
/* /my-account/ (and any other page.html-templated page) wraps its content in
   <!-- wp:post-content {"layout":{"type":"constrained"}} /-->, which — unlike
   a generic group block's ">* " constraint — applies max-width directly to
   .entry-content itself via real (non-:where()) specificity, so a .wrap
   class on a descendant can't out-specificity it and wouldn't help anyway
   since the squeeze is on entry-content's own box, not a child's. Confirmed
   live: it capped the whole auth layout to 720px, compressing the 420px
   fixed-width auth-card and the copy column together into far less room
   than either needs — the "skewed, doesn't fit the page" look. Scoped to
   pages that actually contain .auth so normal prose pages (About, FAQ,
   Portfolio) keep their intended comfortable reading width. */
/* Same fix, but scoped to the my-account-section class templates/page-my-
   account.html now carries (added because :has(.auth) only ever matched
   the logged-out login/signup state — the logged-in dashboard has no
   .auth element at all, so it was still hitting the exact same 720px
   squeeze the login page had, just via the same page's other state). */
.my-account-section .entry-content,.entry-content:has(.auth){max-width:none}
/* WooCommerce's own woocommerce-blocktheme.css (loaded automatically on any
   block theme, not something this theme enqueues) sets
   ".woocommerce-account main .woocommerce{max-width:1000px}" — and the
   [woocommerce_my_account] shortcode's real output wraps itself in exactly
   that class, inside a real <body class="...woocommerce-account..."> and a
   real <main>. Confirmed live: with the entry-content squeeze above
   already fixed, this was the *next* layer still capping the page — .auth
   stuck at 792px regardless of viewport, and their rule's own `main`
   element selector out-specified a class-only override. Matched with an
   equally-specific selector (also scoped to the my-account page, not
   overridden globally, since cart/checkout carry this same class and
   haven't been asked for here) rather than reaching for !important. */
.woocommerce-account main .my-account-section .woocommerce{max-width:none}
/* Widened to match every other page's real container (.wrap's own
   max-width, the same one category/product pages use) instead of an
   arbitrary narrower cap — the "boxed-in, too narrow" look. Both columns
   are now fr-based (not a fixed 420px card) so they genuinely expand
   together as the viewport grows; line length on the copy side is capped
   separately (see .auth-copy p below) so wide columns don't read edge-to-
   edge. */
.auth{display:grid;gap:var(--space-6);align-items:start;max-width:calc(var(--container-max) + 64px);margin:0 auto}
@media(min-width:1000px){.auth{grid-template-columns:1.2fr 1fr;gap:var(--space-8)}}
.auth-copy p,.auth-copy h1{max-width:60ch}
/* min-width:0: .auth-card is also a CSS Grid item wherever it sits beside
   a sibling column (e.g. the car-wraps quote form beside its photo
   gallery, in the SAME .wrap.grid.g2 .veh-gallery-col already needed this
   fix for) - without it, its own space-6 (48px each side) padding plus
   its form's content floors the grid TRACK itself at that combined width,
   which then re-widens every other item sharing that track too. Confirmed
   live: at 320px this was still forcing ~16px of page-wide horizontal
   overflow even after .veh-gallery-col's own fix. Harmless everywhere
   .auth-card has enough room already (the account-access login/signup
   page, where it isn't squeezed beside anything). */
.auth-card{border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-primary);border-radius:var(--radius-md);padding:var(--space-6);background:var(--color-bg);min-width:0}
.auth-card form{max-width:480px}
/* Forminator's Select2-powered dropdowns (the "What do you need?" field on
   every Forminator form - car wraps, contact, services, account-access
   signup) set a fixed inline pixel width on .select2-container via JS at
   init time (e.g. style="width: 368.4px") and never recompute it on
   resize - Select2 is not responsive by default. Confirmed live: on a
   360px-wide screen that inline width alone (368px, 8px wider than the
   viewport) was forcing the ENTIRE form, and therefore the CSS Grid
   column/track it sits in beside the car-wraps photo gallery, into
   horizontal overflow - removing just this one inline style fixed the
   whole page. !important is required here because it's overriding an
   inline style, not a stylesheet rule; this is the standard, widely-used
   fix for Select2's own non-responsive width, applied globally since
   every Forminator form site-wide has the same field. */
.select2-container{width:100% !important;max-width:100%}
/* Car-wraps page: "Recent work" photo gallery, now its own full-width
   section/row (previously squeezed into the second column beside the quote
   form, which capped every thumbnail at roughly half the page width) - see
   patterns/car-wraps-section.php. Reuses .grid.g3 for the same 1/2/3-column
   responsive breakpoints every other multi-column grid on the site already
   uses (mobile/700px/1000px); only .veh-gallery__item's overflow-clip,
   hover-zoom and figcaption are new here. */
.veh-gallery__item{display:block;overflow:hidden;border-radius:var(--radius-sm);min-width:0}
.veh-gallery__item img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;transition:transform var(--transition-base)}
.veh-gallery__item:hover img{transform:scale(1.08)}
.veh-gallery__item figcaption{padding:var(--space-3) 0 0;font-size:var(--text-small);font-weight:var(--weight-bold);color:var(--color-text)}
/* -------------------------- car wrap price estimator ---------------------- */
/* Same 3fr/2fr configurator-plus-sticky-summary shape as .pdp (the real
   single-product layout) - a form column of swatch groups on the left, a
   sticky live-updating price card on the right, collapsing to one column
   under 700px. See assets/js/site.js#initWrapCalculator for the live math;
   every number here is a static placeholder until that script runs the
   first update() on load. */
.wrap-calc{display:grid;gap:var(--space-5);grid-template-columns:minmax(0,1fr)}
@media(min-width:700px){.wrap-calc{grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:var(--space-6);align-items:start}}
.wrap-calc__result{border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-primary);border-radius:var(--radius-md);padding:var(--space-5);background:var(--color-bg);min-width:0}
@media(min-width:700px){.wrap-calc__result{position:sticky;top:24px}}
.wrap-calc__price{display:block;font-family:var(--font-display);font-size:40px;letter-spacing:var(--tracking-display);color:var(--color-primary);margin:var(--space-2) 0}
.wrap-calc__exact summary{cursor:pointer;color:var(--color-primary);font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-small);letter-spacing:var(--tracking-label);text-transform:uppercase}
.wrap-calc__exact[open] summary{margin-bottom:var(--space-1)}
/* ---- account access: CSS-only :target tabs ---- */
.tabs{display:flex;gap:2px;border-bottom:var(--border-strong) solid var(--color-border-strong);margin-bottom:var(--space-5)}
.tab{display:inline-flex;align-items:center;gap:8px;padding:14px var(--space-4);font-family:var(--font-ui);font-size:var(--text-small);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-muted);text-decoration:none;border:var(--border-strong) solid transparent;border-bottom:0;border-radius:var(--radius-sm) var(--radius-sm) 0 0;margin-bottom:calc(-1 * var(--border-strong));background:transparent}
.tab:hover{color:var(--color-primary);text-decoration:none}
.tab svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;flex:0 0 auto}
.tab--on{color:var(--color-text);background:var(--color-bg);border-color:var(--color-border-strong);box-shadow:inset 0 3px 0 var(--color-primary)}
.ap{display:none}
#login{display:block}
#signup:target{display:block}
body:has(#signup:target) #login{display:none}
body:has(#signup:target) .tab[href="#login"]{color:var(--color-muted);background:transparent;border-color:transparent;box-shadow:none}
body:has(#signup:target) .tab[href="#signup"]{color:var(--color-text);background:var(--color-bg);border-color:var(--color-border-strong);box-shadow:inset 0 3px 0 var(--color-primary)}
.tab-switch{margin-top:var(--space-4);padding-top:var(--space-4);border-top:var(--border-hairline) solid var(--color-border);font-size:var(--text-small)}
/* reseller — certificate panel revealed by the Account Type radio, CSS only.
   NOTE: with Forminator handling the real signup form, Forminator's own
   conditional-logic engine drives this reveal in production; this rule is
   kept for the hand-authored preview markup in patterns/reseller-signup.php
   and for any non-Forminator fallback markup. */
.reseller-fields{display:none}
.signup-form:has(#at-reseller:checked) .reseller-fields{display:grid;gap:var(--space-3);margin-bottom:var(--space-4)}
.signup-form:has(#at-reseller:checked) .reseller-note{display:block}
.reseller-note{display:none;margin-bottom:var(--space-4)}
.wholesale{border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-accent);border-radius:var(--radius-sm);background:var(--color-surface);padding:var(--space-4)}
.wholesale .amt{font-family:var(--font-mono);font-weight:var(--weight-bold)}

/* ------------------------------ misc utilities --------------------------- */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.icon{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;flex:0 0 auto}
.notice{border:var(--border-hairline) solid var(--color-border);border-top:var(--border-accent) solid var(--color-primary);background:var(--color-primary-tint);padding:var(--space-3) var(--space-4);border-radius:var(--radius-sm);font-size:var(--text-small)}
.map-ph{aspect-ratio:16/9;background:var(--color-surface);border:var(--border-strong) solid var(--color-border-strong);display:grid;place-items:center;text-align:center;padding:var(--space-4);gap:var(--space-2)}
.divider{height:1px;background:var(--color-border);margin:var(--space-5) 0}
/* 2-column layout: a large image on the left (roughly half the page width
   at tablet/desktop — significantly bigger than the old 3-column layout's
   image, which shared its column with only ~28% of the row) and a tabbed
   "Product Options" / "Shipping & Turnaround" column on the right. Source
   order is gallery then tabs-col, already the correct mobile stacking
   order (image, then tabs), so no reordering is needed at the single-
   column breakpoint. */
.pdp{display:grid;gap:var(--space-5);grid-template-columns:minmax(0,1fr)}
/* Left image column stays the dominant side — 55/45 at tablet/small
   desktop, 60/40 at large desktop, per spec — while still giving the
   right (tabs) column real width of its own instead of the old 3-column
   layout's ~28%-of-row sliver. A prior pass here had this flipped (right
   column wider than left, 45/55 then 40/60) chasing "the right column
   looks condensed" by over-correcting past balanced into right-dominant;
   the actual fix for "condensed" turned out to be .pdp-tabs-col's own
   float/width:48% below, not the track ratio — so the track can go back
   to matching the requested left-heavy split. */
@media(min-width:700px){.pdp{grid-template-columns:minmax(0,11fr) minmax(0,9fr);gap:var(--space-6);align-items:start}}
@media(min-width:1280px){.pdp{grid-template-columns:minmax(0,3fr) minmax(0,2fr)}}
.pdp-gallery{display:grid;gap:var(--space-2)}
/* WooCommerce's own legacy woocommerce-layout.css has
   ".woocommerce div.product div.images{float:left;width:48%}" — a classic-
   theme-era rule sized to sit next to a same-width summary column in a
   float layout. .woocommerce-product-gallery carries the "images" class,
   and .pdp carries both "woocommerce" (via body) and "product" ancestry,
   so it matched and capped the real gallery to 48% of its grid column
   width, floated, with the rest of the column sitting empty — the visual
   "extra narrow columns" effect this was reported as. Same class of bug
   already fixed once this session for ul.products li.product; same fix
   here: neutralize the float/width so the gallery fills its real grid
   column. */
.pdp-gallery .woocommerce-product-gallery{float:none!important;width:100%!important;max-width:100%!important}
/* Same fixed-ratio treatment as the shop/category loop cards
   (.prod-media img.wp-post-image above) - the single product page's main
   photo is another bare <img class="wp-post-image"> from
   external_image_handler.php's get_gallery_img_html(), one per product,
   each a different natural aspect ratio, so without this the hero image
   was a different height on every product page. */
.pdp-gallery img.wp-post-image{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-sm);background:var(--placeholder)}
/* The other half of that same legacy pair: ".woocommerce div.product div.
   summary{float:right;width:48%;clear:none}". .pdp-tabs-col carries the
   real "summary entry-summary" classes (kept so WooCommerce's own
   summary-related selectors/plugins still recognize it) and matched this
   too — confirmed live, it was capping the tabs column to 48% of its
   already-widened 60% grid track (≈332px of a 691px track), which is
   exactly why the right column still looked condensed after only the
   grid-track width was increased. Missed in the earlier gallery fix
   because that pass only checked the image side of this float pair. */
.pdp-tabs-col{float:none!important;width:100%!important;max-width:100%!important}
.pdp-thumbs{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-2)}
@media(min-width:700px){
  /* Plain CSS sticky: pinned while the taller right-hand tab column is
     still scrolling past, releasing naturally once the visitor reaches the
     bottom of .pdp (i.e. once the tab interface has been fully scrolled) —
     no JS needed, this is sticky's normal behavior inside a grid row once
     the sticky item's own column is shorter than its sibling. Replaces an
     earlier "unstick as soon as options are selected" behavior (removed
     per updated spec: it isn't the same as sticking through the whole tab
     interface). */
  .pdp-gallery{position:sticky;top:190px;align-self:start}
}
.pdp-gallery .woocommerce-product-gallery{max-width:none}
.status{display:inline-flex;align-items:center;gap:8px;flex:0 0 auto;white-space:nowrap;font-size:var(--text-small);font-weight:var(--weight-bold);padding:5px 10px;border-radius:var(--radius-sm);border:var(--border-hairline) solid}
.status .dot{width:7px;height:7px;border-radius:var(--radius-pill);background:currentColor}
.status--ok{background:var(--color-success-tint);border-color:#bfe6cd;color:#12833c}
.status--warn{background:var(--color-warning-tint);border-color:#ecd9a3;color:#8a6100}
.status--err{background:var(--color-error-tint);border-color:#f3c4c4;color:#b62222}
.tiers .opt-title{font-family:var(--font-display);font-size:22px;letter-spacing:var(--tracking-display)}
.tier-top{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:6px}
.breakdown{background:var(--color-surface);border:var(--border-hairline) solid var(--color-border);border-radius:var(--radius-sm);padding:var(--space-4);display:grid;gap:var(--space-2)}
.bd-row{display:flex;justify-content:space-between;gap:var(--space-3);font-size:var(--text-small)}
.bd-row span:last-child{font-family:var(--font-mono);font-weight:var(--weight-bold);white-space:nowrap}
.bd-row--muted{color:var(--color-muted)}
.bd-row--credit span:last-child{color:var(--color-success)}
.bd-rule{height:1px;background:var(--color-border-strong);margin:var(--space-1) 0}
.bd-total{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-3)}
.bd-total .amt{font-family:var(--font-display);font-size:28px;letter-spacing:var(--tracking-display)}
/* Single-product template additions not in the source design (WooCommerce's
   own markup needs light bridging, not a full override, to keep every core
   hook — including the image-handler plugin's swatch/upload injections —
   working exactly as before). */
.pdp-tabs-col .product_title{margin-bottom:var(--space-2);font-size:32px}
.pdp-tabs-col .woocommerce-product-rating{margin-bottom:var(--space-3)}
.pdp-tabs-col .price{display:block;font-family:var(--font-display);font-size:32px;letter-spacing:var(--tracking-display);margin:0 0 var(--space-3)}
.pdp-tabs-col .price ins{text-decoration:none;color:var(--color-primary)}
.pdp-tabs-col .price del{color:var(--color-muted);font-size:20px;margin-right:8px}
.pdp-tabs-col .woocommerce-product-details__short-description{color:var(--color-muted);margin-bottom:var(--space-5)}
.pdp-tabs-col .cart{margin-top:var(--space-4)}
.pdp-tabs-col table.woocommerce-product-attributes{width:100%;border-collapse:collapse;margin-top:var(--space-4)}
.pdp-tabs-col table.woocommerce-product-attributes th,.pdp-tabs-col table.woocommerce-product-attributes td{text-align:left;padding:8px 0;border-bottom:var(--border-hairline) solid var(--color-border);font-size:var(--text-small)}
/* ---- product-options / shipping-turnaround: CSS-only :target tabs ----
   Same #id:target technique as the account-access.php Log in / Create
   account tabs (.tabs/.tab/.tab--on, already styled) — no new JS. Both
   panels are real siblings inside <form class="cart"> (see functions.php
   impress_pdp_options_panel_open/_close_and_shipping_panel), so the
   Shipping & Turnaround radios still submit with the real add-to-cart POST
   even while their panel is display:none. */
.pdp-tabpanel{display:none}
#pdp-options{display:block}
#pdp-shipping:target{display:block}
body:has(#pdp-shipping:target) #pdp-options{display:none}
body:has(#pdp-shipping:target) .pdp-tabnav .tab[href="#pdp-options"]{color:var(--color-muted);background:transparent;border-color:transparent;box-shadow:none}
body:has(#pdp-shipping:target) .pdp-tabnav .tab[href="#pdp-shipping"]{color:var(--color-text);background:var(--color-bg);border-color:var(--color-border-strong);box-shadow:inset 0 3px 0 var(--color-primary)}
.pdp-tabnav{margin:var(--space-5) 0 var(--space-4)}
#pdp-shipping fieldset:last-of-type{margin-bottom:var(--space-3)}
/* The real Add to Cart button renders inside #pdp-options structurally
   (WooCommerce's core template gives no hook point to stop that — see the
   long docblock on impress_pdp_next_tab_prompt() in functions.php) and
   impress_pdp_relocate_add_to_cart_js() moves it into #pdp-shipping's
   .eih-shipping-add-to-cart-slot at the very end of the page load. Hiding
   it here by class (not by relying on #pdp-options{display:none}, which
   only ever applies once #pdp-shipping is the active tab) closes the gap
   between first paint and that script running, so it's never visible in
   the wrong tab even for a moment. Once moved, this selector no longer
   matches it (it's not a descendant of #pdp-options anymore), so the
   button displays normally whenever #pdp-shipping itself is visible. */
#pdp-options .single_add_to_cart_button{display:none!important}
.eih-next-tab-prompt{margin-top:var(--space-4)}
.eih-shipping-add-to-cart-slot{margin-top:var(--space-4)}
.eih-shipping-add-to-cart-slot .single_add_to_cart_button{width:100%}
.pdp-meta{margin-top:0;font-size:var(--text-small);color:var(--color-muted)}
.pdp-meta a{color:inherit;text-decoration:underline}
/* ---- related products: real WooCommerce data, carousel presentation ----
   Markup from woocommerce/single-product/related.php (arrows + a scrolling
   track wrapping the same real content-product.php cards used everywhere
   else). Arrow click-to-scroll is assets/js/site.js initCarousel(); with
   JS disabled .carousel-track is still a normal horizontally-scrollable/
   swipeable row (scroll-snap keeps cards aligned either way), so nothing
   here depends on JS to be usable. */
.related.products{max-width:calc(var(--container-max) + 64px);margin:0 auto;padding:var(--section-mobile) var(--space-4)}
@media(min-width:700px){.related.products{padding:var(--section-tablet) var(--space-4)}}
@media(min-width:1000px){.related.products{padding:var(--section-desktop) var(--space-4)}}
.related.products .sec-head{border-top:var(--border-accent) solid var(--color-border-strong);padding-top:var(--space-3);margin-bottom:var(--space-5);display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-4)}
.related.products .sec-head h2{font-family:var(--font-display);letter-spacing:var(--tracking-display);line-height:var(--leading-display);text-transform:uppercase;font-size:28px;margin:0}
.carousel-arrows{display:flex;gap:8px;flex:0 0 auto}
.carousel-arrow{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border:var(--border-strong) solid var(--color-border);border-radius:var(--radius-pill);background:var(--color-bg);cursor:pointer;color:var(--color-text);transition:border-color var(--transition-fast),color var(--transition-fast)}
.carousel-arrow svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2}
.carousel-arrow:hover{border-color:var(--color-primary);color:var(--color-primary)}
.carousel-arrow:disabled{opacity:.35;cursor:not-allowed;border-color:var(--color-border);color:var(--color-text)}
.carousel-track{overflow-x:auto;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;padding-bottom:4px;scrollbar-width:none;scroll-snap-type:x proximity}
.carousel-track::-webkit-scrollbar{display:none}
.carousel-track ul.products{display:flex;flex-wrap:nowrap;gap:var(--space-4)}
.carousel-track ul.products li.product{scroll-snap-align:start;flex:0 0 220px}
@media(min-width:560px){.carousel-track ul.products li.product{flex-basis:250px}}
@media(min-width:1000px){.carousel-track ul.products li.product{flex-basis:270px}}
/* ---- Description / Additional information / Reviews tabs ----
   Real markup + real tab-switching JS from WooCommerce's own single-
   product-tabs.php + frontend/woocommerce.js (unchanged) — this only
   restyles it. ul.wc-tabs shares the word "tabs" with the unrelated
   account-access .tabs component, so every rule here is scoped under
   .wc-tabs-wrapper to stay independent of it. */
.woocommerce-tabs.wc-tabs-wrapper{margin-top:var(--space-7)}
.woocommerce-tabs.wc-tabs-wrapper ul.wc-tabs{display:flex;flex-wrap:wrap;gap:2px;list-style:none;margin:0 0 var(--space-5);padding:0;border-bottom:var(--border-strong) solid var(--color-border-strong)}
.woocommerce-tabs.wc-tabs-wrapper ul.wc-tabs li{margin:0}
.woocommerce-tabs.wc-tabs-wrapper ul.wc-tabs li a{display:inline-flex;align-items:center;padding:14px var(--space-4);font-family:var(--font-ui);font-size:var(--text-small);font-weight:var(--weight-bold);letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--color-muted);text-decoration:none;border:var(--border-strong) solid transparent;border-bottom:0;border-radius:var(--radius-sm) var(--radius-sm) 0 0;margin-bottom:calc(-1 * var(--border-strong))}
.woocommerce-tabs.wc-tabs-wrapper ul.wc-tabs li a:hover{color:var(--color-primary)}
.woocommerce-tabs.wc-tabs-wrapper ul.wc-tabs li.active a{color:var(--color-text);background:var(--color-bg);border-color:var(--color-border-strong);box-shadow:inset 0 3px 0 var(--color-primary)}
.woocommerce-tabs.wc-tabs-wrapper .woocommerce-Tabs-panel{max-width:860px;font-size:var(--text-body);line-height:1.6}
.woocommerce-tabs.wc-tabs-wrapper .woocommerce-Tabs-panel h2{font-family:var(--font-display);letter-spacing:var(--tracking-display);text-transform:uppercase;font-size:24px;margin:0 0 var(--space-4)}
.woocommerce-tabs.wc-tabs-wrapper table.shop_attributes{width:100%;border-collapse:collapse;font-size:var(--text-small)}
.woocommerce-tabs.wc-tabs-wrapper table.shop_attributes th,.woocommerce-tabs.wc-tabs-wrapper table.shop_attributes td{text-align:left;padding:var(--space-2) 0;border-bottom:var(--border-hairline) solid var(--color-border);vertical-align:top}
.woocommerce-tabs.wc-tabs-wrapper table.shop_attributes th{width:220px;font-weight:var(--weight-bold)}
.woocommerce-tabs.wc-tabs-wrapper #reviews .comment-form input[type=text],.woocommerce-tabs.wc-tabs-wrapper #reviews .comment-form input[type=email],.woocommerce-tabs.wc-tabs-wrapper #reviews .comment-form textarea{max-width:480px}
.woocommerce-tabs.wc-tabs-wrapper #reviews .star-rating{color:var(--color-primary)}
/* WooCommerce Blocks' own product grid (e.g. the empty-cart cross-sell
   block) ships a -8px/-8px margin on .wc-block-grid__products as a gutter-
   compensation technique, meant to be absorbed by matching padding on an
   ancestor - this theme's page layouts never provide that padding at this
   depth, so the negative margin bled straight past the container edge and
   caused a real (if small, ~8px) horizontal-scroll overflow on mobile.
   Zeroing it here is a harmless ~8px inward shift, not a visual regression
   - WooCommerce core markup, not a theme file, so this is the safe place
   to correct it rather than patching WooCommerce itself. */
.wc-block-grid__products{margin-left:0;margin-right:0}
.spec-list{list-style:none;margin:0;padding:0;display:grid;gap:var(--space-2)}
.spec-list li{display:flex;justify-content:space-between;gap:var(--space-3);font-size:var(--text-small);border-bottom:var(--border-hairline) solid var(--color-border);padding-bottom:8px}
.spec-list b{font-weight:var(--weight-bold)}

/* -------------------- accessibility: focus-visible rings ----------------- */
a:focus-visible,button:focus-visible,.btn:focus-visible,.wp-block-button__link:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{
  outline:var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset:var(--focus-ring-offset);
}
.vb-skip-link{position:absolute;left:8px;top:-60px;z-index:100;background:var(--color-text);color:#fff;padding:10px 16px;border-radius:var(--radius-sm);text-decoration:none;font-weight:var(--weight-bold);transition:top var(--transition-fast)}
.vb-skip-link:focus{top:8px}

/* ---------------------- mobile navigation panel -------------------------- */
@media(max-width:999px){
  .has-js-nav .mainnav{position:absolute;left:0;right:0;top:100%;background:var(--color-bg);border-top:var(--border-hairline) solid var(--color-border);border-bottom:var(--border-strong) solid var(--color-border-strong);box-shadow:var(--shadow-md);opacity:0;visibility:hidden;transform:translateY(-10px);transition:opacity var(--transition-base),transform var(--transition-base),visibility var(--transition-base);z-index:45}
  .has-js-nav .mainnav.is-open{opacity:1;visibility:visible;transform:translateY(0)}
  .mainnav-list{flex-direction:column;gap:0;overflow:visible;max-height:min(70vh,520px);overflow-y:auto}
  .mainnav-list > li > a{padding:14px var(--space-4);border-bottom:var(--border-hairline) solid var(--color-border);font-size:var(--text-body)}
  .mainnav-list > li:last-child > a{border-bottom:0}
  .mega{display:none}
}
@media(prefers-reduced-motion:reduce){.has-js-nav .mainnav{transition:none}}
body.nav-open{overflow:hidden}
@media(min-width:1000px){body.nav-open{overflow:visible}}

/* ------------------------- responsive refinements ------------------------ */
@media(max-width:860px){
  .ba{grid-template-columns:1fr}
  .steps{gap:var(--space-3)}
}
@media(max-width:600px){
  .g4{grid-template-columns:1fr}
  .cat-tiles{grid-template-columns:1fr}
  .stats{grid-template-columns:1fr}
  .logo-mq .mq__group{gap:var(--space-4);padding:var(--space-3) var(--space-4)}
  .designs{grid-template-columns:1fr}
  .toolbar select,.toolbar .woocommerce-ordering select{min-width:0;width:100%}
  .toolbar .row,.toolbar .woocommerce-ordering{width:100%}
  .h-hero{font-size:30px}
  /* overflow-wrap:break-word only affects how content overflows AFTER an
     element's own size is already determined - it does NOT reduce that
     element's min-content contribution to a flex/grid parent's sizing
     (the classic "word-break doesn't work in a flex/grid item" gap).
     `anywhere` is the value that also counts toward the specified/min-
     content size, letting a heading genuinely shrink instead of flooring
     its grid column - confirmed live: the car-wraps gallery's "Vehicles
     we've wrapped" heading, sitting in a narrow grid column beside the
     quote form, needed exactly this to stop forcing that column (and so
     the whole page) into horizontal overflow at 320px. break-word alone
     never caught this because nothing on the site previously put a
     heading inside a column this narrow. */
  .display{overflow-wrap:anywhere}
  .sec-head{gap:var(--space-3)}
  .hero-facts{gap:var(--space-3) var(--space-4)}
  .hero-fact b{font-size:22px}
  .stat b{font-size:32px}
  .foot-legal{flex-direction:column;gap:var(--space-2)}
  .res-strip{align-items:flex-start}
  .opts{grid-template-columns:1fr}
  .tabs{gap:0}
  .tab{padding:12px var(--space-3);font-size:var(--text-label)}
}
@media(max-width:699px){
  .header-row{gap:var(--space-2) var(--space-3)}
  /* Login/cart icons + hamburger sit next to the logo on mobile (per
     request), with search moved to its own full-width row below instead
     of sharing the top row — there isn't room for a usable text input
     alongside the logo and three tap targets at 375px. Real DOM order is
     logo, search, util-nav, navtoggle (see parts/header.html, and this is
     still the order desktop uses unchanged); giving search a higher
     `order` here is enough to move it after the other two without
     touching their relative order — items that don't set `order` keep
     their default (0) and simply keep their existing DOM order relative
     to each other, so wordmark -> util-nav -> navtoggle still comes out
     in that sequence, just with search sorted after all three. */
  .site-search{order:1;flex:1 1 100%}
  /* flex-basis:100% on search means it can't fit on line 1 next to the
     logo+icons (a 100%-wide item never shares a line), so it wraps to
     line 2 by itself — which is what pushes util-nav+navtoggle onto line
     1 alongside the logo instead of their own line 3, the mobile layout
     used before this change. margin-left:auto (base rule above) then
     pushes .util-nav — and .navtoggle right after it, since neither has
     an auto margin of its own — to line 1's right edge as one grouped,
     right-aligned cluster: login icon, cart icon, hamburger. */
  .util-nav{gap:var(--space-2) var(--space-3);font-size:var(--text-label)}
  .topbar{font-size:var(--text-label)}
}
.table-scroll{-webkit-overflow-scrolling:touch}
/* WooCommerce Blocks' own ProductDetails component (cart/checkout "order
   summary" line-item description) renders every item_data entry - Size,
   Shape, Paper Stock, Turnaround, Shipping, etc. - as inline spans joined by
   a literal " / " separator baked into its compiled JS, with no PHP/theme
   hook to change that structure. Each separator is already aria-hidden
   (decorative only, screen readers already skip it), so forcing every
   option onto its own line and hiding the now-redundant separator is a
   pure CSS change: nothing here touches what data is shown, only how it's
   laid out, and it applies identically to the cart page and the checkout
   "Order summary" since both render through this same component. */
.wc-block-components-product-details>span{display:block}
.wc-block-components-product-details>span+span{margin-top:2px}
.wc-block-components-product-details>span>span[aria-hidden="true"]{display:none}

@media print{.site-header,.topbar,.site-footer,.vb-skip-link{display:none}}