Configularity — Configuration tools for bespoke businesses

/* base.css */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
-moz-text-size-adjust: none;
-webkit-text-size-adjust: none;
text-size-adjust: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
hanging-punctuation: first last;
scroll-padding-top: var(–space-16);
}

body {
min-height: 100dvh;
line-height: 1.6;
font-family: var(–font-body, sans-serif);
font-size: var(–text-base);
color: var(–color-text);
background-color: var(–color-bg);
}

img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }
ul[role=”list”], ol[role=”list”] { list-style: none; }
input, button, textarea, select { font: inherit; color: inherit; }

h1, h2, h3, h4, h5, h6 { text-wrap: balance; line-height: 1.15; }
p, li, figcaption { text-wrap: pretty; max-width: 72ch; }

::selection {
background: oklch(from var(–color-primary) l c h / 0.25);
color: var(–color-text);
}

:focus-visible {
outline: 2px solid var(–color-primary);
outline-offset: 3px;
border-radius: var(–radius-sm);
}

@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

button { cursor: pointer; background: none; border: none; }
table { border-collapse: collapse; width: 100%; }

a, button, [role=”button”], [role=”link”], input, textarea, select {
transition: color var(–transition-interactive),
background var(–transition-interactive),
border-color var(–transition-interactive),
box-shadow var(–transition-interactive);
}

.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;
}

/* =============================================
CONFIGULARITY — Design Tokens & Styles
============================================= */

:root {
/* Type Scale */
–text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
–text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
–text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
–text-lg: clamp(1.125rem, 1rem + 0.75vw, 1.5rem);
–text-xl: clamp(1.5rem, 1.2rem + 1.25vw, 2.25rem);
–text-2xl: clamp(2rem, 1.2rem + 2.5vw, 3.5rem);
–text-3xl: clamp(2.5rem, 1rem + 4vw, 5rem);

/* Spacing */
–space-1: 0.25rem;
–space-2: 0.5rem;
–space-3: 0.75rem;
–space-4: 1rem;
–space-5: 1.25rem;
–space-6: 1.5rem;
–space-8: 2rem;
–space-10: 2.5rem;
–space-12: 3rem;
–space-16: 4rem;
–space-20: 5rem;
–space-24: 6rem;
–space-32: 8rem;

/* Radius */
–radius-sm: 0.375rem;
–radius-md: 0.5rem;
–radius-lg: 0.75rem;
–radius-xl: 1rem;
–radius-full: 9999px;

/* Transitions */
–transition-interactive: 180ms cubic-bezier(0.16, 1, 0.3, 1);
–ease-out: cubic-bezier(0.16, 1, 0.3, 1);

/* Content widths */
–content-narrow: 640px;
–content-default: 960px;
–content-wide: 1200px;

/* Fonts */
–font-display: ‘Cabinet Grotesk’, ‘Helvetica Neue’, sans-serif;
–font-body: ‘Satoshi’, ‘Inter’, sans-serif;
}

/* =============================================
COLOR PALETTE — Dark industrial/tech aesthetic
Inspired by blueprint/engineering meets warm craft
============================================= */

:root, [data-theme=”dark”] {
–color-bg: #0c0e12;
–color-surface: #12151b;
–color-surface-2: #181c24;
–color-surface-offset: #1a1e27;
–color-divider: #252a35;
–color-border: #2d3340;

–color-text: #e2e4e8;
–color-text-muted: #8b919d;
–color-text-faint: #555d6e;
–color-text-inverse: #0c0e12;

/* Primary — warm amber/gold (craft, precision) */
–color-primary: #d4922a;
–color-primary-hover: #e5a43e;
–color-primary-active: #b87a1e;
–color-primary-highlight: #2a2218;

/* Accent — cool teal (tech, blueprint) */
–color-accent: #3d9ca6;
–color-accent-hover: #4fb3be;
–color-accent-light: rgba(61, 156, 166, 0.12);

/* Shadows */
–shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
–shadow-md: 0 4px 12px rgba(0,0,0,0.4);
–shadow-lg: 0 12px 32px rgba(0,0,0,0.5);
}

[data-theme=”light”] {
–color-bg: #f5f4f0;
–color-surface: #ffffff;
–color-surface-2: #fafaf8;
–color-surface-offset: #eeedea;
–color-divider: #dddcd8;
–color-border: #cccbc5;

–color-text: #1a1c20;
–color-text-muted: #5e636e;
–color-text-faint: #9a9ea8;
–color-text-inverse: #f5f4f0;

–color-primary: #b87a1e;
–color-primary-hover: #a06a16;
–color-primary-active: #8c5c12;
–color-primary-highlight: #f5efe5;

–color-accent: #2d7f88;
–color-accent-hover: #236970;
–color-accent-light: rgba(45, 127, 136, 0.08);

–shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
–shadow-md: 0 4px 12px rgba(0,0,0,0.08);
–shadow-lg: 0 12px 32px rgba(0,0,0,0.12);
}

@media (prefers-color-scheme: light) {
:root:not([data-theme]) {
–color-bg: #f5f4f0;
–color-surface: #ffffff;
–color-surface-2: #fafaf8;
–color-surface-offset: #eeedea;
–color-divider: #dddcd8;
–color-border: #cccbc5;
–color-text: #1a1c20;
–color-text-muted: #5e636e;
–color-text-faint: #9a9ea8;
–color-text-inverse: #f5f4f0;
–color-primary: #b87a1e;
–color-primary-hover: #a06a16;
–color-primary-active: #8c5c12;
–color-primary-highlight: #f5efe5;
–color-accent: #2d7f88;
–color-accent-hover: #236970;
–color-accent-light: rgba(45, 127, 136, 0.08);
–shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
–shadow-md: 0 4px 12px rgba(0,0,0,0.08);
–shadow-lg: 0 12px 32px rgba(0,0,0,0.12);
}
}

/* =============================================
GLOBAL
============================================= */

.container {
max-width: var(–content-wide);
margin-inline: auto;
padding-inline: var(–space-4);
}

@media (min-width: 768px) {
.container { padding-inline: var(–space-8); }
}

/* =============================================
HEADER / NAV
============================================= */

.header {
position: sticky;
top: 0;
z-index: 50;
background: oklch(from var(–color-bg) l c h / 0.92);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid var(–color-divider);
transition: box-shadow 0.3s var(–ease-out);
}

.header–scrolled {
box-shadow: var(–shadow-md);
}

.header__inner {
display: flex;
align-items: center;
justify-content: space-between;
padding-block: var(–space-3);
}

.header__logo {
display: flex;
align-items: center;
gap: var(–space-2);
text-decoration: none;
color: var(–color-text);
}

.header__logo-mark {
width: 36px;
height: 36px;
flex-shrink: 0;
}

.header__logo-text {
font-family: var(–font-display);
font-size: var(–text-lg);
font-weight: 700;
letter-spacing: -0.02em;
}

.header__logo-text span {
color: var(–color-primary);
}

.header__nav {
display: none;
align-items: center;
gap: var(–space-6);
}

@media (min-width: 768px) {
.header__nav { display: flex; }
}

.header__nav a {
font-size: var(–text-sm);
color: var(–color-text-muted);
text-decoration: none;
font-weight: 500;
transition: color var(–transition-interactive);
}

.header__nav a:hover {
color: var(–color-text);
}

.header__actions {
display: flex;
align-items: center;
gap: var(–space-3);
}

.theme-toggle {
padding: var(–space-2);
color: var(–color-text-muted);
border-radius: var(–radius-md);
transition: color var(–transition-interactive), background var(–transition-interactive);
}

.theme-toggle:hover {
color: var(–color-text);
background: var(–color-surface-offset);
}

/* Mobile nav */
.mobile-toggle {
display: flex;
padding: var(–space-2);
color: var(–color-text-muted);
}

@media (min-width: 768px) {
.mobile-toggle { display: none; }
}

.mobile-nav {
display: none;
padding: var(–space-4);
border-top: 1px solid var(–color-divider);
background: var(–color-bg);
}

.mobile-nav.open {
display: flex;
flex-direction: column;
gap: var(–space-3);
}

.mobile-nav a {
font-size: var(–text-base);
color: var(–color-text-muted);
text-decoration: none;
padding: var(–space-2) 0;
}

/* =============================================
HERO
============================================= */

.hero {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
overflow: hidden;
}

.hero__inner {
display: grid;
gap: var(–space-8);
align-items: center;
}

@media (min-width: 900px) {
.hero__inner {
grid-template-columns: 1fr 1.1fr;
gap: var(–space-12);
}
}

.hero__eyebrow {
font-family: var(–font-body);
font-size: var(–text-xs);
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(–color-primary);
margin-bottom: var(–space-3);
}

.hero__title {
font-family: var(–font-display);
font-size: var(–text-2xl);
font-weight: 800;
letter-spacing: -0.03em;
color: var(–color-text);
margin-bottom: var(–space-4);
}

.hero__subtitle {
font-size: var(–text-base);
color: var(–color-text-muted);
max-width: 50ch;
margin-bottom: var(–space-6);
line-height: 1.7;
}

.hero__ctas {
display: flex;
flex-wrap: wrap;
gap: var(–space-3);
}

.hero__image {
border-radius: var(–radius-xl);
overflow: hidden;
box-shadow: var(–shadow-lg);
border: 1px solid var(–color-border);
}

.hero__image img {
width: 100%;
display: block;
aspect-ratio: 16/9;
object-fit: cover;
}

/* =============================================
BUTTONS
============================================= */

.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(–space-2);
font-family: var(–font-body);
font-size: var(–text-sm);
font-weight: 600;
text-decoration: none;
padding: var(–space-3) var(–space-6);
border-radius: var(–radius-md);
transition: background var(–transition-interactive),
color var(–transition-interactive),
box-shadow var(–transition-interactive),
transform var(–transition-interactive);
cursor: pointer;
white-space: nowrap;
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn–primary {
background: var(–color-primary);
color: #fff;
}

.btn–primary:hover {
background: var(–color-primary-hover);
box-shadow: var(–shadow-md);
}

.btn–secondary {
background: transparent;
color: var(–color-text);
border: 1px solid var(–color-border);
}

.btn–secondary:hover {
background: var(–color-surface-offset);
border-color: var(–color-text-faint);
}

/* =============================================
LIVE DEMO BANNER
============================================= */

.demo-banner {
background: var(–color-surface);
border-top: 1px solid var(–color-divider);
border-bottom: 1px solid var(–color-divider);
padding-block: clamp(var(–space-8), 5vw, var(–space-16));
}

.demo-banner__inner {
display: grid;
gap: var(–space-6);
align-items: center;
text-align: center;
}

@media (min-width: 768px) {
.demo-banner__inner {
grid-template-columns: 1fr auto;
text-align: left;
}
}

.demo-banner__text h2 {
font-family: var(–font-display);
font-size: var(–text-xl);
font-weight: 700;
margin-bottom: var(–space-2);
}

.demo-banner__text p {
color: var(–color-text-muted);
font-size: var(–text-base);
}

/* =============================================
HOW IT WORKS
============================================= */

.how-it-works {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
}

.section-header {
margin-bottom: clamp(var(–space-8), 5vw, var(–space-16));
}

.section-label {
font-size: var(–text-xs);
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(–color-accent);
margin-bottom: var(–space-2);
}

.section-title {
font-family: var(–font-display);
font-size: var(–text-xl);
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: var(–space-3);
}

.section-subtitle {
color: var(–color-text-muted);
font-size: var(–text-base);
max-width: 56ch;
}

.steps {
display: grid;
gap: var(–space-8);
}

@media (min-width: 768px) {
.steps {
grid-template-columns: repeat(3, 1fr);
gap: var(–space-6);
}
}

.step {
position: relative;
padding: var(–space-6);
background: var(–color-surface);
border: 1px solid var(–color-divider);
border-radius: var(–radius-lg);
transition: border-color var(–transition-interactive),
box-shadow var(–transition-interactive);
}

.step:hover {
border-color: var(–color-border);
box-shadow: var(–shadow-md);
}

.step__number {
font-family: var(–font-display);
font-size: var(–text-2xl);
font-weight: 800;
color: var(–color-primary);
opacity: 0.2;
line-height: 1;
margin-bottom: var(–space-4);
}

.step__title {
font-family: var(–font-display);
font-size: var(–text-lg);
font-weight: 700;
margin-bottom: var(–space-2);
}

.step__desc {
font-size: var(–text-base);
color: var(–color-text-muted);
line-height: 1.65;
}

/* =============================================
INDUSTRIES / USE CASES
============================================= */

.industries {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
background: var(–color-surface);
border-top: 1px solid var(–color-divider);
border-bottom: 1px solid var(–color-divider);
}

.industry-grid {
display: grid;
gap: var(–space-6);
}

@media (min-width: 600px) {
.industry-grid {
grid-template-columns: repeat(2, 1fr);
}
}

@media (min-width: 1000px) {
.industry-grid {
grid-template-columns: repeat(3, 1fr);
}
.industry-card:first-child {
grid-column: 1 / 3;
}
}

.industry-card {
padding: var(–space-6);
background: var(–color-bg);
border: 1px solid var(–color-divider);
border-radius: var(–radius-lg);
transition: border-color var(–transition-interactive),
box-shadow var(–transition-interactive);
}

.industry-card:hover {
border-color: var(–color-border);
box-shadow: var(–shadow-sm);
}

.industry-card__icon {
font-size: 1.75rem;
margin-bottom: var(–space-3);
line-height: 1;
}

.industry-card__title {
font-family: var(–font-display);
font-size: var(–text-lg);
font-weight: 700;
margin-bottom: var(–space-2);
}

.industry-card__desc {
font-size: var(–text-base);
color: var(–color-text-muted);
line-height: 1.65;
margin-bottom: var(–space-3);
}

.industry-card__tags {
display: flex;
flex-wrap: wrap;
gap: var(–space-2);
}

.tag {
font-size: var(–text-xs);
font-weight: 500;
padding: var(–space-1) var(–space-3);
background: var(–color-accent-light);
color: var(–color-accent);
border-radius: var(–radius-full);
white-space: nowrap;
}

/* =============================================
CUSTOMER JOURNEY
============================================= */

.journey {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
}

.journey-steps {
display: grid;
gap: var(–space-6);
margin-top: var(–space-8);
}

@media (min-width: 768px) {
.journey-steps {
grid-template-columns: repeat(2, 1fr);
}
}

.journey-step {
display: grid;
grid-template-columns: auto 1fr;
gap: var(–space-4);
align-items: start;
padding: var(–space-5);
background: var(–color-surface);
border: 1px solid var(–color-divider);
border-radius: var(–radius-lg);
}

.journey-step__marker {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(–color-primary);
color: #fff;
font-family: var(–font-display);
font-weight: 700;
font-size: var(–text-sm);
border-radius: var(–radius-md);
flex-shrink: 0;
}

.journey-step__title {
font-family: var(–font-display);
font-size: var(–text-base);
font-weight: 700;
margin-bottom: var(–space-1);
}

.journey-step__desc {
font-size: var(–text-sm);
color: var(–color-text-muted);
line-height: 1.6;
}

/* =============================================
WHY CONFIGULARITY
============================================= */

.why {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
background: var(–color-surface);
border-top: 1px solid var(–color-divider);
border-bottom: 1px solid var(–color-divider);
}

.why-grid {
display: grid;
gap: var(–space-6);
}

@media (min-width: 768px) {
.why-grid {
grid-template-columns: repeat(2, 1fr);
}
}

.why-card {
padding: var(–space-5);
}

.why-card__title {
font-family: var(–font-display);
font-size: var(–text-base);
font-weight: 700;
margin-bottom: var(–space-2);
display: flex;
align-items: center;
gap: var(–space-2);
}

.why-card__title svg {
color: var(–color-primary);
flex-shrink: 0;
}

.why-card__desc {
font-size: var(–text-base);
color: var(–color-text-muted);
line-height: 1.65;
padding-left: calc(20px + var(–space-2));
}

/* =============================================
CTA / CONTACT
============================================= */

.cta {
padding-block: clamp(var(–space-12), 8vw, var(–space-24));
}

.cta__inner {
display: grid;
gap: var(–space-8);
align-items: start;
}

@media (min-width: 768px) {
.cta__inner {
grid-template-columns: 1fr 1fr;
gap: var(–space-12);
}
}

.cta__info h2 {
font-family: var(–font-display);
font-size: var(–text-xl);
font-weight: 700;
margin-bottom: var(–space-3);
}

.cta__info > p {
color: var(–color-text-muted);
font-size: var(–text-base);
margin-bottom: var(–space-6);
line-height: 1.65;
}

.cta__benefits {
list-style: none;
display: flex;
flex-direction: column;
gap: var(–space-3);
margin-bottom: var(–space-6);
}

.cta__benefits li {
display: flex;
align-items: flex-start;
gap: var(–space-2);
font-size: var(–text-sm);
color: var(–color-text-muted);
}

.cta__benefits svg {
color: var(–color-primary);
flex-shrink: 0;
margin-top: 2px;
}

.cta__location {
font-size: var(–text-xs);
color: var(–color-text-faint);
margin-top: var(–space-4);
}

/* Form */
.contact-form {
padding: var(–space-6);
background: var(–color-surface);
border: 1px solid var(–color-divider);
border-radius: var(–radius-lg);
}

.form-group {
margin-bottom: var(–space-4);
}

.form-group label {
display: block;
font-size: var(–text-sm);
font-weight: 500;
margin-bottom: var(–space-1);
color: var(–color-text);
}

.form-group input,
.form-group textarea {
width: 100%;
padding: var(–space-3);
background: var(–color-bg);
border: 1px solid var(–color-border);
border-radius: var(–radius-md);
font-size: var(–text-base);
color: var(–color-text);
transition: border-color var(–transition-interactive),
box-shadow var(–transition-interactive);
}

.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(–color-primary);
box-shadow: 0 0 0 3px oklch(from var(–color-primary) l c h / 0.15);
}

.form-group textarea {
min-height: 100px;
resize: vertical;
}

.form-submit {
width: 100%;
}

/* =============================================
FOOTER
============================================= */

.footer {
padding-block: var(–space-8);
border-top: 1px solid var(–color-divider);
}

.footer__inner {
display: flex;
flex-direction: column;
gap: var(–space-3);
align-items: center;
text-align: center;
}

@media (min-width: 768px) {
.footer__inner {
flex-direction: row;
justify-content: space-between;
text-align: left;
}
}

.footer__copy {
font-size: var(–text-xs);
color: var(–color-text-faint);
}

.footer__links {
display: flex;
gap: var(–space-4);
}

.footer__links a {
font-size: var(–text-xs);
color: var(–color-text-faint);
text-decoration: none;
transition: color var(–transition-interactive);
}

.footer__links a:hover {
color: var(–color-text-muted);
}

/* =============================================
SCROLL ANIMATIONS
============================================= */

.fade-in { opacity: 1; }

@supports (animation-timeline: scroll()) {
.fade-in {
opacity: 0;
animation: reveal-fade linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
}

@keyframes reveal-fade {
to { opacity: 1; }
}

{
“@context”: “https://schema.org”,
“@type”: “ProfessionalService”,
“name”: “Configularity”,
“description”: “Configuration studio building interactive product configurators for bespoke businesses”,
“areaServed”: “United Kingdom”,
“address”: {
“@type”: “PostalAddress”,
“addressLocality”: “Belfast”,
“addressCountry”: “GB”
},
“creator”: {
“@type”: “SoftwareApplication”,
“name”: “Perplexity Computer”,
“url”: “https://www.perplexity.ai/computer”
}
}


Skip to content

Configuration Studio

Turn bespoke products into guided buying experiences

We build interactive configurators that let your customers explore options, personalise their build, and request quotes — without the back-and-forth.

Split view showing a campervan blueprint transforming into a fully configured luxury interior

See a live configurator in action

Explore our campervan configurator built for Bespoke Leisure — choose colours, interiors, and finishes with real-time visual updates.

Launch demo →

From complex offerings to clear journeys

We map your products, options, and pricing into guided flows your customers actually understand — and your team can maintain.

01

Discover

We work with your team to unpack your products, use-cases, constraints, and pricing — modelling the real decisions your customers face.

02

Design

We create step-by-step flows, option groups, and validation logic that keep customers confident — not overwhelmed — as they personalise their choices.

03

Build

We implement your configurator on your existing site, connect it to your CRM or quote tools, and hand over a structure your team can update.

Built for businesses with bespoke offerings

A few of the sectors we design configuration experiences for.

Campervans and vehicle conversions

Layouts, power options, storage, finishes, and equipment — configured visually so customers see exactly what they’re building before they enquire.

Layouts
Power
Storage
Finishes

Body kits and automotive styling

Let customers select body panels, spoilers, diffusers, and paint finishes — with visual previews and compatibility checks built in.

Panels
Paint
Compatibility

Upholstery and interiors

Fabric choices, stitching patterns, foam density, and dimensions — guided so customers understand the trade-offs between comfort, durability, and cost.

Materials
Stitching
Dimensions

Commercial vehicles

Racking, livery, telematics, and payload options for fleets — configured per vehicle type with clear pricing and spec sheets.

Racking
Livery
Telematics

Spaces and fit-outs

Modular spaces, kiosks, and interiors where clients choose footprints, modules, finishes, and services in a guided flow.

Modules
Finishes
Utilities

What your customers see

From first visit to qualified enquiry — a clear path that works for both sides.

1

Explore possibilities

Your customer lands on your site and plays with a simple configurator to see what’s possible — no pressure, no forms.

2

Shape their ideal build

They answer targeted questions to narrow down layouts, budgets, and key options — guided, not guessing.

3

Review and request

They see a clear summary of their configuration and send it as a structured enquiry or quote request.

4

You follow up with clarity

Your team picks up a qualified enquiry with all the right detail — no endless back-and-forth over email.

Built for how your business actually works

Tailored to your products

Not a template. Every configurator is designed around your specific product range, pricing logic, and customer journey.

Works on your existing site

We build on WordPress, custom platforms, or standalone — integrating with your CRM and quote systems, not replacing them.

Qualified leads, not tyre kickers

Customers who complete a configurator have already thought through what they want. Your team gets structured enquiries, not vague “just looking” emails.

You own it

We hand over a clean, maintainable codebase. Your team can update options, pricing, and images without calling us every time.

Start with a 30-minute call

Tell us how you sell today, and we’ll suggest a configuration journey that fits your products and your customers.

  • We map your existing offers and options

  • You get a proposed flow and rough implementation plan

  • No obligation — useful even if you’re just exploring

Based in Belfast. Available remotely across the UK and beyond.

Name

Email

Business name

What do you want customers to configure?

// Theme toggle
(function(){
const t = document.querySelector(‘[data-theme-toggle]’);
const r = document.documentElement;
let d = matchMedia(‘(prefers-color-scheme: dark)’).matches ? ‘dark’ : ‘light’;
r.setAttribute(‘data-theme’, d);
if (t) {
t.addEventListener(‘click’, () => {
d = d === ‘dark’ ? ‘light’ : ‘dark’;
r.setAttribute(‘data-theme’, d);
t.setAttribute(‘aria-label’, ‘Switch to ‘ + (d === ‘dark’ ? ‘light’ : ‘dark’) + ‘ mode’);
t.innerHTML = d === ‘dark’
? ”
: ”;
});
}
})();

// Sticky header scroll shadow
(function(){
const header = document.getElementById(‘header’);
let ticking = false;
window.addEventListener(‘scroll’, () => {
if (!ticking) {
requestAnimationFrame(() => {
header.classList.toggle(‘header–scrolled’, window.scrollY > 10);
ticking = false;
});
ticking = true;
}
});
})();

// Mobile nav toggle
(function(){
const toggle = document.querySelector(‘.mobile-toggle’);
const nav = document.querySelector(‘.mobile-nav’);
if (toggle && nav) {
toggle.addEventListener(‘click’, () => {
const open = nav.classList.toggle(‘open’);
toggle.setAttribute(‘aria-expanded’, open);
});
nav.querySelectorAll(‘a’).forEach(a => {
a.addEventListener(‘click’, () => {
nav.classList.remove(‘open’);
toggle.setAttribute(‘aria-expanded’, ‘false’);
});
});
}
})();

// Contact form handler (simple feedback)
function handleSubmit(e) {
e.preventDefault();
const form = e.target;
const btn = form.querySelector(‘.form-submit’);
btn.textContent = ‘Message sent ✓’;
btn.style.background = ‘#2d7f88’;
btn.disabled = true;
setTimeout(() => {
btn.textContent = ‘Send message’;
btn.style.background = ”;
btn.disabled = false;
form.reset();
}, 3000);
}

// Show desktop CTA button when scrolled past hero
(function(){
const ctaBtn = document.querySelector(‘.header__actions .btn–primary’);
if (!ctaBtn) return;
const hero = document.querySelector(‘.hero’);
const observer = new IntersectionObserver(([entry]) => {
ctaBtn.style.display = entry.isIntersecting ? ‘none’ : ‘inline-flex’;
}, { threshold: 0 });
observer.observe(hero);
})();