/* ============================================================
   Appure - appure.pl
   Dark, minimal, one page. No frameworks, no external requests.
   ============================================================ */

/* ---------- Reset ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
* {
  margin: 0;
  padding: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  /* Native smooth scrolling for every in-page anchor. scroll-padding keeps
     section headings clear of the fixed header when they land. Both are
     overridden under prefers-reduced-motion further down. */
  scroll-behavior: smooth;
  scroll-padding-top: 92px;
}
img,
svg,
canvas {
  display: block;
  max-width: 100%;
}
button,
input,
textarea {
  font: inherit;
  color: inherit;
}
a {
  color: inherit;
  text-decoration: none;
}
ul {
  list-style: none;
}

/* ---------- Tokens ---------- */
:root {
  /* Dark is the design's native state; light is an override further down.
     --ink is the foreground as bare RGB components so the many translucent
     overlays in this file can be written once and still invert: white veils on
     dark, black veils on light. A literal rgba(255,255,255,...) would stay
     white in light mode and wash the page out. */
  --ink: 255, 255, 255;
  --shadow: 0, 0, 0;
  --shadow-a: 0.95;
  --knob: #ffffff;

  --bg: #07080b;
  --bg-blur: rgba(7, 8, 11, 0.72); /* stuck header */
  --bg-panel: rgba(7, 8, 11, 0.96); /* mobile nav panel */
  --scrim: rgba(4, 5, 7, 0.6); /* behind panel and cookie dialog */
  --surface: rgba(12, 14, 19, 0.96); /* raised dialogs */
  --panel: rgba(var(--ink), 0.028);
  --line: rgba(var(--ink), 0.09);
  --line-soft: rgba(var(--ink), 0.055);

  --fg: #f2f4f8;
  --fg-mid: rgba(242, 244, 248, 0.66);
  --fg-dim: rgba(242, 244, 248, 0.42);

  /* Primary accent. Teal is bright enough on this background to carry the
     brand on its own; the blue is kept only as a gradient partner. */
  --accent: #00fed3;
  --accent-2: #5b8cff;
  --accent-rgb: 0, 254, 211;
  --accent-glow: rgba(0, 254, 211, 0.45);
  --accent-lift: #6ee7ff; /* brighter partner used by hovers and the shimmer */
  --accent2-rgb: 91, 140, 255;
  /* Teal is very light, so solid buttons need dark text to stay readable. */
  --on-accent: #04120f;

  --font:
    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  /* Third control point >1 gives a slight overshoot before settling. */
  --spring: cubic-bezier(0.16, 1.08, 0.3, 1);
  --shell: 1120px;
  --pad: 24px;

  /* Regular flat-top hexagon - all six sides equal, all angles 120°.
     Percentages in clip-path resolve against width and height separately, so
     the polygon is only regular when the box itself is in the hexagon's
     natural ratio: height = width * sin(60°) = 0.8660254. Every element using
     --hex therefore also sets --hex-ratio as its aspect-ratio; changing one
     without the other is what makes a hexagon look stretched. */
  --hex: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
  --hex-ratio: 1 / 0.8660254;
}

/* ---------- Light theme ----------
   Same design, inverted ground. Three things are not a straight inversion:

   1. --ink flips to black, so every translucent overlay in this file becomes a
      dark veil instead of a white one and keeps its intended weight.
   2. --accent darkens to #00876f. The neon teal is designed to glow on near
      black; on white it fails contrast badly for text and links.
   3. Shadows lose most of their weight - on a light ground a 95% black shadow
      reads as dirt, not depth.

   [data-theme] is set on <html> by the toggle, and the media query covers the
   first visit before any choice is stored. */
:root[data-theme="light"] {
  --ink: 17, 20, 26;
  --shadow: 18, 24, 38;
  --shadow-a: 0.16;
  --knob: #ffffff;

  --bg: #f7f8fa;
  --bg-blur: rgba(247, 248, 250, 0.78);
  --bg-panel: rgba(247, 248, 250, 0.97);
  --scrim: rgba(20, 24, 32, 0.35);
  --surface: rgba(255, 255, 255, 0.97);
  --panel: rgba(var(--ink), 0.022);
  --line: rgba(var(--ink), 0.13);
  --line-soft: rgba(var(--ink), 0.085);

  --fg: #11141a;
  --fg-mid: rgba(17, 20, 26, 0.72);
  --fg-dim: rgba(17, 20, 26, 0.52);

  /* Darkened for contrast on a light ground; --accent-rgb keeps the bright
     hue, because the tints built from it are backgrounds, not text. */
  --accent: #00876f;
  --accent-2: #2f5fd8;
  --accent-rgb: 0, 135, 111;
  --accent-glow: rgba(0, 135, 111, 0.3);
  --accent-lift: #00b894; /* darker, not brighter: it must stay legible on white */
  --accent2-rgb: 47, 95, 216;
  --on-accent: #ffffff;
}

/* Theme cross-fade. Applied only while the switch is mid-flight, so ordinary
   hovers keep their own timings and nothing transitions on first paint. */
:root.is-theming,
:root.is-theming *,
:root.is-theming *::before,
:root.is-theming *::after {
  transition:
    background-color 0.45s var(--ease-out),
    border-color 0.45s var(--ease-out),
    color 0.45s var(--ease-out),
    fill 0.45s var(--ease-out),
    box-shadow 0.45s var(--ease-out) !important;
}
@media (prefers-reduced-motion: reduce) {
  :root.is-theming,
  :root.is-theming *,
  :root.is-theming *::before,
  :root.is-theming *::after {
    transition: none !important;
  }
}

/* ---------- Base ---------- */
body {
  background: var(--bg);
  color: var(--fg-mid);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Ambient wash. Fixed so it never scrolls with content. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(
      720px circle at 78% -8%,
      rgba(var(--accent-rgb), 0.12),
      transparent 60%
    ),
    radial-gradient(
      560px circle at 4% 22%,
      rgba(var(--accent2-rgb), 0.1),
      transparent 62%
    );
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: absolute;
  top: -60px;
  left: 16px;
  z-index: 10000;
  padding: 11px 18px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  transition: top 0.25s var(--ease-out);
}
.skip:focus {
  top: 16px;
}

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

/* ---------- Reveal on scroll ----------
   Driven by IntersectionObserver in main.js. Without JS the .js class is never
   set, so content stays visible - progressive enhancement.

   The spring easing overshoots slightly, which is what stops the entry from
   feeling like a plain fade. Blur is included because it reads as depth on a
   dark background far more cheaply than a shadow would. */
/* Elements arrive tilted slightly away from the viewer and rotate flat as
   they rise. The perspective is set on the element itself so no wrapper is
   needed, and the whole thing collapses to `none` once shown, which keeps the
   rendered result pixel-identical to having no transform at all. */
.js .reveal {
  opacity: 0;
  transform: perspective(1000px) translateY(40px) rotateX(9deg) scale(0.96);
  transform-origin: 50% 100%;
  filter: blur(8px);
  /* clip-path wipes the element in from below as it rises, so it feels like it
     is being drawn rather than just fading. inset() animates on the compositor. */
  clip-path: inset(0 0 12% 0 round 18px);
  transition:
    opacity 0.85s var(--ease-out),
    transform 1.05s var(--spring),
    filter 0.8s var(--ease-out),
    clip-path 0.95s var(--ease-out);
}
.js .reveal.is-in {
  opacity: 1;
  transform: none;
  filter: none;
  clip-path: inset(0 0 0 0 round 18px);
}
/* The wipe's rounded corners would clip square-cornered blocks, so plain text
   containers opt out and animate on transform alone. The flow steps and track
   items are here because the 18px radius was shaving the bottom-left corner off
   the pill badge that sits at the foot of each one; .track itself is here
   because the same radius cut into the first letter of its heading, which
   starts hard against the block's top-left corner. */
.js .section__head.reveal,
.js .flow__step.reveal,
.js .track.reveal,
.js .track__item.reveal,
.js .step.reveal,
.js .stackrow.reveal {
  clip-path: none;
}

/* Sideways variant, used where an element sits beside its sibling. */
.js .reveal--left {
  transform: translateX(-30px);
}
.js .reveal--right {
  transform: translateX(30px);
}

/* Stagger. Set via data-d on the element, or applied automatically to the
   children of a [data-stagger] container by main.js. */
.js .reveal[data-d="1"] {
  transition-delay: 0.07s;
}
.js .reveal[data-d="2"] {
  transition-delay: 0.14s;
}
.js .reveal[data-d="3"] {
  transition-delay: 0.21s;
}
.js .reveal[data-d="4"] {
  transition-delay: 0.28s;
}
.js .reveal[data-d="5"] {
  transition-delay: 0.35s;
}
.js .reveal[data-d="6"] {
  transition-delay: 0.42s;
}
.js .reveal[data-d="7"] {
  transition-delay: 0.49s;
}
.js .reveal[data-d="8"] {
  transition-delay: 0.56s;
}

/* Hero content animates on load rather than on scroll - it is already in
   view, so waiting for an intersection would leave it blank for a beat. */
.js .hero__in > * {
  opacity: 0;
  animation: hero-in 1s var(--spring) both;
}
.js .hero__in > *:nth-child(1) {
  animation-delay: 0.15s;
}
.js .hero__in > *:nth-child(2) {
  animation-delay: 0.24s;
}
.js .hero__in > *:nth-child(3) {
  animation-delay: 0.46s;
}
.js .hero__in > *:nth-child(4) {
  animation-delay: 0.58s;
}
.js .hero__in > *:nth-child(5) {
  animation-delay: 0.7s;
}
@keyframes hero-in {
  0% {
    opacity: 0;
    transform: translateY(34px) scale(0.98);
    filter: blur(10px);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* The headline is the one element worth animating per line: each line sits in
   its own overflow-hidden strip and slides up from beneath it, which reads as
   type being set rather than a block fading in. Spans are added by main.js. */
.js .hero__title .line {
  display: block;
  overflow: hidden;
}
.js .hero__title .line > span {
  display: inline-block;
  transform: translateY(105%) rotate(2deg);
  animation: line-up 1.05s var(--spring) both;
}
.js .hero__title .line:nth-child(1) > span {
  animation-delay: 0.26s;
}
.js .hero__title .line:nth-child(2) > span {
  animation-delay: 0.36s;
}
.js .hero__title .line:nth-child(3) > span {
  animation-delay: 0.46s;
}
@keyframes line-up {
  0% {
    transform: translateY(105%) rotate(2deg);
  }
  100% {
    transform: translateY(0) rotate(0);
  }
}
/* The eyebrow's dot ripples out once the badge has landed. */
.js .eyebrow {
  animation-delay: 0.15s;
}

/* The header slides down once, ahead of the hero copy. */
.js .header {
  animation: header-in 0.7s var(--ease-out) both;
}
@keyframes header-in {
  0% {
    opacity: 0;
    transform: translateY(-100%);
  }
  100% {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Header ---------- */
.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 900;
  padding-block: 18px;
  border-bottom: 1px solid transparent;
  transition:
    background 0.4s var(--ease-out),
    border-color 0.4s var(--ease-out),
    padding 0.4s var(--ease-out);
}
.header.is-stuck {
  padding-block: 12px;
  background: var(--bg-blur);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
  border-bottom-color: var(--line-soft);
}
.header__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

/* Wordmark only. The dot is the single accented character, so the logo still
   carries the brand colour without needing a symbol next to it. */
.logo {
  display: inline-flex;
  align-items: center;
}
.logo__text {
  font-size: clamp(26px, 3vw, 32px);
  font-weight: 680;
  letter-spacing: -0.035em;
  color: var(--fg);
  transition: color 0.3s var(--ease-out);
}
.logo__dot {
  color: var(--accent);
  transition: text-shadow 0.35s var(--ease-out);
}
.logo:hover .logo__dot {
  text-shadow: 0 0 14px var(--accent-glow);
}

/* ---------- Logo cube flip ----------
   The wordmark is the front face of a cube hinged on its own centre line: on
   hover it rolls away from the viewer while an identical face swings up from
   below. Mouse-out rolls it back.

   Two real faces rather than the usual attr(data-text) trick, because this
   wordmark has a coloured dot in the middle and attr() would flatten it to a
   single colour. .logo__flat holds the layout box and stays readable to
   screen readers; the cube on top is aria-hidden. */
.logo__text {
  position: relative;
  display: inline-block;
  perspective: 420px;
}
/* The flat copy reserves the space and is what assistive tech reads. It is
   hidden from sight only while the cube can actually render. */
.logo__cube {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.55s cubic-bezier(0.65, 0, 0.35, 1);
}
.logo__face {
  position: absolute;
  inset: 0;
  white-space: nowrap;
  backface-visibility: hidden;
}
/* Three faces cover the 180 degree roll: front (start), bottom (passes through
   the middle, which is what makes it read as a solid box rather than a card
   turning over), and back (lands). Each is rotated into place, then pushed out
   from the centre by half the cube depth in its OWN rotated frame - that is
   what puts translateZ on the correct side. */
.logo__face--front {
  transform: translateZ(0.4em);
}
.logo__face--bottom {
  transform: rotateX(90deg) translateZ(0.4em);
}
/* Built already flipped so it reads upright when the roll brings it round. */
.logo__face--back {
  transform: rotateX(180deg) translateZ(0.4em);
}
.logo:hover .logo__cube,
.logo:focus-visible .logo__cube {
  transform: rotateX(-180deg);
}

/* Only hide the flat copy where the 3D transform will actually run. */
@supports (transform-style: preserve-3d) {
  .logo__flat {
    visibility: hidden;
  }
}
/* No cube under reduced motion: show the real wordmark and keep a plain tint. */
@media (prefers-reduced-motion: reduce) {
  .logo__flat {
    visibility: visible;
  }
  .logo__cube {
    display: none;
  }
}

.nav {
  display: flex;
  align-items: center;
  gap: 30px;
}
/* Underline grows from the left instead of appearing all at once. */
.nav__link {
  position: relative;
  font-size: 14px;
  color: var(--fg-dim);
  transition: color 0.28s var(--ease-out);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -5px;
  width: 100%;
  height: 1.5px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.38s var(--spring);
}
.nav__link:hover {
  color: var(--fg);
}
.nav__link:hover::after {
  transform: scaleX(1);
}

/* ---------- Section navigator ----------
   Bottom-centre pill: jump to the next section, step back, or let it advance
   on its own. Adapted from the manico3 build; no animation library here, so
   the dwell ring is driven by requestAnimationFrame in main.js. */
.secnav {
  position: fixed;
  left: 50%;
  /* --secnav-lift is set by JS once the footer reaches the pill, so it rests
     just above the footer instead of covering it. */
  bottom: calc(22px + var(--secnav-lift, 0px));
  transform: translateX(-50%) translateY(0);
  z-index: 880;
  display: flex;
  align-items: center;
  gap: 9px;
  opacity: 1;
  transition:
    opacity 0.35s var(--ease-out),
    transform 0.5s var(--spring);
}
.secnav[hidden] {
  display: none;
}

.secnav button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--surface);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 14px 34px -16px rgba(var(--shadow), calc(var(--shadow-a) * 0.8));
  transition:
    background 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out),
    color 0.3s var(--ease-out),
    transform 0.3s var(--ease-out);
}
.secnav button:hover {
  border-color: rgba(var(--accent-rgb), 0.45);
  color: var(--accent);
  transform: translateY(-2px);
}
.secnav button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.secnav button svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

.secnav .secnav__next {
  padding: 12px 18px;
  overflow: hidden;
  white-space: nowrap;
  /* width is animated from JS when the label changes, so the pill resizes
     smoothly instead of snapping. */
  transition:
    background 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out),
    color 0.3s var(--ease-out),
    transform 0.3s var(--ease-out),
    width 0.42s var(--ease-out);
}
.secnav__back,
.secnav__play {
  width: 42px;
  height: 42px;
  padding: 0;
  position: relative;
}
/* 2px under the others - the solid accent fill makes it read slightly larger
   than the outlined buttons at a matched size. Width is set by the reveal
   rules below, since it collapses to 0 while hidden. */
.secnav__top {
  height: 40px;
  padding: 0;
  position: relative;
  flex: none;
}
.secnav__back {
  display: none;
}
.secnav.has-back .secnav__back {
  display: flex;
}
.secnav.at-end .secnav__next {
  display: none;
}

/* Dwell ring, drawn just inside the play button's edge. The extra specificity
   is needed: `.secnav button svg` sizes icons to 14px and would shrink it. */
.secnav .secnav__ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  transform: rotate(-90deg); /* start the sweep at 12 o'clock */
  overflow: visible;
}
.secnav__ring circle {
  fill: none;
  stroke-width: 2;
}
.secnav__ring-bg {
  stroke: rgba(var(--ink), 0.14);
}
.secnav__ring-fg {
  stroke: var(--accent);
  stroke-linecap: round;
  /* 2*pi*r with r=20 = 125.66; JS drives stroke-dashoffset from full to 0 */
  stroke-dasharray: 125.66;
  stroke-dashoffset: 125.66;
}
.secnav__play--off .secnav__ring {
  opacity: 0;
}
.secnav__play .ic-play {
  display: none;
}
.secnav__play--off .ic-play {
  display: block;
}
.secnav__play--off .ic-pause {
  display: none;
}

/* Back to top, last in the pill. Same round geometry as the other buttons,
   but it keeps the accent fill so it stays the one that stands out.

   Hidden until the visitor is a screen or so down, then it slides up into
   place. It sits in a flex row, so the width and margin collapse too -
   otherwise it would leave a gap in the pill while invisible. */
.secnav .secnav__top {
  border-color: transparent;
  background: var(--accent);
  color: var(--on-accent);
  opacity: 0;
  transform: translateY(120%);
  pointer-events: none;
  width: 0;
  margin-left: -9px; /* cancels the pill's gap while collapsed */
  overflow: hidden;
  transition:
    opacity 0.35s var(--ease-out),
    transform 0.5s var(--spring),
    width 0.35s var(--ease-out),
    margin-left 0.35s var(--ease-out),
    background 0.3s var(--ease-out);
}
.secnav .secnav__top.is-in {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
  width: 40px;
  margin-left: 0;
}
.secnav .secnav__top:hover {
  border-color: transparent;
  background: var(--accent-lift);
  color: var(--on-accent);
}
.secnav .secnav__top:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 3px var(--on-accent);
}
.secnav .secnav__top svg {
  width: 17px;
  height: 17px;
}

@media (max-width: 620px) {
  .secnav {
    bottom: calc(14px + var(--secnav-lift, 0px));
    gap: 7px;
  }
  .secnav .secnav__next {
    padding: 11px 14px;
    font-size: 10px;
  }
  .secnav__back,
  .secnav__play {
    width: 38px;
    height: 38px;
  }
  .secnav__top {
    height: 36px;
  }
  .secnav .secnav__top {
    margin-left: -7px;
  }
  .secnav .secnav__top.is-in {
    width: 36px;
    margin-left: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .secnav {
    transition: opacity 0.2s linear;
  }
  .secnav button:hover {
    transform: none;
  }
  .secnav .secnav__top {
    transition: opacity 0.2s linear;
    transform: none;
  }
  .secnav .secnav__top.is-in {
    transform: none;
  }
}

/* ---------- Back to top (standalone) ----------
   Used by the policy pages, which have no section navigator. On the main
   pages the same button lives inside the pill instead - see .secnav__top.
   Slides up from below the fold once the visitor is a screen or so down. */
.totop {
  position: fixed;
  right: clamp(16px, 3vw, 28px);
  bottom: clamp(16px, 3vw, 28px);
  z-index: 860;
  display: grid;
  place-items: center;
  width: 46px;
  aspect-ratio: var(--hex-ratio); /* the hexagon used throughout the page */
  clip-path: var(--hex);
  padding: 0;
  border: 0;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
  opacity: 0;
  transform: translateY(120%);
  pointer-events: none;
  transition:
    opacity 0.35s var(--ease-out),
    transform 0.5s var(--spring),
    background 0.3s var(--ease-out);
}
.totop.is-in {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.totop:hover {
  background: var(--accent-lift);
}
/* clip-path crops an outline, so focus is shown as a ring drawn inside. */
.totop:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 3px var(--on-accent);
}
.totop svg {
  width: 18px;
  height: 18px;
}
@media (prefers-reduced-motion: reduce) {
  .totop {
    transition: opacity 0.2s linear;
    transform: none;
  }
  .totop.is-in {
    transform: none;
  }
}

/* ---------- Theme switch ---------- */
.themesw {
  display: grid;
  place-items: center;
  width: 34px;
  aspect-ratio: var(--hex-ratio); /* same hexagon as the card icons */
  clip-path: var(--hex);
  padding: 0;
  border: 0;
  background: rgba(var(--ink), 0.06);
  color: var(--fg-dim);
  cursor: pointer;
  transition:
    background 0.3s var(--ease-out),
    color 0.3s var(--ease-out);
}
.themesw:hover {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--accent);
}
/* clip-path crops any outline, so focus is shown as a ring of colour instead. */
.themesw:focus-visible {
  outline: none;
  background: rgba(var(--accent-rgb), 0.28);
  color: var(--accent);
}
.themesw svg {
  width: 16px;
  height: 16px;
  grid-area: 1 / 1;
}
/* Each icon shows the theme you would switch *to*, so the moon is the one on
   a light page. Rotation makes the swap read as one movement. */
.themesw__sun {
  opacity: 0;
  transform: rotate(-70deg) scale(0.6);
}
.themesw__moon {
  opacity: 1;
  transform: none;
}
:root[data-theme="light"] .themesw__sun {
  opacity: 1;
  transform: none;
}
:root[data-theme="light"] .themesw__moon {
  opacity: 0;
  transform: rotate(70deg) scale(0.6);
}
.themesw svg {
  transition:
    opacity 0.28s var(--ease-out),
    transform 0.4s var(--spring);
}

/* ---------- Mobile navigation ----------
   Below 860px the section links move into a panel behind a burger; EN/PL and
   LinkedIn stay in the bar, because they are one tap and carry no wayfinding.
   The button is hidden above that width, so the desktop bar is untouched. */
.burger {
  display: none;
  position: relative;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--panel);
  cursor: pointer;
  transition:
    border-color 0.3s var(--ease-out),
    background 0.3s var(--ease-out);
}
.burger:hover {
  border-color: rgba(var(--accent-rgb), 0.4);
}
.burger:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Two bars that cross into an X. Positioned from the centre so the rotation
   pivots on the middle of the button rather than drifting. */
.burger__bar {
  position: absolute;
  left: 50%;
  width: 16px;
  height: 1.5px;
  margin-left: -8px;
  border-radius: 2px;
  background: var(--fg);
  transition:
    transform 0.34s var(--spring),
    opacity 0.2s linear;
}
.burger__bar:nth-child(1) {
  top: 17px;
}
.burger__bar:nth-child(2) {
  top: 22px;
}
.burger.is-open .burger__bar:nth-child(1) {
  transform: translateY(2.5px) rotate(45deg);
}
.burger.is-open .burger__bar:nth-child(2) {
  transform: translateY(-2.5px) rotate(-45deg);
}
.burger.is-open {
  background: rgba(var(--accent-rgb), 0.1);
  border-color: rgba(var(--accent-rgb), 0.4);
}

.navpanel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 880; /* under the header (900) so the burger stays clickable */
  padding: 84px 0 22px;
  background: var(--bg-panel);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-bottom: 1px solid var(--line-soft);
  transform: translateY(-100%);
  transition: transform 0.42s var(--ease-out);
}
.navpanel.is-open {
  transform: translateY(0);
}
.navpanel__in {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.navpanel__link {
  padding: 13px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 17px;
  font-weight: 560;
  letter-spacing: -0.015em;
  color: var(--fg);
  transition:
    color 0.25s var(--ease-out),
    transform 0.3s var(--ease-out);
}
.navpanel__link:hover {
  color: var(--accent);
  transform: translateX(3px);
}
.navpanel__cta {
  margin-top: 18px;
  justify-content: center;
}

.navscrim {
  position: fixed;
  inset: 0;
  z-index: 870;
  background: var(--scrim);
  opacity: 0;
  transition: opacity 0.35s var(--ease-out);
}
.navscrim.is-in {
  opacity: 1;
}

@media (max-width: 860px) {
  .burger {
    display: block;
  }
  /* Section links and the contact button live in the panel at this width.
     .nav__cta needs the .btn qualifier: .btn sets display:inline-flex with
     equal specificity, and wins on source order otherwise. */
  .nav__link {
    display: none;
  }
  .btn.nav__cta {
    display: none;
  }
}
@media (min-width: 861px) {
  /* Never leave the panel on screen if the viewport grows while it is open. */
  .navpanel,
  .navscrim {
    display: none !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .navpanel {
    transition: none;
  }
  .navpanel__link:hover {
    transform: none;
  }
}

.nav__icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line-soft);
  color: var(--fg-dim);
  transition:
    color 0.25s,
    border-color 0.25s,
    background 0.25s,
    transform 0.3s var(--ease-out);
}
.nav__icon svg {
  width: 15px;
  height: 15px;
}
.nav__icon:hover {
  color: var(--fg);
  border-color: rgba(var(--accent-rgb), 0.45);
  background: rgba(var(--accent-rgb), 0.1);
  transform: translateY(-2px);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 13px 22px;
  border: 1px solid transparent;
  border-radius: 100px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background 0.3s,
    border-color 0.3s,
    color 0.3s,
    transform 0.3s var(--ease-out),
    box-shadow 0.3s var(--ease-out);
}
.btn--solid {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 650;
}
.btn--solid:hover {
  background: var(--accent-lift);
  transform: translateY(-3px) scale(1.03);
  box-shadow:
    0 18px 38px -12px var(--accent-glow),
    0 0 0 4px rgba(var(--accent-rgb), 0.14);
}
.btn--ghost {
  border-color: var(--line);
  color: var(--fg);
}
.btn--ghost:hover {
  background: rgba(var(--accent-rgb), 0.08);
  border-color: rgba(var(--accent-rgb), 0.45);
  color: var(--accent);
  transform: translateY(-3px);
}
.btn--sm {
  padding: 10px 18px;
  font-size: 13px;
}
.btn svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  transition: transform 0.35s var(--spring);
}
/* Arrow nudges forward on hover; the icon is the only thing that moves, so it
   reads as direction rather than the whole button wobbling. */
.btn:hover svg {
  transform: translateX(3px);
}
.btn:active {
  transform: translateY(0) scale(0.98);
}

/* Sheen that sweeps across a solid button on hover. */
.btn--solid {
  position: relative;
  overflow: hidden;
}
.btn--solid::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(
    100deg,
    transparent,
    rgba(var(--ink), 0.5),
    transparent
  );
  transform: skewX(-18deg);
  opacity: 0;
  pointer-events: none;
}
.btn--solid:hover::after {
  animation: sheen 0.75s var(--ease-out);
}
@keyframes sheen {
  0% {
    left: -60%;
    opacity: 0.9;
  }
  100% {
    left: 115%;
    opacity: 0;
  }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: 140px 90px;
  overflow: hidden;
}
/* Canvas particle field behind the copy. If canvas or JS is unavailable it
   stays blank and the layout is unaffected. */
.hero__canvas {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  /* 60% of the previous 0.85. Dialled back here rather than in the per-line
     alphas so the lattice keeps its internal contrast (lit cells still read
     brighter than unlit ones) and only the whole layer recedes. */
  opacity: 0.51;
}
/* The hero used to cap itself at 880px, which read as a different layout from
   every section below it. There is deliberately no max-width here now: the
   element already carries .shell, so it inherits the same 1120px grid and the
   same gutters as the rest of the page. Setting one here would override that. */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 15px 7px 11px;
  margin-bottom: 30px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--panel);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--fg-mid);
}
.eyebrow__pulse {
  position: relative;
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #35d07f;
}
.eyebrow__pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: #35d07f;
  animation: pulse 2.4s var(--ease-out) infinite;
}
@keyframes pulse {
  0% {
    opacity: 0.7;
    transform: scale(1);
  }
  70% {
    opacity: 0;
    transform: scale(3.2);
  }
  100% {
    opacity: 0;
    transform: scale(3.2);
  }
}

.hero__title {
  /* The cap is set by the longest line - "Twój następny etat" in Polish, at 18
     characters - which must not wrap inside its overflow-hidden strip. The
     lines are short enough now to carry a larger size than the shell strictly
     allows for longer copy; check this again if the headline ever changes. */
  font-size: clamp(2.5rem, 7.6vw, 5.9rem);
  line-height: 1.03;
  font-weight: 660;
  letter-spacing: -0.038em;
  color: var(--fg);
  margin-bottom: 26px;
}
/* Gradient is kept shallow so the highlighted words stay unmistakably teal;
   a full teal->blue sweep made a wrapped second line read as a different
   colour entirely. */
/* The gradient is wider than the text and slides, so the accent words shimmer
   slowly instead of sitting flat. background-clip:text means the animation
   costs one paint and no layout. */
.hero__title em {
  font-style: normal;
  /* The shimmer's bright stop is a token: #6ee7ff glows on near-black but is
     unreadable on a light ground. */
  background: linear-gradient(
    96deg,
    var(--accent) 10%,
    var(--accent-lift) 40%,
    var(--accent) 75%
  );
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: shimmer 7s ease-in-out infinite;
}
@keyframes shimmer {
  0%,
  100% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
}

.hero__lead {
  font-size: clamp(1rem, 2.1vw, 1.16rem);
  line-height: 1.72;
  /* Body copy stays at a readable measure even though its parent is now the
     full page width. */
  max-width: 640px;
  margin-bottom: 40px;
}

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 62px;
}
/* The phone button shows the number beside its label: on a desktop there is
   nothing to tap, so the number has to be readable. It is filled in by JS, so
   the separator lives on the span and disappears with it when JS never runs. */
.btn__num {
  color: var(--fg-dim);
  font-weight: 500;
}
.btn__num:not(:empty)::before {
  content: "\00b7";
  margin-right: 7px;
  opacity: 0.55;
}
.btn--phone:hover .btn__num {
  color: inherit;
}
/* Below this the row of four buttons needs the space back, and a phone can
   dial from the label alone. */
@media (max-width: 700px) {
  .btn__num {
    display: none;
  }
}

/* Buttons carry a full and a short label; CSS picks one so the swap survives a
   resize and does not depend on JS having run. .btn__short is hidden rather
   than absent so screen readers never read both. */
.btn__short {
  display: none;
}
@media (max-width: 560px) {
  .btn__full {
    display: none;
  }
  .btn__short {
    display: inline;
  }
}

.hero__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px 34px;
  /* Full width: the rule along its top edge now aligns with the section
     borders further down the page. */
  max-width: 100%;
  padding-top: 30px;
  /* Its own colour rather than --line-soft: this rule sits on the hero's
     darkest area with no section boundary behind it to help, and at 5.5% white
     it read as nothing at all. Brighter and 2px, so it registers as a
     deliberate divider under the buttons. */
  border-top: 2px solid rgba(var(--ink), 0.16);
}
.stat {
  transition: transform 0.4s var(--spring);
}
.stat:hover {
  transform: translateY(-3px);
}
.stat__num {
  font-size: 25px;
  font-weight: 640;
  letter-spacing: -0.03em;
  color: var(--fg);
  line-height: 1.2;
  transition: color 0.35s var(--ease-out);
}
.stat:hover .stat__num {
  color: var(--accent);
}
.stat__label {
  font-size: 12.5px;
  color: var(--fg-dim);
  line-height: 1.45;
}
/* The one inline link in a stat label - underlined so it does not read as
   plain text, since the global reset strips link styling. */
.stat__label a {
  color: var(--fg-mid);
  text-decoration: underline;
  text-decoration-color: rgba(var(--accent-rgb), 0.45);
  text-underline-offset: 3px;
  transition:
    color 0.3s var(--ease-out),
    text-decoration-color 0.3s var(--ease-out);
}
.stat__label a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* ---------- Section furniture ---------- */
.section {
  position: relative;
  z-index: 1;
  padding-block: clamp(80px, 11vw, 128px);
}
.section--line {
  border-top: 1px solid var(--line-soft);
}

.section__head {
  max-width: 700px;
  margin-bottom: 58px;
}
.section__tag {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Rule that draws itself outward once the heading is revealed. */
.section__tag::after {
  content: "";
  flex: 1;
  max-width: 90px;
  height: 1px;
  background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.6), transparent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.9s var(--ease-out) 0.25s;
}
.js .reveal.is-in .section__tag::after,
.js .section__head:not(.reveal) .section__tag::after {
  transform: scaleX(1);
}
.section__title {
  font-size: clamp(1.85rem, 4.3vw, 2.85rem);
  line-height: 1.13;
  font-weight: 640;
  letter-spacing: -0.032em;
  color: var(--fg);
  margin-bottom: 18px;
}
.section__sub {
  font-size: 16.5px;
  line-height: 1.7;
  max-width: 580px;
}

/* ---------- Services ---------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
  gap: 18px;
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 32px 30px 34px;
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  background: var(--panel);
  overflow: hidden;
  transition:
    border-color 0.4s var(--ease-out),
    transform 0.4s var(--ease-out),
    background 0.4s var(--ease-out);
}
/* Cursor-tracked highlight: JS writes --mx/--my as percentages on hover. */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(
    340px circle at var(--mx, 50%) var(--my, 0%),
    rgba(var(--accent-rgb), 0.039),
    transparent 68%
  );
  transition: opacity 0.4s var(--ease-out);
}
/* --rx/--ry are written by main.js from the pointer position. They default to
   0deg so a card that has never been hovered is perfectly flat, and they are
   only read here (not in .reveal) so the entry animation is never fought over. */
.card:hover {
  border-color: rgba(var(--accent-rgb), 0.38);
  background: rgba(var(--ink), 0.05);
  transform: perspective(900px) rotateX(var(--rx, 0deg))
    rotateY(var(--ry, 0deg)) translateY(-8px) scale(1.015);
  box-shadow:
    0 30px 60px -28px rgba(var(--shadow), var(--shadow-a)),
    0 0 0 1px rgba(var(--accent-rgb), 0.08);
}

/* Accent line that draws across the top edge of a hovered card. */
.card::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  transform: scaleX(0);
  transition: transform 0.55s var(--ease-out);
}
.card:hover::after {
  transform: scaleX(1);
}
/* Snappier while tracking the cursor, slower on the way back to flat. */
.card {
  transition-duration: 0.5s;
}
.card:hover {
  transition-duration: 0.18s;
}
.card:hover::before {
  opacity: 1;
}

/* Icon lifts, rotates a touch and picks up a glow, so the hexagon reads as the
   focal point of the hovered card. */
.card__icon {
  transition:
    transform 0.5s var(--spring),
    filter 0.45s var(--ease-out);
}
.card:hover .card__icon {
  transform: translateY(-3px) scale(1.1) rotate(-6deg);
  filter: drop-shadow(0 6px 16px rgba(var(--accent-rgb), 0.35));
}
.card__icon::before {
  transition: background 0.4s var(--ease-out);
}
.card:hover .hexline {
  color: rgba(var(--accent-rgb), 0.75);
}
.hexline {
  transition: color 0.4s var(--ease-out);
}

/* Title picks up the accent on hover to confirm the card is one unit. */
.card__title {
  transition: color 0.35s var(--ease-out);
}
.card:hover .card__title {
  color: var(--accent);
}

/* Tags stagger up very slightly, which reads as the card "waking up". */
.card__tags .tag {
  transition:
    border-color 0.35s var(--ease-out),
    color 0.35s var(--ease-out),
    transform 0.4s var(--spring);
}
.card:hover .tag {
  border-color: rgba(var(--accent-rgb), 0.28);
  color: var(--fg-mid);
}
.card:hover .tag:nth-child(1) {
  transform: translateY(-2px);
  transition-delay: 0.02s;
}
.card:hover .tag:nth-child(2) {
  transform: translateY(-2px);
  transition-delay: 0.06s;
}
.card:hover .tag:nth-child(3) {
  transform: translateY(-2px);
  transition-delay: 0.1s;
}
.card:hover .tag:nth-child(4) {
  transform: translateY(-2px);
  transition-delay: 0.14s;
}

/* Hexagon tile. The fill is a clipped pseudo-element and the teal edge is an
   inline SVG outline (.hexline) - clip-path cannot carry a border. */
.card__icon {
  position: relative;
  width: 48px;
  aspect-ratio: var(--hex-ratio); /* 48 x 41.6 - keeps the hexagon regular */
  margin-bottom: 22px;
  display: grid;
  place-items: center;
  color: var(--accent);
}
.card__icon::before {
  content: "";
  position: absolute;
  inset: 0;
  clip-path: var(--hex);
  background: rgba(var(--accent-rgb), 0.12);
  transition: background 0.4s var(--ease-out);
}
.card:hover .card__icon::before {
  background: rgba(var(--accent-rgb), 0.2);
}

/* The stroked hexagon that sits on top of the fill. */
.hexline {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  color: rgba(var(--accent-rgb), 0.4);
}
.card__icon > svg:not(.hexline) {
  position: relative;
  width: 20px;
  height: 20px;
}

.card__title {
  position: relative;
  font-size: 18px;
  font-weight: 630;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--fg);
  margin-bottom: 11px;
  text-wrap: balance;
}
.card__text {
  position: relative;
  font-size: 14.5px;
  line-height: 1.7;
  margin-bottom: 22px;
}

.card__tags {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: auto;
}
.tag {
  padding: 5px 11px;
  border: 1px solid var(--line-soft);
  border-radius: 100px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-dim);
}

/* Explicit 2x2 rather than auto-fit: four cards of equal weight read better as
   a square block than a thin four-across strip, and auto-fit stranded the last
   card on its own row at some widths. Collapses to a single column on phones. */
@media (min-width: 640px) {
  .services__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
  }
}
@media (max-width: 639px) {
  .services__grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Process ---------- */
.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 20px;
}
.step {
  position: relative;
  padding-top: 26px;
  border-top: 1px solid var(--line);
}
/* The accent is a separate line drawn over the border, growing from the
   hexagon outwards rather than the whole rule changing colour at once.
   ::before is already the hexagon node, so this rides on ::after. */
.step::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(var(--accent-rgb), 0.65);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease-out);
}
.step:hover::after {
  transform: scaleX(1);
}
/* Node grows and the title shifts right, so the whole column responds. */
.step:hover::before {
  transform: scale(1.35);
}
.step__title,
.step__num {
  transition:
    color 0.35s var(--ease-out),
    transform 0.4s var(--spring);
}
.step:hover .step__title {
  color: var(--accent);
  transform: translateX(3px);
}
.step:hover .step__num {
  transform: translateX(3px);
}
/* Hexagon node sitting on the rule above each step. */
.step::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 12px;
  aspect-ratio: var(--hex-ratio); /* 12 x 10.4 */
  clip-path: var(--hex);
  background: var(--accent);
  transition: transform 0.4s var(--spring);
}
.step__num {
  display: block;
  margin-bottom: 12px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  color: var(--accent);
}
.step__title {
  font-size: 16px;
  font-weight: 620;
  color: var(--fg);
  margin-bottom: 8px;
  letter-spacing: -0.015em;
}
.step__text {
  font-size: 14px;
  line-height: 1.65;
}

/* ---------- Track record ---------- */
.track {
  margin-top: 26px;
}
.track__head {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 22px;
}
.track__list {
  display: grid;
  /* Four items, so only 1, 2 and 4 columns divide evenly - auto-fit picked 3
     at desktop width and left the fourth stranded on its own row. */
  grid-template-columns: 1fr;
  gap: 26px 30px;
  list-style: none;
}
@media (min-width: 560px) {
  .track__list {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 1000px) {
  .track__list {
    grid-template-columns: repeat(4, 1fr);
  }
}
.track__item {
  position: relative; /* anchors the accent line in ::after */
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.track__item::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(var(--accent-rgb), 0.65);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease-out);
}
.track__item:hover::after {
  transform: scaleX(1);
}
.track__num {
  font-size: 22px;
  font-weight: 640;
  letter-spacing: -0.028em;
  color: var(--accent);
  margin-bottom: 10px;
  line-height: 1.25;
}
/* The unit rides along on the same line at body weight so the figure keeps
   all of the emphasis. */
.track__unit {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-top: 5px;
}
.track__text {
  font-size: 14px;
  line-height: 1.65;
}
.track__text strong {
  color: var(--fg);
  font-weight: 600;
}

/* ---------- Language switcher ---------- */
.langsw {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: var(--panel);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
}
.langsw__on,
.langsw__off {
  display: block;
  padding: 4px 9px;
  border-radius: 100px;
  transition:
    background 0.3s var(--ease-out),
    color 0.3s var(--ease-out);
}
.langsw__on {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--accent);
}
.langsw__off {
  color: var(--fg-dim);
}
.langsw__off:hover {
  background: rgba(var(--ink), 0.07);
  color: var(--fg);
}
.langsw__off:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Worked example ----------
   The stage list reuses .step for its rule-and-hexagon look; only the grid,
   the AUTO/MANUAL badge and the before/after table are new here. */
.flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  /* Generous row gap: each stage draws its own rule and hexagon node, and a
     tight gap made row two look like it belonged to the card above it. */
  gap: clamp(34px, 4vw, 48px) 30px;
  margin-bottom: clamp(46px, 6vw, 68px);
  list-style: none;
}
.flow__step {
  display: flex;
  flex-direction: column;
}
/* Badge sits on the bottom edge of the column so the six cards line up even
   when their descriptions run to different lengths. */
.flow__badge {
  align-self: flex-start;
  margin-top: 14px;
  padding: 4px 10px;
  border: 1px solid rgba(var(--accent-rgb), 0.35);
  border-radius: 100px;
  background: rgba(var(--accent-rgb), 0.09);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  transition:
    border-color 0.35s var(--ease-out),
    background 0.35s var(--ease-out);
}
/* Steps needing a human decision are deliberately not teal - the point of the
   section is that almost everything else is. */
.flow__badge--manual {
  border-color: var(--line);
  background: rgba(var(--ink), 0.04);
  color: var(--fg-mid);
}
.flow__step:hover .flow__badge {
  border-color: rgba(var(--accent-rgb), 0.55);
}
.flow__step p {
  margin-bottom: auto;
}

.ba {
  padding: clamp(26px, 4vw, 44px);
  border: 1px solid var(--line-soft);
  border-radius: 24px;
  background:
    radial-gradient(
      560px circle at 100% 0%,
      rgba(var(--accent-rgb), 0.07),
      transparent 62%
    ),
    var(--panel);
}
.ba__head {
  margin-bottom: 26px;
}
.ba__title {
  font-size: 19px;
  font-weight: 630;
  letter-spacing: -0.02em;
  color: var(--fg);
  margin-bottom: 8px;
}
.ba__sub {
  font-size: 14.5px;
  line-height: 1.65;
  max-width: 520px;
}

/* The two figure columns must never wrap mid-range, so the table has a floor
   width and scrolls inside its own box rather than squashing the page. The
   floor is kept low enough that the "after" column - the whole point of the
   table - still lands on screen on a phone without scrolling. */
.ba__scroll {
  overflow-x: auto;
  margin: 0 -4px;
  padding: 0 4px;
}
.ba__table {
  width: 100%;
  min-width: 400px;
  border-collapse: collapse;
  text-align: left;
}
.ba__table thead th {
  padding: 0 14px 12px 0;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.ba__table tbody th {
  padding: 13px 14px 13px 0;
  font-size: 14.5px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--fg-mid);
}
.ba__table td {
  padding: 13px 14px 13px 0;
  font-size: 14px;
  line-height: 1.5;
  white-space: nowrap;
}
.ba__table tbody tr {
  border-bottom: 1px solid var(--line-soft);
}
.ba__table tbody tr:last-child {
  border-bottom: 0;
}
.ba__table tbody tr {
  transition: background 0.3s var(--ease-out);
}
.ba__table tbody tr:hover {
  background: rgba(var(--ink), 0.028);
}
.ba__table tbody tr:hover th {
  color: var(--fg);
}
/* Struck through rather than dimmed: the whole argument of the table is that
   these rows stop existing. */
.ba__before {
  color: var(--fg-dim);
  text-decoration: line-through;
  text-decoration-color: rgba(var(--ink), 0.25);
}
.ba__after {
  color: var(--accent);
  font-weight: 560;
}
.ba__after--gone {
  color: rgba(var(--accent-rgb), 0.62);
  font-style: italic;
  font-weight: 500;
}

.result {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 20px 30px;
  margin-top: 32px;
  padding-top: 28px;
  border-top: 1px solid var(--line);
}
/* Label above number here, unlike the hero, because the labels are the
   question and the figures are the answer. */
.result .stat__label {
  margin-bottom: 6px;
}
.result .stat__num {
  font-size: clamp(1.35rem, 3vw, 1.75rem);
  color: var(--accent);
}
.result .stat:hover .stat__num {
  color: var(--accent-lift);
}

.ba__note {
  margin-top: 24px;
  font-size: 13px;
  line-height: 1.65;
  color: var(--fg-dim);
  max-width: 640px;
}

@media (max-width: 520px) {
  /* Task labels wrap to two lines here; that is cheaper than a sideways
     scroll that hides the "after" column. */
  .ba__table {
    min-width: 300px;
  }
  .ba__table thead th,
  .ba__table tbody th,
  .ba__table td {
    padding-right: 9px;
  }
  .ba__table tbody th {
    font-size: 13.5px;
  }
  .ba__table td {
    font-size: 13px;
  }
}

/* ---------- Stack ---------- */
.stackset {
  display: grid;
  gap: 34px;
}
/* Label sits in its own column on wide screens and stacks above the chips
   when there is no room for it. */
.stackrow {
  display: grid;
  grid-template-columns: 168px 1fr;
  gap: 10px 28px;
}
.stackrow__label {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-dim);
  padding-top: 9px;
}
@media (max-width: 700px) {
  .stackrow {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .stackrow__label {
    padding-top: 0;
  }
}

.stack {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
}
.stack__item {
  padding: 9px 16px;
  border: 1px solid var(--line-soft);
  border-radius: 100px;
  background: var(--panel);
  font-size: 13.5px;
  color: var(--fg-mid);
  transition:
    border-color 0.3s,
    color 0.3s,
    transform 0.3s var(--ease-out);
}
.stack__item {
  transition:
    border-color 0.3s,
    color 0.3s,
    background 0.3s,
    transform 0.38s var(--spring);
}
.stack__item:hover {
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.08);
  color: var(--fg);
  transform: translateY(-3px);
}

/* ---------- Founder ----------

   PORTRAIT TUNING - the two knobs for the hexagon portrait live here.

     --portrait-size   how big the whole thing is (frame + photo + caption).
                       Width only: the height follows from aspect-ratio, so
                       the hexagon keeps its shape. The grid column below has
                       to be given the same number.

     --portrait-drop   how far the photo sits INSIDE the frame, in pixels.
                       Larger = lower. The frame does not move.
   ---------------------------------- */
:root {
  --portrait-size: 180px;
  --portrait-drop: 8px;
}

.founder {
  display: grid;
  grid-template-columns: var(--portrait-size) 1fr;
  gap: 44px;
  align-items: start;
  padding: clamp(30px, 4vw, 46px);
  border: 1px solid var(--line-soft);
  border-radius: 24px;
  background:
    radial-gradient(
      520px circle at 0% 0%,
      rgba(var(--accent-rgb), 0.08),
      transparent 60%
    ),
    var(--panel);
}
@media (max-width: 800px) {
  .founder {
    grid-template-columns: 1fr;
    gap: 26px;
  }
  /* Stacked layout: the portrait is alone on its row, so it is centred with
     its caption instead of sitting against the left edge. */
  /* width:100% so align-items:center does not shrink the figure to its
     content - without it the hexagon came out ~50px narrower on phones. */
  .founder__figure {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
  }
}

/* Nudged down so the portrait sits level with the quotation beside it rather
   than against the top of the card. */
.founder__figure {
  /* margin-inline:auto rather than 0: the stacked layout centres the portrait,
     and a plain `margin: 12px 0 0` here overrode that and pinned it left. */
  margin: 18px auto 0;
  /* The hexagon's width lives here rather than on .founder__hex so the caption
     below is centred on the SHAPE. While the grid track stayed wider than the
     hexagon, centred text sat about 11px right of the portrait.

     width as well as max-width: as a grid item this figure was sizing to its
     content (the caption text) and came out ~175px, so raising max-width alone
     changed nothing on screen. */
  width: 100%;
  max-width: var(--portrait-size);
}

/* The photo is clipped to a hexagon. clip-path cannot be stroked, so the
   teal edge is a slightly larger hexagon sitting behind it - the 2px inset on
   the image is what shows through as the border. */
.founder__hex {
  position: relative;
  width: 100%;
  aspect-ratio: var(--hex-ratio); /* required for the hexagon to stay regular */
}
/* The teal edge is a stroked outline, not a filled hexagon: the portrait is a
   cut-out with a transparent background, so a filled shape here would show
   through it as a teal block instead of the card behind.

   The gradient is masked by a stroked-hexagon SVG - the same path and viewBox
   as the .hexline outlines used elsewhere in this file - so only the 1.4px
   stroke is painted and the middle stays genuinely empty. A mask is used
   rather than two stacked hexagons because the card behind is a translucent
   gradient, so there is no flat colour an inner hexagon could match. */
.founder__hex::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    150deg,
    rgba(var(--accent-rgb), 0.85),
    rgba(var(--accent-rgb), 0.25)
  );
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 41.57' fill='none' preserveAspectRatio='none'%3E%3Cpath d='M0.7 20.78 12.35 0.61H35.65L47.3 20.78 35.65 40.96H12.35L0.7 20.78Z' stroke='%23000' stroke-width='1.4' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% 100% no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 41.57' fill='none' preserveAspectRatio='none'%3E%3Cpath d='M0.7 20.78 12.35 0.61H35.65L47.3 20.78 35.65 40.96H12.35L0.7 20.78Z' stroke='%23000' stroke-width='1.4' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% 100% no-repeat;
  transition: opacity 0.45s var(--ease-out);
}
/* Slow rotation of the gradient edge while hovered, so the portrait feels
   alive without the image itself moving. */
.founder__hex {
  transition: transform 0.55s var(--spring);
}
.founder__hex:hover {
  transform: translateY(-4px);
}
.founder__photo {
  transition: filter 0.5s var(--ease-out);
}
.founder__hex:hover .founder__photo {
  filter: saturate(1.15) contrast(1.05);
}
/* Fills the hexagon exactly - the teal edge is a stroke drawn on top of the
   same shape, so no inset is needed to reveal it. No background of its own:
   the portrait is a transparent cut-out and the card behind is what shows
   around the figure. */
.founder__photo {
  position: absolute;
  /* Given room ABOVE the hexagon's box - without it the clip can be open at
     the top all it likes, because there are no pixels up there to show, so the
     hair has nowhere to go. The box grows upward ONLY: the bottom is inset by
     the stroke's own width so the portrait finishes inside the outline rather
     than sitting on top of it and hiding the bottom edge. */
  top: calc(-14% + var(--portrait-drop));
  right: 0;
  bottom: calc(-1 * var(--portrait-drop));
  left: 0;
  width: 100%;
  height: 114%;
  /* The hexagon that crops the portrait, offset from the one the outline
     draws in two ways:

     - open at the TOP: the upper corners sit far above the box (-60%) so the
       hair runs past the edge instead of being sliced flat by it;
     - short at the BOTTOM: this box is 114% of the outline's height, so the
       same percentages land higher up the page and the portrait's lower point
       finishes just inside the outline instead of on top of it, leaving the
       bottom two edges of the frame visible in front of the shoulders. */
  clip-path: polygon(25% -60%, 75% -60%, 97% 47%, 73% 92%, 27% 92%, 3% 47%);
  /* `cover` on a box wider than it is tall already crops the avatar's circular
     edge past the hexagon's corners, so no extra scaling is needed - an earlier
     scale() here overflowed the 2px inset and swallowed the teal border. */
  object-fit: cover;
  /* Centred, no vertical nudge. If this ever needs adjusting, pan it with
     object-position and never a transform: the clip-path travels with the
     element, so translating it drags the hexagon off the teal edge behind. */
  object-position: 50% 50%;
}
.founder__caption {
  margin-top: 14px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg-dim);
  /* Centred under the portrait rather than flush left: the hexagon is the
     widest thing in this column, so left-aligned text hangs off its point. */
  text-align: center;
}
.founder__caption strong {
  display: block;
  color: var(--fg);
  font-weight: 620;
  font-size: 14.5px;
}

.founder__quote {
  font-size: clamp(1.05rem, 2.2vw, 1.25rem);
  line-height: 1.6;
  letter-spacing: -0.015em;
  color: var(--fg);
  margin-bottom: 20px;
}
.founder__text {
  font-size: 15.5px;
  line-height: 1.72;
  margin-bottom: 14px;
}
.founder__list {
  margin-top: 22px;
  display: grid;
  gap: 11px;
}
.founder__list li {
  position: relative;
  padding-left: 26px;
  font-size: 14.5px;
  line-height: 1.6;
  transition:
    color 0.3s var(--ease-out),
    transform 0.4s var(--spring);
}
.founder__list li:hover {
  color: var(--fg);
  transform: translateX(4px);
}
/* Teal check, drawn in CSS so there is no icon request per row. */
.founder__list li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 7px;
  width: 6px;
  height: 10px;
  border: solid var(--accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ---------- CTA ---------- */
.cta {
  position: relative;
  padding: clamp(42px, 6vw, 68px) clamp(26px, 5vw, 60px);
  border: 1px solid var(--line);
  border-radius: 26px;
  background: rgba(var(--ink), 0.026);
  overflow: hidden;
  transition:
    border-color 0.5s var(--ease-out),
    transform 0.5s var(--spring),
    box-shadow 0.5s var(--ease-out);
}
/* The ambient glow follows the pointer (--cx/--cy written by main.js) and
   parks at a fixed corner until the pointer arrives, so the box looks
   deliberate before anyone touches it. */
.cta::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(
    560px circle at var(--cx, 82%) var(--cy, 0%),
    rgba(var(--accent-rgb), 0.06),
    transparent 62%
  );
  transition: opacity 0.5s var(--ease-out);
}
/* A second, tighter highlight that only appears on hover. */
.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(
    260px circle at var(--cx, 50%) var(--cy, 50%),
    rgba(var(--accent-rgb), 0.048),
    transparent 70%
  );
  transition: opacity 0.45s var(--ease-out);
}
.cta:hover {
  border-color: rgba(var(--accent-rgb), 0.4);
  transform: translateY(-4px);
  box-shadow: 0 30px 60px -34px rgba(var(--shadow), var(--shadow-a));
}
.cta:hover::after {
  opacity: 1;
}

/* Animated border sheen: a rotating conic gradient masked to the 1px edge.
   Uses its own layer so the box content is never repainted by the spin. */
.cta__edge {
  position: absolute;
  inset: -1px;
  border-radius: 27px;
  padding: 1px;
  opacity: 0;
  pointer-events: none;
  background: conic-gradient(
    from var(--angle, 0deg),
    transparent 0deg,
    rgba(var(--accent-rgb), 0.85) 35deg,
    transparent 90deg,
    transparent 360deg
  );
  /* Mask keeps only the padding ring, leaving the middle transparent. */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  transition: opacity 0.5s var(--ease-out);
}
.cta:hover .cta__edge {
  opacity: 1;
  animation: spin-edge 4s linear infinite;
}
@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
@keyframes spin-edge {
  to {
    --angle: 360deg;
  }
}

.cta__in {
  position: relative;
}
.cta__title {
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  line-height: 1.14;
  font-weight: 640;
  letter-spacing: -0.032em;
  color: var(--fg);
  margin-bottom: 16px;
}
.cta__text {
  font-size: 16px;
  line-height: 1.7;
  margin-bottom: 32px;
  /* Only the prose is capped: the heading and the button row are free to use
     the full width of the panel. */
  max-width: 660px;
}
.cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.cta__note {
  font-size: 13px;
  color: var(--fg-dim);
}

/* ---------- Footer ---------- */
.footer {
  position: relative;
  z-index: 1;
  padding-block: 64px 30px;
  border-top: 1px solid var(--line-soft);
}
/* Brand block sits in a wider first column than the two link columns: it
   carries a sentence, they carry single words. */
.footer__top {
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
  padding-bottom: 40px;
}
@media (min-width: 760px) {
  /* The link columns are pulled in from the right edge: at 1fr they sat hard
     against the shell boundary and the whole footer read as two islands. */
  .footer__top {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 56px;
  }
  .footer__nav {
    grid-template-columns: repeat(3, minmax(130px, auto));
    gap: 44px;
  }
}

.footer__brand {
  max-width: 420px;
}
.footer__logo {
  display: inline-block;
  margin-bottom: 14px;
}
.footer__pitch {
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--fg-dim);
}

/* Three columns on anything but the narrowest screens; two below that, where
   a third would squeeze the labels into wrapping. */
.footer__nav {
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px 24px;
}
@media (min-width: 420px) {
  .footer__nav {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.footer__col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 11px;
}
.footer__head {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: 3px;
}
.footer__col a,
.footer__col button {
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  font: inherit;
  font-size: 13.5px;
  color: var(--fg-mid);
  cursor: pointer;
  transition:
    color 0.25s var(--ease-out),
    transform 0.3s var(--ease-out);
}
.footer__col a:hover,
.footer__col button:hover {
  color: var(--accent);
  transform: translateX(2px);
}

/* Legal strip: quieter than everything above it, separated by its own rule. */
.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px 28px;
  padding-top: 26px;
  border-top: 1px solid var(--line-soft);
}
/* On the legal pages the bar is the whole footer, so it carries no rule of
   its own and lays its two links out in a row. */
.footer__bar--lone {
  padding-top: 0;
  border-top: 0;
}
.footer__col--inline {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 20px;
}
.footer__col--inline a {
  font-size: 12.5px;
  color: var(--fg-dim);
}

.footer__meta {
  font-size: 12.5px;
  color: var(--fg-dim);
}
.footer__ids {
  font-size: 12.5px;
  color: var(--fg-dim);
}
/* Each identifier keeps its label and number together when the line wraps. */
.footer__id {
  white-space: nowrap;
  margin-left: 14px;
}
@media (max-width: 620px) {
  .footer__ids {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
  }
  .footer__id {
    margin-left: 0;
  }
}

/* ============================================================
   Cookie consent - adapted from the manico3 implementation
   ============================================================ */
.cookie-scrim {
  position: fixed;
  inset: 0;
  z-index: 9994;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}
.cookie-scrim[hidden] {
  display: none;
}
.cookie-scrim.is-in {
  opacity: 1;
}

.cookie {
  position: fixed;
  left: 22px;
  bottom: 22px;
  z-index: 9995;
  width: min(436px, calc(100vw - 44px));
  padding: 24px;
  border-radius: 22px;
  background:
    linear-gradient(rgba(var(--ink), 0.05), rgba(var(--ink), 0)), var(--surface);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(var(--ink), 0.12);
  box-shadow:
    0 32px 70px -24px rgba(var(--shadow), calc(var(--shadow-a) * 0.84)),
    inset 0 1px 0 rgba(var(--ink), 0.07);
  color: rgba(var(--ink), 0.72);
  overflow: hidden;
  /* Entry/exit is CSS-only so the banner animates even if main.js is deferred
     or blocked. Origin is the anchored corner, so it grows out of it. */
  transform-origin: 0% 100%;
  transform: translateY(30px) scale(0.94);
  opacity: 0;
  transition:
    opacity 0.4s var(--ease-out),
    transform 0.4s var(--ease-out);
}
.cookie[hidden] {
  display: none;
}
.cookie.is-in {
  opacity: 1;
  transform: none;
  animation: cookie-in 0.85s cubic-bezier(0.16, 1.24, 0.32, 1) both;
}
/* Exit is the reverse gesture of the entry, so dismissing reads as the card
   being put away rather than blinking out. JS strips both classes after. */
.cookie.is-out {
  animation: cookie-out 0.5s cubic-bezier(0.5, 0, 0.75, 0) both;
  pointer-events: none;
}
@keyframes cookie-out {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  30% {
    opacity: 1;
    transform: translateY(-6px) scale(1.01);
  }
  100% {
    opacity: 0;
    transform: translateY(46px) scale(0.9);
  }
}
/* The card leaves as one piece: kill every entry keyframe still on the
   children so nothing re-animates out of step while it slides away. */
.cookie.is-out .cookie__head,
.cookie.is-out .cookie__icon,
.cookie.is-out .cookie__title,
.cookie.is-out .cookie__desc,
.cookie.is-out .cookie__glow,
.cookie.is-out .cookie__panel,
.cookie.is-out .cookie__actions,
.cookie.is-out .cookie__actions .cookie__btn {
  animation: none;
  opacity: 1;
  transform: none;
}
@keyframes cookie-in {
  0% {
    opacity: 0;
    transform: translateY(30px) scale(0.94);
  }
  55% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Children cascade in behind the card so it never reads as one flat slab. */
.cookie.is-in .cookie__icon {
  animation: cookie-pop 0.6s var(--ease-out) 0.18s both;
}
.cookie.is-in .cookie__title {
  animation: cookie-rise 0.55s var(--ease-out) 0.24s both;
}
.cookie.is-in .cookie__desc {
  animation: cookie-rise 0.55s var(--ease-out) 0.31s both;
}
.cookie.is-in .cookie__actions {
  animation: cookie-rise 0.55s var(--ease-out) 0.4s both;
}
@keyframes cookie-pop {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  60% {
    transform: scale(1.12);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes cookie-rise {
  0% {
    opacity: 0;
    transform: translateY(9px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.cookie__glow {
  position: absolute;
  top: -70px;
  left: -70px;
  width: 220px;
  height: 220px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(var(--accent-rgb), 0.55),
    transparent 68%
  );
  filter: blur(34px);
  pointer-events: none;
}
.cookie.is-in .cookie__glow {
  animation: cookie-bloom 1.5s var(--ease-out) 0.1s both;
}
@keyframes cookie-bloom {
  0% {
    opacity: 0;
    transform: scale(0.5);
  }
  45% {
    opacity: 1;
    transform: scale(1.15);
  }
  100% {
    opacity: 0.85;
    transform: scale(1);
  }
}

.cookie__head {
  position: relative;
  display: flex;
  gap: 15px;
}
/* Deliberately not a hexagon: the brand shape belongs to the site itself, not
   to a consent dialog sitting on top of it. */
.cookie__icon {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 13px;
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  background: rgba(var(--accent-rgb), 0.14);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.cookie__icon svg {
  width: 24px;
  height: 24px;
}
.cookie__title {
  font-size: 16px;
  font-weight: 650;
  letter-spacing: -0.015em;
  color: var(--fg);
  margin-bottom: 6px;
}
.cookie__desc {
  font-size: 13px;
  line-height: 1.65;
}
.cookie__desc a {
  color: var(--accent);
  font-weight: 600;
}
.cookie__desc a:hover {
  text-decoration: underline;
}

/* Panel: max-height is driven from JS (0 <-> measured height <-> none);
   margin and padding animate too so there is no dead space when collapsed. */
.cookie__panel {
  position: relative;
  overflow: hidden;
  max-height: 0;
  margin-top: 0;
  padding-top: 0;
  border-top: 1px solid rgba(var(--ink), 0);
  transition:
    max-height 0.5s var(--ease-out),
    margin-top 0.5s var(--ease-out),
    padding-top 0.5s var(--ease-out),
    border-top-color 0.5s var(--ease-out);
}
.cookie__panel[hidden] {
  display: none;
}
.cookie__panel.is-open {
  margin-top: 18px;
  padding-top: 6px;
  border-top-color: rgba(var(--ink), 0.1);
}
.cookie__opt {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 0;
  opacity: 0;
  transform: translateX(-14px);
  transition:
    opacity 0.42s var(--ease-out),
    transform 0.42s var(--ease-out);
}
.cookie__panel.is-open .cookie__opt {
  opacity: 1;
  transform: none;
}
.cookie__panel.is-open .cookie__opt:nth-child(1) {
  transition-delay: 0.14s;
}
.cookie__panel.is-open .cookie__opt:nth-child(2) {
  transition-delay: 0.24s;
}

/* The action row is pushed down as the card grows, so it re-settles at the end
   of the expand instead of just riding along. JS toggles .is-shifting to
   restart the keyframe on each open/close, and it must win over the entry
   cascade rule above if the panel is opened mid-entry. */
.cookie.is-in .cookie__actions.is-shifting,
.cookie__actions.is-shifting {
  animation: cookie-settle 0.5s var(--ease-out) 0.16s both;
}
@keyframes cookie-settle {
  0% {
    opacity: 0.35;
    transform: translateY(-8px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}
.cookie__actions.is-shifting .cookie__btn {
  animation: cookie-btn-in 0.44s var(--ease-out) both;
}
.cookie__actions.is-shifting .cookie__btn--link {
  animation-delay: 0.2s;
}
.cookie__actions.is-shifting .cookie__btn--ghost {
  animation-delay: 0.28s;
}
.cookie__actions.is-shifting .cookie__btn--solid {
  animation-delay: 0.36s;
}
@keyframes cookie-btn-in {
  0% {
    opacity: 0;
    transform: translateY(7px) scale(0.94);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.cookie__opt + .cookie__opt {
  border-top: 1px solid rgba(var(--ink), 0.06);
}
.cookie__optText {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.cookie__optName {
  font-size: 13px;
  font-weight: 620;
  color: var(--fg);
}
.cookie__optHint {
  font-size: 12px;
  line-height: 1.55;
  color: rgba(var(--ink), 0.5);
}
.cookie__always {
  flex-shrink: 0;
  align-self: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(var(--ink), 0.42);
  border: 1px solid rgba(var(--ink), 0.16);
  border-radius: 100px;
  padding: 5px 10px;
  white-space: nowrap;
}

.cookie__switch {
  position: relative;
  flex-shrink: 0;
  align-self: center;
  width: 44px;
  height: 25px;
  cursor: pointer;
}
.cookie__switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.cookie__track {
  position: absolute;
  inset: 0;
  border-radius: 100px;
  background: rgba(var(--ink), 0.14);
  border: 1px solid rgba(var(--ink), 0.16);
  transition:
    background 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out);
}
.cookie__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--knob);
  box-shadow: 0 2px 6px rgba(var(--shadow), calc(var(--shadow-a) * 0.42));
  transition: transform 0.3s var(--ease-out);
}
.cookie__switch input:checked + .cookie__track {
  background: var(--accent);
  border-color: var(--accent);
}
.cookie__switch input:checked + .cookie__track::after {
  transform: translateX(19px);
}
.cookie__switch input:focus-visible + .cookie__track {
  outline: 2px solid var(--fg);
  outline-offset: 2px;
}

.cookie__actions {
  position: relative;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}
/* With the panel open the three labels are wider than the card, so the link
   takes its own row and the two real choices share the one below it. */
.cookie__panel.is-open ~ .cookie__actions .cookie__btn--link {
  flex-basis: 100%;
  margin-right: 0;
  text-align: left;
}
.cookie__panel.is-open ~ .cookie__actions .cookie__btn--ghost,
.cookie__panel.is-open ~ .cookie__actions .cookie__btn--solid {
  flex: 1;
}
.cookie__btn {
  padding: 11px 16px;
  border-radius: 100px;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: -0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background 0.3s,
    border-color 0.3s,
    color 0.3s,
    transform 0.3s var(--ease-out);
}
.cookie__btn:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 2px;
}
.cookie__btn--link {
  margin-right: auto;
  padding: 11px 2px;
  background: none;
  border: 0;
  color: rgba(var(--ink), 0.55);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cookie__btn--link:hover {
  color: var(--fg);
}
.cookie__btn--ghost {
  background: transparent;
  border: 1px solid rgba(var(--ink), 0.22);
  color: rgba(var(--ink), 0.72);
}
.cookie__btn--ghost:hover {
  background: rgba(var(--ink), 0.08);
  border-color: rgba(var(--ink), 0.34);
  color: var(--fg);
  transform: translateY(-2px);
}
.cookie__btn--solid {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--on-accent);
  box-shadow: 0 8px 20px -8px var(--accent-glow);
}
.cookie__btn--solid:hover {
  background: var(--accent-lift);
  border-color: var(--accent-lift);
  transform: translateY(-2px);
}

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .nav {
    gap: 14px;
  }
}
/* On a narrow phone the bar has to hold the wordmark, EN/PL, LinkedIn and the
   burger: without tightening these the burger was pushed off the right edge
   and the menu could not be opened at all. */
@media (max-width: 540px) {
  .nav {
    gap: 9px;
  }
  .header__in {
    gap: 10px;
  }
  .logo__text {
    font-size: 23px;
  }
  .langsw {
    font-size: 10px;
    padding: 2px;
  }
  .langsw__on,
  .langsw__off {
    padding: 4px 7px;
  }
  .nav__icon {
    width: 32px;
    height: 32px;
  }
  .burger {
    width: 36px;
    height: 36px;
  }
  .burger__bar:nth-child(1) {
    top: 15px;
  }
  .burger__bar:nth-child(2) {
    top: 20px;
  }
}
@media (max-width: 620px) {
  :root {
    --pad: 18px;
  }
  .hero {
    padding-block: 120px 70px;
  }
  .hero__cta .btn {
    flex: 1;
    justify-content: center;
  }
  .nav__link {
    display: none;
  }
}
@media (max-width: 560px) {
  .cookie {
    left: 12px;
    right: 12px;
    bottom: 12px;
    width: auto;
    padding: 20px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
  }
  .cookie__btn--link {
    flex-basis: 100%;
    margin-right: 0;
    text-align: left;
  }
  .cookie__btn--ghost,
  .cookie__btn--solid {
    flex: 1;
  }
}

@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;
  }
  html {
    scroll-behavior: auto;
  }
  .js .reveal {
    opacity: 1;
    transform: none;
    filter: none;
    clip-path: none;
  }
  /* Headline strips never get built (main.js skips them), but neutralise the
     rules anyway in case the class is present from a previous render. */
  .js .hero__title .line > span {
    animation: none;
    transform: none;
  }
  .cta:hover {
    transform: none;
  }
  .cta:hover .cta__edge {
    animation: none;
    opacity: 0;
  }
  .card::after {
    transition: none;
  }
  /* The rule accents appear at once instead of wiping across. */
  .step::after,
  .track__item::after {
    transition: none;
  }
  /* Entry animations are decorative; show the end state immediately. */
  .js .hero__in > *,
  .js .header {
    animation: none;
    opacity: 1;
    transform: none;
    filter: none;
  }
  .btn--solid:hover::after {
    animation: none;
  }
  .nav__link::after {
    transition: none;
  }
  .hero__title em {
    animation: none;
    background-position: 0 50%;
  }
  .section__tag::after {
    transition: none;
    transform: scaleX(1);
  }
  /* No pointer tilt, no lift: hover stays a colour change only. */
  .card:hover {
    transform: none;
  }
  .stat:hover,
  .founder__list li:hover,
  .founder__hex:hover {
    transform: none;
  }
  .cookie,
  .cookie-scrim {
    transition: opacity 0.2s linear;
    transform: none;
  }
  .cookie.is-in {
    transform: none;
  }
  .btn:hover,
  .card:hover,
  .stack__item:hover {
    transform: none;
  }
  /* Fade only - no spring, no cascade, no bloom pulse. */
  .cookie.is-in,
  .cookie.is-in .cookie__glow,
  .cookie.is-in .cookie__icon,
  .cookie.is-in .cookie__title,
  .cookie.is-in .cookie__desc,
  .cookie.is-in .cookie__actions {
    animation: none;
    opacity: 1;
    transform: none;
  }
  /* Panel snaps open; JS sets max-height:none directly in this mode. */
  .cookie__panel,
  .cookie__opt {
    transition: none;
  }
  .cookie__opt {
    opacity: 1;
    transform: none;
  }
}
