/* ---------- Design tokens ----------
   Two neutral families: a warm paper family for light surfaces and a cool "stone" scale (near-neutral grey with a
   hint of the lake's blue, low saturation) for text and dark surfaces. One brand blue, one deep heading
   blue, one amber accent. */
:root {
  /* Warm surfaces */
  --paper: #F6F3EE;
  --paper-2: #EDE8E1;
  --line: #D8D2C9;

  /* Cool scale (stone) */
  --cool-900: #1B2025;
  --cool-800: #272E34;
  --cool-700: #3A434B;
  --cool-500: #6E7780;
  --cool-300: #A7AEB4;
  --cool-100: #DCDFE2;

  /* Moss: reserved secondary surface (forest behind the lake). Not used on the site today; kept for the style guide */
  --moss-700: #3C4642;
  --moss-800: #2A312E;

  /* State colours for product UI (success / warning / error / info). Not used on the site; kept for the style guide.
     Each has a text-safe tone for light surfaces, a lighter tone for dark surfaces and a tint for message backgrounds. */
  --ok: #2F6B4F;
  --ok-dark: #8FC2A6;
  --ok-tint: color-mix(in srgb, var(--ok) 12%, var(--paper));
  --warn: #9A6112;
  --warn-dark: #E7B563;
  --warn-tint: color-mix(in srgb, var(--warn) 12%, var(--paper));
  --err: #A9432F;
  --err-dark: #E8907E;
  --err-tint: color-mix(in srgb, var(--err) 12%, var(--paper));
  --info: #005293;
  --info-dark: #7FB3E0;
  --info-tint: color-mix(in srgb, var(--info) 12%, var(--paper));

  /* Brand */
  --blue: #005293;        /* KUVIO blue: the mark, links, lattice, focus on light surfaces */
  --ink: #00175A;         /* headings and strong text on light surfaces */
  --accent: #D4922B;      /* amber: nodes, hover underlines, contact underline, focus on dark surfaces */

  /* Roles */
  --text: var(--cool-800);
  --muted: var(--cool-500);
  --lake: var(--cool-700);
  --lake-deep: var(--cool-800);
  --mist: var(--cool-100);
  --mist-2: var(--cool-300);
  --glass: color-mix(in srgb, var(--paper) 72%, transparent);
  --glass-blur: 16px;

  /* Type: Inter, weights 300 display / 400 body / 500 UI; headings tracked -0.03em; two small sizes; three line-heights; one label style */
  --fs-label: 0.8rem;
  --fs-ui: 0.9rem;
  --fs-lead: clamp(1.15rem, 1.7vw, 1.4rem);
  --lh-display: 1.05;
  --lh-heading: 1.2;
  --lh-body: 1.55;
  --track-label: 0.16em;
  --measure: 62ch;
  --header-h: 74px;
}

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

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: Inter, system-ui, sans-serif;
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

/* Text links: an amber underline grows from the left on hover/focus. Links styled as controls opt out below. */
a {
  color: var(--blue);
  text-decoration: none;
  background: linear-gradient(var(--accent), var(--accent)) no-repeat 0 100% / 0 1px;
  transition: background-size 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
a:hover, a:focus-visible { background-size: 100% 1px; }
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--blue);   /* blue on light surfaces; .contact switches to amber on dark */
  outline-offset: 4px;
  border-radius: 2px;
}
::selection { background: color-mix(in srgb, var(--blue) 18%, transparent); }

.wrap {
  width: min(100% - 3rem, 1120px);
  margin-inline: auto;
}

/* ---------- Header ---------- */
.top {
  position: sticky;
  top: 0;
  z-index: 10;
  padding-block: 1.25rem;
  border-bottom: 1px solid transparent;
  transition: background-color 600ms ease, border-color 600ms ease;
}
.top .wrap { display: flex; align-items: center; justify-content: space-between; }
.top.is-stuck {
  background: var(--glass);
  border-color: var(--line);
  /* stylelint-disable-next-line property-no-vendor-prefix -- Safari < 18 only knows the prefixed form */
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.2);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.2);
}
.top nav { display: flex; gap: 1.75rem; font-size: var(--fs-ui); font-weight: 500; }
.top nav a { color: var(--ink); }
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  color: var(--blue);
  font-weight: 500;
  letter-spacing: var(--track-label);
  font-size: var(--fs-ui);
  background: none;
}
.brand svg { width: 34px; height: 34px; }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block: clamp(4rem, 10vw, 9rem) clamp(5rem, 12vw, 11rem);
}
.hero .wrap {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 3rem;
  align-items: center;
}
.hero h1 {
  font-weight: 300;
  font-size: clamp(2.5rem, 6.2vw, 4.75rem);
  line-height: var(--lh-display);
  letter-spacing: -0.03em;
  color: var(--ink);
  max-width: 14ch;
}
.hero p {
  margin-top: 1.75rem;
  font-size: var(--fs-lead);
  line-height: var(--lh-body);
  max-width: 40ch;
}
.hero .cta {
  display: inline-block;
  margin-top: 2.5rem;
  padding: 0.9rem 1.6rem;
  border: 1px solid var(--ink);
  border-radius: 999px;
  color: var(--ink);
  font-weight: 500;
  font-size: var(--fs-ui);
  background: none;
  transition: background-color 160ms ease, color 160ms ease;
}
.hero .cta:hover,
.hero .cta:focus-visible {
  background: var(--ink);
  color: var(--paper);
}

/* The "kuvio": a 19-cell hexagonal rosette (R = 70, viewBox 640) that resolves into the mark.
   Stroke widths are user units tuned per breakpoint to land at ~1px / ~2px on screen. */
.lattice { --cell-w: 1.4; --mark-w: 2.8; width: 100%; max-width: 520px; justify-self: end; color: var(--blue); }
.lattice .mark { stroke-width: var(--mark-w); }
.lattice .cell { stroke-width: var(--cell-w); opacity: 0.5; }

/* The field: script.js fills this hero-sized svg with hex cells (ring 3 outward) aligned to the rosette, in px units */
.field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  color: var(--blue);
  fill: none;
  stroke: currentcolor;
  stroke-width: 1;
  stroke-linecap: square;
  stroke-linejoin: miter;
  opacity: 0.25;
  pointer-events: none;

  /* Visible only to the right of the copy column (soft horizontal edge), fading at the hero's top and bottom */
  mask-image: linear-gradient(to right, transparent 46%, #000 62%), linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent);
  mask-composite: intersect;
}
.field polygon { opacity: var(--o, 1); }   /* steps lighter every three rings, set per cell in script.js */

/* Nodes: three glowing points on the rosette's vertices; script.js positions them (--x/--y) once the field is drawn.
   Hover or focus shows the slogan card; click/tap pins it (the only way on touch screens). */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.nodes { position: absolute; inset: 0; z-index: 2; list-style: none; pointer-events: none; }
.node { position: absolute; z-index: 0; left: var(--x, -100px); top: var(--y, -100px); transform: translate(-50%, -50%); }
.node:hover, .node:focus-within, .node.is-open, .node.is-touring { z-index: 1; }   /* active node and its card above sibling dots */

/* The pop-in animates the button, not .node: an opacity effect on the card's parent would confine the card's backdrop blur */
.node.is-placed button { animation: pop 650ms cubic-bezier(0.34, 1.56, 0.64, 1) both; animation-delay: var(--pop-delay, 0ms); }
@keyframes pop { 0% { opacity: 0; transform: scale(0.2); } 60% { opacity: 1; } 100% { opacity: 1; transform: scale(1); } }
.node button {
  position: relative;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--accent);
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
}
.node-dot {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentcolor;
  filter: drop-shadow(0 0 6px currentcolor);
  transition: transform 200ms ease, filter 200ms ease;
}
.node-dot::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1px solid currentcolor;
  opacity: 0;
  animation: pulse 2.6s ease-out infinite;
  animation-delay: var(--pulse-delay, 0ms);
}
.node button:hover .node-dot,
.node button:focus-visible .node-dot,
.node.is-open .node-dot,
.node.is-touring .node-dot { transform: scale(1.35); filter: drop-shadow(0 0 10px currentcolor); }
@keyframes pulse { from { transform: scale(0.6); opacity: 0.7; } to { transform: scale(2.4); opacity: 0; } }
.node-card {
  position: absolute;
  top: 50%;
  left: calc(100% + 0.5rem);
  transform: translateY(-50%);
  width: 17rem;
  padding: 0.9rem 1.1rem;
  background: var(--glass);
  /* stylelint-disable-next-line property-no-vendor-prefix -- Safari < 18 only knows the prefixed form */
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.2);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.2);
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);   /* the edge facing the dot; placement rules move it */
  border-radius: 2px;
  font-size: var(--fs-ui);
  line-height: var(--lh-body);
  color: var(--muted);
  pointer-events: auto;
  opacity: 0;
  visibility: hidden;
  translate: -6px 0;
  transition: opacity 140ms ease-in, translate 140ms ease-in, visibility 0s 140ms;
}
.node-card strong { display: block; margin-bottom: 0.35rem; font-weight: 300; font-size: 1.15rem; line-height: var(--lh-heading); letter-spacing: -0.03em; color: var(--ink); }
.node-card p { margin-top: 0; font-size: inherit; line-height: inherit; max-width: none; }   /* undo .hero p */

/* Placement (flip / below / above) is set by script.js. Closed cards sit 6px towards their dot and slide out as they fade in;
   the connector line (::before) draws from the dot to the card. */
.node.flip .node-card { left: auto; right: calc(100% + 0.5rem); translate: 6px 0; border-left-width: 1px; border-left-color: var(--line); border-right: 2px solid var(--accent); }
.node.below .node-card,
.node.above .node-card { inset: auto; left: 50%; top: calc(100% + 0.75rem); width: min(17rem, calc(100vw - 3rem)); transform: translateX(calc(-50% + var(--shift, 0px))); translate: 0 -6px; border-left-width: 1px; border-left-color: var(--line); border-top: 2px solid var(--accent); }
.node.above .node-card { top: auto; bottom: calc(100% + 0.75rem); translate: 0 6px; border-top-width: 1px; border-top-color: var(--line); border-bottom: 2px solid var(--accent); }
.node-card::before { content: ""; position: absolute; top: 50%; right: 100%; width: calc(0.5rem + 16px); height: 1px; background: var(--accent); opacity: 0.7; transform-origin: 0 50%; scale: 0 1; transition: scale 140ms ease-in; }
.node.flip .node-card::before { right: auto; left: 100%; transform-origin: 100% 50%; }
.node.below .node-card::before,
.node.above .node-card::before { inset: auto; left: calc(50% - var(--shift, 0px)); bottom: 100%; width: 1px; height: calc(0.75rem + 16px); transform-origin: 50% 0; scale: 1 0; }
.node.above .node-card::before { bottom: auto; top: 100%; transform-origin: 50% 100%; }
.node.is-open .node-card,
.node.is-touring .node-card { opacity: 1; visibility: visible; translate: 0 0; transition: opacity 180ms ease-out, translate 240ms cubic-bezier(0.2, 0.7, 0.2, 1), visibility 0s; }
.node.is-open .node-card::before,
.node.is-touring .node-card::before { scale: 1 1; transition: scale 220ms cubic-bezier(0.2, 0.7, 0.2, 1) 60ms; }
@media (hover: hover) {
  .node:hover .node-card { opacity: 1; visibility: visible; translate: 0 0; transition: opacity 180ms ease-out, translate 240ms cubic-bezier(0.2, 0.7, 0.2, 1), visibility 0s; }
  .node:hover .node-card::before { scale: 1 1; transition: scale 220ms cubic-bezier(0.2, 0.7, 0.2, 1) 60ms; }
}

/* ---------- Sections ---------- */
section { padding-block: clamp(4rem, 8vw, 7.5rem); }
section[id] { scroll-margin-top: calc(var(--header-h) + 0.5rem); }
h1, h2, h3 { text-wrap: balance; }
p, dd { text-wrap: pretty; }
.band { background: var(--paper-2); }

h2 {
  font-weight: 300;
  font-size: clamp(1.9rem, 3.4vw, 2.75rem);
  line-height: var(--lh-heading);
  letter-spacing: -0.03em;
  color: var(--ink);
  max-width: 22ch;
}
h3 {
  font-weight: 300;
  font-size: 1.5rem;
  line-height: var(--lh-heading);
  letter-spacing: -0.03em;
  color: var(--ink);
}
.lead {
  margin-top: 1.5rem;
  font-size: var(--fs-lead);
  line-height: var(--lh-body);
  max-width: var(--measure);
  color: var(--text);
}
p + p { margin-top: 1rem; }

/* Intro */
.intro .wrap {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
  gap: 3rem;
}
.intro .lead { margin-top: 0; }

/* Services: heading, then two equal columns (3:2 figure, title, copy, keyword tags) */
.eyebrow {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--muted);
}
.services-head { max-width: 62ch; }
.eyebrow + h2 { margin-top: 0.75rem; }
.services-head .lead { margin-top: 1rem; }
.services-grid {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
.service h3 { margin-top: 1.5rem; }
.service p { margin-top: 0.75rem; max-width: 48ch; }
.service .credential { margin-top: 0.4rem; max-width: none; font-size: var(--fs-label); font-weight: 500; letter-spacing: 0.01em; color: var(--muted); }
.service .credential + p { margin-top: 1rem; }
.tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}
.tags li {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.8rem 0.35rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink);
}
.tags li::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

.figure {
  aspect-ratio: 3 / 2;
  background: var(--paper-2);
  position: relative;
  overflow: hidden;
}
.figure img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Full-bleed divider image between the activity blocks and the closing text */
.divider {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3392 / 1248;
}
.divider img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Principles */
.principles {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
  border-top: 1px solid var(--line);
}
.principles > div {
  padding-top: 1.5rem;
}
.principles p {
  margin-top: 0.6rem;
  max-width: 32ch;
}

/* Contact: closing block in the lake's palette (water above, deep water below); the footer continues it */
.contact {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, var(--lake), var(--lake-deep));   /* ends on the footer's surface, so the two read as one block */
  color: var(--mist);
  padding-block: clamp(5rem, 10vw, 9rem);
}
.contact .wrap { position: relative; z-index: 1; display: grid; gap: clamp(2.5rem, 5vw, 4rem); }
.contact-main { max-width: 40rem; }
.contact .eyebrow { color: var(--mist-2); }
.contact h2 { color: var(--mist); max-width: 18ch; }
.contact .lead { color: var(--mist-2); max-width: 44ch; }
.contact-grid { display: grid; }
.channel-label { font-size: var(--fs-label); font-weight: 500; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--mist-2); }
.contact a.mail,
.contact a.profile {
  display: inline-block;
  margin-top: 0.75rem;
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  font-weight: 300;
  line-height: var(--lh-display);
  letter-spacing: -0.03em;
  color: var(--mist);
  background: none;
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.16em;
  overflow-wrap: anywhere;
  transition: text-decoration-thickness 160ms ease, text-underline-offset 160ms ease;
}
.contact a.mail:hover,
.contact a.profile:hover { text-decoration-thickness: 3px; text-underline-offset: 0.2em; }
.copy-row { margin-top: 1.25rem; display: flex; align-items: center; gap: 1rem; }
.copy {
  padding: 0.6rem 1.1rem;
  border: 1px solid color-mix(in srgb, var(--mist) 45%, transparent);
  border-radius: 999px;
  background: none;
  color: var(--mist);
  font: inherit;
  font-size: var(--fs-ui);
  font-weight: 500;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}
.copy:hover,
.copy.is-done { background: var(--mist); color: var(--lake-deep); border-color: var(--mist); }
.copy-status { font-size: var(--fs-ui); color: var(--mist-2); }
.contact :is(a, button, input, textarea):focus-visible { outline-color: var(--accent); }
.hero .cta:focus-visible,
.copy:focus-visible,
.submit:focus-visible { border-radius: 999px; }   /* keep the pills round under the generic focus rule */

/* Contact form: underlined fields on the dark surface, no boxes. Posts to the KUVIO Mailer API (script.js, Module 8);
   field names are the Mailer's config.php `fields` keys. .form-field, not .field — .field is the hero's hex field. */
.contact-form { max-width: 40rem; transition: opacity 300ms ease; }
.contact-form.is-done { opacity: 0; pointer-events: none; }
.hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }   /* honeypot: off-screen, not display:none, so bots still fill it */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.6rem 2rem; }
.form-field { margin: 0; display: grid; gap: 0.25rem; }
.form-field-wide { grid-column: 1 / -1; }
.form-field label { font-size: var(--fs-label); font-weight: 500; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--mist-2); }
.form-field .opt { font-weight: 400; letter-spacing: 0; text-transform: none; }
.form-field :is(input, textarea) {
  width: 100%;
  padding: 0.55rem 0;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--mist) 35%, transparent);
  border-radius: 0;
  background: linear-gradient(var(--accent), var(--accent)) no-repeat 0 100% / 0 2px;   /* the text-link underline, grown on focus */
  color: var(--mist);
  font: inherit;
  line-height: var(--lh-body);
  transition: border-color 160ms ease, background-size 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.form-field :is(input, textarea):focus { border-bottom-color: transparent; background-size: 100% 2px; }
.form-field textarea { resize: vertical; min-height: 6.5rem; }
.form-field :is(input, textarea):-webkit-autofill { -webkit-text-fill-color: var(--mist); box-shadow: 0 0 0 1000px var(--lake-deep) inset; caret-color: var(--mist); }
.form-field :is(input, textarea):focus-visible { outline-offset: 6px; }
.form-field.is-invalid :is(input, textarea) { border-bottom-color: var(--err-dark); background-image: linear-gradient(var(--err-dark), var(--err-dark)); }
.field-error { font-size: var(--fs-ui); color: var(--err-dark); }
.field-error:empty { display: none; }   /* no empty grid track under the input */
.field-error:not(:empty) { animation: rise 240ms ease both; }
.form-actions { margin-top: 2rem; display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.submit {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1.4rem;
  border: 1px solid color-mix(in srgb, var(--mist) 45%, transparent);
  border-radius: 999px;
  background: none;
  color: var(--mist);
  font: inherit;
  font-size: var(--fs-ui);
  font-weight: 500;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease, opacity 160ms ease;
}
.submit:hover { background: var(--mist); color: var(--lake-deep); border-color: var(--mist); }
.submit[disabled] { opacity: 0.6; cursor: progress; }
.submit[disabled]:hover { background: none; color: var(--mist); border-color: color-mix(in srgb, var(--mist) 45%, transparent); }
.submit-spinner { display: none; width: 1em; height: 1em; border: 2px solid currentcolor; border-right-color: transparent; border-radius: 50%; animation: spin 700ms linear infinite; }
.contact-form.is-sending .submit-spinner { display: inline-block; }
.form-status { font-size: var(--fs-ui); color: var(--mist-2); }
.form-status.is-error { color: var(--err-dark); }
.form-status a { color: inherit; background-image: linear-gradient(currentcolor, currentcolor); background-size: 100% 1px; }
.form-privacy { margin-top: 1.25rem; max-width: 44ch; font-size: var(--fs-ui); color: var(--mist-2); }

/* Success: the form fades, the mark draws itself (same dash trick as the hero lattice), the text rises after it */
.form-success { max-width: 40rem; padding-block: 1rem; }
.form-success[hidden] { display: none; }
.success-mark { width: 3rem; height: 3rem; fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: square; stroke-linejoin: miter; }
.success-mark :is(circle, path) { stroke-dasharray: 1; stroke-dashoffset: 1; }
.form-success.is-shown circle { animation: draw 600ms cubic-bezier(0.4, 0, 0.2, 1) forwards; }
.form-success.is-shown path { animation: draw 360ms cubic-bezier(0.4, 0, 0.2, 1) 450ms forwards; }
.success-title { margin-top: 1.25rem; font-size: clamp(1.5rem, 2.6vw, 2.25rem); font-weight: 300; line-height: var(--lh-display); letter-spacing: -0.03em; color: var(--mist); outline: none; }
.success-text { margin-top: 0.75rem; max-width: 44ch; color: var(--mist-2); }
.form-success.is-shown :is(.success-title, .success-text) { animation: rise 500ms cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.form-success.is-shown .success-title { animation-delay: 300ms; }
.form-success.is-shown .success-text { animation-delay: 420ms; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Echo of the hero rosette: <use> clones of the lattice groups, static and faint */
.contact-lattice {
  position: absolute;
  top: 50%;
  right: -6%;
  width: min(46vw, 560px);
  transform: translateY(-50%);
  color: var(--mist);
  fill: none;
  stroke: currentcolor;
  stroke-linecap: square;
  stroke-linejoin: miter;
  opacity: 0.14;
  pointer-events: none;
  mask-image: radial-gradient(circle at 50% 50%, #000 45%, transparent 75%);
}
.contact-lattice use:first-child { stroke-width: 1.4; }
.contact-lattice use:last-child { stroke-width: 2.8; }

/* Footer */
footer {
  background: var(--lake-deep);
  border-top: 1px solid color-mix(in srgb, var(--mist) 22%, transparent);
  padding-block: 2.5rem 3rem;
  font-size: var(--fs-ui);
  color: var(--mist-2);
}
footer .wrap {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem 2rem;
}
footer nav { display: flex; gap: 1.75rem; flex-wrap: wrap; }
footer .meta { display: flex; flex-wrap: wrap; }
footer .meta span + span::before { content: "·"; margin-inline: 0.6rem; opacity: 0.5; }
footer .meta span:first-child { color: var(--mist); }
footer a { color: var(--mist); }
footer a:hover, footer a:focus-visible { color: var(--paper); }

/* Right-hand group: LinkedIn, then the language switcher. .social pushes both to the far edge; the current language
   is a link styled as a label, so it opts out of the underline like .brand does. */
footer .social, footer .lang { display: flex; gap: 1.1rem; flex-wrap: wrap; font-size: var(--fs-label); letter-spacing: 0.04em; }
footer .social { margin-inline-start: auto; }
footer .lang a[aria-current] { color: var(--paper); background: none; cursor: default; }

/* ---------- Motion ---------- */
html.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 600ms ease, transform 600ms cubic-bezier(0.2, 0.7, 0.2, 1);
  transition-delay: var(--reveal-delay, 0ms);
}
html.js [data-reveal].is-visible { opacity: 1; transform: none; }

/* Service figures: wipe open from the bottom and settle from a slight zoom instead of rising; tags stagger in */
html.js .service .figure[data-reveal] { transform: none; transition: opacity 700ms ease; transition-delay: var(--reveal-delay, 0ms); }
html.js .service .figure[data-reveal] img { clip-path: inset(100% 0 0 0); transform: scale(1.06); transition: clip-path 900ms cubic-bezier(0.2, 0.7, 0.2, 1), transform 1400ms cubic-bezier(0.2, 0.7, 0.2, 1); transition-delay: var(--reveal-delay, 0ms); }
html.js .service .figure.is-visible img { clip-path: inset(0); transform: none; }
html.js .tags[data-reveal] li { opacity: 0; translate: 0 6px; transition: opacity 400ms ease, translate 400ms cubic-bezier(0.2, 0.7, 0.2, 1); }
html.js .tags.is-visible li { opacity: 1; translate: 0 0; }
html.js .tags.is-visible li:nth-child(2) { transition-delay: 90ms; }
html.js .tags.is-visible li:nth-child(3) { transition-delay: 180ms; }
html.js .tags.is-visible li:nth-child(4) { transition-delay: 270ms; }
html.js .tags.is-visible li:nth-child(5) { transition-delay: 360ms; }
html.js .tags.is-visible li:nth-child(6) { transition-delay: 450ms; }
@media (hover: hover) {
  .service .figure.is-visible img { transition: clip-path 900ms cubic-bezier(0.2, 0.7, 0.2, 1), transform 600ms cubic-bezier(0.2, 0.7, 0.2, 1); }
  .service:hover .figure.is-visible img { transform: scale(1.03); }
}

/* Hero text: same motion, runs on load */
html.js .hero-in {
  animation: rise 700ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: var(--reveal-delay, 0ms);
}
@keyframes rise { from { opacity: 0; transform: translateY(18px); } }

/* Lattice draw-in, started by .is-visible on the hero: the mark outline draws, spokes shoot out from the centre,
   the dot fades in, the rosette grows outward ring by ring, then the field continues the wave to the hero's edges.
   pathLength="1" makes every dash 0..1; field delays (--d) are computed per cell in script.js. */
html.js :is(.lattice, .field) :is(polygon, line) { stroke-dasharray: 1; stroke-dashoffset: 1; }
html.js .lattice circle { opacity: 0; }
html.js .hero.is-visible .lattice .mark polygon { animation: draw 900ms cubic-bezier(0.4, 0, 0.2, 1) 200ms forwards; }
html.js .hero.is-visible .lattice .mark line { animation: draw 380ms cubic-bezier(0.4, 0, 0.2, 1) forwards; animation-delay: calc(720ms + var(--i, 0) * 40ms); }
html.js .hero.is-visible .lattice circle { animation: fade 400ms ease 1000ms forwards; }
html.js .hero.is-visible .lattice .cell polygon { animation: draw 700ms cubic-bezier(0.4, 0, 0.2, 1) forwards; animation-delay: calc(var(--t0) + var(--i, 0) * var(--step)); }
html.js .hero.is-visible .field polygon { animation: draw 700ms cubic-bezier(0.4, 0, 0.2, 1) forwards; animation-delay: var(--d, 0ms); }
html.js .field polygon.is-drawn { animation: none; stroke-dashoffset: 0; }
.lattice .r1 { --t0: 1100ms; --step: 50ms; }
.lattice .r2 { --t0: 1550ms; --step: 45ms; }
.lattice :is(polygon, line):nth-child(2) { --i: 1; }
.lattice :is(polygon, line):nth-child(3) { --i: 2; }
.lattice :is(polygon, line):nth-child(4) { --i: 3; }
.lattice :is(polygon, line):nth-child(5) { --i: 4; }
.lattice :is(polygon, line):nth-child(6) { --i: 5; }
.lattice :is(polygon, line):nth-child(7) { --i: 6; }
.lattice :is(polygon, line):nth-child(8) { --i: 7; }
.lattice :is(polygon, line):nth-child(9) { --i: 8; }
.lattice :is(polygon, line):nth-child(10) { --i: 9; }
.lattice :is(polygon, line):nth-child(11) { --i: 10; }
.lattice :is(polygon, line):nth-child(12) { --i: 11; }
@keyframes fade { to { opacity: 1; } }
@keyframes draw { to { stroke-dashoffset: 0; } }

@media (prefers-reduced-motion: reduce) {
  html.js [data-reveal] { opacity: 1; transform: none; transition: none; }
  html.js .hero-in { animation: none; }
  html.js .service .figure[data-reveal] img, html.js .tags[data-reveal] li { clip-path: none; transform: none; translate: 0 0; opacity: 1; transition: none; }
  html.js :is(.lattice, .field) :is(polygon, line) { animation: none; stroke-dashoffset: 0; }
  html.js .lattice circle { animation: none; opacity: 1; }
  .node.is-placed button { animation: none; opacity: 1; }
  .node-card, .node-card::before { translate: 0 0; scale: 1 1; transition-duration: 120ms; }
  .node-dot::before { animation: none; opacity: 0.35; transform: none; }
  .form-field :is(input, textarea), .contact-form { transition: none; }
  .field-error:not(:empty), .form-success.is-shown :is(.success-title, .success-text) { animation: none; }
  .form-success.is-shown :is(circle, path) { animation: none; stroke-dashoffset: 0; }
  .submit-spinner { animation: none; }
}

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .top nav { gap: 1.1rem; font-size: var(--fs-label); }
  .hero { padding-block: clamp(3rem, 8vw, 5rem) 3.5rem; }
  .hero .wrap { grid-template-columns: 1fr; gap: 2.75rem; }
  .lattice { --cell-w: 2.9; --mark-w: 5.6; max-width: 220px; justify-self: center; }
  .field { mask-image: linear-gradient(to bottom, transparent 50%, #000 68%), linear-gradient(to bottom, transparent, #000 10%, #000 90%, transparent); }
  .intro .wrap { grid-template-columns: 1fr; gap: 1.5rem; }
  .services-grid { grid-template-columns: 1fr; gap: 3rem; }
  .divider { aspect-ratio: 16 / 9; }
  .principles { grid-template-columns: 1fr; gap: 0; }
  .principles > div { padding-block: 1.5rem; border-bottom: 1px solid var(--line); }
  .principles > div:last-child { border-bottom: 0; }
  .contact-lattice { width: 70vw; right: -25%; top: 0; transform: none; opacity: 0.1; }
  .form-grid { grid-template-columns: 1fr; gap: 1.4rem; }
  footer .meta { flex-direction: column; gap: 0.35rem; }
  footer .meta span + span::before { content: none; }
  footer nav { gap: 0.6rem 1.25rem; }
  footer .lang { gap: 0.9rem; }
  footer .social { margin-inline-start: 0; }
}
