/* ==========================================================================
   BLOGS SUB-PROJECT — component styles for /blogs article + hub pages.
   ------------------------------------------------------------------------
   This file is loaded IN ADDITION to the root site's own styles.css shim
   (variables/base/header-footer/layout/page-legal/responsive) — it does
   NOT replace or duplicate anything already defined there. It only adds
   the components /blogs needs that don't exist anywhere else on the site:
   the two-column article+sidebar shell, the sidebar widgets (Recent Posts /
   Trending Topics), the blog hub post grid, the reused navy "proof block"
   card treatment, breadcrumbs, and long-form article typography.

   It intentionally reuses the SAME design tokens as the rest of the site
   (--primary, --accent-blue, --text-main, --radius-*, --container-*, etc.
   from assets/css/variables.css) rather than introducing new ones, so a
   blog page looks like a natural extension of the main site — not a
   re-skinned sub-brand the way /locations is.

   CACHE-BUSTING: this file has its OWN independent "?v=N" query string,
   separate from the root site's styles.css?v=31 shim. Bump the "?v=N" on
   EVERY <link> tag that references blogs.css (every file under /blogs/)
   whenever this file's content changes — do not touch the root shim's
   version number for a blogs-only change, and don't forget every blog
   page's own <link> tag either. Current version: v=1.
   ========================================================================== */

/* ---------- Breadcrumbs (root site has no breadcrumb component) ----------
   The site header is position:fixed (see header-footer.css) with no
   global body/main top-padding rule — every page is expected to supply
   its own clearance via its first element (normally .hero's large
   clamp() top padding). Because the breadcrumb sits BEFORE the hero as
   the first thing inside <main>, it needs that same clearance itself,
   sized to the fixed header's real height (~87px desktop / ~115px on
   the narrowest phones, measured via Playwright) plus headroom. */
.breadcrumb-bar {
    /* Below the 1350px nav-wrap breakpoint the header also shows its
       mobile .header-topbar row, making it taller (measured ~115-129px)
       than the desktop-only navbar row above 1350px (measured ~87px) —
       so, unlike most fluid spacing on this site, this needs a real
       breakpoint switch rather than a vw-scaled clamp(). */
    padding: 145px var(--container-padding) 0;
    max-width: var(--container-max);
    margin: 0 auto;
}
@media (min-width: 1360px) {
    .breadcrumb-bar { padding-top: 108px; }
}
.breadcrumb-bar ol {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: 0.9rem;
    color: var(--text-muted);
    padding: 0;
    margin: 0;
}
.breadcrumb-bar a { color: var(--text-muted); font-weight: 600; }
.breadcrumb-bar a:hover { color: var(--primary-hover); }
.breadcrumb-bar li[aria-current] { color: var(--text-main); font-weight: 700; }
.breadcrumb-bar li:not(:last-child)::after { content: '/'; margin-left: 8px; color: var(--border); }

/* .hero's own huge top padding exists to clear the fixed header when the
   hero is a page's first element. On /blogs pages the breadcrumb above
   it already does that job, so knock the hero's top padding down to a
   normal section gap instead of stacking both clearances. */
.breadcrumb-bar + .hero.hero--compact { padding-top: clamp(20px, 3vw, 36px); }

/* ---------- Blog article hero: align its columns with .blog-shell below ----------
   layout.css's shared .hero uses flex with .hero-content/.hero-visual
   basis values (420px/380px) tuned for the homepage's photo hero — on a
   blog article that gave the Quick Answer card a noticeably wider column
   than the sidebar below it, so the two sections' right edges didn't
   line up (a page that otherwise reads as one consistent column grid).
   Using the EXACT same column template as .blog-shell further down
   guarantees the Quick Answer card's right edge lines up with the
   sidebar's right edge, since both are centered under the same
   var(--container-max) with the same gap. Scoped to blog pages only
   (blogs.css isn't loaded anywhere else), so the homepage/service pages'
   hero is completely untouched. */
.hero.hero--compact {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
    align-items: center;
}
.hero.hero--compact .hero-content,
.hero.hero--compact .hero-visual {
    min-width: 0;
}
@media (max-width: 1040px) {
    .hero.hero--compact { grid-template-columns: 1fr; text-align: center; }
}

/* Hero CTA buttons: layout.css's shared .hero-actions is a wrapping flex
   row with align-items:center, so a long button ("Get a Free Switchboard
   Assessment") and a much shorter one ("Call Now: ...") wrap onto
   separate lines but each keeps its own natural width — same row, two
   different widths, reads as mismatched rather than as one deliberate
   button group. Side by side and equal width on desktop (matching the
   two-CTA pattern used elsewhere on the site, e.g. the homepage's
   "Book a Service Now" / "Call Now" pair), stacking to full-width only
   once the hero itself stacks to a single column on mobile. */
.hero.hero--compact .hero-actions {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    max-width: 760px;
}
.hero.hero--compact .hero-actions .btn {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    text-align: center;
    /* Wider container + a slightly smaller size than the default
       .btn-large (below) is what keeps "Get a Free Switchboard
       Assessment" on a single line instead of wrapping to 2-3 lines —
       a single wide, rectangular button reads as a more confident CTA
       than a tall, near-square, multi-line one. */
    white-space: nowrap;
    font-size: clamp(0.8rem, 0.33vw + 0.59rem, 0.98rem);
    padding: 16px 20px;
}
@media (max-width: 1040px) {
    .hero.hero--compact .hero-actions {
        flex-direction: column;
        max-width: 100%;
        margin-inline: auto;
    }
    .hero.hero--compact .hero-actions .btn {
        /* Full-width stacked buttons on mobile have plenty of room, so
           the long label can wrap normally there without looking
           cramped — nowrap is only needed for the narrower desktop
           side-by-side layout above. */
        white-space: normal;
        font-size: 1.05rem;
    }
}

/* ---------- Blog HUB hero: full-bleed navy treatment ----------
   The hub page's hero previously reused the same plain light
   .hero--compact + .hero-content--centered treatment as an article's
   Quick-Answer hero, but with no second column filled in — under
   .hero.hero--compact's two-column grid (above) that left the whole
   right-hand grid track completely empty, reading as a large dead
   patch of white space next to the centered text (flagged directly by
   the user from a screenshot: "landing page of blog home page looks
   very flat and boring with lots of empty space... similar to
   location landing page we should have better hero section").
   Scoped to .hub-hero (added ONLY on /blogs/index.html's hero) so
   every article page's existing hero is completely untouched — this
   borrows the exact navy/gold/trust-chip/stats-strip recipe already
   proven on the /locations sub-project's hub + suburb pages, rebuilt
   against this site's OWN --accent-blue/--primary tokens (same pattern
   already used for .card-navy above). */
.hub-hero {
    background: transparent;
    overflow: visible;
}
/* .hero (layout.css) is capped at var(--container-max) and centered —
   invisible on a light hero, but on THIS dark hero it would leave pale
   gutters down both sides on any screen wider than the container. A
   full-bleed ::before, sized to the viewport and centered behind the
   real (still width-capped) content, gives an edge-to-edge navy band
   instead — same trick /locations uses for .location-hero. */
.hub-hero::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    width: 100vw;
    height: 100%;
    transform: translateX(-50%);
    background: linear-gradient(135deg, var(--accent-blue), var(--accent-blue-dark));
    z-index: -1;
}
.hub-hero h1 { color: #fff; }
.hub-hero p { color: var(--text-on-dark-muted); }
.hub-hero .badge {
    background: rgba(255, 255, 255, 0.1);
    color: var(--primary);
}
/* .text-gradient (base.css) fades gold -> navy, correct on a light
   section, but the navy end disappears into this hero's own navy
   background. Solid gold instead — same fix already used on
   /locations for its dark hero. */
.hub-hero .text-gradient {
    background: none;
    -webkit-text-fill-color: initial;
    color: var(--primary);
}
/* The "Call Now" button's default .btn-outline treatment (layout.css)
   is a SOLID navy-gradient fill — the exact same colour family as this
   hero's own new navy background, so it would all but disappear into
   it (the same bug already caught and fixed on /locations' hero).
   Transparent + a light border reads correctly against navy instead. */
.hub-hero .hero-actions .btn-outline {
    background: transparent;
    color: #fff;
    border: 2px solid rgba(255, 255, 255, 0.4);
}
.hub-hero .hero-actions .btn-outline:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.65);
    color: #fff;
}
.hub-hero .hero-background {
    background: radial-gradient(circle, rgba(var(--primary-rgb), 0.22) 0%, rgba(0, 0, 0, 0) 60%);
}
.hub-hero .image-wrapper.glass-panel {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.14);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
.hub-hero .floating-card { background: #fff; }

/* Trust-chip pills under the hero CTA row — designed for a dark
   background (semi-transparent white pill, white text), so only used
   on the hub hero for now. */
.trust-chips { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
.trust-chip {
    display: flex;
    align-items: center;
    gap: 9px;
    background: rgba(255, 255, 255, 0.09);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 999px;
    padding: 10px 16px;
    font-size: clamp(0.85rem, 0.25vw + 0.8rem, 0.98rem);
    font-weight: 600;
    color: #fff;
}
.trust-chip svg { color: var(--primary); flex: 0 0 auto; width: 16px; height: 16px; }

/* Floating "5.0 rated" badge pinned to the hero photo's top-right
   corner (separate from .floating-card, which sits bottom-left, so the
   two don't collide). */
.hero-media-badge {
    position: absolute;
    top: 16px;
    right: 16px;
    z-index: 3;
    background: #fff;
    color: var(--accent-blue);
    border-radius: var(--radius-sm);
    padding: 12px 16px;
    box-shadow: var(--shadow-md);
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: clamp(0.8rem, 0.25vw + 0.75rem, 0.92rem);
    line-height: 1.25;
}
.hero-media-badge .stars-inline { color: var(--primary); letter-spacing: 1px; }

@media (max-width: 720px) {
    .hero-media-badge { top: 10px; right: 10px; padding: 10px 12px; gap: 8px; }
    /* Trust chips repeat the same claims as the stats-strip immediately
       below on phones — hide here so the two don't say "licensed" /
       "years experience" / "rated" twice in a row on a narrow screen. */
    .trust-chips { display: none; }
}

/* ---------- Stats strip (sits directly under the blog hub hero) ---------- */
.stats-strip { background: var(--accent-blue-dark); }
.stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1px;
    background: rgba(255, 255, 255, 0.12);
}
.stat { background: var(--accent-blue-dark); padding: clamp(24px, 2.6vw, 40px) 20px; text-align: center; }
.stat strong { display: block; font-family: var(--font-heading); font-size: clamp(1.8rem, 1.4vw + 1.5rem, 2.7rem); color: var(--primary); font-weight: 800; line-height: 1; }
.stat span { display: block; margin-top: 8px; font-size: clamp(0.9rem, 0.3vw + 0.82rem, 1.05rem); font-weight: 500; color: rgba(255, 255, 255, 0.85); }
@media (max-width: 720px) { .stats { grid-template-columns: repeat(2, 1fr); } }

/* ---------- Long-form article typography (root has no .article-copy) ---------- */
.article-copy h2 {
    margin-top: clamp(40px, 5vw, 56px);
    margin-bottom: 16px;
    font-size: clamp(1.5rem, 1.1vw + 1.2rem, 2rem);
}
.article-copy h3 {
    margin-top: clamp(26px, 3vw, 34px);
    margin-bottom: 12px;
    font-size: clamp(1.2rem, 0.6vw + 1.05rem, 1.45rem);
}
/* Fluid, not fixed, so body copy keeps pace with the viewport the same
   way .hero p already does (layout.css) instead of plateauing at a flat
   1.08rem while everything else on the page keeps growing — that
   mismatch was exactly why the hero's own intro paragraph read larger/
   more comfortable than the article body underneath it. */
.article-copy p { color: var(--text-muted); font-size: clamp(1.05rem, 0.6vw + 0.9rem, 1.3rem); line-height: 1.75; margin-bottom: 18px; }
.article-copy ul, .article-copy ol { color: var(--text-muted); font-size: clamp(1.02rem, 0.55vw + 0.87rem, 1.25rem); line-height: 1.7; margin: 0 0 20px 22px; }
.article-copy li { margin-bottom: 10px; }
.article-copy li strong, .article-copy p strong { color: var(--text-main); }

/* The generic ".article-copy p" rule above (correct for ordinary prose)
   would otherwise also match — and, since blogs.css loads last, win
   against and override — the root site's own dark ("on-dark") card
   components whenever one is used inside an article, such as
   .faq-card and .cta-container (both used further down every article).
   Those cards set their own light text colour in layout.css, but with
   equal CSS specificity to the rule above, so restore it explicitly
   here for any dark card nested inside .article-copy. */
.article-copy .faq-card p,
.article-copy .cta-container p {
    color: var(--text-on-dark-muted);
}
.article-copy .faq-card li,
.article-copy .cta-container li,
.article-copy .faq-card p strong,
.article-copy .cta-container p strong {
    color: inherit;
}
.article-copy a:not(.btn) { color: var(--primary-hover); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.article-copy a:not(.btn):hover { color: var(--accent-red); }
.article-copy blockquote {
    margin: 24px 0;
    padding: 18px 24px;
    border-left: 4px solid var(--primary);
    background: var(--bg-alt);
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
    color: var(--text-main);
    font-size: clamp(1.02rem, 0.5vw + 0.9rem, 1.2rem);
}
.article-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 22px;
    color: var(--text-muted);
    font-size: 0.95rem;
    margin-bottom: 6px;
}
.article-meta span { display: inline-flex; align-items: center; gap: 7px; }
.article-meta i { color: var(--primary); }

/* ---------- Hero "quick answer" card (replaces a hero photo) ---------- */
.quick-answer-card {
    padding: clamp(26px, 3vw, 34px);
    max-width: 460px;
}
.quick-answer-card .quick-answer-label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--primary-hover);
    margin-bottom: 14px;
}
.quick-answer-card p { color: var(--text-main); font-size: 1.05rem; line-height: 1.65; margin: 0; }
.quick-answer-card p + p { margin-top: 14px; font-size: 0.95rem; color: var(--text-muted); }

/* ---------- Inline CTA band inside long-form content ---------- */
.inline-cta-band {
    margin: clamp(36px, 5vw, 56px) 0;
    padding: clamp(26px, 3vw, 36px);
    border-radius: var(--radius-lg);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: clamp(16px, 3vw, 28px);
}
.inline-cta-band p { margin: 0; font-weight: 700; font-size: 1.1rem; color: var(--text-on-dark); }
.inline-cta-band .btn { flex-shrink: 0; }
@media (max-width: 600px) {
    .inline-cta-band { flex-direction: column; align-items: stretch; text-align: center; }
    .inline-cta-band p { font-size: 1.15rem; line-height: 1.35; }
    .inline-cta-band .btn { width: 100%; }
}

/* ---------- Article disclaimer box (partials/disclaimer.html, injected by
   build-blogs.php between DISCLAIMER:START/END on every article) ----------
   Deliberately quiet/muted styling (small text, tinted background, left
   accent bar) rather than a bold call-out — this is a legal/liability
   notice, not a design element competing with the article's real content
   or CTAs. */
.disclaimer-box {
    margin-top: 28px;
    padding: 18px 22px;
    border-radius: var(--radius-sm);
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-left: 4px solid var(--text-muted);
}
.disclaimer-box p {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.7;
    color: var(--text-muted);
}
.disclaimer-box strong { color: var(--text-main); }
.disclaimer-box a { color: var(--text-main); text-decoration: underline; }

/* ---------- Shared "navy proof block" card treatment ----------
   Same recipe used by the /locations sub-project's .card-navy, rebuilt
   here against the ROOT theme's own --accent-blue tokens (same colour
   family — root's --accent-blue is the same deep blue /locations was
   re-themed to match) so the reused footer-block content looks native
   to this site rather than imported from elsewhere. */
.card-navy {
    background: linear-gradient(135deg, var(--accent-blue), var(--accent-blue-dark));
    border: 1px solid rgba(255, 255, 255, 0.14);
    box-shadow: var(--shadow-lg);
}
/* Text-contrast rule for every .card-navy variant: body copy is near-white
   (not the homepage's --text-on-dark-muted 82% — bumped to 95% here after
   checking against measured WCAG contrast ratios; 82% still reads fine on
   the homepage's .feature-card but this card has smaller secondary text
   too, so it gets the same higher bar), and — unlike the /locations
   version this was adapted from — body text and card headings/names never
   use --primary (gold) as their main colour: gold-on-navy measures ~3.9:1
   contrast, under the 4.5:1 WCAG AA floor for normal-size text, which is
   exactly why it read as "dark"/low-prominence. Gold is kept ONLY for
   icons and tiny uppercase eyebrow labels (decorative / large enough to
   qualify for the lower 3:1 large-text threshold). */
.card-navy p { color: rgba(255, 255, 255, 0.95); }
.card-navy h3, .card-navy h4 { color: var(--text-on-dark); }
.card-navy i { color: var(--primary); }
.card-navy .btn i { color: inherit; }
.card-navy .reviewer h4 { color: #fff; }
.card-navy .reviewer span { color: rgba(255, 255, 255, 0.85); }
.card-navy .review-text { color: rgba(255, 255, 255, 0.95); }
.trust-fixed-item.card-navy p { color: rgba(255, 255, 255, 0.95); }
.project-card.card-navy .project-card-body span { color: rgba(255, 255, 255, 0.85); }

.btn-outline-on-dark {
    background: transparent;
    color: #fff;
    border: 2px solid rgba(255, 255, 255, 0.4);
}
.btn-outline-on-dark:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.65);
    color: #fff;
    transform: translateY(-3px);
}

.proof-block { padding: clamp(50px, 7vw, 90px) 0; }
.proof-section + .proof-section { margin-top: clamp(40px, 6vw, 64px); }
.proof-section .section-header { margin-bottom: clamp(24px, 4vw, 36px); }

.trust-fixed-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: clamp(18px, 2.5vw, 28px);
}
.trust-fixed-item { display: flex; gap: 14px; align-items: flex-start; padding: clamp(20px, 2.5vw, 26px); border-radius: var(--radius-md); }
.trust-fixed-item i { color: var(--primary); font-size: 1.5rem; margin-top: 2px; flex-shrink: 0; }
.trust-fixed-item h4 { font-size: 1.05rem; margin-bottom: 4px; }
.trust-fixed-item p { font-size: 0.92rem; margin: 0; }

/* Reviews marquee — always animating, every screen size */
.reviews-marquee { overflow: hidden; position: relative; }
.reviews-track {
    display: flex;
    align-items: stretch;
    gap: clamp(16px, 2.5vw, 24px);
    width: max-content;
    animation: blogs-scroll 70s linear infinite;
    padding: 4px 0;
}
.reviews-marquee:hover .reviews-track,
.reviews-marquee:focus-within .reviews-track { animation-play-state: paused; }
.reviews-track .review-strip-card { width: clamp(280px, 26vw, 360px); flex: 0 0 auto; }

/* Brand / project strip — marquee on mobile/tablet, static grid on desktop */
.scroll-strip-marquee { overflow: hidden; position: relative; margin: 6px 0 18px; scrollbar-width: none; }
.scroll-strip-marquee::-webkit-scrollbar { display: none; }
.scroll-strip {
    display: flex;
    align-items: stretch;
    gap: clamp(16px, 2.5vw, 24px);
    width: max-content;
    animation: blogs-scroll 34s linear infinite;
}
.scroll-strip-marquee:hover .scroll-strip,
.scroll-strip-marquee:focus-within .scroll-strip { animation-play-state: paused; }
.scroll-strip > * { flex: 0 0 auto; }

@keyframes blogs-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
    .scroll-strip, .reviews-track, .sidebar-widget-track { animation: none; }
    .scroll-strip-marquee, .reviews-marquee { overflow-x: auto; }
    /* Mobile marquee falls back to a plain manual-scroll row, matching
       the reduced-motion fallback above — the duplicate set is only
       needed for the seamless animated loop, so hide it once the
       animation itself is off. */
    @media (max-width: 1024px) {
        .sidebar-widget-list { overflow-x: auto; }
        .sidebar-widget-track > *[aria-hidden="true"] { display: none; }
    }
}

@media (min-width: 1025px) {
    .scroll-strip-marquee { overflow: visible; margin: 0; }
    .scroll-strip {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
        width: auto;
        animation: none;
    }
    .scroll-strip > *[aria-hidden="true"] { display: none; }
    .scroll-strip > * { flex: initial; }
}

@media (max-width: 720px) {
    .reviews-track { gap: 14px; }
    .reviews-track .review-strip-card { width: min(80vw, 300px); }
}

/* Brand logo strip (Aug 2026 rebuild) — replaced the earlier text/
   logo-side-by-side .logo-chip design entirely. Per explicit user
   request, this now matches /brands' own "Explore Other Brands" grid
   exactly: logo-only white cards (no brand name/category text), all 30
   real brands we have a logo image for, static 5-per-row grid on
   desktop (>=1025px) and a continuous auto-scroll marquee on mobile/
   tablet — same .brand-explore-marquee/-track/-chip classes as /brands,
   reusing this project's own `blogs-scroll` keyframe (already defined
   above for the reviews/nearby strips) rather than introducing a
   duplicate keyframe under a different name. Sourced from
   blogs/brand-logo-images/ (same source set shared with /brands and
   /locations), auto-cropped to remove the ~55-85% baked-in white
   padding the raw JPGs ship with (same root-cause fix as /brands' Pass
   5). Daikin/Gree/TCL have no source logo file anywhere in this project
   (a known permanent gap also documented for /brands) and are simply
   omitted from this list — no text-only fallback chip, matching
   /brands' own real-logo-only convention for this exact component. */
.brand-explore-marquee {
    overflow: hidden;
    position: relative;
    margin-top: clamp(20px, 3vw, 32px);
    scrollbar-width: none;
}

.brand-explore-marquee::-webkit-scrollbar { display: none; }

.brand-explore-track {
    display: flex;
    align-items: center;
    gap: clamp(10px, 1.4vw, 16px);
    width: max-content;
    animation: blogs-scroll 34s linear infinite;
}

.brand-explore-marquee:hover .brand-explore-track,
.brand-explore-marquee:focus-within .brand-explore-track { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
    .brand-explore-track { animation: none; }
    .brand-explore-marquee { overflow-x: auto; }
}

@media (min-width: 1025px) {
    .brand-explore-marquee { overflow: visible; margin-top: clamp(24px, 3vw, 36px); }

    .brand-explore-track {
        display: grid;
        grid-template-columns: repeat(5, 1fr);
        gap: clamp(12px, 1.4vw, 18px);
        width: auto;
        animation: none;
    }

    .brand-explore-chip[aria-hidden="true"] { display: none; }
}

@media (min-width: 1025px) and (max-width: 1300px) {
    .brand-explore-track { grid-template-columns: repeat(3, 1fr); }
}

.brand-explore-chip {
    flex: 0 0 auto;
    width: clamp(100px, 11vw, 132px);
    height: clamp(72px, 7.6vw, 92px);
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: clamp(6px, 0.7vw, 9px);
    box-shadow: var(--shadow-sm);
    transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

@media (min-width: 1025px) { .brand-explore-chip { width: auto; } }

.brand-explore-chip:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-md);
    border-color: var(--primary);
}

.brand-explore-chip img { max-width: 100%; max-height: 100%; object-fit: contain; }

.project-card { border-radius: var(--radius-md); overflow: hidden; width: 260px; }
/* /brands and /locations both switch .project-card to width:auto at
   desktop widths so the card fills whatever the grid track computes,
   instead of staying pinned to its mobile-marquee width — this project
   was missing that override, so its cards never actually stretched to
   fill their grid track on desktop even before the fix below. */
@media (min-width: 1025px) { .project-card { width: auto; } }
.project-card img { width: 100%; height: 160px; object-fit: cover; display: block; }
.project-card-body { padding: 14px 18px; }
.project-card-body h4 { font-size: 1rem; margin-bottom: 2px; }
.project-card-body span { font-size: 0.82rem; }

/* Aug 2026: the shared .scroll-strip grid (repeat(auto-fit, minmax(220px,
   1fr)) from layout.css) only fits 5 of this strip's 6 real photo cards
   per row within the ~1400px container cap, wrapping the 6th card onto
   an orphaned second row on every one of /brands, /blogs, /locations —
   confirmed via screenshot, not just this project. Reviews cards
   (.review-strip-card) share the same .scroll-strip grid and read fine
   at the wider 220px minimum, so narrowing that shared value directly
   would needlessly shrink the reviews strip too. Scoping a narrower
   minimum to just the grid that actually contains .project-card
   (via :has(), supported in all evergreen desktop browsers this site
   targets) fits all 6 photo cards on one row at realistic desktop
   widths without touching the reviews strip. */
@media (min-width: 1025px) {
    .scroll-strip:has(.project-card) {
        grid-template-columns: repeat(auto-fit, minmax(175px, 1fr));
    }
}

/* ---------- Blog article shell: 2-col grid (content + sidebar) ---------- */
.blog-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
    gap: clamp(32px, 4vw, 56px);
    align-items: stretch;
    max-width: var(--container-max);
    margin: 0 auto;
    padding: clamp(20px, 4vw, 48px) var(--container-padding) clamp(40px, 5vw, 64px);
}
.blog-main { min-width: 0; }

/* .blog-sidebar is a NORMAL (non-scrolling, non-sticky) column.
   align-items:stretch on .blog-shell above makes it the same full height
   as .blog-main (the article), so Recent Posts / Trending Posts always
   render their FULL content inline with the page — no internal
   scrollbar, no clipped mini-window, it just scrolls along with the rest
   of the page like normal content.
   The CTA card is the ONLY sticky element (see .blog-sidebar >
   .sidebar-cta-card below for how it pins near the bottom of the
   viewport) — it travels down normally at first, then "catches" near the
   bottom of the viewport once you've scrolled past the widgets above it,
   and stays pinned there for the rest of the (usually much longer)
   article — releasing again only right at the very end of the sidebar
   column, so it never overlaps the footer-block below. */
.blog-sidebar {
    display: flex;
    flex-direction: column;
    gap: 20px;
    /* Grid items default to min-width:auto (content-based), which would
       let the sidebar's mobile horizontal-scroll rows (see
       .sidebar-widget-list below) force this whole grid column wider
       than the viewport. min-width:0 lets the grid track actually
       shrink to the available width and the rows scroll internally
       instead. */
    min-width: 0;
}
.sidebar-scroll-area {
    display: flex;
    flex-direction: column;
    gap: 26px;
    min-width: 0;
}
/* Pins near the BOTTOM of the viewport (not the top) as you scroll past the
   widgets above it — implemented via `top`, not `bottom`. `position:sticky`
   with a `bottom` offset was tested here and does not reliably engage in
   this nested layout (it released far too early); `top` is the reliable
   form, so blog-blocks.js computes --sidebar-cta-top on load/resize as
   `100vh - 24px - <the card's real height>`, which puts the card's OWN
   bottom edge 24px above the viewport's bottom edge — the same visual
   result `bottom: 24px` would have given if it had worked. The 60vh
   fallback only matters for the brief instant before JS runs. */
.blog-sidebar > .sidebar-cta-card {
    flex: 0 0 auto;
    position: sticky;
    top: var(--sidebar-cta-top, 60vh);
    z-index: 2;
}

@media (max-width: 1024px) {
    .blog-shell { grid-template-columns: 1fr; padding-left: 0; padding-right: 0; }
    .blog-shell > .blog-main { padding: 0 var(--container-padding); }
    /* On mobile the sidebar is no longer a floating column — it stacks
       normally as full-width cards (Recent Posts, then Trending Posts,
       then the CTA card). Only the LIST inside each widget scrolls
       horizontally (see .sidebar-widget-list below); the sidebar itself
       does not scroll as a row of cards, and the CTA card is no longer
       sticky here — mobile already has its own separate fixed bottom
       bar (.mobile-sticky-cta) for a persistent call-to-action. */
    .blog-sidebar { gap: 18px; }
    .blog-sidebar > .sidebar-cta-card { position: static; }
    /* Unwraps .sidebar-scroll-area so its two widgets become direct flex
       children of .blog-sidebar, stacking in document order together
       with the CTA card sibling — instead of living in their own
       scrollable box. */
    .sidebar-scroll-area { display: contents; }
}

/* ---------- Sidebar widgets ---------- */
.sidebar-widget {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: clamp(20px, 2.5vw, 26px) 0;
    box-shadow: var(--shadow-sm);
    min-width: 0;
}
.sidebar-widget-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 clamp(20px, 2.5vw, 26px) 16px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
}
.sidebar-widget-header i { color: var(--primary); font-size: 1.25rem; }
.sidebar-widget-header h3 { font-size: 1.05rem; margin: 0; }

.sidebar-widget-list { display: flex; flex-direction: column; min-width: 0; }
/* .sidebar-widget-track wraps build-blogs.php's generated items (real
   set + an aria-hidden duplicate set, same convention as .reviews-track
   / .scroll-strip below). display:contents on desktop makes the wrapper
   generate no box of its own, so the real items visually participate in
   .sidebar-widget-list's flex layout exactly as if this wrapper didn't
   exist — BUT display:contents does NOT change the actual DOM tree, so
   CSS child-combinator selectors (>) must still target
   `.sidebar-widget-track > ...`, never `.sidebar-widget-list > ...` —
   the items are DOM grandchildren of the list, only visual children of
   it. Only the mobile media query (further down) turns the track into
   an actual flex row with the looping marquee animation. */
.sidebar-widget-track { display: contents; }
@media (min-width: 1025px) {
    /* Desktop never shows the duplicate set — it only exists for the
       mobile marquee loop below. */
    .sidebar-widget-track > *[aria-hidden="true"] { display: none; }
}
.sidebar-post-item {
    display: block;
    padding: 14px clamp(20px, 2.5vw, 26px);
    border-bottom: 1px solid var(--border);
    color: var(--text-main);
}
/* Targets the one real item build-blogs.php marks as .is-last-item,
   NOT :last-child — with the duplicate set appended after the real
   items, the DOM's last-child is always a (desktop-hidden) duplicate,
   so :last-child would strip the border off the wrong element. */
.sidebar-widget-track > .is-last-item { border-bottom: none; }
.sidebar-post-item .sidebar-post-tag {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--primary-hover);
    margin-bottom: 4px;
}
.sidebar-post-item h4 { font-size: 0.96rem; line-height: 1.4; margin: 0 0 3px; font-weight: 600; }
.sidebar-post-item p { font-size: 0.84rem; color: var(--text-muted); margin: 0; line-height: 1.5; }
a.sidebar-post-item:hover { background: var(--bg-alt); }
a.sidebar-post-item:hover h4 { color: var(--primary-hover); }

.sidebar-post-item.is-coming-soon {
    opacity: 0.72;
}
.sidebar-post-item.is-coming-soon .sidebar-post-tag { color: var(--text-muted); }
.sidebar-post-item.is-coming-soon:hover { background: transparent; }
.sidebar-post-item.is-coming-soon:hover h4 { color: var(--text-main); }

/* ---------- Trending Posts widget: real most-read articles (ranked by
   the view-tracker), NOT abstract category tags ---------- */
.trending-post-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px clamp(20px, 2.5vw, 26px);
    border-bottom: 1px solid var(--border);
    color: var(--text-main);
}
.trending-post-rank {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.78rem;
    font-weight: 800;
    color: var(--primary-hover);
}
.trending-post-item:first-child .trending-post-rank {
    background: linear-gradient(135deg, var(--primary), var(--primary-hover));
    color: #fff;
    border-color: transparent;
}
.trending-post-info { min-width: 0; }
.trending-post-info h4 { font-size: 0.92rem; line-height: 1.35; margin: 0 0 3px; font-weight: 600; }
.trending-post-views { font-size: 0.76rem; color: var(--text-muted); display: inline-flex; align-items: center; gap: 4px; }
.trending-post-views i { font-size: 0.7rem; }
a.trending-post-item:hover { background: var(--bg-alt); }
a.trending-post-item:hover h4 { color: var(--primary-hover); }
.trending-post-item.is-coming-soon { opacity: 0.72; }

/* .sidebar-cta-card also carries .sidebar-widget (for consistent card
   padding/radius/shadow) AND .card-navy (for the dark background + white
   text). .sidebar-widget's own `background` is declared LATER in this file
   than .card-navy's, so at equal specificity it was silently winning and
   leaving this card WHITE instead of navy — making the white-text CTA copy
   and the Call button invisible. The combined selector below has higher
   specificity than either class alone, so it wins regardless of source
   order and actually restores the navy card look. */
.sidebar-widget.card-navy {
    background: linear-gradient(135deg, var(--accent-blue), var(--accent-blue-dark));
    border-color: rgba(255, 255, 255, 0.14);
}
.sidebar-cta-card { padding: clamp(22px, 3vw, 28px); text-align: center; }
.sidebar-cta-card h4 { color: #fff; font-size: 1.1rem; margin-bottom: 8px; }
.sidebar-cta-card p { color: rgba(255, 255, 255, 0.95); font-size: 0.9rem; margin-bottom: 18px; }
/* Two CTAs (Get a Free Quote + Call Now) stacked with a small, even gap —
   not a single button under a big decorative icon with a large gap. */
.sidebar-cta-actions { display: flex; flex-direction: column; gap: 10px; }
.sidebar-cta-card .btn { width: 100%; }

/* ---------- Mobile: each widget's LIST scrolls in its own single row;
   the sidebar itself stacks normally (Recent Posts card, then Trending
   Posts card, then the full-width CTA card) ---------- */
@media (max-width: 1024px) {
    .sidebar-widget { width: 100%; }
    /* Same auto-looping marquee treatment as .reviews-track / .scroll-strip
       above, instead of the old manual drag-to-scroll row: the outer
       .sidebar-widget-list clips, and .sidebar-widget-track (build-blogs.php's
       real items + an aria-hidden duplicate set) scrolls itself in a
       seamless loop via the shared blogs-scroll keyframe. Per-widget speed
       comes from the --sidebar-marquee-duration custom property
       build-blogs.php computes from the actual item count, so it stays a
       comfortable reading pace as more posts get published later. */
    .sidebar-widget-list {
        overflow: hidden;
        scrollbar-width: none;
    }
    .sidebar-widget-list::-webkit-scrollbar { display: none; }
    .sidebar-widget-track {
        display: flex;
        flex-direction: row;
        gap: 14px;
        width: max-content;
        padding: 16px clamp(20px, 2.5vw, 26px) 6px;
        animation: blogs-scroll var(--sidebar-marquee-duration, 40s) linear infinite;
    }
    .sidebar-widget-list:hover .sidebar-widget-track,
    .sidebar-widget-list:focus-within .sidebar-widget-track { animation-play-state: paused; }
    .sidebar-widget-track > * {
        flex: 0 0 min(78vw, 280px);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        background: var(--bg-alt);
    }
    .sidebar-post-item.is-coming-soon { background: transparent; }
    .sidebar-widget-track > .is-last-item { border: 1px solid var(--border); }
    a.sidebar-post-item:hover, a.trending-post-item:hover { background: #fff; }
    .trending-post-item { flex-direction: column; align-items: flex-start; gap: 8px; padding: 14px 16px; }
}

/* ---------- Blog hub grid (index.html) ---------- */
.hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: clamp(22px, 3vw, 32px);
    max-width: var(--container-max);
    margin: 0 auto;
}
.hub-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: clamp(24px, 2.5vw, 30px);
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-sm);
    transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.hub-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); border-color: var(--primary); }
.hub-card .hub-card-tag {
    display: inline-flex;
    align-self: flex-start;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: #fff;
    background: linear-gradient(135deg, var(--primary), var(--primary-hover));
    padding: 5px 12px;
    border-radius: 20px;
    margin-bottom: 16px;
}
.hub-card h3 { font-size: 1.25rem; margin-bottom: 10px; line-height: 1.35; }
.hub-card p { color: var(--text-muted); font-size: 0.96rem; margin-bottom: 18px; flex-grow: 1; }
.hub-card .link-arrow { display: inline-flex; align-items: center; gap: 6px; color: var(--primary); font-weight: 700; font-size: 0.95rem; }

.hub-card-coming {
    background: transparent;
    border: 2px dashed var(--border);
    box-shadow: none;
}
.hub-card-coming:hover { transform: none; border-color: var(--border); box-shadow: none; }
.hub-card-coming .hub-card-tag { background: var(--bg-alt); color: var(--text-muted); }
.hub-card-coming h3 { color: var(--text-main); }

/* ---------- Pricing tier cards ---------- */
.pricing-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: clamp(16px, 2.5vw, 22px);
    margin: 20px 0 28px;
}
/* Same brand-tinted treatment as .signal-card above (see that comment) —
   a soft blue-to-amber wash and an accent bar instead of a plain white,
   bordered box, so the pricing tiers read as part of the same themed
   page rather than a bare table. Accent bar sits on TOP here (not the
   left side, like .signal-card) since each tile's focal point is the
   dollar figure right below it, and a small lift-on-hover matches the
   other interactive cards used elsewhere on the page (.faq-card, etc). */
.pricing-card {
    background: linear-gradient(135deg, hsla(212, 88%, 30%, 0.07), hsla(35, 95%, 50%, 0.06));
    border: 1px solid hsla(212, 88%, 30%, 0.16);
    border-top: 4px solid var(--accent-blue);
    border-radius: var(--radius-md);
    padding: clamp(18px, 2vw, 22px);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.pricing-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-sm);
}
.pricing-card h4 { font-size: 0.98rem; margin-bottom: 8px; }
.pricing-card .price { font-family: var(--font-heading); font-size: 1.35rem; font-weight: 700; color: var(--primary-hover); display: block; margin-bottom: 6px; }
.pricing-card p { font-size: 0.86rem; color: var(--text-muted); margin: 0; }
.pricing-note { font-size: 0.9rem; color: var(--text-muted); }

/* ---------- Signs / timing checklist ---------- */
.signal-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: clamp(16px, 2.5vw, 22px);
    margin: 20px 0 8px;
}
/* Soft brand-tinted background (a light navy-to-amber wash) instead of a
   plain white/bordered card — ties this checklist into the same blue +
   orange palette used everywhere else on the page (FAQ cards, CTA cards,
   badges) rather than reading as a plain, undecorated list. Kept light
   enough that the dark body text stays easily readable, and the left
   accent bar echoes the icon colour for a quick visual scan. */
.signal-card {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    background: linear-gradient(135deg, hsla(212, 88%, 30%, 0.07), hsla(35, 95%, 50%, 0.06));
    border: 1px solid hsla(212, 88%, 30%, 0.16);
    border-left: 4px solid var(--accent-blue);
    border-radius: var(--radius-md);
    padding: clamp(18px, 2vw, 22px);
}
.signal-card.is-urgent {
    background: linear-gradient(135deg, hsla(4, 90%, 55%, 0.09), hsla(35, 95%, 50%, 0.05));
    border-color: hsla(4, 90%, 55%, 0.28);
    border-left: 4px solid var(--accent-red);
}
.signal-card i { color: var(--primary); font-size: 1.4rem; margin-top: 2px; flex-shrink: 0; }
.signal-card.is-urgent i { color: var(--accent-red); }
.signal-card h4 { font-size: 1rem; margin-bottom: 4px; }
.signal-card p { font-size: 0.92rem; color: var(--text-muted); margin: 0; }

/* ---------- Mobile: force single-column full-width cards ----------
   The auto-fit minmax() grids above already collapse to one column on
   most phones, but this makes it explicit and correct at every phone
   width (not just an incidental side-effect of the minmax value), per
   the "titles / CTAs / single-line statements / cards must be full
   width on mobile" requirement. */
@media (max-width: 700px) {
    .hub-grid,
    .pricing-grid,
    .signal-grid {
        grid-template-columns: 1fr;
    }
    .badge,
    .quick-answer-card,
    .faq-card,
    .hub-card,
    .pricing-card,
    .signal-card {
        width: 100%;
    }
}

/* ---------- Blog article FAQ grid: 2 columns, not the site's default 3 ----------
   layout.css's shared .faq-grid rule (used on the homepage, service pages,
   and location pages too) goes to 3 columns at 1100px+. On a blog article
   the FAQ answers run noticeably longer than the homepage's short blurbs,
   so 3 narrow columns made every card feel slim and very tall. Scoped to
   .article-copy (blog articles only, see the <article class="blog-main
   article-copy"> wrapper) so the homepage/services/locations FAQ grids —
   which still work well at 3 columns — are completely untouched. */
@media (min-width: 700px) {
    .article-copy .faq-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ---------- Blog article FAQ cards: keep scaling up on very large screens ----------
   responsive.css already has a site-wide min-width:1900px block that grows
   .faq-card's icon/heading/body font sizes instead of letting them plateau
   (see that file for the full rationale) — that still applies here too.
   With only 2 (wider) cards per row on a blog article instead of 3, there's
   extra room per card on a large monitor, so the card's icon badge, padding
   and text are nudged a little larger again here so the extra width reads
   as "a bigger, roomier card" rather than empty leftover padding. */
@media (min-width: 1900px) {
    .article-copy .faq-card {
        padding: clamp(32px, 2vw, 46px);
    }
    .article-copy .faq-card i {
        width: clamp(56px, 3vw, 68px);
        height: clamp(56px, 3vw, 68px);
        font-size: clamp(1.6rem, 0.3vw + 1.45rem, 1.85rem);
    }
    .article-copy .faq-card h4 {
        font-size: clamp(1.6rem, 0.35vw + 1.4rem, 1.95rem);
    }
    .article-copy .faq-card p {
        font-size: clamp(1.2rem, 0.2vw + 1.1rem, 1.4rem);
    }

    /* Body copy: same "keep growing instead of plateauing" treatment as
       .hero p gets at this breakpoint (see responsive.css) so the article
       text doesn't fall noticeably behind the hero's intro paragraph on a
       large monitor. */
    .article-copy p {
        font-size: clamp(1.3rem, 0.3vw + 1.2rem, 1.55rem);
    }
    .article-copy ul, .article-copy ol {
        font-size: clamp(1.25rem, 0.3vw + 1.15rem, 1.48rem);
    }
}
