/*
 Theme Name:   Tonecraft Piano Works
 Theme URI:    https://tonecraftpianoworks.com
 Description:  Custom child theme for Tonecraft Piano Works, built on Astra. Concert-level piano tuning, voicing, repair, cleaning, and rebuilding services across Northern Michigan and beyond.
 Author:       Concept2FORM
 Author URI:   https://concept2form.com
 Template:     astra
 Version:      1.9.3
 Text Domain:  tcp
*/

/* ==========================================================================
   Tonecraft Piano Works — Custom Styles
   Brand palette LOCKED July 2026 — see :root variables below.

   v1.9.3 (2026-09-13): --tcp-hero-booking-offset added — front-page
   hero self-scheduling module nudged 40px further off the right edge,
   clear of the tick nav's upper labels.

   v1.9.2 (2026-09-13): --tcp-ticknav-clearance added, so the interior
   pages' bottom-right hero plate stops rendering underneath the fixed
   tick nav. Consumed in assets/css/tcp-interior.css.

   v1.9.1 (2026-09-13): TICK-NAV CONTRAST FIX. v1.9.0's approach was
   wrong in two ways, both visible as "still illegible over the hero":
   (a) it classed the whole nav, but the nav is taller than the interior
   pages' dark hero band and genuinely straddles it, so no single class
   could be right about both halves — contrast is now decided PER LINK;
   (b) the `body.theme-light .tcp-tick-nav a` fallback sat at (0,2,2) and
   outranked the contrast classes anyway, so even a correct class would
   have lost. Those fallbacks are now wrapped in :where() and the on-dark
   rules were removed from tcp-interior.css entirely, leaving style.css
   the single place the two states are declared. Resting alphas also
   raised (tick .62->.78, text .72->.88, label .58->.78).

   v1.9.0 (2026-09-13): BOOKING + TICK-NAV VISIBILITY PASS.
   1. Tick nav resting legibility. The nav rendered 1px ticks at 0.35
      alpha with its labels at opacity 0 until hover, which on landing
      read as nothing being there at all. New "Tick-nav visibility"
      Control Panel subsection holds the resting alphas/sizes, and the
      nav now also carries a real .is-over-dark state driven by
      assets/js/tcp-tick-nav-contrast.js — the class tcp-interior.css
      had styled since August but which no script ever set.
   2. Hero self-scheduling module (.tcp-hero-booking), top-right of the
      front-page hero, plus .tcp-cta-actions for the closing CTA pair.
      Booking is the site's primary conversion and now has weight to
      match instead of sitting fourth in the hero button row.
   3. .tcp-hero h1 letter-spacing -.01em -> .01em, per the standing
      brand rule that tracking is never set tighter than +0.01em.
   v1.8.0 (2026-09-13): SITEWIDE INTERIOR OVERHAUL. Nothing in this
   file's front-page rules changed — the front page is deliberately
   untouched and is now the stated foundation every other page is
   styled against. The overhaul lives in two new files,
   assets/css/tcp-interior.css and assets/js/tcp-interior.js, enqueued
   on every view except the front page (tcp_enqueue_interior_assets()
   in functions.php, priority 20 so it always prints after the
   page-specific stylesheets). In summary, on the interior pages:
   the plain centred page titles became dark, soft-lit, left-justified
   hero bands; every section gained the front page's eyebrow-tick →
   display-headline → lede heading block; all gold and glass card
   edges (services.css's .tcp-svc-card was the last holdout) were
   replaced with matte hairline framing; primary buttons are burgundy
   with gold type; Alga joined the type system as the third voice
   alongside Fraunces and Poppins; entrance animation and easing now
   run on every page rather than only the home page; and the
   Astra-rendered blog/archive views no longer tuck their content under
   the fixed header. The only edits in THIS file are this note and the
   Version bump above — which, per the v1.3.5 note below, is what
   cache-busts every enqueued asset in the theme, including the two new
   ones.
   v1.8.1 (2026-09-13): Version bump only, per the v1.3.5 rule below —
   tcp-interior.css and tcp-interior.js were revised during the first
   review pass (entrance-reveal resting-transform fix, a failsafe sweep
   so nothing can ever be left stranded at opacity 0, Services grid
   pinned to three columns to match the front page, blog header
   clearance calibrated against Astra's own row offset, WPForms label
   selector widened to catch <legend>), and the enqueued URL only
   changes when this number does.
   v1.8.2 (2026-09-13): Services page margin fix — the FAQ triage
   section was rendering with no left/right gutter because
   services.css redeclares that section's padding as a shorthand with
   zero side padding. tcp-interior.css now states the interior gutter
   as its own longhands so a page stylesheet's shorthand can't zero it.
   Triage answer panel, its Contact Us CTA and the follow-up chips also
   brought onto the sitewide type/control system. Version bumped so the
   revised tcp-interior.css actually reaches the browser.
   v1.2.0: front page rebuilt to match tonecraft-luxury-demo-v1.html —
   per-section header theme flip, Story/Craft sections, alternating
   white/cream sections, animated count-up stats.
   v1.2.1: primary body font switched to Poppins (weight 300), now
   served via the Adobe Fonts kit alongside Proxima Nova. Multiple
   fonts will be in play across the site going forward — Poppins is
   the default body voice, not the only typeface.
   v1.2.2: h1/h2/h3 now force color:#f3f3f3 (!important) — Astra ships
   its own bare h1-h6 color rule at equal CSS specificity, which was
   winning and rendering hero headings almost invisible against the
   dark background. Light-section heading override updated to match.
   v1.2.3: same fix applied to .tcp-service-card h3 and .tcp-area-tier
   h3 — they set their own color but without !important, so they lost
   to the new v1.2.2 base rule and washed out to near-white on white
   card backgrounds (Services grid, Service Area tiers).
   v1.2.4: Contact section email overflowed into the Instagram column.
   Cause: "aaron@tonecraftpianoworks.com" has no spaces, so the browser
   had no natural break point and the flex item wouldn't shrink below
   its content width by default. Fixed with overflow-wrap/word-break on
   .tcp-cval plus min-width:0 on .tcp-cta-contact-item so the wrap can
   actually take effect.
   v1.2.5: White gap between the Contact section and the footer. Cause:
   .tcp-footer had margin-top:60px, which sits outside the footer's own
   charcoal background and exposed Astra core's forced white body
   background behind it. Fixed by forcing body background to cream
   (!important, over Astra's rule) and moving the footer's spacing from
   margin-top into padding-top so it stays inside the charcoal bg.
   v1.2.7: About/Services/Contact headings (page titles, bio, credentials,
   service-area blurb, contact info) were rendering invisible white-on-
   cream. Cause: the v1.2.2 global h1/h2/h3 rule forces cream for the
   front page's dark hero/sections, and only .tcp-section-light (the
   front-page light-section class) was ever taught to override it back
   to charcoal. .tcp-page-header/.tcp-about-.tcp-contact-* are a
   separate, older template system that never used .tcp-section-light,
   so they had no override and silently inherited the forced-white
   color. Added an explicit charcoal override scoped to those wrapper
   classes — see "About / Services / Contact page headers" below.
   v1.2.8: RECOVERY PASS. Root cause of last night's crash: the two
   header files (assets/css/tcp-header-theme.css and its companion JS)
   were not present in this handoff and are assumed corrupted/lost —
   most likely an unscoped selector in that file was leaking outside
   .tcp-header and clobbering .tcp-btn-primary's fill and .tcp-eyebrow's
   gold color sitewide, plus possibly the alternating section backgrounds.
   Both files have been rebuilt from scratch, scoped strictly to
   .tcp-header so they can never again touch anything outside the header
   bar. Nothing in THIS file needed to change to fix buttons/eyebrow/
   alternating sections — those rules were already correct — but a few
   !important safety nets were added below so a future header-file bug
   can't take them down again. Font fallback stack widened so the nav
   degrades to a real sans-serif (not serif) if Adobe Fonts ever fails
   to load. Services and Contact page templates were also converted
   from their old ad-hoc background system to the same
   .tcp-section-light / .tcp-bg-white / .tcp-bg-cream tokens the Home
   and About pages already use successfully — see page-services.php
   and page-contact.php.
   v1.2.9: Footer social/contact row replaced with a proper icon set.
   footer.php now renders 5 linked icons (phone, email, Instagram,
   Facebook, YouTube) using the lightblue-*.svg files from
   assets/svg/icons/ instead of the old text-link placeholder — see
   .tcp-footer-icons below. Facebook link is now live
   (facebook.com/TonecraftPianoWorks); the "unconfirmed handle" comment
   that previously suppressed it has been removed accordingly.
   v1.3.0: Contact page Phone/Email replaced plain text links with
   .tcp-contact-btn reveal buttons — outline style borrowed from the
   header CTA (burgundy border/text, gold border + lift on hover) but
   shorter, and self-contained rather than extending .tcp-btn, since
   the header's ::before gold mix-blend-mode wash is tuned for dark
   backgrounds and looked wrong on this light-section outline variant.
   Label/value are two absolutely-positioned spans crossfaded via
   opacity on :hover/:focus-visible; the anchor's real tel:/mailto:
   href is present from the start so touch devices (no hover state)
   can tap straight through. See .tcp-contact-btns below.
   v1.3.1: Front-page Contact section (.tcp-cta-contact-item) now
   matches the same label/value crossfade pattern — only Call/Email/
   Instagram show by default, hover or :focus-visible reveals the
   real value. Email value no longer wraps (white-space: nowrap).
   v1.3.2: Front-page Contact section restructured (see full note
   above the .tcp-cta-contact-item rules below) — item is now a
   wrapper div holding a separate link and a top-right copy-to-
   clipboard button; hover no longer lets the value overflow, the
   item's width now expands to fit it (measured by
   front-page-interactions.js); Facebook and YouTube added as two
   more items in the same grid.
   v1.3.3: Sticky-footer fix for Astra-default-templated pages (blog
   archive, single posts, search, 404). These don't render inside a
   #page wrapper at all — Astra's #primary div is the direct flex
   child of <body> — so the existing flex:1 rule on .site-content/
   main never applied (main sits nested inside #primary, which
   wasn't itself a flex container). Added #primary to the flex-grow
   selector group below so short content (e.g. the placeholder
   "Hello world!" post) still pushes the footer to the bottom of the
   viewport instead of leaving it stranded right under the content.
   v1.3.4: --tcp-charcoal changed from #241c14 to #4a4b4c, and
   --tcp-text-dark changed from #2b2b2b to the same #4a4b4c, per
   Aaron's request (2026-08-07) — the two "ink" tokens are now
   deliberately identical. Also updated: 14 separate places that had
   the old charcoal hand-derived as a raw rgba() tint (rgba(36, 28, 20,
   alpha)) instead of referencing the variable — body copy color on
   every .tcp-section-light, the tick nav's dot/label color, the
   homepage stats dividers, the old service-card/area-tier borders and
   text. All of those are now rgba(74, 75, 76, same-alpha). This is a
   sitewide change: the header, footer, and html background all shift
   to the lighter value, and body text across every inner page gets
   noticeably lighter/grayer than the old near-black. Worth a full
   visual pass, not just a spot-check on the services page.
   NOT changed: the hero gradient overlay (~line 490) has a first
   color stop of rgba(43, 33, 24, 0.25) — close to but not exactly the
   old charcoal (36, 28, 20), so it wasn't swept up in this replace.
   Its later two stops in the same gradient ARE now the new gray,
   which may look like a mismatched transition — worth a look.
   v1.3.5: Version bump only — no CSS changes in this file itself.
   This exists purely to bust the browser/host cache. Every
   wp_enqueue_style/wp_enqueue_script call across the theme uses
   wp_get_theme()->get('Version') — i.e. this file's Version header —
   as its cache-busting query string. The v1.3.4 → services.css /
   services-triage.js "Contact Us" CTA button change didn't bump this
   number, so the enqueued URL for those files never changed, and the
   old cached versions kept being served even after the new file
   content was uploaded — which is why the button didn't appear.
   Going forward: bump this version on ANY change to ANY enqueued
   asset (CSS or JS, not just this file), not only when style.css
   itself changes — that's the actual scope of what this number
   controls.
   v1.3.6: Version bump only, again — same reason as v1.3.5.
   contact-prefill.js was rewritten (wrong WPForms class name
   corrected, now anchors on .tcp-contact-form-wrap instead) and this
   bump ensures that new file content actually reaches the browser
   instead of a cached copy of the previous version.
   v1.3.7: RESTORE. The circle-repeat header pattern work from the
   prior chat session (--tcp-cream-glass alpha drop, .tcp-header-inner
   position/z-index, .tcp-header-bg-pattern base rule) was missing from
   this file — it looks like the v1.3.5/v1.3.6 cache-bust edits above
   were made against a copy of this file from before that work landed,
   so it never made it into the merged theme. Re-added below, nothing
   else touched.
   v1.3.8: Header opacity/pattern now driven by a single named control
   block in :root (search "HEADER OPACITY & PATTERN CONTROLS") instead
   of hardcoded numbers scattered across two files. --tcp-cream-glass,
   --tcp-charcoal-glass, .tcp-header-bg-pattern's background-size, and
   tcp-header-theme.css's pattern opacity all now reference these five
   variables. No visual change from this bump alone — same values as
   before (0.80 / 0.86 / 0.12 / 3px / 3px), just centralized.
   v1.4.0: Introduced the CSS CONTROL PANEL — the former "HEADER
   OPACITY & PATTERN CONTROLS" block in :root is now a subsection of
   this larger, permanent home for every adjustable custom property in
   the file. Going forward, every CSS file in the theme (new or
   revised) gets a Control Panel section near its top, even if empty —
   see the project instructions doc for the full convention.
   Two things moved into it this round:
   (1) Hero figure (Aaron) positioning — --tcp-hero-figure-left,
   -right-buffer, -min-left, -height, -max-height moved out of the
   .tcp-hero-figure rule itself and into the Control Panel, alongside a
   new --tcp-hero-figure-body-fraction (0.542) control. The new
   aaron-hero-image-w-credentials.png (1144x855) now has his
   credentials/title text baked into the same canvas as his figure, so
   the image is wider than before — his solid opaque body was measured
   (via the PNG's alpha channel) to end around x=620 of the 1144px
   canvas, meaning ~54.2% of the image is actually him. The slide-
   trigger calc previously measured the FULL rendered image width
   (Aaron + text), which made him start sliding noticeably sooner than
   before, since the text now inflates that measurement. The calc now
   multiplies the measured width by this fraction, so the trigger
   reacts to his own width only — the text portion simply clips under
   .tcp-hero's overflow:hidden as the viewport narrows, ahead of him
   ever needing to move. Re-measure and update this fraction if the
   hero image is ever re-exported at different dimensions.
   (2) Hero background scene sizing/position — --tcp-hero-bg-width,
   -height, -position-x, -position-y added to the Control Panel and
   wired into .tcp-hero-bg's background-size/background-position,
   replacing the hardcoded "2400px auto" / "center bottom" values.
   Values unchanged (2400px / auto / center / bottom) — this is a
   refactor for editability, not a visual change. Per the client: the
   background image's native dimensions (2688x1372) have been
   intentionally scaled/cropped down via these controls to get the
   piano's position right within the hero, so the native file size is
   NOT expected to match the rendered background-size 1:1.
   v1.4.1: FIX — Aaron's hero figure was rendering horizontally
   squeezed (confirmed via DevTools: intrinsic image 1144x855,
   rendering at 99x756). Root cause: Astra core's global
   "img { max-width: 100%; }" reset was still applying to
   .tcp-hero-figure-img underneath our rule, since our rule never set
   max-width at all. Combined with .tcp-hero-figure (the parent) being
   width:auto/shrink-to-fit, that percentage max-width created a
   circular sizing calculation that collapsed his rendered width down
   to a small fraction of his real proportional size. Added
   max-width:none to .tcp-hero-figure-img to remove Astra's rule from
   the equation. Pre-existing bug, not something introduced by v1.4.0
   — the wider credentials-baked-in image just made it visible.
   v1.5.0: Hero + Services rebuilt with the responsive "scaling" pattern
   from the tonecraft-hero-scaling-demo mockup (reverse-engineered from
   gurbakshchahal.com — see _handoff/tonecraft-hero-scaling-demo/README.md),
   adapted to this theme's real markup, brand tokens, and fonts (Fraunces
   / Poppins — the demo used stand-in Google Fonts and is NOT what's
   loaded here). Changes:
   (1) Hero copy replaced per client direction — new headline, added
   second eyebrow line, added a role line, added a third CTA ("Watch
   the Craft", opens a placeholder video modal — real footage is on the
   to-do list). The existing .tcp-eyebrow ("THE CRAFTSMAN · EST. 25+
   YEARS") keeps its original copy; everything else in .tcp-hero-content
   is new.
   (2) .tcp-hero-bg's photo and .tcp-hero-figure (Aaron's cutout) are
   TEMPORARILY out of the hero markup (front-page.php) — not this file.
   .tcp-hero-figure's rule block below is untouched and ready the moment
   the markup comes back. .tcp-hero-bg now points at a generated
   placeholder (assets/images/tcp-hero-bg-placeholder-temp.jpg) via the
   new .tcp-hero-bg--placeholder modifier class, added below the base
   rule — remove that class from the markup to fall straight back to
   the real photo with zero CSS changes.
   (3) .tcp-hero-overlay rewritten with a two-state gradient: horizontal-
   dominant above the new --tcp-hero-bp-tablet breakpoint (920px — reuses
   the same cutoff the header nav/tick-nav already collapse at, rather
   than a new value), vertical-dominant below it, matching the direction-
   swap the reference site does when its hero content relocates to the
   bottom of the frame on narrower viewports. Also folds in the fix
   already flagged in the v1.3.4 note above (this rule's first gradient
   stop was still the pre-v1.3.4 charcoal rgba instead of the current
   74,75,76 token) since this rule was being rewritten anyway.
   (4) New responsive rules at 920px/560px: hero content bottom-anchors,
   the kicker eyebrow and .tcp-plate-label caption hide, CTA buttons
   stack at 560px. New CSS Control Panel entries document both values.
   (5) Services section moved to directly follow the Hero (was after
   Story) and recolored to cream; to keep the alternating white/cream
   rhythm intact with Services now taking the first slot, Service Area,
   Craft, and Contact each got their .tcp-bg-white/.tcp-bg-cream class
   swapped too — none of their copy, layout, or other markup changed.
   New copy: replaced h2, added a lede paragraph and a second "Every
   service, held to a concert-level standard." statement line (new
   .tcp-svc-substatement rule) — the service-card grid itself is
   untouched.
   tick-nav.php's link order was deliberately left as-is per instruction
   ("keep the tick-nav... everything else remain the same") — it no
   longer matches the page's new scroll order (Services now comes right
   after Home instead of after Story), which is a known, accepted
   side-effect of this change, not a bug.
   Not visually verified against a running WordPress instance — this was
   built and reviewed as static files only. Please give the front page a
   look on tonecraft-dev.local (desktop + mobile) before merging.

   v1.6.0: Follow-up round after the v1.5.0 review.
   (1) template-parts/tick-nav.php reordered so its link order matches
   the page's real scroll order now that Services sits right after
   Home — closes out the known side-effect flagged in v1.5.0. No CSS
   change required for this (tick-nav.php only).
   (2) Front-page service cards (.tcp-service-card) are now links to
   the Services page — background-color hover fill (new
   --tcp-service-hover-bg token, a monochromatic step off --tcp-cream,
   deliberately not a red/burgundy tint) plus a lift + soft shadow, and
   a bottom row (.tcp-service-card:nth-child(n+4)) gets a matching
   border-bottom so it's bookended the same as the top row. Cards link
   to /services/?from=home-services — see services-triage.js for the
   delayed-scroll landing behavior that query param triggers, and
   page-services.php / services.css for the #tcp-svc-elite-anchor
   scroll target.
   (3) New .tcp-svc-section-heading entrance reveal on the Services
   page ("Elite Performance Services") — reuses the sitewide
   .tcp-anim-slide-up utility class already defined below; its
   IntersectionObserver trigger lives in services-triage.js since that
   page doesn't load front-page-interactions.js.

   v1.6.1: Two small Services-page fixes, no changes in this file —
   both live in assets/css/services.css and assets/js/services-triage.js.
   (1) .tcp-svc-copyright (the © mark next to "Elite Performance
   Services") sized down from .55em to .38em and given its own ~60%-
   gray color instead of inheriting the heading's full-strength
   charcoal. (2) Service-card detail-item dropdowns (Seasonal tuning,
   First appointment, etc.) are now an accordion per card — opening one
   closes any other open item in the same card — instead of allowing
   several open at once.

   v1.6.2: Tick-nav click-scroll offset — first section done, rest to
   follow per client direction ("I'll get maybe the rest later").
   Previously tick-nav clicks had NO header offset at all: scrollIntoView
   with no scroll-margin-top tucked the target's top edge flush against
   the viewport top, hidden behind the fixed 100px header. Fixed for
   Services via a new opt-in mechanism — data-scroll-target on a
   tick-nav.php link (falls back to the existing data-target when
   absent, so every other link is unchanged) — pointed at a new
   #services-heading id on the Services <h2>, with scroll-margin-top:
   var(--tcp-ticknav-scroll-offset) (new Control Panel value, 120px,
   estimated from the client's reference screenshot). Landing on the
   heading itself rather than the section's own top is deliberate — it
   crops the eyebrow above out of frame, matching the screenshot.

   v1.6.3: "Not showing up below? Click here." — no changes in this
   file, both live in assets/css/services.css (.tcp-svc-missing-link)
   and page-services.php. Sits between the "Elite Performance
   Solutions" heading and the card grid, links to #faq. First real use
   of --tcp-font-proxima (declared since v1.2.1, never assigned to
   anything until now) at its thin (100) weight, ~60% gray. Hover
   reveals a translucent/blurred box with a 1px outline inset from its
   own edge (outline-offset: -1px).

   v1.6.4: Refined .tcp-svc-missing-link per pixel measurements the
   client took directly off the live page in DevTools (two screenshots
   with guide lines/rectangle overlaid at their real 1920px viewport).
   No changes in this file, all in assets/css/services.css:
     - Position pulled ~20px further left (margin-left -14px -> -34px,
       now a Control Panel var) per the two vertical guide lines the
       client drew — one on the button's then-current edge, one 20px
       left of it marking the target.
     - Vertical spacing evened out to a symmetric 22px above and below
       (was 28px top / 22px bottom) per the two pink measurement bands
       the client drew flanking the button, each reading ~21-23px tall.
     - Hover "glass" box reworked from one flat rgba fill to layered
       flat gradients (directional linear + a whisper of radial gold
       glow) at a lower base opacity with more blur (6px -> 10px) —
       same technique as .tcp-hero-overlay below, just re-tuned for a
       light cream surface instead of the hero's dark scrim. Explicit
       goal per the client: more glass, but matte — no glossy
       highlight streak, ever.

   v1.6.5: Four-part follow-up round.
     1. .tcp-svc-missing-link restructured (assets/css/services.css) —
        wrapped in .tcp-svc-missing-link-wrap so hovering (or keyboard-
        focusing) a 10px zone around the link, not just the link
        itself, reveals a popup reading "Contact us directly for a
        solution." (different from the link's own resting text). Full
        manual Control Panel added: horizontal offset, top/bottom gap,
        all 4 padding sides, hover-zone size, and blur are now each
        their own variable. v1.6.4's screenshot-derived position
        (-34px, symmetric 22px gaps) overshot the container's left
        margin per client feedback — reset to the known-good pre-
        v1.6.4 defaults (-14px / 28px-22px) rather than guess a third
        time from another screenshot.
     2. Services page: the "Contact us for a solution." link that used
        to sit under every expanded service description (client's Aug
        5 reference screenshot) was gone with no surviving copy in
        _bak/_handoff or project docs — rebuilt in services-triage.js,
        applied to every .tcp-svc-detail-desc-inner paragraph via one
        shared loop, using the same /contact/?message= + encoded text
        mechanism already live and working for the FAQ triage CTA.
        .tcp-svc-detail-desc's max-height bumped 220px -> 240px to fit
        the added line.
     3. Front page Story section background image fixed — the file
        (assets/images/piano_strings_bg.jpg) had ended up sitting in
        an assets/images/_bak/ subfolder, so the #story CSS rule
        pointing at assets/images/piano_strings_bg.jpg was a silent
        404 (section just showed its plain cream background instead).
        Restored the file to its active path and removed a duplicate,
        stale copy of the same #story rule that had also been added to
        this file (global stylesheet) — front-page.css (front-page-
        only) is now the one canonical copy.

   v1.6.6: Follow-up correction round on .tcp-svc-missing-link, all in
     assets/css/services.css — position/spacing tightened per a second
     annotated screenshot, and the hover popup's whole look replaced
     per explicit client direction:
       - Popup: v1.6.5's blurred/tinted "glass" background removed
         entirely, replaced with a plain #FFFFFF fill matching
         .tcp-bg-white (the section's real background). Padding bumped
         to a generous 25px on every side (new --tcp-svc-missing-link-
         popup-pad). On hover it now also nudges 3px to the right
         (eased via transform, --tcp-svc-missing-link-popup-shift-x)
         and gets a soft, low-alpha, close-radius box-shadow glow
         (--tcp-svc-missing-link-glow-color/-blur) standing in for the
         old outline — deliberately restrained per "delicate... not
         harsh... don't extend too far off the boundaries."
       - Position: gap-top/gap-bottom tightened from 28px/22px down to
         8px/8px (client's arrows showed the heading underline coming
         down to meet the link's top and the card grid coming up to
         meet its bottom — read as "close this whitespace way down,"
         not literally to zero). Horizontal offset flipped from a -14px
         leftward pull to +6px rightward, with an explicit constraint
         from the client that it never sit left of the page's own
         content edge.
       - All of the above are still just the Control Panel's starting
         values, same as v1.6.5 — every one of them is a named variable
         at the top of services.css for further hand-tuning.

   v1.6.7: Four-part correction round.
     1. CRITICAL FIX: .tcp-svc-missing-link's href was still "#faq"
        (page-services.php) — now points at the Contact page
        (home_url('/contact/')). The v1.6.5 popup text ("Contact us
        directly for a solution.") had already been promising this;
        the click just wasn't honoring it.
     2. The "Contact us for a solution." per-service CTA
        (services-triage.js) now prepends a fixed two-line intro ahead
        of the item's own answer text in the message it sends —
        client's exact wording, typed out on a live example. Scoped
        only to this CTA, not the FAQ triage widget's own CTA.
     3. WPForms message textarea (style.css) was rendering at ~200px —
        about 4 sentences before scrolling. min-height: 600px (3x+).
     4. REAL BUG FOUND AND FIXED: the "huge whitespace above the
        button" the client kept flagging across the last two rounds
        was never actually a wrong Control Panel value — .tcp-svc-
        section-heading (assets/css/services.css) had its own
        margin-bottom: 40px, left over from before the missing-link
        Control Panel existed, silently stacking in front of
        --tcp-svc-missing-link-gap-top every time (inline-block margins
        don't collapse with a block sibling's margin, so the two simply
        added together instead of one winning). Set that margin-bottom
        to 0 — gap-top and gap-bottom now render genuinely equal at 8px
        each, confirmed by measuring actual rendered box positions, not
        just re-reading the CSS.
   v1.6.8: Icon/flyout round on the services page — client confirmed
     v1.6.7's padding fix worked, then asked for new functionality,
     pixel-measured off a new annotated screenshot.
       1. The .tcp-svc-missing-link-popup hover box (v1.6.5/v1.6.6) is
          RETIRED — removed entirely (CSS + the PHP span). Replaced
          with two click-toggled icon badges (mail, phone) sitting
          after the link text.
       2. New .tcp-svc-icon-btn component (assets/css/services.css):
          a small rounded-square badge (var(--tcp-svc-icon-badge-bg),
          #abb8c3 — the exact fill color already used by
          assets/svg/icons/lightblue-*.svg, so the badge reads as a
          filled version of that same icon family) housing a
          cream-*.svg glyph (near-white, for contrast against the
          badge). Click toggles a small flyout showing the real value
          (email address / phone number) as a live link, plus a
          separate copy button (reuses style.css's existing
          .tcp-copy-icon/.tcp-copy-icon-check crossfade markup and
          .is-copied JS pattern from the front-page contact items,
          under new non-conflicting class names so neither page's
          styling can drift the other). Copy button is a DOM sibling
          of the value link, not a descendant — same structural trick
          .tcp-cta-contact-item already uses — so its click can never
          bubble into the link's own navigate/call.
       3. Applied in two places: the "Not showing up below?" link
          (page-services.php, static markup) and every per-service
          "Contact us for a solution." CTA (services-triage.js, same
          shared loop that already built the CTA text link — one
          function now builds both, so a service added later gets the
          icons for free, matching the client's "persistent across all
          descriptions" instruction). Email badge's value link points
          to the same /contact/ destination as the CTA text/missing
          link (?message= prefill included in the per-service context);
          phone badge's value link is a real tel: link. Clicking the
          badge itself only ever opens/closes the flyout — never
          navigates or calls — see the file-disclosure notes on this
          round for exactly which click does what, since this was a
          judgment call on an ambiguous point in the client's request.
       4. "Not showing up below? Click here." font-size increased from
          .82rem to a new var(--tcp-svc-missing-link-font-size, 1rem)
          per the screenshot's measured cap-height. The old proximity-
          hover padding/margin trick on .tcp-svc-missing-link-wrap
          (built for the now-removed popup) is gone — the wrap is a
          plain inline-flex row now, positioned by its Control Panel
          margins directly, no padding illusion needed.
       5. Per-service CTA line restructured: .tcp-svc-detail-cta (the
          "Contact us for a solution." text) and its new icon badges
          now share a flex parent (.tcp-svc-detail-cta-line,
          services.css) with justify-content: space-between — text
          left, icons pushed to the far right — matching the
          screenshot's layout exactly (client's own word: "justify").
          Icon flyouts in this context open UPWARD
          (.tcp-svc-icon-flyout--up) since the CTA line sits inside
          .tcp-svc-detail-desc's overflow:hidden accordion box, close
          to its bottom edge — opening downward would clip.
   ========================================================================== */

/* Global CSS Variables */
:root {
  --tcp-gold: #c9a33b;
  --tcp-burgundy: #601828;
  --tcp-burgundy-dark: #4a1220;
  --tcp-charcoal: #4a4b4c;
  /* --tcp-charcoal: #241c14; */
  --tcp-cream: #f4efe6;
  /* v1.6.0: front-page service-card hover fill. Deliberately a step
     off --tcp-cream toward warm taupe rather than any accent/red hue —
     "monochromatic to the background" per client direction. Used by
     .tcp-service-card:hover / :focus-visible below. */
  --tcp-service-hover-bg: #ece3d3;
  --tcp-text-dark: #4a4b4c;
  /* --tcp-text-dark: #2b2b2b; */
  --tcp-font-primary: "poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; /* v1.2.8: widened fallback so nav/menu degrades to a real sans-serif, not serif, if Adobe Fonts fails to load */
  --tcp-font-body: "poppins", -apple-system, BlinkMacSystemFont, sans-serif;
  --tcp-font-proxima: "proxima-nova", sans-serif; /* still available site-wide, not currently assigned */
  --tcp-font-display: "Fraunces", serif;
  --tcp-charcoal-glass: rgba(74, 75, 76, var(--tcp-header-bg-opacity-dark));
  --tcp-cream-glass: rgba(244, 239, 230, var(--tcp-header-bg-opacity-light));
  --tcp-ease: cubic-bezier(.16, .84, .44, 1);

  /* ============================================================
     ============================================================
     CSS CONTROL PANEL
     Every adjustable "knob" in this stylesheet lives here, grouped
     by feature area, so tuning a section never means hunting
     through the rest of the file. Add new controls here going
     forward rather than inline in a component's own rule block —
     see the "CSS Control Panel" convention in the project
     instructions doc. Every CSS file in this theme carries a
     section like this now, even ones with nothing in it yet.
     ============================================================
     ============================================================ */

  /* ---- Header opacity & pattern ----
     Edit these five values to dial in the header — nothing else
     in either file needs to change. Everything header-related
     (background-color glass fill above, and the pattern layer
     rules in the "Header" section further down + in
     assets/css/tcp-header-theme.css) reads from these.

     --tcp-header-bg-opacity-light   0–1   header's solid-color
       fill opacity on the light theme (inner pages, and the
       homepage header once scrolled onto a light section)
     --tcp-header-bg-opacity-dark    0–1   same, dark theme
       (homepage header over the hero)
     --tcp-header-pattern-opacity    0–1   opacity of the circle
       pattern overlay itself, independent of the fill above it.
       Pattern currently only renders on the light theme, but this
       controls its opacity if that ever changes.
     --tcp-header-pattern-spacing-x  px    horizontal repeat cell
       size. Image is a 2px dot, so 3px = 1px gap; drop toward 2px
       for a tighter/denser pattern, raise for more gap.
     --tcp-header-pattern-spacing-y  px    same, vertical. */
  --tcp-header-bg-opacity-light: 0.2;
  --tcp-header-bg-opacity-dark: 0.2;
  --tcp-header-pattern-opacity: 0.36;
  --tcp-header-pattern-spacing-x: 3px;
  --tcp-header-pattern-spacing-y: 3px;

  /* ---- Hero figure (Aaron) positioning ----
     Used by .tcp-hero-figure further down. He's positioned via a
     fixed left offset on wide screens, then slides left in step
     with the viewport once it narrows past the point where his
     right edge would otherwise get clipped.

     --tcp-hero-figure-left          px    his fixed distance from
       the viewport's left edge on wide screens.
     --tcp-hero-figure-right-buffer  px    gap kept from the
       viewport's right edge once he starts sliding.
     --tcp-hero-figure-min-left      px    floor — he never slides
       further left than this on very narrow screens.
     --tcp-hero-figure-height        vh    his rendered height;
       width follows automatically from the image's own aspect
       ratio.
     --tcp-hero-figure-max-height    px    caps the vh value above
       on very tall/short viewports.
     --tcp-hero-figure-body-fraction  0–1  what fraction of the
       flattened hero image (aaron-hero-image-w-credentials.png,
       currently 1144x855) is actually Aaron's own opaque figure,
       vs. the credential text baked in beside him. Measured via the
       PNG's alpha channel: his solid body ends around x=620 of
       1144px wide → 620/1144 ≈ 0.542. The slide-trigger calc below
       multiplies the image's real measured width (set at runtime by
       front-page-interactions.js into --tcp-hero-figure-w) by this
       fraction, so the trigger only reacts to HIS width — the text
       portion is left to clip under .tcp-hero's overflow:hidden as
       the viewport narrows, rather than pulling him into motion
       early. Re-measure and update this value if the hero image is
       ever re-exported at different dimensions or he/the text move
       within the canvas. */
  --tcp-hero-figure-left: 620px;
  --tcp-hero-figure-right-buffer: 40px;
  --tcp-hero-figure-min-left: 20px;
  --tcp-hero-figure-height: 79vh;
  --tcp-hero-figure-max-height: 760px;
  --tcp-hero-figure-body-fraction: 0.542;

  /* ---- Hero background scene (size & position) ----
     Used by .tcp-hero-bg further down. The source file
     (tcp-hero-background-only.jpg) is natively 2688x1372 — these
     controls intentionally scale/position it away from its native
     size to get the piano placed correctly within the hero, so
     don't "fix" these to match the native file dimensions.

     --tcp-hero-bg-width       background-size width value (px, %,
       cover, etc.)
     --tcp-hero-bg-height      background-size height value —
       "auto" preserves the image's native aspect ratio at the
       width above.
     --tcp-hero-bg-position-x  background-position horizontal
       keyword/value.
     --tcp-hero-bg-position-y  background-position vertical
       keyword/value. */
  --tcp-hero-bg-width: 2400px;
  --tcp-hero-bg-height: auto;
  --tcp-hero-bg-position-x: center;
  --tcp-hero-bg-position-y: bottom;

  /* ---- Hero responsive breakpoints (v1.5.0) ----
     Documentation only — CSS can't feed a custom property into an
     @media condition, so the actual "@media (max-width: …)" rules
     under "HERO — responsive scaling" further down must be kept in
     sync with these two values by hand if either ever changes.

     --tcp-hero-bp-tablet   px   below this width: hero content
       bottom-anchors, the overlay gradient flips from horizontal- to
       vertical-dominant, the kicker eyebrow and .tcp-plate-label
       caption hide. Deliberately reuses 920px — the same cutoff the
       header nav and .tcp-tick-nav already collapse at — instead of
       introducing a new breakpoint value.
     --tcp-hero-bp-mobile   px   below this width: the hero's CTA
       buttons stack full-width instead of sitting side by side. */
  --tcp-hero-bp-tablet: 920px;
  --tcp-hero-bp-mobile: 560px;

  /* ---- Services section sub-statement (v1.5.0) ----
     Used by .tcp-svc-substatement further down — the "Every service,
     held to a concert-level standard." line that now sits below the
     Services section's lede paragraph, styled in the display typeface
     like a heading but smaller than the actual <h2> above it.
     --tcp-svc-substatement-size   clamp()   font-size. */
  --tcp-svc-substatement-size: clamp(1.3rem, 2.4vw, 1.7rem);

  /* ---- Site-wide scroll-landing offset (v1.7.0) ----
     Sitewide standard for where ANY scroll-navigation-driven landing
     (tick-nav clicks, auto-scroll, jump buttons — anywhere on the
     site) should come to rest: a 35px gap between the bottom of the
     fixed header and the top of the target's first visual element.
     Client-specified exact value (Aug 2026 round), superseding the
     old --tcp-ticknav-scroll-offset (120px, an eyeballed estimate).

     --tcp-header-height   px   the fixed header's actual rendered
       height (see .tcp-header below — currently a hardcoded 100px,
       confirmed via grep that no theme state overrides it). Kept as
       its own variable, separate from the gap below, so the two
       numbers stay independently readable/adjustable.
     --tcp-scroll-gap      px   the 35px landing gap itself — the
       actual client-specified number. Nudge THIS value if the gap
       itself ever needs adjusting; leave --tcp-header-height alone
       unless the header's own height changes.
     --tcp-scroll-margin    computed — don't edit directly. Every
       scroll-target element (.tcp-scroll-target below, plus
       #services-heading) reads this. A page that needs a DIFFERENT
       gap (brokerage.css's 48px "Current Instruments" exception)
       defines its own local override of the same formula rather than
       changing this shared default — see that file's Control Panel. */
  --tcp-header-height: 100px;
  --tcp-scroll-gap: 35px;
  --tcp-scroll-margin: calc(var(--tcp-header-height) + var(--tcp-scroll-gap));

  /* ---- Tick-nav visibility (v1.9.0) ----
     The right-side tick nav was effectively invisible on landing: its
     ticks rendered 1px tall at 0.35 alpha and its labels sat at
     opacity 0 until hover, so a visitor arriving on a page saw
     almost nothing at all. These knobs set its RESTING legibility —
     the hover/active treatment is unchanged and still reads as a
     clear step up from resting.

     Consumed by the ".tcp-tick-nav" block further down this file, and
     mirrored (not shared — separate component, per Aaron's 2026-08-07
     instruction) by assets/css/services.css for .tcp-svc-tick-nav.

     --tcp-tick-rest-alpha      0-1   resting opacity of the tick MARK
       itself, applied to both the on-dark cream and on-light charcoal
       variants. 0.35 was the old value and read as invisible; 0.62 is
       the middle ground that stays quiet without disappearing.
     --tcp-tick-text-rest-alpha 0-1   resting opacity of the tick LINK
       colour (the label's colour before hover).
     --tcp-tick-label-rest      0-1   resting opacity of the LABEL
       itself. Was 0 (labels fully hidden until hover) — the single
       biggest reason the nav read as "not there". Set above 0 so the
       section names are legible the moment the page loads.
     --tcp-tick-height          px    thickness of the tick mark. 1px
       sub-pixel-renders away on some displays; 2px always draws.
     --tcp-tick-width-rest      px    resting tick length.
     --tcp-tick-width-active    px    tick length on hover/active. */
  --tcp-tick-rest-alpha: 0.78;
  --tcp-tick-text-rest-alpha: 0.88;
  --tcp-tick-label-rest: 0.78;
  --tcp-tick-height: 2px;
  --tcp-tick-width-rest: 22px;
  --tcp-tick-width-active: 38px;

  /* ---- Tick-nav clearance (v1.9.2) ----
     How much room the fixed tick nav needs reserved on the right edge
     so absolutely-positioned hero furniture does not end up underneath
     it. The nav's own footprint is right-offset (32px) + tick length
     (up to --tcp-tick-width-active) + label gap (10px) + the longest
     label rendered at .62rem/.14em tracking, which on the widest pages
     ("CLIMATE CONTROL", "WHAT WE RECOMMEND") lands near 220px.

     Consumed by .tcp-page-hero-plate in assets/css/tcp-interior.css —
     the interior pages' bottom-right hero caption, which was rendering
     directly beneath the nav's lower items.

     Only matters above 920px; below that the tick nav is hidden and the
     plate is display:none anyway. */
  --tcp-ticknav-clearance: 220px;

  /* ---- Hero self-scheduling module offset (v1.9.3) ----
     Extra right-edge inset for .tcp-hero-booking on the front page,
     on top of its 5vw base. Nudges the module clear of the tick nav's
     upper labels, which were running up against its right edge.

     --tcp-hero-booking-offset  px  added to the module's right
       position. Raise it to push the module further left, lower it to
       let it sit closer to the edge. Consumed only by
       .tcp-hero-booking's `right` below; it does not move the tick nav
       itself (see --tcp-ticknav-clearance above for that side of the
       relationship). No effect below 1100px, where the module leaves
       absolute positioning and rejoins the hero's column flow. */
  --tcp-hero-booking-offset: 40px;
}

html,
body {
  margin: 0;
  padding: 0;
  font-family: var(--tcp-font-body);
}

html {
  background: var(--tcp-charcoal);
  scroll-behavior: smooth;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--tcp-cream) !important; /* Astra core ships its own
    body { background-color: #fff; } rule that was winning in any gap
    where this background shows through (e.g. margin above the footer) */
  font-family: var(--tcp-font-primary);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--tcp-font-display);
  font-weight: 500;
  margin: 0;
  color: #f3f3f3 !important; /* Astra ships its own bare h1-h6 color rule
                                 at equal specificity, so this needs
                                 !important rather than relying on load
                                 order. .tcp-section-light below still
                                 overrides this correctly for light
                                 sections since it's more specific. */
}

.tcp-italic-accent {
  font-style: italic;
  font-weight: 400;
  color: var(--tcp-gold);
}

#page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  flex: 1;
}

.site-content,
main,
#primary,
#tcp-home,
#tcp-about,
#tcp-services,
#tcp-contact {
  flex: 1 0 auto;
}

.tcp-footer {
  flex-shrink: 0;
}

.ast-separate-container {
  background-color: var(--tcp-cream) !important;
}

/* Custom styles go below this line. */
/* ==========================================================================
   Layout container
   ========================================================================== */
.tcp-container {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 20px;
}

/* ==========================================================================
   Scroll reveal utility classes
   ========================================================================== */
.tcp-anim-fade {
  opacity: 0;
  transition: opacity .9s var(--tcp-ease);
}
.tcp-anim-fade.is-visible {
  opacity: 1;
}

.tcp-anim-slide-up {
  opacity: 0;
  transform: translateY(36px);
  transition: opacity .9s var(--tcp-ease), transform .9s var(--tcp-ease);
}
.tcp-anim-slide-up.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .tcp-anim-fade,
  .tcp-anim-slide-up {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .tcp-contact-btn,
  .tcp-contact-btn .tcp-cbtn-label,
  .tcp-contact-btn .tcp-cbtn-value {
    transition: none !important;
  }

  .tcp-cta-contact-item,
  .tcp-cta-contact-link,
  .tcp-cta-contact-link .tcp-clabel,
  .tcp-cta-contact-link .tcp-cval,
  .tcp-copy-btn,
  .tcp-copy-icon {
    transition: none !important;
  }
}

/* ==========================================================================
   Header — fixed, solidifies on scroll, and flips between dark (cream
   text, on the homepage's dark hero) and light (charcoal text, over
   every other section/page) chrome. Theme is decided by
   assets/js/tcp-header-theme.js, which reads data-theme off whichever
   section sits behind the fixed header and sets that same value (plus
   .is-solid) directly on .tcp-header — see assets/css/tcp-header-theme.css
   for the data-theme-driven rules this replaces the old
   body.theme-dark/light header rules with. Works identically on every
   template, not just the front page.
   ========================================================================== */

/* header style,  hstyle, hdrcss headercss */

.tcp-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: 100px;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background .5s var(--tcp-ease), border-color .5s var(--tcp-ease),
    padding .4s var(--tcp-ease), backdrop-filter .5s var(--tcp-ease), box-shadow .4s var(--tcp-ease);
    border-bottom: none;
}

.tcp-header-inner {
  position: relative; /* v1.3.5 (restored v1.3.7): needed so z-index below
    actually applies — stacks the real header content above
    .tcp-header-bg-pattern regardless of DOM order (a positioned sibling
    would otherwise paint on top of an unpositioned one no matter which
    comes first in the markup). */
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  padding: 0 30px 0 40px; /* left 40px (logo), right 30px (nav) */
  transition: color .45s var(--tcp-ease);
}

/* v1.3.5 (restored v1.3.7): circle-repeat pattern layer. Structural/
   sizing rules only — hidden by default here; assets/css/
   tcp-header-theme.css turns it on for the light header theme only and
   supplies the actual image, so the "when does this render" logic
   stays with the rest of the data-theme rules instead of being split
   across two files. Separate div (not a ::before on .tcp-header) so
   this and the header's background-color fill (--tcp-cream-glass,
   above) can be adjusted independently. */
.tcp-header-bg-pattern {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-repeat: repeat;
  background-position: center;
  background-size: var(--tcp-header-pattern-spacing-x) var(--tcp-header-pattern-spacing-y);
  /* v1.3.8: was a hardcoded "3px 3px" — now reads from the HEADER
     OPACITY & PATTERN CONTROLS block near the top of :root. Image is
     a 2px dot, so the default 3px/3px cell gives a 1px gap on both
     axes, per spec. */
}

.tcp-logo-img {
  height: 56px;
  width: auto;
  display: block;
}

/* Two logo variants: light-colored mark for the dark header chrome,
   dark-colored mark for the light header chrome. Both display states
   now live in assets/css/tcp-header-theme.css, keyed off .tcp-header's
   data-theme attribute instead of a body class — no flash either way. */

.tcp-primary-nav .tcp-menu {
  display: flex;
  gap: 2.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tcp-primary-nav .tcp-menu a {
  text-decoration: none;
  color: #d9d9d9;
  font-family: var(--tcp-font-primary);
  font-weight: 300;
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  position: relative;
  padding-bottom: 6px;
  opacity: .85;
  transition: opacity .3s var(--tcp-ease), color .3s var(--tcp-ease);
}
.tcp-primary-nav .tcp-menu a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--tcp-gold);
  transition: width .35s var(--tcp-ease);
}
.tcp-primary-nav .tcp-menu a:hover { opacity: 1; color: var(--tcp-gold); }
.tcp-primary-nav .tcp-menu a:hover::after { width: 100%; }

/* Light-header nav-link colors now live in assets/css/tcp-header-theme.css,
   keyed off .tcp-header[data-theme="light"] instead of a body class. */

.tcp-header-cta {
  margin-left: 2.4rem;
}

/* Hamburger button */
.tcp-menu-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  color: #f3f3f3;
}

.tcp-bar {
  width: 24px;
  height: 2px;
  background: currentColor;
}

@media (max-width: 920px) {
  .tcp-primary-nav,
  .tcp-header-cta,
  .tcp-tick-nav {
    display: none;
  }
  .tcp-menu-toggle { display: flex; }
}

@media (max-width: 768px) {
  .tcp-primary-nav {
    position: absolute;
    top: 100px;
    left: 0;
    right: 0;
    background: rgba(0, 0, 0, 0.92);
    z-index: 10;
    margin-top: 0;
  }
  .tcp-primary-nav.is-open { display: block; }
  .tcp-primary-nav .tcp-menu {
    flex-direction: column;
    gap: 0;
    padding: 8px 24px;
  }
  .tcp-primary-nav .tcp-menu li {
    padding: 10px 0;
    border-bottom: 1px solid rgba(244, 239, 230, 0.15);
  }
}

/* ==========================================================================
   Buttons — outline (default) and filled variants
   ========================================================================== */
.tcp-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 28px;
  font-size: .72rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--tcp-cream);
  border: 1px solid rgba(244, 239, 230, 0.55);
  background: transparent;
  position: relative;
  overflow: hidden;
  transition: border-color .35s var(--tcp-ease), transform .35s var(--tcp-ease), color .35s var(--tcp-ease);
}
.tcp-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--tcp-gold);
  mix-blend-mode: multiply;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s var(--tcp-ease);
  z-index: 0;
}
.tcp-btn span { position: relative; z-index: 1; }
.tcp-btn:hover { border-color: var(--tcp-gold); transform: translateY(-2px); }
.tcp-btn:hover::before { transform: scaleX(1); }

.tcp-btn-primary {
  background: var(--tcp-burgundy) !important; /* v1.2.8: !important safety net — this fill went missing/transparent last night */
  border-color: var(--tcp-burgundy) !important;
  color: var(--tcp-cream) !important;
}
.tcp-btn-primary::before { background: var(--tcp-gold); mix-blend-mode: multiply; }
.tcp-btn-primary:hover { border-color: var(--tcp-gold) !important; }

/* Header CTA is the one exception: once the header goes light (inner
   pages, or scrolled state on Home) it switches to an outline button
   instead of a solid fill — see assets/css/tcp-header-theme.css, which
   overrides these !important rules with an equally-specific
   [data-theme="light"] selector scoped to .tcp-header-cta only. */

.tcp-btn-secondary {
  background: transparent;
  border-color: rgba(244, 239, 230, 0.55);
}

/* Buttons on light sections need dark-friendly outline/fill */
.tcp-section-light .tcp-btn-secondary { border-color: rgba(74, 75, 76, 0.4); color: var(--tcp-charcoal); }
.tcp-section-light .tcp-btn-secondary:hover { border-color: var(--tcp-burgundy); }

/* Light-header CTA button colors now live in assets/css/tcp-header-theme.css,
   keyed off .tcp-header[data-theme="light"] instead of a body class. */

/* ==========================================================================
   Hero
   ========================================================================== */
.tcp-hero {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 5vw;
  position: relative;
  z-index: 0;
  color: var(--tcp-cream);
  background-color: var(--tcp-charcoal);
  overflow: hidden;
}

.tcp-hero-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  background-image: url('assets/images/tcp-hero-background-only.jpg');
  background-repeat: no-repeat;
  background-position: var(--tcp-hero-bg-position-x) var(--tcp-hero-bg-position-y);
  /* background-position: bottom center; */
  background-size: var(--tcp-hero-bg-width) var(--tcp-hero-bg-height);
  /* background-size: 2400px auto; */
  pointer-events: none;
}

.tcp-hero-overlay {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* v1.5.0: horizontal-dominant by default (text column on the left
     reads on solid tone, photo breathes on the right) — flips to a
     vertical-dominant gradient at --tcp-hero-bp-tablet once the hero
     content relocates to the bottom of the frame, see "HERO —
     responsive scaling" below. Also folds in the charcoal-token fix
     flagged in the v1.3.4 note above (first stop was still the old
     43,33,24 value; now consistently 74,75,76 like the rest of the
     file) since this rule was being rewritten anyway. */
  background:
    radial-gradient(ellipse at 75% 30%, rgba(168, 135, 47, 0.14), transparent 5%),
    linear-gradient(90deg, rgba(74, 75, 76, 0.88) 0%, rgba(74, 75, 76, 0.6) 32%, rgba(74, 75, 76, 0.28) 58%, rgba(74, 75, 76, 0.08) 82%, rgba(74, 75, 76, 0.02) 100%),
    linear-gradient(180deg, rgba(74, 75, 76, 0.12) 0%, rgba(74, 75, 76, 0.28) 60%, rgba(74, 75, 76, 0.5) 100%);
  pointer-events: none;
  transition: background .5s var(--tcp-ease);
}

/* v1.5.0: temporary stand-in for the real photo while
   .tcp-hero-figure/.tcp-hero-bg's photo markup is out of front-page.php
   — see the version note at the top of this file. Only the
   background-image differs from the base rule above; background-size/
   position still read from the same --tcp-hero-bg-* control-panel
   values, so this is a drop-in swap either direction. */
.tcp-hero-bg.tcp-hero-bg--placeholder {
  background-image: url('assets/images/tcp-hero-bg-placeholder-temp.jpg');
}

/* v1.5.0: screen-reader-only utility. Standard clip-based visually-
   hidden pattern (not display:none, so it stays in the accessibility
   tree) — used in the hero right now to keep Aaron's name/credentials
   in the page while his visual figure is temporarily removed. */
.tcp-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- Aaron figure, separated from the hero background image ----
   Stacking order (all relative to .tcp-hero, which is the positioning
   context established above): bg (-2) < overlay (-1) < figure (1) <
   text content (2).

   All of --tcp-hero-figure-left/-right-buffer/-min-left/-height/
   -max-height/-body-fraction now live in the :root CSS CONTROL PANEL
   above, not here — edit them there.

   Horizontal position logic: on wide viewports he sits at a fixed
   --tcp-hero-figure-left from the left edge. As the viewport narrows,
   min() switches to the second value once "100vw minus his effective
   width minus the right buffer" drops below that fixed value — at
   that point his RIGHT edge holds a fixed distance from the
   viewport's right edge instead, so he slides left in step with the
   shrinking window rather than getting clipped or ducking under the
   headline. max() puts a floor on it so he never slides past
   --tcp-hero-figure-min-left off the left side on very narrow screens.

   --tcp-hero-figure-w is set at runtime by front-page-interactions.js
   (it measures the actual rendered width of the full PNG — Aaron plus
   his baked-in credential text — since width is auto/derived from
   height). That measured width is multiplied by
   --tcp-hero-figure-body-fraction below so the slide trigger reacts
   to HIS width only, not the wider text-inclusive canvas — the text
   portion clips under .tcp-hero's overflow:hidden as the viewport
   narrows, and only starts sliding once the shrink would actually
   crop him. The 420px fallback is just the pre-JS/no-JS default. */
.tcp-hero-figure {
  position: absolute;
  z-index: 1;
  bottom: 0;
  left: max(
            var(--tcp-hero-figure-min-left),
            min(
            var(--tcp-hero-figure-left),
            calc(100vw - (var(--tcp-hero-figure-w, 420px) * var(--tcp-hero-figure-body-fraction)) - var(--tcp-hero-figure-right-buffer))
    )
  );
  height: var(--tcp-hero-figure-height);
  max-height: var(--tcp-hero-figure-max-height);
  width: auto;
  pointer-events: none;
  transition: left .01s ease;
}
.tcp-hero-figure-img {
  display: block;
  height: 100%;
  width: auto;
  max-width: none; /* v1.4.1: Astra core ships a global "img { max-width:
    100%; }" reset. This rule never set max-width at all, so Astra's
    kept applying underneath it unopposed. Because .tcp-hero-figure
    (the parent) is width:auto/shrink-to-fit, a percentage max-width
    on a child of a not-yet-sized shrink-to-fit box is a circular
    calculation — browsers resolve it inconsistently, and here it was
    collapsing him down to ~99px wide instead of his real proportional
    width (confirmed via DevTools: intrinsic 1144x855, rendered
    99x756). max-width:none removes Astra's rule from the equation
    entirely so width:auto is free to size him from his real aspect
    ratio again. This bug predates the credentials-baked-in image; the
    new, wider image just made it visible for the first time. */
}

/* Text needs its own stacking context above the figure — without
   this, normal-flow content (h1, p, etc.) stacks below any positioned
   positive z-index sibling regardless of DOM order. */
.tcp-hero-content {
  position: relative;
  z-index: 2;
}

.tcp-eyebrow {
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: .72rem;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--tcp-gold) !important; /* v1.2.8: !important safety net — this is the exact rule that went invisible last night */
  margin-bottom: 28px;
}
.tcp-eyebrow .tcp-rule {
  width: 60px;
  height: 1px;
  background: linear-gradient(90deg, var(--tcp-gold), transparent);
}

.tcp-hero h1 {
  font-size: clamp(2.8rem, 7vw, 5.6rem);
  line-height: 1.02;
  /* v1.9.0: was -.01em. Standing brand rule is a +0.01em floor on
     tracking everywhere — nothing on this site is ever set tighter. */
  letter-spacing: .01em;
}
.tcp-hero p.tcp-sub {
  max-width: 560px;
  margin-top: 28px;
  font-size: 1.05rem;
  line-height: 1.7;
  color: rgba(244, 239, 230, 0.78);
}
.tcp-hero-actions {
  display: flex;
  gap: 18px;
  margin-top: 44px;
  flex-wrap: wrap;
}

/* ==========================================================================
   Hero self-scheduling module (v1.9.0)
   Top-right of the hero, deliberately outside .tcp-hero-actions above.
   Booking is the site's primary conversion — putting it in the button row
   would demote it to one of four equal options. See front-page.php's
   comment on this block for the full reasoning.
   ========================================================================== */
.tcp-hero-booking {
  position: absolute;
  top: calc(var(--tcp-header-height) + 48px);
  right: calc(5vw + var(--tcp-hero-booking-offset));
  z-index: 3;
  width: min(300px, 30vw);
  text-align: right;
  /* Sits above the hero's dark photographic background, so it needs its
     own quiet ground to stay readable over a busy image rather than
     relying on the overlay alone. */
  padding: 22px 24px;
  background: rgba(36, 28, 20, 0.55);
  border: 1px solid rgba(168, 135, 47, 0.28);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.tcp-hero-booking-label {
  font-size: .62rem;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--tcp-gold);
  margin-bottom: 10px;
}
.tcp-hero-booking-lede {
  margin: 0 0 18px;
  font-size: .92rem;
  line-height: 1.5;
  letter-spacing: .01em;
  color: rgba(244, 239, 230, 0.86);
}
.tcp-hero-booking-btn {
  width: 100%;
  justify-content: center;
}
/* Sets the expectation that this link leaves the site for Gazelle, whose
   own UI we cannot brand — an expected handoff instead of a jarring jump. */
.tcp-hero-booking-note {
  margin: 12px 0 0;
  font-size: .66rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.45);
}

/* Below 1100px the absolute placement starts crowding both the headline
   and the fixed tick nav, so the module rejoins the normal column flow.
   Flex order keeps it BELOW the headline rather than above it, which is
   where its DOM position would otherwise put it. */
@media (max-width: 1100px) {
  .tcp-hero { justify-content: center; }
  .tcp-hero-content { order: 1; }
  .tcp-hero-booking {
    position: static;
    order: 2;
    width: 100%;
    max-width: 420px;
    margin-top: 36px;
    text-align: left;
  }
  .tcp-hero-booking-btn { width: auto; }
}
/* Closing-CTA button pair (v1.9.0) — replaces the inline
   style="margin-top:44px" that used to sit on the front page's lone
   closing button. Book (filled) leads; Contact (outlined) follows. */
.tcp-cta-actions {
  display: flex;
  gap: 18px;
  margin-top: 44px;
  flex-wrap: wrap;
  justify-content: center;
}

.tcp-plate-label {
  position: absolute;
  right: 5vw;
  bottom: 34px;
  font-size: .68rem;
  letter-spacing: .18em;
  color: rgba(244, 239, 230, 0.4);
  text-transform: uppercase;
}

/* v1.5.0: second hero eyebrow, ported from the scaling demo. Reuses
   .tcp-eyebrow's color/size/tracking but drops the flex layout and
   .tcp-rule tick (there isn't one in this variant — it's plain text),
   and sits tighter to the headline above it than the standard 28px
   .tcp-eyebrow margin. */
.tcp-eyebrow-accent {
  display: block;
  margin-bottom: 14px;
}

/* v1.5.0: role line under the hero's thesis paragraph — "Aaron
   Sandner, RPT — Founder & Lead Piano Technician". */
.tcp-hero-role {
  margin-top: 18px;
  font-size: .92rem;
  letter-spacing: .02em;
  color: rgba(244, 239, 230, 0.72);
}

/* ==========================================================================
   HERO — responsive scaling (v1.5.0)
   Reverse-engineered from gurbakshchahal.com and rebuilt standalone as
   the tonecraft-hero-scaling-demo mockup (see
   _handoff/tonecraft-hero-scaling-demo/README.md for the full
   breakdown), adapted here to this theme's real markup/tokens. Below
   --tcp-hero-bp-tablet (920px, documented in the Control Panel above):
   hero content bottom-anchors instead of vertically centering, the
   overlay gradient flips from horizontal- to vertical-dominant (see
   .tcp-hero-overlay above), and the kicker eyebrow + corner caption
   hide since there's no longer room for them without crowding the
   headline. Below --tcp-hero-bp-mobile (560px) the CTA row stacks.
   ========================================================================== */
@media (max-width: 920px) {
  .tcp-hero {
    justify-content: flex-end;
    padding-bottom: clamp(2.4rem, 8vw, 3.6rem);
  }
  .tcp-hero-overlay {
    background:
      radial-gradient(ellipse at 75% 15%, rgba(168, 135, 47, 0.12), transparent 5%),
      linear-gradient(180deg, rgba(74, 75, 76, 0.3) 0%, rgba(74, 75, 76, 0.5) 45%, rgba(74, 75, 76, 0.92) 100%);
  }
  .tcp-hero-kicker,
  .tcp-plate-label {
    display: none;
  }
  .tcp-hero-actions {
    margin-top: 32px;
  }
}

@media (max-width: 560px) {
  .tcp-hero-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .tcp-hero-actions .tcp-btn {
    justify-content: center;
  }
}

/* ==========================================================================
   Right-side tick navigation
   ========================================================================== */
.tcp-tick-nav {
  position: fixed;
  right: 32px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 22px;
  align-items: flex-end;
}
/* Resting state is deliberately legible, not hidden (v1.9.0) — see the
   "Tick-nav visibility" Control Panel subsection above for why and for
   the knobs every alpha/size below reads. The on-dark treatment is the
   base; .is-over-dark / on-light overrides follow. */
.tcp-tick-nav a {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: rgba(244, 239, 230, var(--tcp-tick-text-rest-alpha));
  transition: color .35s var(--tcp-ease);
}
.tcp-tick-nav .tcp-tick {
  width: var(--tcp-tick-width-rest);
  height: var(--tcp-tick-height);
  background: rgba(244, 239, 230, var(--tcp-tick-rest-alpha));
  transition: width .35s var(--tcp-ease), background .35s var(--tcp-ease);
}
.tcp-tick-nav .tcp-tick-label {
  font-size: .62rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: var(--tcp-tick-label-rest);
  transform: translateX(0);
  transition: opacity .3s var(--tcp-ease), transform .3s var(--tcp-ease);
  white-space: nowrap;
}
.tcp-tick-nav a:hover .tcp-tick-label,
.tcp-tick-nav a.is-active .tcp-tick-label { opacity: 1; transform: translateX(0); }
.tcp-tick-nav a:hover .tcp-tick,
.tcp-tick-nav a.is-active .tcp-tick { width: var(--tcp-tick-width-active); background: var(--tcp-gold); }
.tcp-tick-nav a.is-active { color: var(--tcp-gold); }

/* No-JS fallback only. Wrapped in :where() so it carries almost no
   specificity and never competes with the per-link classes below — those
   are the real source of truth whenever the contrast engine is running.
   Before v1.9.1 this was an unwrapped `body.theme-light .tcp-tick-nav a`,
   which at (0,2,2) actually OUTRANKED the contrast classes and was the
   reason the nav still rendered charcoal-on-charcoal over the hero. */
:where(body.theme-light) .tcp-tick-nav a { color: rgba(36, 28, 20, var(--tcp-tick-text-rest-alpha)); }
:where(body.theme-light) .tcp-tick-nav .tcp-tick { background: rgba(36, 28, 20, var(--tcp-tick-rest-alpha)); }
:where(body.theme-light) .tcp-tick-nav a:hover .tcp-tick,
:where(body.theme-light) .tcp-tick-nav a.is-active .tcp-tick { background: var(--tcp-burgundy); }
:where(body.theme-light) .tcp-tick-nav a.is-active { color: var(--tcp-burgundy); }

/* ==========================================================================
   Live per-link contrast (v1.9.1)
   assets/js/tcp-tick-nav-contrast.js classes EACH LINK, not the nav, from
   the [data-theme] section under that link's own midpoint.

   Per link, because per nav is not expressible here: the nav is vertically
   centred and taller than the interior pages' dark hero band, so at
   scroll-top its upper items are genuinely on charcoal while its lower
   items are genuinely on cream. A single class for the whole nav has to
   pick one and be wrong about the other half.
   ========================================================================== */
.tcp-tick-nav a.is-over-dark { color: rgba(244, 239, 230, var(--tcp-tick-text-rest-alpha)); }
.tcp-tick-nav a.is-over-dark .tcp-tick { background: rgba(244, 239, 230, var(--tcp-tick-rest-alpha)); }
.tcp-tick-nav a.is-over-dark:hover .tcp-tick,
.tcp-tick-nav a.is-over-dark.is-active .tcp-tick { background: var(--tcp-gold); }
.tcp-tick-nav a.is-over-dark.is-active { color: var(--tcp-gold); }

.tcp-tick-nav a.is-over-light { color: rgba(36, 28, 20, var(--tcp-tick-text-rest-alpha)); }
.tcp-tick-nav a.is-over-light .tcp-tick { background: rgba(36, 28, 20, var(--tcp-tick-rest-alpha)); }
.tcp-tick-nav a.is-over-light:hover .tcp-tick,
.tcp-tick-nav a.is-over-light.is-active .tcp-tick { background: var(--tcp-burgundy); }
.tcp-tick-nav a.is-over-light.is-active { color: var(--tcp-burgundy); }

/* Generic scroll-landing target (v1.7.0) — any element a tick-nav
   link, jump button, or auto-scroll lands on should carry this class
   so it comes to rest with the sitewide 35px header-bottom gap (see
   --tcp-scroll-margin in the Control Panel above). A page needing a
   different gap defines its own local class instead (e.g. brokerage.
   css's .tcp-scroll-target-brokerage, 48px). */
.tcp-scroll-target {
  scroll-margin-top: var(--tcp-scroll-margin);
}

/* ==========================================================================
   Sections — dark hero + alternating white/cream "light" sections
   ========================================================================== */
section { padding: 140px 5vw; position: relative; }

.tcp-section-head { max-width: 640px; margin-bottom: 64px; }
.tcp-section-head .tcp-eyebrow { margin-bottom: 18px; }
.tcp-section-head h2 { font-size: clamp(2rem, 4vw, 3rem); }

.tcp-section-light {
  color: var(--tcp-charcoal);
  padding: 150px 5vw 90px;
}
.tcp-section-light.tcp-bg-white { background: #FFFFFF; }
.tcp-section-light.tcp-bg-cream { background: var(--tcp-cream); }
.tcp-section-light .tcp-eyebrow { color: var(--tcp-burgundy); }
.tcp-section-light .tcp-eyebrow .tcp-rule { background: linear-gradient(90deg, var(--tcp-burgundy), transparent); width: 34px; }
.tcp-section-light h2 { color: var(--tcp-charcoal) !important; font-size: clamp(2.2rem, 4.6vw, 3.4rem); line-height: 1.08; }
.tcp-section-light .tcp-sub,
.tcp-section-light p { color: rgba(74, 75, 76, 0.68); max-width: 640px; margin-top: 22px; font-size: 1.02rem; line-height: 1.8; }
.tcp-section-light .tcp-italic-accent { color: var(--tcp-burgundy); }

.tcp-plate-mark {
  position: absolute;
  top: 150px;
  right: 5vw;
  font-family: var(--tcp-font-display);
  font-style: italic;
  font-size: .95rem;
  color: rgba(74, 75, 76, 0.4);
  letter-spacing: .08em;
}

/* ===== Stat row (The Craft / numbers) ===== */
.tcp-stat-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid rgba(74, 75, 76, 0.18);
  border-bottom: 1px solid rgba(74, 75, 76, 0.18);
  margin-top: 60px;
}
.tcp-stat { padding: 34px 28px; border-left: 1px solid rgba(74, 75, 76, 0.18); }
.tcp-stat:first-child { border-left: none; padding-left: 0; }
.tcp-stat .tcp-stat-num {
  font-family: var(--tcp-font-display);
  font-weight: 500;
  color: var(--tcp-burgundy);
  font-size: clamp(2.4rem, 4vw, 3.4rem);
  line-height: 1;
}
.tcp-stat .tcp-stat-label { margin-top: 12px; font-size: .82rem; color: rgba(74, 75, 76, 0.62); }

.tcp-trained-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-top: 44px;
  margin-top: 44px;
}
.tcp-trained-strip .tcp-tlead {
  font-size: .68rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(74, 75, 76, 0.45);
}
.tcp-trained-strip .tcp-marks { display: flex; flex-wrap: wrap; gap: 44px; }
.tcp-trained-strip .tcp-marks span {
  font-family: var(--tcp-font-display);
  font-size: 1.15rem;
  color: rgba(74, 75, 76, 0.55);
  letter-spacing: .02em;
}

@media (max-width: 920px) {
  .tcp-stat-row { grid-template-columns: repeat(2, 1fr); }
  .tcp-plate-mark { display: none; }
}

/* v1.6.5: the duplicate "STORY SECTION — piano strings background"
   #story rule that used to live here was removed — front-page.css
   (front-page-only stylesheet) is the correct home for it, not this
   global one, and the two copies had drifted into pointing at the
   same (at the time 404ing) file two different ways. See the
   style.css v1.6.5 changelog entry and the surviving copy in
   front-page.css for the fix. */

/* v1.5.0: second heading-style statement under the Services section's
   lede paragraph — "Every service, held to a concert-level standard."
   Styled in the display typeface like the h2 above it, but smaller;
   overrides the generic ".tcp-section-light p" body-copy rule further
   up, which would otherwise apply to this <p> too. Size is a Control
   Panel value (--tcp-svc-substatement-size) since there was no
   existing convention to match here. */
.tcp-svc-substatement {
  font-family: var(--tcp-font-display);
  font-weight: 500;
  font-size: var(--tcp-svc-substatement-size) !important;
  line-height: 1.25;
  color: var(--tcp-charcoal) !important;
  opacity: 1 !important;
  max-width: 640px;
  margin-top: 14px !important;
}

/* v1.6.2: tick-nav's Services link scrolls here instead of #services'
   own top (see data-scroll-target in tick-nav.php + the click handler
   in front-page-interactions.js) — lands with the eyebrow above
   cropped out and this heading framed just under the fixed header.
   v1.7.0: now reads the sitewide --tcp-scroll-margin (35px gap)
   instead of the old dedicated --tcp-ticknav-scroll-offset var. */
#services-heading {
  scroll-margin-top: var(--tcp-scroll-margin);
}

/* ===== Services grid ===== */
.tcp-services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  margin-top: 8px;
}
/* v1.6.0: cards are now <a> links to the Services page (were plain
   <div>s) — reset link styling, and add horizontal padding + a
   matching negative margin so the hover fill below has room to show
   as a "boxed" highlight without shifting where the text itself sits
   at rest (padding pushes content back in exactly as far as the
   margin pulls the box out). padding-bottom is new too (there wasn't
   one before) so the hover box reads as a symmetrical box, not just a
   top-heavy strip. */
.tcp-service-card {
  display: block;
  border-top: 1px solid rgba(74, 75, 76, 0.22);
  padding: 24px 20px 28px;
  margin: 0 -20px;
  border-radius: 3px;
  text-decoration: none;
  color: inherit;
  /* transform kept at .35s (faster than .tcp-anim-slide-up's .9s,
     which also animates transform on entrance) so the hover lift
     feels snappy rather than inheriting the slow scroll-reveal pace —
     opacity stays at .9s so the entrance fade itself still matches
     every other .tcp-anim-slide-up element on the page. */
  transition: opacity .9s var(--tcp-ease), transform .35s var(--tcp-ease),
    background-color .35s var(--tcp-ease), box-shadow .35s var(--tcp-ease);
}
/* Bottom row (services 4/5/6 in the 3-column grid) — bookend with a
   border-bottom to match, so it reads the same "line above and
   below" as the top row instead of only having the shared line the
   two rows already meet at. */
.tcp-service-card:nth-child(n+4) {
  border-bottom: 1px solid rgba(74, 75, 76, 0.22);
}
.tcp-service-card:hover,
.tcp-service-card:focus-visible {
  background-color: var(--tcp-service-hover-bg);
  transform: translateY(-4px);
  box-shadow: 0 16px 34px -18px rgba(74, 75, 76, 0.32);
}
.tcp-service-card .tcp-card-num {
  font-family: var(--tcp-font-display);
  font-style: italic;
  color: var(--tcp-burgundy);
  font-size: 1.1rem;
}
.tcp-service-card h3 { font-size: 1.3rem; margin: 14px 0 12px; color: var(--tcp-charcoal) !important; }
.tcp-service-card p { color: rgba(74, 75, 76, 0.65); line-height: 1.7; font-size: .95rem; margin-top: 0; transition: color .35s var(--tcp-ease); }
.tcp-service-card:hover p,
.tcp-service-card:focus-visible p {
  color: rgba(74, 75, 76, 0.88);
}

.tcp-link-more {
  display: inline-block;
  margin-top: 40px;
  font-weight: 600;
  color: var(--tcp-burgundy);
  text-decoration: none;
  letter-spacing: .02em;
}
.tcp-link-more:hover { color: var(--tcp-gold); }

@media (max-width: 920px) {
  .tcp-services-grid { grid-template-columns: 1fr; }
}

/* ===== Service area — three tiers, styled like the services cards ===== */
.tcp-area-tiers {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  margin-top: 8px;
}
.tcp-area-tier { border-top: 1px solid rgba(74, 75, 76, 0.22); padding-top: 24px; }
.tcp-area-tier h3 { font-size: 1.15rem; margin: 0 0 12px; color: var(--tcp-burgundy) !important; }
.tcp-area-tier p { color: rgba(74, 75, 76, 0.65); line-height: 1.7; font-size: .95rem; margin-top: 0; }

@media (max-width: 920px) {
  .tcp-area-tiers { grid-template-columns: 1fr; }
}

/* ===== Contact grid (CTA section) =====
   v1.3.2: Restructured from a single <a class="tcp-cta-contact-item">
   per platform into a wrapper <div class="tcp-cta-contact-item"> that
   holds two independent children: the real tel:/mailto:/https: link
   (.tcp-cta-contact-link, absolutely positioned to fill the wrapper —
   carries the border/background/padding the old single <a> used to)
   and a separate <button class="tcp-copy-btn"> in the top-right
   corner. A <button> can't legally sit inside an <a>, so this split
   was required once the copy feature was added, not just a style
   choice. Label/value crossfade on hover/focus-within exactly as
   before (v1.3.1).
   Hover-EXPAND sizing: rather than letting the revealed value
   overflow past the item's resting width (the v1.3.1 approach), the
   item's flex-basis now grows on hover/focus-within to
   var(--tcp-cval-w), a per-item pixel value that
   front-page-interactions.js measures from each .tcp-cval's actual
   rendered text width on load (mirrors the existing hero-figure
   ResizeObserver pattern) and re-measures on resize + once Adobe
   Fonts finishes loading, since font metrics before/after Fraunces
   loads can differ. Falls back to 200px via the var()'s second
   argument if JS hasn't run yet (e.g. JS error) — the item just
   won't expand on hover in that case, no layout is broken.
   Neighboring items keep flex:1 1 200px, so they absorb the change
   in flex-shrink and the row wraps naturally at narrow widths.
   z-index is raised on hover/focus-within so the growing item never
   renders beneath a neighbor mid-transition. */
.tcp-cta-contact-grid {
  display: flex;
  border-top: 1px solid rgba(244, 239, 230, 0.15);
  margin-top: 48px;
  max-width: 760px;
  flex-wrap: wrap;
}
.tcp-cta-contact-item {
  position: relative;
  flex: 1 1 200px;
  min-width: 0;
  min-height: 78px;
  transition: flex-basis .35s var(--tcp-ease);
}
.tcp-cta-contact-item:hover,
.tcp-cta-contact-item:focus-within {
  flex-basis: var(--tcp-cval-w, 200px);
  z-index: 5;
}
.tcp-cta-contact-link {
  position: absolute;
  inset: 0;
  display: block;
  padding: 26px 28px;
  border-left: 1px solid rgba(244, 239, 230, 0.15);
  border-bottom: 1px solid rgba(244, 239, 230, 0.15);
  text-decoration: none;
  color: var(--tcp-cream);
  transition: background .3s var(--tcp-ease), padding-left .3s var(--tcp-ease);
}
.tcp-cta-contact-item:first-child .tcp-cta-contact-link { border-left: none; }
.tcp-cta-contact-item:hover .tcp-cta-contact-link,
.tcp-cta-contact-item:focus-within .tcp-cta-contact-link {
  background: rgba(168, 135, 47, 0.08);
  padding-left: 36px;
}
.tcp-cta-contact-link .tcp-clabel,
.tcp-cta-contact-link .tcp-cval {
  position: absolute;
  left: 28px;
  top: 26px;
  transition: opacity .3s var(--tcp-ease), left .3s var(--tcp-ease);
}
.tcp-cta-contact-link .tcp-clabel {
  font-size: .66rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--tcp-gold);
  opacity: 1;
}
.tcp-cta-contact-link .tcp-cval {
  font-family: var(--tcp-font-display);
  font-size: 1.1rem;
  white-space: nowrap; /* no word-wrap — the item now expands to fit
    this instead (see the flex-basis rule above) */
  opacity: 0;
  pointer-events: none;
}
.tcp-cta-contact-item:hover .tcp-clabel,
.tcp-cta-contact-item:focus-within .tcp-clabel {
  opacity: 0;
  left: 36px; /* mirrors the hover padding-left slide */
}
.tcp-cta-contact-item:hover .tcp-cval,
.tcp-cta-contact-item:focus-within .tcp-cval {
  opacity: 1;
  pointer-events: auto;
  left: 36px;
}

.tcp-section-light .tcp-cta-contact-grid { border-top-color: rgba(74, 75, 76, 0.15); }
.tcp-section-light .tcp-cta-contact-link { border-color: rgba(74, 75, 76, 0.15); color: var(--tcp-charcoal); }
.tcp-section-light .tcp-cta-contact-item:hover .tcp-cta-contact-link,
.tcp-section-light .tcp-cta-contact-item:focus-within .tcp-cta-contact-link { background: rgba(96, 24, 40, 0.05); }
.tcp-section-light .tcp-cta-contact-link .tcp-clabel { color: var(--tcp-burgundy); }
.tcp-section-light .tcp-cta-contact-link .tcp-cval { color: var(--tcp-charcoal); }

/* ===== Contact grid — copy-to-clipboard button =====
   Top-right corner of each item, per client request. Hidden until
   the item is hovered/focus-within (or the button itself gets
   keyboard focus directly via Tab). Two stacked SVGs crossfade: a
   copy icon and a checkmark, toggled by JS adding/removing
   .is-copied on a successful clipboard write — see
   front-page-interactions.js. */
.tcp-copy-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--tcp-gold);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s var(--tcp-ease), color .25s var(--tcp-ease);
}
.tcp-section-light .tcp-copy-btn { color: var(--tcp-burgundy); }
.tcp-cta-contact-item:hover .tcp-copy-btn,
.tcp-cta-contact-item:focus-within .tcp-copy-btn {
  opacity: 1;
  pointer-events: auto;
}
.tcp-copy-btn:hover { color: var(--tcp-gold); }
.tcp-section-light .tcp-copy-btn:hover { color: var(--tcp-burgundy-dark); }
.tcp-copy-icon {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 16px;
  height: 16px;
  transition: opacity .2s var(--tcp-ease), transform .2s var(--tcp-ease);
}
.tcp-copy-icon-check {
  opacity: 0;
  transform: scale(.6);
}
.tcp-copy-btn.is-copied .tcp-copy-icon:not(.tcp-copy-icon-check) {
  opacity: 0;
  transform: scale(.6);
}
.tcp-copy-btn.is-copied .tcp-copy-icon-check {
  opacity: 1;
  transform: scale(1);
}

/* v1.6.8: services-page icon-flyout copy buttons (.tcp-svc-icon-btn,
   see assets/css/services.css) reuse this same .tcp-copy-icon/
   .tcp-copy-icon-check crossfade markup and JS .is-copied toggle, just
   under their own button class (.tcp-svc-flyout-copy) instead of
   .tcp-copy-btn — that button is always visible once its flyout is
   open (no hover-gating needed, unlike the front-page item), so it
   doesn't need any of .tcp-copy-btn's own opacity/position rules,
   only this same crossfade behavior. */
.tcp-svc-flyout-copy.is-copied .tcp-copy-icon:not(.tcp-copy-icon-check) {
  opacity: 0;
  transform: scale(.6);
}
.tcp-svc-flyout-copy.is-copied .tcp-copy-icon-check {
  opacity: 1;
  transform: scale(1);
}

@media (max-width: 700px) {
  .tcp-cta-contact-grid { flex-direction: column; }
  .tcp-cta-contact-item,
  .tcp-cta-contact-item:hover,
  .tcp-cta-contact-item:focus-within {
    flex-basis: auto; /* items already go full-width in the stacked
      mobile layout, so the hover-expand sizing has nothing to do
      here and is turned off */
  }
  .tcp-cta-contact-link { border-left: none !important; }
  .tcp-copy-btn {
    /* Touchscreens have no hover state to reveal this button, so it
       stays visible by default on mobile — same accepted tradeoff
       already in place for the label/value reveal itself (a tap on
       the link still calls/emails/opens the platform immediately
       either way; this just makes the copy option reachable too). */
    opacity: 1;
    pointer-events: auto;
  }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.tcp-footer {
  background: var(--tcp-charcoal);
  color: var(--tcp-cream);
  margin-top: 0; /* was 60px — that margin sat outside this element's own
    background, exposing whatever was behind the page (Astra's white)
    as a visible gap above the footer. Same visual spacing is now in
    padding-top below instead, which stays inside the charcoal bg. */
  padding: 35px 0 40px;
  border-top: none;
}

.tcp-footer-inner {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: center;
}

.tcp-footer-tagline {
  margin: 0;
  font-size: 0.9rem;
  opacity: 0.75;
}

.tcp-footer-menu {
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tcp-footer-menu a {
  text-decoration: none;
  color: var(--tcp-cream);
  font-size: 0.9rem;
}

/* Footer icon row — phone / email / Instagram / Facebook / YouTube.
   Fixed 300x32px container, centered in the footer, icons distributed
   with equal space between them via justify-content: space-between. */
.tcp-footer-icons {
  width: 300px;
  height: 32px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.tcp-footer-icons a {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  line-height: 0;
}

.tcp-footer-icons img {
  height: 24px;
  width: auto;
  opacity: 0.5;
  transition: opacity .3s var(--tcp-ease);
}

.tcp-footer-icons a:hover img,
.tcp-footer-icons a:focus-visible img {
  opacity: 0.9;
}

.tcp-footer-copyright {
  margin: 0;
  font-size: 0.8rem;
  opacity: 0.6;
}

.tcp-footer-logo {
  height: 58px;
  width: auto;
  margin: 0 auto 8px;
  display: block;
  margin-bottom: 30px;
}

/* ==========================================================================
   About / Services / Contact page headers
   v1.2.7: these wrappers never opt into .tcp-section-light — that class
   belongs to the front-page dark/light section system — so they were
   silently inheriting the global forced-white h1/h2/h3 color above and
   rendering as invisible white-on-cream. None of these pages have a
   dark hero behind them, so their headings are charcoal, full stop.
   ========================================================================== */
.tcp-page-header h1, .tcp-page-header h2, .tcp-page-header h3,
.tcp-about-bio h1, .tcp-about-bio h2, .tcp-about-bio h3,
.tcp-about-credentials h1, .tcp-about-credentials h2, .tcp-about-credentials h3,
.tcp-about-area h1, .tcp-about-area h2, .tcp-about-area h3,
.tcp-contact-main h1, .tcp-contact-main h2, .tcp-contact-main h3,
.tcp-contact-info h1, .tcp-contact-info h2, .tcp-contact-info h3 {
  color: var(--tcp-charcoal) !important;
}

.tcp-page-header {
  padding: 160px 0 40px;
  text-align: center;
}

.tcp-page-header h1 {
  margin-bottom: 12px;
}

.tcp-page-subtitle {
  font-size: 1.1rem;
  opacity: 0.75;
  max-width: 560px;
  margin: 0 auto;
}

.tcp-about-bio {
  padding: 20px 0 60px;
}

.tcp-about-bio .tcp-container {
  max-width: 760px;
}

.tcp-about-bio h2 {
  margin-bottom: 20px;
}

.tcp-about-bio p {
  margin-bottom: 20px;
  line-height: 1.7;
}

.tcp-about-differentiator {
  font-style: italic;
  color: var(--tcp-gold);
  font-weight: 500;
  margin-top: 8px;
}

.tcp-about-credentials {
  background: #f7f5f2;
  padding: 50px 0;
}

.tcp-credentials-grid {
  display: flex;
  justify-content: space-between;
  gap: 40px;
  text-align: center;
  flex-wrap: wrap;
}

.tcp-credential {
  flex: 1;
  min-width: 200px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tcp-credential-stat {
  font-size: 2rem;
  font-weight: 700;
}

.tcp-credential-label {
  font-size: 0.95rem;
  opacity: 0.75;
}

.tcp-about-area {
  padding: 60px 0;
  text-align: center;
}

.tcp-about-area .tcp-container {
  max-width: 700px;
  margin: 0 auto;
}

.tcp-about-area h2 {
  margin-bottom: 16px;
}

.tcp-about-cta {
  text-align: center;
  padding: 0 0 70px;
}

.tcp-services-differentiator {
  font-style: italic;
  color: var(--tcp-gold);
  font-weight: 500;
  text-align: center;
  max-width: 700px;
  margin: 24px auto 0;
}

.tcp-contact-main {
  padding: 20px 0 70px;
}

.tcp-contact-info h2 {
  margin-top: 0;
  margin-bottom: 18px;
}

.tcp-contact-info h2:not(:first-child) {
  margin-top: 20px;
}

.tcp-contact-info p {
  margin-bottom: 8px;
  line-height: 1.6;
}

/* ==========================================================================
   Contact page — Phone/Email reveal buttons
   Default state shows PHONE/EMAIL (outline, burgundy). Hover/focus
   crossfades to the real tel:/mailto: value so it can be read, clicked,
   or copy-selected. The anchor's href is the real value from the start,
   so tapping on mobile (no hover state) still works immediately.
   ========================================================================== */
.tcp-contact-btns {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 320px;
  margin: 4px 0 8px;
}

.tcp-contact-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 46px;
  padding: 0 20px;
  border: 1px solid var(--tcp-burgundy);
  background: transparent;
  color: var(--tcp-burgundy);
  text-decoration: none !important;
  overflow: hidden;
  transition: border-color .3s var(--tcp-ease), background .3s var(--tcp-ease), transform .3s var(--tcp-ease);
}

.tcp-contact-btn:hover,
.tcp-contact-btn:focus-visible {
  border-color: var(--tcp-gold);
  background: var(--tcp-burgundy);
  color: #f3f3f3; /* matches the global h1/h2/h3 heading color token used
    elsewhere in this file — no separate CSS variable exists for it yet */
  transform: translateY(-2px);
}

.tcp-contact-btn .tcp-cbtn-label,
.tcp-contact-btn .tcp-cbtn-value {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  text-decoration: none !important;
  transition: opacity .3s var(--tcp-ease);
}

.tcp-contact-btn .tcp-cbtn-label {
  font-family: var(--tcp-font-primary);
  font-size: .72rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: 1;
}

.tcp-contact-btn .tcp-cbtn-value {
  font-family: var(--tcp-font-primary);
  font-size: .82rem;
  letter-spacing: normal;
  padding: 0 12px;
  opacity: 0;
  pointer-events: none;
}

.tcp-contact-btn:hover .tcp-cbtn-label,
.tcp-contact-btn:focus-visible .tcp-cbtn-label {
  opacity: 0;
  pointer-events: none;
}

.tcp-contact-btn:hover .tcp-cbtn-value,
.tcp-contact-btn:focus-visible .tcp-cbtn-value {
  opacity: 1;
  pointer-events: auto;
}

@media (max-width: 480px) {
  .tcp-contact-btn .tcp-cbtn-value {
    font-size: .74rem;
  }
}

.tcp-contact-grid {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 48px;
  align-items: start;
}

.wpforms-container .wpforms-submit {
  background: var(--tcp-burgundy) !important;
  border-color: var(--tcp-burgundy) !important;
}

.wpforms-container .wpforms-submit:hover {
  background: var(--tcp-burgundy-dark) !important;
}

.wpforms-container input:focus,
.wpforms-container textarea:focus {
  border-color: var(--tcp-gold) !important;
}

/* v1.6.7: WPForms' default message textarea rendered at ~200px tall —
   about 4 sentences before scrolling, which reads as cramped,
   especially now that the services-page "Contact us for a solution."
   CTA pre-fills a multi-paragraph message (see services-triage.js).
   min-height (not height) so it can still grow if WPForms' own rows
   setting or a future field change pushes it taller than this. Kept
   resizable — no resize:none override — so a visitor can still expand
   it further themselves. */
.wpforms-container textarea {
  min-height: 600px;
}

@media (max-width: 768px) {
  .tcp-credentials-grid {
    flex-direction: column;
    gap: 28px;
  }

  .tcp-contact-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .tcp-header,
  .tcp-header.is-solid,
  .tcp-header[data-theme="dark"],
  .tcp-header[data-theme="light"] {
    border-bottom: none !important;
  }
}

/* =====================================================================
   "Watch the Craft" video modal (v1.5.0)
   Third hero CTA — opens a modal with placeholder content until real
   footage of Aaron working is filmed (see master to-do). JS in
   assets/js/front-page-interactions.js toggles [hidden] + .is-open and
   handles focus/scroll-lock. The [hidden] override below is required —
   learned the hard way on the scaling-demo mockup that an author
   display:flex rule beats the UA [hidden]{display:none} default.
   ===================================================================== */
.tcp-craft-modal {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(23, 18, 13, 0.82);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity .35s var(--tcp-ease);
}

.tcp-craft-modal[hidden] {
  display: none;
}

.tcp-craft-modal.is-open {
  opacity: 1;
}

.tcp-craft-modal-inner {
  position: relative;
  width: min(920px, 100%);
  background: var(--tcp-charcoal);
  border: 1px solid rgba(201, 163, 59, 0.28);
  border-radius: 6px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
  transform: translateY(16px) scale(.98);
  transition: transform .4s var(--tcp-ease);
}

.tcp-craft-modal.is-open .tcp-craft-modal-inner {
  transform: translateY(0) scale(1);
}

.tcp-craft-modal-close {
  position: absolute;
  top: -44px;
  right: 0;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(244, 239, 230, 0.4);
  border-radius: 50%;
  background: transparent;
  color: var(--tcp-cream);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color .3s var(--tcp-ease), color .3s var(--tcp-ease), transform .3s var(--tcp-ease);
}

.tcp-craft-modal-close:hover,
.tcp-craft-modal-close:focus-visible {
  border-color: var(--tcp-gold);
  color: var(--tcp-gold);
  transform: rotate(90deg);
}

.tcp-craft-modal-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  text-align: center;
  overflow: hidden;
  border-radius: 6px;
}

.tcp-craft-modal-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(ellipse at 50% 40%, rgba(201, 163, 59, 0.16), transparent 60%),
    linear-gradient(160deg, rgba(74, 75, 76, 1) 0%, rgba(36, 28, 20, 1) 100%);
}

.tcp-craft-modal-placeholder {
  position: relative;
  z-index: 1;
  max-width: 480px;
}

.tcp-craft-modal-kicker {
  display: block;
  font-family: var(--tcp-font-primary);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--tcp-gold);
  margin-bottom: 14px;
}

.tcp-craft-modal-placeholder h3 {
  font-family: var(--tcp-font-display);
  font-weight: 500;
  font-size: clamp(1.3rem, 2.6vw, 1.8rem);
  color: var(--tcp-cream);
  margin: 0 0 10px;
}

.tcp-craft-modal-placeholder p {
  font-family: var(--tcp-font-primary);
  font-size: .95rem;
  line-height: 1.6;
  color: rgba(244, 239, 230, 0.72);
  margin: 0;
}

body.tcp-modal-open {
  overflow: hidden;
}

@media (max-width: 560px) {
  .tcp-craft-modal {
    padding: 16px;
  }

  .tcp-craft-modal-close {
    top: auto;
    bottom: calc(100% - 46px);
  }

  .tcp-craft-modal-frame {
    padding: 20px;
  }
}