/* ============================================================
   Appure - appure.dev
   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 {
  --bg:        #07080b;
  --panel:     rgba(255, 255, 255, 0.028);
  --line:      rgba(255, 255, 255, 0.09);
  --line-soft: rgba(255, 255, 255, 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);
  /* 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;
}

/* ---------- 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(91, 140, 255, 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. */
.js .section__head.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: rgba(7, 8, 11, 0.72);
  -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(21px, 2.4vw, 26px);
  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); }

.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); }

.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: #5cffe1;
  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(255, 255, 255, 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;
}
.hero__in { max-width: 780px; }

.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 {
  font-size: clamp(2.6rem, 7.2vw, 5rem);
  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;
  background: linear-gradient(96deg, var(--accent) 10%, #6ee7ff 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;
  max-width: 610px;
  margin-bottom: 40px;
}

.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 62px; }

.hero__stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px 34px;
  max-width: 640px;
  padding-top: 30px;
  border-top: 1px solid var(--line-soft);
}
.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; }

/* ---------- 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.13), 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(255, 255, 255, 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(0, 0, 0, 0.95),
    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);
  transition: border-top-color 0.4s var(--ease-out);
}
.step:hover { border-top-color: rgba(var(--accent-rgb), 0.5); }
/* 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; }

/* ---------- 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 ---------- */
.founder {
  display: grid;
  grid-template-columns: 232px 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; }
}

.founder__figure { margin: 0; }

/* 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%;
  max-width: 232px;
  aspect-ratio: var(--hex-ratio); /* required for the hexagon to stay regular */
}
.founder__hex::before {
  content: '';
  position: absolute;
  inset: 0;
  clip-path: var(--hex);
  background: linear-gradient(150deg, rgba(var(--accent-rgb), 0.85), rgba(var(--accent-rgb), 0.25));
  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); }
/* Inset by 2px on every side so the gradient hexagon behind shows as an edge.
   The source avatar is a circle on its own dark disc, so it is scaled up and
   nudged down to fill the hexagon's width without leaving its corners empty;
   object-position keeps the face centred while doing so. */
.founder__photo {
  position: absolute;
  inset: 2px;
  width: calc(100% - 4px);
  height: calc(100% - 4px);
  clip-path: var(--hex);
  /* `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;
  object-position: 50% 38%;
  background: #0d1b22; /* matches the avatar's own backdrop */
}
.founder__caption {
  margin-top: 14px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg-dim);
}
.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(255, 255, 255, 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.2), 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.16), 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(0, 0, 0, 0.95);
}
.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; max-width: 620px; }
.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; }
.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: 46px 40px;
  border-top: 1px solid var(--line-soft);
}
.footer__in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 30px;
}
.footer__meta { font-size: 13px; color: var(--fg-dim); }
.footer__links { display: flex; flex-wrap: wrap; gap: 22px; }
.footer__links a, .footer__links button {
  background: none;
  border: 0;
  padding: 0;
  font-size: 13px;
  color: var(--fg-dim);
  cursor: pointer;
  transition: color 0.25s;
}
.footer__links a:hover, .footer__links button:hover { color: var(--fg); }

/* Company identifier line (NIP / REGON / VATEU) shown in the footer */
.footer__ids {
  font-size: 13px;
  color: var(--fg-dim);
  margin-left: 0;
}

@media (min-width: 720px) {
  .footer__ids { order: 0; margin-left: 6px; }
}

/* ============================================================
   Cookie consent - adapted from the manico3 implementation
   ============================================================ */
.cookie-scrim {
  position: fixed;
  inset: 0;
  z-index: 9994;
  background: rgba(4, 5, 8, 0.55);
  -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(255, 255, 255, 0.05), rgba(255, 255, 255, 0)),
    rgba(12, 14, 19, 0.96);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow:
    0 32px 70px -24px rgba(0, 0, 0, 0.8),
    inset 0 1px 0 rgba(255, 255, 255, 0.07);
  color: rgba(255, 255, 255, 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: #fff;
  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(255, 255, 255, 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(255, 255, 255, 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(255, 255, 255, 0.06); }
.cookie__optText { display: flex; flex-direction: column; gap: 3px; }
.cookie__optName { font-size: 13px; font-weight: 620; color: #fff; }
.cookie__optHint { font-size: 12px; line-height: 1.55; color: rgba(255, 255, 255, 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(255, 255, 255, 0.42);
  border: 1px solid rgba(255, 255, 255, 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(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 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: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
  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 #fff; 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 #fff; outline-offset: 2px; }
.cookie__btn--link {
  margin-right: auto;
  padding: 11px 2px;
  background: none;
  border: 0;
  color: rgba(255, 255, 255, 0.55);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cookie__btn--link:hover { color: #fff; }
.cookie__btn--ghost {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: rgba(255, 255, 255, 0.72);
}
.cookie__btn--ghost:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.34);
  color: #fff;
  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: #5cffe1; border-color: #5cffe1; transform: translateY(-2px); }

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .nav { gap: 20px; }
  .nav__link--hide { display: none; }
}
@media (max-width: 620px) {
  :root { --pad: 18px; }
  .hero { padding-block: 120px 70px; }
  .hero__cta .btn { flex: 1; justify-content: center; }
  .nav__link { display: none; }
  .footer__in { flex-direction: column; align-items: flex-start; }
}
@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; }
  /* 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; }
}
