/*
 * KaamKrew — global.css
 * Design tokens ported from the Figma AI export's src/index.css
 * (@theme block + font imports). Layout/spacing patterns ported from the
 * same export's inline styles, translated to classes per this theme's
 * hand-written-CSS convention (see project CLAUDE.md).
 */

:root {
	--kk-orange: #FA6B2C;
	--kk-orange-light: #FEF0E8;
	--kk-orange-dark: #E5531A;
	--kk-blue: #0A378F;
	--kk-blue-light: #E8EEF9;
	--kk-blue-dark: #072A6F;
	--kk-navy: #050E1F;
	--kk-lavender: #F5F0FF;
	--kk-purple: #7C3AED;
	--kk-beauty: #D62DF9; /* BeautyCare section's actual accent, confirmed by Bilal — distinct from --kk-purple */
	--kk-beauty-dark: #B11BD1;
	/* Confirmed 2026-09-03 by direct Figma inspection (node 2136-7232,
	   "06 - Men's Grooming") — same real value as --kk-blue/--kk-blue-dark
	   above, not a distinct hue. Was a PLACEHOLDER (#2563EB) since
	   2026-08-27; every rule below that references --kk-grooming picks up
	   the real value automatically now that this one line is fixed. */
	--kk-grooming: #0A378F;
	--kk-grooming-dark: #072A6F;
	--kk-green: #10B981;
	--kk-ink: #0D0D0D;
	--kk-muted: #6B7280;
	--kk-muted-2: #9CA3AF;
	--kk-line: #E5E7EB;
	--kk-line-soft: rgba(0,0,0,0.06);
	--kk-paper: #ffffff;
	--kk-surface: #F9FAFB;
	--kk-radius: 16px;
	--kk-container: 1340px;
	--kk-header-h: 115px;
	--kk-header-h-mobile: 58px;
	/* Card sizing (2026-08-29, Option B) — ceiling values, unchanged at
	   >=1680px, tiered down below that same as --kk-container above. See
	   the container's own comment + CLAUDE.md's 2026-08-29 entry for why:
	   these were flat regardless of viewport, same class of "designed
	   against 1920, applied everywhere" issue as the typography fix, just
	   not caught in the first pass since cards don't use clamp(). */
	--kk-card-image-h: 192px;
	--kk-card-pad: 20px;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* overflow-x:hidden was only ever on body, not html — the classic gap
   that lets a real device still show page-wide horizontal scroll even
   though body itself "clips": the viewport's actual scrolling behavior is
   governed by html, not body, in most browsers, so body's own rule alone
   doesn't reliably suppress it. Confirmed live via a real DevTools
   375px-wide check (2026-09-02) — the page was genuinely wider than the
   viewport, not just visually cramped. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; }
body {
	font-family: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
	color: var(--kk-ink);
	background: var(--kk-paper);
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }
/* 2026-08-27: corrected against the real Figma file (Cleaning Services Hub,
   node 2154-42555; Home Services Hub, node 2118-38015) — checked the actual
   Typography panel on each real text layer, not guessed from pixels. Base
   heading text is Inter Bold, not the DM Serif Display this was shipping
   with; the serif only belongs on the italic accent phrase (.accent-*
   below), and it's "DM Serif Text", a distinct Google Fonts family from
   "DM Serif Display", not the same font in a different style. */
.font-display, h1, h2 { font-family: 'Inter', system-ui, -apple-system, sans-serif; }

/* width: min(1340px, 100% - 48px) — NOT max-width + fixed padding. With
   box-sizing: border-box, a max-width + padding combo shrinks the actual
   rendered content to (max-width - padding) on wide viewports (was
   1340 - 128 = 1212px here, not the real design's 1340px). min() keeps
   the container at exactly 1340px on desktop and only introduces a 24px
   gutter once the viewport gets narrower than that. Bug found and fixed
   2026-08-21 against Bilal's real Figma measurements. */
.container { width: min(var(--kk-container), 100% - 48px); margin: 0 auto; }
.container--narrow { max-width: 720px; padding: 0 20px; }
/* Bug fixed 2026-09-03: this used to be `.container { padding: 0 20px; }`
   ON TOP OF the width:min() rule above, which ALREADY shrinks the box by
   24px/side once the viewport drops below --kk-container — exactly the
   double-inset (width-shrink + padding, stacking instead of replacing)
   the 2026-08-21 comment above says was fixed once already, reintroduced
   here for mobile only. Real effect: a 44px total side margin at mobile
   (24px from the width formula + 20px padding), not the intended 20px —
   confirmed directly against Figma's own mobile frame (Home Services hub,
   node 2359-5982: Section padding is a uniform 20px on a 394px frame) and
   against Bilal's own screenshots flagging "too much white space on both
   the left and right." Fixed by making width:100% the ONLY thing this
   query changes — padding becomes the sole inset mechanism at mobile,
   giving exactly 20px/side, matching Figma. Sitewide, not card-specific:
   every section using .container gets the correction, since the
   complaint ("overall mobile container width... very little usable width
   for the actual content") was about the container itself, not one
   section. */
@media (max-width: 860px) { .container { width: 100%; padding: 0 20px; } }

/* ── Option B reproportioning — container + card tiers ─────────────────
   CORRECTED 2026-08-29 (same day, after Bilal's own Figma-vs-live
   screenshot comparison at his real ~1900px window). The first version
   of this block treated >=1680px as "already matches Figma, leave it
   alone" — wrong: Bilal's real maximized window (~1900px, i.e. INSIDE
   that supposedly-fine range) still looked visibly larger/denser than
   the Figma prototype player showing the identical section, because the
   Figma player itself scales its whole 1920 frame down to fit whatever
   window it's opened in — proof the intended experience is "fits the
   real window," not "is pixel-exact to 1920 the moment the window
   happens to be close to 1920." So the ceiling itself was reachable too
   easily. Fixed by moving where DEFAULT (non-strict) mode's tiers
   converge on the literal Figma numbers from 1680px out to 2560px (a
   real ultra-wide/2K reference, not an arbitrary round number) — a
   normal 1366-1920px laptop/monitor now NEVER sees the literal, full-
   size Figma numbers by default, only on genuinely spacious screens.
   --kk-container's 1340px (and each card ceiling below) is still the
   real, confirmed Figma number — it just now only appears in DEFAULT
   mode at >=2560px, or at ANY width via the .kk-figma-strict switch
   below (unchanged by this correction — strict mode was never anchored
   to a viewport in the first place, so "no gap on revert" still holds
   exactly as before). Tiered, not continuously fluid, because container/
   card width are layout decisions, not something that should drift
   pixel-by-pixel with viewport (see CLAUDE.md's 2026-08-29 entries for
   the full before/after). All tier numbers below (1120/1240/1300 for
   the container, the matching card-sizing/gap steps) are a reasonable
   editorial judgement call, not independently Figma-measured — revisit
   if Product wants them tuned once this ships.

   Card sizing (--kk-card-image-h/--kk-card-pad) covers the same ground
   as the container: card WIDTH already responds to the container tiers
   (a 1fr grid column of a narrower container is narrower), but the
   card's own internal dimensions (192px image height, 20px padding)
   were flat px regardless of viewport — same pattern, same fix. Grid
   gaps get the same treatment on .product-grid-3/.service-grid.

   Scoped to body:not(.kk-figma-strict) so the strict-mode switch at the
   bottom of this file reverts everything here with zero extra rules. */
/* EXTENDED 2026-08-29 (3rd pass, same day) — Bilal asked for a full sweep
   for the same pattern, not just the cards already covered above. Found
   by grepping global.css for other large, flat, never-tiered px values:
   the homepage's own main hero graphic (.hero-visual, 560px tall — the
   single most prominent block on the whole site, above the fold on
   every visit), every category page's own hero image (.category-hero-
   image img, 380px — used site-wide, not just the homepage),
   .split-media (400px, the Why-KaamKrew/mobile-app split sections), and
   .app-mockup-screen (260x500px). Same root cause as the cards: sized
   once against the 1920 canvas, never revisited per-viewport. Same fix,
   same reversibility — direct property overrides instead of new custom
   properties (these aren't reused across multiple rules the way
   --kk-card-image-h is, so a dedicated token would be pure indirection).
   Tier values are a proportional judgement call (roughly the same ~79/
   90/95% curve used for the cards above), not independently Figma-
   measured — revisit if Product wants them tuned. */
@media (min-width: 1024px) and (max-width: 1439px) {
	body:not(.kk-figma-strict) { --kk-container: 1120px; --kk-card-image-h: 152px; --kk-card-pad: 16px; }
	body:not(.kk-figma-strict) .product-grid-3, body:not(.kk-figma-strict) .product-grid-cat { gap: 28px; }
	body:not(.kk-figma-strict) .service-grid { gap: 20px; }
	body:not(.kk-figma-strict) .hero-visual { height: 420px; }
	body:not(.kk-figma-strict) .hero-visual-phone { width: 220px; height: 320px; }
	body:not(.kk-figma-strict) .hero-visual-photo { width: 250px; height: 330px; }
	body:not(.kk-figma-strict) .category-hero-image img { height: 300px; }
	body:not(.kk-figma-strict) .split-media { height: 300px; }
	/* Further reduced 2026-09-15, on top of this tier's own already-correct
	   reproportioning — a deliberate compactness pass for "The KaamKrew
	   App" section (see .app-mockup-complete's own comment for the real
	   uploaded-graphic case; this rule is its FALLBACK, no-image-configured
	   counterpart, kept visually consistent with the same ~24% reduction). */
	body:not(.kk-figma-strict) .app-mockup-screen { width: 160px; height: 305px; }
}
@media (min-width: 1440px) and (max-width: 1679px) {
	body:not(.kk-figma-strict) { --kk-container: 1240px; --kk-card-image-h: 172px; --kk-card-pad: 18px; }
	body:not(.kk-figma-strict) .product-grid-3, body:not(.kk-figma-strict) .product-grid-cat { gap: 34px; }
	body:not(.kk-figma-strict) .service-grid { gap: 24px; }
	body:not(.kk-figma-strict) .hero-visual { height: 480px; }
	body:not(.kk-figma-strict) .hero-visual-phone { width: 250px; height: 360px; }
	body:not(.kk-figma-strict) .hero-visual-photo { width: 285px; height: 375px; }
	body:not(.kk-figma-strict) .category-hero-image img { height: 340px; }
	body:not(.kk-figma-strict) .split-media { height: 350px; }
	body:not(.kk-figma-strict) .app-mockup-screen { width: 180px; height: 342px; }
}
/* NEW tier (2026-08-29 correction) — this used to be "no override, keep
   the literal ceiling" for the whole >=1680px range, which is exactly
   the range Bilal's real window sat in when it still looked oversized.
   Now stays reproportioned all the way to 2559px. */
@media (min-width: 1680px) and (max-width: 2559px) {
	body:not(.kk-figma-strict) { --kk-container: 1300px; --kk-card-image-h: 182px; --kk-card-pad: 19px; }
	body:not(.kk-figma-strict) .product-grid-3, body:not(.kk-figma-strict) .product-grid-cat { gap: 37px; }
	body:not(.kk-figma-strict) .service-grid { gap: 26px; }
	body:not(.kk-figma-strict) .hero-visual { height: 520px; }
	body:not(.kk-figma-strict) .hero-visual-phone { width: 265px; height: 380px; }
	body:not(.kk-figma-strict) .hero-visual-photo { width: 305px; height: 400px; }
	body:not(.kk-figma-strict) .category-hero-image img { height: 360px; }
	body:not(.kk-figma-strict) .split-media { height: 375px; }
	body:not(.kk-figma-strict) .app-mockup-screen { width: 190px; height: 361px; }
}

/* 60px, not 120px — real measurement (Bilal, 2026-08-21) across every
   section checked so far (Home Services/BeautyCare/Men's Grooming all
   read Top:60/Bottom:60/Right:64/Left:64 in Figma's Layout panel). The
   64px left/right is redundant with .container's own width:min() gutter
   at desktop widths, so not duplicated here. */
.section { padding: 60px 0; }
.section--muted { background: var(--kk-surface); }
.section--dark { background: #111; }
@media (max-width: 860px) { .section { padding: 56px 0; } }
/* Sitewide spacing pass (2026-09-11) — Bilal: "a lot of unnecessary white
   space... too much space above the eyebrow... same issue... below the
   main content as well", flagged as systemic, not page-specific. Each
   .section's own 60px top+bottom is the real, individually Figma-measured
   value (see the comment above) and is left untouched — the actual
   culprit, confirmed by reading the real markup (business-hub.php and
   every other multi-section page share this same shape): two adjacent
   .section elements each apply their own full 60px, so the visible gap at
   EVERY such boundary sitewide is 60+60=120px — literally the exact "not
   120px" number that same comment already flagged as wrong, just
   re-introduced by stacking two individually-correct values rather than
   one wrong one. Fixed at the boundary, not by shrinking each section's
   own padding (which would understate every section's own real, confirmed
   Figma measurement): the SECOND of two adjacent sections gets a reduced
   top padding, so the total gap becomes one section's real bottom padding
   (60px, untouched) plus a real but smaller cushion (24px) instead of two
   full paddings stacked — a deliberate, moderate reduction (not zeroed
   out, so the design still reads as separated sections, just not doubled)
   since Figma's own frame-to-frame page rhythm wasn't re-measured this
   pass. Reaches every page using this class consistently, not one section
   at a time. */
.section + .section { padding-top: 24px; }

.skip-link { position: absolute; left: -9999px; top: 0; z-index: 1000; background: var(--kk-ink); color: #fff; padding: 8px 16px; }
.skip-link:focus { left: 16px; top: 16px; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; padding: 14px 24px; border-radius: 14px; font-weight: 800; font-size: 14px; border: none; cursor: pointer; transition: transform .15s, box-shadow .15s; }
.btn--sm { padding: 10px 18px; font-size: 13px; border-radius: 12px; }
.btn--lg { padding: 18px 36px; font-size: 15px; }
.btn--block { width: 100%; }
.btn--primary { background: linear-gradient(135deg, var(--kk-orange), var(--kk-orange-dark)); color: #fff; box-shadow: 0 8px 24px rgba(250,107,44,.3); }
.btn--dark { background: linear-gradient(135deg, var(--kk-blue), var(--kk-blue-dark)); color: #fff; box-shadow: 0 8px 24px rgba(10,55,143,.25); }
/* Hero's own "Open the App" — confirmed 2026-08-27 against Figma this is
   solid black, not the navy .btn--dark used elsewhere (for-business.php's
   own CTA) — a separate class rather than changing .btn--dark globally,
   since that navy usage elsewhere wasn't flagged as wrong. */
.btn--black { background: #0D0D0D; color: #fff; box-shadow: 0 8px 24px rgba(0,0,0,.2); }
.btn--outline-gray { background: #fff; color: var(--kk-ink); border: 1.5px solid var(--kk-line); }
.btn--outline-gray .icon { color: var(--kk-muted); }
.btn--purple { background: linear-gradient(135deg, var(--kk-beauty), var(--kk-beauty-dark)); color: #fff; box-shadow: 0 8px 24px rgba(214,45,249,.3); }
.btn--grooming { background: linear-gradient(135deg, var(--kk-grooming), var(--kk-grooming-dark)); color: #fff; box-shadow: 0 8px 24px rgba(10,55,143,.3); }
.btn--muted { background: #F3F4F6; color: var(--kk-ink); }
.btn--white { background: #fff; color: var(--kk-ink); }
.btn--white-on-orange { background: #fff; color: var(--kk-orange); box-shadow: 0 8px 24px rgba(0,0,0,.15); }
.btn--outline { background: transparent; color: var(--kk-ink); border: 2px solid var(--kk-line); }
.btn--outline-green { background: transparent; color: #059669; border: 2px solid var(--kk-green); }
.btn--outline-light { background: rgba(255,255,255,.1); color: #fff; border: 2px solid rgba(255,255,255,.35); backdrop-filter: blur(8px); }
.btn--whatsapp { background: #25D366; color: #fff; box-shadow: 0 8px 24px rgba(37,211,102,.3); }
.btn:hover { transform: translateY(-2px); }

/* ── Badge / section head ───────────────────────────────────────────── */
.badge { display: inline-flex; align-items: center; gap: 6px; background: var(--kk-orange-light); color: var(--kk-orange); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; padding: 6px 14px; border-radius: 100px; margin-bottom: 20px; }
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .7; }
.badge--purple { background: rgba(214,45,249,.1); color: var(--kk-beauty-dark); }
.badge--grooming { background: rgba(10,55,143,.1); color: var(--kk-grooming-dark); }
.badge--blue { background: var(--kk-blue-light); color: var(--kk-blue); }
.badge--green { background: rgba(16,185,129,.1); color: #059669; }
.badge--on-dark { background: rgba(255,255,255,.1); color: #fff; }

/* Real bug fixed 2026-09-04 (Bilal, on /for-business/: eyebrow badge and
   heading rendering far apart instead of stacked) — this base rule was
   `flex-direction: row; justify-content: space-between`, spreading the
   badge and <h2> to opposite ends of the full container width. That layout
   only makes sense paired with a right-side `.section-head-aside` element —
   and a full grep confirms `.section-head-aside` is defined but has ZERO
   real callers anywhere in the theme. Every actual usage of the bare
   `.section-head` class (business-hub.php ×3 — Capabilities/Industries/
   Process, builder-inquiry.php, business-inquiry.php) is just a badge+h2
   pair with no aside — and the very next rule below, this class's own
   ≤860px override, already sets `flex-direction: column; align-items:
   flex-start` — i.e. the "stacked, left-aligned" layout Figma actually
   shows was already the intended MOBILE default; the desktop rule just
   never matched it. Fixed to match, sitewide default now: stacked +
   left-aligned at every width, not just ≤860px. `.section-head-aside`'s
   own rule is left in place, unused, in case a future section actually
   pairs one with a right-side aside — not deleted speculatively. */
/* margin-bottom reduced 56px -> 36px 2026-09-11, same sitewide spacing
   pass as .section + .section above — Bilal's own complaint named this
   exact gap directly ("too much space between the Capability heading/
   content and the cards"). Not dropped to mobile's already-smaller 32px
   outright — kept a small, deliberate desktop > mobile difference rather
   than making the two identical outright. */
.section-head { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; margin-bottom: 36px; }
/* Real, confirmed second cause of the same sitewide spacing report
   (2026-09-11, Bilal's follow-up: "still a lot of white space in between
   ALL these elements" — eyebrow, heading, AND subtitle, not just the head
   block as a whole). .section-head is a flex column with its own
   `gap: 12px` between children — but .badge (`margin-bottom: 20px`),
   .section-title--sm (`margin-bottom: 20px`), and .section-subtitle
   (`margin: 10px 0 32px`) EACH also carry their own real margin. Flexbox
   never collapses a flex item's own margin against the container's `gap`
   or against sibling margins — they fully stack — so the real rendered
   gap between badge and heading was 20 (badge's own margin) + 12 (flex
   gap) = 32px, same again between heading and subtitle, and the subtitle's
   OWN 32px trailing margin was ALSO stacking on top of .section-head's own
   36px margin-bottom (just fixed above) before whatever content follows.
   The previous pass fixed .section-head's own margin and the section-to-
   section boundary but never touched this — a genuinely separate,
   compounding source, not the same bug re-appearing. Fixed at the exact
   place it happens: zero out each child's own margin specifically when
   it's a direct child of .section-head, so the flex `gap` is the ONE real
   source of spacing between them — every other usage of .badge/.section-
   title/.section-subtitle OUTSIDE a .section-head wrapper (e.g. the
   "Get started" badge+heading pair on business-hub.php, which isn't
   wrapped in .section-head at all) is completely untouched, since those
   still need their own margin as their only spacing mechanism. */
.section-head > .badge { margin-bottom: 0; }
.section-head > .section-title { margin-bottom: 0; }
.section-head > .section-subtitle { margin: 0; }
/* Systemic fix (2026-09-09) — this margin-bottom exists to separate a
   heading from CONTENT that follows it in the same section (a grid, cards,
   a form). Several real sections sitewide are heading-ONLY — a standalone
   CMS intro block with nothing else in that <section> — where this same
   margin becomes pure dead trailing space with nothing to separate from,
   directly inflating the page's own height for no visual reason. This
   exact pattern had already been patched three separate times, one-off,
   scoped to a single page each (`.contact-hero .section-head`, `.about-
   hero .section-head`, the Home Services teaser's own heading) rather than
   fixed at the root — found again on Cleaning Services' standalone
   "Trusted Home Cleaning Services..." intro section while investigating
   Bilal's sitewide spacing report (a real 56px of dead space, confirmed by
   direct measurement, not assumed). :last-child correctly targets every
   such case sitewide going forward (a .section-head with nothing after it
   inside its own container) without needing a new per-page override each
   time one is found — the 3 existing one-off overrides above are
   redundant now but harmless (same value, just no longer the only fix). */
.section-head:last-child { margin-bottom: 0; }
.section-head--center { align-items: center; text-align: center; }
.section-head-aside { max-width: 240px; text-align: right; color: var(--kk-muted); font-size: 15px; line-height: 1.7; }
.section-head-aside--on-dark { color: #93C5FD; }
/* Real bug fixed 2026-09-03 (Bilal, on HomeCare: "eyebrow, heading and
   content needs to be center align") — `.section-head--center` (line
   243, no media query, applies at every width) and this mobile rule for
   plain `.section-head` are equal specificity (both single-class
   selectors); on any element carrying BOTH classes (every centered
   section-head sitewide — HomeCare, Why KaamKrew, category-page "Why
   book with KaamKrew"/FAQ/testimonials, the review sections, etc., 12
   templates in total), source order decided the winner at ≤860px, and
   this rule comes later in the file — so `align-items: flex-start` was
   silently overriding `--center`'s `align-items: center` on every one of
   them, sitewide, not just HomeCare. Fixed by explicitly re-asserting
   center alignment for the `--center` variant inside this same query,
   rather than just for HomeCare — the bug was never HomeCare-specific. */
@media (max-width: 860px) { .section-head { flex-direction: column; align-items: flex-start; margin-bottom: 32px; } .section-head-aside { text-align: left; max-width: none; } .section-head--center { align-items: center; } }

/* Exact max values confirmed against real Figma text layers, 2026-08-27
   (Cleaning Services Hub node 2154-42555, Home Services Hub node
   2118-38015): H1 = 66px / line-height 72.6 (1.1) / letter-spacing -1px;
   H2 (--sm) = 42px / line-height 48.3 (1.15) / letter-spacing 0.
   RECALIBRATED 2026-08-29, twice the same day (Option B). First pass:
   the original clamp() coefficients reached 66px/42px at this site's own
   container width (~1320px) — a 1440/1536px laptop rendered the exact
   same absolute size as a 1920px monitor — so the curve was re-anchored
   between 375px and 1920px instead, min/max bounds unchanged.
   CORRECTED same day, second pass: even that 1920px anchor was too
   aggressive — Bilal's real ~1900px window still looked visibly larger
   than the Figma prototype player showing the same section (the player
   scales its 1920 frame to fit whatever window it's opened in, which is
   the actual intended "fits the real screen" behavior). Re-anchored
   again, from 1920px out to 2560px — min/max bounds STILL unchanged
   (still 32-66px / 28-42px, the real Figma-confirmed numbers), only
   WHERE the ceiling is reached moved further out. Default mode now
   never reaches the literal 66px/42px on any normal 1366-1920px laptop/
   monitor — only at >=2560px, or at any width via the .kk-figma-strict
   switch below (that mechanism itself didn't change — it was never
   viewport-anchored, so "no gap on revert" still holds exactly). */
.section-title { font-size: clamp(2rem, 1.6353rem + 1.5561vw, 4.125rem); line-height: 1.1; color: var(--kk-ink); font-weight: 700; letter-spacing: -1px; text-wrap: balance; }
.section-title--sm { font-size: clamp(1.75rem, 1.5998rem + 0.6407vw, 2.625rem); line-height: 1.15; letter-spacing: 0; margin-bottom: 20px; }
.section-title--light { color: #fff; }
/* 2026-09-02: "Loved by Pakistani families." was wrapping to 2 lines on
   mobile at .section-title's normal clamp() size (~32px, far too wide for
   that full phrase in a ~340px container) — Figma keeps it on one line.
   Scoped to the testimonials heading specifically, not a global
   .section-title change — other headings on this page (e.g. "Everything
   Your Home Needs, One-call Away.") are MEANT to wrap across multiple
   lines at their normal size. */
@media (max-width: 640px) { #testimonials .section-title { font-size: clamp(1.25rem, 1rem + 2.5vw, 1.5rem); } }
.section-lead { font-size: 17px; color: var(--kk-muted); max-width: 460px; margin: 14px auto 0; line-height: 1.75; }
/* Stacked badge -> heading -> subtitle -> content, left-aligned — the
   real pattern for Home Services/BeautyCare/Men's Grooming (no side-by-
   side aside text; confirmed absent in Bilal's screenshots). */
.section-subtitle { font-size: 13.5px; color: var(--kk-muted); max-width: 640px; margin: 10px 0 32px; line-height: 1.6; }
.section-subtitle--on-dark { color: #93C5FD; }
/* 2026-09-05, Bilal's real screenshot flag (Cleaning Services hub's
   "Mattress Cleaning" sub-section): the base 640px cap is a deliberate
   readability constraint for the CENTERED lede paragraphs under a
   .section-head--center (a real, already-confirmed design choice — left
   untouched). It was never meant for a LEFT-ALIGNED description sitting
   under a plain, non-centered heading — there it just wraps early and
   leaves a visible dead-space gap the rest of the section's own width
   doesn't have. Applied at each real, confirmed instance of that second
   case rather than a blanket ":not(.section-head--center)" selector,
   since the paragraph isn't always a direct child of .section-head. */
.section-subtitle--full { max-width: none; }
/* Confirmed against Figma (2026-08-27): the italic accent phrase within a
   heading is set in "DM Serif Text" (italic), not an italicized cut of the
   heading's own base font (Inter). Applies wherever .accent-* sits inside
   an h1/h2/.section-title. */
.accent-orange, .accent-purple, .accent-grooming, .accent-blue, .accent-green { font-family: 'DM Serif Text', Georgia, serif; font-style: italic; font-weight: 400; }
.accent-orange { color: var(--kk-orange); }
.accent-purple { color: var(--kk-beauty); }
.accent-grooming { color: var(--kk-grooming); }
.accent-blue { color: var(--kk-blue); }
.accent-green { color: var(--kk-green); }

.section-cta { margin-top: 36px; display: flex; justify-content: center; }

/* ── Header ──────────────────────────────────────────────────────────── */
/* Two rows, ~72px total on desktop (115px at the design's 1920px canvas,
   scaled to the --kk-container width) — corrected 2026-08-21 against the
   real approved design, see header.php's header comment. */
.site-header { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: #fff; box-shadow: 0 1px 0 rgba(0,0,0,.07); transition: box-shadow .3s; }
.site-header.is-scrolled { background: rgba(255,255,255,.94); backdrop-filter: blur(20px); box-shadow: 0 1px 0 rgba(0,0,0,.07), 0 8px 32px rgba(0,0,0,.06); }
.site-header-row { width: min(var(--kk-container), 100% - 32px); margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.site-header-row--top { height: 58px; }
.site-header-row--nav { height: 57px; border-top: 1px solid var(--kk-line-soft); }
.site-logo-link { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.site-logo-text { font-weight: 900; font-size: 17px; letter-spacing: -.5px; color: var(--kk-ink); }
/* No real Figma export dimensions confirmed for either logo this pass —
   constrained by DISPLAY height instead (standard for an admin-uploaded
   image of unknown native resolution): upload any resolution, this caps
   how tall it renders. Recommended source size: at least 2x the display
   height so it stays crisp on retina (e.g. 96px+ tall source for the
   80px-tall header slot this constrains). */
.site-logo { max-height: 40px; width: auto; display: block; }
.site-logo--footer { max-height: 32px; }
main#main-content { padding-top: var(--kk-header-h); }

.mega-nav { display: flex; align-items: center; gap: 24px; }
.mega-nav-item { position: relative; }
.mega-nav-trigger { background: none; border: none; cursor: pointer; font-size: 13px; font-weight: 600; color: #374151; white-space: nowrap; display: flex; align-items: center; gap: 4px; padding: 4px 0; }
.mega-nav-trigger:hover, .mega-nav-item.is-open .mega-nav-trigger { color: var(--kk-orange); }
.mega-nav-item.is-open .mega-nav-trigger .icon { transform: rotate(180deg); }
.mega-panel { display: none; position: absolute; top: calc(100% + 14px); left: 50%; transform: translateX(-50%); background: #fff; border-radius: 20px; box-shadow: 0 24px 64px rgba(0,0,0,.14); border: 1px solid var(--kk-line-soft); padding: 20px; z-index: 200; min-width: 240px; }
.mega-nav-item.is-open .mega-panel { display: block; }
/* Bug fix 2026-08-24: centering every panel on its own trigger (left:50% +
   translateX(-50%)) pushed wide panels off the left edge of the viewport
   for nav items near the left edge — confirmed via Bilal's screenshot on
   "Home Services" (the first item), and re-confirmed via real
   getBoundingClientRect() measurements that "BeautyCare" (the second item)
   overflowed too, by a smaller amount — not just a first/last-child special
   case, since it depends on each panel's actual rendered width (1/2/3
   columns) vs. how close its trigger sits to either edge. A pure CSS rule
   can't know a dynamic width vs. viewport width at author time, so this is
   clamped in JS instead — see assets/js/nav.js's positionMegaPanel(),
   which measures the real rect on open and only overrides left/transform
   when the default centered position would actually overflow. */
.mega-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #F3F4F6; gap: 24px; }
.mega-panel-eyebrow { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: var(--kk-muted-2); }
.mega-panel-viewall { font-size: 11px; font-weight: 700; color: var(--kk-orange); display: flex; align-items: center; gap: 3px; }
.mega-panel-grid { display: grid; grid-template-columns: repeat(var(--mega-cols, 1), 1fr); gap: 4px; min-width: max-content; }
.mega-panel-cell { position: relative; }
.mega-panel-link { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 10px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; color: #374151; transition: background .12s, color .12s; }
.mega-panel-link:hover { background: var(--kk-orange-light); color: var(--kk-orange); }
.mega-panel-icon { width: 32px; height: 32px; border-radius: 10px; background: #F9FAFB; display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: #6B7280; }
.mega-panel-caret { margin-left: auto; color: var(--kk-muted-2); }
.mega-panel-nested { display: none; position: absolute; left: 100%; top: 0; background: #fff; border-radius: 16px; box-shadow: 0 16px 40px rgba(0,0,0,.12); border: 1px solid var(--kk-line-soft); padding: 8px 0; min-width: 200px; z-index: 210; }
.mega-panel-cell.is-open .mega-panel-nested { display: block; }
.mega-panel-nested a { display: block; padding: 10px 18px; font-size: 13px; font-weight: 500; color: #374151; }
.mega-panel-nested a:hover { background: var(--kk-orange-light); color: var(--kk-orange); }

.site-header-actions { display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
.city-select { display: flex; align-items: center; gap: 5px; background: var(--kk-orange-light); border: none; border-radius: 100px; padding: 6px 12px; cursor: pointer; font-size: 12px; font-weight: 600; color: #374151; }
.header-link { font-size: 13px; font-weight: 600; color: var(--kk-ink); }
.icon-btn { background: none; border: none; cursor: pointer; color: #6B7280; display: flex; align-items: center; justify-content: center; padding: 6px; position: relative; }
.cart-btn { color: #6B7280; }
.site-header-nav-actions { display: flex; align-items: center; gap: 14px; }

/* Header search toggle (2026-09-05) — the icon button lives in normal
   flow; the expanding field is an absolutely-positioned dropdown anchored
   to this wrapper, matching the .mega-panel pattern already used for the
   nav's own dropdowns. [hidden] override is required, not decorative —
   .header-search-form's own `display:flex` below is the exact same
   specificity as the browser's UA `[hidden]{display:none}` rule, and this
   project has hit that exact silent-double-render bug several times
   already (city-modal, testimonials, account-auth-panel) — always pair a
   `display` declaration on a toggle target with its own `[hidden]` override. */
.header-search { position: relative; }
.header-search-form { position: absolute; top: 100%; right: 0; margin-top: 10px; display: flex; align-items: center; gap: 8px; background: #fff; border-radius: 14px; padding: 8px 8px 8px 14px; box-shadow: 0 16px 48px rgba(0,0,0,.16); border: 1px solid var(--kk-line-soft); width: 320px; z-index: 220; }
.header-search-form[hidden] { display: none; }
.header-search-form .icon { flex-shrink: 0; width: 16px; height: 16px; color: var(--kk-muted-2); }
.header-search-form input { flex: 1; min-width: 0; border: none; outline: none; font-size: 13px; font-weight: 500; background: transparent; }

.site-header-mobile-actions { display: none; align-items: center; gap: 4px; }
.hamburger, .hamburger::before, .hamburger::after { content: ''; display: block; width: 20px; height: 2px; background: var(--kk-ink); border-radius: 2px; transition: transform .2s; }
.hamburger { position: relative; }
.hamburger::before { position: absolute; top: -6px; }
.hamburger::after { position: absolute; top: 6px; }

@media (max-width: 860px) {
	.site-header-row--nav, .site-header-actions { display: none; }
	.site-header-mobile-actions { display: flex; }
	.site-header-row--top { height: var(--kk-header-h-mobile); padding: 0 16px; }
	main#main-content { padding-top: var(--kk-header-h-mobile); }
}

/* City pill takes the search icon's slot in the mobile header row — a
   touch more compact than the desktop pill (see header.php's own comment
   on why it's there at all). */
.city-select--mobile { font-size: 11.5px; padding: 5px 10px; }

/* Full-width mobile search bar (2026-09-02, homepage-only — see
   header.php's own comment) — sits in normal document flow directly under
   the fixed header, scrolling away with the rest of the page like every
   other section; nothing like this exists at desktop widths, where the
   hero's own search bar covers this. */
.mobile-search-bar { display: none; }
@media (max-width: 860px) {
	.mobile-search-bar {
		display: flex; align-items: center; gap: 10px;
		margin-top: var(--kk-header-h-mobile);
		background: var(--kk-blue);
		padding: 12px 16px;
	}
	.mobile-search-bar-field { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; background: #fff; border-radius: 12px; padding: 10px 14px; }
	.mobile-search-bar-field .icon { flex-shrink: 0; width: 16px; height: 16px; color: var(--kk-muted-2); }
	.mobile-search-bar-field input { flex: 1; min-width: 0; border: none; outline: none; background: transparent; font-size: 13px; font-weight: 500; }
	.mobile-search-bar .btn { flex-shrink: 0; padding: 10px 16px; font-size: 13px; }
	/* The search bar's own margin-top already clears the fixed header (it's
	   now site-wide, not homepage-only, since 2026-09-03) — main's own
	   padding-top would double that gap, since the bar sits between the
	   header and <main> in the DOM on every page now. */
	main#main-content { padding-top: 0; }
}

/* ── City modal (added 2026-08-26) ──────────────────────────────────── */
.city-select { transition: background .15s; }
.city-select:hover { background: #FBDECE; }
.city-modal[hidden] { display: none; }
.city-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; padding: 20px; }
.city-modal-scrim { position: absolute; inset: 0; background: rgba(13,13,13,.5); backdrop-filter: blur(4px); }
.city-modal-panel { position: relative; z-index: 1; width: 100%; max-width: 420px; max-height: 82vh; overflow-y: auto; background: #fff; border-radius: var(--kk-radius); box-shadow: 0 24px 64px rgba(0,0,0,.25); padding: 24px; }
.city-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.city-modal-head h2 { font-size: 18px; font-weight: 800; color: var(--kk-ink); margin: 0; }
.city-modal-sub { font-size: 13px; color: #6B7280; margin: 6px 0 18px; }
.city-modal-list { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 420px) { .city-modal-list { grid-template-columns: minmax(0, 1fr); } }
.city-modal-option { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-radius: 12px; border: 1px solid #E5E7EB; font-size: 14px; font-weight: 600; color: var(--kk-ink); transition: border-color .15s, background .15s; }
.city-modal-option:hover { border-color: var(--kk-orange); background: var(--kk-orange-light); }
.city-modal-option.is-active { border-color: var(--kk-orange); background: var(--kk-orange-light); color: var(--kk-orange-dark); }
.city-modal-option .icon { flex-shrink: 0; color: var(--kk-orange); }
.city-modal-option-icon { width: 32px; height: 32px; flex-shrink: 0; object-fit: contain; }

/* City-switch confirm view (2026-09-06) — a second panel inside the same
   .city-modal-panel, not a separate popup (see city-modal.php's own header
   comment). [hidden] on the view itself is enough to hide it (no extra
   .city-modal-view rule needed) — this only styles what's inside it. */
.city-modal-confirm-text { font-size: 14px; line-height: 1.6; color: var(--kk-ink); margin: 4px 0 22px; }
.city-modal-confirm-text strong { color: var(--kk-orange); }
.city-modal-confirm-actions { display: flex; gap: 10px; justify-content: flex-end; }
.city-modal-confirm-actions .btn { flex: 1; }

/* ── Mobile drawer ───────────────────────────────────────────────────── */
.mobile-drawer[hidden] { display: none; }
.mobile-drawer { position: fixed; inset: 0; z-index: 200; display: flex; }
.mobile-drawer-scrim { position: absolute; inset: 0; background: rgba(0,0,0,.45); backdrop-filter: blur(4px); }
.mobile-drawer-panel { position: relative; z-index: 1; width: 84vw; max-width: 340px; height: 100%; background: #fff; overflow-y: auto; box-shadow: 4px 0 32px rgba(0,0,0,.18); }
.mobile-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid #F3F4F6; }
.mobile-drawer-item { border-bottom: 1px solid transparent; }
.mobile-drawer-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 14px 20px; background: none; border: none; cursor: pointer; font-size: 14px; font-weight: 700; color: var(--kk-ink); text-align: left; }
.mobile-drawer-item.is-open .mobile-drawer-toggle .icon { transform: rotate(180deg); }
.mobile-drawer-sub[hidden] { display: none; }
.mobile-drawer-sub { background: #F9FAFB; border-top: 1px solid #F3F4F6; border-bottom: 1px solid #F3F4F6; }
.mobile-drawer-viewall { display: flex; align-items: center; gap: 8px; padding: 11px 28px; font-size: 13px; font-weight: 700; color: var(--kk-orange); }
.mobile-drawer-sublink { display: flex; align-items: center; gap: 10px; padding: 10px 28px; font-size: 13px; font-weight: 600; color: #374151; }
.mobile-drawer-actions { padding: 20px; display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }

/* ── Hero ────────────────────────────────────────────────────────────── */
/* Section 705.34px total (Header 115 + Hero 705.34 = 820.34, per Bilal's
   measurement), inner grid content Hug = 624.34px -> ~40px top/bottom. */
.hero { padding: 40px 0 41px; background: linear-gradient(150deg, #fff 0%, #fff8f4 45%, #f2f6ff 100%); }
.hero-inner { display: grid; grid-template-columns: 1fr 1fr; column-gap: 80px; row-gap: 80px; align-items: center; }
.hero-eyebrow { display: inline-block; background: var(--kk-orange-light); color: var(--kk-orange); border-radius: 999px; padding: 5px 12px; font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; margin: 0 0 16px; }
/* font-weight and the accent's missing font-family were the same two gaps
   found and fixed on .section-title/.accent-* on 2026-08-27 — this class
   just hadn't been touched in that pass since it's hero.php's own,
   separate from the category-page headings that prompted it. Size (44px
   cap) is UNCHANGED — not confirmed against Figma this pass, Bilal's own
   screenshot only measured the "Everything your home needs..." homepage
   SECTION heading (.section-title--homepage below), not this hero H1. */
/* RECALIBRATED 2026-08-29 (Option B), twice — see .section-title above
   for the full story. min/max bounds unchanged (30-44px, the 44px cap
   itself still NOT independently Figma-confirmed); anchor now 2560px,
   not 1920px — default mode no longer reaches 44px on any normal
   laptop/monitor, only >=2560px or via .kk-figma-strict. */
.hero-title { font-size: clamp(1.875rem, 1.7248rem + 0.6407vw, 2.75rem); line-height: 1.12; letter-spacing: -.3px; color: var(--kk-ink); margin: 0 0 14px; font-weight: 700; }
.hero-title-accent { font-family: 'DM Serif Text', Georgia, serif; color: var(--kk-orange); font-style: italic; font-weight: 400; }

/* Confirmed against Figma 2026-08-27 (Bilal's own screenshot, Typography
   panel on the real "Everything your home needs, one call away." layer):
   58px, line-height 63.8 (1.1), letter-spacing 0 — smaller than the 66px
   .section-title uses for a category hub page's own H1. The homepage's 3
   teaser sections (Home Services/BeautyCare/Men's Grooming) all share this
   size tier, confirmed for one and applied consistently to the other two
   since all three use identical markup/weight/pattern. */
/* RECALIBRATED 2026-08-29 (Option B), twice — see .section-title above
   for the full story. 58px max unchanged (Figma-confirmed); anchor now
   2560px, not 1920px — this is the exact heading Bilal's screenshot
   comparison flagged as still too large at his real ~1900px window. */
.section-title--homepage { font-size: clamp(1.75rem, 1.4282rem + 1.3730vw, 3.625rem); line-height: 1.1; letter-spacing: 0; }
.hero-subtitle { font-size: 14.5px; line-height: 1.65; color: var(--kk-muted); margin-bottom: 22px; max-width: 460px; }
.hero-search { position: relative; display: flex; align-items: center; gap: 8px; padding: 7px; border-radius: 14px; background: #fff; box-shadow: 0 4px 28px rgba(0,0,0,.09); margin-bottom: 16px; max-width: 470px; }
.hero-search .icon { margin-left: 8px; color: var(--kk-muted-2); flex-shrink: 0; width: 17px; height: 17px; }
.hero-search input { flex: 1; min-width: 0; border: none; outline: none; font-size: 12.5px; font-weight: 500; background: transparent; padding: 0 4px; }
.hero-search .btn { padding: 9px 18px; font-size: 12.5px; }

/* ── Site-wide search suggestions dropdown (2026-09-15) ─────────────────
   assets/js/search.js appends this as a sibling of the real search
   <input> inside its own already-positioned wrapper (.hero-search /
   .mobile-search-bar-field, both given position:relative above). Renders
   REAL card-product.php markup (server-rendered, inc/search-ajax.php) —
   .search-suggestions-list below only re-proportions that same component
   into a compact horizontal row for a dropdown context, the same "reuse
   the mobile Single View card treatment, just smaller" idea already
   proven for .kk-mobile-single elsewhere in this file, not a new card
   design. */
.search-suggestions { position: absolute; top: calc(100% + 8px); left: 0; right: 0; background: #fff; border-radius: 14px; box-shadow: 0 16px 48px rgba(0,0,0,.16); border: 1px solid var(--kk-line-soft); max-height: 420px; overflow-y: auto; z-index: 50; text-align: left; }
.search-suggestions-state { padding: 20px 16px; text-align: center; font-size: 13px; color: var(--kk-muted); display: flex; align-items: center; justify-content: center; gap: 8px; }
.search-suggestions-state--error { color: var(--kk-orange-dark); }
.search-suggestions-spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--kk-line); border-top-color: var(--kk-orange); animation: kk-search-spin .7s linear infinite; }
@keyframes kk-search-spin { to { transform: rotate(360deg); } }
.search-suggestions-list { padding: 6px; }
.search-suggestions-list .product-card { display: flex; flex-direction: row; align-items: center; gap: 12px; padding: 10px; border-radius: 10px; background: transparent; box-shadow: none; border: none; transition: background .15s ease; }
.search-suggestions-list .product-card:hover { background: var(--kk-surface); transform: none; box-shadow: none; }
.search-suggestions-list .product-card-image { width: 56px; height: 56px; flex-shrink: 0; border-radius: 10px; }
.search-suggestions-list .product-card-body { flex: 1; min-width: 0; padding: 0; }
.search-suggestions-list .product-card-meta { margin-bottom: 2px; }
.search-suggestions-list .product-card-category { font-size: 9px; }
.search-suggestions-list .product-card-duration { font-size: 9px; }
.search-suggestions-list .product-card-name { font-size: 13px; -webkit-line-clamp: 1; min-height: 0; margin-bottom: 4px; }
.search-suggestions-list .product-card-subtitle { display: none; } /* keep the row compact — the full detail still shows once a visitor is on the real Search Results page */
/* Real, confirmed bug (2026-09-16, reported directly against a live
   screenshot — Beauty Care's "Book Appointment" label, but every category
   was affected the same way): .product-card-price-row's own BASE rule
   (further down this file) is `display:flex; justify-content:space-between`
   — price on the left, the Add/Book-Appointment button or quantity stepper
   pinned to the right — which only has room in a normal grid card
   (200px+ wide). This dropdown's own card is a compact 56px-image row, so
   .product-card-body — everything to the right of the image — is often
   under 200px wide once the dropdown's own padding/gap are subtracted;
   there was never enough width for a price string AND a pill button
   side by side, so the button visibly overlapped the price at EVERY
   screen size, not just mobile — the existing `<1024px` compact-tier fix
   for the real grid (`.product-grid-cat .product-card-price-row { flex-
   direction:column }`) is correctly scoped to `.product-grid-cat` only
   and was never meant to reach this dropdown, so it did nothing here.
   Fixed the same way: stack price above the button/stepper unconditionally
   here, since this component's own container is always this narrow
   regardless of the visitor's actual screen width. */
.search-suggestions-list .product-card-price-row { flex-direction: column; align-items: flex-start; margin-bottom: 0; gap: 6px; }
.search-suggestions-list .price-now { font-size: 13px; }
.search-suggestions-list .price-old { font-size: 11px; }
.search-suggestions-list .product-card-add { padding: 6px 10px; font-size: 11px; }
.search-suggestions-list .product-card-qty-btn { width: 26px; height: 26px; }
.search-suggestions-list .product-card-qty-value { font-size: 12px; }
.search-suggestions-list .product-card-discount { top: 2px; left: 2px; font-size: 8px; padding: 1px 5px; }
@media (max-width: 640px) { .search-suggestions { max-height: 60vh; } }

.hero-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 28px; }
.chip { background: #F9FAFB; border: 1px solid var(--kk-line); border-radius: 100px; padding: 6px 11px; font-size: 10.5px; font-weight: 600; color: var(--kk-muted); }
.chip:hover { border-color: var(--kk-orange); color: var(--kk-orange); }
.hero-cta-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hero-cta-row .btn { padding: 11px 20px; font-size: 13px; border-radius: 12px; }

/* Category-page hero "quick facts" row (2026-08-30) — e.g. Men's Grooming's
   area/hours/scheduling badges, Contact's trust-badge row. CMB2-driven, see
   inc/category-cms.php's `quick_facts` repeater. */
.hero-quick-facts { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hero-quick-facts li { font-size: 13.5px; font-weight: 600; color: var(--kk-muted); display: flex; align-items: center; gap: 8px; }

/* Sized so the right column's natural height (image + overlapping float
   cards) drives the grid row to ~624px Hug, matching the real design —
   the left (text) column is naturally shorter and centers within that. */
.hero-visual { position: relative; height: 560px; display: flex; align-items: center; justify-content: center; }
.hero-visual-glow { position: absolute; inset: 0; border-radius: 32px; background: radial-gradient(ellipse at 60% 50%, rgba(250,107,44,.1) 0%, transparent 70%); }
.hero-visual-phone { width: 280px; height: 400px; border-radius: 32px; box-shadow: 0 28px 68px rgba(0,0,0,.17); border: 3px solid rgba(255,255,255,.8); position: relative; z-index: 2; background: linear-gradient(160deg, #e5e7eb, #cbd5e1); }
/* Real hero photo (2026-08-27) — same box the placeholder occupied, so the
   floating cards' existing overlap positioning (.hero-float--*) needs no
   change once a real image is uploaded via the Hero photo CMB2 field. */
.hero-visual-photo { width: 320px; height: 420px; object-fit: cover; border-radius: 32px; box-shadow: 0 28px 68px rgba(0,0,0,.17); border: 3px solid rgba(255,255,255,.8); position: relative; z-index: 2; }
.hero-float { position: absolute; width: 172px; border-radius: 18px; background: #fff; box-shadow: 0 10px 32px rgba(0,0,0,.12); border: 1px solid var(--kk-line-soft); padding: 16px; z-index: 3; }
.hero-float-icon { width: 36px; height: 36px; border-radius: 11px; background: var(--kk-orange-light); display: flex; align-items: center; justify-content: center; font-size: 18px; margin-bottom: 10px; }
.hero-float-title { font-weight: 700; font-size: 12.5px; color: var(--kk-ink); margin-bottom: 3px; }
.hero-float-sub { font-size: 10.5px; color: var(--kk-muted); line-height: 1.35; }
.hero-float--home { top: 32px; left: -10px; }
.hero-float--beauty { bottom: 32px; right: -10px; }
.hero-float--groom { top: 50%; right: -28px; transform: translateY(-50%); width: 148px; background: var(--kk-blue); }
.hero-float--groom .hero-float-title, .hero-float--groom .hero-float-sub { color: #fff; }
.hero-float--groom .hero-float-icon { background: rgba(255,255,255,.15); }
.hero-float--booked { bottom: 96px; left: -24px; width: 148px; box-shadow: 0 8px 22px rgba(0,0,0,.1); padding: 12px 14px; }

/* 2026-09-02: the hero photo + floating cards were previously hidden
   outright below 860px — a real gap against the mobile Figma, which keeps
   them, just reflowed to sit below the text column at a smaller scale
   with the floats pulled inward so they never overflow the viewport
   edges (the desktop offsets go negative — e.g. left:-10px — which is
   fine against a wide visual column but would clip against a 375-428px
   screen's own edge). */
@media (max-width: 860px) {
	.hero { padding: 24px 0 32px; }
	/* minmax(0, 1fr), not plain 1fr — a bare 1fr track still won't shrink a
	   grid item below its CONTENT's own natural min-content width (the
	   classic CSS Grid sizing gap). That's what was actually causing the
	   real page-wide horizontal scroll Bilal found on a real device
	   (confirmed live: .hero-copy was rendering 607px wide against a real
	   331px-ish available track before this fix) — html's own
	   overflow-x:hidden (added the same pass) is a second, independent
	   safety net, not a substitute for fixing the actual oversized track. */
	.hero-inner { grid-template-columns: minmax(0, 1fr); gap: 28px; }
	.hero-visual { height: 380px; }
	.hero-visual-phone { width: 220px; height: 300px; }
	.hero-visual-photo { width: 100%; max-width: 300px; height: 320px; }
	.hero-float { width: 142px; padding: 12px; }
	.hero-float-icon { width: 30px; height: 30px; margin-bottom: 8px; }
	.hero-float-title { font-size: 11.5px; }
	.hero-float-sub { font-size: 10px; }
	.hero-float--home { top: 8px; left: 4px; }
	.hero-float--beauty { bottom: 8px; right: 4px; }
	.hero-float--groom { width: 124px; right: -4px; }
	.hero-float--booked { bottom: 56px; left: -4px; width: 124px; }
}
@media (max-width: 420px) {
	.hero-visual { height: 340px; }
	.hero-visual-photo { max-width: 260px; height: 280px; }
	.hero-float { width: 128px; }
	.hero-float--groom, .hero-float--booked { width: 112px; }
}

/* 2026-09-02 (round 2 of mobile fixes, against Bilal's real-device
   screenshots): the category chips were wrapping to multiple lines
   instead of scrolling as a single row — Figma shows one horizontal
   carousel row, the next chip peeking at the edge. Same swipe pattern as
   the product-card rows elsewhere on this page. */
@media (max-width: 860px) {
	.hero-chips {
		flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
		margin: 0 -20px 28px; padding: 0 20px 2px;
	}
	.hero-chips::-webkit-scrollbar { display: none; }
	.hero-chips .chip { flex: 0 0 auto; }

	/* "Open the App" / "Need help? Chat with us" were wrapping onto their
	   own lines below ~390px — Figma keeps them on one row. Forced
	   nowrap + tightened padding/gap/font so both fit a real 375-390px
	   screen without either button truncating. */
	.hero-cta-row { flex-wrap: nowrap; gap: 6px; }
	/* min-width: 0 is the real fix — a flex item's default min-width:auto
	   refuses to shrink below its own nowrap text's full width, which is
	   exactly what pushed this row ~10px past the viewport edge on a real
	   375px screen (confirmed live). Padding/font tightened further on
	   top of that so both buttons' full text stays genuinely readable
	   rather than just barely not overflowing. */
	.hero-cta-row .btn { flex: 0 1 auto; min-width: 0; padding: 11px 10px; font-size: 11.5px; gap: 5px; white-space: nowrap; }
}

/* ── Trust bar ───────────────────────────────────────────────────────── */
.trust-bar { background: var(--kk-navy); }
/* Bar 90px tall (real Figma measurement) via vertical padding around one
   row of centered content — the literal number `.trust-bar-inner` still
   uses under body.kk-figma-strict, below. */
.trust-bar-inner { padding: 35px 0; display: flex; align-items: center; justify-content: center; gap: 48px; flex-wrap: wrap; }
/* Compacted on desktop (2026-09-15) — Bilal, real screenshot: "excessive
   white space on the top and bottom." Measured first: the bar's real
   content (icon + label) is only 18px tall, so the literal Figma padding
   above (35px top+bottom) was ~80% of the bar's real 88px height — the
   dominant, near-only lever here, same shape as the two other sections
   just compacted this same session. Reduced to 20px (58px total, a real
   ~34% cut) while staying a comfortable, deliberate padding value, not a
   cramped one. Reverts to the literal 35px under body.kk-figma-strict,
   same mechanism as every other reproportioned element in this file — a
   real, one-flag-away "back to exact Figma" guarantee, not silently
   discarded. Explicitly min-width-scoped (not just relying on source
   order) — a bare `body:not(.kk-figma-strict) .trust-bar-inner` selector
   is HIGHER specificity than the plain `.trust-bar-inner` the ≤860px
   mobile tier below uses, so without this it would have won on mobile too
   and silently overridden that tier's own separately-tuned 24px/16px
   padding regardless of media-query source order. */
@media (min-width: 861px) {
	body:not(.kk-figma-strict) .trust-bar-inner { padding: 20px 0; }
}
.trust-item { display: flex; align-items: center; gap: 8px; color: #fff; }
.trust-item .icon { opacity: .65; width: 16px; height: 16px; }
.trust-item span:last-child { font-size: 12px; font-weight: 600; letter-spacing: .02em; }
/* 2x2 grid on mobile per the mobile Figma — not a single stacked column
   (the previous treatment). */
@media (max-width: 860px) {
	/* minmax(0, 1fr) — the labels use white-space:nowrap (below) so they
	   stay on one line, which gives each column a large min-content width;
	   a bare 1fr track would refuse to shrink the column to fit, which is
	   exactly the grid-sizing bug that caused the real page-wide
	   horizontal scroll fixed in .hero-inner above. */
	/* justify-items:start (the desktop/tablet value) lets each .trust-item
	   size to its own content and ignore the grid track boundary — fine
	   when there's room to spare, but on mobile a wide item then visually
	   intrudes into the NEXT column even with overflow:hidden on it,
	   since there's no real overflow to clip inside a box already sized
	   to fit its own content exactly. Confirmed live: the 2nd column's
	   icon was starting at x=192.5 while the 1st column's text still
	   extended to x=203.5 — an real ~11px overlap. Fixed by making each
	   item fill its actual grid column (justify-items default/stretch)
	   so the column boundary is genuinely enforced, and overflow:hidden
	   above now has a real box to clip against if content is ever still
	   too wide. */
	/* width:100% overrides the .container class this element ALSO carries
	   (width: min(1340px, 100% - 48px)) — that formula's own -48px gutter
	   was stacking with this rule's own 16px padding, double-constraining
	   the real available width and making the truncation/overlap chase
	   above harder than it needed to be. One gutter mechanism (this
	   padding), not two. */
	.trust-bar-inner { width: 100%; padding: 24px 16px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 10px; justify-items: stretch; }
	/* 2026-09-02: each label was wrapping onto 2 lines — Figma keeps every
	   one on a single line. Smaller icon/gap/font buys back just enough
	   width per column (~170px on a 375px screen) for the longest label
	   ("Identity-verified professionals") to fit without wrapping. */
	/* 2026-09-02, round 3: a real device (iOS Safari, via Live Link) still
	   showed the two columns overlapping — the nowrap text was fitting in
	   Chromium's own font metrics during testing but rendering measurably
	   wider in real Safari, spilling past its own grid cell into the
	   neighbor since nothing clipped it locally (minmax(0,1fr) stops the
	   TRACK from growing, it doesn't stop overflowing CONTENT from
	   painting past its own cell). Fixed with real safety margin this
	   time (smaller font/icon/gap, not a razor-thin fit tuned to one
	   browser) plus overflow:hidden on the item itself as a hard backstop
	   — if a label is ever still too wide for its column on some device,
	   it now clips within its own cell instead of visibly overlapping the
	   next one. */
	.trust-item { gap: 5px; align-items: center; overflow: hidden; min-width: 0; }
	.trust-item .icon { width: 13px; height: 13px; flex-shrink: 0; }
	.trust-item span:last-child { font-size: 9.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* ── Carousel / product card ────────────────────────────────────────── */
/* Static 3-up grid — NOT a scrolling carousel. The Home Services/
   BeautyCare/Men's Grooming sections show exactly 3 cards with no
   prev/next arrows anywhere in the real design (confirmed by their
   absence in Bilal's screenshots, and the math: 3 x 420px card +
   2 x 40px gap = 1340px, exactly the container width — the row is
   designed to fit, not scroll). .carousel/.carousel-nav kept below for
   any future section that genuinely needs to scroll past more items
   than fit; these 3 homepage sections no longer use it, see §9 commit. */
.product-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
@media (max-width: 960px) { .product-grid-3 { grid-template-columns: repeat(2, 1fr); gap: 20px; } }
/* 2026-09-02: a real horizontal swipe carousel on narrow mobile, not a
   vertical 1-column stack — matches the mobile Figma's Home Services/
   BeautyCare/Men's Grooming card rows (edge-to-edge scroll, next card
   peeking at ~78% width). Figma also shows round prev/next arrow buttons
   overlapping these cards; not built this pass (would need new markup +
   JS, not just CSS) — flagged as a real follow-up, not silently dropped.
   Touch/trackpad swipe already carries navigation here regardless. */
@media (max-width: 640px) {
	.product-grid-3 {
		display: flex; grid-template-columns: unset;
		overflow-x: auto; gap: 14px; scroll-snap-type: x proximity; scrollbar-width: none;
		margin: 0 -20px; padding: 0 20px 6px;
		/* Real bug fixed 2026-09-03 (Bilal: "service cards are touching
		   the wall... limited to the homepage") — the 20px inset above
		   (padding-left/right) looked correct in the CSS but never
		   actually rendered. Root-caused directly, through two wrong
		   fixes first: it's not a padding-visibility quirk, and it's not
		   fixable by moving the inset onto the first/last card's own
		   margin either — it's `scroll-snap-align: start` on the cards
		   below. Confirmed by forcing `element.scrollLeft = 0` via
		   script and reading it straight back: it snapped right back to
		   the first card's edge every time, proving the snap engine was
		   continuously re-correcting the resting scroll position past
		   ANY leading content (padding, margin, or a real spacer element
		   — all three were tried and all three got scrolled past the
		   same way) straight to the first snap target.
		   The actual fix: `scroll-padding`, not `padding` alone —
		   `padding` only creates the VISUAL space; `scroll-padding`
		   tells the snap engine that the reserved region IS the correct
		   "start" to rest a snapped item against, instead of treating it
		   as excess space to auto-scroll past. Both are needed together:
		   padding alone gets snapped past (as measured); scroll-padding
		   alone would still visually clip the gutter under
		   overflow:hidden's scrollable-area math. Scoped to
		   .product-grid-3 only (the homepage's 3 teaser carousels) per
		   Bilal's own "limited to the homepage" — .product-grid-cat
		   (category pages) isn't a scroll container at all, never
		   affected. */
		scroll-padding-left: 20px; scroll-padding-right: 20px;
	}
	.product-grid-3::-webkit-scrollbar { display: none; }
	.product-grid-3 > * { flex: 0 0 78%; scroll-snap-align: start; }
}

/* Category pages' product-card grid — Popular Services (category-hub.php),
   "Choose a service" (category-leaf.php), and each sub-category's own grid
   (category-hub-cleaning.php). Deliberately a SEPARATE class from
   .product-grid-3 above, not a shared one, per Bilal 2026-09-03: "the
   Figma shows two service cards per row... this should also be treated as
   a global service-card pattern and applied consistently wherever service
   cards are rendered, including the Category Hub and Category pages, but
   excluding the Homepage, which should retain its existing design." So
   .product-grid-3's homepage-only 78%-peek swipe carousel below 640px is
   untouched here — category pages instead stay a real, non-scrolling
   2-column grid all the way down to mobile, matching the Figma reference
   (Home Services Hub, node 2359-5982) exactly. Also replaces the old
   .service-grid-flex (flex-wrap, fixed 300px cards) category-leaf.php/
   category-hub-cleaning.php used previously — unifies what were two
   different, inconsistent mechanisms into one. */
/* Column counts are CSS custom properties, not fixed numbers, as of
   2026-09-09 — the customer-facing layout switcher (grid-view-switcher.php/
   assets/js/grid-view.js) sets --kk-grid-cols-desktop/--kk-grid-cols-mobile
   on the root element; every .product-grid-cat grid on the page reacts
   instantly (a live CSS reflow, no JS re-render, no page reload) since
   they all read the SAME two variables. The literal fallback values here
   (4 desktop / 2 mobile) ARE the required defaults — a page with the
   switcher's JS not yet run (or not present at all) already renders
   correctly with zero flash-of-wrong-layout risk; the JS is a progressive
   enhancement on top of an already-correct default, not a dependency.
   Desktop was 3 before this feature (unchanged as the "3 cards" OPTION,
   just no longer the default) — see the switcher component's own header
   comment for the full requirement this implements. */
.product-grid-cat { display: grid; grid-template-columns: repeat(var(--kk-grid-cols-desktop, 3), 1fr); gap: 28px; } /* fallback 4->3 (2026-09-15) — the desktop 3-vs-4 toggle is disabled (grid-view-switcher.php), 3 is now the only real desktop layout; kept as a CSS custom property, not a hardcoded 3, so re-enabling the toggle later is still a one-flag change, not a rule rewrite */
@media (max-width: 960px) { .product-grid-cat { grid-template-columns: repeat(var(--kk-grid-cols-mobile, 2), minmax(0, 1fr)); gap: 20px; } }
@media (max-width: 640px) { .product-grid-cat { grid-template-columns: repeat(var(--kk-grid-cols-mobile, 2), minmax(0, 1fr)); gap: 12px; } }

/* ── Service-card layout switcher (2026-09-09, corrected same day) ──
   Background changed from a plain gray (#F3F4F6) to --kk-orange-light —
   Bilal supplied a real screenshot of the mobile app's own view-toggle
   pill (a warm light-orange background, not gray) and asked the website
   to visually match it for cross-platform consistency, same reasoning as
   the icon swap below. */
.grid-view-switcher { display: flex; align-items: center; }
.grid-view-switcher-group { display: inline-flex; align-items: center; gap: 2px; background: var(--kk-orange-light); border-radius: 12px; padding: 4px; }
.grid-view-btn {
	display: inline-flex; align-items: center; gap: 6px;
	border: none; background: transparent; color: var(--kk-muted-2);
	font-family: inherit; font-size: 13px; font-weight: 700;
	padding: 8px 12px; border-radius: 8px; cursor: pointer;
	transition: background .15s, color .15s, box-shadow .15s;
}
.grid-view-btn .icon { width: 16px; height: 16px; }
.grid-view-btn:hover { color: var(--kk-ink); }
.grid-view-btn.is-active { background: #fff; color: var(--kk-orange); box-shadow: 0 1px 4px rgba(0,0,0,.1); }
/* Only one group is ever shown at a time, at the SAME 960px boundary
   .product-grid-cat itself already changes column-count at — so the
   control that's visible always matches the layout it's actually
   affecting; there's no separate breakpoint to keep in sync by hand. */
.grid-view-switcher-group--mobile { display: none; }
@media (max-width: 960px) {
	.grid-view-switcher-group--desktop { display: none; }
	.grid-view-switcher-group--mobile { display: inline-flex; }
}
/* Mobile buttons are icon-only now (no more "Grid"/"Single" text, see
   grid-view-switcher.php's own comment) — tighter, roughly-square padding
   instead of the text-pill padding above, matching the compact reference
   screenshot instead of leaving text-sized padding around a bare icon.
   The old <420px "drop the text" rule is gone — there's no text left to
   drop at any width now, so it was dead code once this shipped. */
.grid-view-switcher-group--mobile .grid-view-btn { padding: 8px; }
/* .grid-view-switcher-bar (a standalone bar ABOVE category-hub-cleaning.php's
   whole sub-categories block) removed 2026-09-09 — Bilal flagged it as
   landing on its own visually separate row, creating exactly the dead
   space this whole feature is meant to avoid. The switcher now renders
   inline inside whichever heading's own .section-head-row it belongs to
   (that file's own header comment explains which one) — the same pattern
   category-leaf.php's "Choose a service" section already used. */

.carousel { position: relative; padding: 4px 0; }
.carousel-track { display: flex; gap: 20px; overflow-x: auto; padding-bottom: 8px; scroll-snap-type: x proximity; scrollbar-width: none; }
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track .product-card { flex-shrink: 0; width: 268px; scroll-snap-align: start; }
.carousel-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 10; width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--kk-line); cursor: pointer; display: flex; align-items: center; justify-content: center; background: #fff; box-shadow: 0 4px 20px rgba(0,0,0,.15); font-size: 20px; }
.carousel-nav--prev { left: -22px; }
.carousel-nav--next { right: -22px; }
@media (max-width: 640px) { .carousel-nav { display: none; } }

.product-card { border-radius: 16px; overflow: hidden; background: #fff; box-shadow: 0 2px 24px rgba(0,0,0,.08); border: 1px solid rgba(0,0,0,.06); transition: transform .25s, box-shadow .25s; } /* 16px, desktop AND mobile — corrected 2026-09-15 per Bilal's own real Figma spec check ("Desktop: 16px... Mobile: 16px... the current implementation is using 28px on both"), which supersedes every earlier direct-measurement reading of this component (28px from node 2154-42857 the day before; 23px/22.76px from a still-earlier narrower instance) — this is the one confirmed against the actual Figma spec value, not another individual node inspection, so it's treated as the settled number going forward rather than one more competing reading to flag. One rule, no separate mobile override — this project never had a distinct mobile radius to begin with, matching Figma's own "same on both" spec exactly. */
.product-card:hover { transform: translateY(-8px); box-shadow: 0 16px 48px rgba(0,0,0,.14); }
.product-card--dark { background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.1); box-shadow: 0 8px 40px rgba(0,0,0,.28); }
.product-card-image { position: relative; height: var(--kk-card-image-h); background: linear-gradient(160deg, #e5e7eb, #d1d5db); }
.product-card-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* .product-grid-cat cards at <1024px (mobile/tablet, 2-per-row) — the
   --kk-card-image-h tiers below only start at min-width:1024px, so below
   that a .product-grid-cat card was silently inheriting the ROOT default
   (192px), sized for a much wider ~280-420px 3-across card — badly
   oversized against a real ~137-280px-wide 2-column card, which is why
   the whole card read as "not matching Figma at all" (Bilal, 2026-09-03).
   Using aspect-ratio instead of a fixed height means the image genuinely
   scales with the card's real width at ANY mobile/tablet viewport, not
   just the one Figma frame was measured at. Scoped to .product-grid-cat
   only — .product-grid-3 (homepage teasers) keeps its own existing
   fixed-height design completely untouched, per Bilal's explicit
   "excluding the Homepage".
   Ratio corrected 2026-09-03 (same day, 2nd pass) — re-verified directly
   against the real "ServiceCard" component in Figma (not a screenshot):
   selecting the actual image-container frame (not the inner image fill
   layer, which reports a different, misleading number) gives 226.18 Fill
   x 156.09 Hug — 1.449:1, not the previously-used 175:129 (1.357:1). Also
   confirmed the real "Frequently booked services" section on the real
   Home Services mobile mockup IS a genuine static 2x2 grid (a separate
   loose "Carousel" component elsewhere in the file had briefly suggested
   otherwise — that turned out to be an unrelated reference instance, not
   what's actually placed on this page). */
@media (max-width: 1023px) {
	.product-grid-cat .product-card-image { height: auto; aspect-ratio: 226 / 156; }
}
.product-card-discount { position: absolute; top: 12px; left: 12px; background: var(--kk-orange); color: #fff; font-size: 11px; font-weight: 800; padding: 4px 10px; border-radius: 100px; }
.icon-star { color: var(--kk-orange); }
.product-card-body { padding: var(--kk-card-pad); }
@media (max-width: 1023px) { .product-grid-cat .product-card-body { padding: 16px; } } /* Figma: 16.26px, same card-body confirmed above */
/* Compact typography/spacing tier for .product-grid-cat below 1024px
   (2026-09-09), per Bilal's real screenshot: the SAME text sizes used at
   every width were making narrow cards (a 2-column mobile grid, or now a
   3/4-column desktop one via the layout switcher) read as unbalanced and
   unnecessarily tall — most concretely, the price row wrapping to 2 lines
   (fixed structurally above, .product-card-price), but the underlying
   font sizes/line-heights/margins were never independently tuned for a
   narrow card the way .product-card-body's own padding and
   .product-card-image's own aspect-ratio already are, a few lines above.
   Scoped to .product-grid-cat only, matching every other card-tuning rule
   in this file — .product-grid-3 (the homepage's own teaser carousels)
   keeps its existing, separately-designed sizing untouched, per Bilal's
   own long-standing "excluding the Homepage" instruction. Values here are
   a considered, coordinated scale (title/price/metadata/subtitle/button
   reviewed together, not tuned in isolation) rather than a blanket
   percentage shrink — each one still reserves real, deterministic height
   (title's own 2-line min-height, subtitle's 1-line min-height above) so
   cards in the same row stay height-consistent regardless of how much
   real content each one actually has. */
@media (max-width: 1023px) {
	.product-grid-cat .product-card-meta { margin-bottom: 4px; }
	.product-grid-cat .product-card-category { font-size: 9px; }
	.product-grid-cat .product-card-duration { font-size: 10px; }
	.product-grid-cat .product-card-name { font-size: 13px; line-height: 1.35; min-height: calc(13px * 1.35 * 2); margin-bottom: 4px; }
	.product-grid-cat .price-now { font-size: 14px; }
	.product-grid-cat .price-old { font-size: 11px; }
	.product-grid-cat .product-card-subtitle { font-size: 11px; margin-bottom: 6px; }
	/* Price row STACKS below 1024px (2026-09-15), reverting to the
	   pre-2026-09-14 arrangement specifically at this width — Bilal, real
	   screenshots: mobile/tablet cards had inherited the desktop's
	   side-by-side price+button row, and "I believe the mobile should
	   follow the same structure where the CTA was in its own row rather
	   than with the price." A ~150-300px-wide card genuinely doesn't have
	   room for both a legible price AND a comfortably tappable button on
	   one line — the desktop row (.product-card-price-row's own base rule
	   above) stays row-based only at >=1024px, where a card is wide enough
	   for it. */
	.product-grid-cat .product-card-price-row { flex-direction: column; align-items: stretch; gap: 8px; margin-bottom: 8px; }
	/* Add button/quantity-selector both go full-width on their own row here
	   (were `flex:0 0 auto` hug-width, correct only for the side-by-side
	   desktop row) — matches a "CTA has its own dedicated row" structure,
	   same as the qty selector's own buttons staying a fixed size while the
	   row around them stretches to fill the card. */
	.product-grid-cat .product-card-add { width: 100%; font-size: 12px; padding: 8px 10px; }
	.product-grid-cat .product-card-qty { width: 100%; justify-content: center; }
	/* border-radius deliberately NOT overridden here (2026-09-09) — Bilal:
	   "the border radius for all CTAs should be exactly 10px... applicable
	   to all services" — stays inherited from the base .product-card-add
	   rule below at every breakpoint, not just this compact tier (that base
	   value moved to 12px 2026-09-14 per a fresher Figma reading — see that
	   rule's own comment — the "one shared value, no per-breakpoint
	   override" PRINCIPLE from this comment is what's preserved here, not
	   the literal old number). */
	/* Added 2026-09-09, later same day — this rule was first written scoped
	   to .kk-mobile-single only (see the Single View correction below), but
	   Bilal flagged the Grid View badge as "very oversized" too once he saw
	   the smaller one — a real gap in the ORIGINAL compact-tier pass above:
	   every other element here (title/price/subtitle/button) was tightened
	   for <1024px, the discount badge was the one left at its full desktop
	   size. Moved up to this shared, mode-agnostic block so Grid View AND
	   Single View both get the identical smaller badge — "the same size
	   badge" — rather than keeping two separate values to maintain. */
	.product-grid-cat .product-card-discount { top: 6px; left: 6px; font-size: 9px; padding: 2px 6px; }
}
/* Mobile Single View — horizontal card layout (2026-09-09), per the real
   Figma "ServiceCard" mobile single-view node (node-id 3480-25999,
   300.44 Hug × 138.44 Hug — inspected via a screenshot Bilal supplied
   directly, Design-mode canvas navigation on this large file proving
   unreliable again, consistent with this project's own repeatedly-
   documented history with it): image on the left, content stacked on the
   right — replacing the vertical image-on-top layout every OTHER layout
   (Grid View, and every desktop column count) still uses unchanged.
   Toggled by .kk-mobile-single on the root element (grid-view.js's
   applyPrefs(), plus the identical early toggle in header.php's <head>
   script) rather than driven by the --kk-grid-cols-mobile custom property
   alone — a flex-direction/structural change like this can't be expressed
   through a custom property switch the way the column COUNT can.
   Deliberately reuses the exact typography scale from the compact tier
   directly above — no new font sizes introduced here, per Bilal's own
   "keep the overall card proportions and balanced typography we just
   established." The star-rating row visible in the Figma reference is
   deliberately NOT included, reconfirmed with Bilal before building this —
   stays consistent with the standing, twice-confirmed "no rating badges,
   real avgRating is still 0.0 on every live product" decision (2026-08-28,
   reconfirmed 2026-09-03), not silently reversed just because this one
   Figma frame happens to show one. */
@media (max-width: 1023px) {
	/* Corrected 2026-09-09, same day, real 2nd pass — the first cut let the
	   image sit flush against the card's own edges (correct for the plain
	   vertical card, where the image spans full-width at the top and only
	   needs the CARD's own overflow:hidden to clip its top corners), but a
	   closer Figma screenshot (Bilal, zoomed into the same "ServiceCard"
	   node) showed the image genuinely INSET on all sides with real card
	   background visible around it — a structural gap, not a spacing nit.
	   Padding now lives on .product-card itself (not .product-card-body,
	   which is zeroed out below in this scope) so the SAME inset applies
	   uniformly to the image, the gap before the content column, and the
	   content column's own edges — one padding source, not two stacking. */
	.kk-mobile-single .product-grid-cat .product-card { display: flex; flex-direction: row; align-items: stretch; padding: 12px; gap: 12px; }
	/* Fixed width, not a percentage of the card — Figma's own "Hug" sizing
	   means the image has its own intrinsic width, not a proportional
	   split of whatever the card's total width happens to be; height is
	   deliberately unset (stretches via align-items:stretch above to match
	   the content column's own natural height, same "Hug" behaviour). Own
	   border-radius + overflow:hidden (2026-09-09 correction) — now that
	   it's inset rather than flush with the card's edges, it needs its own
	   rounded corners; the card's own overflow:hidden no longer reaches it. */
	.kk-mobile-single .product-grid-cat .product-card-image { width: 130px; height: auto; aspect-ratio: unset; flex-shrink: 0; border-radius: 14px; overflow: hidden; }
	/* padding: 0 — the outer .product-card padding above now provides this
	   column's own right/top/bottom inset (and the image's left inset),
	   so the base .product-card-body padding (16px, used by every OTHER
	   layout) would double up here if left in place. */
	.kk-mobile-single .product-grid-cat .product-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 0; }
	/* Discount badge itself (size/position) lives in the SHARED compact
	   tier above now, not here — see that rule's own comment. Originally
	   written scoped to just this Single View block (Bilal's real Figma
	   selection showed it at 35×14 Hug×Hug, tucked into the image's own
	   corner — at this layout's ~130px image width the old full-size badge
	   was landing squarely over the worker's face in the real photo), then
	   promoted to apply everywhere below 1024px once Bilal flagged the
	   Grid View badge as equally oversized. Sized/positioned as a close
	   proportional match to that real Figma selection, not a literal
	   35×14px box (unreadable at that size) — flagged as an estimate, not
	   a pixel-exact Dev Mode value, same honesty standard as every other
	   screenshot-derived measurement in this project. */
}
/* Extra-narrow phones — one more defensive step down for the price pair
   specifically, on top of the flex/nowrap fix above, so a longer 4-5
   digit price pair still has real room to stay on one line even on the
   smallest common real device widths (~320-375px), not just the
   flex-wrap:nowrap structural guarantee alone. */
@media (max-width: 380px) {
	.product-grid-cat .price-now { font-size: 12px; }
	.product-grid-cat .price-old { font-size: 10px; }
}
.product-card-meta { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 6px; }
/* min-width:0 + ellipsis (2026-09-15) — a real bug spotted in Bilal's own
   screenshot: a long category name ("WATER TANK CLEANING") was wrapping to
   2 lines inside this flex row, which both looked wrong and quietly added
   height to every card in that category. A flex child's default min-width
   is `auto` (its own content width), so it never actually shrinks to make
   room for ellipsis without this. */
.product-card-category { color: var(--kk-orange); font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.product-card-duration { color: var(--kk-muted-2); font-size: 11px; flex-shrink: 0; }
.product-card--dark .product-card-duration { color: rgba(255,255,255,.4); }
/* Typography hierarchy corrected 2026-09-15, per Bilal: "too large... a
   balanced visual hierarchy... do not simply reduce everything
   uniformly." The 2026-09-14 pass applied the exact raw pixel values read
   off a 430px-WIDE desktop Figma frame — correct as a measurement, but
   never checked against how wide a real card on this site actually
   renders (a 3-4-per-row grid inside a ~1120-1340px container is closer
   to ~260-320px per card) — at that real width, those absolute sizes read
   as oversized and made every card taller than it needed to be, the exact
   regression Bilal flagged as "resurfaced... we were fixing yesterday."
   Rebalanced as a deliberate, non-uniform scale: price-now stays the
   single most prominent number on the card (17px, still clearly a
   "price"), the title sits just under it (15px — a service name is
   important but shouldn't compete with the price for attention),
   price-old/subtitle are both deliberately muted/small (12-13px) since
   neither is the primary thing a visitor is scanning for. Real Figma
   pixel values are no longer literally reproduced at this card width, but
   the same relative relationships (price-now > title > everything else)
   are what the Figma reference itself also shows — this scale preserves
   that hierarchy while fitting the width this card actually renders at.
   Always reserves 2 lines of height (2026-09-03), even for a 1-word title
   like "Electrician" — cards in the same row were reading as inconsistent
   heights because some titles wrap to 2 lines and others don't.
   -webkit-line-clamp also caps a genuinely long title at 2 lines with an
   ellipsis, so a rare 3-line title can't blow the reserved height back
   out. */
.product-card-name { font-weight: 700; font-size: 15px; line-height: 1.3; min-height: calc(15px * 1.3 * 2); margin-bottom: 6px; color: var(--kk-ink); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.product-card--dark .product-card-name { color: #fff; }
/* Price + the Add button/quantity-selector share ONE ROW (2026-09-14,
   real Figma structure — see card-product.php's own header comment for
   the full investigation). This REVERSES the 2026-09-03 restructure,
   which stacked a full-width button below a separate price row — that
   version was built off a DIFFERENT, narrower mobile-scale reference that
   happened to show a stacked layout; this exact node (2154-42857) is
   unambiguous: price on the left, a compact, right-aligned button, one
   row, with the subtitle as its own line below. Deliberately DESKTOP-ONLY
   now (2026-09-15) — see the <1024px compact tier below, which reverts to
   a stacked row on mobile/tablet per Bilal's own explicit "the CTA [should
   be] in its own row rather than with the price" on narrow cards, where
   there genuinely isn't room for both a legible price AND a tappable
   button side by side. */
.product-card-price-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
/* Real bug fixed 2026-09-09, not just a sizing tweak — this row had no
   display:flex/nowrap at all, so .price-now/.price-old were just two plain
   inline spans in normal text flow: the moment their combined width
   exceeded the card's content width (any narrow card — a 2-column mobile
   grid, or now a 4-column desktop grid via the layout switcher), the
   browser wrapped .price-old onto its own line like ordinary text,
   exactly the "original price and discounted price are wrapping onto two
   lines" Bilal flagged with a real screenshot. flex-wrap:nowrap + gap
   (replacing price-old's old margin-left) makes this structurally
   impossible to wrap under any REALISTIC price pair, not just less likely
   — the narrow-phone font-size tier below is the remaining defensive
   layer for extreme cases (5-digit prices on the smallest phones). Own
   margin-bottom removed 2026-09-14 — spacing before the subtitle now
   comes from .product-card-price-row's own margin-bottom above, since
   .product-card-price is a flex CHILD of that row now, not a stacked
   block above the button. */
.product-card-price { display: flex; align-items: baseline; flex-wrap: nowrap; gap: 6px; overflow: hidden; min-width: 0; }
.price-now { font-family: 'Inter', sans-serif; font-weight: 700; font-size: 17px; color: var(--kk-ink); white-space: nowrap; }
.product-card--dark .price-now { color: #fff; }
.price-old { font-family: 'Inter', sans-serif; color: var(--kk-muted-2); font-size: 13px; text-decoration: line-through; white-space: nowrap; }
/* Clamped to 1 line (2026-09-03) — Figma's own subtitle example is short
   ("Per seat · minimum 5") and clearly fits one line at the card's real
   width; the live backend's shortDescription field is sometimes longer
   ("Price Per Seat x 400, min count 5"), which was wrapping to 2 lines and
   was a real, measurable contributor to cards reading "too tall/vertically
   stretched" (Bilal, 2026-09-03) versus Figma's compact proportions. This
   truncates DISPLAY only via ellipsis — the real text is untouched, same
   category of change as the leading "-" strip in
   kaamkrew_map_product_to_card(), not content invention. */
/* min-height reserves exactly 1 line even with no real text (2026-09-09) —
   card-product.php now always renders this <p>, empty or not, for exactly
   this reason. Same "reserve the space regardless of content" precedent
   .product-card-name already uses above, applied here so a card WITHOUT a
   real subtitle isn't shorter than one that has one — without this, cards
   in the same row varied in height purely based on whether that one
   backend field happened to be populated, a real, visible inconsistency
   Bilal flagged as part of "cards with more content becoming
   disproportionately taller." 1em, not a fixed px, so this stays correct
   automatically whichever font-size tier below is actually active. */
.product-card-subtitle { font-family: 'Inter', sans-serif; font-size: 12px; color: var(--kk-muted-2); line-height: 1.4; min-height: calc(1em * 1.4); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* 12px (was 14px, 2026-09-15 hierarchy rebalance — see .product-card-name's own comment) — a secondary/muted line, deliberately smaller than both the title and the price */
.product-card--dark .product-card-subtitle { color: rgba(255,255,255,.45); }
/* Button dimensions corrected 2026-09-14 against the exact Figma node
   Bilal supplied this pass (2154-42857): 71x34 Hug, padding 8px vertical /
   14px horizontal, icon-label gap 5px, corner radius 12px — no longer a
   full-width button (width:100% removed; this is now a compact,
   right-aligned control sharing .product-card-price-row with the price,
   see that rule's own comment). Supersedes the 2026-09-03/09-09 readings
   (9.76px≈10px radius, 6.5/11.38px padding, 4.06px gap) taken off a
   different ServiceCard instance — flagged, not silently picked, same as
   the card's own corner-radius discrepancy above. */
.product-card-add { background: var(--kk-orange); color: #fff; border: none; border-radius: 12px; padding: 8px 14px; font-size: 13px; font-weight: 800; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 5px; white-space: nowrap; flex: 0 0 auto; }
/* [hidden] override — real bug caught 2026-09-14 during live verification,
   same specificity trap this project has already hit and fixed several
   times (account-auth-panel, testimonials-viewport, city-modal, etc.): a
   plain class selector's own `display` declaration has equal specificity
   to the browser's UA `[hidden]{display:none}` rule, and author styles
   always win that tie regardless of source order — so both this button
   AND .product-card-qty below were rendering SIMULTANEOUSLY, always,
   completely ignoring the `hidden` attribute assets/js/cart.js and
   card-product.php both rely on to toggle between them. */
.product-card-add[hidden] { display: none; }
.product-card-qty[hidden] { display: none; }
/* Real bug fixed 2026-09-09 — "Book Appointment" (Beauty Care's own CTA
   label, longer than every other category's plain "Add") was wrapping
   onto 2 lines at narrow card widths. white-space:nowrap on the button
   above stops the label text itself from wrapping (flex's own
   flex-wrap:nowrap default only keeps the icon+label as two side-by-side
   items, it doesn't stop the TEXT inside the label from wrapping on its
   own); the plus icon is also dropped specifically for Beauty Care now
   (card-product.php, same request) — one fewer flex item + its 4px gap is
   real width back for a visibly longer label. Between the two, "Book
   Appointment" fits on one line at every real card width tested, with no
   further font-size/padding change needed beyond the compact <1024px tier
   already shared by every CTA — so it fits without the button reading as
   oversized relative to the rest of the (already rebalanced) card. */
.product-card-add-label { white-space: nowrap; }
/* Quantity selector (2026-09-14) — replaces the Add button in place once a
   service is in the cart (card-product.php toggles which of the two is
   [hidden]; assets/js/cart.js drives that after a real add/update/remove
   call, never a client-only counter — see card-product.php's own header
   comment). Visual reference: Bilal's own attached screenshot (two solid
   rounded-square buttons, not one continuous pill) — matched here rather
   than the Employer App's own web CSS, which uses a different, single
   bordered-pill treatment; the app's REAL, confirmed logic (optimistic
   +/- against the actual cart, counter seeded from the real quantity) is
   what's reused, not its specific visual chrome, per Bilal's own framing
   of the screenshot as the UI source of truth and the app as the
   behavioural one. Buttons reuse the Add button's own radius/fill for a
   consistent CTA language across the whole card. */
.product-card-qty { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.product-card-qty-btn { width: 34px; height: 34px; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; background: var(--kk-orange); color: #fff; border: none; border-radius: 12px; cursor: pointer; padding: 0; }
.product-card-qty-btn:hover { background: var(--kk-orange-dark); }
.product-card-qty-btn:disabled { opacity: .5; cursor: default; }
.product-card-qty-value { font-weight: 800; font-size: 15px; color: var(--kk-ink); min-width: 14px; text-align: center; }
.product-card--dark .product-card-qty-value { color: #fff; }
@media (max-width: 1023px) {
	.product-grid-cat .product-card-qty-btn { width: 28px; height: 28px; border-radius: 10px; }
	.product-grid-cat .product-card-qty-value { font-size: 13px; }
}
.product-card--accent-beauty .product-card-discount { background: var(--kk-beauty); }
.product-card--accent-beauty .product-card-category { color: var(--kk-beauty); }
.product-card--accent-grooming .product-card-category { color: var(--kk-grooming); }
/* Team decision, 2026-09-05: service-card CTAs (the Add/Book Appointment
   button) use the standard orange across every category, including Beauty
   Care and Men's Grooming — no longer following kaamkrew_category_theme()'s
   purple/grooming accent the way the discount badge and category label
   still do. .product-card-add already defaults to var(--kk-orange) (see its
   own base rule above) — this is a removal of the two accent overrides that
   used to redirect it to --kk-beauty/--kk-grooming, not a new rule. */

/* ── Section variants ────────────────────────────────────────────────── */
/* Real gradient stops from Figma: #D62DF9 at 11% opacity, #B11BD1 at 3%
   opacity, over white — reads as a very soft pink-purple wash. */
.section--beautycare { background: linear-gradient(135deg, rgba(214,45,249,.11) 0%, rgba(177,27,209,.03) 100%), #fff; }
/* Solid #0A378F, not a gradient — corrected 2026-08-21 against Bilal's
   screenshot (Colors panel showed one solid hex, no gradient stops). */
.section--groom { background: var(--kk-blue); }

/* ── Stats ───────────────────────────────────────────────────────────── */
.stats-band { background: var(--kk-surface); border-top: 1px solid #F0F0F0; border-bottom: 1px solid #F0F0F0; }
.stats-band--about { background: #F3F4F6; } /* real Figma fill (2026-09-06) — was inheriting the homepage's own --kk-surface (#F9FAFB), a close but not exact gray; scoped so the homepage's own 3-stat band is untouched */
.stats-grid { padding: 48px 64px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; } /* was repeat(4, 1fr) — dropped to 3 columns when 'Average rating' was removed 2026-08-28 */
.stat { text-align: center; }
.stat-number { font-weight: 900; font-size: 32px; color: var(--kk-orange); margin-bottom: 6px; }
.stat-label { font-size: 13px; color: var(--kk-muted); font-weight: 500; }
@media (max-width: 860px) { .stats-grid { grid-template-columns: repeat(2, 1fr); padding: 32px 20px; gap: 20px; } .stat-number { font-size: 26px; } }

/* ── HomeCare ────────────────────────────────────────────────────────── */
.homecare-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; max-width: 980px; margin: 0 auto; }
.homecare-card { background: var(--kk-surface); border-radius: 24px; padding: 36px 32px; border: 1px solid var(--kk-line-soft); box-shadow: 0 4px 24px rgba(0,0,0,.05); position: relative; transition: transform .25s; }
.homecare-card:hover { transform: translateY(-8px); }
.homecare-card--plus { background: #fff; border: 2px solid var(--kk-orange); box-shadow: 0 20px 60px rgba(250,107,44,.18); }
/* Premium's dark-navy treatment (was here since this component's first
   build) was never actually checked against a real Figma reference for
   this specific card — direct inspection of the real HomeCare page
   (node 2137-11034, 2026-09-04) confirms Premium is a plain LIGHT card,
   visually identical to Basic (only Plus/Recommended gets a distinct
   treatment). Removed the dark override entirely rather than scope it
   away, since nothing anywhere actually calls for it. */
.homecare-popular-tag { position: absolute; top: -14px; left: 50%; transform: translateX(-50%); background: var(--kk-orange); color: #fff; font-size: 11px; font-weight: 800; padding: 5px 16px; border-radius: 100px; white-space: nowrap; }
.homecare-plan-name { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--kk-muted-2); margin-bottom: 6px; }
.homecare-price { margin-bottom: 10px; }
.homecare-price-num { font-weight: 900; font-size: 38px; line-height: 1; }
.homecare-price-period { font-size: 13px; color: var(--kk-muted); margin-left: 4px; }
.homecare-best { font-size: 12px; color: var(--kk-muted); line-height: 1.65; margin-bottom: 20px; }
.homecare-best strong { color: #374151; }
.homecare-divider { border: none; height: 1px; background: #F3F4F6; margin-bottom: 20px; }
.homecare-features { list-style: none; display: flex; flex-direction: column; gap: 12px; margin-bottom: 28px; }
.homecare-features li { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: #374151; line-height: 1.5; }
/* Check-icon color per tier, confirmed against the real HomeCare page:
   Plus's (Recommended) checks are orange, Basic/Premium's are a muted
   blue — not a plain currentColor checkmark. */
.homecare-features li svg { color: var(--kk-blue); flex-shrink: 0; }
.homecare-card--plus .homecare-features li svg { color: var(--kk-orange); }
/* 2026-09-02: same horizontal-swipe treatment as .product-grid-3 above —
   the mobile Figma shows the HomeCare plan cards as a swipeable row too,
   not a vertical stack. */
@media (max-width: 860px) {
	.homecare-grid {
		display: flex; grid-template-columns: unset; max-width: none;
		overflow-x: auto; gap: 14px; scroll-snap-type: x proximity; scrollbar-width: none;
		margin: 0 -20px; padding: 20px 20px 6px;
		/* Two real bugs fixed 2026-09-03 (Bilal: "cards are being touching
		   by the wall" + "'Most Popular' badge is overlapping"), same root
		   causes already found and fixed on .product-grid-3 above:
		   1. Left/right `padding` here looked correct but never actually
		      rendered — `scroll-snap-align: start` on the cards below
		      continuously re-corrects the resting scroll position past any
		      leading/trailing padding, same as .product-grid-3's own fix.
		      `scroll-padding-left/right` (not just `padding`) is what
		      actually reserves real, snap-respecting edge space.
		   2. The "Most Popular" badge (.homecare-popular-tag) is
		      absolutely positioned at `top: -14px` — floating ABOVE its
		      own card's top edge. Setting `overflow-x: auto` here also
		      forces `overflow-y` to compute as `auto` (a real CSS
		      spec rule: an element can't mix `overflow-x` and
		      overflow-y` as visible/non-visible — the visible one gets
		      forced to auto too), which clips anything extending past
		      THIS container's own top edge. Since the grid had zero
		      top padding, the badge's negative offset had nowhere to go
		      and was rendering clipped/overlapping the section boundary.
		      Added real top padding to give it room. */
		scroll-padding-left: 20px; scroll-padding-right: 20px;
	}
	.homecare-grid::-webkit-scrollbar { display: none; }
	.homecare-grid > * { flex: 0 0 82%; scroll-snap-align: start; }
}
.homecare-empty { max-width: 560px; margin: 0 auto; text-align: center; color: var(--kk-muted); font-size: 15px; line-height: 1.6; }
/* Plans section's real 2-line subtitle (node 2137-11034) — Figma stacks
   these tightly as one block, not as two separately-margined paragraphs;
   .section-subtitle's own margin (meant for a single line) would otherwise
   double up between them. */
.homecare-plans-lead p { margin: 0; }
.homecare-plans-lead p + p { margin-top: 4px; }
/* HomeCare plan-selection feedback (2026-09-16) — replaces the old inline
   request form (removed once the flow became "click a plan, we submit it
   using your account" — see homecare-inquiry.php's own header comment).
   Shown/hidden entirely by assets/js/homecare-inquiry.js; sits between the
   Plans heading and the plan cards so it's visible without scrolling
   regardless of which plan was clicked. */
.homecare-status { max-width: 560px; margin: 0 auto 28px; text-align: center; font-size: 14px; font-weight: 600; border-radius: 12px; padding: 14px 18px; line-height: 1.6; }
.homecare-status[hidden] { display: none; }
.homecare-status--loading { background: #F3F4F6; color: var(--kk-muted); }
.homecare-status--success { background: #F0FDF4; color: #15803D; border: 1px solid #BBF7D0; }
.homecare-status--error { background: #FEF2F2; color: #B91C1C; border: 1px solid #FECACA; }

/* ── Reviews ─────────────────────────────────────────────────────────── */
.review-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.review-card { background: #fff; border-radius: 20px; padding: 28px 24px; box-shadow: 0 4px 24px rgba(0,0,0,.06); border: 1px solid var(--kk-line-soft); transition: transform .25s; }
.review-card:hover { transform: translateY(-4px); }
.review-stars { display: flex; gap: 2px; margin-bottom: 12px; }
.review-text { font-size: 13px; color: #4B5563; line-height: 1.75; margin-bottom: 20px; }
.review-person { display: flex; align-items: center; gap: 12px; }
.review-avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; font-weight: 800; flex-shrink: 0; }
.review-name { font-size: 13px; font-weight: 800; color: var(--kk-ink); }
.review-meta { font-size: 11px; color: var(--kk-muted-2); margin-top: 2px; }
@media (max-width: 860px) { .review-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; } }

.video-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.video-card { border-radius: 20px; overflow: hidden; position: relative; cursor: pointer; box-shadow: 0 8px 32px rgba(0,0,0,.4); transition: transform .25s; aspect-ratio: 3/4.4; }
.video-card:hover { transform: translateY(-8px); }
.video-card-thumb { position: absolute; inset: 0; background: linear-gradient(160deg, #3a3a3a, #1a1a1a); }
.video-card-overlay { position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,.88) 40%, transparent 75%); }
.video-card-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 56px; height: 56px; border-radius: 50%; background: rgba(250,107,44,.9); display: flex; align-items: center; justify-content: center; backdrop-filter: blur(8px); }
.icon-play { color: #fff; }
.video-card-info { position: absolute; bottom: 0; left: 0; right: 0; padding: 0 20px 20px; }
.video-card-duration { display: inline-block; background: rgba(250,107,44,.9); color: #fff; font-size: 10px; font-weight: 800; padding: 3px 8px; border-radius: 100px; margin-bottom: 6px; }
.video-card-name { font-size: 13px; font-weight: 700; color: #fff; line-height: 1.3; }
.video-card-city { font-size: 11px; color: rgba(255,255,255,.6); margin-top: 2px; }
@media (max-width: 860px) { .video-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; } }

.audio-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.audio-card { background: var(--kk-surface); border-radius: 20px; padding: 28px 24px; box-shadow: 0 4px 24px rgba(0,0,0,.05); border: 1px solid var(--kk-line-soft); }
.audio-card-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; }
.audio-card-name { font-weight: 800; font-size: 14px; color: var(--kk-ink); margin-bottom: 4px; }
.audio-card-service { font-size: 12px; color: var(--kk-muted-2); }
.audio-card-duration { background: #E5E7EB; color: var(--kk-muted); font-size: 12px; font-weight: 600; padding: 4px 12px; border-radius: 100px; }
.waveform { display: flex; align-items: flex-end; gap: 3px; height: 44px; margin-bottom: 20px; }
.waveform-bar { flex: 1; border-radius: 4px; background: #D1D5DB; animation: kk-wave 1.2s ease-in-out infinite; animation-play-state: paused; }
.audio-card.is-playing .waveform-bar { background: var(--kk-green); animation-play-state: running; }
@keyframes kk-wave { 0%, 100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
.audio-card-controls { display: flex; align-items: center; gap: 14px; }
.audio-play-btn { width: 40px; height: 40px; border-radius: 50%; background: var(--kk-green); color: #fff; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; box-shadow: 0 4px 16px rgba(16,185,129,.3); }
.audio-progress { flex: 1; height: 5px; background: #E5E7EB; border-radius: 3px; overflow: hidden; }
.audio-progress-fill { height: 100%; background: var(--kk-green); width: 0; transition: width .5s; }
.audio-card.is-playing .audio-progress-fill { width: 42%; }
@media (max-width: 860px) { .audio-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; } }

/* ── Testimonials (unified card, replaces the review/video/audio-card
   trio above for anywhere new — see testimonial-card.php) ─────────────── */
/* Real, confirmed leftover removed 2026-09-11: an OLD .testimonials-grid
   rule (display:grid, align-items:start) used to live here from before
   the "always carousel" rewrite further down this file redefined
   .testimonials-grid as a flex row. Its own `display`/`grid-template-
   columns` were already harmlessly overridden by the later rule (same
   specificity, later wins) — but `align-items: start` was NOT (the later
   rule never set align-items at all), silently blocking flexbox's own
   default `align-items: stretch`, which .testimonial-card's fixed-height
   mechanism (see that class's own comment) actually depends on as its
   safety-net fallback. Removed outright rather than re-declared, since the
   later rule is now the single real source of truth for this class. */

/* Rewritten 2026-09-11 (round 2), per Bilal's explicit "Text Review and
   Voice Review card structure is now finalized. The Video Review card
   should follow the exact same structure and layout... All testimonial
   cards should maintain a consistent fixed height... regardless of the
   testimonial type or amount of content." Every type (see
   testimonial-card.php) now renders the identical DOM order — badge,
   person, stars (ALWAYS present, even at rating 0 — an absent stars row
   was a real, confirmed source of inconsistent height), then ONE
   `.testimonial-media` slot that's the only genuinely different part.
   `--testimonial-media-h` is the ONE number that keeps every card the
   same height regardless of type: Text's clamp height, Video/Audio's
   thumbnail height, all read the same variable — change it once, every
   card type stays in sync. `.testimonial-card` is a flex column with
   `height: 100%%`, so it also stretches to match the tallest sibling in
   its row (flexbox's own default `align-items: stretch` on the parent
   `.testimonials-grid`/`.product-grid-3`, never overridden) — a real
   safety net on top of the fixed media height, not the only mechanism,
   covering the rare case of a person/meta line wrapping to an extra line
   on one card and not another. */
.testimonial-card {
	--testimonial-accent: var(--kk-orange);
	--testimonial-accent-light: var(--kk-orange-light);
	--testimonial-media-h: 114px; /* = 13px font x 1.75 line-height x 5 lines — Text's own real clamp height, reused verbatim so Video/Audio's thumbnail matches it exactly rather than a separately-chosen number */
	background: #fff; border-radius: 20px; padding: 28px 24px; box-shadow: 0 4px 24px rgba(0,0,0,.06); border: 1px solid var(--kk-line-soft); transition: transform .25s;
	display: flex; flex-direction: column; height: 100%;
}
.testimonial-card:hover { transform: translateY(-4px); }
/* Category theming — hard requirement, no fallback to default orange on a
   Beauty Care/Men's Grooming page (Bilal, 2026-08-28). Every accent-colored
   element inside the card reads --testimonial-accent, so overriding it
   here is the ONE place that needs to change, not four separate rules. */
.testimonial-card--theme-beauty { --testimonial-accent: var(--kk-beauty); --testimonial-accent-light: rgba(214,45,249,.12); }
.testimonial-card--theme-grooming { --testimonial-accent: var(--kk-grooming); --testimonial-accent-light: rgba(10,55,143,.12); }
.testimonial-card-badge { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--kk-muted-2); margin-bottom: 14px; }
.testimonial-person { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.testimonial-avatar { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; object-fit: cover; }
.testimonial-avatar--initial { background: var(--testimonial-accent-light); color: var(--testimonial-accent); display: flex; align-items: center; justify-content: center; font-weight: 800; }
.testimonial-name { font-size: 13px; font-weight: 800; color: var(--kk-ink); }
.testimonial-meta { font-size: 11px; color: var(--kk-muted-2); margin-top: 2px; }
/* min-height reserves this row's own space even when rating is 0 and it
   renders with nothing inside — the exact fix for one of the two real
   height-inconsistency causes Bilal flagged. */
.testimonial-stars { display: flex; gap: 2px; min-height: 16px; margin: 12px 0; }
.testimonial-card .icon-star { color: var(--testimonial-accent); }
.icon-star--filled { fill: currentColor; }

/* The one shared "media slot" every type renders exactly one of.
   margin-top: auto pushes it to the bottom of the flex column, so a card
   stretched taller than its own natural content (to match a taller
   sibling) never leaves a gap ABOVE the media slot — badge/person/stars
   stay pinned to the top, the media slot absorbs the extra space below. */
.testimonial-media { margin-top: auto; }

/* Text — clamped to 5 lines + "Read More" (Bilal: cards were "unnecessarily
   large... display a maximum of 5 lines... truncate... Read More"). A
   fixed `height` (not `min-height`) is what actually GUARANTEES every text
   card is the same height as every video/audio card — min-height alone
   would still let a short testimonial's card measure shorter than the
   media-slot height on video/audio's own fixed-height box.
   -webkit-line-clamp is the real, correct truncation mechanism (not a JS
   character-count guess, which cuts mid-word and varies with font size) —
   assets/js/testimonials.js measures `scrollHeight > clientHeight` AFTER
   this clamp applies to decide whether "Read More" is actually needed. */
.testimonial-text {
	font-size: 13px; color: #4B5563; line-height: 1.75;
	display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; overflow: hidden;
	height: var(--testimonial-media-h);
}
.testimonial-text--full { -webkit-line-clamp: unset; overflow: visible; height: auto; margin-top: 0; }
.testimonial-read-more { display: block; margin-top: 6px; font-size: 12.5px; font-weight: 700; color: var(--testimonial-accent); text-decoration: underline; background: none; border: none; padding: 0; cursor: pointer; }
.testimonial-card--text { cursor: pointer; }

/* Video + Audio — the SAME visual shape (photo/GIF + centered play/pause
   button), same fixed height as Text's own clamp box above, via the one
   shared --testimonial-media-h variable — not an aspect-ratio, which would
   make the box a different height per type/card-width and directly
   contradict "consistent fixed height... regardless of type." */
.testimonial-video, .testimonial-audio-visual {
	position: relative; border-radius: 14px; overflow: hidden; cursor: pointer;
	height: var(--testimonial-media-h);
	background: linear-gradient(160deg, #3a3a3a, #1a1a1a);
}
.testimonial-media-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.testimonial-media-thumb--placeholder { background: linear-gradient(160deg, #3a3a3a, #1a1a1a); }
/* Audio's own generated visual (2026-09-16, replacing a pulled-from-
   YouTube cover image — see testimonial-card.php's own comment on why).
   Pure CSS, no image — can never be pixelated, never needs cropping, and
   is themed via the same --testimonial-accent every other accent element
   on this card already reads, so it re-colors correctly on a Beauty
   Care/Men's Grooming page with zero extra rules. Sits on the same dark
   gradient .testimonial-audio-visual already has by default (shared with
   Video's own no-photo placeholder above), a decorative background
   texture the solid, opaque play button always reads clearly on top of —
   not two competing icons the way the old pulled-in cover's own baked-in
   mic icon was. */
.testimonial-audio-waveform { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 3px; }
.testimonial-audio-waveform span { display: block; width: 3px; border-radius: 2px; background: var(--testimonial-accent); opacity: .5; }
.testimonial-audio-waveform span:nth-child(4n+1) { height: 30%; }
.testimonial-audio-waveform span:nth-child(4n+2) { height: 65%; }
.testimonial-audio-waveform span:nth-child(4n+3) { height: 45%; }
.testimonial-audio-waveform span:nth-child(4n)   { height: 85%; }
.testimonial-media-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 52px; height: 52px; border-radius: 50%; background: var(--testimonial-accent); opacity: .92; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; backdrop-filter: blur(6px); z-index: 1; }
.testimonial-media-play .icon { color: #fff; }
.testimonial-media-play .icon-pause { display: none; }
.testimonial-card.is-playing .testimonial-media-play .icon-play { display: none; }
.testimonial-card.is-playing .testimonial-media-play .icon-pause { display: inline-flex; }
/* The real YouTube iframe the API creates inside here on first play —
   genuinely present in the DOM (never `display:none`, which some browsers
   throttle/pause) but visually reduced to nothing and click-through
   disabled, so the visible card is always just the image + button above.
   Applied to a WRAPPER div, not the element the YT API itself is given
   (see assets/js/testimonials.js's own comment — the API replaces its
   target element outright, which would have silently dropped this exact
   class off the real rendered <iframe>). */
.testimonial-audio-yt { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden; }

/* Video/Text testimonial modal (2026-09-11) — one shared shell, created
   lazily by assets/js/testimonials.js (not server-rendered — there's no
   single template every testimonial-card.php usage funnels through that
   would make "render it once per page" straightforward, so it's built on
   first use and reused after that). Video's own popup is deliberately
   COMPACT (Bilal: "should not occupy the entire screen... reasonable,
   compact size that matches the Figma design") — capped at 720px wide,
   16:9, not a full-viewport takeover the way the original inline-swap
   implementation's own on-page embed could read as on a narrow screen.
   Text's own panel reuses the real rendered person/stars/badge markup
   (cloned from the card that opened it, not rebuilt) inside a normal
   content-card shape instead of a video-shaped box. */
.testimonial-modal[hidden] { display: none; }
.testimonial-modal { position: fixed; inset: 0; z-index: 400; display: flex; align-items: center; justify-content: center; padding: 20px; }
.testimonial-modal-scrim { position: absolute; inset: 0; background: rgba(13,13,13,.7); }
.testimonial-modal-panel { position: relative; z-index: 1; width: 100%; background: #fff; border-radius: 16px; box-shadow: 0 24px 64px rgba(0,0,0,.35); overflow: hidden; }
.testimonial-modal-close { position: absolute; top: 12px; right: 12px; z-index: 2; width: 36px; height: 36px; border-radius: 50%; background: rgba(0,0,0,.55); color: #fff; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 20px; line-height: 1; }
.testimonial-modal-close:hover { background: rgba(0,0,0,.75); }

.testimonial-modal-panel--video { max-width: 720px; background: #000; }
.testimonial-modal-video-frame { position: relative; width: 100%; aspect-ratio: 16/9; }
.testimonial-modal-video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.testimonial-modal-panel--text { max-width: 480px; padding: 32px 28px; max-height: 82vh; overflow-y: auto; }
.testimonial-modal-panel--text .testimonial-card-badge { margin-bottom: 16px; }
.testimonial-modal-panel--text .testimonial-person { margin-bottom: 12px; }
.testimonial-modal-panel--text .testimonial-stars { margin: 12px 0; }

/* ── Testimonials Hub (/testimonials/) ──────────────────────────────── */
.testimonials-filter-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 20px 28px; margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px solid var(--kk-line-soft); }
.testimonials-filter-group { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.testimonials-filter-label { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--kk-muted-2); }
.testimonials-filter-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.chip--filter { cursor: pointer; text-decoration: none; }
.chip--filter.is-active { background: var(--kk-orange); border-color: var(--kk-orange); color: #fff; }
.chip--filter--purple.is-active { background: var(--kk-beauty); border-color: var(--kk-beauty); }
.chip--filter--grooming.is-active { background: var(--kk-grooming); border-color: var(--kk-grooming); }
.testimonials-filter-clear { margin-left: auto; font-size: 12px; font-weight: 700; color: var(--kk-muted); text-decoration: underline; }
.testimonials-filter-clear:hover { color: var(--kk-orange); }

/* Shared testimonials grid/carousel + Type filter (2026-08-31) — used by
   the homepage section and every category page's "Customer Reviews"
   section, see template-parts/components/testimonials-grid.php. Client-
   side filter (chip row, no page reload) — distinct from the dedicated
   Hub's own real-link filter above, which needs its own indexable URLs. */
.testimonials-type-filter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 28px; }

/* Rewritten 2026-09-11, per Bilal's explicit ask to always carousel on
   mobile ("we previously decided that testimonials on mobile should use a
   horizontal carousel... following the same interaction pattern already
   used for the service cards") and to bring the arrows back ("customers
   can navigate... using left/right arrows"). The old "3-or-fewer: plain
   CSS grid, no carousel at all" branch is GONE — the carousel shape is now
   unconditional. This is deliberately not a behavior regression for a
   wide desktop screen where everything already fits: assets/js/carousel.js
   still measures real slide positions and adds `is-single-page` (hiding
   the controls below) the moment nothing actually needs to scroll — a
   static 3-card row still LOOKS identical to the old plain-grid path,
   it's just genuinely swipeable/paginated the moment the viewport is too
   narrow to show them all, which a fixed CSS grid could never do. */
.testimonials-carousel { position: relative; }
.testimonials-viewport {
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
	-ms-overflow-style: none;
}
.testimonials-viewport::-webkit-scrollbar { display: none; }
.testimonials-grid { display: flex; gap: 24px; }
.testimonials-slide { flex: 0 0 calc((100% - 2 * 24px) / 3); min-width: 0; scroll-snap-align: start; }
@media (max-width: 1024px) { .testimonials-slide { flex-basis: calc((100% - 24px) / 2); } }
@media (max-width: 640px)  { .testimonials-slide { flex-basis: 88%; } }

.testimonials-slide.is-filtered-out { display: none; }

/* ── Shared carousel controls (2026-09-11, REPOSITIONED same day per
   Bilal's direct follow-up) — genuinely reused, not just visually
   matched: testimonials AND the homepage's service-card carousels
   (.product-grid-3/.homecare-grid, see their own section templates) both
   render these exact classes, driven by the ONE shared engine in
   assets/js/carousel.js, per Bilal's explicit "reuse the existing
   carousel component... avoid introducing a separate carousel interaction
   pattern."
   Arrows now float ON the card row (absolutely positioned, vertically
   centered, overlapping the row's own left/right edges) instead of
   sitting in a row below it — a deliberate REVERSAL of the "below the
   cards" layout shipped earlier the same day (2026-09-01), which existed
   specifically to avoid overlapping card content, and which was itself a
   reinstatement of an even earlier overlapping design Bilal had rejected
   for that exact reason. Reintroduced now because the real Employer App
   uses this overlapping pattern for its own carousels, and Bilal's
   explicit instruction is that cross-platform consistency with the app
   now outweighs the original overlap objection ("that's exactly the same
   pattern we implemented once but I rejected... but the mobile app is
   following the same experience therefore it has to be consistent").
   `pointer-events: none` on the row + `pointer-events: auto` on each
   button keeps the (now full-width, full-height) row from blocking
   clicks/taps on whatever card content it happens to sit over. ── */
.carousel-controls {
	position: absolute;
	top: 50%; left: 0; right: 0;
	transform: translateY(-50%);
	display: flex; align-items: center; justify-content: space-between;
	padding: 0 10px;
	margin-top: 0;
	z-index: 2;
	pointer-events: none;
}

.carousel-arrow {
	position: static; flex: 0 0 auto;
	pointer-events: auto;
	width: 44px; height: 44px; border-radius: 50%;
	background: var(--kk-orange-light); border: 1px solid rgba(250,107,44,.25);
	box-shadow: 0 4px 14px rgba(0,0,0,.16);
	display: flex; align-items: center; justify-content: center;
	color: var(--kk-orange); cursor: pointer;
	transition: background .15s ease, color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.carousel-arrow:hover:not(:disabled) { background: var(--kk-orange); color: #fff; box-shadow: 0 6px 18px rgba(250,107,44,.35); transform: scale(1.06); }
.carousel-arrow:disabled { opacity: .45; cursor: default; box-shadow: 0 2px 8px rgba(0,0,0,.1); }

/* Per-category theme colors (2026-09-13, Bilal: "the carousel navigation
   arrows should follow the respective theme accent colors... Beauty Care...
   Men's Grooming") — same `--{theme}` modifier convention already used for
   `.badge--purple`/`.chip--filter--purple`/etc. elsewhere in this theme,
   applied on top of the base .carousel-arrow rule above rather than
   duplicating it. Only these two categories have their own accent — every
   other carousel (Home Services, HomeCare) keeps the default orange, per
   Bilal's own explicit scope. */
.carousel-arrow--purple { background: rgba(214,45,249,.1); border-color: rgba(214,45,249,.25); color: var(--kk-beauty); }
.carousel-arrow--purple:hover:not(:disabled) { background: var(--kk-beauty); color: #fff; box-shadow: 0 6px 18px rgba(214,45,249,.35); }
.carousel-arrow--grooming { background: rgba(10,55,143,.08); border-color: rgba(10,55,143,.25); color: var(--kk-grooming); }
.carousel-arrow--grooming:hover:not(:disabled) { background: var(--kk-grooming); color: #fff; box-shadow: 0 6px 18px rgba(10,55,143,.35); }

.carousel-dots { display: flex; align-items: center; gap: 8px; }
.carousel-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--kk-line-soft); border: none; padding: 0; cursor: pointer; transition: background .15s ease, width .15s ease; }
.carousel-dot.is-active { background: var(--kk-orange); width: 22px; border-radius: 4px; }
.is-single-page .carousel-controls { display: none; }
/* Wrapper for the homepage's own product-card/HomeCare carousels
   (.product-grid-3/.homecare-grid + the .carousel-controls row below
   them) — a plain block, same minimal role .testimonials-carousel plays
   for testimonials. */
.product-carousel { position: relative; }

.testimonials-empty-state { text-align: center; color: var(--kk-muted); padding: 32px 0; }

/* Real Testimonials Hub grid (2026-09-13) — was accidentally reusing
   .testimonials-grid, the CAROUSEL TRACK's class (display:flex, no wrap) —
   a real bug, not a deliberate design, that squeezed every card into one
   un-wrapping row instead of a responsive grid. `.testimonials-hub-grid`
   is its own real CSS Grid, matching the general column-count/breakpoint
   shape .product-grid-cat's own service-card grid already uses (3 wide
   desktop -> 2 tablet -> 1 mobile), per Bilal's own "same general
   structure and responsive behavior as the service-card grid" — kept as a
   distinct class rather than literally reusing .product-grid-cat, since
   testimonial cards have different internal sizing needs (fixed media
   height, stretch-to-equal-height) already handled by .testimonial-card
   itself. NEVER a carousel, on any width — no overflow-x, no scroll-snap,
   no arrows/dots; see this page's own header comment. */
.testimonials-hub-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
@media (max-width: 1024px) { .testimonials-hub-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .testimonials-hub-grid { grid-template-columns: 1fr; } }

.pagination-row { margin-top: 40px; display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; }
.pagination-row .page-numbers { display: inline-flex; align-items: center; justify-content: center; min-width: 36px; height: 36px; padding: 0 10px; border-radius: 8px; border: 1px solid var(--kk-line-soft); color: var(--kk-muted); font-size: 13px; font-weight: 600; text-decoration: none; transition: border-color .15s ease, color .15s ease; }
.pagination-row .page-numbers.current { background: var(--kk-orange); border-color: var(--kk-orange); color: #fff; }
.pagination-row a.page-numbers:hover { border-color: var(--kk-orange); color: var(--kk-orange); }

/* Per-category theme colors (2026-09-13, Bilal: "category leaf pages...
   should use the same theme accent colors for the paginations") — same
   modifier convention as .carousel-arrow--purple/--grooming above. */
.pagination-row .page-numbers.current.page-numbers--purple { background: var(--kk-beauty); border-color: var(--kk-beauty); }
.pagination-row a.page-numbers--purple:hover { border-color: var(--kk-beauty); color: var(--kk-beauty); }
.pagination-row .page-numbers.current.page-numbers--grooming { background: var(--kk-grooming); border-color: var(--kk-grooming); }
.pagination-row a.page-numbers--grooming:hover { border-color: var(--kk-grooming); color: var(--kk-grooming); }

/* ── Why cards ───────────────────────────────────────────────────────── */
.why-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
/* Real bug fixed 2026-09-03 (Bilal: "'Why Book with KaamKrew' section is
   totally off... needs to be center aligned") — .why-grid's base rule is
   a 4-column grid, but why-cards.php (the actual component behind every
   "Why Book With KaamKrew" section — homepage, every category hub/leaf,
   Cleaning Services) always renders exactly 3 cards (confirmed directly:
   kaamkrew_hp_why_cards() and every real feature_cards set seeded in
   category-cms-provision.php are all exactly 3 items, consistently).
   3 items in a 4-column grid occupy columns 1-3 and leave the 4th column
   empty, which reads as the whole row hugging the left side of the
   container instead of sitting centered — this, not a missing
   text-align, was the actual "off/not centered" cause. Scoped to a new
   `.why-grid--3` modifier (added only by why-cards.php) rather than
   changing the base `.why-grid` rule itself, since business-hub.php/
   business-inquiry.php's own "Capabilities" section reuses the bare
   `.why-grid` class directly with a genuinely different (5-item) count —
   changing the shared base class would have fixed one real bug by
   introducing another. */
.why-grid--3 { grid-template-columns: repeat(3, 1fr); }
/* Icon+heading same-row layout, made the universal default 2026-09-11 —
   Bilal: the For Business "Capabilities" and Renovation "What Matters"
   cards (both this same shared why-cards.php component) were "too tall,
   creating unnecessary vertical space" at real desktop widths, with the
   icon and heading stacked; his own suggestion — "align the icon and
   heading in the same row" — is exactly the grid-template-areas
   restructure already proven correct on mobile since 2026-09-02 (round 3).
   Rather than duplicate that structure at a second breakpoint, it's now
   the base rule for every width; the `@media (max-width: 860px)` block
   below only tightens SIZE (smaller icon/padding) on top of the same
   shape, not the shape itself. Reaches every real caller of this
   component automatically — homepage/category "Why Book With KaamKrew",
   For Business's Capabilities, Construction/Renovation's own sections,
   About Us's 4-column Difference grid — all get the same compact card. */
.why-card {
	background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 20px;
	padding: 24px; transition: transform .25s;
	display: grid; grid-template-columns: auto 1fr; grid-template-areas: "icon heading" "desc desc";
	column-gap: 16px; row-gap: 10px; align-items: center;
}
.why-card:hover { transform: translateY(-4px); }
.why-card-icon { grid-area: icon; width: 48px; height: 48px; border-radius: 16px; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; }
/* Business Hub/Inquiry pages (2026-08-30) reuse .why-card for their
   Capabilities/"What matters" cards, but Business's own accent is blue
   throughout (badges, headings, buttons) — Figma confirms blue icons here
   too, not the default orange. */
.why-card-icon--blue { background: var(--kk-blue-light); color: var(--kk-blue); }
.why-card-icon--purple { background: rgba(214,45,249,.12); color: var(--kk-beauty); }
.why-card-icon--grooming { background: rgba(10,55,143,.12); color: var(--kk-grooming); }
.why-card h3 { grid-area: heading; font-weight: 800; font-size: 16px; color: var(--kk-ink); margin: 0; }
.why-card p { grid-area: desc; font-size: 13px; color: var(--kk-muted); line-height: 1.75; }
/* 2026-09-02: was forced to 2 columns on mobile — Figma shows one full-
   width card per row (each card fully legible, real visible border), not
   a cramped 2-up grid. */
@media (max-width: 860px) {
	.why-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
	.why-card { padding: 20px; border-color: var(--kk-line); }
	.why-card-icon { width: 44px; height: 44px; }
}

/* ── Split sections (mobile app / whatsapp) ──────────────────────────── */
.split-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: center; }
.split-inner--reverse { direction: rtl; }
.split-inner--reverse > * { direction: ltr; }
.split-text { font-size: 17px; color: var(--kk-muted); line-height: 1.75; margin-bottom: 32px; max-width: 460px; }
.split-text--on-dark { color: #93C5FD; }
.split-media { border-radius: 28px; height: 400px; background: linear-gradient(160deg, #e2e8f0, #cbd5e1); box-shadow: 0 24px 64px rgba(0,0,0,.13); }
@media (max-width: 860px) {
	.split-inner, .split-inner--reverse { grid-template-columns: minmax(0, 1fr); gap: 28px; direction: ltr; }
	.split-media { height: 200px; }
}

/* ── Business + Builders section (2026-09-01) ────────────────────────
   Merged from two separate stacked split-sections into one section, two
   matching cards side by side — see business-builders.php's own header
   comment. */
.business-builder-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px; }
/* display:flex + the button's own margin-top:auto (2026-09-16, Bilal:
   the two cards' CTA buttons weren't aligned since the two subtitles are
   different lengths) — pins each card's button flush to the bottom
   regardless of how many lines its own subtitle wraps to, so the buttons
   stay aligned even if either subtitle is edited again later in wp-admin
   (the copy itself was also lengthened to a comparable ~3 lines — see
   business-builders.php/cmb2-fields.php — this is the layout-level
   safety net on top of that, not a substitute for it). Deliberately left
   at the default align-items:stretch — the image/heading/subtitle all
   still stretch full-width exactly as before (block-level, centered via
   text-align), only the badge and button (naturally content-sized, not
   full-width) get an explicit align-self:center to keep their existing
   pill shape instead of stretching too. */
.business-builder-card { display: flex; flex-direction: column; border-radius: 28px; padding: 40px 36px; text-align: center; border: 1px solid; }
.business-builder-card--business { background: linear-gradient(180deg, var(--kk-blue-light) 0%, #fff 55%); border-color: rgba(10,55,143,.15); }
.business-builder-card--builders { background: linear-gradient(180deg, var(--kk-orange-light) 0%, #fff 55%); border-color: rgba(250,107,44,.2); }
.business-builder-image { border-radius: 20px; overflow: hidden; height: 260px; margin-bottom: 28px; background: linear-gradient(160deg, #e2e8f0, #cbd5e1); }
.business-builder-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
.business-builder-card .badge { align-self: center; margin-bottom: 20px; }
.business-builder-text { max-width: 420px; margin-left: auto; margin-right: auto; }
.business-builder-card .btn { align-self: center; margin-top: auto; }
/* Mobile shows genuinely different, frozen-old content (business-
   builders.php's own header comment explains why) — a straight toggle,
   not a reflow of the same markup. */
.business-builder-mobile-legacy { display: none; }
@media (max-width: 860px) {
	.business-builder-grid { display: none; }
	.business-builder-mobile-legacy { display: block; }
	.business-builder-mobile-legacy .split-section:first-child { margin-bottom: 48px; }
	.business-builder-mobile-legacy-image { border-radius: 20px; overflow: hidden; height: 200px; margin-top: 28px; background: linear-gradient(160deg, #e2e8f0, #cbd5e1); }
	.business-builder-mobile-legacy-image img { width: 100%; height: 100%; object-fit: cover; display: block; }
}

/* ── Mobile app section ──────────────────────────────────────────────── */
.mobile-app-section { background: linear-gradient(135deg, var(--kk-navy) 0%, var(--kk-blue) 100%); position: relative; overflow: hidden; padding-bottom: 40px; } /* 2026-09-15: scoped override of .section's own shared 60px bottom padding (left untouched everywhere else — that 60px is a real, independently-confirmed Figma constant, not part of this compactness pass) — this section sits on its own distinct navy background, self-contained enough not to need the same breathing room a plain-background section transition does. A real but secondary lever next to the mockup graphic's own height (below); saves 20px on its own. */
.mobile-app-section .badge { margin-bottom: 14px; } /* was the generic 20px */
.mobile-app-section .split-text { margin-bottom: 20px; } /* was the generic 32px — this column's own height never drives the section's total height (.split-inner centers it against the taller image column), but a tighter, more intentional-feeling copy block is still part of "review the internal spacing," not just a no-op */
.store-badges { display: flex; gap: 12px; flex-wrap: wrap; }
/* Admin-uploaded override for the App Store/Google Play icon (2026-09-03,
   see mobile-app.php's own field comments) — sized to match the built-in
   SVG glyph's own 20x20 footprint so an uploaded image of any real
   resolution stays crisp and consistent, never stretched. */
.store-badge-icon { width: 20px; height: 20px; object-fit: contain; flex-shrink: 0; }
.app-mockup { position: relative; display: flex; justify-content: center; }
/* position: relative added 2026-09-03 (was un-positioned) — this is now
   the floats' own positioning anchor, see .app-float's comment below. */
.app-mockup-screen { position: relative; width: 260px; height: 500px; border-radius: 44px; background: #0D0D0D; border: 5px solid rgba(255,255,255,.12); box-shadow: 0 40px 80px rgba(0,0,0,.5); padding: 20px; display: flex; flex-direction: column; }
/* Complete admin-uploaded mockup graphic (2026-09-03, replaces the block
   above entirely when set — see mobile-app.php's own header comment).
   No frame/border/shadow of our own: the uploaded export already has its
   own phone frame and floating cards baked in as pixels, so we just size
   it and let it carry its own design.
   CORRECTED same day (1st time): first cut reused .app-mockup-screen's
   own narrow 210-260px width tiers (deliberately slim there, since
   that's a bare phone silhouette meant to sit centered in a much wider
   column) — for a full composed graphic that ALSO includes overhanging
   cards, Bilal correctly flagged that as "relatively very small,"
   floating in a mostly-empty column. Widened to a flat 440px max-width.
   CORRECTED same day (2nd time), against a direct Figma dev-mode
   measurement Bilal shared (the whole section's real frame: 1920x623) —
   a flat width cap was the wrong anchor: at a real 1920px viewport, the
   440px-wide render of Bilal's own uploaded image (510x598 natural)
   worked out to 515.9px tall, pushing the section to 635.9px, ~13px over
   Figma's real 623px. HEIGHT is the real Figma-anchored dimension here,
   not width — switched to max-height instead: 503px (623px real section
   height, minus .section's own real, already-Figma-confirmed 120px
   combined top+bottom padding — see .section's own comment above), with
   width:auto/max-width:100% only as a narrow-viewport safety net so a
   differently-proportioned future upload can never overflow its column.
   Flat, not tiered like .hero-visual/.category-hero-image's own 3-tier
   system elsewhere in this file — those were tiered because they'd been
   measured hitting their ceiling too early (~1320-1420px, a genuinely
   common laptop width); this element has no such history and already
   lands within 1px of the real Figma number at a real 1920px viewport
   with a single flat value, so a speculative multi-tier breakdown wasn't
   invented without real per-tier Figma measurements to back it. */
.app-mockup-complete { width: auto; max-width: 100%; height: auto; max-height: 503px; display: block; } /* 503px is the literal Figma-measured ceiling — body.kk-figma-strict reproduces it exactly; see the default-mode override right below for the real, day-to-day compact value */
/* Compacted 2026-09-15 (Bilal, real screenshot: "this section feels too
   tall and takes almost the entire viewport"). Root cause, measured
   directly rather than guessed: this section's total height is
   (padding-top + padding-bottom + the TALLER of its two columns) — the
   left (copy) column measured only ~247px tall even with every one of its
   own internal gaps completely untouched, because .split-inner's own
   align-items:center means the shorter column never drives total section
   height at all. The image column, at its literal 503px Figma ceiling,
   was the one real, dominant lever — 503 of the section's real 587px
   total (1440px viewport), everything else combined was 84px of padding.
   Rebalancing "spacing/padding/hierarchy" without touching this number
   would have changed how the section FEELS at the same height, not
   actually made it shorter. Reduced ~24%, same ratio already used
   elsewhere in this file's own Option-B reproportioning tiers (the "roughly
   79/90/95% curve" .hero-visual/.category-hero-image/etc. already use) —
   not one more independently-measured Figma number, a deliberate
   compactness choice for this specific ask. Reverts to the literal 503px
   under body.kk-figma-strict, same mechanism as every other reproportioned
   element in this file — "maintain the Figma/design language" stays a
   real, one-flag-away guarantee, not a silently discarded option. */
body:not(.kk-figma-strict) .app-mockup-complete { max-height: 380px; }
.app-mockup-brand { color: #fff; font-size: 12px; font-weight: 700; }
.app-mockup-card { margin-top: auto; display: flex; flex-direction: column; gap: 10px; }
.app-mockup-card > p { color: #fff; font-size: 12px; font-weight: 700; }
.app-mockup-status { border-radius: 14px; padding: 10px 12px; background: rgba(255,255,255,.08); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,.12); }
.app-mockup-status p:first-child { color: #fff; font-size: 11px; font-weight: 600; }
.app-mockup-status p:last-child { color: #93C5FD; font-size: 10px; margin-top: 2px; }
/* 2026-09-02: the phone mockup itself (not just its floating cards,
   already hidden below) isn't shown on mobile per Figma — the section is
   copy + store buttons only there. */
@media (max-width: 860px) { .app-mockup { display: none; } }
/* Floating info cards overlapping the phone (2026-09-01), same visual
   language as the hero's .hero-float. FIXED 2026-09-03 (Bilal: "the cards
   must overlap over the phone as per Figma") — these were positioned
   absolute against .app-mockup, the section's own flex-centered wrapper,
   which is significantly wider than the phone (a full grid column, e.g.
   520px, against a 210-260px phone depending on viewport tier). At every
   viewport where that gap exceeds the float's own -20px/-24px offset, the
   card landed in the dead space beside the phone instead of overlapping
   it at all — confirmed live via getBoundingClientRect() at a normal
   1400px desktop width, zero pixels of overlap on either card. Fixed at
   the root, not by re-tuning offsets per breakpoint (fragile, the same
   bug the moment tiers change again): mobile-app.php now nests these
   inside .app-mockup-screen itself (the phone), so the same top/left/
   right offsets are always measured against the phone's own box — real
   overlap guaranteed at every tier without breakpoint-specific values. */
.app-float { position: absolute; width: 168px; border-radius: 16px; background: rgba(255,255,255,.97); box-shadow: 0 12px 32px rgba(0,0,0,.18); padding: 14px 16px; z-index: 3; }
.app-float-title { font-weight: 700; font-size: 12.5px; color: var(--kk-ink); margin-bottom: 3px; }
.app-float-sub { font-size: 10.5px; color: var(--kk-muted); line-height: 1.35; }
.app-float--rated { top: 20px; left: -24px; }
.app-float--confirmed { bottom: 96px; right: -24px; }
/* Proportional adjustment (2026-09-15) for the fallback phone's own new,
   shorter default-mode heights above (~76% of their previous values) —
   keeps this card sitting at roughly the same RELATIVE position on the
   now-shorter phone rather than crowding its very bottom edge. Only the
   `bottom` offset needed this; `top`/`left`/`right` aren't measured
   against the phone's own height the same way. Reverts to the literal
   96px under body.kk-figma-strict, same as the phone itself. */
body:not(.kk-figma-strict) .app-float--confirmed { bottom: 72px; }
@media (max-width: 860px) { .app-float { display: none; } }

/* ── WhatsApp section ────────────────────────────────────────────────── */
/* Real stops confirmed 2026-09-03 via direct Figma fill-panel inspection
   (Bilal's own screenshot) — was a generic green tint (#F0FFF9 -> #ECFDF5),
   never actually Figma-confirmed. Real gradient: warm cream (#FFF7EB) at
   0% into cool light gray (#F5F9F7) at 100%. Angle wasn't visible in the
   screenshot (Figma's fill panel doesn't show it, only the stops) — kept
   the existing 135deg diagonal rather than guess a different one; flag if
   Figma's actual angle turns out to differ once checked directly. */
.section--whatsapp { background: linear-gradient(135deg, #FFF7EB 0%, #F5F9F7 100%); }
/* 2026-09-01: dropped the right-column chat-preview mockup entirely (was
   .whatsapp-preview, .whatsapp-preview-x, .whatsapp-msg-x — all removed,
   nothing else referenced them) — one row, copy left + a single outline
   CTA right, matching the updated Figma screenshot.

   REAL BUG FOUND AND FIXED 2026-09-03: this comment's own prose used to
   list those old class names with a trailing wildcard immediately
   followed by a slash, e.g. "-preview-" then a star then a slash — that
   exact three-character sequence is a real CSS comment-close token, so
   it silently ended THIS comment several lines early, right there
   mid-sentence (deliberately not spelling that sequence out literally
   again here, for the obvious reason). Everything from
   that point up to the next real "{" (the .whatsapp-cta-col rule right
   below) was then parsed as one large invalid selector and the ENTIRE
   rule got silently discarded by the browser — the base (non-mobile)
   .whatsapp-cta-col rule never actually applied, at all, since the day
   this comment was written, which is exactly why the WhatsApp CTA button
   read as "misaligned" (it was just falling back to default block-level
   left alignment, not actually respecting flex/justify-content). Nothing
   else was affected — confirmed directly via the live CSSOM (every other
   rule between here and the next real comment parsed correctly); this
   was a single swallowed rule, not a wider corruption. Re-worded here
   with plain periods instead of wildcards so no future edit to this
   comment can reintroduce the same accidental self-close. */
.whatsapp-cta-col { display: flex; justify-content: flex-end; }
@media (max-width: 860px) { .whatsapp-cta-col { justify-content: flex-start; } }

/* ── FAQ ─────────────────────────────────────────────────────────────── */
.faq-list { display: flex; flex-direction: column; gap: 10px; }
.faq-item { border-radius: 18px; overflow: hidden; border: 1px solid var(--kk-line-soft); background: #fff; transition: border .2s, background .2s; }
.faq-item[open] { border: 2px solid rgba(250,107,44,.45); background: #FEF7F3; }
.faq-question { display: flex; align-items: center; justify-content: space-between; padding: 22px 28px; gap: 16px; cursor: pointer; font-weight: 700; font-size: 15px; color: var(--kk-ink); list-style: none; }
.faq-question::-webkit-details-marker { display: none; }
.faq-toggle-icon { flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%; background: #F3F4F6; display: flex; align-items: center; justify-content: center; position: relative; }
.faq-toggle-icon::before, .faq-toggle-icon::after { content: ''; position: absolute; background: #6B7280; }
.faq-toggle-icon::before { width: 12px; height: 2px; }
.faq-toggle-icon::after { width: 2px; height: 12px; transition: transform .2s; }
.faq-item[open] .faq-toggle-icon { background: var(--kk-orange); }
.faq-item[open] .faq-toggle-icon::before, .faq-item[open] .faq-toggle-icon::after { background: #fff; }
.faq-item[open] .faq-toggle-icon::after { transform: scaleY(0); }
.faq-answer { padding: 0 28px 24px; font-size: 14px; color: #4B5563; line-height: 1.8; }

/* ── Final CTA ───────────────────────────────────────────────────────── */
.final-cta { border-radius: 36px; padding: 96px 64px; text-align: center; position: relative; overflow: hidden; background: linear-gradient(135deg, var(--kk-orange) 0%, var(--kk-orange-dark) 60%, #B83D10 100%); }
/* RECALIBRATED 2026-08-29 (Option B), twice — see .section-title above
   for the full story. 64px max unchanged (not independently Figma-
   confirmed, carried over as the existing implementation value); anchor
   now 2560px, not 1920px. */
.final-cta h2 { font-size: clamp(2.5rem, 2.2426rem + 1.0984vw, 4rem); line-height: 1.08; color: #fff; margin-bottom: 16px; font-weight: 400; }
.final-cta p { font-size: 17px; color: rgba(255,255,255,.8); max-width: 400px; margin: 0 auto 36px; line-height: 1.75; }
.final-cta-actions { display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; }
@media (max-width: 860px) { .final-cta { padding: 56px 24px; border-radius: 24px; } }
/* Buttons stack full-width on mobile per Figma, not wrapped side by side. */
@media (max-width: 640px) {
	.final-cta-actions { flex-direction: column; align-items: stretch; }
	.final-cta-actions .btn { width: 100%; }
}

/* ── Footer ──────────────────────────────────────────────────────────── */
.site-footer { background: var(--kk-navy); }
.footer-inner { padding: 80px 64px 40px; display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 48px; }
.footer-brand p { font-size: 13px; color: #6B7280; line-height: 1.8; margin: 16px 0 20px; max-width: 220px; }
.footer-social { display: flex; gap: 10px; list-style: none; }
.footer-social a { width: 36px; height: 36px; border-radius: 10px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.footer-col h3 { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em; color: #fff; margin-bottom: 16px; }
.footer-col ul { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.footer-col a { font-size: 13px; color: #6B7280; }
.footer-col a:hover { color: #fff; }
.footer-legal { display: flex; align-items: center; justify-content: space-between; padding: 24px 64px; border-top: 1px solid rgba(255,255,255,.07); gap: 12px; flex-wrap: wrap; }
.footer-legal p { font-size: 12px; color: #4B5563; }
.footer-legal-links { display: flex; gap: 24px; flex-wrap: wrap; }
.footer-legal-links a { font-size: 12px; color: #4B5563; }
.footer-legal-links a:hover { color: #9CA3AF; }
@media (max-width: 860px) {
	.footer-inner { grid-template-columns: 1fr 1fr; padding: 48px 20px 32px; gap: 32px; }
	.footer-brand { grid-column: 1 / -1; }
	.footer-legal { padding: 16px 20px; flex-direction: column; align-items: flex-start; }
}

/* ── Category pages (hub + leaf) ─────────────────────────────────────── */
/* Added 2026-08-24 building the /services/{slug}-{id}/ pages — see
   inc/category-routing.php and template-parts/pages/category-*.php. */

/* Home icon + chevron separator confirmed against Figma 2026-08-27
   (previously: plain "Home" text, "/" separator). */
.breadcrumb { margin-bottom: 20px; }
.breadcrumb-list { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; list-style: none; padding: 0; margin: 0; font-size: 13px; }
.breadcrumb-item { display: flex; align-items: center; gap: 6px; color: var(--kk-muted-2); }
.breadcrumb-item:not(:first-child)::before { content: ''; width: 5px; height: 5px; border-top: 1.5px solid var(--kk-line); border-right: 1.5px solid var(--kk-line); transform: rotate(45deg); }
.breadcrumb-item a { display: inline-flex; align-items: center; gap: 5px; color: var(--kk-orange); font-weight: 600; }
.breadcrumb-item a:hover { color: var(--kk-orange-dark); }
.breadcrumb-item a .icon, .breadcrumb-item [aria-current="page"] .icon { width: 14px; height: 14px; }
.breadcrumb-item [aria-current="page"] { display: inline-flex; align-items: center; gap: 5px; color: var(--kk-muted); font-weight: 600; }
/* Themed breadcrumb links (2026-09-03) — see the theming-pass comment
   further down this file near .service-card--purple for the full context. */
.breadcrumb--purple .breadcrumb-item a { color: var(--kk-beauty); }
.breadcrumb--purple .breadcrumb-item a:hover { color: var(--kk-beauty-dark); }
.breadcrumb--grooming .breadcrumb-item a { color: var(--kk-grooming); }
.breadcrumb--grooming .breadcrumb-item a:hover { color: var(--kk-grooming-dark); }

.category-hero { padding-top: 56px; }
.category-hero-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 64px; align-items: center; }
/* Was max-width: 480px — real, confirmed gap: the copy column itself
   (.category-hero-grid's 1.1fr track) runs much wider than that on a real
   desktop viewport, so the subtitle wrapped early and left visible dead
   space before the hero image, on every category hub/leaf page (2026-09-05,
   Bilal's real screenshot flag, same structural-gap pattern also found on
   the Cleaning Services sub-sections' own .section-subtitle). */
.category-hero-copy .hero-subtitle { max-width: none; }
.category-hero-image { border-radius: 28px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,.12); }
.category-hero-image img { width: 100%; height: 380px; object-fit: cover; display: block; background: var(--kk-line-soft); }
/* No real photo uploaded yet for this category (see category-hub.php's
   own comment — $cms['hero_image'] is empty) — a plain placeholder block,
   same convention as the homepage hero, never a missing/broken image. */
.category-hero-image--empty { height: 380px; background: linear-gradient(160deg, #e2e8f0, #cbd5e1); }
@media (max-width: 960px) {
	.category-hero-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; }
	.category-hero-image img, .category-hero-image--empty { height: 260px; }
}

/* Men's Grooming hero — dark navy background, confirmed 2026-09-03 by
   direct Figma inspection (node 2136-7232). Every other themed element on
   this category's own page (product-card buttons, "Why book" icons, chips,
   etc.) correctly uses the real --kk-grooming color on the site's normal
   light background — this hero is the ONE deliberate exception, since the
   hero's own accent text is switched to orange for the same reason (see
   category-leaf.php's $hero_accent_class comment): grooming-blue text/UI
   on a grooming-blue background would be illegible. Mirrors the existing
   .builder-hero-copy .badge / .builder-hero-sub light-on-dark pattern
   rather than inventing a new one. Placed after .breadcrumb--grooming
   above so this wins at equal specificity — order matters here, don't
   move this block earlier in the file. */
.category-hero--groom { background: var(--kk-blue); }
.category-hero--groom .badge { background: rgba(255,255,255,.1); color: #fff; }
.category-hero--groom .hero-subtitle { color: rgba(255,255,255,.72); }
.category-hero--groom .hero-quick-facts li { color: rgba(255,255,255,.85); }
.category-hero--groom .btn--grooming { background: #fff; color: var(--kk-blue); box-shadow: 0 8px 24px rgba(0,0,0,.15); }
.category-hero--groom .breadcrumb-item { color: rgba(255,255,255,.55); }
.category-hero--groom .breadcrumb-item:not(:first-child)::before { border-top-color: rgba(255,255,255,.35); border-right-color: rgba(255,255,255,.35); }
.category-hero--groom .breadcrumb-item a { color: rgba(255,255,255,.85); }
.category-hero--groom .breadcrumb-item a:hover { color: #fff; }
.category-hero--groom .breadcrumb-item [aria-current="page"] { color: rgba(255,255,255,.55); }

/* Category tile card (template-parts/components/card-service.php) — the
   component already existed but had never been styled. */
.service-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
/* Icon tile — matches the real Figma "Browse by Category" tile (Home
   Services Hub, node 2118-38014): icon in a rounded square, name,
   description, "See Services" link. Replaced the old photo-card layout
   2026-08-27 — Figma's tiles never had a product photo at all. */
.service-card { display: block; padding: 24px; border-radius: 20px; background: var(--kk-surface); border: 1px solid transparent; transition: transform .2s, box-shadow .2s, border-color .2s; }
.service-card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgba(0,0,0,.08); border-color: var(--kk-line); background: #fff; }
.service-card-icon { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 12px; background: #fff; color: var(--kk-orange); margin-bottom: 16px; }
.service-card-icon .icon { width: 22px; height: 22px; }
.service-card-body { display: flex; flex-direction: column; gap: 4px; }
.service-card-title { font-weight: 700; font-size: 16px; color: var(--kk-ink); margin: 0; }
.service-card-tagline { font-size: 13.5px; color: var(--kk-muted); margin: 0 0 6px; line-height: 1.55; }
.service-card-link { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 700; color: var(--kk-orange); }
.service-card-link .icon { width: 14px; height: 14px; transition: transform .2s; }
.service-card:hover .service-card-link .icon { transform: translateX(3px); }
@media (max-width: 960px) { .service-grid { grid-template-columns: repeat(2, 1fr); gap: 18px; } }
@media (max-width: 640px) { .service-grid { grid-template-columns: minmax(0, 1fr); } }

/* Beauty Care / Men's Grooming theming pass (2026-09-03) — Bilal: "since
   BeautyCare and Men's Grooming pages have their induvial colors pallets
   therefore everything on those pages should follow those colors
   consistently... use their specific accent colors rather than generic
   orange color." Every one of these follows the same already-established
   --purple/--grooming modifier-class convention (.badge--purple,
   .accent-purple, .btn--purple, .testimonial-card--theme-beauty, etc. —
   see this file's own :root comment and card-product.php's accent
   classes) rather than inventing a new naming scheme. */
.service-card--purple .service-card-icon { background: rgba(214,45,249,.1); color: var(--kk-beauty); }
.service-card--purple .service-card-link { color: var(--kk-beauty); }
.service-card--purple:hover { border-color: var(--kk-beauty); }
.service-card--grooming .service-card-icon { background: rgba(10,55,143,.1); color: var(--kk-grooming); }
.service-card--grooming .service-card-link { color: var(--kk-grooming); }
.service-card--grooming:hover { border-color: var(--kk-grooming); }

/* margin-bottom reduced 40px -> 28px 2026-09-11, same sitewide spacing
   pass and same reasoning as .section-head's own reduction above — the
   row-layout variant of the identical heading-to-content gap. */
.section-head-row { display: flex; align-items: flex-end; justify-content: space-between; flex-wrap: wrap; gap: 16px; margin-bottom: 28px; }
.section-eyebrow { font-size: 13px; color: var(--kk-muted-2); font-weight: 600; margin: 0 0 8px; }

.section--tight { padding: 40px 0; }

/* ── Cleaning Services hub — inline sub-category sections (2026-08-27) ── */
.cleaning-sub-section { padding: 44px 0; }
.cleaning-sub-more { margin: 20px 0 0; }
.cleaning-sub-more a { font-size: 13px; font-weight: 700; color: var(--kk-orange-dark); }
.cleaning-sub-more a:hover { text-decoration: underline; }
.chip-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.chip--link { text-decoration: none; }
.chip--primary { background: var(--kk-orange); border-color: transparent; color: #fff; font-weight: 700; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.chip--primary:hover { color: #fff; }
.chip--primary--purple { background: var(--kk-beauty); }
.chip--primary--grooming { background: var(--kk-grooming); }
/* Outline pill link — 2026-09-04, HomeCare's "Explore more from KaamKrew"
   section (node 2137-11034): white/transparent bg, orange border+text,
   distinct from the plain gray .chip--link and the solid .chip--primary. */
.chip--outline { background: #fff; border: 1px solid var(--kk-orange); color: var(--kk-orange); font-weight: 700; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.chip--outline:hover { background: rgba(250,107,44,.06); }

/* ── Related Categories — "While you're at it" card (2026-09-16) ────────
   Rebuilt to match the real mobile-app design (3 screenshots: a light-blue
   rounded card, bold navy heading + gray subtitle, white pill chips each
   with their own real icon) — see related-categories.php's own header
   comment. Estimated proportions from the screenshots, not a Figma
   measurement — revisit if a fresh reference lands with exact numbers. */
.related-categories-card { background: var(--kk-blue-light); border-radius: 20px; padding: 24px; }
.related-categories-title { font-size: 17px; font-weight: 800; color: var(--kk-blue); margin: 0 0 2px; }
.related-categories-subtitle { font-size: 13px; color: var(--kk-muted); margin: 0 0 16px; }
.related-categories-pills { display: flex; flex-wrap: wrap; gap: 10px; }
.related-category-pill { display: inline-flex; align-items: center; gap: 8px; background: #fff; border-radius: 100px; padding: 8px 16px 8px 8px; font-size: 13px; font-weight: 600; color: var(--kk-ink); text-decoration: none; }
.related-category-pill:hover { box-shadow: 0 4px 12px rgba(0,0,0,.08); }
.related-category-pill-icon { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: var(--kk-orange-light); color: var(--kk-orange); flex-shrink: 0; }
.related-category-pill-icon--purple { background: rgba(214,45,249,.1); color: var(--kk-beauty); }
.related-category-pill-icon--grooming { background: rgba(10,55,143,.1); color: var(--kk-grooming); }
.related-category-pill-icon .icon { width: 15px; height: 15px; }

.service-grid-fallback { color: var(--kk-muted); padding: 32px 0; text-align: center; border: 1px dashed var(--kk-line); border-radius: var(--kk-radius); }

/* ── Cart (2026-08) — header badge, product-card Add button states, ───── */
/* full Cart page: items list + summary. Checkout/payment/voucher fields  */
/* deliberately absent — that's the WebApp's job, not this page's.        */

.product-card-add[disabled] { opacity: .4; cursor: not-allowed; }
.product-card-add.is-busy { opacity: .6; cursor: wait; }
.product-card-add.is-added { background: var(--kk-green); }

/* Coming Soon (2026-09-06) — city-specific product/service availability,
   see kaamkrew_resolve_city_price()'s docblock (category-content.php) for
   the real Employer App logic this mirrors. Deliberately NOT a dimmed/
   grayed-out overlay on the whole card (the app's own treatment) — Bilal's
   own reference screenshot shows the image/category/title still at full
   normal contrast, only the price+CTA area replaced by a plain disabled
   pill, so that's what this styles: a light-gray, muted button in the
   exact slot the real price/Add button would occupy — same width/height/
   radius as `.product-card-add`, no icon, not clickable. */
.product-card-add--soon[disabled] { background: var(--kk-line); color: var(--kk-muted); opacity: 1; cursor: default; } /* higher specificity than the plain .product-card-add[disabled] opacity:.4 rule above, which would otherwise win and wash this out to translucent */

.cart-badge {
	display: none;
	position: absolute;
	top: -2px;
	right: -2px;
	min-width: 16px;
	height: 16px;
	padding: 0 4px;
	border-radius: 100px;
	background: var(--kk-orange);
	color: #fff;
	font-size: 10px;
	font-weight: 800;
	line-height: 16px;
	text-align: center;
}
.cart-badge.is-visible { display: block; }

.cart-page { padding: 40px 0 80px; }
.cart-page-head { margin: 24px 0 32px; }
.cart-page-title { font-size: 32px; font-weight: 800; margin: 0 0 8px; }
.cart-page-city { display: flex; align-items: center; gap: 6px; color: var(--kk-muted); font-size: 14px; margin: 0; }
.cart-page-city .icon { width: 16px; height: 16px; color: var(--kk-orange); }

.cart-state { text-align: center; padding: 64px 24px; background: var(--kk-surface); border-radius: var(--kk-radius); }
.cart-state-title { font-size: 20px; font-weight: 800; margin: 0 0 8px; }
.cart-state-sub { color: var(--kk-muted); margin: 0 0 24px; }
.cart-state--error { color: var(--kk-ink); }
.cart-state--error p { margin: 0 0 20px; }

.cart-layout { display: grid; grid-template-columns: 1fr 360px; gap: 32px; align-items: start; }
@media (max-width: 900px) { .cart-layout { grid-template-columns: minmax(0, 1fr); } }

.cart-items { display: flex; flex-direction: column; gap: 16px; }

.cart-item {
	display: grid;
	grid-template-columns: 88px 1fr auto;
	gap: 16px;
	align-items: center;
	background: var(--kk-paper);
	border: 1px solid var(--kk-line);
	border-radius: var(--kk-radius);
	padding: 16px;
}
.cart-item--unavailable { opacity: .6; border-color: #F2B8B5; background: #FEF6F6; }

.cart-item-image { width: 88px; height: 88px; border-radius: 12px; overflow: hidden; background: var(--kk-surface); flex-shrink: 0; }
.cart-item-image img { width: 100%; height: 100%; object-fit: cover; display: block; }

.cart-item-body { min-width: 0; }
.cart-item-category { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--kk-orange-dark); }
.cart-item-name { font-size: 15px; font-weight: 700; margin: 4px 0; }
.cart-item-duration { font-size: 12px; color: var(--kk-muted); }
.cart-item-warning { font-size: 12px; color: #B42323; margin: 6px 0 0; font-weight: 600; }
.cart-item-price { margin-top: 8px; display: flex; align-items: baseline; gap: 8px; }
.cart-item-price .price-now { font-weight: 800; }
.cart-item-price .price-old { font-size: 12px; color: var(--kk-muted-2); text-decoration: line-through; }

.cart-item-controls { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.cart-item-stepper { display: flex; align-items: center; gap: 10px; border: 1.5px solid var(--kk-line); border-radius: 100px; padding: 4px; }
.cart-item-qty-btn { width: 26px; height: 26px; border-radius: 50%; border: none; background: var(--kk-surface); display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cart-item-qty-btn:disabled { opacity: .5; cursor: wait; }
.cart-item-qty-btn .icon { width: 14px; height: 14px; }
.cart-item-qty { min-width: 16px; text-align: center; font-weight: 700; font-size: 14px; }
.cart-item-line-total { font-weight: 800; margin: 0; }
.cart-item-remove { background: none; border: none; color: var(--kk-muted); font-size: 12px; font-weight: 700; text-decoration: underline; cursor: pointer; padding: 0; }
.cart-item-remove:disabled { opacity: .5; cursor: wait; }

.cart-summary {
	position: sticky;
	top: calc(var(--kk-header-h) + 16px);
	background: var(--kk-paper);
	border: 1px solid var(--kk-line);
	border-radius: var(--kk-radius);
	padding: 24px;
}
.cart-summary-title { font-size: 16px; font-weight: 800; margin: 0 0 16px; }
.cart-summary-row { display: flex; justify-content: space-between; font-size: 14px; color: var(--kk-muted); padding: 8px 0; }
.cart-summary-row--discount .cart-summary-discount { color: var(--kk-green); font-weight: 700; }
.cart-summary-row--total { border-top: 1px solid var(--kk-line); margin-top: 8px; padding-top: 16px; font-size: 16px; font-weight: 800; color: var(--kk-ink); }
.cart-summary-note { font-size: 12px; color: var(--kk-muted-2); margin: 12px 0 20px; }
.cart-checkout-btn { width: 100%; }

/* ── Builder Inquiry (Construction + Renovation) ────────────────────────
   Field label/border/fill values below are EXACT, confirmed 2026-08-28 by
   inspecting the real Figma file in Design mode (node 2138-14740,
   Renovation) — not estimated from the screenshot. See
   template-parts/pages/builder-inquiry.php's own header comment for what
   still doesn't have a confirmed Construction-specific frame. */
.builder-hero { padding-bottom: 48px; }
.builder-hero-grid { margin-top: 8px; }
.builder-hero-copy { max-width: 640px; margin-top: 24px; }
.builder-hero-sub { color: rgba(255,255,255,.72); }
/* 2026-09-04: real direct Figma inspection (node 2137-12418) shows the
   badge as a dark orange-tinted pill (translucent orange bg, orange text),
   not the white/translucent-white treatment this had been copied from
   Men's Grooming's category-hero--groom without checking — same class of
   bug already found and fixed on the About/Contact page heroes. */
.builder-hero-copy .badge { background: rgba(250,107,44,.15); color: var(--kk-orange); }
/* Real photo on the hero's right column, confirmed live in Figma — this
   hero had no image column at all before. Reuses .category-hero-grid/
   .category-hero-image directly (same component every other category
   hero already uses) rather than inventing a parallel one; .builder-hero-
   grid's own margin-top is kept as an additive class alongside it. */
.builder-hero .category-hero-grid { margin-top: 8px; }
.builder-hero .category-hero-copy { max-width: none; }
.builder-trust-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
.builder-trust-card { background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 16px; padding: 24px; }
.builder-trust-icon { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px; background: var(--kk-orange-light); color: var(--kk-orange); margin-bottom: 14px; }
.builder-trust-title { font-weight: 800; font-size: 14px; color: var(--kk-ink); margin-bottom: 4px; }
.builder-trust-text { font-size: 12.5px; color: var(--kk-muted); line-height: 1.6; }
@media (max-width: 760px) { .builder-trust-grid { grid-template-columns: minmax(0, 1fr); } }

/* "Project types" — Construction/Renovation cross-link cards, node 2137-12418 */
.builder-project-types { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; margin-top: 28px; }
.builder-project-card { background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 20px; padding: 32px; text-align: left; transition: border-color .15s ease, box-shadow .15s ease; } /* was text-align:center — real Figma inspection (2026-09-04) shows left-aligned content */
.builder-project-card.is-current { border-color: var(--kk-orange); box-shadow: 0 4px 24px rgba(0,0,0,.05); }
.builder-project-icon { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; border-radius: 14px; background: var(--kk-orange-light); color: var(--kk-orange); margin-bottom: 16px; }
.builder-project-name { font-weight: 800; font-size: 18px; color: var(--kk-ink); margin-bottom: 8px; }
.builder-project-text { font-size: 13.5px; color: var(--kk-muted); line-height: 1.6; }
@media (max-width: 640px) { .builder-project-types { grid-template-columns: minmax(0, 1fr); } }

/* "Process" — 5-step numbered row. Real Figma (node 2137-12418) shows a
   thin horizontal line connecting the numbered circles — added as a
   pseudo-element on the row, sitting behind the circles (step content has
   its own opaque background via z-index so the line doesn't show through
   the number itself). */
.builder-process-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 20px; margin-top: 28px; position: relative; }
.builder-process-row::before { content: ''; position: absolute; top: 18px; left: 0; right: 0; height: 1px; background: var(--kk-line); z-index: 0; }
.builder-process-step { text-align: center; position: relative; z-index: 1; }
.builder-process-num { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: var(--kk-ink); color: #fff; font-size: 14px; font-weight: 800; margin-bottom: 14px; }
.builder-process-title { font-weight: 800; font-size: 14px; color: var(--kk-ink); margin-bottom: 6px; }
.builder-process-text { font-size: 12.5px; color: var(--kk-muted); line-height: 1.55; }
@media (max-width: 900px) { .builder-process-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .builder-process-row { grid-template-columns: minmax(0, 1fr); } }

.builder-form-grid { display: grid; grid-template-columns: 1.6fr 1fr; gap: 24px; align-items: start; margin-top: 32px; }
@media (max-width: 900px) { .builder-form-grid { grid-template-columns: minmax(0, 1fr); } }

.builder-form-card { background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 20px; padding: 28px; box-shadow: 0 4px 24px rgba(0,0,0,.05); display: flex; flex-direction: column; gap: 20px; }
.builder-section-title { font-size: 15px; font-weight: 800; color: var(--kk-ink); margin: 8px 0 -4px; padding-top: 8px; border-top: 1px solid var(--kk-line-soft); }
.builder-form-card > .builder-section-title:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.builder-required { color: var(--kk-orange); margin-left: 2px; }
.builder-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 560px) { .builder-field-row { grid-template-columns: minmax(0, 1fr); } }
/* Account Sign Up's Title/First name/Last name row (2026-09-07) — Title
   deliberately narrow, matching the small prefix-select the Employer App's
   own signUp.js sits next to its Full Name field. */
.builder-field-row--title { grid-template-columns: 110px 1fr 1fr; }
@media (max-width: 560px) { .builder-field-row--title { grid-template-columns: minmax(0, 1fr); } }

.builder-field label,
.builder-field-label { display: block; font-family: 'Inter', sans-serif; font-weight: 600; font-size: 13px; color: var(--kk-ink); margin-bottom: 8px; }
.builder-field input[type="text"],
.builder-field input[type="tel"],
.builder-field input[type="number"],
.builder-field input[type="email"],
.builder-field input[type="password"],
.builder-field select,
.builder-field textarea {
	width: 100%;
	height: 44px;
	padding: 0 14px;
	background: #fff;
	border: 1px solid #C2C2C2;
	border-radius: 8px;
	font-size: 14px;
	color: var(--kk-ink);
	font-family: inherit;
}
.builder-field input::placeholder,
.builder-field textarea::placeholder { color: rgba(17,17,17,.5); }
.builder-field input:focus,
.builder-field select:focus,
.builder-field textarea:focus { outline: none; border-color: var(--kk-orange); }
.builder-field input[readonly] { background: var(--kk-surface); color: var(--kk-muted); }
.builder-field textarea { height: auto; padding: 12px 14px; resize: vertical; }

/* Premium custom radio — hidden native input + styled dot, replacing the
   plain accent-color browser default Bilal flagged as "not crisp". Label
   text sits on its own baseline next to the dot (was misaligned before
   because the native control's own box model doesn't line up with text
   the same way across browsers). */
.builder-radio-row { display: flex; flex-wrap: wrap; gap: 24px; padding-top: 4px; }
.builder-radio { display: inline-flex; align-items: center; gap: 9px; font-size: 14px; line-height: 1; color: var(--kk-ink); cursor: pointer; position: relative; }
.builder-radio input[type="radio"] { position: absolute; opacity: 0; width: 20px; height: 20px; margin: 0; cursor: pointer; }
.builder-radio-dot { flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%; border: 1.5px solid #C2C2C2; background: #fff; display: inline-flex; align-items: center; justify-content: center; transition: border-color .12s ease; }
.builder-radio-dot::after { content: ''; width: 10px; height: 10px; border-radius: 50%; background: var(--kk-orange); transform: scale(0); transition: transform .12s ease; }
.builder-radio input[type="radio"]:checked ~ .builder-radio-dot { border-color: var(--kk-orange); }
.builder-radio input[type="radio"]:checked ~ .builder-radio-dot::after { transform: scale(1); }
.builder-radio input[type="radio"]:focus-visible ~ .builder-radio-dot { box-shadow: 0 0 0 3px var(--kk-orange-light); }
.builder-radio-text { padding-top: 1px; }

.builder-chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip--select { cursor: pointer; border: 1px solid #C2C2C2; background: #fff; }
.chip--select.is-active { background: var(--kk-orange); border-color: var(--kk-orange); color: #fff; }
.builder-other-input { margin-top: 10px; }

.builder-upload { display: flex; align-items: center; justify-content: center; height: 96px; border: 1.5px dashed #C2C2C2; border-radius: 10px; cursor: pointer; text-align: center; padding: 12px; }
.builder-upload span { font-size: 13px; color: var(--kk-muted); }
.builder-upload:hover { border-color: var(--kk-orange); }
.builder-upload-error { color: #B34A18; font-size: 12.5px; margin: 10px 0 0; }

/* Design-image thumbnails (2026-09-07) — a real preview grid per selected
   file, matching the Employer App's own FileUploadComponent, replacing the
   plain "N images selected" text-only count. */
.builder-upload-grid { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.builder-upload-thumb { position: relative; width: 76px; height: 76px; border-radius: 10px; overflow: hidden; border: 1px solid var(--kk-line-soft); }
.builder-upload-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.builder-upload-thumb-remove { position: absolute; top: 4px; right: 4px; width: 20px; height: 20px; border-radius: 50%; border: none; background: rgba(17,17,17,.65); color: #fff; font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.builder-upload-thumb-remove:hover { background: rgba(17,17,17,.85); }

/* Area — Google Places autocomplete dropdown (2026-09-07). Anchored off
   .builder-field--address (position: relative) rather than the whole form,
   so it always sits directly under this one input regardless of where the
   field falls in the layout. */
.builder-field--address { position: relative; }
.builder-address-suggestions { position: absolute; top: 100%; left: 0; right: 0; z-index: 20; margin-top: 4px; background: #fff; border: 1px solid var(--kk-line); border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,.12); max-height: 240px; overflow-y: auto; }
.builder-address-suggestion { display: block; width: 100%; text-align: left; padding: 10px 14px; font-size: 13.5px; color: var(--kk-ink); background: none; border: none; border-bottom: 1px solid var(--kk-line-soft); cursor: pointer; }
.builder-address-suggestion:last-child { border-bottom: none; }
.builder-address-suggestion:hover,
.builder-address-suggestion:focus { background: var(--kk-surface); }

.builder-form-error { background: #FEF0E8; border: 1px solid #F5C6A5; color: var(--kk-orange-ink, #B34A18); border-radius: 8px; padding: 12px 14px; font-size: 13px; }

/* Per-field inline validation (2026-09-07) — reuses the same error ink
   (#B34A18) the existing top-level .builder-form-error banner already
   uses, rather than inventing a separate red, matching how this project's
   own house style treats "error" as a tone of the brand orange, not a
   stock red. Applies to both plain inputs/selects and the custom radio
   dot (.builder-radio-dot) so a radio-group error (size_unit, property_
   type, design_needed, design_exist) reads clearly too, not just text
   fields. */
.builder-field--error input[type="text"],
.builder-field--error input[type="tel"],
.builder-field--error input[type="number"],
.builder-field--error input[type="email"],
.builder-field--error input[type="password"],
.builder-field--error select,
.builder-field--error .builder-radio-dot { border-color: #B34A18; } /* matches the base .builder-field input[type="..."] selectors' own specificity (0,0,2,1) — a plain ".builder-field--error input" (0,0,1,1) loses to that base rule regardless of source order, so this repeats its type list rather than relying on one generic tag match */
.builder-field-error-text { color: #B34A18; font-size: 12.5px; margin: 6px 0 0; }
.builder-submit { width: 100%; justify-content: center; }
.builder-form-success { text-align: center; padding: 32px 12px; }
.builder-form-success h3 { font-size: 20px; margin-bottom: 8px; }
.builder-form-success p { color: var(--kk-muted); }

.builder-info-card { background: var(--kk-surface); border: 1px solid var(--kk-line-soft); border-radius: 20px; padding: 28px; position: sticky; top: calc(var(--kk-header-h) + 16px); }
.builder-info-card h3 { font-size: 16px; font-weight: 800; margin-bottom: 18px; }
.builder-info-steps { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.builder-info-steps li { display: flex; gap: 12px; font-size: 13px; color: var(--kk-ink); line-height: 1.55; }
.builder-info-steps li span { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; background: var(--kk-ink); color: #fff; font-size: 11px; font-weight: 800; display: flex; align-items: center; justify-content: center; }
.builder-info-call { font-size: 13px; color: var(--kk-orange); font-weight: 700; border-top: 1px solid var(--kk-line-soft); padding-top: 16px; }
.builder-info-call a { color: inherit; }

/* Business Inquiry (2026-08-29, rebuilt 2026-08-30 against real Figma
   inspection) — the form itself reuses every .builder-* class above (form
   card, field, info aside) as-is, since it's the identical component Figma
   uses for Builder Inquiry's own form. What's new for the Business Hub
   page (template-parts/pages/business-hub.php) is the "Industries we
   serve" tile grid and the "How a business engagement works" step grid,
   neither of which existed as a component anywhere else in this theme. */

/* Corrected 2026-09-04, against a direct Figma inspection (node 2138-15898,
   the real form card) rather than the screenshot-based build above — two
   real, measured differences from what shipped:
   1) Compactness — Figma's field-group rows stack with ~0px gap between
      them (each row's own label+input already totals ~64px, confirmed by
      selecting consecutive rows and reading their Y positions: 24, 88.24 —
      an exact 64.24px step, no extra gap layered on top). The shared
      .builder-form-card's 20px flex gap was adding real height on top of
      that on every row — 5 extra 20px gaps across this 6-row form, ~100px
      of avoidable height. Card padding is 32px in Figma, was 28px.
   2) "Prefer to talk directly?" — shared .builder-info-call defaults to
      var(--kk-orange), correct for Builder's own orange identity, but
      Figma shows this in the business theme's blue on the Business form.
   Scoped to .business-form-grid (added to business-inquiry-form.php's own
   wrapper) rather than changed on the shared .builder-* classes directly —
   Builder Inquiry's own form was NOT re-checked against Figma for this
   same spacing in this pass, so its existing 20px/28px/orange defaults are
   deliberately left untouched pending its own dedicated check. */
.business-form-grid .builder-form-card { gap: 4px; padding: 32px; }
.business-form-grid .builder-field label,
.business-form-grid .builder-field-label { margin-bottom: 4px; }
.business-form-grid .builder-info-call { color: var(--kk-blue); }

/* ── Contact Us page (2026-09-04, built against Figma node 2138-19556;
   corrected 2026-09-04 same day against direct Figma dev-mode inspection —
   exact padding/gap/font-size values read from the real file via Design
   panel, not eyeballed from screenshots. Real measurements cited inline. */
.contact-hero { padding: 64px 0; } /* Figma's real Section (2138-20847): 1920 Fill x 281 Hug, padding 64/60 — was 64px 0 56px, close but off */
/* Real, confirmed source of the "hero fills the whole viewport" complaint:
   .section-head's own base rule carries `margin-bottom: 56px`, meant for
   separating a section's head from body content below it (cards, a grid).
   The hero's own .section-head IS the entire hero content — nothing sits
   below it inside this section, so that 56px was pure dead space pushing
   the hero to 374px measured live vs Figma's real 281px hug height for the
   same content. Scoped removal, not a change to the shared .section-head
   rule (which other pages legitimately rely on for real spacing below it). */
.contact-hero .section-head { margin-bottom: 0; }
/* Real bug, not a style choice: badge and heading were rendering full-width
   inside .contact-form-card. .builder-form-card is `display:flex;
   flex-direction:column` with no align-items set, which defaults to
   `stretch` — correct for the field rows (they SHOULD fill the card width)
   but wrong for the badge/heading, which Figma shows hugging their own
   content width, left-aligned, inside a full-width row. Scoped to these two
   direct children only, so the field rows' own stretch is untouched. */
.contact-form-card > .badge,
.contact-form-card > h2 { align-self: flex-start; }
/* Card padding: Figma's real Container padding is 40px (confirmed via
   Design panel — was 28px here, the shared .builder-form-card default).
   Gap: Figma's own inner "Form" auto-layout (the field rows + submit
   button) runs a real 16px gap, vs. the shared .builder-form-card default
   of 20px — the card's OUTER auto-layout (badge → heading → form) is
   actually gap:0 in Figma (each element spaces itself via its own
   intrinsic height), but this card's real markup keeps badge/heading/
   fields/button as one flat flex stack rather than introducing Figma's own
   extra nesting for no functional reason — 16px, the dominant real number,
   applied uniformly is the closest match without restructuring markup. */
.contact-form-card { border-color: var(--kk-orange); padding: 40px; gap: 16px; }
/* Heading: Figma's real "Contact us" text is 36px (confirmed directly on
   the Text node) — .section-title--sm's clamp (a page-section heading
   size, up to 42px) was never right for a heading living inside a compact
   card. Fixed size, not a clamp — this card doesn't scale with viewport
   width the way a full section does. */
.contact-form-card h2 { font-size: 36px; line-height: 1.15; letter-spacing: normal; }
/* Column ratio: Figma's real 2 columns measure 708.89 (form) : 567.12
   (info cards) — a ~1.25:1 ratio, not the 1.6:1 the shared
   .builder-form-grid default uses (that ratio is real for Business/Builder/
   HomeCare's own single-aside-card layout, just never independently
   confirmed for Contact's 4-stacked-card layout until now). This directly
   explains "the form is still very much wide" — the form column really was
   proportionally wider than Figma's own design calls for. */
/* Real, confirmed bug (2026-09-07, QA report + screenshot): this rule had
   no media query at all, so its own `grid-template-columns` unconditionally
   beat .builder-form-grid's own `@media (max-width: 900px)` mobile-stacking
   override (equal specificity, later in source order always wins) — the
   info cards never stacked below the form on a real phone, they just ran
   off the right edge. Scoped to the same >900px breakpoint
   .builder-form-grid itself uses, so mobile now correctly falls through to
   that shared single-column behavior, unmodified. */
@media (min-width: 901px) {
	.contact-grid { gap: 64px; grid-template-columns: 1.25fr 1fr; } /* gap: Figma's own real 2-column grid gap, confirmed via Design panel (was 24px, .builder-form-grid's shared default) */
}

.contact-info-cards { display: flex; flex-direction: column; gap: 20px; } /* confirmed matches Figma exactly */
.contact-info-card { background: var(--kk-surface); border: 1px solid var(--kk-line-soft); border-radius: 20px; padding: 28px; } /* Figma's real Container padding, confirmed (was 24px) */
.contact-info-card-head { display: flex; align-items: center; gap: 16px; margin-bottom: 0; } /* Figma's real icon-row gap is 16px (was 12px); real gap to the value line below is 0 — the icon+title row and the value text sit flush, confirmed by their Y-positions in the file (was an invented 14px margin) */
.contact-info-card-head h3 { font-size: 15px; font-weight: 800; margin: 0; color: var(--kk-ink); }
.contact-info-icon { width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; }
.contact-info-icon--blue { background: var(--kk-blue-light); color: var(--kk-blue); }
.contact-info-icon .icon { width: 18px; height: 18px; }
.contact-info-value { display: block; font-size: 17px; font-weight: 700; color: var(--kk-ink); text-decoration: none; margin: 0 0 8px; } /* Figma's real value-line block carries an 8px bottom pad before the note (was 4px) */
.contact-info-value:hover { color: var(--kk-orange); }
.contact-info-value--sm { font-size: 14px; font-weight: 600; }
.contact-info-note { font-size: 13px; color: var(--kk-muted); margin: 0; }

/* ── About Us page (2026-09-04, built against Figma node 2150-34503) ───
   New components only — trust-bar/testimonials/mobile-app/whatsapp/
   faq-list/final-cta/footer/why-cards are all exact reuse, no CSS needed. */

/* Hero — solid navy, left-aligned (NOT .section-head--center, unlike
   Contact's own hero). Same "dark hero, light text" override set already
   proven correct for .category-hero--groom, copy-adapted under its own
   name since this isn't a category page. */
.about-hero { background: var(--kk-blue); padding: 64px 0; }
/* Real bug, same class already found and fixed on Contact's own hero: the
   .section-head base rule carries `margin-bottom: 56px`, meant for
   separating a section's head from body content sitting below it. About's
   hero .section-head IS the entire hero content — nothing sits below it in
   this section — so that 56px was pure dead space, measured live adding a
   real ~56px of unaccounted-for height between the subtitle and the hero's
   own bottom padding (was never zeroed out here the way Contact's was). */
.about-hero .section-head { margin-bottom: 0; }
/* Real bug: badge text was set to white, overriding the site-wide default
   badge color (var(--kk-orange), already correct here per Figma) — copied
   from .category-hero--groom's own badge treatment without checking
   whether About's hero actually wants the same white-text pill (it
   doesn't; Figma shows the same orange eyebrow text every other page's
   badge already uses, just on a translucent dark pill background). */
.about-hero .badge { background: rgba(255,255,255,.1); color: var(--kk-orange); }
/* Real bug: subtitle was capped at an invented 720px. Figma's real text
   node (confirmed via Design panel) is "1340 Fill" — it spans the FULL
   container width, same as every other section on this page, not a
   narrower reading-width column. The shared .hero-subtitle base rule
   ALSO carries its own max-width (460px, for the homepage's narrower hero
   column) — simply removing my own override let that one win instead, so
   this has to explicitly cancel it with `none`, not just omit the property. */
.about-hero .hero-subtitle { color: rgba(255,255,255,.72); max-width: none; }
.about-hero .breadcrumb-item { color: rgba(255,255,255,.55); }
.about-hero .breadcrumb-item:not(:first-child)::before { border-top-color: rgba(255,255,255,.35); border-right-color: rgba(255,255,255,.35); }
/* Real bug, same class as the badge above: "Home" was set to translucent
   white, overriding the site-wide default breadcrumb-link color
   (var(--kk-orange), which is what Figma actually shows here too). */
.about-hero .breadcrumb-item a { color: var(--kk-orange); }
.about-hero .breadcrumb-item a:hover { color: var(--kk-orange-dark); }
.about-hero .breadcrumb-item [aria-current="page"] { color: rgba(255,255,255,.55); }

/* Stats band — reuses .stats-band/.stats-grid/.stat/.stat-number/.stat-label
   (already shared with the homepage's own 3-stat strip). Only new bit: a
   4-column modifier (base .stats-grid is 3-col, dropped from 4 back in
   2026-08-28 when the homepage lost a stat) and a --dark modifier for the
   "2023" stat, which Figma shows in plain ink, not orange, unlike the other
   3 — a real, confirmed visual difference, not an inconsistency. */
/* Real Figma measurement (Design panel, node 2150-34614): each stat is its
   own bordered cell — 1px solid rgba(10,55,143,.33) on all 4 sides,
   padding 32px vertical / 20px horizontal, gap 0 between cells (the
   borders ARE the separation, confirmed real, not missing by accident).
   The outer .stats-grid's own 48px/64px padding + 32px gap (the homepage's
   own values, inherited wholesale) were never right for this layout — real
   spacing lives on the cell itself, which is also why the band read as
   taller/more spaced-out than Figma: padding was being applied twice
   (grid AND, implicitly, the extra gap) where Figma only applies it once. */
.stats-grid--4 { grid-template-columns: repeat(4, 1fr); padding: 0; gap: 0; }
/* Padding axes were swapped — real Figma dev-mode measurement (node
   2150-34588, 2026-09-06) confirms 20px vertical / 32px horizontal (the
   inner content group sits at Y:20 within the cell, not Y:32); this had
   been coded backwards as 32px vertical / 20px horizontal since the
   2026-09-04 pass, making every cell measurably taller than Figma and
   compounding the "band feels taller/gappier than the mockup" complaint. */
.stats-grid--4 .stat { border: 1px solid rgba(10, 55, 143, .33); padding: 16px 24px; } /* 2026-09-07: 20px/32px read as oversized for how little text sits in each cell — tightened so the band reads compact rather than stretched */
.stats-grid--4 .stat:first-child { border-left: none; }
.stats-grid--4 .stat:last-child { border-right: none; }
.stats-grid--4 .stat-number { font-size: 28px; line-height: 32px; color: var(--kk-ink); } /* 2026-09-07: 36px read as oversized — reduced; 2026-09-06: real Figma check shows every number in this row plain ink, not just the first — the old .stat--dark modifier only darkened $i===0 (a leftover from a design that was never actually built this way) */
.stats-grid--4 .stat-label { color: #E85D04; text-transform: uppercase; font-size: 14px; font-weight: 700; letter-spacing: .05em; } /* font-size/weight/letter-spacing added 2026-09-06 — real Figma measurement (Inter Bold 14px, 0.7px letter-spacing ≈ .05em at this size), was inheriting the homepage's own 13px/500/normal label style. color: real Figma fill — was inheriting the homepage's own muted-gray label color, which was never right here; homepage's own 3-stat labels are untouched */
@media (max-width: 860px) { .stats-grid--4 { grid-template-columns: repeat(2, 1fr); } }

/* Shared left-aligned badge+heading wrapper for the About sections that
   don't use .section-head (Founder/Cities/Affiliations sit left-aligned,
   not centered, per Figma). */
.about-plain-head { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; margin-bottom: 28px; }

/* Leadership/Founder — confirmed 2026-09-06 via direct Figma dev-mode
   inspection (mobile frame, "LeadershipSection" > "Container", 0 gap/0
   padding) that the "Founder" heading sits with ZERO space directly under
   the badge, not this shared block's usual 16px/28px — a real, deliberate
   compact treatment for this one section, not the generic section-head
   spacing every other About section uses. The live desktop frame itself
   couldn't be reached this session (Figma access repeatedly timed out) —
   revisit if that surfaces a different desktop-specific number. */
.about-plain-head--tight { gap: 0; margin-bottom: 20px; }
.about-founder-heading { font-size: 28px; font-weight: 900; line-height: 1.2; margin: 0; color: var(--kk-ink); }

/* Why We Exist — numbered story blocks (left column) + a real photo with a
   floating caption card (right column). Rebuilt 2026-09-06 against a direct
   Figma dev-mode inspection (node 2154-41271, real desktop "15 - About Us"
   frame) after Bilal flagged the cards' border, the right side's "separate
   card" look, and the 50/50 column split as all wrong vs Figma — every
   value below is a real measured number, not a guess:
   - Column ratio: left "Container" 800.67 Fill vs right image 479.33 fixed
     (≈1.67:1), row gap 60 (was an even 1fr/1fr split, gap 80) — own
     .split-inner--why-exist modifier so the Mission/Why-KaamKrew/Mobile-App
     sections' shared .split-inner (1fr/1fr, gap 80) is untouched.
   - Card: real 4-side border (was left-strip-only), 1px solid #0A378F,
     6%-opacity #0A378F fill, radius 0 (sharp — confirmed 0 in Figma, not
     this site's usual rounded card), padding 16px, 16px gap card-to-card.
   - Label: Plus Jakarta Sans Bold 14px/14 line-height/2.8px letter-spacing
     (0.2em), #0A378F, with a real 2px #0A378F rule directly under it
     (Figma's own "VerticalBorder" layer — a horizontal divider despite the
     name, not a vertical bar) — implemented as a label border-bottom
     rather than a second DOM element. The leading "|" is a real glyph in
     Figma's own label text, added here via ::before so it's decorative
     only and never has to be retyped into the editable copy.
   - Right image: fixed 3:4 aspect ratio (479.33:639.11), radius 0 (sharp,
     confirmed — was 28px). No longer height:100%/min-height, which is what
     let the placeholder stretch to the LEFT column's full 3-card height
     and pushed the floating card off the bottom of the viewport.
   - Floating card: real "OUR STANDARD" composition is a bottom-anchored,
     full-width gradient scrim (transparent → ~55% #0A378F) holding a
     genuinely glassy card — white @ 80% + backdrop-blur, 1px solid
     #FA6B2C on 3 sides + a 4px #FA6B2C accent on the left, radius 0,
     24px padding, 8px gap. Badge: Inter Bold 14px/1.4px letter-spacing/
     #001D5B, plain text (no pill — Figma has no background on it, unlike
     this site's usual orange kicker pills). Body: Inter Regular 16px/24
     line-height/#1C1B1B. This is what replaces the old fixed 24px-inset
     "separate card floating over a stretched placeholder" version. */
.about-story-blocks { display: flex; flex-direction: column; gap: 16px; margin-top: 28px; }
.about-story-block { border: 1px solid var(--kk-blue); background: rgba(10, 55, 143, .06); padding: 16px; border-radius: 0; }
.about-story-block-label { display: block; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 14px; line-height: 14px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--kk-blue); margin: 0 0 8px; }
.about-story-block-label::before { content: '| '; }
.about-story-block p { font-size: 14px; line-height: 1.65; color: var(--kk-muted); margin: 0; }
.split-inner--why-exist { grid-template-columns: 1.67fr 1fr; gap: 60px; }
.about-story-photo { position: relative; aspect-ratio: 479 / 639; border-radius: 0; overflow: hidden; }
.about-story-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about-story-photo--empty { background: linear-gradient(160deg, #e2e8f0, #cbd5e1); } /* same honest-placeholder pattern as .category-hero-image--empty — no photo uploaded yet, never just blank space */
.about-photo-caption { position: absolute; left: 0; right: 0; bottom: 0; padding: 32px; background: linear-gradient(180deg, rgba(10, 55, 143, 0) 0%, rgba(10, 55, 143, .55) 100%); }
.about-photo-caption-card { background: rgba(255, 255, 255, .8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid var(--kk-orange); border-left: 4px solid var(--kk-orange); border-radius: 0; padding: 24px; display: flex; flex-direction: column; gap: 8px; }
.about-photo-caption-badge { display: block; font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #001D5B; }
.about-photo-caption-card p { font-family: 'Inter', sans-serif; font-size: 16px; line-height: 24px; color: #1C1B1B; margin: 0; }
@media (max-width: 860px) { .split-inner--why-exist { grid-template-columns: minmax(0, 1fr); gap: 28px; } .about-photo-caption { padding: 20px; } .about-photo-caption-card { padding: 18px; } }

/* Our Mission — 2 cards, one solid-navy ("For Customers"), one light
   bordered ("For Professionals") — matches the real Figma treatment
   exactly (not two identically-styled cards). */
.about-mission-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 32px; }
@media (max-width: 760px) { .about-mission-grid { grid-template-columns: minmax(0, 1fr); } }
.about-mission-card { border-radius: 20px; padding: 32px; }
.about-mission-card--dark { background: var(--kk-blue); color: #fff; }
.about-mission-card--light { background: #fff; border: 1px solid var(--kk-line-soft); }
.about-mission-card .badge { margin-bottom: 16px; }
.about-mission-card--dark .badge { background: rgba(255,255,255,.15); color: #fff; }
.about-mission-card h3 { font-size: 22px; font-weight: 800; line-height: 1.25; margin: 0 0 12px; }
.about-mission-card p { font-size: 14px; line-height: 1.6; margin: 0; }
.about-mission-card--dark p { color: rgba(255,255,255,.75); }
.about-mission-card--light p { color: var(--kk-muted); }

/* How We Verify Every Professional — dark section, 6-step horizontal
   stepper. Real Figma measurement: each number badge is a 64×64 rounded
   square, connected by a single line running through the row; step 1 is
   the only "active" (orange) one, the rest sit inactive (dark gray) — the
   verification sequence itself, not which step the CURRENT visitor is on
   (there's no such concept here, unlike a real multi-step form). */
.about-verify { background: var(--kk-navy); }
.about-verify .badge { background: rgba(250,107,44,.15); color: var(--kk-orange); }
.about-verify .section-title { color: #fff; }
.about-verify-steps { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; margin-top: 48px; position: relative; }
.about-verify-steps::before { content: ''; position: absolute; top: 32px; left: 32px; right: 32px; height: 1px; background: rgba(255,255,255,.12); z-index: 0; }
.about-verify-step { display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; flex: 1; position: relative; z-index: 1; }
.about-verify-circle { width: 64px; height: 64px; border-radius: 18px; background: rgba(255,255,255,.06); color: rgba(255,255,255,.5); display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 800; }
.about-verify-step--active .about-verify-circle { background: var(--kk-orange); color: #fff; }
.about-verify-step-label { font-size: 13px; font-weight: 700; color: #fff; }
@media (max-width: 760px) { .about-verify-steps { flex-wrap: wrap; row-gap: 28px; } .about-verify-steps::before { display: none; } .about-verify-step { flex: 0 0 calc(33.33% - 8px); } }

/* Founder card. Real Figma measurement: photo 343×401 (≈0.856:1), card
   border-radius 20px matching every other card on the site. 2026-09-07:
   the border shown in Figma is only ever around the text/content side —
   the photo itself has none — so it lives on .about-founder-body (top/
   right/bottom, no left edge, so it doesn't read as a divider against the
   photo), not on the outer card, with matching corner radii only where
   the body panel actually meets a rounded outer corner. */
.about-founder-card { display: grid; grid-template-columns: 343px 1fr; background: #fff; border-radius: 20px; overflow: hidden; margin-top: 32px; box-shadow: 0 4px 24px rgba(0,0,0,.05); }
.about-founder-photo { width: 100%; height: 100%; min-height: 320px; object-fit: cover; display: block; }
.about-founder-photo--empty { background: linear-gradient(160deg, #e2e8f0, #cbd5e1); }
.about-founder-body { padding: 32px 40px; display: flex; flex-direction: column; border: 1px solid var(--kk-blue); border-left: none; border-top-right-radius: 20px; border-bottom-right-radius: 20px; }
.about-founder-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.about-founder-name { font-size: 26px; font-weight: 800; margin: 0; }
.about-founder-title { font-size: 14px; font-weight: 700; color: var(--kk-blue); margin: 4px 0 0; }
.about-founder-tags { display: flex; gap: 8px; flex-wrap: wrap; }
.about-founder-tag { font-size: 11px; font-weight: 700; color: var(--kk-muted); background: var(--kk-surface); border: 1px solid var(--kk-line-soft); border-radius: 999px; padding: 4px 12px; }
.about-founder-stats { display: flex; gap: 32px; margin: 24px 0; flex-wrap: wrap; }
.about-founder-stat-value { font-size: 15px; font-weight: 800; color: var(--kk-orange); margin: 0; }
.about-founder-stat-label { font-size: 12px; color: var(--kk-muted); margin: 2px 0 0; white-space: nowrap; }
.about-founder-quote { border-left: 3px solid var(--kk-orange); padding-left: 18px; font-size: 14px; font-style: italic; line-height: 1.65; color: var(--kk-ink); margin: 0 0 20px; }
.about-founder-linkedin { display: inline-flex; align-items: center; gap: 8px; align-self: flex-start; background: var(--kk-blue); color: #fff; font-size: 13px; font-weight: 700; border-radius: 10px; padding: 12px 18px; text-decoration: none; }
.about-founder-linkedin:hover { background: var(--kk-blue-dark); }
@media (max-width: 760px) { .about-founder-card { grid-template-columns: minmax(0, 1fr); } .about-founder-photo { height: 280px; min-height: 0; } .about-founder-body { padding: 24px; border: 1px solid var(--kk-blue); border-top: none; border-radius: 0 0 20px 20px; } }

/* Where We Operate — city cards. Real Figma measurement: 260×224, radius
   16px, photo + a dark gradient for text legibility (matches the real
   backend city list, kaamkrew_catalog_cities(), horizontally scrollable —
   same swipe-row pattern already used for product-card carousels). */
/* 2026-09-07: was a horizontal swipe-row (overflow-x:auto), same pattern as
   the product-card carousels — Bilal: real cities never fill the row evenly
   and it was showing a visible scrollbar with cards clipped at the edge
   instead of a clean fit. Switched to flex-wrap: every card keeps its real
   Figma-measured 260px cap but can shrink down to a 200px floor so the row
   distributes evenly with zero scroll at any width the real, small city
   list (5 today, "more being added over time" per copy elsewhere on this
   page) needs to wrap onto a 2nd row only once cards would drop below that
   floor. */
.about-cities-row { display: flex; flex-wrap: wrap; gap: 20px; margin-top: 32px; }
.about-city-card { position: relative; flex: 1 1 200px; max-width: 260px; height: 224px; border-radius: 16px; overflow: hidden; background: var(--kk-surface); }
.about-city-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about-city-card-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(0,0,0,.75) 100%); }
.about-city-card-body { position: absolute; left: 16px; right: 16px; bottom: 14px; color: #fff; }
.about-city-card-name { display: flex; align-items: center; gap: 6px; font-size: 16px; font-weight: 800; margin: 0; }
.about-city-card-name .icon { width: 14px; height: 14px; }
.about-city-card-tagline { font-size: 12px; color: rgba(255,255,255,.8); margin: 2px 0 0; }

/* Affiliations — 2 simple outline pill links, no cards/logos in this
   design (confirmed directly against the Figma prototype). */
.about-affiliations-links { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 24px; }
.about-affiliation-link { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--kk-line-soft); border-radius: 999px; padding: 12px 22px; font-size: 14px; font-weight: 700; color: var(--kk-blue); text-decoration: none; }
.about-affiliation-link:hover { border-color: var(--kk-blue); background: var(--kk-blue-light); }
.about-affiliation-link .icon { width: 16px; height: 16px; }

.industry-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.industry-tile { display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 16px; padding: 24px 16px; transition: border-color .2s, transform .2s; }
.industry-tile:hover { border-color: var(--kk-blue); transform: translateY(-2px); }
.industry-tile-icon { width: 44px; height: 44px; border-radius: 12px; background: var(--kk-blue-light, #E8F0FE); color: var(--kk-blue); display: flex; align-items: center; justify-content: center; }
.industry-tile-name { font-size: 13px; font-weight: 700; color: var(--kk-ink); }
@media (max-width: 860px) { .industry-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; } }

.process-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.process-step { background: #fff; border: 1px solid var(--kk-line-soft); border-radius: 20px; padding: 28px 24px; }
.process-step-number { width: 32px; height: 32px; border-radius: 50%; background: var(--kk-blue); color: #fff; font-size: 13px; font-weight: 800; display: flex; align-items: center; justify-content: center; margin-bottom: 16px; }
/* Orange variant — 2026-09-04, HomeCare's "How It Works" 4-step section
   (node 2137-11034) reuses this exact component (Business Hub's own
   Process explainer) but with orange numbered circles, not blue — HomeCare
   has no blue "identity" the way For Business does. */
.process-step-number--orange { background: var(--kk-orange); }
.process-step h3 { font-weight: 800; font-size: 15px; color: var(--kk-ink); margin-bottom: 8px; }
.process-step p { font-size: 13px; color: var(--kk-muted); line-height: 1.6; }
@media (max-width: 860px) { .process-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; } }

@media (max-width: 640px) {
	/* Real, confirmed bug (2026-09-16, reported directly against a live
	   screenshot): this rule shrinks the grid's own first track to 64px,
	   but .cart-item-image's base rule (width/height: 88px, unscoped to any
	   breakpoint) was never shrunk to match — a fixed-size grid child
	   doesn't shrink to fit its own track just because the track did, so
	   the 88px image spilled 24px into the text column at every viewport
	   this wide or narrower, confirmed via getBoundingClientRect() (88px
	   image in a 64px track). Every category/product renders through this
	   exact same markup, so this reached every cart line item, every
	   screen ≤640px, regardless of which service was in the cart — not a
	   one-off glitch specific to the Sofa Cleaning example reported. Fixed
	   by shrinking the image to match its own track, right alongside the
	   grid-column change that requires it, so the two can never drift out
	   of sync again the way they just did. */
	.cart-item { grid-template-columns: 64px 1fr; }
	.cart-item-image { width: 64px; height: 64px; }
	.cart-item-controls { grid-column: 1 / -1; flex-direction: row; justify-content: space-between; align-items: center; margin-top: 8px; }
}

/* ══════════════════════════════════════════════════════════════════════
   FIGMA-STRICT MODE — the Option A revert switch (2026-08-29)
   ══════════════════════════════════════════════════════════════════════
   Everything above is Option B, the direction agreed 2026-08-29: Figma is
   the visual/proportional source of truth, reproportioned with clamp()
   so a real laptop/monitor doesn't render literal 1920px-canvas pixel
   values the way the pre-2026-08-29 implementation did. Corrected once
   the same day — the first cut still converged on the literal numbers
   by ~1680-1920px, which Bilal's own screenshot comparison at his real
   ~1900px window showed was still too large versus Figma's own prototype
   player (which scales its 1920 frame down to fit whatever window it's
   opened in). DEFAULT mode's tiers now don't reach the literal ceiling
   until >=2560px on any of these — see each rule's own comment above and
   CLAUDE.md's 2026-08-29 entries for the full before/after.

   If Product/Business later decide the 1920px Figma should be followed
   literally on every real screen regardless of size (Option A), the
   ENTIRE revert is a single wp-config.php constant — no CSS, template,
   or other file needs to change:

       define( 'KAAMKREW_FIGMA_STRICT', true );

   (mirrors the existing KAAMKREW_WAM_ENV pattern — see
   kaamkrew-webapp-migrator.php). That constant stamps a `kk-figma-strict`
   class on <body> (see inc/design-scale.php), and every rule below pins
   straight back to the literal, Figma-confirmed pixel number at EVERY
   viewport, unconditionally — this block was never anchored to a
   specific viewport width to begin with (it's a flat override, not a
   clamp()), so today's correction to the default-mode anchor (1920px ->
   2560px) required zero changes here. This is the actual "no gap on
   revert" guarantee: not that default mode happens to match Figma at
   some specific width, but that this switch always does, at any width,
   on demand. */
body.kk-figma-strict { --kk-container: 1340px; --kk-card-image-h: 192px; --kk-card-pad: 20px; }
body.kk-figma-strict .section-title { font-size: 66px; } /* confirmed 2026-08-27, node 2154-42555/2118-38015 */
body.kk-figma-strict .section-title--sm { font-size: 42px; } /* confirmed 2026-08-27, same nodes */
body.kk-figma-strict .section-title--homepage { font-size: 58px; } /* confirmed 2026-08-27, Bilal's own screenshot */
body.kk-figma-strict .hero-title { font-size: 44px; } /* carried over as-is — NOT independently Figma-confirmed, see .hero-title's own comment */
body.kk-figma-strict .final-cta h2 { font-size: 64px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .product-grid-3, body.kk-figma-strict .product-grid-cat { gap: 40px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .service-grid { gap: 28px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .hero-visual { height: 560px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .hero-visual-phone { width: 280px; height: 400px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .hero-visual-photo { width: 320px; height: 420px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .category-hero-image img { height: 380px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .split-media { height: 400px; } /* carried over as-is — NOT independently Figma-confirmed */
body.kk-figma-strict .app-mockup-screen { width: 260px; height: 500px; } /* carried over as-is — NOT independently Figma-confirmed */

/* ==========================================================================
   CUSTOMER ACCOUNT PAGE — 2026-09-10 (Sign Up/Login/Profile/Addresses/
   Logout + Order-History-to-app redirect modal). No Figma reference existed
   for this page — built against the established component language
   (.builder-form-card/.builder-field/.section-title--sm/.btn--outline-gray
   etc.) rather than inventing a new visual system, same discipline as
   every other functional (non-marketing) page in this theme.
   ========================================================================== */

.account-hero .section-head { margin-bottom: 0; } /* same dead-space fix already applied to .contact-hero — this hero's own .section-head IS the entire hero content, nothing else sits below it in the section */

/* Login / Sign Up tabs */
.account-auth { max-width: 480px; margin: 0 auto; }
.account-auth-tabs { display: flex; gap: 6px; background: #F3F4F6; border-radius: 12px; padding: 4px; margin-bottom: 20px; }
.account-auth-tab { flex: 1; padding: 10px; border: none; border-radius: 9px; background: transparent; font-family: inherit; font-size: 14px; font-weight: 700; color: var(--kk-muted); cursor: pointer; transition: background .15s, color .15s; }
.account-auth-tab.is-active { background: #fff; color: var(--kk-ink); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
.account-auth-panel { max-width: none; }
/* Real, confirmed bug, caught via live browser testing: .builder-form-card
   sets `display: flex`, which — same specificity as the browser's own
   default `[hidden] { display: none }` UA rule, and coming later in the
   cascade — silently WINS, so toggling the `hidden` attribute via JS had
   zero visual effect on either the #account-login-form or #account-
   signup-form panel (both carry .builder-form-card). Same class of CSS-
   specificity trap already documented elsewhere in this file (see the
   2026-09-07 Builder Inquiry validation entry in CLAUDE.md) — fixed the
   same way: a higher-specificity rule that actually wins. */
.account-auth-panel[hidden] { display: none; }
.account-auth-switch { text-align: center; font-size: 13px; color: var(--kk-muted); margin: 14px 0 0; }
.account-auth-switch-link { border: none; background: transparent; padding: 0; font-family: inherit; color: var(--kk-orange); font-size: inherit; font-weight: 700; text-decoration: underline; cursor: pointer; }

/* Forgot Password / OTP Verification / Create New Password (2026-09-11) —
   same specificity trap as .account-auth-panel above (.account-auth-tabs
   also sets `display: flex`, which otherwise wins over the browser's own
   `[hidden]` default when assets/js/auth.js hides the tab bar while one of
   these 3 panels is open). */
.account-auth-tabs[hidden] { display: none; }
.account-auth-forgot { text-align: right; margin: -8px 0 0; font-size: 13px; }
.account-auth-panel--centered { text-align: center; }
.account-auth-panel--centered .builder-field { text-align: left; }
.account-auth-icon { width: 48px; height: 48px; border-radius: 50%; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; margin: 0 auto 4px; }
.account-auth-heading { font-size: 19px; font-weight: 800; color: var(--kk-ink); margin: 0; }
.account-auth-subtext { font-size: 13px; color: var(--kk-muted); line-height: 1.6; margin: 0; }
.account-auth-notice { font-size: 13px; color: #16A34A; background: #F0FDF4; border: 1px solid #BBF7D0; border-radius: 10px; padding: 10px 12px; text-align: center; }

/* Sign Up / Log In account-verification OTP gate (2026-09-07) — the "Back"
   button is hidden via JS for that context (see auth.js's own otpFlow
   handling); same specificity trap as every other [hidden] toggle on a
   .btn in this file (.btn's own `display: inline-flex` otherwise wins). */
[data-otp-back][hidden] { display: none; }
.account-otp-boxes { display: flex; justify-content: center; gap: 12px; }
.account-otp-box { width: 52px; height: 56px; text-align: center; font-size: 22px; font-weight: 700; border: 1.5px solid var(--kk-line); border-radius: 12px; }
.account-otp-box:focus { border-color: var(--kk-orange); outline: none; }

.account-password-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; text-align: left; }
.account-password-checklist li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--kk-muted); }
.account-password-checklist li .icon { color: var(--kk-line); flex-shrink: 0; width: 16px; height: 16px; }
.account-password-checklist li.is-met { color: var(--kk-ink); }
.account-password-checklist li.is-met .icon { color: #16A34A; }

/* HomeCare hand-off banner (2026-09-16) — shown only via JS (auth.js) when a
   visitor arrives here mid-way through a HomeCare plan request; see
   account-auth-forms.php's own header comment. Sits above the Log In/Sign Up
   tabs regardless of which is active. */
.account-homecare-banner { display: flex; align-items: flex-start; gap: 10px; background: var(--kk-orange-light); border: 1px solid rgba(250, 107, 44, .3); border-radius: 12px; padding: 12px 14px; margin-bottom: 16px; font-size: 13px; color: var(--kk-ink); line-height: 1.5; }
.account-homecare-banner[hidden] { display: none; }
.account-homecare-banner .icon { color: var(--kk-orange); flex-shrink: 0; width: 18px; height: 18px; margin-top: 1px; }
.account-homecare-banner p { margin: 0; }

/* Logged-in dashboard — real, confirmed density fix (2026-09-07), per
   Bilal's direct feedback against a real screenshot: this page previously
   sat inside .container--narrow (720px, correct for the Sign Up/Login
   forms, wrong for this real 2-column dashboard grid — see account.php's
   own comment) and every card heading used .section-title--sm, a 28-42px
   marketing-page-section scale ("feels like a sectionized About Us page").
   Both fixed at the root (the container swap in account.php, and
   .account-card-title below) rather than patched with more padding
   tweaks — a real eCommerce account page uses the site's normal content
   width and small, functional headings, not landing-page section scale. */
.account-grid { display: grid; grid-template-columns: 1.6fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 900px) { .account-grid { grid-template-columns: minmax(0, 1fr); } }
.account-col { display: flex; flex-direction: column; gap: 16px; }
.account-card { max-width: none; padding: 24px; gap: 16px; }
.account-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.account-card-head h2 { margin: 0; }
/* Real, compact card-title scale — reuses .account-auth-heading's own
   already-established 19px/800/1.3 values (the Forgot Password/OTP
   screens' own heading) so the whole account experience shares one
   consistent "small but real heading" language, instead of borrowing the
   marketing .section-title scale a plain settings card never needed. */
.account-card-title { font-size: 19px; font-weight: 800; color: var(--kk-ink); line-height: 1.3; margin: 0; }

.account-field-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--kk-line-soft); font-size: 14px; }
.account-field-row:last-child { border-bottom: none; padding-bottom: 0; }
.account-field-row span { color: var(--kk-muted); }
.account-field-row strong { color: var(--kk-ink); font-weight: 700; }

.account-profile-actions { display: flex; gap: 10px; margin-top: 4px; }
.account-profile-actions .btn { flex: 1; }

.account-empty { font-size: 14px; color: var(--kk-muted); padding: 8px 0; }
.account-address-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 4px; }
.account-address-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 14px; border: 1px solid var(--kk-line-soft); border-radius: 12px; font-size: 14px; }
.account-address-row strong { display: block; color: var(--kk-ink); }
.account-address-row p { margin: 4px 0 0; color: var(--kk-muted); }
.account-address-form { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--kk-line-soft); }

.account-order-card { text-align: center; }
.account-order-icon { width: 48px; height: 48px; border-radius: 50%; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; margin: 0 auto 14px; }
.account-order-card p { font-size: 14px; color: var(--kk-muted); margin: 10px 0 18px; }

/* Profile card — real visual parity pass (2026-09-07), matching a real
   screenshot of the Employer App's own profile screen (see account-
   dashboard.php's own header comment) rather than the plain view/edit
   toggle this page shipped with originally. */
.account-avatar-wrap { position: relative; width: fit-content; margin: 0 auto 20px; }
.account-avatar { width: 88px; height: 88px; border-radius: 50%; background: #E4E7F7; color: #4C4FA0; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.account-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.account-avatar .icon { width: 40px; height: 40px; }
.account-avatar-edit { position: absolute; bottom: -2px; right: -2px; width: 28px; height: 28px; border-radius: 50%; background: var(--kk-orange); color: #fff; border: 2px solid #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
.account-avatar-edit .icon { width: 14px; height: 14px; }

/* "Filled" input style — a soft gray fill instead of .builder-field's own
   default white-with-border, matching the app screenshot's own input
   treatment. Additive to .builder-field (label/spacing/error markup all
   stay shared, unchanged) — only the input/select's own background/border
   differ. The existing `.builder-field input[readonly]` rule (higher
   specificity: class+tag+attribute vs. this rule's class+tag) already
   wins for the read-only Phone field, so it keeps its own distinct muted
   look automatically, with no extra override needed here. */
.builder-field--filled input[type="text"],
.builder-field--filled input[type="tel"],
.builder-field--filled input[type="email"],
.builder-field--filled select { background: var(--kk-surface); border-color: transparent; }
.builder-field--filled input[readonly] { cursor: not-allowed; }

.account-field-hint { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--kk-muted); margin: 8px 0 0; }
.account-field-hint .icon { width: 14px; height: 14px; flex-shrink: 0; }

.account-manage-addresses { margin: -8px 0 4px; }
.account-manage-addresses a { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 700; color: var(--kk-blue); text-decoration: none; }
.account-manage-addresses a:hover { text-decoration: underline; }
.account-manage-addresses .icon { width: 16px; height: 16px; }

.account-danger-zone { margin-top: 4px; padding-top: 20px; border-top: 1px dashed var(--kk-line); }
.account-danger-label { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--kk-muted); margin: 0 0 12px; }
.btn--danger-outline { background: #fff; color: #B91C1C; border: 1.5px solid #FECACA; }
.btn--danger { background: #DC2626; color: #fff; }
.app-modal-icon--danger { background: #FEF2F2; color: #DC2626; }

/* App-redirect modal — same fixed/scrim/panel shape as city-modal.php's
   own, own class names to keep the two independent (different triggers,
   different content shape: a single centered CTA card, not a list). */
.app-modal[hidden] { display: none; }
.app-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; padding: 20px; }
.app-modal-scrim { position: absolute; inset: 0; background: rgba(13,13,13,.5); backdrop-filter: blur(4px); }
.app-modal-panel { position: relative; z-index: 1; width: 100%; max-width: 420px; background: #fff; border-radius: var(--kk-radius); box-shadow: 0 24px 64px rgba(0,0,0,.25); padding: 32px 28px 28px; text-align: center; }
.app-modal-close { position: absolute; top: 14px; right: 14px; }
.app-modal-close .icon { transform: rotate(45deg); } /* reuses the shared 'minus' glyph, rotated into an "x" — same trick city-modal's own close button doesn't need since it uses a dedicated icon, but this one avoids adding a near-duplicate svg path just for a 45deg rotation */
.app-modal-icon { width: 56px; height: 56px; border-radius: 50%; background: var(--kk-orange-light); color: var(--kk-orange); display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; }
.app-modal-icon .icon { width: 28px; height: 28px; }
.app-modal-panel h2 { font-size: 19px; font-weight: 800; color: var(--kk-ink); margin: 0 0 10px; line-height: 1.3; }
.app-modal-panel p { font-size: 14px; color: var(--kk-muted); line-height: 1.6; margin: 0 0 22px; }
.app-modal-actions { display: flex; flex-direction: column; gap: 10px; }
.app-modal-store { display: flex; align-items: center; justify-content: center; gap: 8px; }
.app-modal-store .icon { width: 18px; height: 18px; }
/* Real, confirmed bug (2026-09-07) — assets/js/auth.js's own real-time
   platform detection sets `link.hidden = true` on whichever store link
   doesn't match a detected iOS/Android device, but this rule's own
   `display: flex` (equal specificity to, and later in the cascade than,
   the browser's native `[hidden]{display:none}`) silently defeated that
   entirely — both buttons always rendered regardless of device, on every
   real phone this modal was ever opened on. Same specificity trap already
   documented several times elsewhere in this file. */
.app-modal-store[hidden] { display: none; }
