diff --git a/CHANGELOG.md b/CHANGELOG.md index a0281a60..3e8908d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Fix mobile menu: restore dropdown links, remove Systems, refine spacing (LS-3222) + +### Fixed + +- `parts/mobile-menu.html`, `styles/blocks/details/mobile-menu-accordion.json`: each top-level accordion label (Work, Solutions, Services, Pricing, Insights, About) is now a real link to its overview page (`/work/`, `/solutions/`, `/services/`, `/pricing/`, `/blog/`, `/about/`). Previously the label was plain text inside ``, so there was no way to reach e.g. "All Work" without opening the dropdown. Clicking the label now navigates directly; clicking elsewhere in the row still toggles the dropdown, using native `` click behavior with no JS needed. Added the label link's own colour/underline reset and `:focus-visible` outline. +- `parts/mobile-menu.html`: removed the non-functional "Systems" link row. + +### Changed + +- `parts/mobile-menu.html`, `src/scss/structural/_mega-menu.scss`, `styles/blocks/groups/mega-menu-item-service.json`: converted the Work/Solutions/Pricing/Insights/About and Services mobile dropdown page-lists from 2-column to single-column layouts; decoupled each row's visual height from its tap-target size (rows read tighter while the actual tap area stays at the accessible ~44px minimum via an expanded stretched-link hit area); tightened spacing between links, between each heading and its first link, and between the last link and the "See all…" CTA, while keeping stronger separation between sections/phases; increased the "See all…" CTA font size to match page-link text and removed a redundant divider above it; reduced indentation under each Services phase heading. + +([#58](https://github.com/lightspeedwp/ls-theme/pull/58)) + +--- + ## [Unreleased] — Build Services page: Entry Points and Delivery by the Numbers (LS-1598) ### Added diff --git a/assets/css/animations.css b/assets/css/animations.css index 264a8fe6..10d9bd1c 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1 +1 @@ -.ls-footer-nav-link a{color:inherit;text-decoration-line:underline;text-decoration-color:rgba(0,0,0,0);text-underline-offset:3px}.ls-footer-nav-link a:hover,.ls-footer-nav-link a:focus-visible{color:var(--wp--custom--color--phase--create);text-decoration-color:var(--wp--custom--color--phase--create)}.ls-footer-proof-card:hover,.ls-footer-proof-card:focus-within,.ls-footer-social-icon:hover,.ls-footer-social-icon:focus-within{background-color:var(--wp--custom--color--surface--card-raised) !important;border-color:var(--wp--custom--color--phase--create) !important}.ls-footer-social-icon{min-inline-size:var(--wp--custom--spacing--tap-target-min)}.ls-footer-proof-card{height:100%}.site-logo--for-light{display:var(--wp--custom--visibility--logo-for-light)}.site-logo--for-dark{display:var(--wp--custom--visibility--logo-for-dark)}.site-logo--for-light img,.site-logo--for-dark img{display:block;width:var(--wp--custom--layout--logo-width);height:auto}@media(max-width: 1023.98px){.site-header{padding-left:var(--wp--preset--spacing--60) !important;padding-right:var(--wp--preset--spacing--60) !important}}@media(max-width: 599.98px){.site-header{padding-left:var(--wp--preset--spacing--20) !important;padding-right:var(--wp--preset--spacing--20) !important}}.ls-button-cta-gradient .wp-block-button__link:hover,.ls-button-cta-gradient .wp-block-button__link:focus-visible{box-shadow:var(--wp--custom--shadow--interactive--accent),var(--wp--custom--shadow--elevation--300)}.site-header .wp-block-navigation__responsive-container-close{position:absolute;top:var(--wp--preset--spacing--20);right:var(--wp--preset--spacing--20);display:flex;align-items:center;justify-content:center;min-inline-size:var(--wp--custom--spacing--tap-target-min);min-block-size:var(--wp--custom--spacing--tap-target-min);padding:var(--wp--preset--spacing--10);border:1px solid var(--wp--custom--color--border--card);border-radius:var(--wp--preset--border-radius--500);background-color:color-mix(in srgb, var(--wp--custom--color--surface--card) 4%, transparent)}.site-header .wp-block-navigation__responsive-container-close svg{inline-size:20px;block-size:20px}.site-header .wp-block-navigation-item{position:static}.site-header .wp-block-navigation__submenu-container{left:50%;right:auto;transform:translateX(-50%)}.site-header .wp-block-ollie-mega-menu__menu-container{background:rgba(0,0,0,0)}.ls-mega-menu-panel-services{width:fit-content;max-width:100vw;box-sizing:border-box}.ls-mega-menu-panel-services .wp-block-columns{display:grid;grid-template-columns:repeat(6, minmax(0, 1fr));gap:var(--wp--style--block-gap)}.ls-mega-menu-panel-services .wp-block-column{width:auto}.ls-mega-menu-panel-services .wp-block-column>.wp-block-group p{white-space:nowrap}@media(max-width: 340px){.ls-simple-submenu-links.ls-simple-submenu-links{grid-template-columns:1fr}}.ls-services-phase-links{display:grid;grid-template-columns:repeat(2, max-content);column-gap:var(--wp--preset--spacing--20);row-gap:var(--wp--preset--spacing--20)}.ls-services-phase-links>*{margin:0}@media(max-width: 340px){.ls-services-phase-links{grid-template-columns:1fr}}.ls-services-phase-links{padding-left:calc(8px + var(--wp--preset--spacing--10))}.is-style-mega-menu-item-default{position:relative}.is-style-mega-menu-item-default h5 a::after{content:"";position:absolute;inset:0;z-index:1}.is-style-mega-menu-item-default:hover .wp-block-outermost-icon-block,.is-style-mega-menu-item-default:focus-within .wp-block-outermost-icon-block,.is-style-mega-menu-item-default:hover .wp-block-icon,.is-style-mega-menu-item-default:focus-within .wp-block-icon{color:var(--wp--custom--color--text--subtle)}.is-style-mega-menu-item-service{position:relative}.is-style-mega-menu-item-service a::after{content:"";position:absolute;inset:0;z-index:1}.is-style-mega-menu-item-service:hover,.is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--link--accent) 8%, transparent);border-left-color:var(--wp--custom--color--link--accent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent);border-left-color:var(--wp--custom--color--phase--discover)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--discover)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent);border-left-color:var(--wp--custom--color--phase--create)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--create)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent);border-left-color:var(--wp--custom--color--phase--build)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--build)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent);border-left-color:var(--wp--custom--color--phase--launch)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--launch)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent);border-left-color:var(--wp--custom--color--phase--grow)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--grow)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent);border-left-color:var(--wp--custom--color--phase--evolve)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--evolve)}@keyframes ls-accent-gradient-shift{to{background-position:var(--ls-accent-gradient-end-position) center}}.site-header__search .wp-block-search__button,.site-header .wp-block-navigation__responsive-container-close,.ls-footer-proof-card,.ls-footer-social-icon{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.site-header__search .wp-block-search__button:hover,.site-header__search .wp-block-search__button:focus-visible,.site-header .wp-block-navigation__responsive-container-close:hover,.site-header .wp-block-navigation__responsive-container-close:focus-visible{background-color:var(--wp--custom--color--surface--card-raised);border-color:var(--wp--custom--color--phase--create)}.is-style-mega-menu-item-default{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.is-style-mega-menu-item-default:hover,.is-style-mega-menu-item-default:focus-within{background-color:var(--wp--custom--color--surface--card-raised)}.is-style-mega-menu-item-service{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-left-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.site-header__search .wp-block-search__button,.site-header .wp-block-navigation__responsive-container-close,.ls-footer-proof-card,.ls-footer-social-icon,.is-style-mega-menu-item-default,.is-style-mega-menu-item-service{transition:none}}.wp-block-details{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.wp-block-details>summary{transition:inherit}.wp-block-details>summary::after{transform:translateY(-1px) rotate(45deg);transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.wp-block-details:not(.is-style-mobile-menu-accordion)>summary{padding:var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);font:inherit;font-weight:var(--wp--custom--typography--font-weight--normal)}.wp-block-details:hover:not(.is-style-mobile-menu-accordion){background:var(--wp--custom--color--surface--card-raised);border-color:var(--wp--custom--color--link--accent)}.wp-block-details[open]:not(.is-style-mobile-menu-accordion),.wp-block-details[open]:hover:not(.is-style-mobile-menu-accordion){background:var(--wp--custom--color--surface--card);border-color:var(--wp--custom--color--link--accent)}.wp-block-details:not(.is-style-mobile-menu-accordion)[open]>summary{border-bottom:1px solid var(--wp--custom--color--link--accent)}.wp-block-details[open]>summary::after{transform:translateY(1px) rotate(-135deg)}.wp-block-details:focus-within:not(.is-style-mobile-menu-accordion){border-color:var(--wp--custom--color--link--accent);box-shadow:0 0 0 2px var(--wp--custom--color--focus--ring)}.wp-element-button,.wp-block-button__link{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),outline-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),outline-offset var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),transform var(--wp--custom--animation--duration--fast) var(--wp--custom--animation--easing--standard);transition-delay:var(--wp--custom--animation--delay--enter)}.wp-element-button:hover,.wp-element-button:focus-visible,.wp-block-button__link:hover,.wp-block-button__link:focus-visible{outline-offset:0}.wp-element-button:active,.wp-block-button__link:active{transform:scale(var(--ls-button-active-scale, var(--wp--custom--animation--scale--active)))}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link{--ls-button-fill-icon-well-size: 3rem;--ls-button-fill-icon-inline-end: 0.625rem;--ls-button-fill-icon-font-size: 1.25rem;--ls-button-fill-icon-block-inset: calc( 50% - (var(--ls-button-fill-icon-well-size) / 2) );--ls-button-fill-icon-inline-start: calc( 100% - var(--ls-button-fill-icon-well-size) - var(--ls-button-fill-icon-inline-end) );background:rgba(0,0,0,0);border:0;transition:transform var(--ls-button-fill-duration, var(--wp--custom--animation--duration--emphasised)) var(--wp--custom--animation--easing--emphasised),color var(--ls-button-fill-duration, var(--wp--custom--animation--duration--emphasised)) var(--wp--custom--animation--easing--emphasised)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link::before{content:"";position:absolute;inset:0;border-radius:inherit;background:var(--ls-button-fill-background, var(--wp--custom--color--button--fill--background));transition:inset-inline-start var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-inline-end var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-block-start var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-block-end var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),border-radius var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised);z-index:var(--wp--custom--z-index--behind)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link::after{content:"\2192";position:absolute;inset-block:var(--ls-button-fill-icon-block-inset);inset-inline-end:var(--ls-button-fill-icon-inline-end);width:var(--ls-button-fill-icon-well-size);display:grid;place-items:center;color:var(--ls-button-fill-icon-colour, currentColor);font-size:var(--ls-button-fill-icon-font-size);font-weight:700;line-height:1;transition:color var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover::before{transition-delay:var(--ls-button-fill-enter-delay, var(--wp--custom--animation--delay--enter))}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover::before,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:focus-visible::before{inset-block:var(--ls-button-fill-icon-block-inset);inset-inline-end:var(--ls-button-fill-icon-inline-end);inset-inline-start:var(--ls-button-fill-icon-inline-start);border-radius:inherit}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:focus-visible{color:var(--ls-button-fill-hover-text, var(--wp--custom--color--text--default))}.wp-block-button.is-style-outline .wp-block-button__link{--ls-button-outline-icon-well-size: calc(3rem - 4px);--ls-button-outline-icon-inset: 4px;--ls-button-outline-icon-font-size: 1.25rem;--ls-button-outline-hover-padding-start: calc(4rem - 4px);--ls-button-outline-hover-padding-end: 1.5rem;--ls-button-outline-icon-inline-start: calc( 100% - var(--ls-button-outline-icon-well-size) - var(--ls-button-outline-icon-inset) );transition:padding-inline-start var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),padding-inline-end var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),transform var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--smooth)}.wp-block-button.is-style-outline .wp-block-button__link::after{content:"\2192";position:absolute;inset-block:var(--ls-button-outline-icon-inset);inset-inline-end:var(--ls-button-outline-icon-inset);width:var(--ls-button-outline-icon-well-size);display:grid;place-items:center;border-radius:var(--ls-button-outline-icon-radius, var(--wp--preset--border-radius--100));background-color:var(--ls-button-outline-accent-background, var(--wp--custom--color--button--outline--border));color:var(--ls-button-outline-accent-colour, var(--wp--custom--color--button--outline--text-hover));font-size:var(--ls-button-outline-icon-font-size);font-weight:700;line-height:1;transition:inset-inline-end var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),background-color var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard),color var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard);z-index:var(--wp--custom--z-index--content)}.wp-block-button.is-style-outline .wp-block-button__link:hover,.wp-block-button.is-style-outline .wp-block-button__link:focus-visible{padding-inline-start:var(--ls-button-outline-hover-padding-start);padding-inline-end:var(--ls-button-outline-hover-padding-end)}.wp-block-button.is-style-outline .wp-block-button__link:hover::after,.wp-block-button.is-style-outline .wp-block-button__link:focus-visible::after{inset-inline-end:var(--ls-button-outline-icon-inline-start)}.is-style-mega-menu-item-default .wp-block-outermost-icon-block,.is-style-mega-menu-item-default .wp-block-icon{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.is-style-mega-menu-item-default .wp-block-outermost-icon-block,.is-style-mega-menu-item-default .wp-block-icon{transition:none}}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.ls-mega-menu-panel-services .wp-block-column>.wp-block-group:first-child{transition:none}}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns{display:grid;grid-template-columns:repeat(2, 1fr);column-gap:var(--wp--style--block-gap);row-gap:var(--wp--preset--spacing--20)}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column{width:auto}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+4){border-bottom:1px dashed var(--wp--custom--color--border--card);padding-bottom:var(--wp--preset--spacing--20)}@media(min-width: 650px){.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns{grid-template-columns:repeat(3, 1fr)}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+4){border-bottom:none;padding-bottom:0}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+3){border-bottom:1px dashed var(--wp--custom--color--border--card);padding-bottom:var(--wp--preset--spacing--20)}}.site-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content{padding-top:var(--wp--preset--spacing--20)}.site-header .wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){background-color:var(--wp--custom--color--surface--canvas)}.site-header .wp-block-navigation:not(.has-text-color) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){color:var(--wp--custom--color--text--default)}.mobile-menu-brand .site-logo--for-light img,.mobile-menu-brand .site-logo--for-dark img{display:block;max-block-size:32px;width:auto;inline-size:auto}.mobile-menu-actions .wp-block-button{width:100%}.mobile-menu-actions .wp-block-button__link{display:block;width:100%;text-align:center}.footer-notes-badge{width:fit-content}.site-footer{position:relative;overflow:clip;background:radial-gradient(ellipse 480px 420px at 12% 0%, color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 16%, transparent) 0%, transparent 60%),radial-gradient(ellipse 460px 400px at 92% 22%, color-mix(in srgb, var(--wp--custom--color--phase--create) 12%, transparent) 0%, transparent 60%),var(--wp--custom--color--surface--canvas)}.site-footer::before{content:"";position:absolute;inset-block-start:0;inset-inline:0;block-size:4px;z-index:var(--wp--custom--z-index--content);background-image:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 24%, var(--wp--custom--color--phase--create) 50%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 76%, transparent 100%);box-shadow:0 0 16px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 70%, transparent),0 8px 32px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 30%, transparent)}.site-footer__columns{position:relative}.site-footer__columns::after{content:"";position:absolute;inset-block-start:10%;inset-inline-end:8%;inline-size:min(38vw,520px);block-size:min(30vw,360px);background:radial-gradient(ellipse closest-side, color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent) 0%, transparent 70%);filter:blur(40px);pointer-events:none;z-index:0}.site-footer__columns>*{position:relative;z-index:1}.ls-footer-nav-link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),text-decoration-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.ls-footer-nav-link a{transition:none}}@media(max-width: 781px){.footer-nav-columns{display:grid;grid-template-columns:repeat(2, 1fr);column-gap:var(--wp--preset--spacing--40);row-gap:var(--wp--preset--spacing--40)}.footer-nav-columns>.wp-block-columns{display:contents}}.site-header+*{margin-block-start:0}@media(max-width: 599.98px){.site-header__actions .wp-block-buttons{display:none}}@media(max-width: 1023.98px){.site-header .wp-block-navigation{margin-inline-start:auto}}.ls-button-cta-gradient .wp-block-button__link{transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-button-cta-gradient .wp-block-button__link:hover,.ls-button-cta-gradient .wp-block-button__link:focus-visible{transform:translateY(-1px)}.site-header__search .wp-block-search__button{display:flex;align-items:center;justify-content:center;min-inline-size:var(--wp--custom--spacing--tap-target-min);min-block-size:var(--wp--custom--spacing--tap-target-min);padding:var(--wp--preset--spacing--10);background-color:color-mix(in srgb, var(--wp--custom--color--surface--card) 4%, transparent)}.site-header__search .wp-block-search__input{inline-size:0;min-inline-size:0;padding-inline:0;border-width:0;opacity:0;transition:inline-size var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard),opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),padding-inline var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard)}.site-header__search.site-header__search--expanded .wp-block-search__input{inline-size:12rem;padding-inline:var(--wp--preset--spacing--10);border-width:1px;opacity:1}@media(prefers-reduced-motion: reduce){.ls-button-cta-gradient .wp-block-button__link,.site-header__search .wp-block-search__input{transition:none}} +.ls-footer-nav-link a{color:inherit;text-decoration-line:underline;text-decoration-color:rgba(0,0,0,0);text-underline-offset:3px}.ls-footer-nav-link a:hover,.ls-footer-nav-link a:focus-visible{color:var(--wp--custom--color--phase--create);text-decoration-color:var(--wp--custom--color--phase--create)}.ls-footer-proof-card:hover,.ls-footer-proof-card:focus-within,.ls-footer-social-icon:hover,.ls-footer-social-icon:focus-within{background-color:var(--wp--custom--color--surface--card-raised) !important;border-color:var(--wp--custom--color--phase--create) !important}.ls-footer-social-icon{min-inline-size:var(--wp--custom--spacing--tap-target-min)}.ls-footer-proof-card{height:100%}.site-logo--for-light{display:var(--wp--custom--visibility--logo-for-light)}.site-logo--for-dark{display:var(--wp--custom--visibility--logo-for-dark)}.site-logo--for-light img,.site-logo--for-dark img{display:block;width:var(--wp--custom--layout--logo-width);height:auto}@media(max-width: 1023.98px){.site-header{padding-left:var(--wp--preset--spacing--60) !important;padding-right:var(--wp--preset--spacing--60) !important}}@media(max-width: 599.98px){.site-header{padding-left:var(--wp--preset--spacing--20) !important;padding-right:var(--wp--preset--spacing--20) !important}}.ls-button-cta-gradient .wp-block-button__link:hover,.ls-button-cta-gradient .wp-block-button__link:focus-visible{box-shadow:var(--wp--custom--shadow--interactive--accent),var(--wp--custom--shadow--elevation--300)}.site-header .wp-block-navigation__responsive-container-close{position:absolute;top:var(--wp--preset--spacing--20);right:var(--wp--preset--spacing--20);display:flex;align-items:center;justify-content:center;min-inline-size:var(--wp--custom--spacing--tap-target-min);min-block-size:var(--wp--custom--spacing--tap-target-min);padding:var(--wp--preset--spacing--10);border:1px solid var(--wp--custom--color--border--card);border-radius:var(--wp--preset--border-radius--500);background-color:color-mix(in srgb, var(--wp--custom--color--surface--card) 4%, transparent)}.site-header .wp-block-navigation__responsive-container-close svg{inline-size:20px;block-size:20px}.site-header .wp-block-navigation-item{position:static}.site-header .wp-block-navigation__submenu-container{left:50%;right:auto;transform:translateX(-50%)}.site-header .wp-block-ollie-mega-menu__menu-container{background:rgba(0,0,0,0)}.ls-mega-menu-panel-services{width:fit-content;max-width:100vw;box-sizing:border-box}.ls-mega-menu-panel-services .wp-block-columns{display:grid;grid-template-columns:repeat(6, minmax(0, 1fr));gap:var(--wp--style--block-gap)}.ls-mega-menu-panel-services .wp-block-column{width:auto}.ls-mega-menu-panel-services .wp-block-column>.wp-block-group p{white-space:nowrap}.ls-simple-submenu-links>.is-style-mega-menu-item-service,.ls-services-phase-links>.is-style-mega-menu-item-service{padding:var(--wp--preset--spacing--5)}.ls-services-phase-links{display:grid;grid-template-columns:1fr;row-gap:0}.ls-services-phase-links>*{margin:0}.ls-services-phase-links{padding-left:calc(8px + var(--wp--preset--spacing--5))}.is-style-mega-menu-item-default{position:relative}.is-style-mega-menu-item-default h5 a::after{content:"";position:absolute;inset:0;z-index:1}.is-style-mega-menu-item-default:hover .wp-block-outermost-icon-block,.is-style-mega-menu-item-default:focus-within .wp-block-outermost-icon-block,.is-style-mega-menu-item-default:hover .wp-block-icon,.is-style-mega-menu-item-default:focus-within .wp-block-icon{color:var(--wp--custom--color--text--subtle)}.is-style-mega-menu-item-service{position:relative}.is-style-mega-menu-item-service a::after{content:"";position:absolute;inset:0;z-index:1}.is-style-mega-menu-item-service:hover,.is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--link--accent) 8%, transparent);border-left-color:var(--wp--custom--color--link--accent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent);border-left-color:var(--wp--custom--color--phase--discover)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--discover)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent);border-left-color:var(--wp--custom--color--phase--create)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--create)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent);border-left-color:var(--wp--custom--color--phase--build)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--build)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent);border-left-color:var(--wp--custom--color--phase--launch)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--launch)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent);border-left-color:var(--wp--custom--color--phase--grow)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--grow)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within{background-color:color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent);border-left-color:var(--wp--custom--color--phase--evolve)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover a,.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within a{color:var(--wp--custom--color--phase--evolve)}@keyframes ls-accent-gradient-shift{to{background-position:var(--ls-accent-gradient-end-position) center}}.site-header__search .wp-block-search__button,.site-header .wp-block-navigation__responsive-container-close,.ls-footer-proof-card,.ls-footer-social-icon{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.site-header__search .wp-block-search__button:hover,.site-header__search .wp-block-search__button:focus-visible,.site-header .wp-block-navigation__responsive-container-close:hover,.site-header .wp-block-navigation__responsive-container-close:focus-visible{background-color:var(--wp--custom--color--surface--card-raised);border-color:var(--wp--custom--color--phase--create)}.is-style-mega-menu-item-default{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.is-style-mega-menu-item-default:hover,.is-style-mega-menu-item-default:focus-within{background-color:var(--wp--custom--color--surface--card-raised)}.is-style-mega-menu-item-service{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-left-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.site-header__search .wp-block-search__button,.site-header .wp-block-navigation__responsive-container-close,.ls-footer-proof-card,.ls-footer-social-icon,.is-style-mega-menu-item-default,.is-style-mega-menu-item-service{transition:none}}.wp-block-details{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.wp-block-details>summary{transition:inherit}.wp-block-details>summary::after{transform:translateY(-1px) rotate(45deg);transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.wp-block-details:not(.is-style-mobile-menu-accordion)>summary{padding:var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);font:inherit;font-weight:var(--wp--custom--typography--font-weight--normal)}.wp-block-details:hover:not(.is-style-mobile-menu-accordion){background:var(--wp--custom--color--surface--card-raised);border-color:var(--wp--custom--color--link--accent)}.wp-block-details[open]:not(.is-style-mobile-menu-accordion),.wp-block-details[open]:hover:not(.is-style-mobile-menu-accordion){background:var(--wp--custom--color--surface--card);border-color:var(--wp--custom--color--link--accent)}.wp-block-details:not(.is-style-mobile-menu-accordion)[open]>summary{border-bottom:1px solid var(--wp--custom--color--link--accent)}.wp-block-details[open]>summary::after{transform:translateY(1px) rotate(-135deg)}.wp-block-details:focus-within:not(.is-style-mobile-menu-accordion){border-color:var(--wp--custom--color--link--accent);box-shadow:0 0 0 2px var(--wp--custom--color--focus--ring)}.wp-element-button,.wp-block-button__link{transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),outline-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),outline-offset var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),transform var(--wp--custom--animation--duration--fast) var(--wp--custom--animation--easing--standard);transition-delay:var(--wp--custom--animation--delay--enter)}.wp-element-button:hover,.wp-element-button:focus-visible,.wp-block-button__link:hover,.wp-block-button__link:focus-visible{outline-offset:0}.wp-element-button:active,.wp-block-button__link:active{transform:scale(var(--ls-button-active-scale, var(--wp--custom--animation--scale--active)))}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link{--ls-button-fill-icon-well-size: 3rem;--ls-button-fill-icon-inline-end: 0.625rem;--ls-button-fill-icon-font-size: 1.25rem;--ls-button-fill-icon-block-inset: calc( 50% - (var(--ls-button-fill-icon-well-size) / 2) );--ls-button-fill-icon-inline-start: calc( 100% - var(--ls-button-fill-icon-well-size) - var(--ls-button-fill-icon-inline-end) );background:rgba(0,0,0,0);border:0;transition:transform var(--ls-button-fill-duration, var(--wp--custom--animation--duration--emphasised)) var(--wp--custom--animation--easing--emphasised),color var(--ls-button-fill-duration, var(--wp--custom--animation--duration--emphasised)) var(--wp--custom--animation--easing--emphasised)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link::before{content:"";position:absolute;inset:0;border-radius:inherit;background:var(--ls-button-fill-background, var(--wp--custom--color--button--fill--background));transition:inset-inline-start var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-inline-end var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-block-start var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),inset-block-end var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised),border-radius var(--wp--custom--animation--duration--slow) var(--wp--custom--animation--easing--emphasised);z-index:var(--wp--custom--z-index--behind)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link::after{content:"\2192";position:absolute;inset-block:var(--ls-button-fill-icon-block-inset);inset-inline-end:var(--ls-button-fill-icon-inline-end);width:var(--ls-button-fill-icon-well-size);display:grid;place-items:center;color:var(--ls-button-fill-icon-colour, currentColor);font-size:var(--ls-button-fill-icon-font-size);font-weight:700;line-height:1;transition:color var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised)}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover::before{transition-delay:var(--ls-button-fill-enter-delay, var(--wp--custom--animation--delay--enter))}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover::before,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:focus-visible::before{inset-block:var(--ls-button-fill-icon-block-inset);inset-inline-end:var(--ls-button-fill-icon-inline-end);inset-inline-start:var(--ls-button-fill-icon-inline-start);border-radius:inherit}:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:hover,:is(.wp-block-button:not([class*=is-style-]):not(.ls-glass-button):not(.ls-button-cta-gradient),.wp-block-button.is-style-fill) .wp-block-button__link:focus-visible{color:var(--ls-button-fill-hover-text, var(--wp--custom--color--text--default))}.wp-block-button.is-style-outline .wp-block-button__link{--ls-button-outline-icon-well-size: calc(3rem - 4px);--ls-button-outline-icon-inset: 4px;--ls-button-outline-icon-font-size: 1.25rem;--ls-button-outline-hover-padding-start: calc(4rem - 4px);--ls-button-outline-hover-padding-end: 1.5rem;--ls-button-outline-icon-inline-start: calc( 100% - var(--ls-button-outline-icon-well-size) - var(--ls-button-outline-icon-inset) );transition:padding-inline-start var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),padding-inline-end var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),transform var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--smooth)}.wp-block-button.is-style-outline .wp-block-button__link::after{content:"\2192";position:absolute;inset-block:var(--ls-button-outline-icon-inset);inset-inline-end:var(--ls-button-outline-icon-inset);width:var(--ls-button-outline-icon-well-size);display:grid;place-items:center;border-radius:var(--ls-button-outline-icon-radius, var(--wp--preset--border-radius--100));background-color:var(--ls-button-outline-accent-background, var(--wp--custom--color--button--outline--border));color:var(--ls-button-outline-accent-colour, var(--wp--custom--color--button--outline--text-hover));font-size:var(--ls-button-outline-icon-font-size);font-weight:700;line-height:1;transition:inset-inline-end var(--wp--custom--animation--duration--emphasised) var(--wp--custom--animation--easing--emphasised),background-color var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard),color var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard);z-index:var(--wp--custom--z-index--content)}.wp-block-button.is-style-outline .wp-block-button__link:hover,.wp-block-button.is-style-outline .wp-block-button__link:focus-visible{padding-inline-start:var(--ls-button-outline-hover-padding-start);padding-inline-end:var(--ls-button-outline-hover-padding-end)}.wp-block-button.is-style-outline .wp-block-button__link:hover::after,.wp-block-button.is-style-outline .wp-block-button__link:focus-visible::after{inset-inline-end:var(--ls-button-outline-icon-inline-start)}.is-style-mega-menu-item-default .wp-block-outermost-icon-block,.is-style-mega-menu-item-default .wp-block-icon{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.is-style-mega-menu-item-default .wp-block-outermost-icon-block,.is-style-mega-menu-item-default .wp-block-icon{transition:none}}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(1)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(2)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(3)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(4)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(5)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-mega-menu-panel-services .wp-block-columns>.wp-block-column:nth-child(6)>.wp-block-group:first-child{border-radius:var(--wp--preset--border-radius--300);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.ls-mega-menu-panel-services .wp-block-column>.wp-block-group:first-child{transition:none}}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns{display:grid;grid-template-columns:repeat(2, 1fr);column-gap:var(--wp--style--block-gap);row-gap:var(--wp--preset--spacing--20)}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column{width:auto}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+4){border-bottom:1px dashed var(--wp--custom--color--border--card);padding-bottom:var(--wp--preset--spacing--20)}@media(min-width: 650px){.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns{grid-template-columns:repeat(3, 1fr)}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+4){border-bottom:none;padding-bottom:0}.mobile-menu-accordion .is-style-mega-menu-item-service.wp-block-columns>.wp-block-column:nth-child(-n+3){border-bottom:1px dashed var(--wp--custom--color--border--card);padding-bottom:var(--wp--preset--spacing--20)}}.site-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content{padding-top:var(--wp--preset--spacing--20)}.site-header .wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){background-color:var(--wp--custom--color--surface--canvas)}.site-header .wp-block-navigation:not(.has-text-color) .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay){color:var(--wp--custom--color--text--default)}.mobile-menu-brand .site-logo--for-light img,.mobile-menu-brand .site-logo--for-dark img{display:block;max-block-size:32px;width:auto;inline-size:auto}.mobile-menu-actions .wp-block-button{width:100%}.mobile-menu-actions .wp-block-button__link{display:block;width:100%;text-align:center}.footer-notes-badge{width:fit-content}.site-footer{position:relative;overflow:clip;background:radial-gradient(ellipse 480px 420px at 12% 0%, color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 16%, transparent) 0%, transparent 60%),radial-gradient(ellipse 460px 400px at 92% 22%, color-mix(in srgb, var(--wp--custom--color--phase--create) 12%, transparent) 0%, transparent 60%),var(--wp--custom--color--surface--canvas)}.site-footer::before{content:"";position:absolute;inset-block-start:0;inset-inline:0;block-size:4px;z-index:var(--wp--custom--z-index--content);background-image:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 24%, var(--wp--custom--color--phase--create) 50%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 76%, transparent 100%);box-shadow:0 0 16px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 70%, transparent),0 8px 32px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 30%, transparent)}.site-footer__columns{position:relative}.site-footer__columns::after{content:"";position:absolute;inset-block-start:10%;inset-inline-end:8%;inline-size:min(38vw,520px);block-size:min(30vw,360px);background:radial-gradient(ellipse closest-side, color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent) 0%, transparent 70%);filter:blur(40px);pointer-events:none;z-index:0}.site-footer__columns>*{position:relative;z-index:1}.ls-footer-nav-link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),text-decoration-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}@media(prefers-reduced-motion: reduce){.ls-footer-nav-link a{transition:none}}@media(max-width: 781px){.footer-nav-columns{display:grid;grid-template-columns:repeat(2, 1fr);column-gap:var(--wp--preset--spacing--40);row-gap:var(--wp--preset--spacing--40)}.footer-nav-columns>.wp-block-columns{display:contents}}.site-header+*{margin-block-start:0}@media(max-width: 599.98px){.site-header__actions .wp-block-buttons{display:none}}@media(max-width: 1023.98px){.site-header .wp-block-navigation{margin-inline-start:auto}}.ls-button-cta-gradient .wp-block-button__link{transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-button-cta-gradient .wp-block-button__link:hover,.ls-button-cta-gradient .wp-block-button__link:focus-visible{transform:translateY(-1px)}.site-header__search .wp-block-search__button{display:flex;align-items:center;justify-content:center;min-inline-size:var(--wp--custom--spacing--tap-target-min);min-block-size:var(--wp--custom--spacing--tap-target-min);padding:var(--wp--preset--spacing--10);background-color:color-mix(in srgb, var(--wp--custom--color--surface--card) 4%, transparent)}.site-header__search .wp-block-search__input{inline-size:0;min-inline-size:0;padding-inline:0;border-width:0;opacity:0;transition:inline-size var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard),opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),padding-inline var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard)}.site-header__search.site-header__search--expanded .wp-block-search__input{inline-size:12rem;padding-inline:var(--wp--preset--spacing--10);border-width:1px;opacity:1}@media(prefers-reduced-motion: reduce){.ls-button-cta-gradient .wp-block-button__link,.site-header__search .wp-block-search__input{transition:none}} diff --git a/parts/mobile-menu.html b/parts/mobile-menu.html index 8f986a50..f2c913f7 100644 --- a/parts/mobile-menu.html +++ b/parts/mobile-menu.html @@ -4,8 +4,8 @@ -
Work -
+
Work +
- - + +
-
Solutions -
+
Solutions +
- - + +
-
Services +
Services
@@ -205,19 +205,15 @@
- - + +
- - - - -
Pricing -
+
Pricing +
-
Insights -
+
Insights +
-
About -
+
About +
diff --git a/specs/001-fix-mobile-menu/checklists/requirements.md b/specs/001-fix-mobile-menu/checklists/requirements.md new file mode 100644 index 00000000..a08cf8b4 --- /dev/null +++ b/specs/001-fix-mobile-menu/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: Fix Mobile Menu — Restore Links and Remove Systems + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-09-14 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- All checklist items pass on first pass. No clarifications required — reasonable defaults documented in the Assumptions section of spec.md. +- "No implementation details" (Content Quality and Feature Readiness) is scoped to the Requirements and Success Criteria sections, which stay technology-agnostic. The Assumptions section intentionally references real file paths (e.g. `parts/mobile-menu.html`) and technical terms (CSS/markup/JS) for grounding and traceability back to the actual codebase — consistent with this project's existing spec convention — rather than describing implementation choices as requirements. diff --git a/specs/001-fix-mobile-menu/data-model.md b/specs/001-fix-mobile-menu/data-model.md new file mode 100644 index 00000000..e020fd9f --- /dev/null +++ b/specs/001-fix-mobile-menu/data-model.md @@ -0,0 +1,50 @@ +# Phase 1 Data Model: Fix Mobile Menu — Restore Links and Remove Systems + +This feature has no persisted data, database schema, or state transitions — it is a front-end +markup/CSS fix to a static WordPress template part. The "entities" below describe the structural +concepts in the markup, for traceability against the functional requirements in +[spec.md](./spec.md), not a data model in the traditional sense. + +## Mobile Menu + +**Represents**: The collapsible mobile navigation surface rendered inside WordPress core's +Navigation block responsive/overlay container. + +**Source**: `parts/mobile-menu.html` + +**Key attributes**: +- Brand row (logo) +- A sequence of top-level entries, each either: + - An accordion (`
`) containing + a page-list dropdown, or + - A standalone link row (`mobile-menu-link-row`) +- A trailing actions block (buttons: "Start a project", "Explore case studies") + +**Relationships**: Contains one or more Menu Items (below); wrapped by core's +`.wp-block-navigation__responsive-container` overlay when open. + +## Menu Item + +**Represents**: A single navigable entry, either top-level or nested inside an accordion's page list. + +**Variants present in `parts/mobile-menu.html`**: +- **Accordion group** — `
` with a `` label (Work, Solutions, Services, Pricing, + Insights, About), containing a page-list grid (`.ls-simple-submenu-links` or, for Services, phase + groups `.ls-services-phase-links`) of individual link rows styled `is-style-mega-menu-item-service`, + plus a trailing "See all …" link styled `is-style-link-arrow-accent`. +- **Standalone link row** — a bare `
`/`` accordion block, GSAP (only if the click-blocking root cause turns out to be an animation/transform state — see research.md), no new dependencies planned + +**Storage**: N/A (template part markup + compiled CSS only; no data persistence) + +**Testing**: Manual QA in a browser/device emulator, plus the existing `tests/specs/navigation.spec.ts` Playwright suite (covers mobile menu open/close and accordion toggle at 375px, but not link destinations or 320px — those stay manual); PHP lint / theme.json schema checks per AGENTS.md validation commands; `validate_blocks` tool is banned per project policy — verify block markup via direct JSON/source inspection or the Site Editor instead + +**Target Platform**: WordPress front end, mobile breakpoints (320px, 375px, and general mobile widths below the desktop nav breakpoint) + +**Project Type**: WordPress block theme (single theme codebase, no frontend/backend split) + +**Performance Goals**: No regression to existing mobile menu open/close animation performance; no additional render-blocking assets + +**Constraints**: Theme-first approach — prefer `theme.json` / `styles/**` JSON partials over hand-authored SCSS wherever a JSON-registrable hook exists (per AGENTS.md); SCSS-only for anything JSON cannot express (e.g. core-generated `.wp-block-navigation__responsive-container*` markup, which has no registrable block-style slug); no hardcoded colors — reuse existing semantic tokens; must not regress WCAG 2.2 AA tap-target sizing when reducing padding + +**Scale/Scope**: Single template part (`parts/mobile-menu.html`) plus its supporting SCSS partials (`src/scss/structural/_mobile-menu.scss`, `src/scss/animations/_mobile-menu-motion.scss`, `src/scss/animations/_details-motion.scss`, and possibly `_menu-motion.scss`/`_header-motion.scss` if the click-blocking cause lives in the shared open/close transition); no new template parts or patterns required + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +`.specify/memory/constitution.md` is still the unpopulated template (no ratified project-specific principles). This repository's actual governing document is [`AGENTS.md`](../../AGENTS.md), referenced from `CLAUDE.md`. Gates evaluated against it: + +- **Theme-first approach**: PASS (planned) — padding/styling changes will prefer `theme.json`/`styles/**` JSON partials; SCSS is reserved for the core-generated overlay markup that has no JSON-registrable slug, consistent with existing comments in `_mobile-menu-motion.scss`. +- **SCSS-only, no hand-authored plain CSS**: PASS (planned) — any new/changed rules go into the existing partials under `src/scss/`, not `assets/css/*.css` directly. +- **No hardcoded colors, semantic token reuse**: PASS (planned) — no new colors are introduced by this fix; existing spacing tokens (`--wp--preset--spacing--*`) will be reused for padding reduction. +- **PHP Minimalism / no unnecessary abstraction**: PASS (planned) — this is a targeted markup removal + CSS fix, no new PHP logic, patterns, or blocks. +- **Accessibility (WCAG 2.2 AA)**: GATE — reduced padding must not shrink tap targets below the theme's existing `--wp--custom--spacing--tap-target-min` token or equivalent; verify during implementation. +- **`validate_blocks` tool banned**: PASS (planned) — verification will use JSON/source inspection and manual Site Editor checks only, per project memory. + +No violations requiring justification. Re-checked after Phase 1 design below — still PASS, no new entities, contracts, or dependencies introduced. + +## Project Structure + +### Documentation (this feature) + +```text +specs/001-fix-mobile-menu/ +├── plan.md # This file (/speckit-plan command output) +├── research.md # Phase 0 output (/speckit-plan command) +├── data-model.md # Phase 1 output (/speckit-plan command) +├── quickstart.md # Phase 1 output (/speckit-plan command) +├── contracts/ # Phase 1 output (/speckit-plan command) — not applicable, see note below +└── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan) +``` + +### Source Code (repository root) + +This git repository's root **is** the theme root (`parts/`, `src/`, `styles/` sit directly at +the top level) — there is no nested `wp-content/themes/ls-theme/` path inside the repo itself; +that only describes where this checkout happens to sit inside a local WordPress install. + +```text +parts/ +└── mobile-menu.html # Template part: accordion labels, page-list links, + # "Systems" row (removed) +src/scss/structural/ +└── _mega-menu.scss # Single-column list layout, mobile-only padding + # override, tap-target notes (Work/Solutions/Pricing/ + # Insights/About + Services phase links) +styles/blocks/ +├── details/mobile-menu-accordion.json # Accordion label link styling/focus states +└── groups/mega-menu-item-service.json # Shared page-list-row style (mobile + desktop) +assets/css/ +└── animations.css # Compiled build output — never hand-edited +``` + +**Structure Decision**: This is a single WordPress block theme codebase (no frontend/backend split, no +new project). The fix touches one template part (`parts/mobile-menu.html`), its existing SCSS +partials, and — where a registrable block-style slug already exists (e.g. `is-style-mobile-menu-accordion`, +`is-style-mega-menu-item-service`) — the corresponding `styles/**` JSON partial, per the theme's +theme-first / JSON-first convention. No new files, directories, patterns, or dependencies are +introduced. A `contracts/` directory is not applicable — this feature exposes no API, CLI, or +service interface; the "quickstart" instead documents manual browser verification steps. + +## Complexity Tracking + +*No Constitution Check violations — this section is not applicable.* diff --git a/specs/001-fix-mobile-menu/quickstart.md b/specs/001-fix-mobile-menu/quickstart.md new file mode 100644 index 00000000..357029bc --- /dev/null +++ b/specs/001-fix-mobile-menu/quickstart.md @@ -0,0 +1,78 @@ +# Quickstart: Validate the Mobile Menu Fix (LS-3222) + +This is a manual verification guide — this feature has no API/CLI/service contract to test against +(see [plan.md](./plan.md) Project Structure note), so validation is done in a browser/device emulator +against the dev environment. + +## Prerequisites + +- Local or dev WordPress environment running `ls-theme` with the changes from this feature applied. +- A way to emulate mobile viewports: browser DevTools device toolbar (Chrome/Firefox/Safari) at + minimum 320px and 375px widths, plus one real mobile device if available. +- Browser DevTools console open to watch for JS errors. + +## Setup + +1. Check out/pull the branch for this feature (`001-fix-mobile-menu` / the Linear branch + `feature/ls-3222-fix-mobile-menu-restore-links-and-remove-systems`). +2. Ensure theme assets are built if `assets/css/*.css` is generated from `src/scss/**` + (follow the repo's existing build command — do not hand-edit `assets/css/*.css`). +3. Load the site's home page (or any page with the global header) in the browser. + +## Validation Scenarios + +### 1. Mobile menu links are clickable (FR-001, FR-002, SC-001) + +1. Resize the viewport to 375px, then 320px. +2. Open the mobile menu (hamburger/menu toggle in the header). +3. For each top-level accordion (Work, Solutions, Services, Pricing, Insights, About): + - Tap/click the row *away from the label text* (e.g. the chevron, or the row's empty + whitespace) to expand it — the label itself is now a link to that section's overview page, + so tapping the label text will navigate away instead of expanding. + - **Expected**: the dropdown expands without navigating anywhere. + - Tap/click the label text itself. + - **Expected**: navigates directly to that section's overview page (`/work/`, `/solutions/`, + `/services/`, `/pricing/`, `/blog/`, `/about/`) without needing to open the dropdown first. + - Re-open the dropdown and tap/click every link inside the page-list. + - Tap/click the trailing "See all …" link. + - **Expected**: each tap navigates to the linked page. No dead taps. +4. Tap/click the standalone "Contact" link row. + - **Expected**: navigates to `/contact/`. +5. Tap/click both action buttons ("Start a project", "Explore case studies"). + - **Expected**: each navigates correctly. + +### 2. "Systems" item is gone (FR-003, FR-007, SC-002) + +1. With the mobile menu open at 375px and 320px, scan every top-level row and every expanded + accordion's page list. + - **Expected**: no "Systems" label or `/systems/` link appears anywhere. +2. Confirm there is no empty gap, stray divider, or broken spacing where the row used to sit + (between "Services" and "Pricing"). + +### 3. No console errors (FR-005, SC-003) + +1. With DevTools console open, repeat scenario 1 (open menu, expand each accordion, tap several + links). + - **Expected**: zero new errors or warnings logged as a result of these interactions. + +### 4. Reduced, still-tappable padding (FR-004, SC-004) + +1. At 320px and 375px, expand any accordion's single-column page-list. +2. Visually compare item padding against the pre-fix baseline (or against the values recorded in + [research.md](./research.md) R3, e.g. `styles/blocks/groups/mega-menu-item-service.json`). + - **Expected**: visibly tighter padding than before, and adequate vertical spacing between + consecutive rows in the single-column list. +3. Attempt to tap each link, including links immediately above and below one another in the list. + - **Expected**: no accidental mis-taps on a neighboring link; every tap target still feels + comfortably sized (informal WCAG 2.2 AA target-size sanity check — 24×24 CSS px minimum). + +### 5. Breakpoint sanity (FR-006) + +- Repeat scenarios 1–4 at exactly 320px and exactly 375px (not just "mobile-ish" widths), since + these are the two acceptance-criteria breakpoints called out in the Linear issue. + +## Sign-off + +All five scenarios passing on dev, across both 320px and 375px, with a clean console, is the +Definition of Done bar for LS-3222's client-facing acceptance criteria. PR review and merge to +`develop` follow the repo's normal PR process (see AGENTS.md). diff --git a/specs/001-fix-mobile-menu/research.md b/specs/001-fix-mobile-menu/research.md new file mode 100644 index 00000000..3095c100 --- /dev/null +++ b/specs/001-fix-mobile-menu/research.md @@ -0,0 +1,106 @@ +# Phase 0 Research: Fix Mobile Menu — Restore Links and Remove Systems + +## R1: Root cause of unclickable mobile menu links + +**Decision**: Investigate and fix at the CSS/markup layer during implementation, prioritizing the +`.is-style-mega-menu-item-service` stretched-link overlay as the primary suspect, with a documented +fallback checklist if that's not the actual cause. + +**Rationale**: Direct source inspection surfaced a concrete, high-probability cause: + +- `src/scss/structural/_mega-menu.scss` (`.is-style-mega-menu-item-service`) implements a + "stretched-link" pattern: `position: relative` on the row (a `

`), with `a::after { content: ""; + position: absolute; inset: 0; z-index: 1; }` on the link, so the whole row (not just the text) is + tappable. This is the style class used for **every** page-list link inside the mobile menu's + Work/Solutions/Pricing/Insights/About accordions and the Services phase columns + (`styles/blocks/groups/mega-menu-item-service.json` description confirms this explicitly). +- The same file's comments record a prior, related bug: a selector that assumed a + `.wp-block-paragraph` prefix never matched real WordPress-rendered markup, silently making the + stretched-link behavior "inert" for Services rows until it was corrected. This shows the + stretched-link wiring in this exact area has broken before due to selector/specificity mismatches + between the mobile grid layout (`.ls-simple-submenu-links`, `columnCount: 2`) and this shared style. +- Because the `

`/`` accordion (`.mobile-menu-accordion.is-style-mobile-menu-accordion`) + and its motion rules (`_details-motion.scss`) apply transitions and open/close state changes, a + secondary candidate is a transform/opacity/pointer-events state left over from the open animation + (e.g., content still `pointer-events: none` or clipped by `overflow: hidden` mid- or + post-transition) — this pattern exists elsewhere in the codebase (`assets/css/gsap-animations.min.css` + uses `pointer-events: none` for pre-animation states). + +**Investigation checklist for implementation** (to run in a mobile viewport / device emulator on dev): + +1. Open the mobile menu, expand an accordion (e.g. "Work"), and inspect a link's computed styles — + confirm whether the link itself, its `::after` overlay, or an ancestor (`
` content wrapper, + `.wp-block-navigation__responsive-container-content`) has `pointer-events: none`, a `z-index` stacking + issue, or zero/negative dimensions after the open transition finishes. +2. Confirm whether the `::after` stretched-link overlay's `inset: 0` on `.is-style-mega-menu-item-service` + resolves against the intended row (`position: relative` parent = the `

`) on mobile's 2-column + grid layout, or whether grid sizing causes it to sit outside/behind the visible row. +3. Check whether the core Navigation block's `.wp-block-navigation__responsive-container` overlay + itself (or a sibling element) sits above the menu content in stacking order while `is-menu-open`, + intercepting pointer events before they reach any link. +4. Rule out a JS/GSAP timing issue (`_mobile-menu-motion.scss`, `_details-motion.scss`, + `_menu-motion.scss`) where the drawer is visually open but an animation-driven class/style hasn't + been cleared yet. + +**Alternatives considered**: +- *Rewrite the mobile menu markup/interactivity from scratch*: rejected — the structure (core + Navigation + `

` accordions + stretched-link rows) is sound and used consistently elsewhere + in the theme; a full rewrite is disproportionate to a targeted click-through bug and risks violating + the project's PHP/markup minimalism principle. +- *Add a JS click handler as a workaround*: rejected — this is a WordPress block theme following a + theme-first, JSON/CSS-driven approach (AGENTS.md); introducing new JS to patch a CSS-caused problem + would fight the existing architecture rather than fix the underlying stacking/selector issue. + +## R2: Removing the "Systems" menu item + +**Decision**: Remove the `mobile-menu-link-row` paragraph block containing the `/systems/` link +(`parts/mobile-menu.html` lines 214–216) entirely, plus its corresponding entry in the WordPress +navigation menu (Appearance → Menus) or wherever the desktop equivalent is sourced from, if one exists, +so the removal is consistent across breakpoints. + +**Rationale**: The row is a standalone `

` carries +`className: is-style-mega-menu-item-service`). Editing it here follows the theme-first approach +(AGENTS.md) instead of adding a new SCSS override, and needs no new selector or `!important` hacks. + +**Constraint carried forward**: Because this style is shared with desktop Services dropdown rows, +the padding change must be verified on both mobile and desktop to confirm it doesn't make desktop +rows feel cramped — if desktop needs to stay as-is, a mobile-only override may be needed instead +(e.g. a scoped rule in `_mobile-menu-motion.scss`/`_mega-menu.scss` for `.mobile-menu-accordion +.is-style-mega-menu-item-service`, keeping the JSON default for desktop). This decision will be +finalized once padding is visually verified in Phase 1/implementation. + +**Alternatives considered**: +- *Reduce the grid `blockGap` instead of per-item padding*: rejected as the sole fix — LS-3222 asks + specifically for padding "around" list items, and `blockGap` only affects spacing *between* items, + not the padding inside each tappable row. +- *Introduce a brand-new smaller spacing token*: rejected — the theme.json spacing scale already has + smaller presets available; adding a new token for a single use would violate the "reuse existing + tokens" convention and this project's token-reuse memory guidance. + +## Summary of unresolved items carried into implementation + +None block planning. Two items are explicitly flagged for confirmation during implementation/QA +rather than left as open specification questions: +- Final root cause of the click-through failure (R1) — the investigation checklist above will + confirm which of the candidate causes applies before a fix is written. +- Whether the padding reduction (R3) can be a single shared JSON change or needs a mobile-only + scoped override to avoid affecting desktop Services rows. diff --git a/specs/001-fix-mobile-menu/spec.md b/specs/001-fix-mobile-menu/spec.md new file mode 100644 index 00000000..89d311b3 --- /dev/null +++ b/specs/001-fix-mobile-menu/spec.md @@ -0,0 +1,103 @@ +# Feature Specification: Fix Mobile Menu — Restore Links and Remove Systems + +**Feature Branch**: `feature/ls-3222-fix-mobile-menu-restore-links-and-remove-systems` + +**Created**: 2026-09-14 + +**Status**: Draft + +**Input**: User description: "LS-3222: Fix mobile menu — restore clickable links, remove the non-functional \"Systems\" menu item, and reduce padding around page lists in the mobile dropdown menu." + +**Linear Issue**: [LS-3222](https://linear.app/lightspeedwp/issue/LS-3222/fix-mobile-menu-restore-links-and-remove-systems) + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Tap a mobile menu link to navigate (Priority: P1) + +A visitor on a mobile device opens the site's mobile menu and taps a link (e.g. Work, Blog, Contact). The link responds to the tap and navigates to the correct page. + +**Why this priority**: This is the core defect — mobile menu links are currently not clickable, which blocks all mobile navigation. Nothing else matters if visitors can't get anywhere. + +**Independent Test**: On a mobile viewport (320px/375px), open the mobile menu and tap each top-level link and each page link inside an expanded dropdown; confirm each one navigates to its expected destination with no dead taps. + +**Acceptance Scenarios**: + +1. **Given** the mobile menu is open, **When** a visitor taps a top-level menu link, **Then** the browser navigates to that link's destination. +2. **Given** a mobile dropdown (page list) is expanded, **When** a visitor taps a link inside it, **Then** the browser navigates to that link's destination. +3. **Given** the mobile menu is open, **When** a visitor taps a link, **Then** no JavaScript console errors are produced. +4. **Given** a top-level accordion row contains both a label link and a disclosure toggle, **When** a visitor activates the label (pointer tap/click, or keyboard Enter while the label has focus), **Then** the browser navigates to that label's overview page; **When** a visitor activates the row's toggle area instead (pointer tap/click elsewhere in the row, or keyboard Enter/Space while the row itself has focus), **Then** the dropdown expands or collapses without navigating away. + +--- + +### User Story 2 - Systems item no longer appears (Priority: P2) + +A visitor opens the mobile menu and no longer sees the non-functional "Systems" menu item that previously led nowhere useful. + +**Why this priority**: A dead menu item erodes trust and clutters navigation, but it's a secondary issue to the broken links in User Story 1. + +**Independent Test**: Open the mobile menu on a mobile viewport and confirm "Systems" does not appear anywhere in the menu, including inside any expanded dropdown. + +**Acceptance Scenarios**: + +1. **Given** the mobile menu is open, **When** a visitor scans the menu items, **Then** "Systems" is not present at any level. +2. **Given** the site's primary/mobile navigation menu is edited going forward, **When** the menu is rendered on mobile, **Then** no orphaned reference to the removed "Systems" item causes an error or empty entry. + +--- + +### User Story 3 - Comfortable spacing in mobile page-list dropdowns (Priority: P3) + +A visitor expands a mobile dropdown containing a list of pages and finds the list visually tight but readable, without excessive padding pushing items off-screen or requiring extra scrolling. + +**Why this priority**: A visual polish/density issue — it doesn't block navigation, but it affects usability and how much of the menu is visible at once on small screens. + +**Independent Test**: On a 320px/375px viewport, expand a mobile dropdown with a page list and confirm the padding around list items is visibly reduced compared to the current implementation, while links remain easily tappable (no overlapping tap targets). + +**Acceptance Scenarios**: + +1. **Given** a mobile dropdown page list is expanded, **When** compared to its current padding, **Then** the padding around each page-list item is reduced. +2. **Given** the reduced padding, **When** a visitor taps a page-list link, **Then** the tap target remains large enough to hit reliably (no accidental mis-taps on adjacent items). + +--- + +### Edge Cases + +- What happens when the mobile menu contains a dropdown with only one page link after "Systems" is removed — does the dropdown still render correctly (not empty, no broken toggle)? +- How does the menu behave if a visitor taps a link immediately as the mobile menu's open/close animation is still running (link should still be tappable, not blocked by a transitional overlay or pointer-events lock)? +- What happens on very small viewports (320px) where reduced padding could bring tap targets too close together? +- How does the menu render if JavaScript fails to load (menu should still allow basic keyboard/no-JS fallback navigation where feasible, or fail gracefully without leaving links inert)? + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The mobile menu MUST allow every top-level navigation link to be tapped/clicked and navigate to its target URL. +- **FR-002**: The mobile menu MUST allow every link inside an expanded dropdown (page list) to be tapped/clicked and navigate to its target URL. +- **FR-003**: The mobile menu MUST NOT contain a "Systems" menu item at any level (top-level or within a dropdown). +- **FR-004**: The mobile dropdown page lists MUST use reduced vertical/horizontal padding around each list item compared to the current implementation, while keeping tap targets large enough for reliable mobile interaction. +- **FR-005**: Interacting with the mobile menu (opening, expanding a dropdown, tapping a link) MUST NOT produce any browser console errors. +- **FR-006**: The mobile menu MUST function correctly at both 320px and 375px viewport widths. +- **FR-007**: Removing the "Systems" item MUST NOT leave an empty or broken dropdown, orphaned divider, or layout gap in its place. + +### Key Entities + +- **Mobile Menu**: The collapsible navigation surface shown on mobile breakpoints (`parts/mobile-menu.html` and any related mega/dropdown menu parts), containing top-level links and expandable page-list dropdowns. +- **Menu Item**: A single navigable entry in the mobile menu, either top-level or nested inside a dropdown page list. +- **Systems Item**: The specific non-functional menu item to be removed from the mobile menu (and its underlying menu location entry, if it originates from a WordPress nav menu assignment). + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: 100% of mobile menu links (top-level and within dropdowns) successfully navigate to their intended destination when tapped, verified at 320px and 375px. +- **SC-002**: The "Systems" item no longer appears anywhere in the mobile menu, verified by manual inspection on dev. +- **SC-003**: Zero new browser console errors are introduced by interacting with the mobile menu, verified via manual QA on dev. +- **SC-004**: Visible padding around mobile dropdown page-list items is measurably reduced from its current value, while every link's tap target meets or exceeds the WCAG 2.2 AA target-size minimum (24×24 CSS px, SC 2.5.8) — verified by measuring each link's rendered bounding box in DevTools at 320px and 375px. + +## Assumptions + +- The mobile menu markup and behavior live primarily in `parts/mobile-menu.html` (and related mega-menu parts such as `parts/solutions-mega-menu.html`), and the broken-link behavior is caused by CSS/markup/JS in the theme rather than by a third-party plugin. +- "Systems" refers to a specific existing WordPress nav menu item (or hardcoded link) that currently renders but does not lead to a working/relevant page; removing it means removing its menu entry rather than fixing its destination. +- No new navigation items or IA changes are in scope — this is a fix/cleanup of existing menu items only. +- "Reduce padding" means adjusting existing spacing tokens/values already used in the theme's CSS, not introducing a new design system or spacing scale. +- Testing is manual on the dev environment (LightSpeedWP.Agency) across mobile breakpoints. An automated suite already exists (`tests/specs/navigation.spec.ts`) covering mobile menu open/close and accordion toggle at 375px; it does not yet assert link destinations or 320px, so those remain manual-QA gaps rather than untested-by-default. +- This spec covers the mobile menu only; desktop navigation is out of scope unless the same root cause affects it. diff --git a/specs/001-fix-mobile-menu/tasks.md b/specs/001-fix-mobile-menu/tasks.md new file mode 100644 index 00000000..3d0e0af7 --- /dev/null +++ b/specs/001-fix-mobile-menu/tasks.md @@ -0,0 +1,192 @@ +--- +description: "Task list for LS-3222: Fix mobile menu — restore links and remove Systems" +--- + +# Tasks: Fix Mobile Menu — Restore Links and Remove Systems + +**Input**: Design documents from `/specs/001-fix-mobile-menu/` + +**Prerequisites**: [plan.md](./plan.md) (required), [spec.md](./spec.md) (required for user stories), [research.md](./research.md), [data-model.md](./data-model.md), [quickstart.md](./quickstart.md) + +**Tests**: No new automated tests included — the feature spec calls for manual QA as the primary method (see [quickstart.md](./quickstart.md)). An automated suite already exists (`tests/specs/navigation.spec.ts`) covering mobile menu open/close and accordion toggle at 375px; it does not assert link destinations or 320px, which remain manual-QA gaps. + +**Organization**: Tasks are grouped by user story (from spec.md) so each can be implemented and verified independently. + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (different files, no dependencies) +- **[Story]**: Which user story this task belongs to (US1, US2, US3) +- File paths are exact and relative to the repository/theme root + +## Path Conventions + +Single WordPress block theme (no frontend/backend split). This git repository's root **is** the +theme root (`parts/`, `src/`, `styles/` sit directly at the top level) — all paths below are +relative to it, per [plan.md](./plan.md) Project Structure. + +--- + +## Phase 1: Setup + +**Purpose**: Confirm the working environment is ready to observe and rebuild the affected styles. + +- [x] T001 Confirm the theme's SCSS build/watch process is running so edits to `src/scss/**` compile into `assets/css/*.css` (do not hand-edit compiled CSS, per AGENTS.md) — confirmed via `npm run build:css` (compressed/production build, matching what's committed; an earlier `build:css:dev` run was reverted because it rewrote all 24 CSS files in the expanded format, unrelated to this change) +- [x] T002 Load the site's mobile menu on dev at 375px and 320px in a browser/device emulator with DevTools console open, to capture the pre-fix baseline (current broken-link behavior, current "Systems" row, current padding) referenced throughout this task list — done on local dev (localhost:8882); baseline showed "Systems" present, links clickable via simulated clicks, no console errors + +**Checkpoint**: Baseline observed and build pipeline confirmed working before any code changes. + +--- + +## Phase 2: Foundational + +**Purpose**: No shared blocking infrastructure is required — User Stories 1, 2, and 3 touch +different, independent parts of `parts/mobile-menu.html` and independent style rules (per +[data-model.md](./data-model.md) and [research.md](./research.md)). This phase is intentionally +empty; proceed directly to Phase 3. + +**Checkpoint**: N/A — user stories may be implemented in any order or in parallel. + +--- + +## Phase 3: User Story 1 - Tap a mobile menu link to navigate (Priority: P1) 🎯 MVP + +**Goal**: Every mobile menu link (top-level and inside expanded dropdowns) is clickable/tappable and +navigates correctly, with zero console errors. + +**Independent Test**: On a 320px/375px viewport, open the mobile menu, expand each accordion, and +tap every link — each must navigate; DevTools console must show no new errors. + +### Implementation for User Story 1 + +- [x] T003 [US1] Run the R1 investigation checklist from [research.md](./research.md) on dev: inspect computed styles on a tapped-but-unresponsive link inside an expanded accordion in `parts/mobile-menu.html` — **superseded by the actual root cause**: simulated clicks on individual page-list links never reproduced a failure. The real defect was that the top-level accordion labels (Work, Solutions, Services, Pricing, Insights, About) had no link at all — the row was a plain-text native `

/` toggle, so there was no way to reach each section's overview page without opening the dropdown first. +- [x] T004 [US1] Fix the confirmed root cause — wrapped each accordion label in an `` to its overview page (`/work/`, `/solutions/`, `/services/`, `/pricing/`, `/blog/`, `/about/`) in `parts/mobile-menu.html`, with matching focus/color styling added in `styles/blocks/details/mobile-menu-accordion.json`. Clicking the label navigates directly; clicking elsewhere in the row still toggles the dropdown via native `` behavior — no JS required. +- [x] T005 [US1] Rebuild theme assets — done via `npm run build:css` after the SCSS/JSON changes across this story and User Story 3 +- [x] T006 [US1] Manually verify every link navigates correctly — verified in-browser at 375px: every accordion label, every page-list link, and every "See all …" CTA navigates to its expected destination +- [x] T007 [US1] Confirm zero new console errors — confirmed via DevTools console during all manual verification passes + +**Checkpoint**: MET. Every accordion label now links to its overview page; the dropdown toggle still works independently. See [research.md](./research.md) for the investigation trail and PR #58's review history for the follow-up tap-target fixes. + +--- + +## Phase 4: User Story 2 - Systems item no longer appears (Priority: P2) + +**Goal**: The non-functional "Systems" link row is fully removed from the mobile menu, with no +layout gap or orphaned reference left behind. + +**Independent Test**: Open the mobile menu on a mobile viewport and confirm "Systems" does not +appear anywhere, and no visual gap remains between the "Services" and "Pricing" accordions. + +### Implementation for User Story 2 + +- [x] T008 [US2] Remove the `mobile-menu-link-row` paragraph block containing the "Systems" link (`Systems`) from `parts/mobile-menu.html` (currently lines 214-216, between the "Services" and "Pricing" `
` accordions) — done +- [x] T009 [US2] Check whether a corresponding "Systems" entry exists elsewhere and remove it too, so removal is consistent — checked `parts/*.html`, `patterns/*.php`, `inc/header.php`: no other standalone "Systems" mobile-menu entry found. Note: the **desktop** nav bar (`header.php`/its pattern) has its own separate "Systems" link, out of scope per LS-3222's "mobile menu" wording — flagging so it's a conscious decision, not an oversight +- [x] T010 [US2] Manually verify on dev at 320px and 375px: "Systems" does not appear at any level of the mobile menu, and the spacing between the "Services" and "Pricing" accordions looks correct with no leftover gap or stray divider — confirmed via screenshot, no gap + +**Checkpoint**: MET for the mobile menu. Desktop nav still shows "Systems" separately — see note on T009. + +--- + +## Phase 5: User Story 3 - Comfortable spacing in mobile page-list dropdowns (Priority: P3) + +**Goal**: Page-list items inside mobile dropdown accordions have visibly reduced padding while +remaining reliably tappable. + +**Independent Test**: On a 320px/375px viewport, expand a mobile dropdown page list and confirm +padding is visibly reduced versus the pre-fix baseline (T002), with no mis-taps on adjacent items. + +### Implementation for User Story 3 + +- [x] T011 [US3] Reduce the `styles.spacing.padding` values (top/right/bottom/left, currently all `var:preset|spacing|10`) in `styles/blocks/groups/mega-menu-item-service.json` to the next-smaller existing spacing preset in the theme's scale (per research.md R3) — reduced to `var:preset|spacing|5` (the only smaller preset in `styles/presets/spacing.json`'s scale) +- [x] T012 [US3] Check the same style's effect on **desktop** Services dropdown rows, since `.is-style-mega-menu-item-service` is shared between mobile and desktop — an initial screenshot-only check missed that `parts/services-mega-menu.html` (desktop) uses the same class; PR review (CodeRabbit/Copilot) caught that the shared JSON padding change was also shrinking desktop rows. Confirmed via computed-style measurement. +- [x] T013 [US3] Mobile-only override fallback — **needed after all**: reverted `styles/blocks/groups/mega-menu-item-service.json` to its original `spacing|10` padding, and added a scoped `.ls-simple-submenu-links > .is-style-mega-menu-item-service, .ls-services-phase-links > .is-style-mega-menu-item-service` override in `_mega-menu.scss` so only mobile rows get the tighter `spacing|5`. Verified: desktop rows measure 8px padding, mobile rows measure 4px. +- [x] T014 [US3] Manually verify on dev at 320px and 375px: padding around each page-list item is visibly reduced from the T002 baseline, and every link remains tappable — confirmed via before/after screenshots (Services accordion) and a successful tap-through on "Discovery" post-change + +**Checkpoint**: MET — mobile dropdown padding is visibly reduced, desktop unaffected, links still tappable. + +--- + +## Phase 6: Polish & Cross-Cutting Concerns + +**Purpose**: Final full-scope verification across all three stories together, matching the Linear +issue's overall Definition of Done (changelog and PR steps happen after merge review, per team +convention — not included here). + +- [ ] T015 [P] Run the full quickstart.md validation guide end-to-end — scenarios 1, 2, 3, and 4 covered manually at 375px; 320px re-verification and real-device/cross-browser QA remain pending +- [x] T016 Re-check the Constitution Check gates from plan.md — no hardcoded colors, JSON-first approach preferred where possible (padding/font-size changes), SCSS used only where a real JSON limitation exists (single-column layout support, the accordion-label link's stretched-link interaction, the mobile-only padding scope); no new files/dependencies. Tap-target sizing was re-measured after PR review: the `a::after` 44px expansion was removed once measurement showed it overlapped adjacent rows and the accordion summary at 0px row-gap — each row's own ~29px box still clears the real WCAG 2.2 AA minimum (24×24px, SC 2.5.8). +- [x] T017 Review the full diff for unrelated/accidental changes — final diff spans `parts/mobile-menu.html` (Systems removal + accordion label links), `src/scss/structural/_mega-menu.scss` (single-column layout, tap-target notes, mobile-only padding scope), `styles/blocks/details/mobile-menu-accordion.json` (label link styling), `styles/blocks/groups/mega-menu-item-service.json` (padding, kept desktop-safe), and the compiled `assets/css/animations.css` — no unrelated files. (An earlier `npm run build:css:dev` run had briefly rewritten all 24 compiled CSS files in the wrong dev format; caught and reverted with the correct `npm run build:css` before this was ever committed.) + +**Checkpoint**: MET for all three user stories. "Systems" is removed, accordion labels link to their overview pages, and padding is reduced (mobile-only, desktop unaffected). Remaining open items are QA breadth (320px re-verification, cross-browser/device testing), not unresolved functionality. + +--- + +## Dependencies & Execution Order + +### Phase Dependencies + +- **Setup (Phase 1)**: No dependencies — start immediately. +- **Foundational (Phase 2)**: Empty — does not block anything. +- **User Stories (Phase 3-5)**: All depend only on Phase 1 (baseline observed). They touch + independent files/rules (per data-model.md) and may proceed in parallel or in priority order + (P1 → P2 → P3). +- **Polish (Phase 6)**: Depends on all three user stories being complete. + +### User Story Dependencies + +- **User Story 1 (P1)**: No dependency on US2/US3. This is the MVP — LS-3222's core defect. +- **User Story 2 (P2)**: No dependency on US1/US3. Pure removal, independently testable. +- **User Story 3 (P3)**: No dependency on US1/US2. Independently testable, but its T012 check + (desktop impact) should happen after its own T011, not gated on the other stories. + +### Parallel Opportunities + +- T001 and T002 (Setup) can run together. +- Once Phase 1 is done, US1 (T003-T007), US2 (T008-T010), and US3 (T011-T014) can be worked on in + parallel by different people, or sequentially in priority order by one person — they do not + conflict on the same lines of `parts/mobile-menu.html` (US1's fix is CSS-only; US2 removes a + specific unrelated block; US3 edits a separate JSON file). +- T015 [P] can run alongside T016/T017 since it's a read-only verification pass. + +--- + +## Parallel Example: All Three Stories After Setup + +```bash +# After T001-T002 (Setup) complete, these can be worked on in parallel: +Task: "US1 — investigate and fix unclickable links (T003-T007)" +Task: "US2 — remove the Systems link row (T008-T010)" +Task: "US3 — reduce page-list padding (T011-T014)" +``` + +--- + +## Implementation Strategy + +### MVP First (User Story 1 Only) + +1. Complete Phase 1: Setup. +2. Complete Phase 3: User Story 1 (T003-T007) — this alone fixes LS-3222's headline defect + (unclickable links). +3. **STOP and VALIDATE**: Confirm every link works and the console is clean. +4. Ship if the "Systems" removal and padding work need to land separately; otherwise continue. + +### Incremental Delivery + +1. Setup → baseline captured. +2. User Story 1 → verify independently → this is the MVP. +3. User Story 2 → verify independently. +4. User Story 3 → verify independently, watching for desktop side effects (T012/T013). +5. Polish (Phase 6) → full quickstart run, Constitution re-check, diff review → open PR. + +--- + +## Notes + +- No new test tasks are included — an automated suite already exists (`tests/specs/navigation.spec.ts`) + but only covers menu open/close and accordion toggle at 375px, not link destinations or 320px; the + spec treats those gaps as manual QA (documented honestly per this project's PR test-plan convention: + only check off what was actually run). +- Changelog entry and PR creation happen after this task list is complete, per team convention — + not tracked here. +- `validate_blocks` must not be used to verify block markup changes (T008) — use direct JSON/source + inspection or the Site Editor instead, per project policy. +- Commit after each user story phase, or after each task if preferred. diff --git a/src/scss/structural/_mega-menu.scss b/src/scss/structural/_mega-menu.scss index 4b6bc6e4..43004dac 100644 --- a/src/scss/structural/_mega-menu.scss +++ b/src/scss/structural/_mega-menu.scss @@ -85,73 +85,81 @@ /* * .ls-simple-submenu-links (Work/Solutions/Pricing/Insights/About accordions, - * parts/mobile-menu.html): a plain 2-column grid of is-style-mega-menu-item-service links. This - * block now uses WordPress core's own "layout":{"type":"grid","columnCount":2} block support - * (with "style":{"spacing":{"blockGap":...}} for the gap) instead of hand-authored CSS — core - * generates equal-fraction grid-template-columns itself, which is exactly right here since every - * label in these five menus is a full phrase ("Travel publisher rebuild") that fills its column. - * .ls-simple-submenu-links itself is now just a stable hook for the narrow-screen fallback below; - * it carries no grid CSS of its own. - * - * JSON limitation: core's grid layout support has no per-instance container class name (WordPress - * generates a numbered wp-container-core-group-is-layout-* class per render), so there's no stable - * selector to target for the narrow-screen single-column override — .ls-simple-submenu-links (this - * theme's own class, always present) is what the media query below hooks into instead. Doubled to - * bump specificity above core's single-class per-instance rule, which the source-order between the - * two stylesheets otherwise leaves ambiguous. + * parts/mobile-menu.html): a single-column list of is-style-mega-menu-item-service links, one page + * link per row for a clear top-to-bottom mobile reading order (LS-3222). Uses WordPress core's own + * "layout":{"type":"grid","columnCount":1} block support (with "style":{"spacing":{"blockGap":...}} + * for the tightened row gap) instead of hand-authored CSS. Previously a 2-column grid with a + * narrow-screen (<340px) single-column fallback; the fallback is now redundant since columnCount:1 + * is the default at every width these mobile-only rows ever render at. */ -@media (max-width: 340px) { - .ls-simple-submenu-links.ls-simple-submenu-links { - grid-template-columns: 1fr; - } + +/* + * Mobile-only padding override, scoped to these two mobile-only parents (LS-3222 follow-up): + * is-style-mega-menu-item-service (styles/blocks/groups/mega-menu-item-service.json) is shared with + * the desktop Services mega-menu (parts/services-mega-menu.html), which must keep its own existing + * spacing|10 padding — confirmed on the PR that an earlier version of this fix changed the shared + * JSON partial's default directly, which also shrank desktop row padding. Overriding it here instead + * (higher specificity than the shared style's own class-level rule) keeps the tighter spacing|5 + * padding mobile-only. + */ +.ls-simple-submenu-links > .is-style-mega-menu-item-service, +.ls-services-phase-links > .is-style-mega-menu-item-service { + padding: var(--wp--preset--spacing--5); } /* - * .ls-services-phase-links (mobile Services accordion, parts/mobile-menu.html): stays a hand- - * authored CSS grid rather than core's native "layout":{"type":"grid"} block support. - * - * JSON limitation, confirmed empirically 2026-08-25 (wp_get_layout_style()): core's grid layout - * support only ever emits equal-fraction columns (`grid-template-columns:repeat(N, minmax(0, 1fr))`) - * — there is no block attribute for content-sized columns. This class's links are short single - * words (Content, Design, Hosting), and equal-fraction columns leave each one stranded at the left - * edge of its own half-width track — the exact bug fixed earlier in this branch by sizing columns - * to content (max-content) instead. Switching this class to core's grid support would silently - * reintroduce that already-fixed, already-tested bug, so it stays custom CSS here. - * - * The wrapping block's own "layout":{"type":"default"} makes WordPress inject its usual - * consecutive-sibling margin-top onto the child paragraphs (core's blockGap fallback for the - * "default" layout type assumes vertical stacking) — that's what staggered column 2 lower than - * column 1, since the margin only applies from the second paragraph onward regardless of which - * grid cell it lands in. Reset to margin:0 here rather than switching the block's own layout type, - * since "default" is what makes this a plain wp:group with no block-supports layout UI fighting - * the grid. + * Tap-target note for the single-column list above: an earlier version of this rule expanded each + * row's invisible tap area to the theme's 44px tap-target-min via the stretched-link `a::after` + * (`calc((min - 100%) / -2)`, expanding top/bottom beyond the row's own ~29px visual box). With + * `blockGap: 0` between rows (by design, for a tight list), that expansion mathematically cannot + * avoid overlapping the neighbouring row's own expanded area — CodeRabbit and Copilot both flagged + * this independently on the PR, and the overlap math checks out: ~7.5px of expansion per side on + * 0px-separated rows overlaps by ~15px, so a tap near a row boundary can resolve to the wrong link. + * The same expansion also bled ~2-3px into the accordion `` above the first row, since the + * heading-to-first-link gap (spacing--5, ~4-5px) is smaller than the expansion itself. + * Removed rather than re-tuned: reintroducing a non-overlapping expansion would require restoring a + * real visible gap between rows (≥ 44px − row height, i.e. ~15px) to leave room for it, undoing the + * tight single-column spacing this list was specifically built for. Each row's own box (~29px) still + * clears the actual WCAG 2.2 AA target-size minimum (24×24px, SC 2.5.8) without any expansion. + */ + +/* + * .ls-services-phase-links (mobile Services accordion, parts/mobile-menu.html): single-column list + * of service links per lifecycle phase (LS-3222 follow-up), matching the Work/Solutions/Pricing/ + * Insights/About dropdowns' single-column reading order. Previously a 2-column + * `repeat(2, max-content)` grid with a <340px single-column fallback; now always one column, so the + * fallback is redundant and removed. Stays hand-authored CSS rather than core's native + * "layout":{"type":"grid"} block support for the same reason as before: this wrapping block's own + * "layout":{"type":"default"} injects consecutive-sibling margin-top on the child paragraphs (core's + * blockGap fallback for the "default" layout type), which the `> * { margin: 0; }` reset below + * still needs to cancel regardless of column count. */ .ls-services-phase-links { display: grid; - grid-template-columns: repeat(2, max-content); - column-gap: var(--wp--preset--spacing--20); - row-gap: var(--wp--preset--spacing--20); + grid-template-columns: 1fr; + row-gap: 0; > * { margin: 0; } } -@media (max-width: 340px) { - .ls-services-phase-links { - grid-template-columns: 1fr; - } -} +/* + * Tap-target note: same as .ls-simple-submenu-links above — an earlier version of this rule + * expanded each row's tap area to 44px via `a::after`, which overlapped adjacent rows given + * `row-gap: 0`. Removed for the same reason; each row's own ~29px box still clears the real WCAG + * 2.2 AA target-size minimum (24×24px, SC 2.5.8). + */ /* * .ls-services-phase-links only: indents under its lifecycle heading rather than the leading dot. - * 8px is the icon width (matching width="8px" on the phase dot in the markup); spacing--10 is the - * header row's own icon-to-label gap — not an arbitrary offset, the two real values that produce - * alignment with the "C" in "CREATE". .ls-simple-submenu-links has no heading/dot to align under, - * so it doesn't need this. + * 8px is the icon width (matching width="8px" on the phase dot in the markup); spacing--5 is the + * reduced offset (LS-3222 follow-up, down from spacing--10) that keeps the indent intentional + * (~12-13px total) without reading as an oversized, disconnected offset from the "C" in "CREATE". + * .ls-simple-submenu-links has no heading/dot to align under, so it doesn't need this. */ .ls-services-phase-links { - padding-left: calc(8px + var(--wp--preset--spacing--10)); + padding-left: calc(8px + var(--wp--preset--spacing--5)); } /* diff --git a/styles/blocks/details/mobile-menu-accordion.json b/styles/blocks/details/mobile-menu-accordion.json index c7e66168..f11e2a19 100644 --- a/styles/blocks/details/mobile-menu-accordion.json +++ b/styles/blocks/details/mobile-menu-accordion.json @@ -6,7 +6,7 @@ "blockTypes": [ "core/details" ], - "description": "Flattens the default core/details card (styles/presets/blocks/core-details.json) into a full-width divider row for the mobile navigation drawer (parts/mobile-menu.html). The :hover/[open]/:focus-within overrides use a doubled &.wp-block-details compound selector so they reliably out-specify the global .wp-block-details:hover/[open]/:focus-within rules in src/scss/animations/_details-motion.scss regardless of stylesheet load order — both would otherwise compile to the same specificity. Each state is its own separate rule rather than a comma-separated list — confirmed empirically (2026-08-11) that the styles/**.json css field mishandles multiple &-prefixed selectors joined by commas. The margin-top:0 override cancels the wrapping .mobile-menu group's default flow blockGap (which lands entirely as margin-top on every row but never as margin-bottom) — left in place, that external gap stacks on top of the summary's own centered padding, so the text sits visibly closer to its own bottom divider than to the previous row's. Zeroing it makes each row's divider-to-divider span exactly the summary's own centered box, so the text reads as centered between dividers, not just within its own row.", + "description": "Flattens the default core/details card (styles/presets/blocks/core-details.json) into a full-width divider row for the mobile navigation drawer (parts/mobile-menu.html). The :hover/[open]/:focus-within overrides use a doubled &.wp-block-details compound selector so they reliably out-specify the global .wp-block-details:hover/[open]/:focus-within rules in src/scss/animations/_details-motion.scss regardless of stylesheet load order — both would otherwise compile to the same specificity. Each state is its own separate rule rather than a comma-separated list — confirmed empirically (2026-08-11) that the styles/**.json css field mishandles multiple &-prefixed selectors joined by commas. The margin-top:0 override cancels the wrapping .mobile-menu group's default flow blockGap (which lands entirely as margin-top on every row but never as margin-bottom) — left in place, that external gap stacks on top of the summary's own centered padding, so the text sits visibly closer to its own bottom divider than to the previous row's. Zeroing it makes each row's divider-to-divider span exactly the summary's own centered box, so the text reads as centered between dividers, not just within its own row. The `:first-of-type` padding-top override (LS-3222) tightens the gap between the summary and the first content row from the base card's spacing|20 down to spacing|5 — a real (non-:where()) selector so it outweighs the base partial's zero-specificity :where() rule regardless of stylesheet order. The `summary > a` rules (LS-3222) support wrapping each top-level label (Work, Solutions, Services, Pricing, Insights, About) in a real link to that section's overview page: clicking the label navigates there via the anchor's own native default action, while a click anywhere else in the row (the chevron, the row's own whitespace) still triggers the native `` toggle — both are independent native browser behaviors on the same click event, so no JS is needed to split them. The link needs its own color/underline reset (it would otherwise inherit the global `elements.link` accent color from styles/presets/links.json) and its own `:focus-visible` outline, since it is a second, independently focusable/tabbable control nested inside the already-focusable ``.", "styles": { "border": { "top": { "style": "none", "width": "0px" }, @@ -18,6 +18,6 @@ "color": { "background": "transparent" }, - "css": "&.wp-block-details {\n\tmargin-top: 0;\n}\n\n&.wp-block-details:hover {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open] {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open]:hover {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details:focus-within {\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open] > summary {\n\tborder-bottom-color: var(--wp--custom--color--border--card);\n}\n\n& > summary {\n\tmin-block-size: 3rem;\n\tpadding-inline: 0 var(--wp--preset--spacing--10);\n\tpadding-block: 0;\n\tfont-family: var(--wp--preset--font-family--body);\n\tfont-size: var(--wp--preset--font-size--200);\n\tfont-weight: var(--wp--custom--typography--font-weight--semibold);\n\tline-height: 1;\n}\n\n& > summary:focus-visible {\n\toutline: 2px solid var(--wp--custom--color--focus--ring);\n\toutline-offset: -2px;\n}\n\n& > :where(:not(summary)) {\n\tpadding-inline: 0;\n}" + "css": "&.wp-block-details {\n\tmargin-top: 0;\n}\n\n&.wp-block-details:hover {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open] {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open]:hover {\n\tbackground: none;\n\tborder-color: var(--wp--custom--color--border--card);\n\tbox-shadow: none;\n}\n\n&.wp-block-details:focus-within {\n\tbox-shadow: none;\n}\n\n&.wp-block-details[open] > summary {\n\tborder-bottom-color: var(--wp--custom--color--border--card);\n}\n\n& > summary {\n\tmin-block-size: 3rem;\n\tpadding-inline: 0 var(--wp--preset--spacing--10);\n\tpadding-block: 0;\n\tfont-family: var(--wp--preset--font-family--body);\n\tfont-size: var(--wp--preset--font-size--200);\n\tfont-weight: var(--wp--custom--typography--font-weight--semibold);\n\tline-height: 1;\n}\n\n& > summary:focus-visible {\n\toutline: 2px solid var(--wp--custom--color--focus--ring);\n\toutline-offset: -2px;\n}\n\n& > summary > a {\n\tcolor: inherit;\n\ttext-decoration: none;\n}\n\n& > summary > a:focus-visible {\n\toutline: 2px solid var(--wp--custom--color--focus--ring);\n\toutline-offset: 2px;\n}\n\n& > :where(:not(summary)) {\n\tpadding-inline: 0;\n}\n\n& > :not(summary):first-of-type {\n\tpadding-top: var(--wp--preset--spacing--5);\n}" } } diff --git a/styles/blocks/groups/mega-menu-item-service.json b/styles/blocks/groups/mega-menu-item-service.json index 777803f2..e8000ca6 100644 --- a/styles/blocks/groups/mega-menu-item-service.json +++ b/styles/blocks/groups/mega-menu-item-service.json @@ -6,7 +6,7 @@ "blockTypes": [ "core/paragraph" ], - "description": "List row for the Service mega menu's phase columns, and every plain link row across the mobile menu's Work/Solutions/Pricing/Insights/About accordions (parts/mobile-menu.html) — this is the single shared row style behind both, not a Services-only style. Rows are direct children of a 2-column grid (.ls-services-phase-links or .ls-simple-submenu-links, src/scss/structural/_mega-menu.scss) with no divider of their own — a transparent left rail reserves space for the accent bar that src/scss/animations/_shared-hover.scss and _menu-motion.scss paint in on hover/focus. The stretched-link hit area itself lives in _mega-menu.scss (a::after) — this file only adds the :focus-visible outline, which that SCSS rule doesn't cover.", + "description": "Shared row style for service mega-menu and mobile menu links.", "styles": { "border": { "radius": "var:preset|border-radius|300",