/* =========================================================================
   KALOCKSCO — Handyman · Electrical · Remodeling
   styles.css  ·  Single source of truth for design tokens
   Rebrand the whole site by editing the :root variables below.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. DESIGN TOKENS
   ------------------------------------------------------------------------- */
:root {
  /* Brand palette ------------------------------------------------------- */
  /* --- Santa Monica palette, re-skinned: TEAL primary ----------------- */
  --sm-teal-1:        #0E7A80;   /* primary surface / headings            */
  --sm-teal-2:        #0B6065;   /* deeper teal — hero / CTA bands         */
  --sm-teal-3:        #084A4E;   /* deepest teal — footer                  */
  --sm-teal-br:       #2BC2C9;   /* bright teal — light tints              */
  --sm-orange:        #FB4D1A;   /* sun — accent + borders                 */
  --sm-orange-dark:   #DD3D0E;   /* orange hover / dark accent             */
  --sm-gold:          #F9B23A;   /* beach — YELLOW: buttons & highlights   */
  --sm-gold-dark:     #DD971C;   /* yellow hover                           */
  --sm-charcoal:      #333333;   /* logo wordmark — body text              */
  --klein:            #002FA7;   /* International Klein Blue — accent       */
  --klein-deep:       #001F7A;   /* darker IKB for gradients               */

  /* --- Semantic roles (every component references THESE) --------------- */
  --color-navy:        var(--sm-teal-1);      /* primary surface / heading */
  --color-navy-deep:   var(--sm-teal-2);      /* darker surface            */
  --color-navy-darker: var(--sm-teal-3);      /* deepest surface (footer)  */
  --color-amber:       var(--sm-orange);      /* accent + borders          */
  --color-amber-dark:  var(--sm-orange-dark); /* accent hover              */
  --color-teal:        var(--sm-teal-br);
  --color-teal-dark:   var(--sm-teal-1);
  --color-gold:        var(--sm-gold);        /* yellow: buttons/highlights*/
  --color-gold-dark:   var(--sm-gold-dark);
  --color-ink:         var(--sm-charcoal);    /* primary body text         */
  --color-muted:       #5B6573;   /* secondary text                        */
  --color-line:        #E3E7ED;   /* hairline borders                      */
  --color-surface:     #FFFFFF;   /* cards / panels                        */
  --color-bg:          #F1FAFB;   /* page background sections (teal tint)   */
  --color-bg-alt:      #E1F4F5;   /* alternating section background         */
  --color-success:     var(--sm-teal-1);      /* checkmarks / verified     */
  --color-star:        var(--sm-gold);        /* review stars (yellow)     */
  /* neutral + semantic UI colors (NOT part of the 4-colour brand palette) */
  --color-white:       #FFFFFF;   /* solid white — text on dark, field bg  */
  --ok-bg:             #E7F6EE;   --ok-fg:     #1C7C45;   /* success msg   */
  --err-bg:            #FCEBEA;   --err-fg:    #B3261E;   /* error msg     */
  --danger:            #B53A2C;   --danger-bg: #FCEAE8;   /* remove button */
  --tint-line:         #D4DAE3;   /* card hover border                     */
  --tint-cool:         #DDE6F2;   /* admin toggle hover                    */

  /* Typography ---------------------------------------------------------- */
  --font-head: "Poppins", "Segoe UI", Arial, sans-serif;
  --font-body: "Inter", "Segoe UI", Arial, sans-serif;

  /* --fs-hero cut 30%; every other text token cut 15% (2026-07-02 density pass) */
  --fs-hero:    clamp(1.47rem, 1.8vw + 0.7rem, 2.28rem);
  --fs-h2:      clamp(1.45rem, 2.1vw + 0.85rem, 2.21rem);
  --fs-h3:      clamp(0.98rem, 0.85vw + 0.77rem, 1.19rem);
  --fs-lead:    clamp(0.89rem, 0.5vw + 0.81rem, 1.06rem);
  --fs-body:    0.85rem;
  --fs-small:   0.74rem;
  /* SHARED TOKEN (2026-07-31): body's line-height:1.65 is right for paragraphs
     but was leaking, unmodified, into every one-line form label/hint/legend —
     at --fs-small that adds ~8px of invisible leading ABOVE AND BELOW each
     line, and a form has six or seven of those stacked, which is what reads
     as a wall of blank space. One token, used everywhere short UI text needs
     tight leading, instead of a hardcoded 1.3x/1.35x repeated per component. */
  --lh-tight:   1.3;

  /* ONE vertical rhythm for every block down a form (field, required-note,
     the phone/email group, the description field). 2026-07-31: previously
     four different margin values (0.9 / 0.6 / 0.55rem, plus a negative
     -0.4rem hack) made the gaps between blocks visibly inconsistent. */
  --form-gap:   0.5rem;

  /* Hero background image — the ONE place it's defined. index.php overrides
     this variable inline when the owner uploads a custom hero (PRO →
     Homepage Content Editor); every .hero rule below references it. */
  --hero-img: url("../img/hero.jpg");

  /* Spacing & layout ---------------------------------------------------- */
  --space-section: clamp(3.5rem, 7vw, 6.5rem);
  --container:     1180px;
  --gutter:        clamp(1.1rem, 4vw, 2rem);
  --radius:        14px;
  --radius-sm:     9px;
  --radius-pill:   999px;

  /* Effects ------------------------------------------------------------- */
  /* drop shadows fall to the BOTTOM-RIGHT (owner rule: darker bottom-right) */
  --shadow-sm: 1px 1px 3px rgba(20, 32, 50, 0.08);
  --shadow-md: 4px 8px 26px rgba(20, 32, 50, 0.10);
  --shadow-lg: 8px 18px 50px rgba(20, 32, 50, 0.16);
  --ring:      0 0 0 3px color-mix(in srgb, var(--sm-orange) 35%, transparent);
  /* Heavy resting shadow for row-header panels + tiles site-wide (panels,
     panel-heads, portal tiles/bands, service/project cards) — deeper than
     --shadow-sm so these read as solid, raised surfaces even before hover. */
  --tile-shadow: 3px 6px 18px rgba(20, 32, 50, 0.16), 1px 2px 6px rgba(20, 32, 50, 0.10);
  /* Strong DEBOSSED (pressed-into-the-page) look for every button's hover/
     press — inset shadows only, no outer drop shadow, no lift. One recipe
     shared by every button-like element site-wide (.btn, .btn-mini,
     .panel-toggle) — see §"Deboss" below. */
  /* pressed look (owner rule 2026-07-13): the dark shading sits at BOTH the
     top-left AND the bottom-right, so the press reads as sunk-in all around */
  --btn-deboss:        inset 4px 4px 10px rgba(0,0,0,0.42), inset -4px -4px 10px rgba(0,0,0,0.42);
  --btn-deboss-active: inset 5px 5px 12px rgba(0,0,0,0.55), inset -5px -5px 12px rgba(0,0,0,0.55);

  /* Skin highlight trio for effects/glow — Beach Vibe = orange (flips per skin, §21) */
  --fx:        #FB4D1A;   /* main highlight                       */
  --fx-soft:   #FF8A5B;   /* lighter — glow text on dark surfaces */
  --fx-bright: #FFB48F;   /* brightest — hover                    */
  --fx-ink:    #3A1505;   /* dark text that sits on the highlight */

  --nav-h: 72px;
  --transition: 0.22s ease;

  /* CONTROL SCALE — the ONE place button/pill sizing lives (owner rule
     2026-07-13 after repeated same-size complaints). Every control (.btn,
     .nav-phone, .phone-pill, .gal-btn, …) references THESE tokens — never
     its own font-size/padding/height — so two controls can not drift apart.
     Owner decision 2026-07-13: ONE size across the ENTIRE site (public +
     admin + portal), no exceptions; titles always outrank buttons. */
  --control-h: 34px;         /* fixed height of every pill / round control */
  --control-pad-x: 1.1rem;   /* horizontal padding inside pills            */
  --control-fs: var(--fs-small);
}

/* -------------------------------------------------------------------------
   2. RESET & BASE
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
/* The HTML hidden attribute must ALWAYS win — without this, any element whose
   class sets its own display (e.g. .form-actions { display:flex }, .btn
   { display:inline-flex }) silently overrides the browser's [hidden] rule and
   stays visible even though scripts set .hidden = true (the Projects bulk-bar
   "still says selected after unchecking" bug). */
[hidden] { display: none !important; }

/* scrollbar-gutter: stable reserves the scrollbar's width on every page, so
   navigating between a short page (no scrollbar) and a long one (scrollbar)
   doesn't shift the layout width — kills the sticky-header "shake". */
/* scroll-padding-top keeps in-page anchor targets (#quoteSimple, #services,
   #quick-quote…) from landing UNDER the sticky header — they stop ~1 header
   height down instead. One place, so every anchor on the site is offset. */
html { scroll-behavior: smooth; scroll-padding-top: 6rem; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scrollbar-gutter: stable; overflow-x: clip; }

/* Backstop: the PAGE itself must never scroll sideways, no matter what a
   child (e.g. a wide table) does. Anything that genuinely needs its own
   horizontal scroll (like .table-scroll) still gets one — scoped to that
   element — this only stops it from also widening the whole page. */
body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  color: var(--color-ink);
  background: var(--color-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  max-width: 100vw;
}

h1, h2, h3, h4 { font-family: var(--font-head); line-height: 1.15; font-weight: 700; color: var(--color-navy); }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
ul { list-style: none; padding: 0; }
button { font-family: inherit; cursor: pointer; }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 6px; }

/* -------------------------------------------------------------------------
   3. LAYOUT HELPERS
   ------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--space-section); }
.section--alt { background: var(--color-bg); }
.section--navy { background: var(--color-navy); color: var(--color-white); }
.section--navy h2, .section--navy h3 { color: var(--color-white); }

.section-head { max-width: 680px; margin-inline: auto; text-align: center; margin-bottom: clamp(2rem, 4vw, 3rem); }
/* Homepage section titles — dark orange (--color-heading), 5px larger than the
   h2 ladder step. Shared by .section-head and .why-copy so all read the same. */
.section-head h2, .why-copy h2 { font-size: calc(var(--fs-h2) + 5px); color: var(--color-heading); margin-top: 0.4rem; }
.section-head p { color: var(--color-ink); font-size: var(--fs-lead); margin-top: 0.8rem; }

/* THE eyebrow style. Every section eyebrow on every page uses THIS rule — no
   per-section variants, no inline colors, no !important overrides. Dark orange
   (readable on the sand background) with a warm-orange glow. The ONE exception
   is the dark-surface rule below (hero photo / navy bands), where dark orange
   would vanish, so it flips to the light antique-gold. */
.eyebrow {
  font-family: var(--font-head);
  font-weight: 800;                          /* heaviest loaded weight */
  font-size: var(--fs-small);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-heading);               /* dark orange — readable on sand */
  text-shadow: 0 1px 6px var(--fx-orange);   /* warm orange glow */
}
.hero .eyebrow, .cta-band .eyebrow, .section--navy .eyebrow {
  color: var(--fx); text-shadow: 0 1px 3px rgba(0,0,0,0.6);   /* light gold on dark surfaces */
}

/* -------------------------------------------------------------------------
   4. BUTTONS
   ------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  box-sizing: border-box;
  font-family: var(--font-head);
  font-weight: 600;
  /* size comes ONLY from the control-scale tokens (see :root) — a fixed
     height makes <a> and <button> pills pixel-identical by construction */
  font-size: var(--control-fs);
  height: var(--control-h);
  padding: 0 var(--control-pad-x);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  transition: transform var(--transition), background var(--transition),
              box-shadow var(--transition), color var(--transition);
  white-space: nowrap;
}
.btn:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); }
.btn:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }

.btn--primary { background: var(--color-gold); color: var(--color-navy); border-color: var(--sm-orange); box-shadow: var(--shadow-sm); }
.btn--primary:hover { background: var(--color-gold-dark); border-color: var(--sm-orange-dark); }

.btn--ghost { background: transparent; color: var(--color-white); border-color: rgba(255,255,255,0.55); }
.btn--ghost:hover { background: rgba(255,255,255,0.12); border-color: var(--color-white); }

.btn--dark { background: var(--color-navy); color: var(--color-white); }
.btn--dark:hover { background: var(--color-navy-deep); }

/* same height as every other control — "large" only means wider */
.btn--lg { padding: 0 2rem; }
.btn--block { width: 100%; }

.btn svg { width: 1.15em; height: 1.15em; }

/* (2026-07-13) the control scale is GLOBAL — defined once in :root above;
   no per-surface redefinition, so public and PRO buttons are identical. */
/* Icon size derives from the CONTROL HEIGHT, not the label text (fixed
   2026-07-31, owner: "why is the icon so small"). The old 1.2em rode on
   --control-fs (= --fs-small, 0.74rem), rendering ~14px glyphs inside a
   34px pill — a flaw masked until lead.php's buttons dropped their
   font-size override. calc keeps it proportional if --control-h changes. */
.billing-page .btn .material-symbols-outlined { font-size: calc(var(--control-h) * 0.62); line-height: 1; }

/* -------------------------------------------------------------------------
   5. HEADER / NAV
   ------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255,255,255,0.92);
  -webkit-backdrop-filter: saturate(180%) blur(10px);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: none;
  /* padding-bottom = stripe height (9px) + 5px gap below the pill button */
  padding: 14px 0 14px;
}
/* slim tricolor accent band along the header's bottom edge */
.site-header::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 9px;
  background: var(--accent-stripe);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  /* positioning context for the open dropdown panels, so they anchor to THIS
     centered content column (i.e., under the hamburger) — not to the
     full-width header, which drifts away from the button on wide monitors */
  position: relative;
}
.brand { display: flex; align-items: center; gap: 0.32rem; font-family: var(--font-head); font-weight: 800; font-size: calc(var(--fs-h3) * 1.18); color: var(--color-navy); letter-spacing: -0.01em; transition: color var(--transition); }
.brand .brand-mark {
  display: grid; place-items: center;
  width: 38px; height: 38px;
  background: var(--color-navy); color: var(--color-gold);
  border-radius: 10px; font-size: var(--fs-h3); font-weight: 800;
  transition: transform var(--transition), box-shadow var(--transition);
}
/* "Pierre" + ".com" as one tight unit next to the CA badge → reads "CAPierre.com" */
.brand .brand-word { white-space: nowrap; }
.brand .brand-amp { color: var(--color-gold); }
/* brand is a link home — same press + gold-ring hover language as every pill */
.brand:hover, .brand:focus-visible { color: var(--color-gold-dark); }
.brand:hover .brand-mark, .brand:focus-visible .brand-mark { transform: translateY(1px); }
.brand:focus-visible { outline: none; }

/* PUBLIC nav is ALWAYS collapsed to the hamburger (every window size) — the
   header shows only the brand + "Get a Free Quote"; all nav items + PRO live
   in the dropdown. (The admin .dash-nav still shows its row on desktop, §18.) */
.nav-links { display: none; }
.nav-links.is-open {
  display: flex;
  /* top = row bottom + the header's 14px bottom padding, so the panel sits
     flush under the header edge with NO hover gap (a gap would fire the
     header's mouseleave-close while travelling from the hamburger down) */
  position: absolute; top: calc(100% + 14px); left: 0; right: 0;
  flex-direction: column; align-items: flex-start; gap: 0;
  background: var(--color-white); border-bottom: 1px solid var(--color-line);
  box-shadow: var(--shadow-md); padding: 0.5rem var(--gutter) 1rem; z-index: 90;
}
.nav-links.is-open a { width: 100%; padding: 0.85rem 0.6rem; border-radius: var(--radius-sm); border-bottom: 1px solid var(--color-line); text-align: right; }
/* Above phone size, the open menu is a compact panel whose RIGHT EDGE lines
   up with the hamburger (right: 0 of the centered .nav column), instead of a
   full-screen-wide sheet. */
@media (min-width: 640px) {
  .nav-links.is-open {
    left: auto; right: var(--gutter); width: 300px;
    border: 1px solid var(--color-line);
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  }
}

/* A couple of quick links kept in the bar on WIDER screens (Services, Gallery);
   hidden on narrow, where everything lives in the hamburger. */
.nav-quick { display: none; }
@media (min-width: 900px) {
  /* margin-right:auto glues the quick links to the brand on the LEFT and
     pushes the CTA cluster to the right (auto margins win over the .nav's
     space-between), instead of the links floating mid-header. */
  .nav-quick { display: flex; align-items: center; gap: 0.3rem; margin-right: auto; margin-left: 0.6rem; }
}

/* Shared nav-link style — same active/hover treatment on the public site nav
   (.nav-links), the wide-screen quick links (.nav-quick), and the admin
   dashboard nav (.dash-nav, §18) so every header looks/behaves the same. */
.nav-links a, .nav-quick a, .dash-nav a {
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-body);
  color: var(--color-navy); padding: 0.45rem 0.85rem; border-radius: var(--radius-pill);
  white-space: nowrap; transition: background var(--transition), color var(--transition),
  transform var(--transition), box-shadow var(--transition);
}
/* same gold-tint lift language as .portal-band-head/.dash-nav.is-open a —
   the ONE hover treatment for every nav link, site-wide (no more flat sand). */
@media (hover: hover) {
  .nav-links a:hover, .nav-quick a:hover, .dash-nav a:hover {
    background: color-mix(in srgb, var(--color-gold) 12%, transparent);
    color: var(--color-gold-dark);
    transform: translateY(-1px);
    box-shadow: 0 6px 14px rgba(20,32,50,.10);
  }
}
.nav-links a:active, .nav-quick a:active, .dash-nav a:active { transform: translateY(1px); box-shadow: var(--btn-deboss-active); }
.nav-links a.is-active, .nav-quick a.is-active, .dash-nav a.is-active { background: var(--color-navy); color: var(--color-white); }
.nav-links a.is-active:hover, .nav-quick a.is-active:hover, .dash-nav a.is-active:hover { background: var(--color-navy-deep); }
/* unread-count bubble on the PRO "Inbox" nav item */
.nav-badge { display: inline-block; min-width: 1.15rem; height: 1.15rem; line-height: 1.15rem; padding: 0 0.3rem;
  border-radius: 999px; background: var(--danger); color: #fff; font-size: var(--fs-small); font-weight: 700; text-align: center; vertical-align: middle; }

.nav-cta { display: flex; align-items: center; gap: 1rem; }
/* header phone — a tap-to-call PILL (icon + number). SAME size AND the SAME
   colour scheme as the primary "Get a Free Quote" button: filled gold pill,
   on-gold ink text, matching border. The two pills are visually identical
   (only the content differs). Hidden on phones (sticky call bar carries "Call"). */
.nav-phone {
  display: none; align-items: center; justify-content: center; gap: 0.5rem;
  box-sizing: border-box;
  /* size from the control-scale tokens ONLY — identical to .btn beside it */
  height: var(--control-h); padding: 0 var(--control-pad-x); border-radius: var(--radius-pill);
  background: var(--color-gold); border: 2px solid var(--sm-orange);
  font-family: var(--font-head); font-weight: 600; font-size: var(--control-fs);
  color: var(--color-navy); white-space: nowrap; box-shadow: var(--shadow-sm);
  transition: transform var(--transition), background var(--transition),
              box-shadow var(--transition), color var(--transition);
}
.nav-phone svg { width: 1.15em; height: 1.15em; flex: none; }
.nav-phone:hover { transform: translateY(-2px); background: var(--color-gold-dark); border-color: var(--sm-orange-dark); box-shadow: var(--shadow-md); }
.nav-phone:active { transform: translateY(0); }
@media (min-width: 640px) { .nav-phone { display: inline-flex; } }
/* Below 640px there's only room for one header pill — swap "Get a Free
   Quote" out for the phone pill so the number stays reachable/visible at
   the narrowest widths, instead of just disappearing. */
@media (max-width: 639px) {
  .nav-cta .btn--primary { display: none; }
  .nav-phone { display: inline-flex; }
}
@media (max-width: 600px) {
  .nav-cta { gap: 0.6rem; }
  .nav-cta .btn { padding: 0.55rem 0.9rem; font-size: var(--fs-body); }
}

/* social proof always in sight: "★ 4.6 (47)" chip → scrolls to reviews.
   Hidden on the smallest screens to keep the minimal bar. */
.nav-rating { display: none; align-items: center; gap: 0.3rem; font-family: var(--font-head); font-weight: 700; font-size: var(--fs-body); color: var(--color-navy); white-space: nowrap; }
.nav-rating .nr-star { color: var(--color-star); }
.nav-rating .nr-count { color: var(--color-muted); font-weight: 600; }
@media (min-width: 640px) { .nav-rating { display: inline-flex; } }

/* Owner entrance — discreet "PRO" bubble that lives INSIDE .nav-links (so it
   collapses into the hamburger with the other menu items on narrow screens) →
   dashboard (login-gated). Scoped to beat the shared .nav-links a pill style;
   uses brand tokens so it matches the active skin. */
.nav-links a.btn-pro {
  color: var(--color-gold); background: color-mix(in srgb, var(--color-gold) 12%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--color-gold) 55%, transparent);
  font-weight: 800; font-size: var(--fs-small); letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.4rem 0.7rem;   /* border-radius (pill) inherited from .nav-links a */
}
.nav-links a.btn-pro:hover { background: var(--color-gold); color: var(--color-bg); box-shadow: var(--shadow-sm); }
/* In the open dropdown it drops the bubble and becomes a normal full-width row —
   same size as the other items so it's clearly legible, kept a distinct gold. */
.nav-links.is-open a.btn-pro {
  background: transparent; border: 0; border-bottom: 1px solid var(--color-line);
  font-size: var(--fs-body); letter-spacing: normal; color: var(--color-gold-dark);
}

.nav-toggle {
  display: none; background: none; border: none; padding: 0.4rem; color: var(--color-navy);
  border-radius: var(--radius-sm);
  transition: background var(--transition), color var(--transition), transform var(--transition);
}
.nav-toggle svg { width: 28px; height: 28px; }
/* hamburger → X morph, driven purely by the aria-expanded state main.js
   already toggles on click — no extra JS needed. */
.nav-toggle svg line { transition: transform .25s cubic-bezier(.22,1,.36,1), opacity .2s ease; transform-origin: 12px 12px; }
@media (hover: hover) {
  .nav-toggle:hover { background: color-mix(in srgb, var(--color-gold) 12%, transparent); color: var(--color-gold-dark); }
}
.nav-toggle:active { transform: scale(0.92); }
.nav-toggle[aria-expanded="true"] { background: color-mix(in srgb, var(--color-gold) 16%, transparent); color: var(--color-gold-dark); }
.nav-toggle[aria-expanded="true"] svg line:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] svg line:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] svg line:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
/* PUBLIC hamburger (#navToggle) is shown at ALL sizes; the admin one (#dashToggle)
   only appears ≤1000px (see §16) so the dashboard keeps its desktop row. */
#navToggle { display: inline-flex; }

/* -------------------------------------------------------------------------
   6. HERO
   ------------------------------------------------------------------------- */
.hero {
  position: relative;
  background:
    linear-gradient(135deg, color-mix(in srgb, var(--sm-teal-3) 94%, transparent), color-mix(in srgb, var(--sm-teal-2) 86%, transparent)),
    var(--hero-img) center/cover no-repeat;
  background-color: var(--color-navy-deep);
  color: var(--color-white);
  /* top padding kept tight so the hero content sits close to the header's
     accent stripe; bottom padding stays generous for the section below */
  padding-top: clamp(1.2rem, 3vw, 2rem);
  padding-bottom: clamp(3.5rem, 8vw, 7rem);
}
.hero-grid { display: grid; gap: 2.5rem; align-items: start; }
/* left column: capped at 410px (matches the grid track max) */
.hero-copy { max-width: 410px; }
/* Readability scrim (2026-07-04): ONLY the two loose-text zones over the
   photo get a translucent dark panel with rounded corners matching the
   boxes — the left copy block (eyebrow→stats, wrapped in .hero-scrim) and
   the Get in Touch block (.hero-box--clear, styled further below). The date
   pill, the View-Our-Work button, and the opaque boxes are deliberately NOT
   covered, so the photo stays sharp and clear all around them. */
.hero-scrim { border-radius: clamp(18px, 3vw, 28px); padding: 1rem 1.2rem 1.3rem; margin: 0.2rem 0 1.2rem; }
[data-theme] .hero-scrim {
  background:
    linear-gradient(135deg, rgba(255,255,255,0.06), transparent 45%),
    color-mix(in srgb, var(--color-navy-darker) 58%, transparent);
  -webkit-backdrop-filter: blur(6px) saturate(130%);
  backdrop-filter: blur(6px) saturate(130%);
  /* same gold border + glow as every other box */
  border: 2px solid color-mix(in srgb, var(--fx) 70%, transparent);
  box-shadow: var(--shadow-lg), 0 0 52px color-mix(in srgb, var(--fx) 20%, transparent);
}
/* headline (the alternate-highlight text) reduced 15% from the type-ladder token */
.hero h1 { color: var(--color-white); font-size: calc(var(--fs-hero) * 0.85); letter-spacing: -0.02em; line-height: 1.2; }
.hero h1 .accent { color: var(--color-amber); }
.hero .lead { font-size: var(--fs-lead); color: rgba(255,255,255,0.86); margin-top: 1.1rem; max-width: 36ch; }
/* Box 4 — editable highlight "pill": brown fill, big rounded ends, with a
   dateline, a gold divider, then the owner's text. Content: PRO → Homepage
   Content Editor. Tokens only — brown = --color-navy in the active skin. */
.hero-pill { color: var(--color-white);
  /* glassy, not solid: translucent brown over a blur of the hero photo,
     with a faint top-left sheen */
  background:
    linear-gradient(135deg, rgba(255,255,255,0.08), transparent 45%),
    color-mix(in srgb, var(--color-navy) 62%, transparent);
  -webkit-backdrop-filter: blur(7px) saturate(140%);
  backdrop-filter: blur(7px) saturate(140%);
  border-radius: clamp(18px, 3vw, 28px); padding: clamp(1rem, 2.2vw, 1.4rem) clamp(1.3rem, 3vw, 1.9rem);
  margin: 0 0 1.5rem;
  /* gold border + glow — the model every other hero box now matches */
  box-shadow: var(--shadow-lg), 0 0 52px color-mix(in srgb, var(--fx) 20%, transparent);
  border: 2px solid color-mix(in srgb, var(--fx, var(--color-gold)) 70%, transparent); }
/* email/other icon set before a box title (e.g. Simple Contact Request) */
/* was color: var(--color-bg) — that token is a near-white tint (#F1FAFB),
   set as the ICON'S OWN colour next to a near-white card, so it rendered
   invisible by definition, not by layout accident. --sm-orange is the site's
   one documented icon/accent colour (b_theme.css: "ORANGE = every action &
   accent"), so this now matches every other icon on the page instead of
   inventing a one-off colour. */
.h3-ico { font-size: 1.05em; vertical-align: -0.16em; margin-left: 0.4rem; color: var(--sm-orange); }
/* font-size comes from the shared ".hero-card h3, .hero-box h3, .hero-pill-date"
   rule below — one place, so the date always matches every box title */
.hero-pill-date { display: block; font-family: var(--font-head); font-weight: 700;
  letter-spacing: 0.02em; color: var(--color-white); opacity: 0.92; }
.hero-pill-rule { display: block; height: 2px; width: 100%; margin: 0.55rem 0 0.6rem;
  background: var(--color-gold); border-radius: 2px; }
.hero-pill-text { margin: 0; color: rgba(255,255,255,0.9); font-size: var(--fs-lead); line-height: 1.5; }
/* Quick-action row at the top of the hero (Quote · Call · Chat) — MOBILE ONLY
   (same ≤640px breakpoint as the bottom .callbar; desktop already has the
   header's phone + Get-a-Free-Quote pills, so it would be redundant there).
   Three equal buttons, sized by the shared control-scale tokens like every
   other control. Added 2026-07-16 — purely additive, nothing was removed. */
.hero-quick { display: none; gap: 0.5rem; margin-bottom: 0.9rem; }
.hero-quick .btn { flex: 1; min-width: 0; padding-inline: 0.5rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1.8rem; justify-content: center; }
.hero-trust { display: flex; flex-wrap: wrap; gap: 1.4rem 2rem; margin-top: 2.2rem; padding-top: 1.6rem; border-top: 1px solid rgba(255,255,255,0.16); justify-content: center; text-align: center; }
.hero-trust .ht-item { display: flex; flex-direction: column; align-items: center; }
.hero-trust .ht-num { font-family: var(--font-head); font-weight: 800; font-size: var(--fs-h2); color: var(--color-gold); line-height: 1; }
.hero-trust .ht-label { font-size: var(--fs-small); color: rgba(255,255,255,0.78); margin-top: 0.3rem; }
/* referrers & partner logos strip (editable HTML embed; hidden when blank) */
.hero-partners { display: flex; flex-wrap: wrap; align-items: center; gap: 0.9rem 1.4rem;
  margin-top: 1.6rem; padding-top: 1.2rem; border-top: 1px solid rgba(255,255,255,0.16); }
.hero-partners img { max-height: 36px; width: auto; display: block; }
.hero-partners a { display: inline-flex; align-items: center; }

/* Hero quote card */
.hero-card {
  background: var(--color-surface);
  color: var(--color-ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: clamp(1.5rem, 3vw, 2.1rem);
}
/* one size rule: box titles AND the date pill's dateline stay identical */
.hero-card h3, .hero-box h3, .hero-pill-date { font-size: calc(var(--fs-h3) * 1.14); }
.hero-card p.sub, .hero-box p.sub { color: var(--color-muted); font-size: var(--fs-small); margin-top: 0.25rem; margin-bottom: 1.2rem; }

/* Six-box quote stack: each region of the old single card is its own box
   (1 start options · 2 live chat · 3 call now · 4 form · 5 bigger job ·
   6 testimonials), all always visible — no chooser show/hide. */
.hero-card-stack { display: flex; flex-direction: column; gap: 0.9rem; }
.hero-box { background: var(--color-surface); color: var(--color-ink);
  border-radius: var(--radius); box-shadow: var(--shadow-md); padding: 1.1rem 1.3rem; }
.hero-box .qc-option { display: block; width: 100%; margin-top: 0.7rem; }
.hero-box .qc-option:first-child { margin-top: 0; }
.hero-box .expert-cta { margin: 0; }
.hero-box .hero-reviews { margin-top: 0; border-top: 0; }
/* Get in Touch rows: wide text left, narrow icon pill right. */
.gt-row { display: flex; align-items: center; gap: 0.9rem; padding: 0.8rem 0;
  border-top: 1px solid var(--color-line); }
.gt-row:first-of-type { border-top: 0; padding-top: 0.2rem; }
.gt-row:last-of-type { padding-bottom: 0; }
.gt-text { flex: 1; min-width: 0; }
.gt-text strong { display: block; font-family: var(--font-head); font-weight: 700; color: var(--color-navy); }
.gt-text span { display: block; font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.15rem; }
.gt-pill { flex: none; padding: 0.55rem 1.1rem; font-size: var(--fs-body); gap: 0.4rem; }
.gt-pill .material-symbols-outlined { font-size: 1.25em; line-height: 1; }

/* -------------------------------------------------------------------------
   7. FORMS
   ------------------------------------------------------------------------- */
/* ONE rhythm value for every block in a form — field, required-note,
   q-reach group, field-instruction all use it as their ONLY margin-bottom.
   FIXED 2026-07-31 (owner: "no coherence from one to the next") — these had
   drifted to four different values (0.9 / 0.6 / 0.55 / a NEGATIVE -0.4rem
   hack) so no two gaps down the form matched. --form-gap (defined in :root,
   token section) is now the ONLY value any of them use. */
.field { margin-bottom: var(--form-gap); }
/* line-height:var(--lh-tight) added 2026-07-31 — every label on the SITE
   was inheriting body's line-height:1.65, turning a one-line label into a
   line box ~1.65x its font size. Site-wide fix, not form-only. */
.field label { display: block; font-size: var(--fs-small); font-weight: 600; line-height: var(--lh-tight); margin-bottom: 0.35rem; color: var(--color-navy); }
/* WCAG AAA 3.3.5 (Help) 2026-07-30: input fields increased to 44px minimum
   touch target (0.75rem → 0.85rem padding). AAA 2.5.5 requires 44×24px for
   target size; this puts inputs at 44px height exactly. */
/* FLOATING LABELS (2026-07-31) — the label lives INSIDE the input at rest
   (visual density, no separate line), then shrinks and moves up onto the
   input's top border the instant it is focused or has a value. UNLIKE
   placeholder-only fields (rejected earlier tonight, WCAG 3.3.2 — a
   placeholder disappears the moment someone types, so the field's name is
   gone right when they need it most): this is a REAL <label>, always present
   in the accessibility tree, only ever repositioned by CSS, never removed.
   DOM order is input-then-label on purpose — that is what lets a plain CSS
   sibling selector react to the input's state with zero JavaScript. The
   for="id" association (and therefore the accessible name) is completely
   unaffected by DOM order, so this reads identically to a normal stacked
   label for a screen reader; only the SIGHTED layout is different. Applied
   selectively — see inc/quote_simple_form.php for which fields use it. */
.field--float { position: relative; }
.field--float input { padding-top: 1.15rem; padding-bottom: 0.4rem; }
.field--float label {
  position: absolute; left: 0.95rem; top: 50%; transform: translateY(-50%) scale(1);
  transform-origin: left center; margin: 0; padding: 0 0.25rem;
  max-width: calc(100% - 2.2rem); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  pointer-events: none; background: transparent;
  transition: transform var(--transition), top var(--transition), background var(--transition);
}
.field--float input:focus + label,
.field--float input:not(:placeholder-shown) + label {
  top: 0; transform: translateY(-50%) scale(0.78); background: var(--color-white);
}
/* the real placeholder (e.g. ZIP's "e.g. Malibu or 90265") stays invisible
   while the floating label sits over it at rest, and only appears once the
   label has moved out of the way on focus — so the two never overlap */
.field--float input:not(:focus)::placeholder { color: transparent; }
.field--float input:focus::placeholder { color: var(--color-muted); }

.field input, .field select, .field textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--fs-lead);
  padding: 0.85rem 0.9rem;
  border: 1.5px solid var(--color-line);
  border-radius: var(--radius-sm);
  background: var(--color-white);
  color: var(--color-ink);
  transition: border-color var(--transition), box-shadow var(--transition);
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--color-amber);
  box-shadow: var(--ring);
  outline: none;
}
.field textarea { resize: vertical; min-height: 96px; }
/* WCAG AAA 3.3.2 Labels + 3.3.5 Help (2026-07-30): required field indicator
   and explicit instruction text for complex fields. */
/* The required note is a footnote, not a section break — the rule under it
   was reading as a divider and adding another band of empty space. */
.form-required-note { font-size: var(--fs-small); line-height: var(--lh-tight); color: var(--color-muted); margin: 0 0 var(--form-gap); }

/* "How should we reach you?" — phone + email are ONE decision, so they sit in
   one bordered group with a single line of copy, not two stacked headings
   above two full-width fields. fieldset needs its browser defaults cleared. */
.q-reach { border: 1.5px solid var(--color-line); border-radius: var(--radius-sm);
  padding: 0.6rem 0.85rem; margin: 0 0 var(--form-gap); min-inline-size: 0; }
.q-reach legend { padding: 0 0.35rem; font-size: var(--fs-small); line-height: var(--lh-tight); font-weight: 600; color: var(--color-navy); }
/* the two fields inside the group are the LAST thing before the fieldset's
   own bottom padding closes it — no margin of their own, or the group would
   carry MORE trailing space than every other block uses. */
.q-reach .field { margin-bottom: 0; }
.required-marker { color: var(--sm-orange); font-weight: 700; }
.field-instruction { font-size: var(--fs-small); line-height: var(--lh-tight); color: var(--color-muted); margin: 0.2rem 0 var(--form-gap); }
.form-note { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.6rem; text-align: center; }
.form-status { display: none; margin-top: 0.8rem; padding: 0.7rem 1rem; border-radius: var(--radius-sm); font-size: var(--fs-small); }
.form-status.is-ok { display: block; background: var(--ok-bg); color: var(--ok-fg); }
.form-status.is-err { display: block; background: var(--err-bg); color: var(--err-fg); }

/* Styled list autocomplete panel (js/list-autocomplete.js, 2026-07-31) —
   real site styling instead of the browser's native <datalist> box, and
   groupable (the City/ZIP fields split into LA County/OC County; admin
   fields sourced from an existing <datalist> render as one unheaded
   group). Generalized from a city-only component the same day (owner:
   "DO THESE TOO") to also cover 4 admin-only fields with the identical
   native-datalist problem. Anchoring container gets position:relative from
   JS (not assumed here — .field is reused on far too many unrelated pages
   to safely blanket it in CSS). */
.ac-panel {
  position: absolute; left: 0; right: 0; top: 100%; margin-top: 0.3rem; z-index: 20;
  max-height: 280px; overflow-y: auto;
  background: var(--color-white); border: 1.5px solid var(--color-line);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
  padding: 0.35rem 0;
}
.ac-group {
  padding: 0.3rem 0.9rem 0.15rem; font-size: var(--fs-small); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-muted);
}
.ac-option {
  min-height: 44px; box-sizing: border-box; display: flex; align-items: center;
  padding: 0.3rem 0.9rem; font-size: var(--fs-lead); color: var(--color-ink);
  cursor: pointer; line-height: var(--lh-tight);
}
.ac-option strong { color: var(--sm-orange); font-weight: 700; }
.ac-option:hover, .ac-option.is-active { background: var(--color-bg-alt); }
.ac-empty { padding: 0.6rem 0.9rem; font-size: var(--fs-small); color: var(--color-muted); }

/* -------------------------------------------------------------------------
   8. TRUST BAR / BADGES
   ------------------------------------------------------------------------- */
.trustbar { background: var(--color-surface); border-bottom: 1px solid var(--color-line); }
.trustbar-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem 2.4rem; padding-block: 1.3rem; text-align: center; }
.trust-item { display: inline-flex; align-items: center; gap: 0.55rem; font-weight: 600; color: var(--color-navy); font-size: var(--fs-lead); }
.trust-item svg { width: 1.4em; height: 1.4em; color: var(--klein); flex: none; }
/* Google Material Symbols icon (editable business references) — same size/
   color slot the old inline SVGs used */
.trust-item .trust-ico { font-size: 1.4em; line-height: 1; color: var(--klein); flex: none; }

/* -------------------------------------------------------------------------
   9. SERVICES
   ------------------------------------------------------------------------- */
.cards { display: grid; gap: 1.4rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
/* HOMEPAGE SERVICE CARDS ONLY — capped at 3 per row (2026-08-01, owner:
   "ON THE HOMEPAGE: 3 SERVICE CARDS PER ROW. NOTHING ELSE."). Scoped to
   #services so the same .cards grid keeps its auto-fit behaviour on
   Scheduled Services, whose row length is a different design problem.
   Designed steps 1 → 2 → 3, never auto-fit: auto-fit was fitting FOUR
   across on wide screens, leaving the six services as a lopsided 4+2. */
#services .cards { grid-template-columns: 1fr; }
@media (min-width: 620px)  { #services .cards { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { #services .cards { grid-template-columns: 1fr 1fr 1fr; } }
.card {
  background: var(--color-surface);
  border: 1.5px solid var(--color-line);
  border-radius: var(--radius);
  padding: 1.7rem;
  box-shadow: var(--tile-shadow);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.card:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); border-color: var(--tint-line); }
.card:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }
/* service cards double as links to their own dedicated page (index.php) —
   .card stays a plain container elsewhere (e.g. the admin preview), so this
   is a separate modifier rather than baking link resets into .card itself. */
.card--link { display: block; text-decoration: none; color: inherit; }
.card .icon {
  display: grid; place-items: center;
  width: 54px; height: 54px;
  border-radius: 12px;
  /* lighter gold top-left → darker orange bottom-right (owner shading rule) */
  background: linear-gradient(135deg, var(--sm-gold), var(--sm-orange));
  color: var(--color-white);
  margin-bottom: 1rem;
}
.card .icon svg { width: 28px; height: 28px; }
.card .icon .material-symbols-outlined { font-size: 28px; line-height: 1; }
.card h3 { font-size: var(--fs-h3); margin-bottom: 0.5rem; }
.card p { color: var(--color-muted); font-size: var(--fs-lead); }
.card ul { margin-top: 0.9rem; display: grid; gap: 0.4rem; }
.card ul li { position: relative; padding-left: 1.4rem; font-size: var(--fs-body); color: var(--color-ink); }
/* Bullets wear the icon-chip scheme (2026-08-01, owner: "uniformize
   buttons and bullets to be same as icons colors and effects scheme") —
   same 155° gradient and same ring+glow as every chip, just at dot scale.
   Sized up 7px → 9px so the gradient and ring are actually perceptible.
   Non-text decoration, so no contrast floor applies to the dot itself. */
.card ul li::before {
  content: ""; position: absolute; left: 0; top: 0.5em;
  width: 9px; height: 9px; border-radius: 50%;
  background: linear-gradient(155deg, var(--fx-bright), var(--fx) 55%, var(--color-gold-dark));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fx) 70%, transparent),
              0 2px 6px color-mix(in srgb, var(--fx) 45%, transparent);
}

/* -------------------------------------------------------------------------
   10. WHY US
   ------------------------------------------------------------------------- */
.why-grid { display: grid; gap: 2.4rem; align-items: center; grid-template-columns: 1fr; }
.why-copy h2 { margin-top: 0.4rem; }   /* size + color: shared .section-head h2, .why-copy h2 rule */
.why-copy .why-intro { color: var(--color-ink); margin-top: 0.8rem; }
.why-list { display: grid; gap: 1.3rem; margin-top: 1.6rem; }
.why-item { display: flex; gap: 1rem; }
.why-item .why-ico {
  flex: none; display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 12px;
  background: color-mix(in srgb, var(--sm-teal-br) 16%, transparent); color: var(--color-teal-dark);
}
.why-item .why-ico svg { width: 24px; height: 24px; }
.why-item .why-ico .material-symbols-outlined { font-size: 24px; line-height: 1; }
.why-item h3 { font-size: var(--fs-h3); margin-bottom: 0.2rem; }
.why-item p { color: var(--color-muted); font-size: var(--fs-lead); }
/* SQUARE photo (spec: uploads must be exactly square, ≥525×525 px). The grid
   columns are equal, so the photo renders at the SAME width as the text
   column beside it — within the owner's "no more than 25px narrower" rule
   by construction. The editable legend overlays the bottom edge. */
.why-media { position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); min-height: 320px; background: var(--color-bg-alt) center/cover no-repeat; }
.why-legend {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 2.2rem 1.1rem 0.9rem;
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-lead);
  color: var(--color-white);
  background: linear-gradient(transparent, rgba(0,0,0,0.62));
}

/* -------------------------------------------------------------------------
   11. REVIEWS
   ------------------------------------------------------------------------- */
.reviews-grid { display: grid; gap: 1.4rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.review {
  background: var(--color-surface);
  border: 1px solid var(--color-line);
  border-radius: var(--radius);
  padding: 1.6rem;
  box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column;
}
.stars { display: inline-flex; gap: 2px; color: var(--color-star); margin-bottom: 0.7rem; }
.stars svg { width: 18px; height: 18px; }
.review p.quote { font-size: var(--fs-lead); color: var(--color-ink); flex: 1; }
.review .reviewer { display: flex; align-items: center; gap: 0.7rem; margin-top: 1.1rem; padding-top: 1.1rem; border-top: 1px solid var(--color-line); }
.review .avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--color-navy); color: var(--color-amber); display: grid; place-items: center; font-family: var(--font-head); font-weight: 700; }
.review .who strong { display: block; font-size: var(--fs-lead); color: var(--color-navy); }
.review .who span { font-size: var(--fs-small); color: var(--color-muted); }

.rating-summary { display: flex; align-items: center; justify-content: center; gap: 0.8rem; margin-top: 1.2rem; flex-wrap: wrap; }
.rating-summary .big { font-family: var(--font-head); font-weight: 800; font-size: var(--fs-h2); color: var(--color-navy); }
.rating-summary .stars svg { width: 22px; height: 22px; }

/* -------------------------------------------------------------------------
   12. SERVICE AREA / CTA BAND
   ------------------------------------------------------------------------- */
.cta-band { background: linear-gradient(135deg, var(--klein), var(--klein-deep)); color: var(--color-white); text-align: center; }
.cta-band h2 { color: var(--color-white); font-size: var(--fs-h2); }
.cta-band p { color: rgba(255,255,255,0.85); font-size: var(--fs-lead); max-width: 56ch; margin: 0.9rem auto 1.8rem; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; }

.area-tags { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center; margin-top: 1.4rem; }
.area-tags span, .area-tags a { background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.18); padding: 0.4rem 0.95rem; border-radius: var(--radius-pill); font-size: var(--fs-small); }
.area-tags a { color: inherit; text-decoration: none; cursor: pointer; transition: background 0.15s; }
.area-tags a:hover, .area-tags a:focus-visible { background: rgba(255,255,255,0.2); }
/* base (non-b-page) county heading — b_theme.css overrides colors/spacing
   for the light bichrome section; this is the fallback if that never loads. */
.area-county-head { text-align: center; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; font-size: var(--fs-small); margin: 1.2rem 0 0.5rem; }
/* County folds (2026-08-01, owner: "collapse each county, too long now") —
   the heading doubles as a native <details> summary: same text styling,
   plus cursor/marker handling, a muted live count and a chevron that flips
   on [open]. Both collapsed by default; 127 pills → two one-line bands. */
.area-fold { margin-top: 0.6rem; }
.area-fold > summary { cursor: pointer; list-style: none; display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
.area-fold > summary::-webkit-details-marker { display: none; }
.area-fold > summary .afc { font-weight: 600; text-transform: none; letter-spacing: normal; color: var(--color-muted); }
.area-fold > summary .af-chev { transition: transform var(--transition); }
.area-fold[open] > summary .af-chev { transform: rotate(180deg); }

/* -------------------------------------------------------------------------
   13. CONTACT
   ------------------------------------------------------------------------- */
/* "Contact Tiles" — text on the left, icon on the right, each on its own
   ivory card (reused as-is in chatwindow.php — keep both in sync). */
.contact-tiles { display: grid; grid-template-columns: 1fr; gap: 1rem; max-width: 640px; margin-inline: auto; }
.contact-tile {
  /* Icon FIRST (2026-08-01, owner: "move icons to the left of the text like
     any other tile") — every other tile kind on the site (portal tiles,
     benefit rows, panel idents) leads with its chip. The chip is last in
     the MARKUP, so `order` moves it visually while the DOM keeps
     heading-then-detail reading order for screen readers; `justify-content`
     drops to flex-start since the chip no longer needs pushing to the far
     edge. */
  display: flex; align-items: center; gap: 0.9rem;
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-gold) 35%, var(--tint-line));
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  box-shadow: var(--shadow-sm);
  text-decoration: none; color: inherit;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.contact-tile .ci-ico { order: -1; }        /* chip leads — see note above */
.contact-tile > div:not(.ci-ico) { min-width: 0; }
/* the 4 actionable tiles are <a> elements — the whole card is clickable,
   including the icon; the 2 info-only tiles (.contact-tile--static) stay
   plain <div>s and skip the hover lift so they don't look clickable. */
a.contact-tile:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--color-gold);
}
a.contact-tile:hover h3 { color: var(--color-amber-dark); }
.contact-tile .ci-ico { flex: none; display: grid; place-items: center; width: 46px; height: 46px; border-radius: 12px; background: var(--color-navy); color: var(--color-amber); overflow: hidden; }
.contact-tile .ci-ico svg { width: 22px; height: 22px; }
.contact-tile .ci-ico .material-symbols-outlined { font-size: 22px; line-height: 1; }
.contact-tile h3 { font-size: var(--fs-h3); margin-bottom: 0.2rem; color: var(--color-ink); transition: color var(--transition); }
.contact-tile p { color: var(--color-muted); font-size: var(--fs-lead); }
.contact-form-wrap { background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius); padding: clamp(1.5rem, 3vw, 2.2rem); box-shadow: var(--shadow-md); }

/* -------------------------------------------------------------------------
   14. FOOTER
   ------------------------------------------------------------------------- */
/* Footer: slim two-line accent — thin orange + thin Klein blue — above the teal body.
   Band orange is its own token so it can be tuned without touching site accents. */
:root {
  --band-orange: var(--sm-orange);
  /* slim tricolor accent — orange · yellow · Klein blue (reused by header + footer) */
  --accent-stripe: linear-gradient(to bottom,
    var(--band-orange) 0 3px,
    var(--sm-gold)     3px 6px,
    var(--klein)       6px 9px);
}
.site-footer {
  position: relative;
  background: var(--color-navy-darker);
  color: rgba(255,255,255,0.78);
  padding-block: 3rem 1.6rem;
}
.site-footer::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 9px;
  background: var(--accent-stripe);
}
/* Repeated at the bottom edge (2026-08-01, owner: "double color lines
   repeated towards the bottom") — bookends the page with the same tricolor
   stripe the header/footer top already carries, one token, same gradient,
   just the opposite edge. */
.site-footer::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 9px;
  background: var(--accent-stripe);
}
.footer-grid { display: grid; gap: 2rem; grid-template-columns: 1fr; }
.site-footer .brand { color: var(--color-white); margin-bottom: 0.8rem; }
/* .footer-h = the footer column titles, now h2 elements for a correct
   heading outline (were h4, changed 2026-07-29) — this rule pins them to the
   exact size/weight the h4s had, so the markup change is invisible. The h4
   selector is kept so any older footer copy still renders correctly. */
.site-footer h4, .site-footer .footer-h { color: var(--color-white); font-family: var(--font-head); font-weight: 700; font-size: var(--fs-lead); margin-bottom: 0.9rem; }
.site-footer a { color: rgba(255,255,255,0.78); transition: color var(--transition); }
.site-footer a:hover { color: var(--color-amber); }
.footer-links { display: grid; gap: 0.5rem; }
.footer-contact li { display: flex; gap: 0.55rem; align-items: flex-start; margin-bottom: 0.5rem; font-size: var(--fs-lead); }
/* li > svg, NOT a bare descendant (2026-08-01, third strike on this bug):
   the loose selector also reached the phone glyph INSIDE .phone-pill,
   painting it accent-on-orange. The standalone mail/pin/help icons are
   direct <li> children; the pill's glyph never is. Same scoping as the
   [data-theme] copy below and b_theme.css's — all three must agree. */
.footer-contact li > svg { width: 1.05em; height: 1.05em; color: var(--color-amber); flex: none; margin-top: 0.25rem; }
.footer-bottom {
  position: relative; margin-top: 2.2rem; padding-top: 1.4rem;
  display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; justify-content: space-between; font-size: var(--fs-small);
}
/* The tricolor divider that briefly sat here (between the footer columns
   and the copyright line) was REMOVED 2026-08-01 — owner: "remove the
   middle set of double colored lines in the footer". The footer keeps the
   stripe at its top edge (.site-footer::before) and bottom edge
   (.site-footer::after); a third one mid-block was one too many.
   .footer-bottom's hairline top border (below) is the divider now. */
.footer-bottom { border-top: 1px solid var(--color-line); }
.footer-bottom .license { color: rgba(255,255,255,0.6); }

/* -------------------------------------------------------------------------
   15. FLOATING MOBILE CALL BAR
   ------------------------------------------------------------------------- */
.callbar {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  background: var(--color-navy); border-top: 1px solid rgba(255,255,255,0.12);
  padding: 0.6rem var(--gutter);
  gap: 0.7rem;
}
.callbar .btn { flex: 1; }

/* -------------------------------------------------------------------------
   16. RESPONSIVE
   ------------------------------------------------------------------------- */
@media (min-width: 768px) {
  .why-grid { grid-template-columns: 1fr 1fr; }
  .contact-tiles { grid-template-columns: 1fr 1fr; }
  .footer-grid { grid-template-columns: 1.6fr 1fr 1.4fr; }
}
/* 3-per-row tier (2026-08-01, owner: "need 3 per row") — the 6 contact
   tiles land as two even rows of three. The 640px cap that suited a
   2-column stack would squeeze 3 columns to ~195px each, so it widens with
   the column count. Designed breakpoint, not auto-fit wrap. */
@media (min-width: 1000px) {
  .contact-tiles { grid-template-columns: 1fr 1fr 1fr; max-width: 980px; }
}
/* Hero columns (owner rule 2026-07-04): LEFT is never under 250px, RIGHT is
   never under 270px. Two columns from the first width where both floors fit
   (~600px); phones below that stack. Tracks grow toward 400/520 and the pair
   stays centered. */
@media (min-width: 600px) {
  /* left +10px (400→410), right -10px (520→510) to even the two columns */
  .hero-grid { grid-template-columns: minmax(250px, 410px) minmax(270px, 510px);
    justify-content: center; gap: clamp(1.2rem, 3vw, 5rem); }
}
@media (max-width: 599px) {
  /* Single-column hero (phones): the left copy must FILL the column like every
     other stacked section. The 410px desktop cap (see .hero-copy above) is a
     line-length limit for the two-column layout only — left here it pins the
     column to the start of a wider container, leaving lopsided space on the
     right (off-centre). max-width:none lets it sit full-width & symmetric. */
  .hero-copy { max-width: none; }
}

/* Admin dashboard nav (.dash-nav) is ALWAYS behind its hamburger (#dashToggle) —
   never a horizontal pill row, at any width (owner preference, ported from Ka Locks Co). */
#dashToggle { display: inline-flex; }

@media (max-width: 640px) {
  .callbar { display: flex; }
  .hero-quick { display: flex; }   /* top-of-hero Quote · Call · Chat row */
  /* room for the floating call bar — ONLY on pages that actually have one
     (otherwise it's a 70px empty band under the footer) */
  body:has(.callbar) { padding-bottom: 70px; }
  .hero-trust { gap: 1rem 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

/* =========================================================================
   17. BILLING TOOL (billing.html, invoice.php)
   Reuses the tokens/components above; no new color values.
   ========================================================================= */
.billing-page { background: var(--color-bg); display: flex; flex-direction: column; min-height: 100vh; }
.billing-page > .site-footer { margin-top: auto; }   /* pin footer to the bottom on short pages */
.bill-wrap { padding-block: 1.6rem 3rem; }
.bill-head h1 { font-size: var(--fs-hero); }
.bill-head .muted { color: var(--color-muted); margin-top: 0.25rem; }

/* nav-back bakes in the "display:inline-block;margin-bottom:…" every admin
   page used to repeat inline — margin-bottom does nothing on an inline <a>
   without it. One definition; no more per-page duplicates. */
.nav-back { font-family: var(--font-head); font-weight: 600; color: var(--color-navy); margin-right: 0.4rem; display: inline-block; margin-bottom: 0.4rem; }
.nav-back:hover { color: var(--color-amber-dark); }

/* Extra button variant: outlined on light */
.btn--ghost-dark { background: transparent; color: var(--color-navy); border-color: var(--sm-orange); }
.btn--ghost-dark:hover { background: color-mix(in srgb, var(--color-gold) 10%, transparent); border-color: var(--color-navy); }
.btn[disabled] { opacity: 0.6; cursor: default; transform: none; }

/* Panels */
.panel {
  background: var(--color-surface);
  border: 1.5px solid var(--color-line);
  border-radius: var(--radius);
  box-shadow: var(--tile-shadow);
  margin-bottom: 0.45rem;
  overflow: hidden;
}
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.4rem 0.75rem;
  border-bottom: 1.5px solid var(--color-line);
}
.panel-head h2 { font-size: var(--fs-body); font-weight: 700; }
.panel-body { padding: 0.6rem 0.75rem; }
.panel.is-collapsed .panel-body { display: none; }
.catalog-box.is-collapsed { display: none; }
.panel-toggle {
  background: var(--color-bg-alt); border: 1px solid var(--color-line);
  color: var(--color-navy); font-family: var(--font-head); font-weight: 600;
  font-size: var(--fs-small); padding: 0.4rem 0.9rem; border-radius: var(--radius-pill);
  transition: transform var(--transition), box-shadow var(--transition), background var(--transition);
}
.panel-toggle:hover { background: color-mix(in srgb, var(--color-gold) 14%, var(--color-bg-alt)); transform: translateY(1px); box-shadow: var(--btn-deboss); }
.panel-toggle:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }

.panel .sub { font-size: var(--fs-lead); margin: 1.2rem 0 0.6rem; color: var(--color-navy); }
.panel .sub:first-child { margin-top: 0; }
/* .hint is used site-wide (every admin panel, every form) — tightening its
   line-height here is a real site-wide fix, not a one-page patch. */
.hint { font-size: var(--fs-small); line-height: var(--lh-tight); color: var(--color-muted); margin-bottom: 0.8rem; }
.hint-inline { font-weight: 400; color: var(--color-muted); font-size: var(--fs-small); line-height: var(--lh-tight); }
.inline-status { font-size: var(--fs-small); margin-left: 0.7rem; color: var(--color-teal-dark); }

/* Read-only key/value detail grid — ONE shared definition used by the lead
   detail page, the leads-list expandable row, and the client contact view, so
   all three read the same. Label small/uppercase/muted, value at body weight. */
.lead-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.5rem 1.4rem; }
.lead-grid .lg-k { color: var(--color-muted); font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.03em; }
.lead-grid .lg-v { color: var(--color-ink); font-weight: 600; margin-top: 0.1rem; }
.lead-msg { margin: 0.4rem 0; padding: 0.8rem 1rem; background: var(--color-bg-alt); border-radius: var(--radius-sm); font-size: var(--fs-lead); white-space: pre-wrap; }

/* Homepage Content Editor tab strip (inc/homepage_editor_tabs.php) — ONE shared
   definition so every editor page (Hero / Services / Why Us / Areas / Contact)
   renders the same pills. Was duplicated in each page's <style> and missing on
   the Why Us page entirely, so its nav rendered as plain text. */
.hce-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1rem; }
.hce-tab { font-size: var(--fs-body); font-weight: 600; color: var(--color-navy); background: var(--color-bg-alt);
  padding: 0.4rem 0.9rem; border-radius: var(--radius-pill); }
/* text-on-gold: same pairing as .btn--primary/.nav-phone — var(--color-bg), never
   var(--color-navy), since the oil-rubbed-bronze gold and navy tokens are both
   dark browns and go near-invisible together */
.hce-tab.is-active { background: var(--color-gold); color: var(--color-bg); }

/* Field grids */
.grid-2 { display: grid; gap: 0.4rem 1rem; grid-template-columns: 1fr; }
.grid-4 { display: grid; gap: 0.4rem 1rem; grid-template-columns: 1fr 1fr; }
@media (min-width: 620px) {
  .grid-2 { grid-template-columns: 1fr 1fr; }
  .grid-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
}

/* Line-item rows (labour + materials) */
.li-row {
  display: grid; gap: 0.5rem; align-items: center; margin-bottom: 0.55rem;
}
.li-row--labour { grid-template-columns: 1.6fr 0.9fr 0.6fr 0.7fr 0.8fr auto; }
.li-row--mat    { grid-template-columns: 1fr 1.4fr 0.6fr 0.8fr 0.8fr auto; }
.li-row > * { min-width: 0; }   /* let grid columns shrink instead of overflowing the panel */
.li-row input, .li-row select { padding: 0.6rem 0.6rem; width: 100%; box-sizing: border-box; }
.li-head {
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-muted);
  margin-bottom: 0.5rem;
}
.li-amt { font-family: var(--font-mono, monospace); font-weight: 600; text-align: right; color: var(--color-navy); }
.row-rm {
  background: transparent; border: none; color: var(--danger); font-size: var(--fs-lead);
  width: 32px; height: 32px; border-radius: 8px;
}
.row-rm:hover { background: var(--danger-bg); }
.btn-add { margin-top: 0.3rem; font-size: var(--fs-body); padding: 0.6rem 1.1rem; }
.task-row { display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; margin-bottom: 0.5rem; }

/* Catalog */
.catalog-box { background: var(--color-bg); border: 1px dashed var(--color-line); border-radius: var(--radius-sm); padding: 1.1rem; margin-bottom: 1.3rem; }
.catalog-list { margin-top: 1rem; display: grid; gap: 0.8rem; }
.cat-group { }
.cat-name { font-family: var(--font-head); font-weight: 700; color: var(--color-navy); font-size: var(--fs-body); margin-bottom: 0.35rem; }
.cat-item {
  display: flex; justify-content: space-between; gap: 1rem; width: 100%; text-align: left;
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: 8px;
  padding: 0.5rem 0.8rem; font-size: var(--fs-body); color: var(--color-ink); margin-bottom: 0.3rem;
}
.cat-item:hover { border-color: var(--color-amber); background: color-mix(in srgb, var(--sm-orange) 6%, var(--color-white)); }
.cat-item em { color: var(--color-muted); font-style: normal; font-size: var(--fs-small); }

/* Totals — default styling suits a normal (light) panel, like every other
   block on the page (admin/project.php's Project Total). */
.tot-row { display: flex; justify-content: space-between; padding: 0.3rem 0; font-size: var(--fs-body); }
.tot-row span:first-child { color: var(--color-muted); }
.tot-grand { border-top: 2px solid var(--color-line); margin-top: 0.3rem; padding-top: 0.5rem; font-family: var(--font-head); font-weight: 800; font-size: var(--fs-body); }
.tot-grand span:last-child { color: var(--color-gold); }

/* Actions + result */
.action-bar { position: sticky; bottom: 0; z-index: 20; padding: 0.9rem 0; background: linear-gradient(to top, var(--color-bg) 70%, transparent); }
.action-bar .btn { width: 100%; }
.result-box { border: 2px solid var(--color-teal); }
.result-title { font-size: var(--fs-h3); color: var(--color-navy); }
.result-email { font-size: var(--fs-small); color: var(--color-muted); margin: 0.4rem 0 1rem; }
.result-actions { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.result-actions .btn { flex: 1 1 auto; }

/* Compact footer variant (gallery/admin/portal pages): only the copyright row,
   so drop the homepage footer's tall top padding + the divider spacing that
   assume grid content sits above — kills the empty dark band. */
.bill-footer { margin-top: 2rem; padding-block: 1.6rem; }
.bill-footer .footer-bottom { margin-top: 0; padding-top: 0.4rem; border-top: 0; }

/* Off-screen invoice render host (used for print + PDF capture) */
.invoice-print-host { position: absolute; left: -10000px; top: 0; width: 720px; background: var(--color-white); }

@media (min-width: 880px) {
  .action-bar .btn { width: auto; min-width: 280px; }
}

/* Stack line items on small screens with field labels */
@media (max-width: 700px) {
  .li-head { display: none; }
  /* 2 rows total instead of 4: the "main" field (Description / Item) takes
     its own full-width row, the remaining short fields (Type, Qty, Rate,
     Amount / Category, Qty, Unit cost, Amount) share ONE row of 4 equal
     columns — owner rule 2026-07-21: never spread short fields across
     multiple lines just because the row is narrow. */
  .li-row--labour, .li-row--mat {
    grid-template-columns: repeat(4, 1fr);
    border: 1px solid var(--color-line); border-radius: var(--radius-sm);
    padding: 0.6rem 0.65rem; gap: 0.35rem; position: relative;
  }
  .li-row--labour > *:first-child, .li-row--mat > *:first-child { grid-column: 1 / -1; }
  .li-row--labour input, .li-row--labour select,
  .li-row--mat input, .li-row--mat select { font-size: var(--fs-small); padding: 0.3rem 0.4rem; }
  .li-amt { text-align: right; align-self: center; font-weight: 700; color: var(--color-navy); font-size: var(--fs-small); }
  /* scoped to these two row types on purpose — a bare ".row-rm" here would
     absolutely-position EVERY remove button on the page (location/contact/
     review cards too), which have no positioned ancestor to anchor to and
     fly off to the top of the page instead of sitting in their card. */
  .li-row--labour .row-rm, .li-row--mat .row-rm { position: absolute; top: 0.35rem; right: 0.35rem; }
}

/* PRINT: show only the rendered invoice */
@media print {
  .billing-page > .site-header,
  .billing-page > .bill-wrap,
  .billing-page > .site-footer,
  .billing-page > datalist { display: none !important; }
  .billing-page .invoice-print-host { position: static; left: 0; width: auto; }
  .billing-page { background: var(--color-white); padding: 0; }
}

/* =========================================================================
   18. OWNER DASHBOARD · PROJECT · REPORTS
   ========================================================================= */
/* Top nav (admin pages) — link style shared with the public nav, see §5.
   nowrap keeps the header a constant height on every page: with wrap, a page
   that has a vertical scrollbar is ~15px narrower and could tip the row onto a
   2nd line, making the header "jump" taller when switching tabs. */
/* Admin top nav is ALWAYS collapsed behind the hamburger (owner preference,
   ported from Ka Locks Co) — never a horizontal pill row, at any width. */
.dash-nav { display: none; gap: 0.4rem; flex-wrap: nowrap; align-items: center; }
.dash-nav.is-open {
  display: flex;
  position: absolute; top: calc(100% + 14px); left: 0; right: 0;
  flex-direction: column; align-items: flex-start; gap: 0;
  background: var(--color-white); border-bottom: 1px solid var(--color-line);
  box-shadow: var(--shadow-md); padding: 0.5rem var(--gutter) 1rem; z-index: 90;
}
.dash-nav.is-open a { width: 100%; padding: 0.85rem 0.6rem; border-radius: var(--radius-sm); border-bottom: 1px solid var(--color-line); text-align: right; }
@media (min-width: 640px) {
  .dash-nav.is-open {
    left: auto; right: var(--gutter); width: 300px;
    border: 1px solid var(--color-line);
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  }
}

/* Status badge */
.badge { display: inline-block; font-family: var(--font-head); font-weight: 700; font-size: var(--fs-small);
  text-transform: uppercase; letter-spacing: 0.04em; padding: 0.2rem 0.6rem; border-radius: var(--radius-pill); }
.badge--open { background: color-mix(in srgb, var(--sm-teal-br) 18%, transparent); color: var(--color-teal-dark); }
.badge--done { background: color-mix(in srgb, var(--sm-orange) 14%, transparent); color: var(--color-amber-dark); }
/* A badge that IS an action (e.g. a message's Reply badge on the client hub):
   same look as the passive badge, plus the site-standard deboss press. */
button.badge { border: 0; cursor: pointer; transition: transform var(--transition), box-shadow var(--transition); }
button.badge:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); }
button.badge:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }

/* Dashboard layout */
.dash-grid { display: grid; gap: 1.1rem; grid-template-columns: 1fr; }
@media (min-width: 820px) { .dash-grid { grid-template-columns: 1fr 1fr; } }

.proj-list { display: grid; gap: 0.9rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .proj-list { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .proj-list { grid-template-columns: 1fr 1fr 1fr; } }
.proj-card {
  display: block; background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-left: 4px solid var(--color-navy); border-radius: var(--radius); padding: 1rem 1.1rem;
  box-shadow: var(--tile-shadow); transition: transform var(--transition), box-shadow var(--transition);
}
.proj-card:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); border-left-color: var(--color-amber); }
.proj-card:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }
.pc-top { display: flex; justify-content: space-between; align-items: center; gap: 0.6rem; }
.pc-top strong { font-family: var(--font-head); color: var(--color-navy); }
.pc-client { color: var(--color-ink); font-size: var(--fs-body); margin-top: 0.3rem; }
.pc-loc { color: var(--color-muted); }
.pc-foot { display: flex; justify-content: space-between; margin-top: 0.7rem; padding-top: 0.6rem; border-top: 1px solid var(--color-line); }
.pc-id { font-family: var(--font-mono, monospace); font-size: var(--fs-small); color: var(--color-muted); }
.pc-total { font-family: var(--font-head); font-weight: 800; color: var(--color-navy); }

/* expandable project card (client dashboard) — click the card to open it */
.proj-card--open { padding: 0; cursor: pointer; }
.proj-card--open > summary {
  list-style: none; padding: 1rem 1.1rem 1rem 2rem; position: relative; cursor: pointer;
}
.proj-card--open > summary::-webkit-details-marker { display: none; }
/* disclosure chevron on the left, rotates when open */
.proj-card--open > summary::before {
  content: ""; position: absolute; left: 0.9rem; top: 1.35rem;
  width: 0.5rem; height: 0.5rem; border-right: 2px solid var(--color-muted);
  border-bottom: 2px solid var(--color-muted); transform: rotate(-45deg);
  transition: transform var(--transition);
}
.proj-card--open[open] > summary::before { transform: rotate(45deg); }
.proj-card--open[open] { border-left-color: var(--color-amber); }
.proj-detail { padding: 0 1.1rem 1.1rem 2rem; }
.proj-detail .pd-scope { color: var(--color-ink); font-size: var(--fs-body); margin: 0 0 0.6rem; }
.proj-detail .pd-sub {
  font-family: var(--font-head); font-weight: 700; font-size: var(--fs-small); text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--color-muted); margin: 0.9rem 0 0.4rem;
}
.proj-detail .rtable { font-size: var(--fs-body); }

/* Project header meta */
.ph-meta { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: center; font-size: var(--fs-lead); }

/* Tabs */
.tabs { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 1rem; border-bottom: 2px solid var(--color-line); }
.tab {
  background: transparent; border: none; border-bottom: 3px solid transparent; margin-bottom: -2px;
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-lead); color: var(--color-muted);
  padding: 0.6rem 0.9rem; cursor: pointer;
}
.tab:hover { color: var(--color-navy); }
.tab.is-active { color: var(--color-navy); border-bottom-color: var(--color-amber); }
.tab-pane { display: none; }
.tab-pane.is-active { display: block; }

/* Timeline */
.tl-row {
  display: grid; grid-template-columns: auto auto 1fr auto auto; gap: 0.7rem; align-items: center;
  padding: 0.7rem 0.4rem; border-bottom: 1px solid var(--color-line);
}
.tl-date { font-family: var(--font-mono, monospace); font-size: var(--fs-small); color: var(--color-muted); white-space: nowrap; }
.tl-icon { font-size: var(--fs-h3); }
.tl-body { display: flex; flex-direction: column; min-width: 0; }
.tl-body strong { color: var(--color-navy); font-size: var(--fs-lead); }
.tl-sub { color: var(--color-muted); font-size: var(--fs-small); }
.tl-amt { font-family: var(--font-head); font-weight: 700; color: var(--color-navy); white-space: nowrap; }
.tl-task { border-left: 3px solid var(--color-teal); }
.tl-material { border-left: 3px solid var(--color-amber); }

.photo-chip { color: var(--color-teal-dark); font-weight: 600; }
.photo-chip:hover { color: var(--color-amber-dark); }

/* Gallery */
.gallery { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }
.gal-item { display: inline-flex; align-items: center; gap: 0.4rem; background: var(--color-bg);
  border: 1px solid var(--color-line); border-radius: var(--radius-pill); padding: 0.3rem 0.4rem 0.3rem 0.8rem; font-size: var(--fs-body); }
.gal-item a { color: var(--color-navy); }
.gal-item--thumb { padding: 0.3rem; border-radius: var(--radius-sm); }
.gal-item--thumb img { width: 64px; height: 64px; object-fit: cover; border-radius: 6px; display: block; }

/* Filters */
/* Reports filters — flat & lightweight, NO card wrapper. The quick presets are
   the primary, instant filters (a tap applies immediately); the explicit
   Client / date / location inputs sit below, de-cluttered and inline. */
.rfilters { display: flex; flex-direction: column; gap: 0.5rem; margin: 0 0 0.7rem; }
.rf-presets { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.rf-lbl { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-muted); margin-right: 0.1rem; }
.rf-preset { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small); color: var(--color-navy);
  background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius-pill);
  padding: 0.4rem 0.95rem; cursor: pointer; transition: background .15s, border-color .15s, color .15s, box-shadow .15s; }
@media (hover: hover) { .rf-preset:hover { border-color: var(--color-gold); box-shadow: var(--shadow-sm); } }
.rf-preset.is-active { background: var(--color-navy); color: var(--color-white); border-color: var(--color-navy); }
.rf-reset { margin-left: auto; background: none; border: 0; padding: 0.2rem 0.1rem; cursor: pointer;
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small); color: var(--color-gold-dark);
  text-decoration: underline; text-underline-offset: 3px; }
@media (hover: hover) { .rf-reset:hover { color: var(--color-amber-dark); } }
.rf-adv { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.rf-adv select, .rf-adv input { height: var(--control-h); padding: 0 0.55rem; font-family: var(--font-body); font-size: var(--fs-small);
  color: var(--color-ink); background: var(--color-surface); border: 1px solid var(--color-line); border-radius: var(--radius-sm); }
.rf-adv select:focus, .rf-adv input:focus { outline: none; border-color: var(--color-gold); box-shadow: var(--ring); }
.rf-adv select { min-width: 8.5rem; }
.rf-adv input[type="date"] { width: 8.4rem; }
.rf-adv input[type="text"] { flex: 1 1 8rem; min-width: 7rem; }
.rf-dash { color: var(--color-muted); }

/* Floating "+ New Client" action button — fixed bottom-right, stays on scroll.
   Reuses .btn--primary colors; .fab only adds the fixed placement + lift. */
.fab { position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 90;
  gap: 0.5rem; border-radius: var(--radius-pill); box-shadow: 0 10px 26px rgba(0,0,0,0.30); }
.fab svg { width: 20px; height: 20px; }
@media (hover: hover) { .fab:hover { transform: translateY(-2px); box-shadow: 0 14px 32px rgba(0,0,0,0.36); } }
.fab:active { transform: translateY(0); box-shadow: 0 8px 20px rgba(0,0,0,0.30); }

/* report results header: summary + expand/collapse-all, pushed to the right.
   --wrap lets the controls drop to their own line on narrow screens instead of
   squeezing the title into two lines and stacking the buttons vertically. */
.panel-head--wrap { flex-wrap: wrap; }
.panel-head--wrap > h2 { white-space: nowrap; }
.rhead-right { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; }
.rcpt-links { font-size: var(--fs-small); color: var(--color-muted); margin: 0 0 0.5rem; }
.rcpt-links a { color: var(--color-navy); font-weight: 600; }

/* Report table */
.table-scroll { overflow-x: auto; }
.table-scroll--stacked { margin-top: 0.6rem; }
.rtable { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
.rtable th, .rtable td { padding: 0.38rem 0.6rem; text-align: left; border-bottom: 1px solid var(--color-line); white-space: nowrap; }
.rtable td.num, .rtable th.num { text-align: right; font-variant-numeric: tabular-nums; }
.rtable td.wrap { white-space: normal; }
.rtable--small { font-size: var(--fs-small); }
.rtable--small th, .rtable--small td { padding: 0.3rem 0.5rem; }
/* in-cell editable inputs (e.g. AI Estimate's results table, receipts.php's
   item-review table) — one definition, every editable .rtable reuses it.
   Excludes checkboxes: js/admin-table.js's bulk-select column (.rt-select-cell)
   also lives inside .rtable and must keep its own native checkbox sizing. */
.rtable input:not([type="checkbox"]), .rtable select {
  width: 100%; box-sizing: border-box;
  padding: 0.3rem 0.4rem; border: 1px solid var(--tint-line); border-radius: var(--radius-sm);
  font-size: var(--fs-small); font-family: var(--font-body);
  background: var(--color-surface); color: var(--color-ink);
}
.rtable thead th { font-family: var(--font-head); font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-muted); background: var(--color-bg); position: sticky; top: 0; }
.rtable th.sortable { cursor: pointer; -webkit-user-select: none; user-select: none; }
.rtable th.sortable:hover { color: var(--color-navy); }
/* rows with a destination are fully clickable (same target as their Open/View
   button) — pointer + slightly stronger hover so it reads as clickable.
   :hover gated to hover-capable pointers so a tap doesn't stick a row
   highlighted on touch. */
.rtable tbody tr[data-href] { cursor: pointer; }
/* same gold-tint language as every other hover site-wide — no transform on
   dense table rows (lifting a single row looks broken), just a warm tint;
   clickable rows get a slightly stronger tint so they read as "go here". */
@media (hover: hover) {
  .rtable tbody tr:hover { background: color-mix(in srgb, var(--color-gold) 5%, var(--color-surface)); }
  .rtable tbody tr[data-href]:hover { background: color-mix(in srgb, var(--color-gold) 9%, var(--color-surface)); }
}
.act-card[data-href] { cursor: pointer; transition: transform var(--transition), box-shadow var(--transition), background var(--transition), border-color var(--transition); }
/* lift + gold glow — the ONE site-wide "clickable card" language (matches
   .portal-band-head / .panel--fold .panel-head) */
@media (hover: hover) {
  .act-card[data-href]:hover { transform: translateY(-2px); border-color: var(--color-gold); box-shadow: 0 12px 26px rgba(20,32,50,.13); }
}
.act-card[data-href]:active { transform: translateY(0); }
/* keyboard: clickable rows/cards show the same focus ring as fold headers */
.rtable tbody tr[data-href]:focus-visible,
.act-card[data-href]:focus-visible { outline: none; box-shadow: var(--ring); }

/* shared admin list component (js/admin-table.js) — date-group headers,
   sortable columns, expandable detail rows */
.rtable th.sortable.is-sorted { color: var(--color-navy); }
.rtable tr.rt-group td {
  background: var(--color-bg-alt); font-family: var(--font-head); font-weight: 700;
  font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-navy);
  white-space: nowrap; padding-top: 0.5rem; padding-bottom: 0.5rem;
}
.rtable tr.rt-group .rt-group-count { margin-left: 0.6rem; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--color-muted); }

/* ---- OWNER DESIGN STANDARD for record lists, SITE-WIDE (2026-07-13):
   2 nested COLLAPSIBLE category bands, then rows of exactly 3 columns
   (5 values total). Each band level wears its own color and rows their own,
   so the nesting reads by color alone; a value never appears twice on the
   page (a column is never also a band); no column headers above the bands.
   Rendered by js/cat-bands.js (Leads, Projects, Reports) and server-side by
   admin/email-activity.php. Tokens only — reskins with the theme.
   Level palette:
     L1 band  = filled bronze (same pairing as .btn--primary: gold gradient,
                ivory text) — the strongest strip on the page
     L2 band  = light bronze tint, dark text, one step in
     rows     = faint tint on white, hover deepens                        */
/* Force the collapse ourselves rather than relying on the UA's ::details-content
   hiding, which some Chromium builds skip — the [open] attribute always toggles
   correctly, so key off it directly. */
.cat-band:not([open]) > .cat-band-body { display: none; }
/* Every expandable row wears the SAME dress as the PRO-dashboard bands
   (owner rule 2026-07-13): white rounded pill, hairline border, tile shadow;
   hover = the shared press (dark at top-left AND bottom-right). Hierarchy
   reads from indentation + type scale, not from color. */
.cat-band > summary.cat-band-head {
  list-style: none; cursor: pointer; -webkit-user-select: none; user-select: none;
  display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem;
  font-family: var(--font-head); color: var(--color-navy);
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-radius: var(--radius); box-shadow: var(--tile-shadow);
  margin-bottom: 0.4rem;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.cat-band > summary.cat-band-head::-webkit-details-marker { display: none; }
/* lift + gold glow — the ONE site-wide "clickable card" language (matches
   .portal-band-head / .panel--fold .panel-head / .act-card) */
@media (hover: hover) {
  .cat-band > summary.cat-band-head:hover { transform: translateY(-2px); border-color: var(--color-gold); box-shadow: 0 12px 26px rgba(20,32,50,.13); }
}
.cat-band > summary.cat-band-head:active { transform: translateY(0); }
.cat-band > summary.cat-band-head:focus-visible { outline: none; box-shadow: var(--ring); }
.cat-band-head .cb-count { font-weight: 400; font-size: var(--fs-small); color: var(--color-muted); }
.cat-band-head .cb-chev { margin-left: auto; font-size: var(--fs-lead); line-height: 1; transition: transform var(--transition); color: var(--color-muted); }
.cat-band[open] > summary .cb-chev { transform: rotate(180deg); }

/* L1 — strongest strip: bold uppercase */
.cat-band--l1 > summary.cat-band-head {
  text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; font-size: var(--fs-body);
}
/* L2 — one step in and one step down the type ladder */
.cat-band--l2 > summary.cat-band-head {
  font-weight: 600; font-size: var(--fs-small);
  margin-left: 1.4rem;
}
.cat-band--l2 { margin-bottom: 0.4rem; }
/* Narrow screens: pull the nested city band's indent back so rows keep their width. */
@media (max-width: 560px) {
  .cat-band--l2 > summary.cat-band-head { margin-left: 0.55rem; }
}

/* rows — their own color: faint tint on the panel surface, hover deepens */
.cat-rows { width: 100%; table-layout: fixed; border-collapse: collapse; }
/* Each cell is ONE line: nowrap makes the overflow:hidden + ellipsis actually
   truncate instead of wrapping (a phone number must never break across lines). */
.cat-rows td { padding: 0.3rem 0.6rem; border-top: 1px solid var(--color-line); font-size: var(--fs-body); vertical-align: middle; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; background: color-mix(in srgb, var(--color-gold) 6%, var(--color-surface)); }
.cat-rows .c1 { width: 46%; padding-left: 2.1rem; }
.cat-rows .c1 a { font-weight: 600; }
.cat-rows .c2 { width: 34%; color: var(--color-ink); }
.cat-rows .c2 a { color: var(--color-navy); font-weight: 500; }
@media (hover: hover) { .cat-rows .c2 a:hover { color: var(--color-amber-dark); text-decoration: underline; text-underline-offset: 3px; } }
.cat-rows .c3 { width: 20%; text-align: right; color: var(--color-ink); font-size: var(--fs-small); }
/* Narrow screens: give the phone the room it needs so it stays on one line,
   trim the name indent, and let the (secondary) email give up width & ellipsis. */
@media (max-width: 560px) {
  .cat-rows .c1 { width: 40%; padding-left: 1.1rem; }
  .cat-rows .c2 { width: 30%; }
  .cat-rows .c3 { width: 30%; }
  .cat-rows.has-sel .c1 { width: calc(40% - 2.2rem); padding-left: 0.2rem; }
}
/* selectable lists (Projects): a leading checkbox cell; c1 gives up its room */
.cat-rows .csel { width: 2.2rem; text-align: center; }
.cat-rows .csel input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.cat-rows.has-sel .c1 { width: calc(46% - 2.2rem); padding-left: 0.2rem; }
/* only rows that actually navigate get the pointer + hover invitation */
.cat-rows tr[data-href] { cursor: pointer; }
@media (hover: hover) {
  .cat-rows tr[data-href]:hover td { background: color-mix(in srgb, var(--color-gold) 12%, var(--color-surface)); }
}
.cat-rows tr[data-href]:focus-visible { outline: none; box-shadow: var(--ring); }

/* List toolbar — a sticky search + expand/collapse-all bar that floats above a
   CatBands list (Clients). MUST sit OUTSIDE the .panel: .panel is overflow:hidden,
   which would cancel position:sticky. Sticks just below the sticky .site-header —
   the page script measures the header and sets --header-h. */
.list-toolbar {
  position: sticky; top: calc(var(--header-h, 3.6rem) + 0.4rem); z-index: 30;
  display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap;
  padding: 0.5rem 0.6rem; margin: 0 0 0.7rem;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-radius: var(--radius); box-shadow: var(--tile-shadow);
}
.list-toolbar .field { margin: 0; flex: 1 1 15rem; min-width: 0; }
.list-toolbar .lt-actions { display: flex; gap: 0.4rem; flex: 0 0 auto; }
.rtable .rt-toggle-cell { width: 2.2rem; text-align: center; }
.rtable .rt-select-cell { width: 2.2rem; text-align: center; }
.rtable .rt-select-cell input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.rtable .rt-toggle {
  background: none; border: 1px solid var(--color-line); border-radius: 6px; width: 1.7rem; height: 1.7rem;
  color: var(--color-muted); font-size: var(--fs-body); line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.rtable .rt-toggle:hover { background: var(--color-bg); color: var(--color-navy); }
.rtable tr.rt-detail-row td { background: var(--color-bg); white-space: normal; }
/* Columns a caller marks { hideOnMobile: true } (see js/admin-table.js) drop out
   below the panel's own scroll-container breakpoint, so the remaining columns
   fit without the table needing its own horizontal scrollbar. Full data for a
   hidden column is still reachable — every admin row opens its own detail page. */
@media (max-width: 640px) {
  .rtable .col-opt { display: none; }
  /* The first column (the row's title/name link, always present) is allowed to
     wrap here — its badge/subtext can drop to its own line instead of forcing
     the whole row wide the way every other nowrap cell does. */
  .rtable td:first-child { white-space: normal; }
}
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Local-environment watermark (2026-07-31, owner: "I really need to stop
   editing it locally") — inc/local_watermark.php, included from BOTH
   header_public.php and header_pro.php, so it must work unscoped: admin
   pages carry body class "billing-page" (no "b-page"), so this can't live
   inside b_theme.css's .b-page-scoped rules the way most page chrome does.
   position:fixed (survives scrolling) + pointer-events:none (can never
   block a real click underneath it) + very high z-index (above the sticky
   header, modals, everything). */
.kco-local-watermark {
  /* Cleared below the sticky header (2026-08-01 fix — it was covering the
     "Detailed Quote" nav link at dead-center-top). Reuses --header-h, the
     SAME variable the sticky filter bar below already measures the real
     header against (js sets it; 3.6rem is the fallback for pages that
     never run that measurement), so this never drifts back into the nav
     if the header's height ever changes. */
  position: fixed; top: calc(var(--header-h, 3.6rem) + 0.5rem); left: 50%; transform: translateX(-50%); z-index: 99999;
  pointer-events: none; user-select: none;
  background: #1a1a1a; color: #FFC72C;
  border: 3px solid #FFC72C; box-sizing: border-box;
  font-family: var(--font-head); font-weight: 800; font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.5rem 0.9rem; border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
}
.btn-mini { display: inline-block; font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small);
  background: var(--color-navy); color: var(--color-white); padding: 0.3rem 0.7rem; border-radius: var(--radius-pill);
  transition: transform var(--transition), box-shadow var(--transition), background var(--transition); }
.btn-mini:hover { background: var(--color-navy-deep); transform: translateY(1px); box-shadow: var(--btn-deboss); }
.btn-mini:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }

/* Portal tiles (dashboard) */
.portal-grid { display: grid; gap: 0.55rem; grid-template-columns: 1fr; }
@media (min-width: 560px) { .portal-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .portal-grid { grid-template-columns: 1fr 1fr 1fr; } }
@media (min-width: 1150px) { .portal-grid { grid-template-columns: 1fr 1fr 1fr 1fr; } }

/* Collapsible dashboard bands (native <details>): a clickable header strip
   toggles its tile grid. Help sits outside a band so it's always visible. */
.portal-band { margin-bottom: 0.75rem; }
/* Title + hint stack as two dedicated lines (grid rows) instead of fighting
   for room side-by-side in one flex row — at narrow widths that used to wrap
   both the title AND the hint independently into a jumbled 3-line mess.
   Grid position is set per-child below, so no markup change is needed even
   though title/hint/chevron are plain sibling spans in the HTML. */
.portal-band-head {
  list-style: none; cursor: pointer; -webkit-user-select: none; user-select: none;
  /* 3 columns since 2026-08-01: identity chip | title+hint stack | chevron */
  display: grid; grid-template-columns: auto 1fr auto; column-gap: 0.65rem; row-gap: 0.05rem;
  align-items: center;
  padding: 0.42rem 0.9rem; margin-bottom: 0.55rem;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  /* the identity accent edge — --band-accent set per band below */
  border-left: 5px solid var(--band-accent, var(--color-line));
  border-radius: var(--radius); box-shadow: var(--tile-shadow);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.portal-band-head::-webkit-details-marker { display: none; }
/* :hover only on devices that actually support it — on touch, a stuck
   :hover after tap would otherwise leave the band looking permanently
   "pressed" until the user taps elsewhere (owner-reported 2026-07-20). */
@media (hover: hover) {
  .portal-band-head:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); border-color: var(--color-amber); }
}
.portal-band-head:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }
/* an author `display:grid` on .portal-grid beats the UA rule that hides a
   closed <details>'s content, so hide it explicitly when the band is closed. */
.portal-band:not([open]) .portal-grid { display: none; }
.portal-band-head .pt-ico { grid-column: 1; grid-row: 1 / 3; }
.portal-band-head .pb-title { grid-column: 2; grid-row: 1; font-family: var(--font-head); font-weight: 700; font-size: var(--fs-body); color: var(--color-navy); }
.portal-band-head .portal-band-hint { grid-column: 2; grid-row: 2; color: var(--color-muted); font-size: var(--fs-small); }
.portal-band-head .pb-chev { grid-column: 3; grid-row: 1 / 3; margin-left: 0; align-self: center; flex: none; width: 20px; height: 20px; color: var(--band-accent, var(--color-navy)); transition: transform var(--transition); }
.portal-band[open] .portal-band-head .pb-chev { transform: rotate(180deg); }

/* BAND IDENTITY (2026-08-01, owner: "i never find anything in this
   dashboard... maybe alternate blocks colors"). Each band owns ONE accent —
   edge stripe, chip fill, chevron, and an 8% header tint while open — so
   the six bands stop being interchangeable white pills and become findable
   by color memory ("green = Grow") without reading. The six hues are all
   ESTABLISHED platform colors (the five PRO-skin chrome values from
   inc/theme.php + Klein blue), not new inventions, and they deliberately do
   NOT follow the active skin: the whole point is a stable color map, so
   switching skins must never repaint it. */
.band--create  { --band-accent: #C96A2E; }   /* ember  — make things   */
.band--grow    { --band-accent: #3E8E7E; }   /* forest — growth        */
.band--clients { --band-accent: #499cd0; }   /* sky    — people        */
.band--content { --band-accent: #8A5A83; }   /* plum   — writing       */
.band--health  { --band-accent: #6B533A; }   /* bronze — maintenance   */
.band--account { --band-accent: #002FA7; }   /* klein  — keys & login  */
/* chip wears its band's accent — placed AFTER the unified chip recipe in
   the cascade and [data-theme]-prefixed, so it wins the background while
   keeping the recipe's bevel/glow shadows */
[data-theme] .portal-band-head .pt-ico,
.portal-band-head .pt-ico { background: var(--band-accent, var(--color-navy)); color: #fff; }
/* ONE tint value for the whole band (2026-08-02, owner: "make the entire
   background of each block of tiles the same color as the header row") —
   defined once here so the header strip and the tile area below it can
   never drift apart. The band's tiles keep their own white surface, so
   they still read as cards ON the tinted block rather than dissolving. */
/* The band is ONE coloured block, open or closed (2026-08-02, owner: "not
   the tiles background, the background of the block they are in"). 14% of
   the band's accent: enough to read as a distinct colour per band, light
   enough that the body ink on it stays 11.9:1+ and the muted hint 4.1:1+
   (measured across all six accents). Header and tile area share the ONE
   --band-tint value, so they can never drift apart; the tiles themselves
   stay white so they read as cards ON the block. */
.portal-band { --band-tint: color-mix(in srgb, var(--band-accent) 14%, var(--color-surface)); }
.portal-band-head {
  background: var(--band-tint);
  border-color: color-mix(in srgb, var(--band-accent) 35%, var(--color-line));
}
/* while open, the header joins the tile block below it into one panel */
.portal-band[open] .portal-band-head {
  margin-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}
.portal-band[open] .portal-grid {
  background: var(--band-tint);
  border: 1.5px solid color-mix(in srgb, var(--band-accent) 35%, var(--color-line));
  border-top: 0; border-left-width: 5px; border-left-color: var(--band-accent);
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 0.6rem; margin-bottom: 0.55rem;
}
.portal-tile {
  display: grid; grid-template-columns: 34px 1fr; grid-template-rows: auto auto; column-gap: 0.65rem; row-gap: 0.1rem; align-items: start;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-radius: var(--radius); padding: 0.55rem 0.85rem; box-shadow: var(--tile-shadow);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
@media (hover: hover) {
  .portal-tile:hover { transform: translateY(1px); box-shadow: var(--btn-deboss); border-color: var(--color-amber); }
}
.portal-tile:active { transform: translateY(2px); box-shadow: var(--btn-deboss-active); }
.pt-ico {
  display: grid; place-items: center; width: 34px; height: 34px; border-radius: 9px;
  background: linear-gradient(135deg, var(--sm-teal-1), var(--sm-teal-2)); color: var(--color-white); margin-bottom: 0; grid-row: 1 / 3; align-self: center;
}
/* Glyph = 62% of its chip, whatever the chip's size (2026-07-31, owner:
   "why is the icon so small?" on the lead page's panel chip). Every chip
   variant used to hardcode its own glyph px at a 50% ratio (34/17, 40/20,
   32/16) — small-reading everywhere, three places to keep in sync. One
   percentage derives from each chip's own box, so the two per-variant svg
   rules below were deleted rather than retuned. Matches the 0.62 ratio the
   in-button icons use. */
.pt-ico svg { width: 62%; height: 62%; }
.pt-title { font-family: var(--font-head); font-weight: 700; font-size: var(--fs-body); color: var(--color-navy); line-height: 1.3; }
/* 2-line clamp added with the 2026-07-31 dashboard redesign (owner: "you
   cannot have random amounts of contents on tiles"). The copy standard
   (dash_tile() caps descriptions at 72 chars) means nothing truncates in
   practice — this is the structural guarantee that a future long description
   can never re-create ragged, unequal tiles. */
.pt-desc {
  color: var(--color-muted); font-size: var(--fs-small); line-height: 1.35;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}

/* nav date chip — every page's .nav-cta, populated by initTodayDate */
.nav-date {
  font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small);
  color: var(--color-muted); white-space: nowrap;
  margin-left: auto; margin-right: 0.75rem;
}

/* -------------------------------------------------------------------------
   PRO ADMIN — density pass (2026-07-20, round 2 tightened further). Scoped
   to .billing-page so the public marketing site's forms/cards are never
   affected — same shared classes (.field, .card, .tab, .grid-2/4, .panel,
   .bill-wrap) are also used site-wide (incl. faq.php, which carries NO
   .billing-page class and must keep its original spacious hero look).
   ------------------------------------------------------------------------- */
.billing-page .bill-wrap { padding-block: 0.6rem 1.2rem; }
.billing-page .bill-head { margin-bottom: 0.5rem; }
.billing-page .bill-head h1 { font-size: var(--fs-h2); }
.billing-page .bill-head .muted { font-size: var(--fs-small); margin-top: 0.1rem; }

.billing-page .field { margin-bottom: 0.3rem; }
.billing-page .field label { margin-bottom: 0.1rem; }
.billing-page .field input,
.billing-page .field select,
.billing-page .field textarea {
  font-size: var(--fs-body);
  padding: 0.28rem 0.5rem;
}
.billing-page .field textarea { min-height: 52px; }
.billing-page .grid-2,
.billing-page .grid-3,
.billing-page .grid-4 { gap: 0.22rem 0.6rem; }
.billing-page .form-actions { margin-top: 0.4rem; gap: 0.4rem; }
.billing-page .tabs { margin-bottom: 0.45rem; }
.billing-page .tab { padding: 0.3rem 0.6rem; font-size: var(--fs-body); }
.billing-page .card { padding: 0.65rem; }
.billing-page .hint, .billing-page .hce-help { margin-bottom: 0.35rem; }
.billing-page .panel .sub { margin: 0.45rem 0 0.2rem; }
.billing-page .li-row { margin-bottom: 0.25rem; }
.billing-page .li-row input, .billing-page .li-row select { padding: 0.24rem 0.4rem; }
.billing-page .catalog-box { padding: 0.5rem; margin-bottom: 0.45rem; }

/* Bulk-action toolbar — was a duplicated inline style="" block on projects.php,
   scheduled-services-manage.php, reviews.php. One class, referenced tokens only. */
.bulk-bar { background: var(--color-bg-alt); border-radius: var(--radius-sm); padding: 0.4rem 0.65rem; margin-bottom: 0.4rem; }
.bulk-bar .bulk-count { color: var(--color-navy); font-weight: 600; }

/* Reusable flex spacer — pushes trailing content (e.g. a status badge) to the
   far edge of a flex row without a bespoke inline style="flex:1". */
.flex-spacer { flex: 1; }
/* Panel body laid out as a single wrapping flex row (e.g. quick-invoice.php's
   edit-mode status bar: label + badge + spacer + action buttons). */
.panel-body--row { display: flex; align-items: center; gap: 0.65rem; flex-wrap: wrap; }

/* Narrow form pages */
.form-narrow { max-width: 720px; }
.bill-head--row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }

/* A PRO page opened from a dashboard tile wears that tile's dress: the SAME
   surface, 1.5px border, radius and --tile-shadow as .portal-tile, plus the
   very same .pt-ico chip and glyph (from inc/icons.php), so the card you tap
   and the page you land on are visibly the same object. No hover/press
   effects here on purpose — the header isn't clickable, only tiles are.
   A modifier, never plain .bill-head: that class is also used by public
   marketing pages, which must keep their flat hero. */
.bill-head--tile {
  display: grid; grid-template-columns: 40px 1fr; column-gap: 0.8rem; align-items: center;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-radius: var(--radius); box-shadow: var(--tile-shadow);
  padding: 0.7rem 1rem; margin-bottom: 0.55rem;
}
/* Top-aligned (2026-07-31, owner: "these icons need top left align
   throughout the site") — center-aligned read fine only when the subtitle
   was one line; a 2-line subtitle (e.g. Leads' "Every quote request
   submitted...") left the icon visibly floating below the title instead of
   anchored beside it. Same fix as .panel-ident below — one rule, every page
   header tile on the site. */
.bill-head--tile .pt-ico { width: 40px; height: 40px; grid-row: 1 / 3; align-self: start; }
.bill-head--tile .bh-text { grid-column: 2; min-width: 0; }
.bill-head--tile h1 { font-size: var(--fs-h3); margin: 0; line-height: 1.15; }
.bill-head--tile .muted { margin-top: 0.12rem; font-size: var(--fs-small); }
/* keeps a trailing action (e.g. Projects' "+ New") on the header's right edge */
.bill-head--tile.bill-head--row { display: grid; grid-template-columns: 40px 1fr auto; }

/* Record-detail pages (one project / lead / client) are reached from a LIST
   rather than a tile, and their record panel already shares the tile's
   surface + border + shadow — what was missing is the mark. This puts the
   section's tile icon beside the record's own title, so a project page still
   reads as part of the Projects tool you came from. */
/* Top-aligned, same fix as .bill-head--tile above (2026-07-31, owner:
   "throughout the site") — panel-ident is the identical icon+title+subtitle
   pattern (see admin/lead.php's name/badge/date band). */
.panel-ident { display: flex; align-items: flex-start; gap: 0.6rem; min-width: 0; }
.panel-ident .pt-ico { width: 32px; height: 32px; flex: none; }
.panel-ident > div { min-width: 0; }
/* City/State/ZIP-style row: one variable-width "main" field (flex:1, takes
   whatever room is left) plus fixed-width short fields (State ~2 chars,
   ZIP ~5-10) that never need their own line — no wrap, no breakpoint, holds
   as one row down to phone width by design (owner rule 2026-07-21: fixed-
   format short fields must never force a 3-field row to stack). */
.grid-3 { display: flex; gap: 0.4rem; align-items: start; }
.grid-3 > .field { min-width: 0; }
.grid-3 > .field:first-child { flex: 1 1 auto; }
.grid-3 > .field:nth-child(2) { flex: 0 0 4rem; }
.grid-3 > .field:nth-child(3) { flex: 0 0 5.5rem; }
.link-accent { color: var(--color-amber-dark); font-weight: 600; }
.link-accent:hover { text-decoration: underline; }

/* Timeline reflow on mobile (keeps delete accessible) */
@media (max-width: 640px) {
  .tl-row {
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "date amt rm" "body body body";
    gap: 0.3rem 0.6rem; padding: 0.7rem 0.2rem;
  }
  .tl-date { grid-area: date; }
  .tl-amt  { grid-area: amt; text-align: right; }
  .tl-row .row-rm { grid-area: rm; }
  .tl-icon { display: none; }
  .tl-body { grid-area: body; }
}

/* =========================================================================
   19. CHECKBOX CHIPS (quote form: skills, preferred reply)
   ========================================================================= */
.check-group { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.15rem; }
/* selector beats ".field label" (which would otherwise force display:block) */
.check-group .check-item {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: var(--fs-body); font-weight: 500; color: var(--color-ink);
  background: var(--color-bg); border: 1.5px solid var(--color-line);
  border-radius: var(--radius-pill); padding: 0.45rem 0.85rem; margin: 0; cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.check-group .check-item:hover { border-color: var(--color-navy); }
.check-group .check-item input { accent-color: var(--sm-orange); width: 16px; height: 16px; margin: 0; flex: none; }
.check-group .check-item:has(input:checked) { border-color: var(--sm-orange); background: color-mix(in srgb, var(--sm-orange) 5%, var(--color-white)); color: var(--color-navy); font-weight: 600; }

/* =========================================================================
   20. REVIEWS MANAGER (admin)
   ========================================================================= */
#rvList { display: grid; gap: 0.8rem; }
.rv-card { border: 1px solid var(--color-line); border-left: 4px solid var(--color-navy); border-radius: var(--radius-sm); padding: 0.8rem 1rem; background: var(--color-surface); }
/* flex-wrap + min-width:0 on the title: a long location address (or any
   long title) wraps onto its own line instead of pushing Edit/Remove off
   the visible edge of the card at narrow widths. */
.rv-card-top { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.rv-card-top strong { color: var(--color-navy); font-family: var(--font-head); min-width: 0; flex: 1 1 auto; }
.rv-stars { color: var(--color-gold); letter-spacing: 1px; }
.rv-card-top .row-rm { margin-left: auto; flex: none; }
.rv-meta { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.1rem; }
.rv-quote { margin-top: 0.4rem; color: var(--color-ink); font-size: var(--fs-lead); }

/* =========================================================================
   21. SKINS
   Default (:root above) = "Beach Vibe" (the original teal / sand / yellow;
   internal slug stays santa-monica-beach).
   Two light, elegant alternates, chosen from the dashboard and applied
   site-wide via <html data-theme="…"> (see inc/theme.php):
     ivory-ink          — warm ivory + charcoal ink + muted teal
     oil-rubbed-bronze  — warm ivory + deep oil-rubbed bronze
   Components read semantic tokens, so most flip from the token blocks below;
   the grouped rules afterward fix the few spots that reuse one token as both
   a foreground and a background.
   ========================================================================= */

/* ---- Ivory & Ink ---- (warm ivory + charcoal INK, monochrome — no color) */
[data-theme="ivory-ink"] {
  --sm-gold:#2E2B25;     --sm-gold-dark:#1A1813;     /* accent = warm charcoal ink */
  --sm-orange:#2E2B25;   --sm-orange-dark:#1A1813;
  --color-gold:#2E2B25;  --color-gold-dark:#1A1813;
  --color-amber:#2E2B25; --color-amber-dark:#1A1813;
  --color-teal:#2E2B25;  --color-teal-dark:#1A1813;
  --color-navy:#26231E;  --color-navy-deep:#1A1813; --color-navy-darker:#13110C;
  --klein:#2E2B25;       --klein-deep:#1A1813;
  --color-ink:#262420;   --color-muted:#6F6A60;
  --color-line:#E6DFCC;  --tint-line:#DAD2BC; --tint-cool:#EEE7D6;
  --color-surface:#FFFFFF; --color-bg:color-mix(in srgb, var(--color-gold) 18%, #F5F1E7); --color-bg-alt:color-mix(in srgb, var(--color-gold) 30%, #F5F1E7);
  --color-star:#2E2B25;  --color-success:#4A463C;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 28%, transparent);
  --fx:#002FA7; --fx-soft:#3D6BFF; --fx-bright:#6E92FF; --fx-ink:#FFFFFF; /* Klein blue effects */
}

/* ---- Oil-Rubbed Bronze & Ivory ---- RETINTED 2026-08-01 (owner: "the
   permanent skin is orange" — brown/antique-gold was wrong, not a session
   regression; this skin name is legacy, the identity underneath it is now
   the site's real bi-chrome: Home-Depot orange + International Klein Blue.
   Values copied verbatim from css/b_theme.css's :root so this skin (which
   governs invoice.php, the PDF/print buttons, and both transactional
   emails via api/billing_lib.php's KCO_* mirror) is provably the same two
   colors as every button and badge on the live public pages, not a third,
   separately-tuned copy. Renaming the skin key itself would touch every
   inc/theme.php reference and data-theme selector site-wide for zero visual
   gain — only the color values needed to change. */
[data-theme="oil-rubbed-bronze"] {
  --sm-gold:#FB4D1A;     --sm-gold-dark:#DD3D0E;
  --sm-orange:#FB4D1A;   --sm-orange-dark:#DD3D0E;
  --color-gold:#FB4D1A;  --color-gold-dark:#DD3D0E;
  --color-amber:#FB4D1A; --color-amber-dark:#DD3D0E;
  --color-teal:#002FA7;  --color-teal-dark:#001F7A;
  --color-navy:#002FA7;  --color-navy-deep:#001F7A; --color-navy-darker:#001233;
  --klein:#002FA7;       --klein-deep:#001F7A;
  --color-ink:#333333;   --color-muted:#6D665D;
  --color-line:#EBD5C2;  --tint-line:#E6C7AE; --tint-cool:#EEE6D6;
  --color-surface:#FFFFFF; --color-bg:#F6F0E7; --color-bg-alt:color-mix(in srgb, var(--color-bg) 90%, var(--color-ink));
  --color-star:#FB4D1A;  --color-success:#002FA7;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 35%, transparent);
  --fx:#FB4D1A; --fx-soft:#FF7A45; --fx-bright:#FFA57E; --fx-ink:#4A1503;
  /* Section titles + eyebrows follow --color-navy (now Klein blue); the
     orange stays the glow/shadow effect only (--fx-orange). */
  --color-heading:var(--color-navy); --fx-orange:var(--sm-orange);
}

/* ---- Sky Ink ---- (warm ivory base + sky-blue #499cd0 accents; the KASITE
   look, opt-in). A full blue twin of oil-rubbed-bronze — same token names,
   blue values — so switching kco_theme() to 'sky-ink' recolours the whole site
   and reverting is a one-word change (owner prefers the blue, 2026-07-22). */
[data-theme="sky-ink"] {
  --sm-gold:#499cd0;     --sm-gold-dark:#3a7da6;
  --sm-orange:#499cd0;   --sm-orange-dark:#3a7da6;
  --color-gold:#499cd0;  --color-gold-dark:#3a7da6;
  --color-amber:#499cd0; --color-amber-dark:#3a7da6;
  --color-teal:#499cd0;  --color-teal-dark:#3a7da6;
  --color-navy:#499cd0;  --color-navy-deep:#3a7da6; --color-navy-darker:#285672;
  --klein:#499cd0;       --klein-deep:#3a7da6;
  --color-ink:#262420;   --color-muted:#6F6A60;
  --color-line:#E6DFCC;  --tint-line:#DAD2BC; --tint-cool:#EEE7D6;
  --color-surface:#FFFFFF; --color-bg:color-mix(in srgb, var(--color-gold) 18%, #F5F1E7); --color-bg-alt:color-mix(in srgb, var(--color-gold) 30%, #F5F1E7);
  /* --color-success was left at #4A463C (brown) when this skin was converted
     to blue — every other token was changed, this one was missed. It made
     "done"/"ok" states render brown against an otherwise blue dashboard, and
     on the Health Check a PASSING row came out brown while a FAILING one came
     out blue. Owner 2026-07-30: normal blue, warning red, no brown. */
  --color-star:#499cd0;  --color-success:#3a7da6;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 28%, transparent);
  --fx:#38b6ff; --fx-soft:#7dd3ff; --fx-bright:#bfeaff; --fx-ink:#FFFFFF;
  --color-heading:var(--color-navy); --fx-orange:#38b6ff;
}

/* ---- Forest Ink ---- (warm ivory base + pine-green accent; a PRO skin) */
[data-theme="forest-ink"] {
  --sm-gold:#3E8E7E;     --sm-gold-dark:#2F6B5E;
  --sm-orange:#3E8E7E;   --sm-orange-dark:#2F6B5E;
  --color-gold:#3E8E7E;  --color-gold-dark:#2F6B5E;
  --color-amber:#3E8E7E; --color-amber-dark:#2F6B5E;
  --color-teal:#3E8E7E;  --color-teal-dark:#2F6B5E;
  --color-navy:#3E8E7E;  --color-navy-deep:#2F6B5E; --color-navy-darker:#1E463C;
  --klein:#3E8E7E;       --klein-deep:#2F6B5E;
  --color-ink:#262420;   --color-muted:#6F6A60;
  --color-line:#E6DFCC;  --tint-line:#DAD2BC; --tint-cool:#EEE7D6;
  --color-surface:#FFFFFF; --color-bg:color-mix(in srgb, var(--color-gold) 18%, #F5F1E7); --color-bg-alt:color-mix(in srgb, var(--color-gold) 30%, #F5F1E7);
  --color-star:#3E8E7E;  --color-success:#4A463C;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 28%, transparent);
  --fx:#4FBFA3; --fx-soft:#84d9c4; --fx-bright:#c3efe4; --fx-ink:#FFFFFF;
  --color-heading:var(--color-navy); --fx-orange:#4FBFA3;
}

/* ---- Plum Ink ---- (warm ivory base + muted-violet accent; a PRO skin) */
[data-theme="plum-ink"] {
  --sm-gold:#8A5A83;     --sm-gold-dark:#6E4768;
  --sm-orange:#8A5A83;   --sm-orange-dark:#6E4768;
  --color-gold:#8A5A83;  --color-gold-dark:#6E4768;
  --color-amber:#8A5A83; --color-amber-dark:#6E4768;
  --color-teal:#8A5A83;  --color-teal-dark:#6E4768;
  --color-navy:#8A5A83;  --color-navy-deep:#6E4768; --color-navy-darker:#4A2F46;
  --klein:#8A5A83;       --klein-deep:#6E4768;
  --color-ink:#262420;   --color-muted:#6F6A60;
  --color-line:#E6DFCC;  --tint-line:#DAD2BC; --tint-cool:#EEE7D6;
  --color-surface:#FFFFFF; --color-bg:color-mix(in srgb, var(--color-gold) 18%, #F5F1E7); --color-bg-alt:color-mix(in srgb, var(--color-gold) 30%, #F5F1E7);
  --color-star:#8A5A83;  --color-success:#4A463C;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 28%, transparent);
  --fx:#B07CA6; --fx-soft:#d0a8c9; --fx-bright:#e8d2e4; --fx-ink:#FFFFFF;
  --color-heading:var(--color-navy); --fx-orange:#B07CA6;
}

/* ---- Ember Ink ---- (warm ivory base + terracotta-orange accent; a PRO skin) */
[data-theme="ember-ink"] {
  --sm-gold:#C96A2E;     --sm-gold-dark:#A0521F;
  --sm-orange:#C96A2E;   --sm-orange-dark:#A0521F;
  --color-gold:#C96A2E;  --color-gold-dark:#A0521F;
  --color-amber:#C96A2E; --color-amber-dark:#A0521F;
  --color-teal:#C96A2E;  --color-teal-dark:#A0521F;
  --color-navy:#C96A2E;  --color-navy-deep:#A0521F; --color-navy-darker:#6E3813;
  --klein:#C96A2E;       --klein-deep:#A0521F;
  --color-ink:#262420;   --color-muted:#6F6A60;
  --color-line:#E6DFCC;  --tint-line:#DAD2BC; --tint-cool:#EEE7D6;
  --color-surface:#FFFFFF; --color-bg:color-mix(in srgb, var(--color-gold) 18%, #F5F1E7); --color-bg-alt:color-mix(in srgb, var(--color-gold) 30%, #F5F1E7);
  --color-star:#C96A2E;  --color-success:#4A463C;
  --ring:0 0 0 3px color-mix(in srgb, var(--color-gold) 28%, transparent);
  --fx:#EE8B3F; --fx-soft:#f5b078; --fx-bright:#f9d5b5; --fx-ink:#FFFFFF;
  --color-heading:var(--color-navy); --fx-orange:#EE8B3F;
}

/* ---- shared across ALL skins (token-driven). Every page carries a data-theme
   in the elegant-alternate family, so [data-theme] matches them all (same
   specificity as listing each). A new skin needs only its own token block +
   registry entry — nothing to add here. ---- */
[data-theme] body { background: var(--color-bg); }

[data-theme] .brand .brand-mark { background: var(--color-gold); color: var(--color-bg); }

[data-theme] .btn--primary,
[data-theme] .nav-phone { color: var(--color-bg); }

/* same gold-on-dark pairing as .btn--primary: ivory numbers on filled wizard dots */
[data-theme] .wiz-dot.is-active { color: var(--color-bg); }

[data-theme] .pt-ico { background: linear-gradient(135deg, var(--color-gold), var(--color-gold-dark)); color: var(--color-bg); }

[data-theme] .hero {
  background: linear-gradient(135deg, color-mix(in srgb, var(--color-navy-darker) 62%, transparent), color-mix(in srgb, var(--color-navy-deep) 34%, transparent)), var(--hero-img) center/cover no-repeat;
  background-color: var(--color-navy-darker);
}

[data-theme] .check-group .check-item:has(input:checked) {
  border-color: var(--color-gold); background: color-mix(in srgb, var(--color-gold) 14%, var(--color-surface)); color: var(--color-ink);
}
[data-theme] .badge--open { background: color-mix(in srgb, var(--color-gold) 16%, transparent); color: var(--color-gold-dark); }
[data-theme] .badge--done { background: color-mix(in srgb, var(--color-success) 18%, transparent); color: var(--color-success); }

/* =========================================================================
   22. SKIN FLAIR (highlight + glow) — shared rules, per-skin highlight color
   Each skin defines the --fx / --fx-soft / --fx-bright / --fx-ink trio (§1 +
   §21); these rules apply that highlight identically to every skin:
     Beach Vibe = orange · Ivory & Ink = Klein blue · Oil-Rubbed Bronze = gold
   ========================================================================= */

/* highlight accents */
[data-theme] .hero h1 .accent {
  color: var(--fx-soft);
  text-shadow: 0 0 30px color-mix(in srgb, var(--fx) 72%, transparent);
}
/* marker-highlight headline: a gold stroke PAINTS ITSELF behind each accent
   word on load, one word after the other, and STAYS — the text flips to the
   skin's on-gold ink once its stroke lands. Unmissable and permanent, not a
   passing shimmer. Token-only (fx / fx-soft / fx-ink).
   box-decoration-break keeps the stroke intact when a phrase wraps. */
[data-theme] .hero h1 .accent {
  padding: 0 0.12em;
  margin: 0;
  border-radius: var(--radius);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  text-shadow: none;
  /* ONE gradient, 3× the word's width, with a bright shimmer band parked in
     the middle third: at rest only gold shows; sliding background-position-x
     sweeps the band across the letters, then it hides off-edge again. */
  background-repeat: no-repeat;
  background-image: linear-gradient(105deg,
    var(--fx) 0%, var(--fx) 46%,
    color-mix(in srgb, var(--fx-bright) 55%, white) 50%,
    var(--fx) 54%, var(--fx) 100%);
  /* band sized to the LETTERFORMS in em (cap-top → below descenders), not to
     the font's ~1.4em metric box: 1.08em band inside 1.2em lines leaves a
     guaranteed 0.12em gap between wrapped lines at any font size */
  background-size: 0 1.08em;
  background-position: left 0.26em;
  /* paint once, then a shimmer sweep re-runs EVERY 3s, forever */
  animation: hero-marker-paint 0.7s ease-out both,
             hero-marker-shimmer 3s linear 1.5s infinite;
}
/* stagger both animations word by word (paint delay, shimmer delay) */
[data-theme] .hero h1 .accent:nth-of-type(2) { animation-delay: 0.35s, 1.85s; }
[data-theme] .hero h1 .accent:nth-of-type(3) { animation-delay: 0.7s, 2.2s; }
[data-theme] .hero h1 .accent:nth-of-type(4) { animation-delay: 1.05s, 2.55s; }
[data-theme] .hero h1 .accent:nth-of-type(5) { animation-delay: 1.4s, 2.9s; }
/* alternate-word headlines can have more highlighted words — keep both
   sequences sequential past the original five */
[data-theme] .hero h1 .accent:nth-of-type(6) { animation-delay: 1.75s, 3.25s; }
[data-theme] .hero h1 .accent:nth-of-type(7) { animation-delay: 2.1s, 3.6s; }
[data-theme] .hero h1 .accent:nth-of-type(8) { animation-delay: 2.45s, 3.95s; }
[data-theme] .hero h1 .accent:nth-of-type(9) { animation-delay: 2.8s, 4.3s; }
/* paint: width grows to the full 300% canvas (band still parked off-edge),
   text color flips to on-gold ink when the stroke lands */
@keyframes hero-marker-paint {
  0%        { background-size: 0 1.08em;    color: var(--fx-soft); }
  60%       { background-size: 300% 1.08em; color: var(--fx-soft); }
  85%, 100% { background-size: 300% 1.08em; color: var(--fx-ink); }
}
/* shimmer: the bright band sweeps across in the first ~0.7s of each 3s cycle,
   then rests hidden — both loop ends show plain gold, so the restart is
   invisible */
@keyframes hero-marker-shimmer {
  0%        { background-position-x: 0%; }
  23%       { background-position-x: 100%; }
  100%      { background-position-x: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  [data-theme] .hero h1 .accent { animation: none; background-size: 300% 1.08em; color: var(--fx-ink); }
}
[data-theme] .hero-trust .ht-num {
  color: var(--fx-soft);
  text-shadow: 0 0 16px color-mix(in srgb, var(--fx) 45%, transparent);
}
[data-theme] .brand .brand-amp { color: var(--fx); }


/* highlight glow in the hero (radial wash, top-right).
   Overlay kept light so the real jobsite photo reads clearly — the darker
   top-left corner plus the text-shadow below keep the headline legible. */
[data-theme] .hero {
  background:
    radial-gradient(72% 95% at 78% 14%, color-mix(in srgb, var(--fx) 18%, transparent), transparent 60%),
    linear-gradient(135deg, color-mix(in srgb, var(--color-navy-darker) 62%, transparent), color-mix(in srgb, var(--color-navy-deep) 34%, transparent)),
    var(--hero-img) center/cover no-repeat;
  background-color: var(--color-navy-darker);
}
[data-theme] .hero h1,
[data-theme] .hero .lead { text-shadow: 0 2px 14px rgba(0,0,0,0.55); }

/* glowing primary CTA — shared by EVERY pill (header nav-phone, hero
   Call-Now phone-pill, form Send Request, Bigger-job link) so all pills on
   the page carry the identical gold ring + glow + hover lift */
[data-theme] .btn--primary,
[data-theme] .nav-phone,
[data-theme] .phone-pill {
  border-color: color-mix(in srgb, var(--fx) 70%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fx) 70%, transparent),
              0 10px 30px color-mix(in srgb, var(--fx) 40%, transparent);
}
[data-theme] .btn--primary:hover,
[data-theme] .nav-phone:hover,
[data-theme] .phone-pill:hover {
  border-color: var(--fx);
  box-shadow: var(--btn-deboss), 0 0 0 1px var(--fx);
  transform: translateY(1px);
}

/* brand mark + hero card glow */
[data-theme] .brand .brand-mark {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fx) 40%, transparent),
              0 0 20px color-mix(in srgb, var(--fx) 55%, transparent);
}
/* on hover the mark's gold ring wakes up — same brightening as .btn--primary */
[data-theme] .brand:hover .brand-mark,
[data-theme] .brand:focus-visible .brand-mark {
  box-shadow: 0 0 0 1px var(--fx),
              0 0 26px color-mix(in srgb, var(--fx) 75%, transparent);
}
/* Every hero box wears the SAME gold border + glow as the top-left date pill
   (2px gold ring at 70%, plus the ambient gold glow). One rule = uniform. */
[data-theme] .hero-card,
[data-theme] .hero-box {
  border: 2px solid color-mix(in srgb, var(--fx) 70%, transparent);
  box-shadow: var(--shadow-lg), 0 0 52px color-mix(in srgb, var(--fx) 20%, transparent);
}

/* Right-column box variants (owner styles 2026-07-04) ---------------------
   --glass: the SAME recipe as .hero-pill (the date box in the left column) —
   translucent brown over a blur of the photo, gold border, ivory text.
   --clear: fully transparent, no card at all, bold ivory text. */
[data-theme] .hero-box--glass {
  background:
    linear-gradient(135deg, rgba(255,255,255,0.08), transparent 45%),
    color-mix(in srgb, var(--color-navy) 62%, transparent);
  -webkit-backdrop-filter: blur(7px) saturate(140%);
  backdrop-filter: blur(7px) saturate(140%);
  border: 2px solid color-mix(in srgb, var(--fx) 70%, transparent);
  border-radius: clamp(18px, 3vw, 28px);
  color: var(--color-white);
}
[data-theme] .hero-box--glass h3 { color: var(--color-white); }
[data-theme] .hero-box--glass .hero-review p { color: rgba(255,255,255,0.92); }
[data-theme] .hero-box--glass .hero-review cite { color: var(--color-white); }
[data-theme] .hero-box--glass .hero-review-stars { color: var(--fx); }
[data-theme] .hero-box--glass .field label { color: var(--color-white); }
[data-theme] .hero-box--glass .reply-promise { color: rgba(255,255,255,0.9); }

[data-theme] .hero-box--clear {
  /* darkened rounded panel — keeps the gold border + glow inherited from
     [data-theme] .hero-box above, so it matches every other box */
  background:
    linear-gradient(135deg, rgba(255,255,255,0.06), transparent 45%),
    color-mix(in srgb, var(--color-navy-darker) 58%, transparent);
  border-radius: clamp(18px, 3vw, 28px);
  -webkit-backdrop-filter: blur(6px) saturate(130%);
  backdrop-filter: blur(6px) saturate(130%);
  color: var(--color-white);
}
[data-theme] .hero-box--clear h3 { color: var(--color-white); }
[data-theme] .hero-box--clear .sub { color: rgba(255,255,255,0.88); }
[data-theme] .hero-box--clear .gt-text strong { color: var(--color-white); font-weight: 800; }
[data-theme] .hero-box--clear .gt-text span { color: rgba(255,255,255,0.92); font-weight: 600; }
/* Same gold divider as .hero-pill-rule (under the date), everywhere a row
   separator or section divider appears in the right column. */
[data-theme] .hero-box--clear .gt-row,
[data-theme] .gt-row,
[data-theme] .hero-trust { border-top-color: var(--color-gold); border-top-width: 2px; }

/* service / why cards: highlight edge + glow on hover */
[data-theme] .card { transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition); }
[data-theme] .card:hover {
  border-color: color-mix(in srgb, var(--fx) 70%, transparent);
  box-shadow: var(--btn-deboss);
  transform: translateY(1px);
}

/* ---- icon "logotypes": high-contrast highlight + glow ---- */
/* contact-tile .ci-ico's private navy-tile recipe DELETED 2026-08-01
   (owner: "every single one of the icons... same CSS as the one shown" —
   the service-card chip). It now rides the ONE unified chip recipe below
   with everything else. */
/* review avatar: highlight disc, contrast initial, glow ring */
[data-theme] .review .avatar {
  background: linear-gradient(135deg, var(--fx-soft), var(--fx));
  color: var(--fx-ink);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--fx) 38%, transparent),
              0 0 16px color-mix(in srgb, var(--fx) 28%, transparent);
}
/* gallery slide-number badge: same brand color as the "Ka" mark / primary button */
[data-theme] .gal-num {
  background: var(--color-gold);
  color: var(--color-bg);
  box-shadow: 0 0 14px color-mix(in srgb, var(--color-gold) 35%, transparent);
}
/* gallery prev/next button: same brand color as the "Ka" mark / primary button */
[data-theme] .gal-btn {
  background: var(--color-gold);
  color: var(--color-bg);
  border-color: color-mix(in srgb, var(--color-gold) 60%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-gold) 45%, transparent),
              0 6px 18px color-mix(in srgb, var(--color-gold) 32%, transparent);
}
[data-theme] .gal-btn:hover {
  background: var(--color-gold-dark);
  border-color: var(--color-gold);
  transform: translateY(-1px);
  box-shadow: 0 0 0 1px var(--color-gold), 0 10px 26px color-mix(in srgb, var(--color-gold) 48%, transparent);
}

/* =========================================================================
   23. QUOTE FORM EXTRAS (lead-gen)
   ========================================================================= */
/* honeypot — hidden from people, visible to bots (no layout/scroll impact) */
.hp-field { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ZIP service-area note */
.svc-note { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.35rem; min-height: 1.15em; }
.svc-note.is-ok { color: var(--color-success); font-weight: 600; }

/* emergency call-now shortcut (revealed when "Emergency" timeframe is picked) */
.emergency-call {
  display: flex; align-items: center; gap: 0.5rem; margin-top: 0.6rem; padding: 0.6rem 0.9rem;
  border-radius: var(--radius-sm); font-family: var(--font-head); font-weight: 700; font-size: var(--fs-lead);
  background: color-mix(in srgb, var(--color-amber) 14%, var(--color-surface));
  color: var(--color-amber-dark);
  border: 1px solid color-mix(in srgb, var(--color-amber) 40%, transparent);
}
.emergency-call:hover { background: color-mix(in srgb, var(--color-amber) 24%, var(--color-surface)); }

/* photo drop zone */
.field .file-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.file-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.35rem;
  text-align: center; padding: 1.4rem 1rem; cursor: pointer;
  border: 2px dashed var(--color-line); border-radius: var(--radius-sm);
  background: var(--color-bg); color: var(--color-muted);
  transition: border-color var(--transition), background var(--transition), color var(--transition);
}
.file-drop:hover, .file-drop.is-drag { border-color: var(--color-amber); background: color-mix(in srgb, var(--color-amber) 7%, var(--color-bg)); color: var(--color-ink); }
.file-drop svg { width: 26px; height: 26px; color: var(--color-amber); }
.file-drop-text { font-weight: 500; }
.file-drop strong { color: var(--color-amber-dark); }
.file-drop-hint { font-size: var(--fs-small); color: var(--color-muted); }
.file-list { list-style: none; margin: 0.6rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.file-list li {
  display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--fs-small);
  background: var(--color-bg-alt); border: 1px solid var(--color-line); border-radius: var(--radius-pill);
  padding: 0.3rem 0.75rem; color: var(--color-ink);
}
.file-remove { background: none; border: 0; padding: 0 0.1rem; cursor: pointer; color: var(--err-fg); font-weight: 700; font-size: 0.9em; line-height: 1; }
.file-list li.file-note { background: none; border: 0; color: var(--err-fg); }

/* trust strip above the submit button */
.trust-strip {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem 1.2rem;
  margin: 0.4rem 0 1.1rem; font-size: var(--fs-small); color: var(--color-muted);
}
.trust-strip span { display: inline-flex; align-items: center; gap: 0.35rem; }
.trust-strip strong { color: var(--color-ink); }
/* compact variant inside the hero quote card */
.trust-strip--form { gap: 0.3rem 0.9rem; margin: 0.2rem 0 0.5rem; font-size: var(--fs-small); }

/* the reply-speed promise, shown BEFORE submitting */
/* left-aligned, not centered (2026-07-31, owner: "visually out of balance,
   not a nice flow top to bottom"). Every label, hint and input in this form
   sits flush left; this single sentence jumping to centre — between a
   left-aligned textarea and left-aligned buttons — was the actual break in
   the flow, not the amount of space around it. */
.reply-promise {
  text-align: left; font-size: var(--fs-small); line-height: var(--lh-tight); color: var(--color-success);
  font-weight: 600; margin: 0 0 var(--form-gap);
}
/* Simple Contact Request footer buttons: "Send Request" + "Add details" split
   into two equal halves (stacks on very narrow widths). */
.hero-form-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.hero-form-actions > * { flex: 1 1 45%; white-space: nowrap; }

/* secondary path to the detailed quote form — a real button, not a footnote.
   white-space:normal overrides .btn's nowrap: the long label must wrap, or its
   min-content width blows the hero column open on ~768-800px viewports. */
.expert-cta { margin-top: 0.8rem; font-size: var(--fs-body); padding: 0.7rem 1rem; white-space: normal; }

/* hero card choose-your-path (simple / detailed / live chat / call now) */
.quote-choice { display: grid; gap: 0.7rem; }
.quote-choice .sub { margin-bottom: 0.3rem; }
.qc-option {
  display: block; width: 100%; text-align: left; cursor: pointer; font: inherit;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-radius: var(--radius-sm); padding: 1rem 1.1rem; text-decoration: none;
  transition: border-color var(--transition), background var(--transition);
}
.qc-option:hover { border-color: var(--color-gold); background: color-mix(in srgb, var(--color-gold) 7%, var(--color-surface)); }
.qc-title { display: block; font-family: var(--font-head); font-weight: 700; color: var(--color-navy); }
.qc-hint { display: block; font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.2rem; }
.qc-call-label { text-align: center; font-size: var(--fs-small); color: var(--color-muted); margin: 0.5rem 0 0.5rem; }
.qc-call { text-align: center; margin: 0; }

/* phone pill — same hover language as .btn (lift + shadow). Every accent
   token (gold/amber/klein/teal) collapses to the same bronze under this
   skin by design, so "its own color" has to come from --color-navy (the
   skin's dark ink tone) — the one token that actually reads as a distinct
   color next to the gold Free-Quote button, not another shade of it. */
.phone-pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; white-space: nowrap;
  box-sizing: border-box;
  /* size from the control-scale tokens ONLY — same as .btn / .nav-phone */
  height: var(--control-h); padding: 0 var(--control-pad-x); border-radius: var(--radius-pill);
  background: var(--color-navy); border: 1px solid var(--color-navy); color: var(--color-white);
  font-family: var(--font-head); font-weight: 700; font-size: var(--control-fs);
  transition: var(--transition);
}
.phone-pill svg { width: 1.05em; height: 1.05em; flex: none; }
.phone-pill:hover { transform: translateY(-2px); background: var(--color-navy-deep); border-color: var(--color-navy-deep); box-shadow: var(--shadow-md); }
.phone-pill:active { transform: translateY(0); }
.qc-back { background: none; border: 0; padding: 0; margin: 0 0 0.6rem; cursor: pointer; font: inherit; font-size: var(--fs-small); color: var(--color-muted); }
.qc-back:hover { color: var(--color-ink); }

/* homepage contact section: panel that hands off to the wizard page */
.wizard-entry h3 { font-family: var(--font-head); font-size: var(--fs-h3); color: var(--color-navy); margin-bottom: 0.5rem; }
.wizard-entry > p { color: var(--color-muted); margin-bottom: 1rem; }
.wizard-entry .next-steps { margin-bottom: 1.2rem; }
.wizard-entry .trust-strip { margin-top: 1rem; }

/* ---- Detailed Estimate wizard (detailed-quote.php) ---- */
.wiz-wrap { max-width: 640px; margin: 0 auto; }
.wiz-progress { counter-reset: wiz; display: flex; gap: 0.7rem; justify-content: center; margin: 0.4rem 0 0.3rem; }
.wiz-dot {
  counter-increment: wiz;
  width: 36px; height: 36px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-bg-alt); color: var(--color-muted);
  border: 1.5px solid var(--color-line);
  font-family: var(--font-head); font-weight: 700; font-size: var(--fs-lead);
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.wiz-dot::before { content: counter(wiz); }
.wiz-dot.is-active { background: var(--color-gold); color: var(--color-navy); border-color: var(--color-gold); }
/* margin-bottom values below use --form-gap (2026-07-31) — same rhythm
   token as every field in the form itself, so the space above the question
   matches the space between its own fields, instead of the two mismatched
   leftover values (1.2rem / 0.9rem) this used to carry. */
.wiz-count { text-align: center; font-size: var(--fs-small); color: var(--color-muted); margin-bottom: var(--form-gap); }
.wiz-step { display: none; border: 0; padding: 0; margin: 0; }
.wiz-step.is-active { display: block; }
.wiz-q { font-family: var(--font-head); font-weight: 700; font-size: var(--fs-h3); color: var(--color-navy); margin-bottom: var(--form-gap); padding: 0; }
.wiz-options { display: grid; gap: 0.55rem; }
.wiz-option {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.85rem 1rem; border: 1.5px solid var(--color-line); border-radius: var(--radius-sm);
  background: var(--color-surface); cursor: pointer; font-weight: 500; color: var(--color-ink);
  transition: border-color var(--transition), background var(--transition);
}
.wiz-option:hover { border-color: var(--color-gold); }
.wiz-option input { accent-color: var(--color-gold); width: 18px; height: 18px; margin: 0; flex: none; }
.wiz-option:has(input:checked) {
  border-color: var(--color-gold);
  background: color-mix(in srgb, var(--color-gold) 10%, var(--color-surface));
  font-weight: 600;
}
.wiz-err { min-height: 1.2em; font-size: var(--fs-small); color: var(--err-fg); margin-top: 0.5rem; }
.wiz-err:not(:empty) {
  font-weight: 600;
  background: color-mix(in srgb, var(--err-fg) 8%, var(--color-surface));
  border: 1px solid color-mix(in srgb, var(--err-fg) 35%, transparent);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.9rem;
}
.wiz-nav { display: flex; gap: 0.8rem; justify-content: space-between; margin-top: 1.4rem; }
.wiz-nav .btn { flex: 1; }
/* .btn's display would beat the [hidden] attribute — wizard.js drives which
   button shows per step (Next on steps 1-3, Send My Request only on the last) */
.wiz-nav .btn[hidden] { display: none; }

/* Box 5: testimonials cycle under the hero quote form (from content/reviews.csv).
   Slides stack; only .is-active shows. Height follows the tallest slide so the
   card doesn't jump as reviews rotate. */
.hero-reviews { position: relative; margin-top: 1.1rem; border-top: 1px solid var(--color-line);
  display: grid; }
.hero-review {
  grid-area: 1 / 1;                 /* stack every slide in the same cell */
  margin: 0; padding: 0.8rem 1rem 0.7rem;
  font-size: var(--fs-small); color: var(--color-muted);
  opacity: 0; visibility: hidden; transition: opacity 0.4s ease;
}
.hero-review.is-active { opacity: 1; visibility: visible; }
.hero-review-head { display: flex; align-items: center; gap: 0.55rem; }
/* thumbnail — placeholder for a future reviewer photo (shows the initial) */
.hero-review-thumb { flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--color-bg-alt); color: var(--color-navy);
  display: grid; place-items: center; font-family: var(--font-head); font-weight: 700; font-size: var(--fs-body); }
.hero-review-stars { color: var(--color-star); letter-spacing: 0.1em; font-size: var(--fs-body); }
.hero-review p { margin: 0.4rem 0 0.3rem; color: var(--color-ink); font-style: italic; line-height: 1.5; }
.hero-review cite { font-style: normal; font-weight: 600; color: var(--color-navy); }

/* "what happens next" steps below the button */
.next-steps { list-style: none; counter-reset: ns; margin: 1.1rem 0 0; padding: 0; display: grid; gap: 0.55rem; }
/* centered variant — the block of steps is centered in the card, each row's
   number + text still reading left-to-right within it. */
.next-steps--center { width: fit-content; margin-inline: auto; }
.next-steps li { counter-increment: ns; display: flex; align-items: flex-start; gap: 0.6rem; font-size: var(--fs-lead); color: var(--color-ink); }
.next-steps li::before {
  content: counter(ns); flex: none; width: 24px; height: 24px; margin-top: 1px; border-radius: 50%;
  display: grid; place-items: center; font-family: var(--font-head); font-weight: 700; font-size: var(--fs-small);
  background: var(--color-amber); color: var(--color-white);
}

/* =========================================================================
   24. SKIN FLAIR — highlight polish for the lead-gen form extras (§23)
   ========================================================================= */
[data-theme] .file-drop:hover,
[data-theme] .file-drop.is-drag { border-color: var(--fx); background: color-mix(in srgb, var(--fx) 8%, var(--color-bg)); }
[data-theme] .file-drop svg,
[data-theme] .file-drop strong { color: var(--fx); }
[data-theme] .next-steps li::before {
  background: var(--fx); color: var(--fx-ink);
  box-shadow: 0 0 10px color-mix(in srgb, var(--fx) 30%, transparent);
}
[data-theme] .emergency-call {
  background: color-mix(in srgb, var(--fx) 14%, var(--color-surface));
  color: var(--fx);
  border-color: color-mix(in srgb, var(--fx) 45%, transparent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--fx) 18%, transparent);
}
[data-theme] .emergency-call:hover { background: color-mix(in srgb, var(--fx) 22%, var(--color-surface)); }
/* footer contact icons + link hover: brighter tint for contrast on the dark footer */
/* li > svg — see the base .footer-contact rule's comment; this skin-layer
   copy was THE rule painting the pill's phone glyph orange (--fx-soft). */
[data-theme] .footer-contact li > svg { color: var(--fx-soft); }
[data-theme] .site-footer a:hover { color: var(--fx-soft); }

/* =========================================================================
   25. ACTIVITY FEED (admin/inbox.php's own feed; admin/client.php's
   per-client slice of the same feed, via api/billing.php?action=client_activity)
   ========================================================================= */
.act-card { border: 1px solid var(--color-line); border-left: 4px solid var(--color-gold); border-radius: var(--radius-sm);
  padding: 0.9rem 1.1rem; margin-bottom: 0.8rem; background: var(--color-surface); }
.act-card.is-new { border-left-color: var(--color-navy); background: var(--color-bg-alt); }
.act-top { display: flex; flex-wrap: wrap; gap: 0.4rem 0.9rem; align-items: baseline; justify-content: space-between; }
.act-client { font-family: var(--font-head); font-weight: 700; color: var(--color-navy); }
.act-date { font-size: var(--fs-small); color: var(--color-muted); }
.act-summary { margin: 0.4rem 0 0.2rem; font-size: var(--fs-lead); }
.act-detail { margin-top: 0.4rem; padding: 0.6rem 0.8rem; background: var(--color-bg); border-radius: 6px; font-size: var(--fs-lead); white-space: pre-wrap; }
.act-links { margin-top: 0.5rem; }

/* =========================================================================
   26. PRO PAGE DESIGN STANDARD
   (documented for the owner in Help & Docs → "Design standard")
   Every PRO page is built from stacked .panel blocks:
   - .panel--fold: the WHOLE header row is the open/close control — click
     anywhere on it (chevron shows the state; clicks on real controls inside
     the head — buttons, links — never toggle). Behavior: js/main.js
     initPanelFolds(). Markup: role="button" tabindex="0" aria-expanded +
     title/aria-label on the .panel-head.
   - Every input/select/textarea lives inside a .field (label above,
     control below) and spans its column's full width (§7 sets width:100%)
     — NEVER inline-flexed next to a button.
   - .form-actions: submit/action buttons ALWAYS go on their own line BELOW
     the fields, left-aligned; the inline status message rides in the same
     row after the buttons.
   ========================================================================= */
.panel--fold > .panel-head {
  cursor: pointer; user-select: none;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
/* lift + gold glow — the ONE site-wide "clickable card" language (matches
   .portal-band-head / .cat-band-head / .act-card), never a flat sand fill.
   :hover gated to hover-capable pointers: on touch, tapping a fold header to
   open it would otherwise leave that header stuck looking "muddy"/pressed
   until an unrelated tap elsewhere (owner-reported 2026-07-20, quick-invoice). */
@media (hover: hover) {
  .panel--fold > .panel-head:hover { transform: translateY(-2px); border-color: var(--color-gold); box-shadow: 0 12px 26px rgba(20,32,50,.13); }
}
.panel--fold > .panel-head:active { transform: translateY(0); }
.panel--fold > .panel-head:focus-visible { outline: none; box-shadow: var(--ring); }
/* first head child stretches so buttons + chevron cluster on the right
   (overrides the head's space-between, which would strand a lone button
   in the middle of the row) */
.panel--fold > .panel-head > :first-child { flex: 1 1 auto; min-width: 0; }
.panel--fold > .panel-head::after {
  content: "▾"; flex: none; color: var(--color-muted);
  font-size: var(--fs-lead); line-height: 1;
  transition: transform var(--transition);
}
.panel--fold.is-collapsed > .panel-head::after { transform: rotate(-90deg); }
/* collapsed: the head's bottom border would double the panel's own edge */
.panel.is-collapsed > .panel-head { border-bottom: none; }
/* open: a deliberate gold edge + chevron tint marks the active section by
   DESIGN, not by relying on a hover/press state — a fold header used to look
   identical open or closed except while actively pressed, which read as
   broken once the touch :hover fix above stopped it from staying "stuck". */
.panel--fold:not(.is-collapsed) > .panel-head { border-bottom-color: var(--color-amber); }
.panel--fold:not(.is-collapsed) > .panel-head::after { color: var(--color-amber-dark); }

.form-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 0.6rem; margin-top: 0.9rem; }
.form-actions .inline-status { margin-left: 0.3rem; }

/* Email / Call / Text contact pill row — shared by admin/lead.php and
   admin/client.php (moved here from lead.php's private style block, which
   client.php borrowed the class name from without ever having the styles).
   GRID, not flex-wrap (fixed 2026-07-31): flex-wrap orphaned the last pill
   onto its own line at arbitrary widths — the exact wrap-orphan pattern the
   platform bans on control rows. Designed breakpoints instead: 2 columns,
   then 3, then all 5 in one row. max-content columns keep every pill at its
   intrinsic width; the column count also holds for client.php's JS-filled
   copy, which renders a variable number of pills. */
.lead-contact { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); gap: 0.8rem 1rem; margin-top: 0.4rem; }
@media (min-width: 620px) { .lead-contact { grid-template-columns: repeat(3, minmax(0, max-content)); } }
@media (min-width: 900px) { .lead-contact { grid-template-columns: repeat(5, minmax(0, max-content)); } }

/* KPI tiles — the number + label blocks. Moved here from analytics.php's
   private <style> block 2026-07-30 (same reason as .lead-contact above: the
   dashboard needed the identical component and would otherwise have grown a
   second, drifting copy). analytics.php and dashboard.php now share ONE
   definition. .an-kpi--link is the dashboard's variant: the whole tile is an
   <a>, so it takes the site-wide clickable-card lift on hover. */
.an-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.5rem; margin-bottom: 0.7rem; }
.an-kpi { background: var(--color-surface); border: 1.5px solid var(--color-line); border-radius: var(--radius);
  box-shadow: var(--tile-shadow); padding: 0.55rem 0.75rem; }
.an-kpi .k { font-family: var(--font-head); font-weight: 600; font-size: var(--fs-small); text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--color-muted); }
.an-kpi .v { font-family: var(--font-head); font-weight: 800; font-size: var(--fs-h3); color: var(--color-navy); line-height: 1.1; margin-top: 0.15rem; }
.an-kpi .s { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.1rem; }
a.an-kpi--link { display: block; text-decoration: none; transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition); }
@media (hover: hover) {
  a.an-kpi--link:hover { transform: translateY(-2px); border-color: var(--color-gold); box-shadow: 0 12px 26px rgba(20,32,50,.13); }
}
a.an-kpi--link:focus-visible { outline: none; box-shadow: var(--ring); }
/* a KPI worth acting on: the number takes the accent, not a new colour */
/* an alerting KPI number is RED, same rule as the rows above — it used
   --color-amber-dark, which the sky-ink skin resolves to blue, so a number
   that meant "look at this" was indistinguishable from a normal one */
.an-kpi--alert .v { color: var(--err-fg); }

/* ── WEBSITE LEADS: work-queue list (rebuilt 2026-07-30) ──────────────────
   Leads left the two-band record-list standard on purpose — see the note at
   the top of js/leads.js. It is a queue to be worked down, not a reference
   list to be browsed, so it needs a filter row and rows you can act on
   without opening them. Colours are the existing tokens only. */
.lead-filters { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.8rem; }
.lead-filter {
  border: 1.5px solid var(--color-line); background: var(--color-surface);
  color: var(--color-navy); font-family: var(--font-head); font-weight: 600;
  font-size: var(--fs-small); border-radius: var(--radius-pill);
  padding: 0.5rem 0.9rem; cursor: pointer; min-height: 40px;
  transition: border-color var(--transition), background var(--transition), transform var(--transition);
}
@media (hover: hover) { .lead-filter:hover { border-color: var(--color-gold); transform: translateY(-1px); } }
.lead-filter:focus-visible { outline: none; box-shadow: var(--ring); }
.lead-filter.is-on { background: var(--color-navy); color: var(--color-white); border-color: var(--color-navy); }
.lead-filter .lf-n { opacity: 0.7; margin-left: 0.15rem; }

.leads-list { display: grid; gap: 0.5rem; }
.lead-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 1rem;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  /* the left edge carries urgency — neutral until something is actually late */
  border-left: 4px solid var(--color-line);
  border-radius: var(--radius); box-shadow: var(--tile-shadow);
  padding: 0.7rem 0.85rem;
}
/* a lead going stale warms toward the alert colour rather than to gold —
   --color-gold is blue in sky-ink, which read as "fine" when it means
   "this person has been waiting" */
.lead-row.is-warm    { border-left-color: color-mix(in srgb, var(--err-fg) 45%, var(--color-line)); }
.lead-row.is-overdue { border-left-color: var(--err-fg); }
/* REBUILT 2026-07-31. The first version gave every row up to four filled
   buttons (Call/Text/Open/Archive) and introduced a dark-blue fill that
   existed nowhere else on the site. Twelve leads meant ~28 competing controls
   and a colour nobody authorised.
   Now it follows the house pattern of the other record lists: the NAME is the
   link, the PHONE is the link, and Archive is the one quiet control. No new
   colours — every value below is an existing token in its existing role. */
.lr-main { flex: 1 1 240px; min-width: 0; }
.lr-top  { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
/* the name is the way in, styled like every other list's row link */
a.lr-name { font-family: var(--font-head); font-weight: 700; color: var(--color-navy); text-decoration: none; }
a.lr-name:hover { text-decoration: underline; }
a.lr-name:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }
.lr-age  { font-size: var(--fs-small); color: var(--color-muted); }
.lr-age.is-overdue { color: var(--err-fg); font-weight: 700; }
.lr-meta { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.1rem; }
/* the number is the call — no separate button for it */
.lr-tel { color: var(--color-navy); font-weight: 600; text-decoration: none; }
.lr-tel:hover { text-decoration: underline; }
/* status as plain words: the coloured left edge already says "urgent", and a
   filled badge would state it twice */
.lr-tags { font-size: var(--fs-small); color: var(--color-muted); margin-top: 0.25rem; }

/* Archive is housekeeping, not the work — quiet, and it must never draw the
   eye away from the names. Still 40px tall for touch. */
.lr-arch {
  flex: 0 0 auto; align-self: center;
  min-height: 40px; padding: 0 0.75rem;
  background: none; border: 0; border-radius: var(--radius-pill);
  color: var(--color-muted); font-family: var(--font-head);
  font-weight: 500; font-size: var(--fs-small); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
@media (hover: hover) { .lr-arch:hover { color: var(--color-navy); background: var(--color-bg-alt); } }
.lr-arch:focus-visible { outline: none; box-shadow: var(--ring); }
.lr-arch:disabled { opacity: 0.6; cursor: default; }
.lead-row.is-archived { opacity: 0.72; }

/* "Needs attention" list on the dashboard — actions derived from those same
   KPIs (api/dashboard_stats.php ds_attention()). Rows, not tiles, because
   each one is a sentence plus its button. */
/* COLOUR MEANS STATE (owner, 2026-07-30): normal = the skin's own blue,
   something wrong = red. Nothing brown.
   These rows used --color-amber for a problem and --color-success for a pass.
   In the active sky-ink skin those resolve to #499cd0 (blue) and #4A463C
   (dark brown) respectively — so a PASSING row rendered brown and a FAILING
   one rendered blue, i.e. exactly backwards. Bound to meaning instead:
   --err-fg is a true red in every skin, --color-navy is the skin's primary
   (blue here), so this keeps following the skin without ever going brown. */
.ds-attention { display: grid; gap: 0.4rem; margin-bottom: 0.9rem; }
.ds-att { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
  background: var(--color-surface); border: 1.5px solid var(--color-line);
  border-left: 4px solid var(--err-fg); border-radius: var(--radius);
  box-shadow: var(--tile-shadow); padding: 0.55rem 0.75rem; }
.ds-att > span { flex: 1 1 220px; min-width: 0; }
.ds-att--ok { border-left-color: var(--color-navy); }
/* a TO-DO is not an alarm (2026-07-31): red is reserved for "someone is
   waiting on you" (untriaged leads, week-old drafts). Setup work wears the
   skin's normal blue — visible, calm, not crying wolf. */
.ds-att--todo { border-left-color: var(--color-navy); }

/* --- gold sweep bar: a thin gold→bright gradient sweeps across the top
   edge of every interactive tile on hover (the homepage KB-tile move). --- */
[data-theme] .card,
[data-theme] a.contact-tile,
[data-theme] .qc-option,
[data-theme] .portal-tile { position: relative; overflow: hidden; }
[data-theme] .card::before,
[data-theme] a.contact-tile::before,
[data-theme] .qc-option::before,
[data-theme] .portal-tile::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--color-gold), var(--fx));
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s ease; pointer-events: none;
}
[data-theme] .card:hover::before,
[data-theme] a.contact-tile:hover::before,
[data-theme] .qc-option:hover::before,
[data-theme] .portal-tile:hover::before { transform: scaleX(1); }

/* --- permanent gold hairline across the top of dark bands (same rule the
   homepage "how it works" band wears) --- */
[data-theme] .section--navy,
[data-theme] .cta-band { position: relative; }
[data-theme] .section--navy::before,
[data-theme] .cta-band::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--fx), transparent);
  opacity: 0.75; pointer-events: none;
}

/* --- panels (PRO + admin): quiet version of the same hairline --- */
[data-theme] .panel { position: relative; }
[data-theme] .panel::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--fx) 50%, transparent);
  opacity: 0.55; pointer-events: none;
}

/* --- filled icon chips: the ONE gradient recipe, applied to EVERY icon
   chip site-wide (owner 2026-08-01: "every single one of the icons
   currently orange... same CSS as the one shown" — the service-card chip).
   Selector list extended the same day from 4 chips to ALL of them:
   contact tiles, hero benefit doodles, step numbers, admin benefit
   preview. Shape/size stays each chip's own; only the fill, ink and edge
   highlights unify. Anything painting one of these a flat orange elsewhere
   is a bug — b_theme.css's old flatteners were deleted in the same pass. --- */
[data-theme] .icon,
[data-theme] .why-item .why-ico,
[data-theme] .pt-ico,
[data-theme] .contact-tile .ci-ico,
[data-theme] .b-doodle,
[data-theme] .next-steps li::before,
[data-theme] .bp-ico,
[data-theme] .wiz-dot.is-active {
  /* `.icon`, NOT `.card .icon` (2026-08-01, owner: "why is this icon
     static?" on the service-page hero): the bare `.icon` chip is the SAME
     component used outside cards (service-page header, scheduled-services
     rows — see b_theme.css's own comment saying exactly that), but the
     card-scoped selector excluded it, so it missed the bevel/glow and
     rendered flat. Targeting the class itself covers every placement,
     current and future, by construction. */
  background: linear-gradient(155deg, var(--fx-bright), var(--fx) 55%, var(--color-gold-dark));
  color: var(--fx-ink);
  box-shadow: inset 0 1.5px 0 rgba(255,255,255,0.55),
              inset 0 -2px 3px rgba(0,0,0,0.22),
              0 0 0 1px color-mix(in srgb, var(--fx) 70%, transparent),
              0 4px 14px color-mix(in srgb, var(--fx) 45%, transparent);
}

/* --- ONE GEOMETRY for the feature-chip kind (2026-08-01, owner: "why are
   all these icons different" — the paint was unified but each context kept
   its own box: cards 54px/12px radius, bare hero 54/16, why-us 46 with a
   24px glyph, contact tiles 46 with 22px. Same paint on four differently
   shaped boxes still reads as four different icons). One spec now: 54px,
   16px radius, 28px glyph. The compact chips (.pt-ico 34, .b-doodle 30,
   step numbers 28) are a deliberately smaller tier — list/tile markers,
   not feature icons — and share only the paint. --- */
[data-theme] .icon,
[data-theme] .why-item .why-ico,
[data-theme] .contact-tile .ci-ico {
  width: 54px; height: 54px; border-radius: 16px;
}
[data-theme] .icon svg,
[data-theme] .why-item .why-ico svg,
[data-theme] .contact-tile .ci-ico svg { width: 28px; height: 28px; }
[data-theme] .icon .material-symbols-outlined,
[data-theme] .why-item .why-ico .material-symbols-outlined,
[data-theme] .contact-tile .ci-ico .material-symbols-outlined { font-size: 28px; }

/* --- active editor tab: same machined edge as the primary pills --- */
[data-theme] .hce-tab.is-active {
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30),
              inset 0 -1px 0 rgba(0,0,0,0.12);
}

/* =========================================================================
   CAPierre — PRO Dashboard sophistication pass (2026-07-20).
   Uses the theme's existing --fx-bright (skin-aware "brightest hover" highlight)
   as the single bright accent + springier, more dynamic motion: lift-and-glow
   tiles, a diagonal sheen sweep, an icon pop, and a motion-safe staggered
   entrance. Scoped to the dashboard's .portal-* chrome; appended last so it
   wins the cascade over the base rules above.
   ========================================================================= */

/* Tiles — replace the press-in hover with a premium lift + gold glow + sheen. */
.portal-tile {
  position: relative; overflow: hidden; will-change: transform;
  transition: transform .3s cubic-bezier(.22,1,.36,1), box-shadow .3s ease, border-color .3s ease;
}
.portal-tile::after {                    /* diagonal sheen, swept across on hover (::before is the theme's top accent bar) */
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0; z-index: 1;
  background: linear-gradient(115deg, transparent 30%,
              color-mix(in srgb, var(--fx-bright) 60%, transparent) 47%, transparent 60%);
  transform: translateX(-120%);
}
/* :hover-driven effects below are gated to hover-capable pointers only — on
   touch, tapping a tile/band would otherwise leave it visually "stuck" lifted
   and glowing until the next unrelated tap (owner-reported 2026-07-20). */
@media (hover: hover) {
  .portal-tile:hover {
    transform: translateY(-4px);
    border-color: var(--color-gold);
    box-shadow: 0 16px 36px rgba(20,32,50,.17),
                0 0 0 1px color-mix(in srgb, var(--color-gold) 50%, transparent),
                0 0 22px color-mix(in srgb, var(--fx-bright) 30%, transparent);
  }
  .portal-tile:hover::after { opacity: 1; animation: pt-sheen .75s cubic-bezier(.22,1,.36,1); }
  .portal-tile:hover .pt-ico {
    transform: translateY(-2px) scale(1.07) rotate(-3deg);
    box-shadow: 0 8px 20px color-mix(in srgb, var(--color-gold) 45%, transparent);
  }
  .portal-tile:hover .pt-title { color: var(--color-amber-dark); }
  .portal-band-head:hover {
    transform: translateY(-2px); border-color: var(--color-gold);
    box-shadow: 0 12px 26px rgba(20,32,50,.13);
  }
  .portal-band-head:hover::after { opacity: .9; }
}
.portal-tile:active { transform: translateY(-1px) scale(.994); }
@keyframes pt-sheen { from { transform: translateX(-120%); } to { transform: translateX(120%); } }

/* Icon — brighten, scale, and glow on hover; title warms to the accent. */
.pt-ico { transition: transform .3s cubic-bezier(.34,1.56,.64,1), box-shadow .3s ease; position: relative; z-index: 2; }
.pt-title, .pt-desc { position: relative; z-index: 2; }
.pt-title { transition: color .22s ease; }

/* Band headers — lift + gold accent + a soft top highlight; springy chevron. */
.portal-band-head {
  position: relative; overflow: hidden;
  transition: transform .28s cubic-bezier(.22,1,.36,1), box-shadow .28s ease, border-color .28s ease;
}
.portal-band-head::after {               /* thin bright top-edge highlight, fades in on hover */
  content: ""; position: absolute; top: 0; left: 12%; right: 12%; height: 2px; opacity: 0;
  background: linear-gradient(90deg, transparent, var(--fx-bright), transparent);
  transition: opacity .28s ease;
}
.portal-band-head:active { transform: translateY(0); }
.portal-band-head .pb-chev { transition: transform .38s cubic-bezier(.34,1.56,.64,1); }

/* Band headers keep the shared white-card dress defined in the base rules
   above — identical to kalocksco.com's dashboard (owner: keep the two sites
   consistent, 2026-07-21). No flat/editorial override here on purpose. */

/* Staggered entrance — motion-safe. Runs on load and when a band opens. */
@media (prefers-reduced-motion: no-preference) {
  .portal-band[open] .portal-tile { animation: pt-rise .5s cubic-bezier(.22,1,.36,1) both; }
  .portal-grid .portal-tile:nth-child(2) { animation-delay: .05s; }
  .portal-grid .portal-tile:nth-child(3) { animation-delay: .10s; }
  .portal-grid .portal-tile:nth-child(4) { animation-delay: .15s; }
  .portal-grid .portal-tile:nth-child(5) { animation-delay: .20s; }
  .portal-grid .portal-tile:nth-child(6) { animation-delay: .25s; }
  @keyframes pt-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
}
/* =========================================================================
   PRO nav dropdown — refined (2026-07-20). The admin hamburger menu was
   reusing the pill-row's press-in hover, which reads oddly as a vertical
   list. This turns it into a proper menu: rounded panel + gold top hairline,
   a soft gold-tint hover fill, a right-edge accent that grows on hover/active
   (text is right-aligned), and a motion-safe staggered entrance. Skin-aware
   via --color-gold / --fx-bright. Appended last to win the cascade.
   ========================================================================= */
.dash-nav.is-open {
  border-radius: var(--radius); overflow: hidden; padding: 0.4rem;
  box-shadow: 0 18px 44px rgba(20,32,50,.18),
              0 0 0 1px color-mix(in srgb, var(--color-gold) 30%, transparent);
  animation: nav-drop .2s cubic-bezier(.22,1,.36,1) both;
}
.dash-nav.is-open::before {              /* gold hairline across the panel top */
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--color-gold), transparent);
}
@keyframes nav-drop { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }

.dash-nav.is-open a {
  position: relative; width: 100%;
  padding: 0.7rem 1rem; border-radius: var(--radius-sm);
  border-bottom: none; text-align: right; box-shadow: none;
  transition: background .16s ease, color .16s ease, transform .16s ease;
}
.dash-nav.is-open a + a { margin-top: 1px; }
.dash-nav.is-open a::after {            /* right-edge accent, grows on hover/active */
  content: ""; position: absolute; right: 0.4rem; top: 50%;
  transform: translateY(-50%) scaleY(0); transform-origin: center;
  width: 3px; height: 56%; border-radius: 3px; background: var(--fx-bright);
  transition: transform .16s ease;
}
.dash-nav.is-open a:hover {
  background: color-mix(in srgb, var(--color-gold) 15%, transparent);
  transform: none; box-shadow: none;
}
.dash-nav.is-open a:hover::after { transform: translateY(-50%) scaleY(1); }
.dash-nav.is-open a.is-active { background: var(--color-navy); color: var(--color-white); box-shadow: var(--shadow-sm); }
.dash-nav.is-open a.is-active:hover { background: var(--color-navy-deep); }
.dash-nav.is-open a.is-active::after { transform: translateY(-50%) scaleY(1); background: var(--color-gold); }

@media (prefers-reduced-motion: no-preference) {
  .dash-nav.is-open a { animation: nav-item .26s cubic-bezier(.22,1,.36,1) both; }
  .dash-nav.is-open a:nth-child(1){animation-delay:.02s}
  .dash-nav.is-open a:nth-child(2){animation-delay:.05s}
  .dash-nav.is-open a:nth-child(3){animation-delay:.08s}
  .dash-nav.is-open a:nth-child(4){animation-delay:.11s}
  .dash-nav.is-open a:nth-child(5){animation-delay:.14s}
  .dash-nav.is-open a:nth-child(6){animation-delay:.17s}
  .dash-nav.is-open a:nth-child(7){animation-delay:.20s}
  .dash-nav.is-open a:nth-child(8){animation-delay:.23s}
  .dash-nav.is-open a:nth-child(9){animation-delay:.26s}
  .dash-nav.is-open a:nth-child(10){animation-delay:.29s}
  .dash-nav.is-open a:nth-child(11){animation-delay:.32s}
  @keyframes nav-item { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }
}
/* Nav dropdown — icons + left-aligned labels (2026-07-20) for faster scanning.
   Overrides the right-aligned refinement above (appended later = wins). */
.dash-nav.is-open a {
  display: flex; align-items: center; justify-content: flex-start;
  text-align: left; gap: .7rem; padding: .7rem 1rem .7rem .95rem;
}
.dash-nav.is-open a .dn-ico { width: 18px; height: 18px; flex: none; opacity: .82;
  transition: opacity .16s ease, transform .16s ease; }
.dash-nav.is-open a .dn-label { flex: 1 1 auto; }
.dash-nav.is-open a .nav-badge { flex: none; margin-left: auto; }
.dash-nav.is-open a:hover .dn-ico { opacity: 1; transform: scale(1.08); }
.dash-nav.is-open a.is-active .dn-ico { opacity: 1; }
.dash-nav.is-open a::after { left: .3rem; right: auto; }   /* accent moves to the icon (left) side */

/* =========================================================================
   SHARED PAGE UTILITIES — KEEP BYTE-IDENTICAL ACROSS SITES (2026-07-21).
   The ONE utility vocabulary for capierre.com, kalocksco.com and the KASITE
   template. These replace per-page inline style="" attributes (margins,
   alignment, narrow measures). Rules of the section:
     - never add a site-specific rule here;
     - never redefine one of these locally or inline;
     - a value change is made here on ALL THREE sites in the same edit
       (the KASITE drift-check page is the watchdog).
   ========================================================================= */
/* vertical rhythm */
.stack-xs  { margin-top: 0.8rem; }
.stack-sm  { margin-top: 1rem; }
.stack-md  { margin-top: 1.2rem; }
.gap-below { margin-bottom: 1.2rem; }
/* alignment */
.ta-center { text-align: center; }
/* narrower reading measures for prose sections */
.container--narrow { max-width: 48rem; }
.container--tight  { max-width: 40rem; }
/* back-link with no trailing gap (login pages — nothing follows it) */
.nav-back--flush { margin-right: 0; }
/* server-rendered status banner sitting above a form needs bottom spacing
   the JS-driven inline ones don't (.is-ok/.is-err already make it visible) */
.form-status.is-shown { margin-bottom: 1rem; }

/* =========================================================================
   BACKUPS — admin/backups.php status line + dot.
   ========================================================================= */
.bk-line { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.3rem; font-size: var(--fs-body); }
.bk-dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; flex: none; }
.bk-dot.is-ok   { background: var(--ok-fg); }
.bk-dot.is-warn { background: var(--color-gold); }
