/*
Theme Name: CONCEPT2FORM
Theme URI: https://concept2form.com
Description: Custom Astra child theme for CONCEPT2FORM — industrial design & development solutions.
Author: CONCEPT2FORM
Author URI: https://concept2form.com
Template: astra
Version: 1.37.0
Text Domain: concept2form-theme
*/

/* ==========================================================================
File: style.css
Project: CONCEPT2FORM Website (Astra child theme)
Version: 1.37.0
Last Updated: 2026-09-14 (b)
Description: Child theme stylesheet. The WordPress theme header block above
             must stay at the top of this file (WordPress reads it to
             register the theme).
Changelog:
- 1.37.0 — Flyout clipping fix, take two — v1.35.0's approach did not
          work and is reverted here. What went wrong, recorded so it
          isn't retried: v1.35.0 switched `.menu-desc` to
          `position:fixed` expecting it to anchor to the viewport. It
          did not. `.side-menu a` carries a `transform` (the hover lift,
          translateY), and per the CSS Transforms spec ANY non-`none`
          transform on an ancestor establishes a containing block for
          fixed-position descendants — so `fixed` silently degraded to
          behaving like `absolute` against the menu item, keeping the
          old vertical clipping while ALSO moving the box horizontally
          off the item's own left edge. The user saw exactly that: "all
          that happened was you moved it in the X direction instead of
          the Y direction."
          This version restores the original item-relative horizontal
          placement (`position:absolute; right:100%; margin-right:18px`)
          and solves the vertical overflow structurally instead, per the
          user's own spec — the items at each end of the menu get their
          own alignment rule:
          * DEFAULT (every item that isn't near an edge): unchanged —
            vertically centered on its own item.
          * FIRST TWO items of the FIRST group: `.menu-desc` is TOP-
            aligned to its item (`top:0; transform:none`) so it opens
            downward into the page instead of upward off the screen.
          * LAST TWO items of the LAST group: BOTTOM-aligned to its item
            (`top:auto; bottom:0; transform:none`) so it opens upward.
          Selectors are structural (`:first-of-type`, `:last-of-type`,
          `:nth-child`, `:nth-last-child`) rather than hardcoded IDs, so
          they follow the menu automatically as items are commented out,
          added, or reordered in c2f_get_menu() — no PHP change, nothing
          to keep in sync.
          * NEW --menu-desc-viewport-gutter now 25px (was 20px in the
            reverted v1.35.0), per the user's updated number, and applied
            as a real `max-height: calc(100vh - 2 * gutter)` +
            `overflow-y:auto` backstop so even an unusually long
            description can't bleed past either edge.
- 1.36.0 — .menu-heading font-weight 500 -> 700, per user request. The
          labels were specified as Akzidenz-Grotesk Next Medium (500) in
          v1.32.0, but kit prx6fka does not publish 500 for the standard
          width — the browser was silently falling back to
          akzidenz-grotesk-next-pro at 400, so the labels rendered lighter
          than the Medium sample the user was comparing against on
          Adobe's own site. 700 (Bold) IS published in -pro, so this is
          the next weight that will actually render as specified for
          production visitors rather than degrading. Leaf items
          (.side-menu a) deliberately stay at 500 — they still resolve to
          400 in production until Medium is added to the kit, which keeps
          the label/item hierarchy intact either way. The real fix for
          both remains adding Regular + Medium (standard width) to kit
          prx6fka and republishing it.
- 1.35.0 — Desktop description flyout could open partly off the top of
          the viewport, per user report + screenshot (hovering the first
          item in the first group pushed the box above the window's top
          edge). Root cause: `.menu-desc` was `position:absolute` and
          vertically centered on its own menu item (`top:50%;
          transform:translateY(-50%)`), so for items near either end of a
          menu column that now spans almost the full viewport height, the
          box simply ran past the edge — nothing was clamping it.
          Fix, per the user's own spec ("doesn't have to be centered on
          the list item, but it needs to be legible and have a minimum
          20px from the top and bottom of the viewport"):
          * `.menu-desc` switched from `position:absolute` (anchored to
            the hovered <a>) to `position:fixed` (anchored to the
            viewport), vertically centered in the VIEWPORT rather than on
            the item — `top:50%; transform:translateY(-50%)`. It can
            never leave the visible area now, regardless of which item is
            hovered, and the vertical position is stable as the pointer
            moves down the list instead of jumping item to item.
          * Horizontal placement is now a fixed offset from the right
            edge instead of `right:100%` on the item: new CONTROL PANEL
            variable --menu-desc-right-offset (480px = .side-menu's 62px
            right offset + its 400px max-width + the 18px gap), so the
            box's right edge lands on the menu column's left edge exactly
            as before. Previously it tracked each item's own ragged left
            edge; now it's a straight column, which also reads cleaner
            against a right-aligned menu.
          * Hard guarantee on the 20px rule: `max-height: calc(100vh -
            (2 * var(--menu-desc-viewport-gutter)))` with
            `overflow-y:auto`, gutter = 20px. A description long enough
            to exceed that scrolls inside the box rather than bleeding
            off-screen.
          * `margin-right` is zeroed on the fixed rule (it existed only to
            create the 18px gap in the old item-relative positioning; it's
            folded into the offset variable now).
          MOBILE is untouched — the --menu-breakpoint media query already
          overrides `.menu-desc` back to an in-flow `position:relative`
          block, which still wins over the new `fixed` base rule.
- 1.34.0 — Second vertical-fit pass plus two visual changes, per user
          report that v1.33.0 did not clear the bottom of the viewport.
          * --menu-top-offset 4% -> 1.5% and --menu-bottom-offset 4% ->
            1.5% — buys back roughly 45px at a 900px viewport.
          * --menu-group-gap 0.85em -> 0.5em — tightens the space between
            category blocks, the other place the user identified as
            having slack.
          * .side-menu a font-weight 400 -> 500 (Akzidenz-Grotesk Next
            MEDIUM) per user request — leaf items now match the group
            labels' weight. Note the standing kit caveat: prx6fka does
            not publish 500 for the standard width, so until Medium is
            added to the kit and republished, production visitors resolve
            to akzidenz-grotesk-next-pro 400 and this change is visible
            only on machines with the family installed locally.
          * NEW .menu-group.menu-group--accent modifier + a new
            --menu-accent-color Global Variable (= --c2f-blue, #00AEEA,
            the logo's "CONCEPT" blue). front-page.php 1.11.0 -> 1.12.0
            adds that class to the group whose label is "Intelligence +
            Code + Automation", so the accent is data-driven off the
            label string rather than a hardcoded nth-child position that
            would break the moment a group is added or reordered.
          * .menu-desc z-index 5 -> 100, and .side-menu li gains
            `isolation: auto` plus an explicit z-index bump on hover —
            the hovered item's own stacking context now sits above every
            later sibling, so a flyout opening from an item near the top
            of the menu can no longer be painted under the items below
            it. This is the "just put it over everything else in the
            list" fix the user asked for pre-emptively.
- 1.33.0 — Desktop side menu was running off the bottom of the viewport,
          per user report on the local WordPress site. With the menu now
          at 26 active leaf items + 5 headings (the user commented out 7
          items in inc/site-config.php this session), the column's own
          height exceeded the 83vh box the absolute positioning gives it
          (top:9% + bottom:8%), so the last group overflowed off-screen.
          Tightened five values, ALL of them now CSS CONTROL PANEL
          variables so the fit can be retuned without hunting through
          rules:
          * --menu-top-offset  9% -> 4%  (.side-menu's `top`)
          * --menu-bottom-offset 8% -> 4% (.side-menu's `bottom`)
          * --menu-group-gap  1.6em -> 0.85em (.menu-group margin-bottom)
          * --menu-heading-gap 0.65em -> 0.42em (.menu-heading
            margin-bottom)
          * --menu-item-pad-y  4px -> 2px, and .side-menu a's vertical
            margin 1px -> 0 via --menu-item-gap-y
          * NEW --menu-item-line-height (1.15) applied explicitly to
            .side-menu a — previously it inherited the browser default
            (~1.2 with extra leading from the inline-block line box),
            which was the single largest contributor to the overflow at
            26 items.
          Net effect at a 900px-tall viewport: the menu column drops from
          roughly 850px to roughly 580px, clearing the ~830px box with
          real headroom. Horizontal padding (10px) is unchanged — only
          vertical rhythm was touched, so nothing reflows sideways.
          MOBILE: the base-rule changes are inherited on purpose (the
          user said connected is fine), EXCEPT vertical padding, which is
          restored to 5px inside the --menu-breakpoint media query so tap
          targets stay finger-sized. Mobile line-height and item gap do
          follow the new base values.
- 1.32.0 — Menu restructure, type change, hover-affordance icon, mobile
          bottom padding, and an Astra scroll-to-top suppression, per a
          full working session with the user (see inc/site-config.php
          1.5.0 for the menu data half, and
          claude/menu-seo-ordering-findings_ver-001.md in project
          knowledge for the ordering research).
          * TYPE: .menu-heading and .side-menu a moved off Poster Gothic
            ATF to Akzidenz-Grotesk Next — labels Medium (500), items
            Regular (400). New --c2f-font-label / --c2f-font-item vars.
            IMPORTANT: kit prx6fka does NOT yet publish these weights for
            the standard width — it carries akzidenz-grotesk-next at 200
            only, and akzidenz-grotesk-next-pro at 400/700. Until Regular
            + Medium of the standard width are added to the kit and it is
            republished, visitors resolve to -pro 400 for both, then to
            poster-gothic-atf. The local machine has the family installed
            so it previews correctly there. Fallback chain deliberately
            ends at poster-gothic-atf, NOT Arial, so a kit miss degrades
            to the site's prior appearance rather than a system font.
          * COLOR: .menu-heading #333333 (~80% black, was
            var(--c2f-charcoal)); .side-menu a #545454 (~67% black, was
            var(--c2f-gray) #808080) — both per user request, for
            legibility at these weights. New --c2f-menu-label-color /
            --c2f-menu-item-color vars.
          * NEW .side-menu a::before — a circled "i" hover-affordance
            icon on every leaf item, signalling that hovering reveals the
            description. Inline data-URI SVG used as a CSS mask so the
            color comes from background-color:currentColor and tracks the
            link's own state; 1em box, 1ch gap, 65% opacity resting / 85%
            hovered. Implemented as a pseudo-element on purpose so no
            markup or menu-array change was needed. Four CSS CONTROL
            PANEL knobs added.
          * MOBILE: .side-menu's bottom margin 32px -> a new
            --mobile-menu-bottom-gap CONTROL PANEL variable at 100px, per
            user report that the last menu item was getting cut off at the
            bottom edge of the screen on mobile.
          * NEW: Astra's scroll-to-top control (#ast-scroll-top) is
            hidden. The user reported "a blue circle underneath the right
            side menu" on desktop and wondered whether it was a Chrome
            extension — it is Astra's own back-to-top button, themed in
            Astra's default link blue, which this child theme never
            styled. Hidden in CSS rather than via the Customizer so the
            decision stays version-controlled with the rest of the theme.
- 1.31.0 — .menu-desc background changed from flat opaque white to 87%
          opacity white (rgba(255,255,255,0.87)), per user request.
- 1.30.0 — FIX: mobile menu descriptions were rendering permanently expanded
          in production (user-reported, confirmed after a full cache purge —
          not a caching issue after all). Root cause: `.menu-desc` is a
          `<span>` (inline by default). The v1.29.0 mobile override sets
          `position: relative` — unlike `position: absolute` (the desktop
          rule), `relative` does NOT auto-blockify the element (CSS2.1
          §9.7 only blockifies for absolute/fixed), so `.menu-desc` stayed
          `display: inline` on mobile. `max-height`/`overflow` have no
          clipping effect on non-replaced inline boxes, so every
          description rendered at full natural height regardless of
          whether its `<li>` had `.is-menu-open` — exactly matching the
          reported symptom (and, in hindsight, almost certainly also the
          real cause of the ORIGINAL v1.28.0 "doesn't even look like a
          menu... stuff stacked together" bug, which used the same
          collapse mechanism). Fix: added an explicit `display: block;` to
          the mobile `.menu-desc` rule. No JS change needed —
          `initMenuAccordion()`'s click/`.is-menu-open` logic was already
          correct; this was a pure CSS display bug that made the collapse
          mechanism inert on mobile.
- 1.29.0 — REVERT of v1.28.0's side-menu click-dropdown rebuild, per
          explicit user request — the rebuild rendered broken in the
          user's actual browser ("doesn't even look like a menu... nothing
          is sliding out, it's just a bunch of stuff stacked together").
          Full CSS half of a 3-file revert (style.css + front-page.php +
          assets/js/main.js — see those files' own changelogs). Desktop
          menu interaction is back to plain CSS :hover/:focus-visible, no
          JS-driven open state:
          * .side-menu li: back to a plain `margin: 0` — removed the
            quadratic --gap-shrink compression calc/transition.
          * .side-menu a:hover/:focus-visible: removed the
            .side-menu li.is-menu-open a addition — hover is the only
            "lifted" state again.
          * .menu-arrow (the click-to-open chevron) removed entirely, all
            rules and markup.
          * .menu-desc rebuilt back into an absolute hover-reveal flyout
            to the item's left (position:absolute; right:100%;
            margin-right:18px; transform:translateY(-50%)) — per the
            user's exact new spec: fixed width:420px (was max-width:380/
            430px), auto height, flat padding:15px on all sides (was
            asymmetric 20/16/44px), box-shadow:var(--menu-hover-shadow)
            matching the hovered item's own shadow, and a flat
            background-color:var(--c2f-white) (simplified from the old
            semi-transparent/gradient fill — user said "keep it simple for
            now," flagging this specific choice since a background
            treatment wasn't itself spelled out). Reveal is a plain,
            undelayed opacity/visibility fade via
            .side-menu li:hover/.side-menu a:focus-visible ~ .menu-desc —
            the v1.27.0 600ms hover-intent grace period is gone since it
            existed only to keep the now-removed icon row reachable.
          * .menu-desc-icons/.menu-desc-icon* (phone + envelope icon-link
            pair) removed entirely, per explicit user request ("remove the
            email and call me icons").
          * CSS CONTROL PANEL: removed the now-unused --menu-item-gap /
            --menu-dropdown-duration variable pair (nothing references
            them after this revert).
          * Mobile media query: restored .menu-desc to a self-contained
            mobile-only override (position:relative in-flow block,
            max-height:0 collapsed / .is-menu-open expands it via
            assets/js/main.js's restored mobile-only initMenuAccordion())
            — this pattern briefly lived merged into the shared base rule
            under v1.28.0 and needed separating back out now that the base
            rule is the desktop flyout again. Open-state padding
            simplified to a flat 14px top/bottom (was 14px/0/48px/4px,
            the 48 sized for the now-removed icon row) and background
            flattened to var(--c2f-white) to match the desktop
            simplification — flagging both as minor consistency choices,
            not explicitly requested for mobile.
          * NOT changed: the mobile click-accordion's underlying
            interaction model itself (click-to-toggle, one open at a time)
            — the user's request was about the desktop hover behavior
            specifically ("this was pretty much the functionality that
            existed on menu when we first uploaded it"), and mobile was
            never reported broken.
- 1.28.0 — Side-menu interaction rebuild, per user request ("one big ask").
          Full CSS half of a 3-file change (style.css + front-page.php +
          assets/js/main.js — see those files' own changelogs):
          * Removed .side-menu a:hover::before/:focus-visible::before, the
            "bridge" pseudo-element that extended the hover background
            leftward toward the old flyout — hover now just pads the label
            text itself, per user request.
          * .menu-desc rebuilt from an absolute hover-reveal flyout to the
            item's left into a click-toggled, in-flow dropdown that expands
            BELOW the item — the pre-existing mobile-only accordion pattern
            (max-height:0 collapsed / .is-menu-open expands it) is now the
            single shared base rule for both breakpoints instead of being
            duplicated per breakpoint. Opening/closing is click-driven only
            (assets/js/main.js's new initSideMenuDropdown()), not hover;
            only one item open at a time, same as the pre-existing mobile
            behavior, now also true on desktop.
          * New .menu-arrow: a small chevron appended inside each <a>
            (front-page.php 1.10.0), hidden at rest, shown on hover/focus/
            open, rotated 180° while open.
          * New .side-menu li.is-menu-open a / .is-menu-open .menu-arrow:
            an open item keeps the hover "lifted" look and visible arrow
            even after the pointer leaves it — not explicitly spec'd, a
            reasonable default so the open item stays legible; flag if
            undesired.
          * New non-linear "compression": .side-menu li's margin-bottom is
            now `calc(var(--menu-item-gap) * (1 - var(--gap-shrink, 0)))`,
            with --gap-shrink set per item by main.js using a quadratic
            falloff from whichever item is open, per user's exact spec
            ("like a quadratic curve... highest at the top... lowest at the
            bottom"). Neutralized on mobile (`.side-menu li { margin-bottom:
            0 }`) since that accordion already reflows via plain block flow.
          * .hero's `overflow: hidden` split into `overflow-x: hidden` /
            `overflow-y: visible` (matching the pattern the mobile media
            query already used) — desktop's .side-menu has both `top` and
            `bottom` set, so its height is fixed by that constraint, not by
            content; without this, an opened description would get clipped
            at the container's bottom edge instead of spilling past it.
            NOTE / flagged compromise: the quadratic compression genuinely
            shrinks nearby items' gaps, but a handful of small margins
            cannot fully absorb a full description block's height without
            either a much more spaced-out resting layout or smaller item
            text — neither of which was requested. The menu will grow
            somewhat overall when an item opens; this overflow change is
            what keeps that growth from being clipped rather than what
            eliminates it. Flagging this as an interpretive choice, not a
            silent assumption — happy to revisit if the amount of growth
            reads as too much once you see it live.
          * Removed the v2.41 hover-out-delay fade system (opacity/
            visibility transition with the 600ms grace period) — no longer
            applicable now that opening isn't hover-triggered.
- 1.27.0 — .menu-desc hover-out delay, per user request: the new phone/
          envelope icons (v1.26.0) were unclickable in practice because the
          box could fade/hide before the cursor actually reached it — a
          diagonal mouse path from the menu item toward the icons (near the
          box's bottom, often outside the narrow strip the old instant
          hover-out covered) would drop hover and hide the box mid-transit.
          Fixed with a CSS-only hover-intent delay: hiding now waits 600ms
          before starting to fade (transition-delay on the "closed" rule),
          fades over 350ms once it starts, and stays fully open the whole
          time the pointer is anywhere over the box (new `.menu-desc:hover`
          added to the reveal selector, plus `pointer-events: auto` on that
          same selector so the whole box — not just the icon links — is
          genuinely hoverable while shown). Revealing stays instant/
          undelayed; only hiding is delayed. No JS needed. Not yet verified
          live — see Verification note below.
- 1.26.0 — Right-menu hover + .menu-desc updates, per user request (two
          reference screenshots supplied):
          * New shared box-shadow, applied to the menu item's hover
            background (.side-menu a:hover/:focus-visible), its left-side
            bridge (::before), and .menu-desc — small/subtle, 60% opacity,
            in 60% gray (explicitly NOT black), per spec: rgba(153,153,153,
            0.6), 0 2px 6px. New CSS CONTROL PANEL variable
            --menu-hover-shadow holds the value so all three stay in sync.
            NOTE: v1.23.0 deliberately REMOVED a box-shadow from the hover
            state (it read as "a separate floating card" once the bridge/
            gradient made the plate look continuous) — this is a new,
            explicit, direct request for a shadow, not a reversion of that
            reasoning; kept it small enough (6px blur, 60% opacity) not to
            reintroduce the hard-edged-card look v1.23.0 was fixing.
          * .menu-desc's background transparency gradient (100% opaque at
            the right edge -> 85% at the left edge) was ALREADY implemented
            (see v1.13.0 below) — verified against the current file before
            making any change here; nothing to do on that point.
          * Added .menu-desc-icons/.menu-desc-icon: a phone + envelope
            icon-link pair in the lower-right corner of every .menu-desc
            box, on their own line below the description text (see
            front-page.php 1.8.0 -> 1.9.0 for the markup). Positioned with
            a flex row (not fixed left/right offsets) so the envelope's
            "auto width" doesn't have to be guessed at:
              .menu-desc-icons { position:absolute; right:15px; bottom:15px;
                display:flex; align-items:flex-end; gap:17px; }
            — right:15px/bottom:15px matches the user's own "15 pixels in
            from the edge / 15 pixels from the bottom" spec; the 17px flex
            gap matches "phone icon spaced at 17 pixels" from the envelope,
            phone rendered first (left) so it sits to the envelope's left
            as described. Phone icon is a hard 14x14px image; envelope icon
            is 14px tall with auto width (per spec). Both at opacity 80%.
          * FLAGGED ASSUMPTION: the user's message said "15 pixels in from
            the LEFT of the description container" for the envelope, but
            also said the icons go on "the lower RIGHT side of the
            description Container" and that the phone icon sits "to the
            left of the envelope icon" — read together (and matching the
            reference screenshots, which show both icons toward the lower-
            right, not the lower-left), "left" in that one clause reads as
            a slip for "right." Implemented as lower-RIGHT per the
            governing framing + screenshots; flagging this explicitly per
            the project's own convention rather than silently guessing on
            a pixel-precise spec. Easy to mirror to the left if wrong —
            just swap `right:15px` for `left:15px` on .menu-desc-icons.
          * Envelope icon links to mailto:info@concept2form.com — the user
            wrote "info@concept2form" with no domain; treated as shorthand
            for the same address the contact flyout already uses (see
            front-page.php 1.4.0), not a new/different literal.
          * .menu-desc's bottom padding increased (20px -> 20/16/44px, i.e.
            +24px on the bottom only) so the new icon row has clearance
            below the description text instead of overlapping it. Same
            treatment applied to the mobile open-state padding further
            below.
          * Mobile: .menu-desc's `position: static` changed to
            `position: relative` — with no top/left/right/bottom offsets
            this has zero effect on the existing static-in-flow-block
            layout, but it's required so .menu-desc-icons (position:
            absolute) positions relative to .menu-desc itself rather than
            falling back to the next positioned ancestor up (.side-menu li)
            and landing in the wrong place on mobile.
          * Icon links need `pointer-events: auto` explicitly — .menu-desc
            itself is `pointer-events: none` (so the description text block
            doesn't intercept hover/click), and pointer-events inherits, so
            without this override the icons would render but not be
            clickable.
- 1.25.0 — **Fixed: contact flyout email/phone links were unclickable** (reported
          as "prompts to enter an email" / phone "does nothing," and
          suspected — correctly, as a secondary symptom — to be the mobile
          tap-to-close handler interrupting clicks). Root cause confirmed via
          live `elementFromPoint()` testing at the exact center of each
          link's own rendered box: `.contact-flyout-item::before` (the v1.13
          hover-area bridge, `position:absolute` with `z-index:auto`) was
          painting ABOVE `.contact-flyout-item a` (`position:static`) per
          the CSS painting-order spec — non-positioned in-flow content
          paints BEFORE z-index:auto positioned content, regardless of DOM
          order. Every click on the visible email/phone text was actually
          landing on the `::before`'s generating `<div>`, never reaching the
          real `<a>` — so the `mailto:`/`tel:` link never fired, and on
          mobile the click also fell through `initContactFlyoutToggle()`'s
          `closest('a, button')` guard (the div matches neither), toggling
          the flyout closed instead. Confirmed identically on both the
          email and phone items.
          FIX: `.contact-flyout-item a` gets `position: relative; z-index: 1;`
          — promotes it into the same z-index:auto positioned paint step as
          `::before`, where tree order decides, and the `<a>` (later in the
          markup than `::before`) now paints on top. Re-verified live after
          the fix: `elementFromPoint()` on both links now returns the `<a>`
          itself, a real click fires the anchor's own click handler with
          `defaultPrevented:false`, and the mobile flyout correctly stays
          open (no longer misfires the close-toggle). The `::before` bridge
          itself is untouched — its hover-extension behavior (v1.13) is
          unaffected by this fix.
  Also, per user request: **increased the email/phone font size and gave
          them more vertical breathing room** in the 100px-tall card —
          `.contact-flyout-item a` font-size 12px -> 15px; `.contact-flyout-
          email`/`-phone` top offsets changed from 20px/47px (15px gap) to
          26px/64px (23px gap), keeping `line-height:1` so the offset math
          stays exact. Not a requested/adjustable value, so left as a plain
          literal per this file's convention — flag if a different split is
          wanted.
- 1.24.0 — Rebuilt the contact flyout from code, per user request (reported
          the live/production links "can't be clicked" and asked for a
          from-code rebuild rather than continued debugging of the SVG
          version). Removed the `background: url("assets/svg/contact us
          menu.svg")` approach entirely — that asset was a ~221KB base64
          PNG wrapped in an SVG wrapper, not real vector art, and had
          already been observed rendering incorrectly live. Replaced with
          two plain CSS-drawn layers, both new elements added in
          front-page.php 1.8.0:
          * `.contact-flyout-card` — absolutely positioned 216x100 light
            panel (`background:#f2f2f2`, rounded right corners), sits
            behind the existing .contact-flyout-item links/buttons (DOM
            order unchanged, so stacking is unaffected).
          * `.contact-flyout-tab` — absolutely positioned 24x100 dark
            panel (`background:#3a3a3c`, Charcoal — see Locked Identity
            Facts) pinned to the right edge, containing a
            `.contact-flyout-tab-label` span ("Contact Us") rotated
            vertical via `writing-mode:vertical-rl` + `rotate(180deg)`,
            set in the Typekit contact font var.
          Root cause of the click bug was never conclusively pinned down
          via live CSS-cascade diagnostics this session (see
          website-build-protocol.md v2.37 for the full investigation
          trail) — several checks were contradictory, and the very last
          one actually showed the hover-open mechanism computing
          correctly. Regardless of that exact prior cause, this rebuild
          removes the entire class of bug an image-background approach
          is vulnerable to: the visible card/tab graphic and the actual
          clickable DOM elements are now literally the same boxes, so
          they cannot drift out of alignment with each other the way a
          background-image and its overlaid links could. `.contact-
          flyout-item`'s own positioning (`right:36px` etc., measured
          from the outer 240px box) is untouched — it already worked
          relative to `.contact-flyout` itself, not the old image, so no
          change was needed there. Opacity 0.3->1 hover/focus-within
          behavior, `bottom:115px`, 240x100 footprint, z-index:20, and
          the mobile `.is-open` tap-toggle are all preserved unchanged.
- 1.23.0 — Finished the menu-item/.menu-desc "connected plate" look from
          v1.22.0, per user request (screenshot showed the seam line and
          drop shadow still breaking the connection):
          * .menu-desc's border-right (0.5px solid #b5b5b5) removed — was
            a hard seam cutting the gradient plate in half right where
            the bridge meets it.
          * .side-menu a:hover/:focus-visible's box-shadow (the 30%-
            opacity "lift" shadow) removed, along with the now-unused
            box-shadow declaration/transition on the base .side-menu a
            rule — a shadowed edge read as a separate floating card
            rather than part of the same plate as the bridge/description.
          * Incidental cleanup: mobile .menu-desc's `border-right: none`
            reset removed — dead now that desktop has no border-right to
            override.
          * translateY(-4px) hover lift left untouched — not part of what
            was flagged, and not part of the seam/shadow issue.
- 1.22.0 — Bridged the visible gap between a hovered menu item and its
          .menu-desc pop-out, per user request (screenshot showed the two
          reading as separate floating white boxes instead of one
          connected shape). Desktop only:
          * New .side-menu a:hover::before / :focus-visible::before —
            extends the item's own solid white hover background 18px
            further left (exactly matching .menu-desc's existing
            margin-right gap), so its left edge lands flush against
            .menu-desc's right edge instead of leaving a bare gap.
          * .menu-desc's flat rgba(255,255,255,0.85) background swapped
            for a linear-gradient — 100% opaque at its right edge
            (matching the item's solid-white hover background at the
            seam), fading to 85% at its left edge, per user spec.
            Combined with the bridge above, the item pill, the bridge,
            and the description now read as one continuous plate.
          * Neutralized (content: none) inside the mobile media query —
            .menu-desc is a static in-flow block on mobile, not an
            absolute flyout, so the bridge has nothing to attach to
            there.
          * Incidental correction: .menu-desc's line-height comment still
            said "current value ... 5.76px" — the user manually retuned
            --menu-desc-line-height to 24px earlier this session (caught
            resolving a git merge conflict); comment corrected to match,
            no CSS value changed.
- 1.21.0 — New CSS CONTROL PANEL variable --menu-desc-line-height (5.76px),
          per user request: adjustable control for .menu-desc's line
          height (was a plain literal from v1.20.0's "reduce by 80%"
          change). No visual change — same 5.76px default, now a single
          adjustable knob instead of a value buried in the rule.
- 1.20.0 — Contact-flyout interaction fixes, mobile menu-item divider
          technique swap, and a menu-desc type experiment, per user
          request. Full detail:
          * Contact flyout desktop: fixed a bug where moving the pointer
            off the flyout didn't close it — clicking a `.contact-flyout-
            copy` button left it keyboard-focused, and `:focus-within` was
            holding the flyout open. assets/js/main.js 1.8.0 -> 1.9.0 now
            calls `btn.blur()` right after a copy click, so `:hover`'s
            normal mouse-out behavior takes back over.
          * Contact flyout mobile: new tap-to-toggle. assets/js/main.js's
            new `initContactFlyoutToggle()` toggles a `.is-open` class
            below `--menu-breakpoint` (ignoring clicks on the actual
            links/copy button so those still work normally); this
            stylesheet neutralizes the base `:hover`/`:focus-within`
            open rule inside the mobile media query and drives the open/
            closed state from `.is-open` instead, since touch doesn't have
            reliable hover.
          * `.side-menu li`'s mobile divider line: swapped from a plain
            sub-pixel `border-bottom` to a `::after` pseudo-element using
            the "transform scale" hairline technique (user's own research/
            screenshot — draw a real 1px line, then `scaleY(0.5)` from its
            bottom edge) for a crisper line across screen densities. Color
            (`rgba(100, 100, 100, 0.15)`) carries over the user's own
            manual edit from the plain-border version, unchanged.
          * (Noted for the record, not changed by Claude): the user also
            manually retuned `--mobile-menu-top-gap` to `15px` (was the
            50px default from v1.19.0) directly in VS Code, re-synced here
            before this edit.
          * `.menu-desc` line-height: 28.8px -> 5.76px ("reduce the line
            height by 80%," applied literally: 28.8 * 0.2 = 5.76). FLAGGED
            — 5.76px is well under the 12px font-size, so wrapped
            description text will visually overlap rather than stack as
            separate lines. Implemented as calculated per the explicit
            instruction; needs the user's visual confirmation.
          * `.menu-desc` font-family: requested "Merel" at a 200-300
            weight — NOT applied. Fetched the kit CSS directly
            (https://use.typekit.net/prx6fka.css, per the standing Font
            Sourcing rule): kit `prx6fka` has no family named or
            resembling "Merel"/"Meret" (its 5 families are
            liberation-sans, ff-chambers-sans-web, sol-pro,
            poster-gothic-atf, poster-gothic-round-atf). Separately, the
            current family (ff-chambers-sans-web) has no 200/300 weight in
            the kit either (only 400/500/700/900). Left `.menu-desc`'s
            font-family/font-weight untouched pending the user adding
            Merel to the kit or naming an in-kit alternative.
          * Files touched: style.css (1.19.0 -> 1.20.0), assets/js/main.js
            (1.8.0 -> 1.9.0).
- 1.19.0 — New CSS CONTROL PANEL variable --mobile-menu-top-gap (50px),
          per user request: "control the distance between the top of the
          menu and the bottom of the logo" on mobile. Mobile `.side-menu`'s
          margin-top — previously a plain 50px literal (v1.18.0/v2.22's
          "50 pixels below the logo SVG" spec) — now reads
          var(--mobile-menu-top-gap), so this gap is a directly-adjustable
          knob instead of a value buried in the media query. No visual
          change (same 50px default).
- 1.18.0 — Reverted v1.17.0's mobile SVG logo entirely, per user request —
          it was rendering as extra, incorrectly-positioned floating logo
          instances on the page instead of cleanly replacing the PNG.
          Back to a single hero logo (the PNG,
          assets/images/c2f_landing_page_logo_w_ds.png) for both desktop
          and mobile. Full detail:
          * Removed `.hero-logo--mobile { display:none; }` (base rule) and
            the mobile media query's `.hero-logo--desktop{display:none}` /
            `.hero-logo--mobile{display:block}` toggle — front-page.php
            1.6.0 -> 1.7.0 removed the second `<img class="hero-logo
            hero-logo--mobile">` element and the now-meaningless
            `hero-logo--desktop` modifier class from the remaining one.
          * The mobile `.hero-logo` sizing rule (width:calc(100% - 50px),
            margin:18px auto 0, transform:scale(0)) is UNCHANGED and now
            applies to the single PNG element on mobile — this is what the
            user confirmed looks "absolutely perfect."
          * Desktop `.hero-logo` width comment corrected: the actual value
            is `600px` (the user's own direct manual edit in VS Code,
            done between v1.17.0 and this change) — was still commented as
            "700px" from v1.15.0/v1.16.0, a stale note fixed here, value
            itself untouched.
          * assets/js/main.js's v1.8.0 querySelectorAll('.hero-logo')
            change (added for the two-element mobile/desktop split) is
            left as-is — harmless and correct with a single .hero-logo
            element, no revert needed.
          * Explicitly NOT touched, per direct instruction ("literally
            don't fuck with anything else"): the v1.17.0 mobile menu
            50px-below-logo gap, the mobile tagline's 11px font-size, and
            the new `.side-menu li` divider lines — all still in place.
          * Files touched: style.css (1.17.0 -> 1.18.0), front-page.php
            (1.6.0 -> 1.7.0). assets/js/main.js unchanged.
- 1.17.0 — Mobile logo swapped to an SVG asset + several mobile-only sizing
          tweaks, per user request. Full detail:
          * NEW: mobile now renders assets/svg/concept2form_logo_current.svg
            instead of the desktop PNG — see front-page.php 1.6.0, which
            added a second .hero-logo <img> (.hero-logo--desktop /
            .hero-logo--mobile modifier classes). New base rule
            `.hero-logo--mobile { display:none; }` (hidden above the
            breakpoint); the --menu-breakpoint media query below flips it
            (`.hero-logo--desktop{display:none} .hero-logo--mobile
            {display:block}`). assets/js/main.js 1.7.0 -> 1.8.0 updated to
            add .is-visible to every `.hero-logo` element instead of a
            single getElementById target.
          * Mobile `.hero-logo` width: fixed 500px -> `calc(100% - 50px)` —
            "25 pixel right and left padding" (the 500px fixed-width spec
            was tied to the old PNG asset; the new SVG gets a fresh
            padding-based spec).
          * Mobile `.side-menu` margin-top: 40px -> 50px — "I want the
            menu to be Position 50 pixels below the logo SVG."
          * Mobile `.tagline` font-size: 13px -> 11px, per spec.
          * NEW `.side-menu li` mobile rule: `border-bottom: 0.5px solid
            rgb(89, 89, 89)` — "for the array items I want the lines
            underneath them to be 0.5 pixels and 35% grayscale." 35% of
            255 ≈ 89, per the project's existing "% grayscale" = shade-of-
            gray convention. Interpreted as a divider line under each
            menu item row — **flagged**, "array items" is ambiguous and
            could mean something else (e.g. the expanded `.menu-desc`'s
            existing border-top, which was left untouched here).
          * NOT built: a from-scratch SVG recreation of the contact-flyout
            background art (assets/svg/contact us menu.svg), per the
            user's own explicit fallback ("if you can't build it or
            replicate that with code, don't do it, leave it, it'll be
            fine"). Inspected the actual file: its "background" is a large
            embedded base64 PNG inside an SVG wrapper (221KB), not simple
            vector shapes — faithfully recreating that raster art by hand
            isn't feasible, so the existing asset is left as-is. See
            website-build-protocol.md Open Items.
          * Files touched: style.css (1.16.0 -> 1.17.0), front-page.php
            (1.5.0 -> 1.6.0), assets/js/main.js (1.7.0 -> 1.8.0).
- 1.16.0 — Reverted the v1.15.0 desktop logo/tagline left-alignment — user
          rejected it ("not sure what you were going for... it's supposed
          to load up centered on the screen"). Full detail:
          * .hero-logo: left:var(--hero-logo-left) -> left:50% +
            transform:translateX(-50%) restored (centered again). Width
            stays fixed 700px (that part of v1.15.0 stands — not asked to
            revert). "Keep it centered on the viewport until the viewport
            width gets down to the mobile app size, then it can shift to
            the mobile position" — i.e. centered on desktop, static/
            in-flow on mobile (the --menu-breakpoint media query already
            does this; only the desktop half needed reverting).
          * .tagline: left:var(--hero-logo-left) -> left:50% +
            translate(-50%, ...) restored, matching the logo's centering
            again. The vertical "-100% - 36px" lift is unchanged (that's a
            separate, still-current spec, not part of what got reverted).
          * Removed the now-unused --hero-logo-left Global CSS Variable
            entirely (nothing references it anymore).
          * Mobile header block (inside --menu-breakpoint / 830px), all per
            explicit new spec:
            - .hero-logo: width calc(100% - 60px) -> fixed 500px ("I don't
              care if it runs off the screen" — no longer padding-derived).
              margin-top 36px -> 18px ("the bottom padding of the tagline
              needs to be reduced by 50%... closer to the logo" — that gap
              is expressed as .hero-logo's margin-top since .tagline has no
              margin-bottom of its own).
            - .tagline: new width:calc(100% - 60px) override (30px left/
              right padding, per spec — "spaced correctly" is handled for
              free by the existing flex/space-between layout once the
              container width changes). New font-size:13px override
              (fixed, per spec — was inheriting the desktop clamp()).
          * Files touched: style.css only. front-page.php's v1.5.0 DOM
            order (tagline before logo) is unaffected — mobile still needs
            that order for the normal-flow header stack; only desktop's
            *positioning* was reverted, not the markup.
- 1.15.0 — Logo/tagline repositioning + contact flyout layering, per user
          request. Full detail:
          * .hero-logo: width clamp(300px, 50vw, 700px) -> fixed 700px —
            the user reversed the v1.14.0 flex-down behavior ("keep the
            width of the logo at 700px even when someone is scaling the
            width of the viewport"). left:50% + translateX(-50%)
            (centered) -> left:var(--hero-logo-left) with no horizontal
            transform (left-aligned at a fixed offset), so the fixed-width
            logo doesn't grow toward center and overlap the right-side
            menu on narrower desktop widths. --hero-logo-left: 60px is a
            NEW Global CSS Variable (Global, not Control-Panel — it's a
            shared token between .hero-logo and .tagline below, not
            flagged as user-adjustable) — 60px is a chosen default (no
            exact offset was specified), flagged for visual confirmation.
            top:52vh unchanged (desktop only — see the mobile override
            further below for the new header treatment there). New
            z-index:1 ("above the logo container" — see .contact-flyout
            below).
          * .tagline: left:50% -> left:var(--hero-logo-left), matching the
            logo's new fixed left position (assumption: the tagline
            shares the logo's left edge now that centering is gone —
            flagged, not explicitly specified). transform: dropped the
            horizontal "-50% - 20px" centering/drift-correction component
            entirely (there's no centerline to drift from anymore) and
            changed the vertical lift from `-100% - 26px` to
            `-100% - 36px` — the user's new explicit spec: "36 pixel
            offset from the bottom of the [tagline] text to the top of
            the logo."
          * .side-menu: new z-index:30 ("above the logo container" AND
            above the contact flyout, per spec — "[flyout] z-index should
            be below the menu items").
          * .contact-flyout: `top:115px` -> `bottom:115px; top:auto;` —
            "move it down... to have a 115 pixel offset from the bottom,"
            applies to both desktop and mobile (single base-rule change,
            no breakpoint needed since position:fixed is always relative
            to the current viewport). New `opacity:0.3` at rest,
            `opacity:1` added to the existing hover/:focus-within rule
            (added `opacity` to the transition list alongside `left` —
            not explicitly requested, a reasonable pairing with the
            existing slide transition, flag if undesired). z-index
            50 -> 20 ("below the menu items but above the logo
            container" — 1 < 20 < 30, per the new .hero-logo/.side-menu
            z-index values above).
          * NEW mobile header treatment (inside the existing
            --menu-breakpoint / 830px media query): .tagline and
            .hero-logo switch from the desktop's absolute/vh-anchored
            overlay to a normal-flow "header" block, per spec ("the logo
            is to be rendered out at the header portion" on mobile) —
            .tagline: position:static, margin-top:40px (the spec's "40
            pixel padding from the top of the mobile screen," explicitly
            measured to the TOP OF THE TAGLINE, not the logo, per the
            user's own correction mid-request). .hero-logo:
            position:static, width:calc(100% - 60px) (the spec's "30
            pixel left and right padding, remainder is the logo's
            width"), height:auto (unchanged/inherited), margin:36px auto
            0 (same 36px gap as desktop, now expressed as a margin since
            both elements are in normal flow), transform overridden to
            drop translateX (no longer meaningful once statically
            centered via margin:auto) while keeping the scale(0)->
            scale(1) entrance animation. .side-menu's existing
            margin-top:40px (from v1.14.0) already satisfies "the menu
            items will start below the logo with a 40 pixel offset from
            the bottom of the logo image container" unchanged — no edit
            needed there, just re-confirmed against the new spec.
          * front-page.php 1.4.0 -> 1.5.0: swapped .tagline/.hero-logo
            DOM order (tagline now first) so the new mobile normal-flow
            stacking renders in the right visual order — no effect on
            desktop (both stay position:absolute).
          * Flagged: a fixed 700px logo + the fixed 60px left offset can
            still overlap the side menu on desktop widths just above the
            830px mobile breakpoint (e.g. ~850-1160px) — inherent to the
            user's explicit "no shrink" instruction, not fixed here; see
            website-build-protocol.md Open Items.
- 1.14.0 — Mobile-responsive pass, per user request. Moves the hero logo
          and tagline off unclamped viewport-relative (vw) sizing and adds
          a click/tap accordion mode for the side menu below a defined
          breakpoint. Full detail:
          * .hero-logo: width 45vw -> clamp(300px, 50vw, 700px). Fixed
            700px "desktop" width, flexes down continuously between
            viewport widths 1400px (where 50vw=700, the ceiling) and 600px
            (where 50vw=300, the floor) — below 600px viewport width
            (covers every real phone width) it's pinned at exactly 300px,
            matching the user's separate "300px on mobile" spec with the
            same rule, no extra breakpoint needed. top: 53vh -> 52vh, per
            spec ("52% of the viewport height"), unchanged for mobile —
            vh is a viewport-height fraction, not a width-based unit, so
            it wasn't part of the "move away from viewport-width-relative
            units" ask.
          * .tagline: width 28vw -> clamp(187px, 31.1vw, 436px) — same
            clamp technique, bounds scaled to preserve the existing
            28/45 = 62.2% ratio to the logo's own old/new bounds (per
            spec: "the width will remain the same [percentage] of the
            logo"). font-size 0.702vw -> clamp(12px, 0.75vw, 15px) — adds
            the requested 12px floor; the 15px ceiling and 0.75vw slope
            are chosen defaults (only a minimum was specified), flagged
            for confirmation. top: 53vh -> 52vh, matching the logo (kept
            in sync since the tagline is positioned relative to the same
            anchor).
          * New CSS CONTROL PANEL variable --menu-breakpoint: 830px
            (= .side-menu's max-width 400px + .menu-desc's max-width
            430px, per the user's own formula) — the single breakpoint
            where the side menu switches from desktop hover-reveal to a
            tap/click accordion. Read live by assets/js/main.js
            (isMenuAccordionActive()) so the JS threshold and the CSS
            media query below stay conceptually tied to one documented
            number — note the @media condition itself must still hardcode
            830px (CSS custom properties aren't allowed inside @media
            conditions); if this value ever changes, update both.
          * Below --menu-breakpoint (830px) — this applies uniformly to an
            actual mobile device AND a narrowed desktop browser window,
            per spec ("when the viewport is getting close to that size the
            [mobile] functionality... will apply to the desktop menu"):
            - .hero switches to height:auto/min-height:100vh with
              overflow-x:hidden (keeps the stagger-item off-screen
              entrance from creating a horizontal scrollbar) but
              overflow-y:visible, so the page can grow taller and scroll
              as an accordion item's description expands, instead of
              being clipped by the old fixed-height overflow:hidden hero.
            - .side-menu switches from position:absolute (anchored
              top/right/bottom, viewport-relative) to a static, in-flow
              block: full-width up to a 500px max-width, 24px side
              padding, margin-top clearing the logo/tagline area. Replaces
              the old percentage-based mobile override
              (top:7%/right:4%/max-width:78%) — percentages here were
              exactly the viewport-relative sizing the user asked to move
              away from.
            - .side-menu a becomes display:block (full-width tap target)
              instead of inline-block.
            - .menu-desc switches from an absolute hover-reveal flyout
              (opacity/visibility) to a static block below its link,
              collapsed via max-height:0/overflow:hidden and expanded via
              a new .is-menu-open class on the parent <li>
              (max-height:400px) — this is what "slides out below" and
              pushes subsequent items down, since it's back in normal
              document flow instead of position:absolute. The desktop
              hover/:focus-visible reveal rule becomes a harmless no-op
              here (opacity was already forced to 1; visibility is no
              longer the hide mechanism) — hovering alone no longer opens
              it, only the tap/click handler does.
          * assets/js/main.js: new initMenuAccordion() — click handler on
            each .side-menu li's <a>. Below --menu-breakpoint: prevents
            default navigation, closes any other open item, and toggles
            the clicked item's .is-menu-open (re-tapping the same item
            closes it) — exactly the open/close/exclusive-open behavior
            in the spec. Above the breakpoint: does nothing, desktop hover
            behavior is untouched. Runs immediately (like
            initContactCopy()), independent of the load-sequence phases.
          * Files touched: style.css (1.13.0 -> 1.14.0), assets/js/main.js
            (1.6.0 -> 1.7.0). front-page.php unchanged — the existing
            <li class="stagger-item"><a>...</a><span class="menu-desc">
            markup already has everything the accordion JS/CSS needs.
- 1.13.0 — Contact flyout fixes per user request: reduced the email/phone
          gap 20px -> 15px (.contact-flyout-phone top 52px -> 47px);
          added a .contact-flyout-item::before hover-bridge extending
          each item's hoverable box 10px above the link, fixing a bug
          where moving the mouse from the link up to the copy icon
          crossed a dead 4px gap, dropped :hover, and hid the icon
          before it could be clicked.
- 1.12.0 — Added the contact flyout (.contact-flyout and friends), per
          user request: a fixed tab using assets/svg/contact us menu.svg
          (240x100 — the SVG's own viewBox, containing a 216px-wide light
          card plus a 24px-wide dark "Contact Us" tab baked into the art)
          as its background. Inactive state shows only the tab (24px)
          flush against the viewport's left edge; hover/focus-within
          slides the whole 240px card into view.
          * New CSS CONTROL PANEL variable --contact-flyout-duration
            (350ms, a chosen default — the user asked for "a smooth
            transition" without a number).
          * New Global CSS Variable --c2f-font-contact: currently just
            Arial, Helvetica, sans-serif. The user asked for "Unica" (Neue
            Haas Unica, per their Adobe Fonts screenshot) but that family
            is NOT present in kit prx6fka (confirmed by fetching the kit
            CSS directly, per the standing Font Sourcing rule) — using
            Arial as a placeholder until the user adds it to the kit or
            names an in-kit alternative. Flagged prominently in this
            session's reply and in website-build-protocol.md Open Items.
          * Easing: uses a true CSS ease-in curve
            (cubic-bezier(0.42, 0, 1, 1)) — slow start, fast finish — per
            explicit, unambiguous user request this time ("ease in
            transition style", no contradicting behavior description).
            This intentionally diverges from the rest of the page's
            standard ease-OUT convention; flagged as a one-off.
          * .contact-flyout-item / .contact-flyout-copy: text right-
            aligned 36px from the SVG's right edge (240px), font-size
            12px, color #4d4d4d, letter-spacing 0.04em, line-height:1 (so
            the "20px below the email's bottom" spec computes cleanly:
            email top 20px + 12px line-height + 20px gap = phone top
            52px). Copy icon (two overlapping squares, stroke-only,
            currentColor) is a small inline SVG in front-page.php, shown
            on hover/focus of its item, positioned above the link text —
            interpretation of "pops up above each item to the right"
            flagged as one reasonable reading of an ambiguous instruction.
          * `:focus-within` added (keyboard accessibility, keeps the
            flyout open while a child link/button has focus) — not
            explicitly requested, same pattern as .menu-desc's
            :focus-visible in v1.8.0; flag if undesired.
- 1.11.0 — .menu-desc font swapped Sol Pro -> FF Chambers Sans (Web)
          Regular, per user request — Sol Pro was illegible at the
          description's size/weight. Sourced by fetching the kit CSS
          directly (https://use.typekit.net/prx6fka.css), per the standing
          "Font sourcing" rule (new-project-instructions.md v1.8) — no
          Adobe Fonts/Creative Cloud lookup tools used. Kit family name
          "ff-chambers-sans-web", Regular = weight 400 (unchanged from
          Sol Pro's weight, only the family changed).
          --c2f-font-desc updated in place (still used only by .menu-desc).
          Also: .menu-desc line-height 16px -> 28.8px (+80%, per user
          request).
- 1.10.0 — Restyled .menu-desc (the hover description text) to Sol Pro
          Regular, per user request (matching the Adobe Fonts catalog
          screenshot for "Sol"). Per the new standing "Font sourcing" rule
          (see new-project-instructions.md v1.8), sourced by reading the
          kit CSS directly (https://use.typekit.net/prx6fka.css) — kit
          family name "sol-pro", Regular = weight 400 — no Adobe Fonts/
          Creative Cloud lookup tools used. Superseded by 1.11.0 above.
          * New Global CSS Variable --c2f-font-desc: "sol-pro", Arial,
            Helvetica, sans-serif.
          * .menu-desc: font-family changed from `inherit` (Arial, via
            body) to var(--c2f-font-desc); added explicit font-weight:400.
          Note: this file also carries the user's own direct manual edits
          from earlier the same session (tagline width/weight/color,
          .side-menu max-width, .menu-desc max-width/font-size/letter-
          spacing) — logged in website-build-protocol.md v2.14, re-synced
          here before this edit.
- 1.9.0 — Applied the Adobe Fonts (Typekit) kit `prx6fka` (already enqueued
          in functions.php 1.2.0) to the side menu, per user request:
          * New Global CSS Variable --c2f-font-menu: "poster-gothic-atf",
            Arial, Helvetica, sans-serif — the kit's actual CSS family name
            (confirmed by fetching the kit stylesheet directly), with the
            prior Arial stack kept as the web-safe fallback.
          * .menu-heading (category labels): font-weight 700 -> 500
            (Poster Gothic ATF MEDIUM). User guessed "400" for Medium;
            confirmed via Adobe's font metadata (font_details) and the
            kit's own @font-face rules that Medium is actually weight 500.
          * .side-menu a (leaf items): font-weight 400 -> 300
            (Poster Gothic ATF LIGHT). User guessed "100" for Light;
            confirmed actual weight is 300 (Adobe's own font_details, cross-
            checked against the kit's @font-face rules) — 100 does not
            exist as a style in this family.
          * .menu-desc's font-family:inherit comment corrected — it inherits
            from body (Arial), NOT from .side-menu a, since .menu-desc is a
            sibling of <a> inside the <li>, not a descendant. (Superseded
            by 1.10.0 above — .menu-desc now has its own explicit font.)
          * Both weights confirmed present and licensed for web use in kit
            prx6fka via a direct fetch of https://use.typekit.net/prx6fka.css
            (weight 300 = Light, weight 500 = Medium, among others).
- 1.8.0 — Added .menu-desc, the hover/focus description pop-out for each
          leaf menu item, per user request:
          * Pure-CSS reveal (no JS): hidden via opacity:0/visibility:hidden
            by default, shown on `li:hover`/`a:focus-visible` via opacity/
            visibility transition (150ms, the same quadratic ease-out
            curve used everywhere else — duration wasn't specified, chosen
            as a reasonable "quick fade" default; flag if a different feel
            is wanted).
          * Positioned with `position:absolute; right:100%; margin-right:
            18px;` relative to the <a> (position:relative) — right:100%
            aligns the description's right edge to the <a>'s own left
            edge (the actual start of that item's text, which varies per
            item since the menu is right-aligned), then margin-right:18px
            adds the requested 18px gap. This tracks each item's real text
            position rather than a fixed column.
          * max-width: 380px, height auto (default). font-size 11px,
            line-height 16px, color #808080, letter-spacing 0.02em,
            font-family inherited (same as menu items). text-align:
            justify with text-align-last:left. text-transform:none
            (sentence case, unlike the uppercase menu items/headings).
            Padding 20px top/bottom, 16px left/right. border-right: 0.5px
            solid #b5b5b5. background: rgba(255,255,255,0.85).
          * Vertically centered on its <li> via top:50%;
            transform:translateY(-50%).
          * Also revealed on `a:focus-visible` (not just :hover) for
            keyboard accessibility — not explicitly requested, added as a
            baseline a11y measure; flag if undesired.
- 1.7.0 — Easing + spacing pass, per user request:
          * Easing changed from quadratic ease-IN-OUT
            (cubic-bezier(0.455, 0.03, 0.515, 0.955)) to quadratic
            ease-OUT (cubic-bezier(0.25, 0.46, 0.45, 0.94)) — fast at the
            start, slowing into the resting position — applied to every
            transition in the file, including .side-menu a's hover
            transition (was plain `ease`). Note: the user said "ease in"
            but described "accelerated at the beginning and slow down at
            the end," which is the definition of ease-OUT, not ease-in
            (slow start, fast finish) — implemented per the described
            behavior; flag if actual ease-in was intended.
          * .side-menu: right offset 5% -> 62px (fixed).
          * .tagline: horizontal correction -30px -> -20px (bumped 10px
            back toward the right, per spec) — transform is now
            translate(calc(-50% - 20px), calc(-100% - 26px)).
- 1.6.0 — Menu-stagger entrance rework + tagline centering fix, per user
          request:
          * .stagger-item's starting position changed from
            `translateX(40px)` (a small partial offset) to
            `translateX(100vw)` — guarantees the element's left edge
            starts at or beyond the viewport's right edge regardless of
            its resting horizontal position, so labels/items now enter
            from fully off-screen rather than just sliding in from
            slightly right of their resting spot.
          * Split .stagger-item's transition-duration by PROPERTY instead
            of by subtype: opacity now uses --menu-opacity-duration
            (200ms, was part of the shared 280ms/320ms durations),
            transform (position) uses --menu-position-duration (420ms).
            This applies identically to both category headings and
            subcategory items, so the old per-subtype
            --menu-heading-duration/--menu-item-duration Control Panel
            knobs (added in 1.5.0) are removed/consolidated into these
            two property-based knobs — .menu-heading.stagger-item and
            li.stagger-item no longer need their own duration overrides.
          * .tagline: added a -30px horizontal correction to the centering
            transform. The container is built the same way as .hero-logo
            (left:50% + translateX(-50%), which should center it on the
            same line as the logo), but the user observed it sitting
            ~30px right of center over the logo. Root cause not isolated
            (Claude-in-Chrome is off this session, so this couldn't be
            inspected live — possibly an Astra global box-model rule
            interacting with the full-bleed .hero, or a scrollbar-width
            asymmetry) — applied as a direct compensating offset per the
            user's own fallback instruction. Flagged in Open Items to
            re-verify/re-tune once live inspection is back on the table.
- 1.5.0 — Moved the 5 entrance-sequence transition durations into the CSS
          CONTROL PANEL as adjustable custom properties (values unchanged
          from 1.4.0). assets/js/main.js reads the logo/tagline durations
          at runtime via getComputedStyle() so CSS edits and JS gating
          stay in sync automatically.
- 1.4.0 — Load-sequence + spacing/color pass: hero fill #f2f2f2 -> #fcfcfc,
          .hero-logo scale+opacity entrance, .tagline resized/repositioned,
          .menu-heading shifted 4px left, quadratic ease-in-out applied
          throughout in place of plain `ease`. See prior version history in
          the project's website-build-protocol.md for full detail.
- 1.3.0 — Hero rebuild: background image -> solid fill + logo image, fluid
          tagline, faster/reordered stagger.
- 1.2.0 — Tagline resized (10px, 48% grayscale, fixed gap).
- 1.1.0 — Ported the hero / tagline / side-menu / stagger-in styles from the
          legacy plain-PHP build's css/style.css v1.4.
- 1.0.0 — Initial child theme scaffold. Global CSS variables carried over
          from the legacy build.
========================================================================== */

/* ==========================================================================
   Global CSS Variables
   ========================================================================== */
:root {
  --c2f-blue: #00AEEA;
  --c2f-charcoal: #3A3A3C;
  --c2f-gray: #808080;        /* menu sub-item text */
  --c2f-gray-dark: #4D4D4D;   /* unused by tagline as of v1.2.0+, kept for reference */
  --c2f-white: #FFFFFF;
  --c2f-footer-gray: #B5B5B5; /* menu-footer note text */
  --c2f-bg-fill: #fcfcfc;     /* hero solid background fill */
  --c2f-font-label: "akzidenz-grotesk-next-pro", "Akzidenz-Grotesk Next",
                    "poster-gothic-atf", Arial, Helvetica, sans-serif;
                               /* Menu GROUP LABELS (.menu-heading), at
                                  weight 500 (Medium). See the 1.32.0
                                  changelog entry above: kit prx6fka does
                                  not yet carry 500 for the standard
                                  width, so until it is added and the kit
                                  republished, visitors fall through to
                                  akzidenz-grotesk-next-pro 400 and then
                                  to poster-gothic-atf. */
  --c2f-font-item: "akzidenz-grotesk-next-pro", "Akzidenz-Grotesk Next",
                   "poster-gothic-atf", Arial, Helvetica, sans-serif;
                               /* Menu LEAF ITEMS (.side-menu a), at
                                  weight 400 (Regular). Same kit caveat as
                                  --c2f-font-label above. */
  --c2f-menu-label-color: #333333;  /* ~80% black — .menu-heading */
  --menu-accent-color: var(--c2f-blue); /* v1.34.0 — accent for a
                               highlighted menu group's heading. Set to
                               the logo's "CONCEPT" blue (#00AEEA).
                               Applied via .menu-group--accent, which
                               front-page.php adds to the "Intelligence +
                               Code + Automation" group by matching the
                               label string. */
  --c2f-menu-item-color: #545454;   /* ~67% black — .side-menu a */
  --c2f-font-menu: "poster-gothic-atf", Arial, Helvetica, sans-serif;
                               /* Adobe Fonts (Typekit) kit prx6fka —
                                  https://use.typekit.net/prx6fka.css,
                                  enqueued in functions.php 1.2.0. Family
                                  name confirmed by fetching the kit CSS
                                  directly. Weights available in-kit include
                                  300 (Light), 400 (Regular), 500 (Medium),
                                  600 (Semibold), 700 (Bold) — see
                                  .menu-heading / .side-menu a below for
                                  which weight each uses. */
  --c2f-font-desc: "ff-chambers-sans-web", Arial, Helvetica, sans-serif;
                               /* Same kit (prx6fka). Used by .menu-desc —
                                  see that rule below (Regular = weight
                                  400, confirmed by fetching the kit CSS
                                  directly). Was "sol-pro" in v1.10.0 —
                                  user found it illegible at menu-desc
                                  size and swapped to FF Chambers Sans
                                  (Web) in v1.11.0. */
  --c2f-font-contact: Arial, Helvetica, sans-serif;
                               /* PLACEHOLDER — user asked for "Unica"
                                  (Neue Haas Unica) but it is NOT in kit
                                  prx6fka (confirmed by fetching the kit
                                  CSS directly). Used by .contact-flyout's
                                  email/phone text below. Swap this to the
                                  real kit family + weight once it's added
                                  to the kit or an in-kit alternative is
                                  named. */
}

/* ==========================================================================
   ==========================================================================
   CSS CONTROL PANEL
   Adjustable knobs only — add a variable here only when a value is
   specifically requested to be adjustable. See website-dev-workflow skill
   for the full convention.
   ==========================================================================
   ========================================================================== */
:root {
  /* ---- Menu link hover "card" ----
     Used by .side-menu a further down.
     --menu-link-radius   px   corner radius on each menu link's
       hover "card" state. */
  --menu-link-radius: 2px;

  /* ---- Entrance-sequence transition durations ----
     Consumed directly by the CSS transition rules below AND (the logo/
     tagline ones) by assets/js/main.js, which reads them at runtime via
     getComputedStyle on :root — see that file's readDurationMs(). Edit a
     value here and both the visual transition and any dependent JS
     phase-gating stay in sync automatically. Always include the unit
     (e.g. "230ms" or "0.23s" — main.js accepts either).
     --logo-scale-duration      ms/s   .hero-logo transform (scale 0->1)
       duration. Also determines when the tagline phase starts (main.js
       waits for the LONGER of this and --logo-opacity-duration).
     --logo-opacity-duration    ms/s   .hero-logo opacity (0->1) duration.
     --tagline-duration         ms/s   .tagline opacity duration. Also
       determines when the menu-stagger phase starts.
     --menu-opacity-duration    ms/s   .stagger-item opacity transition
       duration (category headings AND subcategory items — same value for
       both). Not read by JS.
     --menu-position-duration   ms/s   .stagger-item transform
       (translateX, the off-screen-to-resting slide) transition duration
       (category headings AND subcategory items — same value for both).
       Not read by JS. */
  --logo-scale-duration: 230ms;
  --logo-opacity-duration: 120ms;
  --tagline-duration: 100ms;
  --menu-opacity-duration: 200ms;
  --menu-position-duration: 420ms;

  /* ---- Contact flyout slide ----
     --contact-flyout-duration   ms/s   .contact-flyout's `left` transition
       duration (closed tab <-> fully open card). Not read by JS. Default
       chosen by Claude — the user asked for "a smooth transition" without
       specifying a number. */
  --contact-flyout-duration: 350ms;

  /* ---- Mobile/narrow menu breakpoint ----
     --menu-breakpoint   px   the single viewport-width threshold below
       which .side-menu switches from desktop hover-reveal to a tap/click
       accordion (see the "Small screens" section near the end of this
       file). Value = .side-menu's max-width (400px) + .menu-desc's
       max-width (430px) = 830px, per spec. Read live by
       assets/js/main.js's isMenuAccordionActive() via getComputedStyle,
       same pattern as the entrance-sequence durations above — BUT the
       @media rule itself still has to hardcode 830px (CSS custom
       properties can't be used inside @media conditions), so this
       variable and that media query's max-width must be changed together
       if this value is ever revisited. */
  --menu-breakpoint: 830px;

  /* ---- Mobile header-to-menu gap ----
     --mobile-menu-top-gap   px   on mobile (below --menu-breakpoint), the
       distance from the bottom of the hero logo to the top of the side
       menu — .side-menu's margin-top in the mobile media query further
       below. Not read by JS, CSS-only. */
  --mobile-menu-top-gap: 15px;

  /* ---- Menu-item description line height ----
     --menu-desc-line-height   px   .menu-desc's line-height (the hover/
       focus-revealed description text on each leaf menu item, desktop and
       mobile alike). Not read by JS, CSS-only. */
  --menu-desc-line-height: 24px;

  /* ---- Menu hover / description shadow ----
     --menu-hover-shadow   box-shadow   the small, subtle drop shadow
       shared by the hovered menu item's white background (.side-menu
       a:hover/:focus-visible), its left-side bridge (::before), and
       .menu-desc — per user request: ~60% opacity, in 60% gray (NOT
       black), i.e. rgba(153,153,153,0.6). Not read by JS, CSS-only. */
  --menu-hover-shadow: 0 2px 6px rgba(153, 153, 153, 0.6);

  /* ---- Desktop side-menu vertical fit (v1.33.0) ----
     The desktop menu is absolutely positioned with BOTH `top` and
     `bottom` set, so its available height is (100vh - top - bottom) and
     its content must fit inside that or it visibly runs off the bottom
     of the screen. These five knobs are the whole vertical budget —
     raise the gaps for a more open feel on a tall screen, lower them if
     items are ever added back.
     --menu-top-offset       %/px   .side-menu's `top` — distance from
       the top of the viewport.
     --menu-bottom-offset    %/px   .side-menu's `bottom`.
     --menu-group-gap        em     space below each group (category
       block) before the next heading.
     --menu-heading-gap      em     space between a category heading and
       its first item.
     --menu-item-pad-y       px     vertical padding inside each item's
       hover card (horizontal padding is a separate literal, 10px).
     --menu-item-gap-y       px     vertical margin between items, on top
       of the padding above.
     --menu-item-line-height number line-height for leaf item text.
       Unitless on purpose so it scales with font-size. */
  --menu-top-offset: 1.5%;   /* v1.34.0 — was 4% */
  --menu-bottom-offset: 1.5%; /* v1.34.0 — was 4% */
  --menu-group-gap: 0.5em;  /* v1.34.0 — was 0.85em */
  --menu-heading-gap: 0.42em;
  --menu-item-pad-y: 2px;
  --menu-item-gap-y: 0px;
  --menu-item-line-height: 1.15;

  /* ---- Mobile menu bottom clearance ----
     --mobile-menu-bottom-gap   px   on mobile (below --menu-breakpoint),
       the space below the LAST menu item before the page ends — the
       bottom half of .side-menu's margin in the mobile media query.
       Raised from a plain 32px literal to 100px per user report that the
       last item was being cut off at the bottom edge of the screen on a
       phone. Not read by JS, CSS-only. */
  --mobile-menu-bottom-gap: 100px;

  /* ---- Hover-affordance info icon ----
     The circled "i" prepended to every leaf menu item (.side-menu
     a::before), signalling that hovering an item reveals its
     description.
     --menu-icon-size            em    icon box; 1em = the item's own
       font-size, so it scales with the menu type.
     --menu-icon-gap             ch/em gap between the icon's right edge
       and the label's first character.
     --menu-icon-opacity         0-1   resting subtlety.
     --menu-icon-opacity-hover   0-1   opacity once the item is hovered
       or keyboard-focused. */
  --menu-icon-size: 1em;
  --menu-icon-gap: 1ch;
  --menu-icon-opacity: 0.65;
  --menu-icon-opacity-hover: 0.85;

  /* ---- Description flyout viewport clearance (v1.37.0) ----
     The flyout is position:absolute and anchored to its own menu item.
     Items near the top or bottom of the column get an alternate
     alignment (see .menu-desc's edge-item rules below) so the box opens
     into the page rather than off the screen.
     --menu-desc-viewport-gutter  px  minimum clearance the flyout keeps
       from the top and bottom edges of the viewport. Enforced two ways:
       as a max-height backstop on the box itself, and via the edge-item
       alignment rules further down (first two items of the first group
       open downward, last two of the last group open upward). */
  --menu-desc-viewport-gutter: 25px;

}

/* ==========================================================================
   Base
   ========================================================================== */
body.c2f-front-page {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: var(--c2f-white);
  font-family: Arial, Helvetica, sans-serif;
  color: var(--c2f-charcoal);
  -webkit-font-smoothing: antialiased;
}

body.c2f-front-page a { font-family: inherit; }

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow-x: hidden;  /* still clips .stagger-item's off-screen
                           translateX(100vw) entrance start */
  overflow-y: visible; /* v1.28.0, per user request (side-menu dropdown
                           rebuild): desktop .side-menu is absolutely
                           positioned with both top AND bottom set, so its
                           height is fixed by that constraint, not by
                           content — when a .menu-desc dropdown opens and
                           pushes the column taller than that box, it needs
                           to spill past the bottom edge rather than get
                           clipped. Was a single `overflow: hidden` before;
                           split into overflow-x/-y so horizontal clipping
                           (needed for the entrance animation) is unchanged
                           while vertical clipping is lifted — same pattern
                           the mobile media query already used below. */
  background: var(--c2f-bg-fill); /* solid fill, per spec */
}

/* Logo image. Entrance: starts at scale(0)/opacity:0 and scales up to
   full size (scale(1)) over --logo-scale-duration while opacity fades in
   over --logo-opacity-duration — independent durations, both adjustable
   in the CSS CONTROL PANEL above, same quadratic ease-out easing.
   Triggered by main.js adding .is-visible on load. */
.hero-logo {
  position: absolute;
  top: 52vh;              /* 52% of viewport height from the top, per spec.
                              Desktop only — see the --menu-breakpoint
                              media query for the mobile header treatment. */
  left: 50%;               /* Centered again (reverted v1.15.0's fixed
                               left-align — "it's supposed to load up
                               centered on the screen... keep it centered
                               on the viewport until the viewport width
                               gets down to the mobile app size"). */
  width: 500px;            /* Fixed — user's own direct manual edit in
                              VS Code (was 700px). See the --menu-breakpoint
                              media query for the separate mobile width
                              rule (also this same single element, as of
                              v1.18.0 — the mobile-only SVG logo was
                              removed). */
  height: auto;
  opacity: 0;
  z-index: 1;              /* "above the logo container" is relative to
                               this — see .contact-flyout (z:20) and
                               .side-menu (z:30) below */
  transform: translateX(-50%) scale(0);
  transition: opacity var(--logo-opacity-duration) cubic-bezier(0.25, 0.46, 0.45, 0.94),
              transform var(--logo-scale-duration) cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.hero-logo.is-visible {
  opacity: 1;
  transform: translateX(-50%) scale(1);
}


/* ---------- Tagline ---------- */

.tagline {
  position: absolute;
  top: 52vh;               /* kept in sync with .hero-logo's new 52vh.
                               Desktop only — see the --menu-breakpoint
                               media query for the mobile header
                               treatment. */
  left: 50%;               /* Centered again (reverted v1.15.0's fixed
                               left-align, matching .hero-logo above). */
  width: clamp(187px, 31.1vw, 436px);
                           /* Same clamp technique as .hero-logo, with
                              every bound scaled to preserve the existing
                              28/45 = ~62.2% ratio to the logo's own
                              bounds (300/436/700 * 0.622), per spec: "the
                              width will remain the same [percentage] of
                              the logo." Replaces the old unclamped 28vw. */
  /* Lifted above the logo image by its own height plus a FIXED 36px gap
     — per spec: "36 pixel offset from the bottom of the [tagline] text to
     the top of the logo" (unchanged by the v1.16.0 revert — only the
     horizontal centering was reverted, this vertical spec still stands).
     Horizontal centering restored via translate(-50%, ...) alongside the
     vertical lift. */
  transform: translate(-50%, calc(-100% - 36px));
  display: flex;
  justify-content: space-between; /* spaces the 3 words across the
                                      container proportionately, so
                                      spacing scales with viewport instead
                                      of using a fixed px gap */
  margin: 0;
  white-space: nowrap;
  font-style: italic;
  font-weight: 300;   /* per user's direct manual edit (was 400) */
  font-size: clamp(12px, 0.75vw, 15px);
                      /* 12px floor per spec (was unclamped 0.702vw,
                         ~11px at a 1568px reference viewport — could
                         shrink below legibility on narrow viewports).
                         15px ceiling and 0.75vw slope are chosen
                         defaults — only the floor was specified, flagged
                         for confirmation. */
  letter-spacing: 0.03em;
  color: rgb(151, 151, 151); /* per user's direct manual edit (was rgb(102,102,102)) */
  opacity: 0;
  transition: opacity var(--tagline-duration) cubic-bezier(0.25, 0.46, 0.45, 0.94);
  pointer-events: none;
}
.tagline.is-visible { opacity: 1; }

/* ---------- Side menu ---------- */

.side-menu {
  position: absolute;
  top: var(--menu-top-offset);      /* v1.33.0 — was a literal 9% */
  right: 62px;   /* fixed offset from viewport's right edge, per spec (was 5%) */
  bottom: var(--menu-bottom-offset); /* v1.33.0 — was a literal 8% */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: right;
  max-width: 400px;   /* per user's direct manual edit (was 320px) */
  z-index: 30;   /* "above the logo container" (z:1) and above the
                    .contact-flyout (z:20) — per spec */
}

.menu-group { margin: 0 0 var(--menu-group-gap); } /* v1.33.0 — was 1.6em */

.menu-heading {
  position: relative;
  right: 4px;    /* shifted 4px left, per spec */
  margin: 0 0 var(--menu-heading-gap); /* v1.33.0 — was 0.65em */
  font-family: var(--c2f-font-label);   /* v1.32.0 — was --c2f-font-menu
                         (Poster Gothic ATF) */
  font-size: 0.8rem;
  font-weight: 600;   /* v1.36.0 — Akzidenz-Grotesk Next BOLD, per user
                         request. Was 500 (Medium), which is NOT published
                         in kit prx6fka for this width and was therefore
                         falling back to -pro 400 and rendering too light.
                         700 is published, so it renders as written. */
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--c2f-menu-label-color);   /* v1.32.0 — ~80% black, was
                         var(--c2f-charcoal) */
}

/* Accent group heading (v1.34.0). Applied by front-page.php to the group
   whose label matches the accent target — see c2f_get_menu()'s label
   strings. Keyed off a class rather than :nth-of-type so adding or
   reordering groups can't silently move the accent to the wrong one. */
.menu-group--accent .menu-heading {
  color: var(--menu-accent-color);
}

.side-menu ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.side-menu li {
  position: relative; /* anchors the mobile hairline divider (.side-menu
                          li::after, further down) AND, as of v1.34.0,
                          gives each item its own stacking position so the
                          hovered one can be raised above its siblings */
  z-index: 1;
  margin: 0;
}
/* v1.34.0 — lift the hovered/focused item (and therefore its .menu-desc
   flyout) above every other item in the list. Without this, later
   siblings paint over a flyout opening from an item near the top, since
   equal-z-index positioned elements fall back to document order. */
.side-menu li:hover,
.side-menu li:focus-within {
  z-index: 50;
}

.side-menu a {
  position: relative; /* anchors .menu-desc's absolute flyout positioning */
  display: inline-block;
  font-family: var(--c2f-font-item);    /* v1.32.0 — was --c2f-font-menu
                         (Poster Gothic ATF) */
  font-size: 0.7rem;
  font-weight: 500;   /* v1.34.0 — Akzidenz-Grotesk Next MEDIUM, per user
                         request ("bump up the array to the medium version
                         of the font"). Was 400 Regular, itself up from
                         300 Poster Gothic ATF Light. See the kit caveat
                         in the 1.34.0 changelog — 500 is not published in
                         prx6fka for this width yet. */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c2f-menu-item-color);    /* v1.32.0 — ~67% black, was
                         var(--c2f-gray) #808080 */
  text-decoration: none;
  line-height: var(--menu-item-line-height); /* v1.33.0 — NEW; was the
                         browser default, the biggest single contributor
                         to the menu overflowing the viewport */
  padding: var(--menu-item-pad-y) 10px;      /* v1.33.0 — was 4px 10px */
  margin: var(--menu-item-gap-y) 0;          /* v1.33.0 — was 1px 0 */
  border-radius: var(--menu-link-radius);
  background-color: transparent;
  transform: translateY(0);
  transition: transform 220ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
              background-color 220ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
              color 220ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Hover: text "rises" with a white card. Drop shadow removed in v1.23.0
   (per user request at the time) — it read as a hard-edged separate card
   once the item's background started bridging into .menu-desc's gradient;
   a flat white surface reads as one continuous plate instead.
   RE-ADDED in v1.26.0 — new, explicit, direct user request for a small/
   subtle shadow (60% opacity, 60% gray) — see --menu-hover-shadow in the
   CSS CONTROL PANEL above. Kept intentionally small/soft so it doesn't
   reintroduce the hard-edged "separate floating card" look v1.23.0 was
   fixing. Applies uniformly to every list item across the whole menu. */
/* v1.29.0: reverted back to a plain two-selector hover rule, per user
   request — the click-driven .is-menu-open state (and the .menu-arrow
   chevron below) is removed entirely; desktop interaction is pure CSS
   :hover/:focus-visible again, no JS-driven open state. The v1.28.0
   .side-menu a:hover::before/:focus-visible::before "bridge" pseudo-
   element stays removed (that was its own, separate, deliberate
   simplification per user request — "cut the extension off the left of
   the array item" — not part of what broke and not part of this revert). */
.side-menu a:hover,
.side-menu a:focus-visible {
  transform: translateY(-4px);
  background-color: var(--c2f-white);
  color: var(--c2f-charcoal);
  box-shadow: var(--menu-hover-shadow); /* v1.26.0, per user request */
}

/* ---------- Hover-affordance info icon (v1.32.0) ----------
   A circled "i" prepended to every leaf menu item, telling the visitor
   that hovering the item reveals a description — previously there was no
   visual cue that the descriptions existed at all.

   Drawn as an inline data-URI SVG used as a MASK rather than as a
   background image, so the glyph's color comes from `background-color`
   (set to currentColor) and therefore tracks the link's own gray ->
   charcoal hover transition for free. Implemented as a pseudo-element
   deliberately: the menu is generated by looping over c2f_get_menu() in
   inc/site-config.php, and doing this in CSS meant neither the PHP data
   array nor front-page.php's markup had to change.

   All four knobs live in the CSS CONTROL PANEL above. Stroke weight is
   baked into the SVG at 1.5/24 (thin on purpose, per user request); if
   it ever reads too faint on a non-retina display, raise the
   stroke-width value inside BOTH url() strings below — they must stay
   identical (-webkit-mask is still required for Safari). */
.side-menu a::before {
  content: "";
  display: inline-block;
  width: var(--menu-icon-size);
  height: var(--menu-icon-size);
  margin-right: var(--menu-icon-gap);
  vertical-align: -0.14em; /* optically centers the circle against
                               uppercase menu type; not a baseline value */
  background-color: currentColor;
  opacity: var(--menu-icon-opacity);
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%228.5%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%227.5%22%20r%3D%220.55%22%20fill%3D%22%23000%22%20stroke%3D%22none%22%2F%3E%3Cpath%20d%3D%22M12%2010.9%20L12%2016.4%22%2F%3E%3C%2Fsvg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%228.5%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%227.5%22%20r%3D%220.55%22%20fill%3D%22%23000%22%20stroke%3D%22none%22%2F%3E%3Cpath%20d%3D%22M12%2010.9%20L12%2016.4%22%2F%3E%3C%2Fsvg%3E") no-repeat center / contain;
  transition: opacity 220ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.side-menu a:hover::before,
.side-menu a:focus-visible::before {
  opacity: var(--menu-icon-opacity-hover);
}

/* ---------- Astra scroll-to-top suppression (v1.32.0) ----------
   Astra renders its own back-to-top control, themed in Astra's default
   link blue, which this child theme never styled — it was appearing as a
   blue circle low on the right side of the desktop layout, near the
   bottom of the side menu. Hidden here rather than switched off in the
   Customizer so the decision travels with the theme in git. If the
   control is ever wanted, delete this block and style it instead. */
#ast-scroll-top,
.ast-scroll-top-icon {
  display: none !important;
}

.menu-footer {
  margin: 1em 0 0;
  font-size: 0.65rem;
  font-style: italic;
  letter-spacing: 0.05em;
  color: var(--c2f-footer-gray);
}

/* ---------- Menu item hover description flyout ----------
   Reverted v1.29.0, per explicit user request — the v1.28.0 click-to-open
   in-flow dropdown (above) rendered broken in the browser ("doesn't even
   look like a menu... nothing is sliding out"), and the user asked to go
   back to the original hover-triggered design: "hovering an item lifts it
   onto a white background with a drop shadow... falls back into inactive
   view when somebody hovers over the next item... the description opens
   up to the left of the menu in a container 420 pixels wide with an
   auto-height based on the contents, 15 pixel padding." Pure CSS
   :hover/:focus-visible reveal again — no JS, no .is-menu-open class, no
   click state — desktop only; see the mobile media query further below
   for the separate, unchanged-in-spirit mobile click-accordion pattern
   (still driven by assets/js/main.js's mobile-only initMenuAccordion()).
   Fixed 420px width and flat 15px padding are the user's exact spec,
   replacing the old max-width:380/430px + asymmetric 20/16px padding.
   Background simplified to flat white (var(--c2f-white)) instead of the
   old semi-transparent/gradient fill, and the old border-right divider is
   dropped in favor of the shared --menu-hover-shadow box-shadow (matching
   the hovered item's own shadow) — both per the user's "keep it simple
   for now" instruction; flagging as an interpretive simplification since
   neither was spelled out explicitly. */
.menu-desc {
  position: absolute; /* v1.37.0 — restored. v1.35.0's `fixed` did NOT
                        anchor to the viewport, because .side-menu a's
                        hover transform makes it a containing block for
                        fixed descendants — see the 1.37.0 changelog. */
  top: 50%;
  right: 100%;      /* v1.37.0 — restored: aligns the flyout's right edge
                        to the <a>'s own left edge, which varies per item
                        since the menu is right-aligned */
  margin-right: 18px;
  transform: translateY(-50%); /* default: centered on its own item. The
                        edge-item rules below override this for the items
                        at the very top and bottom of the menu. */
  max-height: calc(100vh - (2 * var(--menu-desc-viewport-gutter)));
  overflow-y: auto; /* backstop on the gutter rule — an unusually long
                        description scrolls inside the box rather than
                        bleeding off-screen */
  z-index: 100;   /* v1.34.0 — was 5; see below, the hovered
                     item's <li> is lifted above later siblings too */
  width: 420px;      /* fixed width, per user's exact spec (was
                        max-width: 380px / 430px) */
  height: auto;      /* auto-height based on contents, per user's exact
                        spec */
  box-sizing: border-box;
  padding: 15px;     /* flat 15px on all sides, per user's exact spec
                        (was asymmetric 20px/16px/44px, sized to clear the
                        now-removed phone/envelope icon row) */
  box-shadow: var(--menu-hover-shadow); /* same shadow as the hovered
                        item itself, per user request — "lifts it onto a
                        white background with a drop shadow" */
  background-color: rgba(255, 255, 255, 0.87); /* 87% opacity white, per
                        user request (was flat opaque var(--c2f-white)) */
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94),
              visibility 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
                          /* simple, non-delayed fade — the v1.27.0 600ms
                             hover-intent grace period existed only to keep
                             the now-removed icon row reachable by mouse;
                             no longer needed, per "keep it simple" */
  font-family: var(--c2f-font-desc); /* FF Chambers Sans (Web) Regular, per
                                         user request — Sol Pro (v1.10.0)
                                         was found illegible at this size.
                                         UNCHANGED here — the user asked to
                                         try "Merel" at a 200-300 weight,
                                         but a direct fetch of the kit CSS
                                         (https://use.typekit.net/prx6fka.css,
                                         per the standing Font Sourcing
                                         rule) confirms no family named or
                                         resembling "Merel"/"Meret" exists
                                         in kit prx6fka — its 5 families
                                         are liberation-sans,
                                         ff-chambers-sans-web, sol-pro,
                                         poster-gothic-atf, and
                                         poster-gothic-round-atf. Also:
                                         ff-chambers-sans-web itself has no
                                         200/300 weight in the kit (only
                                         400/500/700/900), so the requested
                                         weight couldn't apply here either
                                         way. Flagged — see
                                         website-build-protocol.md Open
                                         Items; add Merel to the kit (or
                                         name an in-kit alternative) to
                                         finish this. */
  font-weight: 400;      /* FF Chambers Sans (Web) REGULAR — unchanged, see
                            font-family comment above */
  font-size: 12px;       /* per user's direct manual edit (was 11px) */
  line-height: var(--menu-desc-line-height); /* was a plain 5.76px literal
                            — now a CSS CONTROL PANEL variable per user
                            request, directly adjustable. Originally set
                            to the "reduce the line height by 80%" result
                            (28.8 * 0.2 = 5.76px); user has since manually
                            retuned the variable itself to 24px in VS Code
                            (caught resyncing after a git merge conflict,
                            v1.22.0) — see the variable's own declaration
                            in the CSS CONTROL PANEL section above for the
                            current value. */
  letter-spacing: 0.03em; /* per user's direct manual edit (was 0.02em) */
  color: #808080;
  text-transform: none;   /* sentence case — NOT uppercase like the menu links */
  text-align: justify;
  text-align-last: left;  /* justified body, last line left-aligned, per spec */
}
/* Reveal on hover of the item OR keyboard focus of its link — simple
   opacity/visibility fade, no delay, no JS. v1.29.0: restored to this
   plain form, per user request — the icon row that justified the v1.27.0
   600ms hover-intent delay is gone. */
/* ---------- Edge-item flyout alignment (v1.37.0) ----------
   The default rule above centers each description on its own menu item.
   That works everywhere except the very top and bottom of the column,
   where half the box would fall outside the viewport — which is exactly
   what the user reported on "Interactive Website + eCommerce" (clipped at
   the top) and "Brokerage + Consultation" (clipped at the bottom).

   Rather than hardcode IDs per item, these are structural selectors, so
   they track the menu automatically as items are commented out, added, or
   reordered in c2f_get_menu()'s arrays — nothing to keep in sync.

   FIRST GROUP, first two items: top-align the box to its item so it opens
   DOWNWARD into the page. */
.menu-group:first-of-type li:nth-child(-n + 2) .menu-desc {
  top: 0;
  bottom: auto;
  transform: none;
}
/* LAST GROUP, last two items: bottom-align so the box opens UPWARD. */
.menu-group:last-of-type li:nth-last-child(-n + 2) .menu-desc {
  top: auto;
  bottom: 0;
  transform: none;
}

.side-menu li:hover .menu-desc,
.side-menu a:focus-visible ~ .menu-desc {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* ---------- Contact flyout ----------
   Fixed tab at the viewport's left edge. Drawn from plain CSS as of
   v1.24.0 (no background image) — a 216px-wide light card (.contact-
   flyout-card) plus a 24px-wide dark "Contact Us" tab (.contact-flyout-
   tab), both absolutely positioned inside this 240px-wide container so
   they sit exactly where the old baked-in SVG artwork used to. Inactive
   state positions the 240px-wide element so only that 24px tab sits in
   the viewport (left: -(240-24) = -216px, i.e. right edge of the whole
   element at x:24); hover/focus-within slides it to left:0 so the full
   card is visible. See front-page.php for the .contact-flyout-card/
   .contact-flyout-tab markup (siblings of the .contact-flyout-item
   divs below, painted first so the real links/buttons stack on top). */
.contact-flyout {
  position: fixed;
  top: auto;
  bottom: 115px;          /* was top:115px — "move it down... to have a
                              115 pixel offset from the bottom," applies
                              to desktop and mobile alike (position:fixed
                              is always relative to the current viewport,
                              so no breakpoint override is needed here) */
  left: -216px;           /* 240 - 24 = 216 — see comment above */
  width: 240px;
  height: 100px;
  z-index: 20;            /* was 50 — "below the menu items [z:30] but
                              above the logo container [z:1]," per spec */
  opacity: 0.3;           /* inactive resting state, per spec */
  transition: left var(--contact-flyout-duration) cubic-bezier(0.42, 0, 1, 1),
              opacity var(--contact-flyout-duration) cubic-bezier(0.42, 0, 1, 1);
                          /* true ease-IN (slow start, fast finish) — see
                             file header changelog note on why this
                             diverges from the page's usual ease-out.
                             opacity added to the transition list to match
                             the slide — not explicitly requested, flag if
                             an instant opacity snap is preferred instead. */
}
.contact-flyout:hover,
.contact-flyout:focus-within {
  left: 0;
  opacity: 1;
}

/* Light card panel — sits under the email/phone items, rounded on its
   outer (right) corners only since its left edge is off-screen at rest
   and its right edge butts against .contact-flyout-tab. */
.contact-flyout-card {
  position: absolute;
  top: 0;
  left: 0;
  width: 216px;
  height: 100%;
  background: #f2f2f2;
  border-radius: 0 4px 4px 0;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}

/* Dark tab — always the rightmost 24px of the container, so it's the
   part still visible at rest (left:-216px hides everything else). */
.contact-flyout-tab {
  position: absolute;
  top: 0;
  right: 0;
  width: 24px;
  height: 100%;
  background: #3a3a3c; /* Charcoal — see Locked Identity Facts */
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0 4px 4px 0;
}
.contact-flyout-tab-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--c2f-font-contact); /* placeholder — see Global CSS
                                            Variables comment above */
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #ffffff;
  white-space: nowrap;
}

.contact-flyout-item {
  position: absolute;
  right: 36px;            /* from the SVG's right edge (240px), per spec */
}
.contact-flyout-email { top: 26px; }            /* was 20px — v1.25.0, more
                                                    vertical breathing room
                                                    in the 100px-tall card */
.contact-flyout-phone { top: 64px; }            /* was 47px — 26px top +
                                                    15px line-height (email)
                                                    + 23px gap, v1.25.0 (was
                                                    a 15px gap) */

/* Invisible hover-area bridge: extends each item's hoverable box 10px
   above the link (covering the dead space between the copy icon at
   top:-18px and the link itself) so moving the mouse from the link up to
   the copy icon doesn't cross a gap with no hovered element in it, which
   was dropping :hover and hiding the icon mid-click. This is itself
   `position:absolute` with `z-index:auto` — per the CSS painting-order
   spec, that paints ABOVE any plain non-positioned in-flow sibling
   (regardless of DOM order), which is exactly what was silently covering
   `.contact-flyout-item a` below and swallowing every click meant for the
   link (fixed v1.25.0 — see that changelog entry and the `a` rule's
   `position: relative` below, which is what keeps this bridge from
   intercepting the link while still extending its hover area). */
.contact-flyout-item::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 0;
  right: 0;
  bottom: 0;
}

.contact-flyout-item a {
  position: relative;      /* v1.25.0 fix — promotes this element into the
                               same z-index:auto positioned paint step as
                               .contact-flyout-item::before, where tree
                               order (this element comes after ::before in
                               the markup) decides the winner, so the real
                               link now paints — and is clickable — above
                               the hover-bridge pseudo-element instead of
                               being covered by it. No visual change (no
                               offset set). */
  z-index: 1;
  display: block;
  font-family: var(--c2f-font-contact); /* placeholder — see Global CSS
                                            Variables comment above */
  font-weight: 400;
  font-size: 15px;         /* was 12px — v1.25.0, per user request */
  line-height: 1;          /* keeps the phone-offset math exact */
  letter-spacing: 0.04em;
  color: #4d4d4d;
  text-decoration: none;
  text-align: right;
  white-space: nowrap;
}

/* Copy-to-clipboard icon button — hidden until its item is hovered/
   focused, positioned just above the link text, right-aligned to match
   it. Icon itself is inline SVG in front-page.php (two overlapping
   stroke-only squares, no fill, currentColor) — sizing/color set here. */
.contact-flyout-copy {
  position: absolute;
  top: -18px;
  right: 0;
  width: 14px;
  height: 14px;
  padding: 0;
  border: 0;
  background: transparent;
  color: #4d4d4d;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.contact-flyout-item:hover .contact-flyout-copy,
.contact-flyout-copy:focus-visible {
  opacity: 1;
  pointer-events: auto;
}
.contact-flyout-copy.is-copied {
  color: var(--c2f-blue); /* brief visual confirmation after a
                              successful copy — see main.js
                              initContactCopy(); not explicitly
                              requested, flag if undesired */
}

/* ---------- Load-in stagger (menu headings, list items) ----------
   Starts fully off-screen to the right (translateX(100vw), guarantees
   the element's left edge begins at/beyond the viewport's right edge
   regardless of its resting horizontal position) and 20% opacity, then
   slides/fades in to its resting position. Per-item delay is set inline
   via --d by assets/js/main.js: category headings (.menu-heading)
   animate first, in top-to-bottom order, then subcategory <li> items
   animate in a randomized order starting MENU_ITEMS_START_MS after the
   headings phase begins — see main.js. Runs after the logo-scale ->
   tagline-fade sequence finishes.

   Duration is split by PROPERTY (not by heading/item subtype, as of
   v1.6.0) via --menu-opacity-duration / --menu-position-duration (CSS
   CONTROL PANEL above) — the two-value transition-duration list below
   pairs positionally with transition-property's opacity/transform list.
   Both use the quadratic ease-out curve. Built on transition-property/
   -duration/-timing-function/-delay LONGHANDS rather than the
   `transition` shorthand: the shorthand resets every sub-property it
   doesn't mention, which would silently zero out the
   `transition-delay: var(--d, 0s)` stagger offset set below. Do not
   collapse these back into a `transition:` shorthand. */
.stagger-item {
  opacity: 0.2;
  transform: translateX(100vw);
  transition-property: opacity, transform;
  transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  transition-duration: var(--menu-opacity-duration), var(--menu-position-duration);
  transition-delay: var(--d, 0s);
}
.side-menu.is-visible .stagger-item {
  opacity: 1;
  transform: translateX(0);
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .hero-logo, .tagline, .stagger-item, .side-menu a, .menu-desc,
  .contact-flyout, .contact-flyout-copy {
    transition-duration: 0.01ms !important;
  }
}

/* ---------- Small screens / narrow desktop — menu layout ----------
   v1.28.0: click-to-open is now universal at all widths (see the base
   .menu-desc/.side-menu li rules above), so this breakpoint (830px =
   .side-menu's 400px max-width + .menu-desc's 430px max-width, per spec)
   no longer switches the OPEN/CLOSE mechanism — only the desktop-only
   quadratic gap-compression and hover-triggered auto-close (both
   neutralized below --menu-breakpoint by assets/js/main.js's
   isMenuAccordionActive() check) and the menu's own layout (absolute/
   fixed-height column vs. static/full-width stack). Applies identically
   to an actual mobile device and a narrowed desktop browser window — same
   breakpoint either way, per spec. */
@media (max-width: 830px) {
  .hero {
    height: auto;
    min-height: 100vh;
    overflow-x: hidden;  /* still clips .stagger-item's off-screen
                             translateX(100vw) entrance start */
    overflow-y: visible; /* lets the page grow/scroll as an accordion
                             item's description expands, instead of being
                             clipped by the old fixed-height hero */
  }

  /* Header block: .tagline + .hero-logo switch from the desktop's
     absolute/vh-anchored overlay to a normal, static-flow "header" at the
     top of the page — per spec ("the logo is to be rendered out at the
     header portion" on mobile). Source order matters here (front-page.php
     1.5.0 puts .tagline before .hero-logo in the markup) since normal
     flow stacks in document order, unlike the desktop's independently-
     positioned absolute overlay. */
  .tagline {
    position: static;
    top: auto; left: auto;
    transform: none;
    width: calc(100% - 60px); /* 30px left/right padding, per spec — was
                                  unchanged/inherited clamp() width; flex
                                  space-between re-spaces the 3 words
                                  automatically against the new width */
    margin: 40px auto 0;  /* the spec's "40px padding from the top of the
                              screen," measured to the TOP OF THE TAGLINE
                              per the user's own correction */
    font-size: 11px;      /* fixed, per spec (was 13px) */
  }

  .hero-logo {
    position: static;
    top: auto; left: auto;
    display: block;
    width: calc(100% - 50px); /* "25 pixel right and left padding" */
    margin: 18px auto 0;
    transform: scale(0); /* no horizontal transform needed — centered via
                             margin:auto, same as before */
  }
  .hero-logo.is-visible { transform: scale(1); }

  .side-menu {
    position: static;    /* was absolute/viewport-anchored */
    top: auto; right: auto; bottom: auto;
    width: 100%;
    max-width: 500px;
    margin: var(--mobile-menu-top-gap) auto var(--mobile-menu-bottom-gap);
                             /* top gap: --mobile-menu-top-gap (user-
                                adjustable logo-to-menu distance).
                                bottom: v1.32.0 — was a plain 32px
                                literal, now --mobile-menu-bottom-gap
                                (100px) per user report that the last menu
                                item was getting cut off at the bottom of
                                the screen on a phone. */
    padding: 0 24px;
    box-sizing: border-box;
    display: block;      /* was flex/column/space-between, which needed a
                             fixed-height anchor this layout no longer has */
  }
  .menu-heading { font-size: 0.72rem; }

  /* Hairline divider under each mobile menu item, via the "transform
     scale" technique (user's own research/screenshot, "Method 2: The
     Transform Scale Hack") instead of a plain sub-pixel border-bottom
     (which the user's earlier 0.5px value + manual color edit above used,
     and which some browsers/screen densities round up to a full 1px or
     drop entirely). Draws a real 1px line, then scales it down 50% from
     its own bottom edge so it renders crisply sub-pixel on any display —
     `.side-menu li` already has `position:relative` from the base rule
     above (outside this media query), so no extra positioning context is
     needed here. Color carries over the user's own manual edit
     (rgba(100,100,100,0.15)) unchanged — only the rendering technique
     changed, not the color/weight/spec. */
  .side-menu li::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: rgba(100, 100, 100, 0.15);
    transform: scaleY(0.5);
    transform-origin: 0 100%; /* anchor to the bottom edge so the halved
                                  line stays flush instead of drifting
                                  upward into the item's content */
    pointer-events: none;
  }
  .side-menu a {
    display: block;      /* was inline-block — full-width tap target */
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    padding-top: 5px;    /* v1.33.0 — desktop tightened its vertical
                             padding to 2px to fit the viewport; mobile
                             keeps a finger-sized tap target instead.
                             Line-height and item gap DO inherit the new
                             base values, per the user's "connected is
                             fine." */
    padding-bottom: 5px;
  }

  /* v1.29.0: mobile keeps its own separate click-accordion pattern —
     unaffected by the desktop hover-flyout revert above. This block is
     restored to a self-contained mobile-only override (it had briefly
     been merged into the shared base rule by v1.28.0, since reverted),
     since the base .menu-desc rule is an absolute-positioned flyout again
     and needs overriding back to an in-flow, click-toggled block here.
     Driven by assets/js/main.js's mobile-only initMenuAccordion(), which
     toggles .is-menu-open on the <li> — unchanged behavior from before
     the v1.28.0 rebuild. */
  .menu-desc {
    display: block;      /* v1.30.0 FIX — see changelog. .menu-desc is a
                             <span> (inline by default). On desktop,
                             position:absolute auto-blockifies it (CSS2.1
                             9.7), so max-height/overflow worked there. On
                             mobile, position:relative does NOT blockify —
                             it was staying inline, and max-height/overflow
                             have no effect on non-replaced inline boxes, so
                             every description rendered at its natural
                             (unclipped) height regardless of .is-menu-open.
                             This single property is the actual fix for the
                             "all descriptions permanently expanded" bug. */
    position: relative; /* was absolute (desktop flyout) */
    top: auto;
    right: auto;
    margin-right: 0;
    transform: none;
    width: 100%;
    max-width: 430px;
    box-sizing: border-box;
    height: auto;
    max-height: 0;       /* collapsed at rest — the open/close mechanism */
    overflow: hidden;
    padding: 0 0 0 4px;  /* left offset + zero vertical padding at rest;
                             open-state padding below restores full
                             spacing once expanded */
    border-top: 0.5px solid #b5b5b5; /* divider between item and its open
                             description, in-flow block only */
    box-shadow: none;    /* the flyout's shadow doesn't apply to an
                             in-flow block sitting under the item */
    background-color: var(--c2f-white); /* flat white, matching the
                             desktop flyout's simplification — icons'
                             removal means there's no longer a reason for
                             the old semi-transparent/gradient fill here */
    opacity: 1;          /* always "visible," just clipped to zero height
                             at rest — no hover on touch devices */
    visibility: visible;
    pointer-events: auto;
    transition: max-height 300ms ease-in, padding 300ms ease-in;
  }
  .side-menu li.is-menu-open .menu-desc {
    max-height: 400px; /* generous ceiling — real height is content-driven */
    padding: 14px 4px 14px 4px; /* flat top/bottom now that the old
                             phone/envelope icon-clearance bottom padding
                             (48px) is no longer needed — icons removed */
  }

  /* Contact flyout: touch doesn't have reliable hover, so the base
     :hover/:focus-within open-on-hover rule is neutralized here and
     replaced with a JS-toggled .is-open class (assets/js/main.js's
     initContactFlyoutToggle()) — tap the closed tab to open, tap the open
     card (anywhere but a link/button) to close, per user request. */
  .contact-flyout:hover,
  .contact-flyout:focus-within {
    left: -216px;
    opacity: 0.3;
  }
  .contact-flyout.is-open {
    left: 0;
    opacity: 1;
  }
}
