diff --git a/assets/css/animations.css b/assets/css/animations.css
index 920b401..91e5940 100644
--- a/assets/css/animations.css
+++ b/assets/css/animations.css
@@ -1009,6 +1009,46 @@
color: var(--ls-button-arrow-compact-colour-active);
}
+/* CTA fill buttons on fixed-dark cards: pin text/icon colour to the on-dark
+ tokens so the fill button's rest and hover states stay readable regardless
+ of the site's light/dark style variation. The background is also pinned to
+ a dark surface token — text--on-dark against the default brand/cta fill
+ colour fails WCAG contrast (~1.2:1) once the fill fully reveals on hover. */
+.ls-cta-band__button .wp-block-button__link,
+.ls-cta-inline__button .wp-block-button__link,
+.ls-cta-reassurance__button .wp-block-button__link {
+ color: var(--wp--custom--color--text--on-dark);
+ --ls-button-fill-background: var(--wp--custom--color--surface--on-dark-card);
+ --ls-button-fill-icon-colour: var(--wp--custom--color--text--on-dark);
+ --ls-button-fill-hover-text: var(--wp--custom--color--text--on-dark);
+}
+
+/* CTA band: the secondary link's arrow colours aren't part of the group's
+ saved block attributes, so they're defined here instead of as an inline
+ style on the wrapper (which would be dropped on save). */
+.ls-cta-band__actions {
+ --ls-link-arrow-colour: var(--wp--custom--color--text--on-dark);
+ --ls-link-arrow-hover-colour: var(--wp--custom--color--text--brand);
+}
+
+/* CTA strip: the arrow sits next to the glass button rather than nested in
+ its rich text, so it needs its own hover contract tied to the shared
+ wrapper. */
+.ls-cta-strip__cta-wrap {
+ position: relative;
+ display: inline-flex;
+}
+
+.ls-cta-strip__button-arrow {
+ transition: transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised), color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard);
+}
+
+.ls-cta-strip__cta-wrap:hover .ls-cta-strip__button-arrow,
+.ls-cta-strip__cta-wrap:focus-within .ls-cta-strip__button-arrow {
+ color: var(--wp--custom--color--text--brand);
+ transform: translateX(0.25em);
+}
+
@media (prefers-reduced-motion: reduce) {
.wp-block-button.is-style-glass-button .wp-block-button__link,
.wp-block-button.is-style-button-arrow-compact .wp-block-button__link,
@@ -1027,4 +1067,11 @@
:is(.is-style-card-solutions, .is-style-card-solutions-accent):focus-within .wp-block-button.is-style-button-arrow-compact .wp-block-button__link::after {
transform: none;
}
+ .ls-cta-strip__button-arrow {
+ transition: none;
+ }
+ .ls-cta-strip__cta-wrap:hover .ls-cta-strip__button-arrow,
+ .ls-cta-strip__cta-wrap:focus-within .ls-cta-strip__button-arrow {
+ transform: none;
+ }
}
diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg
new file mode 100644
index 0000000..0afaf5d
--- /dev/null
+++ b/assets/icons/arrow-right.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/chat.svg b/assets/icons/chat.svg
new file mode 100644
index 0000000..99a8020
--- /dev/null
+++ b/assets/icons/chat.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/check.svg b/assets/icons/check.svg
new file mode 100644
index 0000000..63acbf0
--- /dev/null
+++ b/assets/icons/check.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/phone.svg b/assets/icons/phone.svg
new file mode 100644
index 0000000..6d1ebe5
--- /dev/null
+++ b/assets/icons/phone.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/star.svg b/assets/icons/star.svg
new file mode 100644
index 0000000..e544193
--- /dev/null
+++ b/assets/icons/star.svg
@@ -0,0 +1 @@
+
diff --git a/patterns/cta/cta-consultation-band.php b/patterns/cta/cta-consultation-band.php
new file mode 100644
index 0000000..6a6da61
--- /dev/null
+++ b/patterns/cta/cta-consultation-band.php
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/patterns/cta/cta-consultation-inline.php b/patterns/cta/cta-consultation-inline.php
new file mode 100644
index 0000000..d4b733f
--- /dev/null
+++ b/patterns/cta/cta-consultation-inline.php
@@ -0,0 +1,57 @@
+
+
+
+
diff --git a/patterns/cta/cta-consultation-reassurance.php b/patterns/cta/cta-consultation-reassurance.php
new file mode 100644
index 0000000..b130c3f
--- /dev/null
+++ b/patterns/cta/cta-consultation-reassurance.php
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/patterns/cta/cta-consultation-strip.php b/patterns/cta/cta-consultation-strip.php
new file mode 100644
index 0000000..bff01f9
--- /dev/null
+++ b/patterns/cta/cta-consultation-strip.php
@@ -0,0 +1,61 @@
+
+
+
+
diff --git a/src/scss/animations/_button-motion.scss b/src/scss/animations/_button-motion.scss
index 8ea4a28..2db0818 100644
--- a/src/scss/animations/_button-motion.scss
+++ b/src/scss/animations/_button-motion.scss
@@ -486,6 +486,50 @@
}
}
+/* CTA fill buttons on fixed-dark cards: pin text/icon colour to the on-dark
+ tokens so the fill button's rest and hover states stay readable regardless
+ of the site's light/dark style variation. The background is also pinned to
+ a dark surface token — text--on-dark against the default brand/cta fill
+ colour fails WCAG contrast (~1.2:1) once the fill fully reveals on hover. */
+.ls-cta-band__button .wp-block-button__link,
+.ls-cta-inline__button .wp-block-button__link,
+.ls-cta-reassurance__button .wp-block-button__link {
+ color: var(--wp--custom--color--text--on-dark);
+ --ls-button-fill-background: var(--wp--custom--color--surface--on-dark-card);
+ --ls-button-fill-icon-colour: var(--wp--custom--color--text--on-dark);
+ --ls-button-fill-hover-text: var(--wp--custom--color--text--on-dark);
+}
+
+/* CTA band: the secondary link's arrow colours aren't part of the group's
+ saved block attributes, so they're defined here instead of as an inline
+ style on the wrapper (which would be dropped on save). */
+.ls-cta-band__actions {
+ --ls-link-arrow-colour: var(--wp--custom--color--text--on-dark);
+ --ls-link-arrow-hover-colour: var(--wp--custom--color--text--brand);
+}
+
+/* CTA strip: the arrow sits next to the glass button rather than nested in
+ its rich text, so it needs its own hover contract tied to the shared
+ wrapper. */
+.ls-cta-strip__cta-wrap {
+ position: relative;
+ display: inline-flex;
+}
+
+.ls-cta-strip__button-arrow {
+ transition:
+ transform var(--wp--custom--animation--duration--base)
+ var(--wp--custom--animation--easing--emphasised),
+ color var(--wp--custom--animation--duration--base)
+ var(--wp--custom--animation--easing--standard);
+}
+
+.ls-cta-strip__cta-wrap:hover .ls-cta-strip__button-arrow,
+.ls-cta-strip__cta-wrap:focus-within .ls-cta-strip__button-arrow {
+ color: var(--wp--custom--color--text--brand);
+ transform: translateX(0.25em);
+}
+
@include motion.reduced-motion {
.wp-block-button.is-style-glass-button .wp-block-button__link,
.wp-block-button.is-style-button-arrow-compact .wp-block-button__link,
@@ -510,4 +554,13 @@
.wp-block-button__link::after {
transform: none;
}
+
+ .ls-cta-strip__button-arrow {
+ transition: none;
+ }
+
+ .ls-cta-strip__cta-wrap:hover .ls-cta-strip__button-arrow,
+ .ls-cta-strip__cta-wrap:focus-within .ls-cta-strip__button-arrow {
+ transform: none;
+ }
}
\ No newline at end of file
diff --git a/styles/dark.json b/styles/dark.json
index 9a14f5b..d64a5c3 100644
--- a/styles/dark.json
+++ b/styles/dark.json
@@ -10,7 +10,9 @@
"inverse": "var:preset|color|contrast",
"muted": "var:preset|color|neutral-400",
"brand": "var:preset|color|cta-500",
- "brand-strong": "var:preset|color|cta-200"
+ "brand-strong": "var:preset|color|cta-200",
+ "on-dark": "var:preset|color|base",
+ "on-dark-muted": "var:preset|color|neutral-400"
},
"surface": {
"canvas": "var:preset|color|contrast",
@@ -23,7 +25,10 @@
"cta": "var:preset|color|accent-two-500",
"accent-light": "var:preset|color|accent-900",
"accent-two-light": "var:preset|color|accent-two-900",
- "accent-three-light": "var:preset|color|accent-three-900"
+ "accent-three-light": "var:preset|color|accent-three-900",
+ "band-start": "var:preset|color|surface-700",
+ "band-end": "var:preset|color|surface-300",
+ "on-dark-card": "var:preset|color|surface-200"
},
"border": {
"card": "var:preset|color|neutral-800"
diff --git a/styles/sections/cta/cta-inline-card.json b/styles/sections/cta/cta-inline-card.json
new file mode 100644
index 0000000..4c01dc5
--- /dev/null
+++ b/styles/sections/cta/cta-inline-card.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "https://schemas.wp.org/wp/6.9/theme.json",
+ "version": 3,
+ "title": "CTA Inline Card",
+ "slug": "cta-inline-card",
+ "blockTypes": [
+ "core/group"
+ ],
+ "description": "Fixed-dark inline CTA card shell with an accent left border, for compact consultation callouts.",
+ "styles": {
+ "color": {
+ "background": "var:custom|color|surface|on-dark-card",
+ "text": "var:custom|color|text|on-dark"
+ },
+ "border": {
+ "color": "var:custom|color|text|brand",
+ "radius": "var:preset|border-radius|200",
+ "style": "solid",
+ "width": "1px"
+ },
+ "spacing": {
+ "blockGap": "var:preset|spacing|20",
+ "padding": {
+ "top": "var:preset|spacing|30",
+ "right": "var:preset|spacing|30",
+ "bottom": "var:preset|spacing|30",
+ "left": "var:preset|spacing|30"
+ }
+ },
+ "css": "&{border-left-width:3px;}"
+ }
+}
diff --git a/theme.json b/theme.json
index f89a1da..0a91e65 100644
--- a/theme.json
+++ b/theme.json
@@ -416,7 +416,9 @@
"inverse": "var:preset|color|base",
"muted": "var:preset|color|neutral-700",
"brand": "var:preset|color|brand-500",
- "brand-strong": "var:preset|color|brand-800"
+ "brand-strong": "var:preset|color|brand-800",
+ "on-dark": "var:preset|color|base",
+ "on-dark-muted": "var:preset|color|neutral-400"
},
"surface": {
"canvas": "var:preset|color|base",
@@ -429,7 +431,10 @@
"cta": "var:preset|color|cta-500",
"accent-light": "var:preset|color|accent-100",
"accent-two-light": "var:preset|color|accent-two-100",
- "accent-three-light": "var:preset|color|accent-three-100"
+ "accent-three-light": "var:preset|color|accent-three-100",
+ "band-start": "var:preset|color|surface-700",
+ "band-end": "var:preset|color|surface-300",
+ "on-dark-card": "var:preset|color|surface-200"
},
"border": {
"card": "var:preset|color|neutral-300"