/*
Theme Name: Cadence
Theme URI: http://107.150.41.186:8130/
Author: Cadence
Description: A multipurpose business theme built on a design-token system. One hue, one scale ratio, one density figure, all set in the Customizer with live preview — change them and the whole site retargets to another industry. Ships six industry presets, a GPU-rendered ambient layer derived from the same tokens, a real light/dark pair, container-query components and subgrid layout.
Version: 1.0.0
Requires at least: 6.4
Tested up to: 6.7
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: cadence
Tags: business, portfolio, one-column, custom-colors, custom-menu, featured-images, translation-ready
*/

/* =====================================================================
   CADENCE
   ---------------------------------------------------------------------
   The whole theme is a function of five numbers:

     --h    hue          0..360   colour identity
     --c    chroma       0..1.4   how saturated the identity is
     --r    scale ratio  1.1..1.45 typographic rhythm
     --rad  radius       0..28px  how soft the geometry is
     --d    density      0.8..1.3 how much air the page carries

   Nothing below hard-codes a colour, a font size or a section padding.
   Everything derives. That is what makes one theme fit a law firm and a
   software company without either of them looking like a compromise.
   The five are set once, in the Customizer. Nothing on the published
   page configures them — a theme that spends its own layout on its own
   controls is a demo, not a product.

   All five are registered with @property, so changing one animates
   rather than snaps. Registering them is not decoration: an unregistered
   custom property is an untyped token and cannot be interpolated at all.
   ===================================================================== */

@property --h   { syntax: "<number>"; inherits: true; initial-value: 254; }
@property --c   { syntax: "<number>"; inherits: true; initial-value: 1; }
@property --r   { syntax: "<number>"; inherits: true; initial-value: 1.28; }
@property --rad { syntax: "<length>"; inherits: true; initial-value: 5px; }
@property --d   { syntax: "<number>"; inherits: true; initial-value: 1; }

/* ---------------------------------------------------------------------
   1 · Tokens
   ------------------------------------------------------------------ */

:root {
	color-scheme: light dark;

	--h: 254;
	--c: 1;
	--r: 1.28;
	--rad: 5px;
	--d: 1;

	/* Lightness ladder. Dark mode swaps only these six numbers — the
	   colours themselves are composed further down and never restated,
	   so the two themes cannot drift apart. */
	--l-bg: 99%;
	--l-sub: 96.4%;
	--l-line: 88%;
	--l-dim: 50%;
	--l-ink: 21%;
	--l-acc: 51%;
	--acc-c: 0.155;
	--shade: 0 0% 0%;
	--veil: 0.06;

	/* Composed palette — one hue in, a whole system out */
	--bg:    oklch(var(--l-bg)   calc(0.007 * var(--c)) var(--h));
	--sub:   oklch(var(--l-sub)  calc(0.013 * var(--c)) var(--h));
	--line:  oklch(var(--l-line) calc(0.021 * var(--c)) var(--h));
	--dim:   oklch(var(--l-dim)  calc(0.030 * var(--c)) var(--h));
	--ink:   oklch(var(--l-ink)  calc(0.036 * var(--c)) var(--h));
	--acc:   oklch(var(--l-acc)  calc(var(--acc-c) * var(--c)) var(--h));
	--acc-2: oklch(calc(var(--l-acc) + 13%) calc(var(--acc-c) * 0.8 * var(--c)) calc(var(--h) + 48));

	/* Derived, never hand-picked */
	--acc-soft:  color-mix(in oklab, var(--acc) 13%, transparent);
	--acc-line:  color-mix(in oklab, var(--acc) 34%, transparent);
	--acc-ink:   color-mix(in oklab, var(--acc) 82%, var(--ink));
	--hair:      color-mix(in oklab, var(--line) 82%, transparent);
	--ink-70:    color-mix(in oklab, var(--ink) 70%, transparent);
	--on-acc:    oklch(98% 0.012 var(--h));

	/* Type. One fluid base; every other step is the ratio applied to it,
	   which is what a modular scale actually is. */
	--s0: clamp(1rem, 0.955rem + 0.19vw, 1.1rem);
	--s-1: calc(var(--s0) / var(--r));
	--s-2: calc(var(--s-1) / var(--r));
	--s1: calc(var(--s0) * var(--r));
	--s2: calc(var(--s1) * var(--r));
	--s3: calc(var(--s2) * var(--r));
	--s4: calc(var(--s3) * var(--r));
	--s5: calc(var(--s4) * var(--r));
	--s6: calc(var(--s5) * var(--r));

	/* Display sizes are the top of the scale, capped by the viewport so a
	   ratio of 1.45 cannot push a headline off a phone. */
	--t-hero: min(var(--s6), 13.5vw);
	--t-sect: min(var(--s4), 9vw);
	--t-lead: min(var(--s2), 6.4vw);

	--ff-disp: "Familjen Grotesk", "Newsreader", ui-sans-serif, system-ui, sans-serif;
	--ff-body: "Manrope", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
	--ff-mono: "Chivo Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
	--disp-wght: 600;
	--disp-ital: 0;
	--disp-track: -0.03em;

	/* Space. Density scales the rhythm, not individual paddings. */
	--sp: calc(1rem * var(--d));
	--gap: calc(var(--sp) * 1.6);
	--band: calc(var(--sp) * clamp(4, 5.5vw, 7.5));
	--edge: clamp(20px, 4vw, 56px);
	--max: 1300px;
	--rail: clamp(0px, 13vw, 13.5rem);

	--ease: cubic-bezier(0.22, 0.72, 0.2, 1);
	--ease-out: cubic-bezier(0.16, 0.86, 0.24, 1);
	--dur: 520ms;

	--shadow-1: 0 1px 2px oklch(var(--l-ink) 0.03 var(--h) / 0.05),
	            0 8px 22px -14px oklch(var(--l-ink) 0.05 var(--h) / 0.30);
	--shadow-2: 0 2px 4px oklch(var(--l-ink) 0.03 var(--h) / 0.05),
	            0 26px 50px -26px oklch(var(--l-ink) 0.06 var(--h) / 0.42);
}

/* Retheming should feel like a dimmer, not a light switch — but only for
   visitors who want motion. This has to live outside the :root block:
   a running transition on a registered property sits in the animation
   origin, which outranks the inline style the Studio writes, so if the
   transition is declared but never ticks the new value never lands. */
@media (prefers-reduced-motion: no-preference) {
	:root {
		transition: --h var(--dur) var(--ease), --c var(--dur) var(--ease),
		            --r var(--dur) var(--ease), --rad var(--dur) var(--ease),
		            --d var(--dur) var(--ease);
	}
}

/* The system preference, honoured only while the visitor has not chosen */
@media (prefers-color-scheme: dark) {
	:root:not([data-theme]) {
		--l-bg: 15.5%;
		--l-sub: 19.5%;
		--l-line: 29%;
		--l-dim: 66%;
		--l-ink: 96%;
		--l-acc: 72%;
		--acc-c: 0.145;
		--veil: 0.4;
	}
}

/* An explicit choice wins in both directions */
:root[data-theme="dark"] {
	--l-bg: 15.5%;
	--l-sub: 19.5%;
	--l-line: 29%;
	--l-dim: 66%;
	--l-ink: 96%;
	--l-acc: 72%;
	--acc-c: 0.145;
	--veil: 0.4;
}
:root[data-theme="light"] {
	--l-bg: 99%;
	--l-sub: 96.4%;
	--l-line: 88%;
	--l-dim: 50%;
	--l-ink: 21%;
	--l-acc: 51%;
	--acc-c: 0.155;
	--veil: 0.06;
}

/* The usual reduced-motion reset sets transition-duration to a hair above
   zero. That is worse than it looks: transition-property defaults to
   `all`, so a duration alone switches transitions ON for every animatable
   property on every element. Removing them outright is both the honest
   reading of the preference and the safer one — a transition that exists
   but never advances holds the old value in the animation origin, which
   outranks everything else. */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 1ms !important;
		animation-iteration-count: 1 !important;
		transition: none !important;
		scroll-behavior: auto !important;
	}
}

/* ---------------------------------------------------------------------
   2 · Base
   ------------------------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	scroll-padding-top: calc(var(--sp) * 6);
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--ink);
	font-family: var(--ff-body);
	font-size: var(--s0);
	font-weight: 400;
	line-height: 1.62;
	font-optical-sizing: auto;
	text-rendering: optimizeLegibility;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
	hanging-punctuation: first last;
}

h1, h2, h3, h4 {
	font-family: var(--ff-disp);
	font-weight: var(--disp-wght);
	font-variation-settings: "wght" var(--disp-wght);
	font-style: normal;
	letter-spacing: var(--disp-track);
	line-height: 1.02;
	margin: 0;
	text-wrap: balance;
}
h1 { font-size: var(--t-hero); }
h2 { font-size: var(--t-sect); }
h3 { font-size: var(--s1); line-height: 1.16; letter-spacing: -0.015em; }
h4 { font-size: var(--s0); line-height: 1.25; letter-spacing: -0.01em; }

p { margin: 0 0 1em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration: none; }

img, svg, video { max-width: 100%; display: block; height: auto; }

:focus-visible {
	outline: 2px solid var(--acc);
	outline-offset: 3px;
	border-radius: 2px;
}

::selection { background: var(--acc); color: var(--on-acc); }

.wrap {
	width: min(100% - var(--edge) * 2, var(--max));
	margin-inline: auto;
}
.wrap--tight { --max: 940px; }

.screen-reader-text {
	position: absolute !important;
	width: 1px; height: 1px;
	overflow: hidden; clip-path: inset(50%);
	white-space: nowrap;
}
.skip {
	position: absolute; top: -60px; left: var(--edge); z-index: 200;
	background: var(--ink); color: var(--bg);
	padding: 10px 16px; border-radius: var(--rad);
	transition: top 180ms var(--ease);
}
.skip:focus { top: 12px; }

/* ---------------------------------------------------------------------
   3 · Shared devices
   ------------------------------------------------------------------ */

/* The label: a small mono line that carries real information —
   a section's role, a token's value, a case study's sector. */
.lab {
	font-family: var(--ff-mono);
	font-size: clamp(0.66rem, 0.62rem + 0.12vw, 0.76rem);
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--dim);
	font-weight: 500;
	display: flex;
	align-items: center;
	gap: 0.6em;
	margin: 0;
}
.lab::before {
	content: "";
	width: 1.6em; height: 1px;
	background: var(--acc);
	flex: none;
}
.lab--bare::before { display: none; }

.lead {
	font-size: var(--t-lead);
	line-height: 1.32;
	letter-spacing: -0.018em;
	font-weight: 500;
	color: var(--ink);
	text-wrap: pretty;
	max-width: 26ch;
}

.prose { max-width: 68ch; color: var(--ink-70); font-size: var(--s0); }
.prose > * + * { margin-top: 1.1em; }
.prose h2 { font-size: var(--s2); margin-top: 1.6em; }
.prose h3 { font-size: var(--s1); margin-top: 1.5em; }
.prose a { color: var(--acc-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.prose blockquote {
	margin: 1.6em 0;
	padding-left: calc(var(--sp) * 1.4);
	border-left: 2px solid var(--acc);
	font-size: var(--s1);
	line-height: 1.4;
	font-family: var(--ff-disp);
	letter-spacing: -0.015em;
	color: var(--ink);
}
.prose ul, .prose ol { padding-left: 1.2em; }
.prose li + li { margin-top: 0.4em; }
.prose figure { margin: 1.8em 0; }
.prose figcaption { font-family: var(--ff-mono); font-size: var(--s-2); color: var(--dim); margin-top: 0.7em; }
.prose img { border-radius: var(--rad); }

/* Buttons */
.btn {
	--btn-bg: var(--ink);
	--btn-fg: var(--bg);
	display: inline-flex;
	align-items: center;
	gap: 0.7em;
	padding: calc(var(--sp) * 0.78) calc(var(--sp) * 1.35);
	background: var(--btn-bg);
	color: var(--btn-fg);
	border: 1px solid var(--btn-bg);
	border-radius: var(--rad);
	font-family: var(--ff-body);
	font-size: var(--s-1);
	font-weight: 600;
	letter-spacing: 0.01em;
	cursor: pointer;
	position: relative;
	overflow: hidden;
	isolation: isolate;
	transition: transform 240ms var(--ease), box-shadow 240ms var(--ease),
	            background 240ms var(--ease), color 240ms var(--ease),
	            border-color 240ms var(--ease);
}
.btn::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: var(--acc);
	transform: translateY(101%);
	transition: transform 420ms var(--ease-out);
}
.btn:hover { color: var(--on-acc); border-color: var(--acc); transform: translateY(-2px); box-shadow: var(--shadow-1); }
.btn:hover::after { transform: translateY(0); }
.btn__ar { transition: transform 320ms var(--ease); }
.btn:hover .btn__ar { transform: translateX(3px); }

.btn--ghost { --btn-bg: transparent; --btn-fg: var(--ink); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--acc); }
.btn--acc { --btn-bg: var(--acc); --btn-fg: var(--on-acc); }
.btn--acc::after { background: var(--ink); }
.btn--acc:hover { color: var(--bg); border-color: var(--ink); }

/* A text link that draws its own rule */
.tl {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	font-weight: 600;
	font-size: var(--s-1);
	color: var(--acc-ink);
	padding-bottom: 2px;
	background-image: linear-gradient(var(--acc), var(--acc));
	background-size: 0% 1px;
	background-repeat: no-repeat;
	background-position: 0 100%;
	transition: background-size 380ms var(--ease-out);
}
.tl:hover { background-size: 100% 1px; }

/* ---------------------------------------------------------------------
   4 · The section chassis
   Every band shares one grid: a rail that carries the section's label and
   live token readout, and a main column. On wide viewports the rail's
   contents stick, so the reader always knows which part of the system
   they are inside. Children opt into subgrid so their own columns line
   up with the parent's — that alignment is the whole reason the page
   reads as one document rather than a stack of blocks.
   ------------------------------------------------------------------ */

.band {
	padding-block: var(--band);
	position: relative;
}
.band + .band { border-top: 1px solid var(--hair); }
.band--sub { background: var(--sub); }
.band--flush { padding-block: 0; }

.grid {
	display: grid;
	grid-template-columns: [rail] var(--rail) [main] minmax(0, 1fr);
	column-gap: clamp(20px, 3.4vw, 60px);
	row-gap: calc(var(--sp) * 2);
}
@media (max-width: 900px) {
	:root { --rail: 0px; }
	.grid { grid-template-columns: [rail] 0 [main] minmax(0, 1fr); column-gap: 0; }
}

.grid__rail {
	grid-column: rail;
	position: sticky;
	top: calc(var(--sp) * 6);
	align-self: start;
	display: flex;
	flex-direction: column;
	gap: 0.9em;
}
@media (max-width: 900px) {
	.grid__rail { position: static; grid-column: main; margin-bottom: calc(var(--sp) * 0.6); }
}
.grid__main { grid-column: main; }

/* A short standfirst under the rail label. Content, not telemetry. */
.rail__note {
	font-size: var(--s-2);
	line-height: 1.5;
	color: var(--dim);
	border-left: 1px solid var(--hair);
	padding-left: 0.9em;
	margin: 0;
	max-width: 22ch;
}
@media (max-width: 900px) { .rail__note { display: none; } }

/* Section head */
.head { display: grid; gap: calc(var(--sp) * 1.1); margin-bottom: calc(var(--sp) * 2.6); }
.head p { max-width: 56ch; color: var(--ink-70); margin: 0; }
.head--split {
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: end;
	column-gap: var(--gap);
}
.head--split > .lab { grid-column: 1 / -1; }
@media (max-width: 760px) {
	.head--split { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------------------
   5 · Reveals — scroll-driven where supported, observer-driven where not
   ------------------------------------------------------------------ */

/* Scoped to .js — see the bootstrap in functions.php. A reveal that
   starts at opacity 0 and is switched on by script is a blank page for
   anyone whose script did not run, which for a theme sold to other
   people is not an acceptable failure mode. */
.js .rv { opacity: 0; transform: translateY(18px); transition: opacity 760ms var(--ease-out), transform 760ms var(--ease-out); }
.rv.in { opacity: 1; transform: none; }
.rv.d1 { transition-delay: 70ms; }
.rv.d2 { transition-delay: 140ms; }
.rv.d3 { transition-delay: 210ms; }
.rv.d4 { transition-delay: 280ms; }

/* Line-masked headings: each line rides up out of its own clip */
.lm { display: block; overflow: hidden; }
.js .lm > i {
	display: block;
	font-style: inherit;
	transform: translateY(110%);
	transition: transform 900ms var(--ease-out);
}
.lm > i { display: block; font-style: inherit; }
.lm.in > i, .in .lm > i { transform: none; }
.lm.d1 > i { transition-delay: 90ms; }
.lm.d2 > i { transition-delay: 180ms; }
.lm.d3 > i { transition-delay: 270ms; }

@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.sd {
			animation: sd-in linear both;
			animation-timeline: view();
			animation-range: entry 8% cover 26%;
		}
		@keyframes sd-in {
			from { opacity: 0; transform: translateY(22px); }
			to   { opacity: 1; transform: none; }
		}
	}
}
