/* ==========================================================================
   Savvant, design system
   One stylesheet for the whole site. No frameworks, no external requests.
   ========================================================================== */

/* Schibsted Grotesk, a Norwegian newspaper grotesk, narrower and with more
   character than the neutral defaults. Instrument Serif carries every emphasised
   phrase. Both SIL OFL, both self-hosted: the page still makes no outside
   request. */
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/schibsted-grotesk-var.woff2") format("woff2");
}

@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/instrument-serif-italic.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Surfaces, the brand ground is dark regardless of the OS theme. */
  --ground: #0a0908;
  --raise: #100e0c;
  --raise-2: #15120f;

  /* Ink */
  --ink: #ffffff;
  --ink-strong: rgba(255, 255, 255, 0.92);
  --ink-body: rgba(255, 255, 255, 0.62);
  --ink-muted: rgba(255, 255, 255, 0.44);
  --ink-faint: rgba(255, 255, 255, 0.28);

  /* Hairlines: the primary structural device of the whole site. */
  --line: rgba(255, 255, 255, 0.09);
  --line-strong: rgba(255, 255, 255, 0.16);
  --line-faint: rgba(255, 255, 255, 0.05);

  /* No accent hue anywhere in the type. Emphasis is carried by the serif italic
     alone, and every bit of colour on the site comes from the product tiles 
     Textly's coral, Hustly's violet. The parent brand stays out of their way. */

  /* Emphasised phrases are set in a serif italic against the grotesk. This is
     the single biggest reason the page does not read as one flat tone. No web
     font is loaded for it: ui-serif resolves to New York on Apple platforms and
     to a decent local serif elsewhere. Swap in a licensed display serif here if
     you want tighter control. */
  --serif: "Instrument Serif", ui-serif, "New York", Palatino, "Times New Roman", serif;

  /* The metadata voice. Everything the register states about a thing, its
     category, its platforms, its status, is set in this. */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* Product brand colours, taken from the products' own design tokens. */
  --textly: #ff6b45;
  --textly-tile: #fdece5;
  --hustly: #4d3fd1;
  --hustly-tile: #e8e6fb;
  --neutral-tile: #ecebe8;


  /* Type scale, fluid, clamped at both ends. */
  --step-display: clamp(2.6rem, min(8.2vw, 15.5vh), 9.5rem);
  --step-title: clamp(2rem, 1.05rem + 3.6vw, 4.25rem);
  --step-section: clamp(1.65rem, 1.1rem + 2.1vw, 2.85rem);
  --step-lead: clamp(1.0625rem, 0.95rem + 0.6vw, 1.375rem);
  --step-body: clamp(0.9375rem, 0.9rem + 0.2vw, 1.0625rem);
  --step-small: 0.8125rem;
  --step-label: 0.6875rem;

  /* Rhythm */
  --gutter: clamp(1.25rem, 4.5vw, 4.5rem);
  --measure: 1320px;
  --section-y: clamp(4rem, 7.5vw, 8rem);

  /* Motion, one curve, one duration family, used everywhere. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --fast: 180ms;
  --mid: 420ms;
  --slow: 900ms;

  --radius: 3px;
  --header-h: 68px;

  /* Film grain, inline so the page still makes no outside requests. */
  --grain-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
}

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink-strong);
  font-family: "Schibsted Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
    Arial, sans-serif;
  font-size: var(--step-body);
  font-weight: 400;
  line-height: 1.62;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body.is-locked {
  overflow: hidden;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 500;
  letter-spacing: -0.035em;
  line-height: 1.04;
  color: var(--ink);
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

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

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

::selection {
  background: rgba(223, 230, 255, 0.22);
  color: #fff;
}

:focus-visible {
  outline: 2px solid rgba(223, 230, 255, 0.75);
  outline-offset: 3px;
  border-radius: 2px;
}

/* --------------------------------------------------------------------------
   Page furniture
   -------------------------------------------------------------------------- */

/* Fixed film grain. Pointer-events off so it never eats a click.

   The opacity is the only knob worth touching. At 0.032 it read on a phone,
   held close and usually on an OLED panel with a truly black black, and
   disappeared on a monitor at arm's length. This is the compromise: still
   texture rather than noise, but present on both. Above roughly 0.08 the page
   starts to look dirty rather than printed. */
.grain {
  position: fixed;
  inset: -50%;
  z-index: 1;
  pointer-events: none;
  opacity: 0.055;
  background-image: var(--grain-src);
  background-size: 220px 220px;
  will-change: transform;
  animation: grain-drift 7s steps(6) infinite;
}

@keyframes grain-drift {
  0%,
  100% { transform: translate(0, 0); }
  20%  { transform: translate(-3%, 2%); }
  40%  { transform: translate(2%, -3%); }
  60%  { transform: translate(-2%, -2%); }
  80%  { transform: translate(3%, 1%); }
}

@media (prefers-reduced-motion: reduce) {
  .grain { animation: none; }
}

.skip {
  position: absolute;
  left: 0.75rem;
  top: -4rem;
  z-index: 100;
  padding: 0.6rem 1rem;
  background: var(--ink);
  color: var(--ground);
  font-size: var(--step-small);
  font-weight: 600;
  transition: top var(--fast) var(--ease);
}

.skip:focus {
  top: 0.75rem;
}

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

.shell--narrow {
  max-width: 920px;
}

main {
  position: relative;
  z-index: 2;
}

/* --------------------------------------------------------------------------
   Notice bar (placeholder-content warning, see scripts/build.py)
   -------------------------------------------------------------------------- */

.notice {
  font-family: var(--mono);
  position: relative;
  z-index: 30;
  border-bottom: 1px solid var(--line-faint);
  background: #050507;
  color: var(--ink-muted);
  font-size: 0.75rem;
  letter-spacing: 0.005em;
}

.notice__inner {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  justify-content: center;
  padding: 0.5rem var(--gutter);
  text-align: center;
}

.notice b {
  color: var(--ink-body);
  font-weight: 500;
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */

.masthead {
  position: sticky;
  top: 0;
  z-index: 40;
  border-bottom: 1px solid transparent;
  transition: border-color var(--mid) var(--ease), background-color var(--mid) var(--ease),
    backdrop-filter var(--mid) var(--ease);
}

.masthead.is-stuck {
  border-bottom-color: var(--line);
  background: rgba(10, 9, 8, 0.72);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
}

.masthead__inner {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  height: var(--header-h);
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Wordmark */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-right: auto;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.wordmark:hover .wordmark__text {
  transition: color var(--mid) var(--ease);
}

.wordmark:hover .wordmark__text {
  color: var(--ink);
}

/* Primary navigation */
.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.15rem, 2.4vw, 2.1rem);
}

.nav__link {
  position: relative;
  padding-block: 0.35rem;
  font-size: 0.875rem;
  color: var(--ink-body);
  transition: color var(--fast) var(--ease);
}

.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--mid) var(--ease);
}

.nav__link:hover {
  color: var(--ink);
}

.nav__link:hover::after {
  transform: scaleX(1);
}

.nav__link[aria-current="page"] {
  color: var(--ink);
}

.nav__link[aria-current="page"]::after {
  transform: scaleX(1);
  background: var(--ink-faint);
}

.nav__cta {
  margin-left: 0.4rem;
}

@media (max-width: 900px) {
  .nav {
    display: none;
  }
}

/* Mobile menu */
.burger {
  display: none;
  position: relative;
  width: 40px;
  height: 40px;
  margin-right: -8px;
}

.burger span {
  position: absolute;
  left: 10px;
  width: 20px;
  height: 1px;
  background: var(--ink);
  transition: transform var(--mid) var(--ease), opacity var(--fast) var(--ease);
}

.burger span:nth-child(1) { top: 17px; }
.burger span:nth-child(2) { top: 23px; }

.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { transform: translateY(-3px) rotate(-45deg); }

@media (max-width: 900px) {
  .burger { display: block; }
}

.drawer {
  position: fixed;
  inset: 0;
  z-index: 39;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.25rem;
  padding: calc(var(--header-h) + 2rem) var(--gutter) 3rem;
  background: rgba(6, 6, 8, 0.97);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--mid) var(--ease), visibility var(--mid) var(--ease);
}

.drawer.is-open {
  opacity: 1;
  visibility: visible;
}

.drawer a {
  padding-block: 0.7rem;
  border-bottom: 1px solid var(--line-faint);
  font-size: clamp(1.5rem, 7vw, 2.25rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  color: var(--ink);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--mid) var(--ease), transform var(--mid) var(--ease);
}

.drawer.is-open a {
  opacity: 1;
  transform: none;
}

.drawer__foot {
  margin-top: 2rem;
  color: var(--ink-muted);
  font-size: var(--step-small);
}

.drawer__foot a {
  border: 0;
  font-size: inherit;
  font-weight: 400;
  color: var(--ink-body);
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  --btn-pad-y: 0.7rem;
  --btn-pad-x: 1.3rem;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  transition: border-color var(--mid) var(--ease), color var(--mid) var(--ease),
    transform var(--mid) var(--ease), background-color var(--mid) var(--ease);
}

.btn__arrow {
  flex: none;
  transition: transform var(--mid) var(--ease);
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

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

/* Solid */
.btn--solid {
  border-color: transparent;
  background: var(--ink);
  color: var(--ground);
  font-weight: 600;
}

.btn--solid:hover {
  background: #e6ebff;
}

/* Outline */
.btn--ghost {
  color: var(--ink-strong);
}

.btn--ghost:hover {
  border-color: rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.045);
  color: var(--ink);
}

.btn--sm {
  --btn-pad-y: 0.5rem;
  --btn-pad-x: 1rem;
  font-size: 0.8125rem;
}

.btn--lg {
  --btn-pad-y: 0.95rem;
  --btn-pad-x: 1.7rem;
  font-size: 0.9375rem;
}

/* Inline text link with a sliding rule */
.tlink {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding-bottom: 2px;
  border-bottom: 1px solid var(--line-strong);
  font-size: 0.9375rem;
  color: var(--ink);
  transition: border-color var(--mid) var(--ease), gap var(--mid) var(--ease);
}

.tlink:hover {
  border-bottom-color: var(--ink);
  gap: 0.7rem;
}

/* --------------------------------------------------------------------------
   Section scaffolding
   -------------------------------------------------------------------------- */

.band {
  position: relative;
  padding-block: var(--section-y);
  border-top: 1px solid var(--line);
}

.band--flush {
  border-top: 0;
}

/* Figures that sit directly under a hero or page header. A full --section-y
   above them reads as a dropped section rather than a caption to what is
   already on screen, so the top padding goes entirely. */
.band--tight {
  padding-block: 0 clamp(2rem, 4vw, 3rem);
}

.band--tint {
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(223, 230, 255, 0.035), transparent 60%),
    var(--raise);
}

/* Eyebrow: index number + label, hairline above */
.eyebrow {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  margin-bottom: clamp(1.75rem, 4vw, 3rem);
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The chapter numeral. Serif, oversized, and hung out into the gutter so the
   label still starts on the page's left edge, the one detail that most says a
   person set this rather than a component library. */
.eyebrow__num {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.15rem;
  letter-spacing: 0;
  color: var(--ink-muted);
  font-variant-numeric: lining-nums;
}

/* Hung relative to the gutter, never a fixed distance: --gutter is fluid, and a
   fixed -3.5rem falls off the left of the viewport at around 1100px where the
   gutter is still under 50px. This keeps the numeral inside the page margin at
   every width the rule applies to. */
@media (min-width: 1240px) {
  .eyebrow__num {
    position: absolute;
    right: 100%;
    top: -0.6rem;
    margin-right: 0.75rem;
    font-size: 2.25rem;
    line-height: 1;
    opacity: 0.85;
  }
}

.eyebrow__rule {
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* Two-column editorial split: sticky heading, flowing body */
.split {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
}

.split__head {
  position: sticky;
  top: calc(var(--header-h) + clamp(1.5rem, 4vw, 3.5rem));
}

@media (max-width: 900px) {
  .split {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
  .split__head {
    position: static;
  }
}

.lead {
  max-width: 46ch;
  font-size: var(--step-lead);
  line-height: 1.5;
  letter-spacing: -0.019em;
  color: var(--ink-body);
}

.prose {
  max-width: 62ch;
  color: var(--ink-body);
}

.prose > * + * {
  margin-top: 1.15em;
}

.prose strong {
  color: var(--ink-strong);
  font-weight: 500;
}

/* h2 on the legal pages, h3 wherever a section heading already sits above the
   prose. Same appearance either way, the level follows the document, not the
   look. */
.prose h2,
.prose h3 {
  margin-top: 2.4em;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
}

.prose a:not(.btn):not(.tlink) {
  border-bottom: 1px solid var(--line-strong);
  color: var(--ink-strong);
  transition: border-color var(--fast) var(--ease);
}

.prose a:not(.btn):not(.tlink):hover {
  border-bottom-color: var(--ink);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: min(92svh, 980px);
  padding-block: clamp(3.5rem, 9vw, 7.5rem) clamp(2.5rem, 6vh, 5.5rem);
  overflow: clip;
}

.hero__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  /* Wider than the rest of the site. The body measure exists so paragraphs stay
     readable; a headline has the opposite job. */
  max-width: 1760px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.display {
  /* No max-width. The line breaks where the page ends, which is what makes a
     headline read as a wall of type rather than a paragraph in a column. */
  font-size: var(--step-display);
  font-weight: 500;
  letter-spacing: -0.045em;
  line-height: 0.95;
}


.display em {
  font-size: 1.16em;
  line-height: 0.85;
}

.display em,
.pagehead__title em,
.closer__title em,
.statement em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.012em;
  color: var(--ink);
}

/* Word-level illumination, driven from savvant.js. Without the script every
   word simply renders at full strength. */
.lw {
  display: inline-block;
  white-space: nowrap;
}

.js [data-lit] .lw {
  opacity: var(--lit, 1);
}

/* Short viewports: a laptop in a window, not a phone in portrait. The display
   is sized to fill the screen, so on a short one it has to give that back or
   the headline runs past the fold. */
@media (max-height: 860px) and (min-width: 821px) {
  :root {
    --step-display: clamp(2.4rem, min(7.6vw, 16vh), 8rem);
  }

  .hero {
    padding-block: clamp(2.5rem, 5vw, 4rem) clamp(1.75rem, 5vh, 3rem);
  }
}

/* Phones in landscape: same problem, much less room. */
@media (max-height: 560px) and (orientation: landscape) {
  :root {
    --step-display: clamp(1.75rem, 0.6rem + 4.6vw, 3.25rem);
  }

  .hero {
    min-height: 0;
    padding-block: 1.5rem;
  }
}

/* --------------------------------------------------------------------------
   Page header (interior pages)
   -------------------------------------------------------------------------- */

.pagehead {
  padding-block: clamp(3.5rem, 8vw, 7rem) clamp(2.5rem, 5vw, 4rem);
}

.pagehead__title {
  max-width: 22ch;
  font-size: var(--step-title);
  letter-spacing: -0.04em;
}

.pagehead .lead {
  margin-top: clamp(1.25rem, 3vw, 2rem);
}

.crumb {
  font-family: var(--mono);
  margin-bottom: clamp(1.25rem, 3vw, 2rem);
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------------
   Stats
   -------------------------------------------------------------------------- */

/* Four figures per block, always. The nth-child rules below encode that count:
   they keep the first figure of each row flush with the page gutter and drop
   the divider at the end of each row. Changing the number of .stat children
   means revisiting them. */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--line);
}

.stat {
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.25rem, 2.2vw, 2rem) clamp(1.5rem, 3vw, 2rem);
  border-right: 1px solid var(--line);
}

.stat:nth-child(4n + 1) {
  padding-left: 0;
}

.stat:nth-child(4n) {
  border-right: 0;
}

/* Two up: rows of 2, so the row edges move to the odd/even positions and the
   first row gains a rule beneath it. */
@media (max-width: 900px) {
  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .stat {
    border-bottom: 1px solid var(--line);
  }

  .stat:nth-child(odd) {
    padding-left: 0;
  }

  .stat:nth-child(even) {
    border-right: 0;
  }

  .stat:nth-last-child(-n + 2) {
    border-bottom: 0;
  }
}

/* One up: no vertical rules left to place. */
@media (max-width: 460px) {
  .stats {
    grid-template-columns: 1fr;
  }

  .stat {
    padding-left: 0;
    border-right: 0;
  }

  .stat:last-child {
    border-bottom: 0;
  }
}

.stat__value {
  display: block;
  font-size: clamp(1.9rem, 1rem + 2.6vw, 3.1rem);
  font-weight: 500;
  letter-spacing: -0.045em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.stat__unit {
  font-size: 0.55em;
  letter-spacing: -0.02em;
  color: var(--ink-faint);
}

.stat__label {
  display: block;
  margin-top: 0.7rem;
  font-size: var(--step-small);
  color: var(--ink-muted);
  letter-spacing: -0.005em;
}

/* --------------------------------------------------------------------------
   Numbered propositions
   -------------------------------------------------------------------------- */

.props {
  display: grid;
  gap: 0;
  counter-reset: prop;
}

.prop {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  padding-block: clamp(1.6rem, 3.2vw, 2.5rem);
  border-top: 1px solid var(--line);
}

.prop:first-child {
  border-top: 0;
  padding-top: 0;
}

.prop__num {
  font-size: var(--step-small);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  padding-top: 0.3rem;
}

.prop__title {
  font-size: clamp(1.15rem, 0.95rem + 0.9vw, 1.6rem);
  letter-spacing: -0.03em;
}

.prop__text {
  max-width: 58ch;
  margin-top: 0.7rem;
  color: var(--ink-body);
}

@media (max-width: 560px) {
  .prop {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  .prop__num {
    padding-top: 0;
  }
}

/* --------------------------------------------------------------------------
   Product cards
   -------------------------------------------------------------------------- */

.grid-products {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}

.product {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  padding: clamp(1.5rem, 2.6vw, 2.15rem);
  background: var(--ground);
  overflow: hidden;
  transition: background-color var(--mid) var(--ease);
}

/* Light leak from the top-left corner on hover. */
.product::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(24rem 16rem at 0% 0%, rgba(205, 216, 255, 0.09), transparent 62%);
  opacity: 0;
  transition: opacity var(--mid) var(--ease);
}

.product:hover {
  background: var(--raise);
}

.product:hover::before {
  opacity: 1;
}

.product__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.product__name {
  font-size: 1.1875rem;
  font-weight: 500;
  letter-spacing: -0.028em;
  color: var(--ink);
}

.product__tag {
  flex: none;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.product__tag--built {
  border-color: rgba(205, 216, 255, 0.32);
  color: #cdd8ff;
}

.product__meta {
  font-family: var(--mono);
  font-size: var(--step-small);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.product__text {
  flex: 1;
  font-size: 0.9375rem;
  color: var(--ink-body);
}

.product__metrics {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  padding-top: 1.05rem;
  border-top: 1px solid var(--line);
}

.product__metric {
  font-size: var(--step-small);
  color: var(--ink-muted);
}

.product__metric b {
  display: block;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-strong);
}

/* --------------------------------------------------------------------------
   Platform / capability rows
   -------------------------------------------------------------------------- */

.rows {
  border-top: 1px solid var(--line);
}

.row {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 5fr) auto;
  gap: clamp(1rem, 3vw, 3rem);
  align-items: baseline;
  padding-block: clamp(1.3rem, 2.6vw, 1.9rem);
  border-bottom: 1px solid var(--line);
  transition: padding-inline var(--mid) var(--ease);
}

.row:hover {
  padding-inline: 0.75rem;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.03), transparent 70%);
}

/* Name above, kind beneath. Side by side they broke apart as soon as the name
   wrapped, leaving the label stranded next to the first line. */
.row__name {
  display: block;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.025em;
  color: var(--ink);
}

.row__kind {
  font-family: var(--mono);
  display: block;
  margin-top: 0.3rem;
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.row__text {
  font-size: 0.9375rem;
  color: var(--ink-body);
}

.row__aside {
  font-family: var(--mono);
  font-size: var(--step-small);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

@media (max-width: 820px) {
  .row {
    grid-template-columns: 1fr;
    gap: 0.5rem;
  }
  .row__aside {
    text-align: left;
  }
}

/* --------------------------------------------------------------------------
   Process steps
   -------------------------------------------------------------------------- */

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 1px;
  background: var(--line);
  border-block: 1px solid var(--line);
}

.step {
  padding: clamp(1.4rem, 2.6vw, 2rem) clamp(1.25rem, 2.2vw, 1.75rem);
  background: var(--ground);
}

.step__num {
  display: block;
  margin-bottom: 1.1rem;
  font-size: var(--step-small);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

.step__title {
  font-size: 1.0625rem;
  letter-spacing: -0.025em;
}

.step__text {
  margin-top: 0.6rem;
  font-size: 0.9375rem;
  color: var(--ink-body);
}

.step__when {
  font-family: var(--mono);
  margin-top: 1rem;
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   Feature / value tiles
   -------------------------------------------------------------------------- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(1.5rem, 3.5vw, 3rem) clamp(1.5rem, 4vw, 3.5rem);
}

.tile__title {
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-strong);
  font-size: 1.0625rem;
  letter-spacing: -0.025em;
}

.tile__text {
  margin-top: 0.7rem;
  font-size: 0.9375rem;
  color: var(--ink-body);
}

/* --------------------------------------------------------------------------
   Roles list
   -------------------------------------------------------------------------- */

.roles {
  border-top: 1px solid var(--line);
}

.role {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: clamp(0.75rem, 2.5vw, 2rem);
  align-items: center;
  padding-block: clamp(1.15rem, 2.2vw, 1.6rem);
  border-bottom: 1px solid var(--line);
  transition: background-color var(--mid) var(--ease), padding-inline var(--mid) var(--ease);
}

.role:hover {
  padding-inline: 0.75rem;
  background: rgba(255, 255, 255, 0.025);
}

.role__title {
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.025em;
  color: var(--ink);
}

.role__team {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--step-small);
  color: var(--ink-muted);
}

.role__where {
  font-size: var(--step-small);
  color: var(--ink-muted);
  white-space: nowrap;
}

.role__go {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--step-small);
  color: var(--ink-body);
  white-space: nowrap;
  transition: color var(--fast) var(--ease);
}

.role:hover .role__go {
  color: var(--ink);
}

.role:hover .role__go svg {
  transform: translateX(3px);
}

.role__go svg {
  transition: transform var(--mid) var(--ease);
}

@media (max-width: 640px) {
  .role {
    grid-template-columns: 1fr;
    gap: 0.5rem;
    align-items: start;
  }
}

/* --------------------------------------------------------------------------
   Quote
   -------------------------------------------------------------------------- */

.quote {
  max-width: 30ch;
  font-size: clamp(1.5rem, 1rem + 2.2vw, 2.6rem);
  font-weight: 500;
  letter-spacing: -0.038em;
  line-height: 1.12;
  color: var(--ink);
}

.quote__by {
  margin-top: clamp(1.25rem, 3vw, 2rem);
  font-size: var(--step-small);
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------------
   Closing call to action
   -------------------------------------------------------------------------- */

.closer {
  position: relative;
  padding-block: clamp(5rem, 11vw, 11rem);
  border-top: 1px solid var(--line);
  overflow: clip;
  text-align: center;
}

.closer::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -60%;
  width: min(120%, 1100px);
  aspect-ratio: 1;
  transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(circle, rgba(196, 210, 255, 0.1), transparent 62%);
}

.closer__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.5rem, 3.5vw, 2.5rem);
}

.closer__title {
  max-width: 24ch;
  font-size: var(--step-title);
  letter-spacing: -0.042em;
}

.closer .lead {
  max-width: 52ch;
  text-align: center;
}

.closer .actions {
  justify-content: center;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.footer {
  position: relative;
  z-index: 2;
  padding-block: clamp(3rem, 6vw, 5rem) clamp(1.75rem, 3vw, 2.5rem);
  border-top: 1px solid var(--line);
  background: #050507;
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
  gap: clamp(2rem, 5vw, 4rem);
}

@media (max-width: 900px) {
  .footer__top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .footer__brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 480px) {
  .footer__top {
    grid-template-columns: 1fr;
  }
}

.footer__tagline {
  max-width: 24ch;
  margin-top: 1.15rem;
  font-size: 1.0625rem;
  letter-spacing: -0.025em;
  color: var(--ink-body);
}

.footer__tagline em {
  font-style: normal;
  color: var(--ink);
}

.footer__heading {
  margin-bottom: 1.1rem;
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.footer__list li + li {
  margin-top: 0.65rem;
}

.footer__list a {
  font-size: 0.9375rem;
  color: var(--ink-body);
  transition: color var(--fast) var(--ease);
}

.footer__list a:hover {
  color: var(--ink);
}

.footer__bottom {
  font-family: var(--mono);
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line-faint);
  font-size: var(--step-small);
  color: var(--ink-faint);
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
}

.footer__legal a {
  transition: color var(--fast) var(--ease);
}

.footer__legal a:hover {
  color: var(--ink-body);
}

/* --------------------------------------------------------------------------
   Scroll reveal, opt-in per element, disabled without JS or with reduced motion
   -------------------------------------------------------------------------- */

.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease);
  transition-delay: var(--rd, 0ms);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .js [data-lit] .lw {
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   Utilities
   -------------------------------------------------------------------------- */

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

.u-mt-s { margin-top: clamp(1.25rem, 3vw, 2rem); }
.u-mt-m { margin-top: clamp(2rem, 4.5vw, 3.25rem); }
.u-mt-l { margin-top: clamp(3rem, 6vw, 4.5rem); }

/* Print, the dark ground would waste a cartridge. */
@media print {
  .grain,
  .masthead,
  .drawer,
  .closer::before {
    display: none !important;
  }
  body {
    background: #fff;
    color: #000;
  }
  h1, h2, h3, .display, .stat__value {
    color: #000;
  }
}

/* ==========================================================================
   Product tiles

   The centrepiece. Light tiles on the dark page, each carrying its product's
   own brand colour, with a device mockup bleeding off the bottom edge. The
   value contrast is what stops the products getting lost in a page that is
   otherwise all near-black, and the colour comes from the products rather
   than from an invented parent-brand palette.
   ========================================================================== */

.ptiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.75rem, 1.4vw, 1.25rem);
}

@media (max-width: 860px) {
  .ptiles {
    grid-template-columns: 1fr;
  }
}

.ptile {
  --tile: var(--neutral-tile);
  --tile-brand: #14141a;
  --tile-ink: #14141a;
  --tile-ink-soft: rgba(20, 20, 26, 0.6);

  position: relative;
  display: flex;
  flex-direction: column;
  min-height: clamp(24rem, 40vw, 34rem);
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
  border-radius: clamp(1.25rem, 2vw, 1.75rem);
  background: var(--tile);
  color: var(--tile-ink);
  overflow: hidden;
  isolation: isolate;
}

.ptile--textly {
  --tile: var(--textly-tile);
  --tile-brand: var(--textly);
}

.ptile--hustly {
  --tile: var(--hustly-tile);
  --tile-brand: var(--hustly);
}

/* A soft wash of the brand colour rising from the bottom, so the tile is not
   one flat fill behind the device. */
.ptile::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 62%;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to top, color-mix(in oklab, var(--tile-brand) 20%, transparent), transparent);
}

.ptile__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.ptile__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.ptile__icon {
  flex: none;
  width: clamp(1.75rem, 2.6vw, 2.25rem);
  height: clamp(1.75rem, 2.6vw, 2.25rem);
  border-radius: 26%;
}

.ptile__name {
  font-size: clamp(1.5rem, 1rem + 1.5vw, 2.25rem);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--tile-ink);
}

.ptile__meta {
  font-family: var(--mono);
  flex: none;
  padding: 0.3rem 0.7rem;
  border: 1px solid rgba(20, 20, 26, 0.16);
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tile-ink-soft);
  white-space: nowrap;
}

.ptile__line {
  max-width: 24ch;
  margin-top: clamp(1rem, 2vw, 1.5rem);
  font-size: clamp(1.0625rem, 0.95rem + 0.55vw, 1.375rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.25;
  color: var(--tile-ink);
}

.ptile__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.1rem;
  margin-top: 0.85rem;
  font-size: 0.8125rem;
  color: var(--tile-ink-soft);
}

.ptile__facts li {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.ptile__facts li::before {
  content: "";
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--tile-brand);
}

/* Device stage, the mockup sits at the bottom and is deliberately cropped by
   the tile, which reads as a window into the product rather than a picture of
   it sitting on a shelf. */
.ptile__stage {
  position: relative;
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: clamp(9rem, 16vw, 14rem);
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
}

.ptile__stage .dev {
  margin-bottom: calc(var(--dw) * -0.44);
  transition: transform var(--slow) var(--ease);
}

.ptile:hover .ptile__stage .dev {
  transform: translateY(-0.75rem);
}

@media (prefers-reduced-motion: reduce) {
  .ptile__stage .dev,
  .ptile:hover .ptile__stage .dev {
    transition: none;
    transform: none;
  }
}

.ptile__link {
  position: absolute;
  inset: 0;
  z-index: 3;
}

/* Wide variant: full row, no device. Used for the product we cannot show yet. */
.ptile--wide {
  grid-column: 1 / -1;
  min-height: 0;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1rem, 3vw, 3rem);
  flex-wrap: wrap;
}

.ptile--wide .ptile__line {
  margin-top: 0;
  max-width: 34ch;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.125rem);
  font-weight: 400;
  color: var(--tile-ink-soft);
}

/* ==========================================================================
   Device mockup

   Built from each product's own tokens: Textly's channel colours and coral,
   Hustly's violet wallet and its real bottom-nav structure. Everything inside
   the screen is sized in em against --dw, so one width change rescales the
   whole thing.
   ========================================================================== */

.dev {
  --dw: clamp(12rem, 42vw, 16.5rem);
  position: relative;
  flex: none;
  width: var(--dw);
  aspect-ratio: 1080 / 2190;
  padding: calc(var(--dw) * 0.026);
  border-radius: calc(var(--dw) * 0.135);
  background: linear-gradient(158deg, #2b2b2f 0%, #0c0c0e 44%, #232327 100%);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08) inset,
    0 2rem 3.5rem -1.5rem rgba(10, 10, 14, 0.55);
}

.dev__screen {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  border-radius: calc(var(--dw) * 0.112);
  overflow: hidden;
  background: #fff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: calc(var(--dw) / 25);
  line-height: 1.35;
  letter-spacing: -0.01em;
  text-align: left;
  color: #14141a;
}

/* Status bar */
.dev__sb {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: none;
  padding: 0.7em 1em 0.35em;
  font-size: 0.82em;
  font-weight: 600;
}

.dev__sb--onbrand {
  color: #fff;
}

.dev__notch {
  width: 1.4em;
  height: 1.4em;
  border-radius: 50%;
  background: #08080a;
}

.dev__bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 0.15em;
}

.dev__bars i {
  width: 0.22em;
  border-radius: 0.1em;
  background: currentColor;
}

.dev__bars i:nth-child(1) { height: 0.3em; }
.dev__bars i:nth-child(2) { height: 0.45em; }
.dev__bars i:nth-child(3) { height: 0.6em; }
.dev__bars i:nth-child(4) { height: 0.75em; }

/* -------- Textly screen -------- */

.dev--textly .dev__screen {
  background: #fff8f4;
}

.dev__search {
  display: flex;
  align-items: center;
  gap: 0.5em;
  flex: none;
  margin: 0.5em 0.85em 0.6em;
  padding: 0.55em 0.75em;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(35, 17, 12, 0.08);
  font-size: 0.86em;
  color: rgba(35, 17, 12, 0.5);
}

.dev__search svg {
  flex: none;
  width: 1.05em;
  height: 1.05em;
}

.dev__pills {
  display: flex;
  gap: 0.35em;
  flex: none;
  padding: 0 0.85em 0.7em;
  font-size: 0.74em;
  font-weight: 600;
}

.dev__pills span {
  padding: 0.3em 0.7em;
  border-radius: 999px;
  background: rgba(35, 17, 12, 0.07);
  color: rgba(35, 17, 12, 0.55);
  white-space: nowrap;
}

.dev__pills span.on {
  background: #23110c;
  color: #fff8f4;
}

.dev__threads {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.15em;
  padding: 0 0.6em;
  overflow: hidden;
}

.dev__thread {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.55em 0.5em;
  border-radius: 0.85em;
}

.dev__thread--on {
  background: #fff;
  box-shadow: 0 0 0 1px rgba(35, 17, 12, 0.06);
}

.dev__av {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 2.3em;
  height: 2.3em;
  border-radius: 50%;
  background: #f0ded5;
  font-size: 0.86em;
  font-weight: 700;
  color: #23110c;
}

/* Channel badge, real brand colours, from Textly's own stylesheet. */
.dev__ch {
  position: absolute;
  right: -0.15em;
  bottom: -0.15em;
  width: 1.15em;
  height: 1.15em;
  border-radius: 50%;
  border: 0.14em solid #fff8f4;
}

.ch-wa  { background: #25d366; }
.ch-sms { background: #ff6b45; }
.ch-tg  { background: #229ed9; }
.ch-dc  { background: #5865f2; }
.ch-sl  { background: #4a154b; }

.dev__body {
  flex: 1;
  min-width: 0;
}

.dev__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5em;
}

.dev__who {
  display: block;
  font-size: 0.88em;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dev__when {
  flex: none;
  font-size: 0.72em;
  color: rgba(35, 17, 12, 0.4);
}

.dev__prev {
  display: block;
  font-size: 0.8em;
  color: rgba(35, 17, 12, 0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dev__badge {
  flex: none;
  min-width: 1.35em;
  padding: 0.05em 0.35em;
  border-radius: 999px;
  background: #ff6b45;
  font-size: 0.68em;
  font-weight: 700;
  color: #fff;
  text-align: center;
}

/* -------- Hustly screen -------- */

.dev--hustly .dev__screen {
  background: #f7f7fb;
}

.dev__wallet {
  flex: none;
  margin: 0.4em 0.7em 0.7em;
  padding: 0.9em 1em 1em;
  border-radius: 1.1em;
  background: linear-gradient(150deg, #5b4cdd 0%, #4d3fd1 55%, #3f33ad 100%);
  color: #fff;
}

.dev__wlabel {
  font-size: 0.7em;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.62);
}

.dev__wsum {
  margin-top: 0.15em;
  font-size: 1.85em;
  font-weight: 700;
  letter-spacing: -0.045em;
  font-variant-numeric: tabular-nums;
}

.dev__states {
  display: flex;
  gap: 0.3em;
  margin-top: 0.8em;
  font-size: 0.66em;
  font-weight: 600;
}

.dev__states span {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  padding: 0.3em 0.55em;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  white-space: nowrap;
}

.dev__states i {
  width: 0.45em;
  height: 0.45em;
  border-radius: 50%;
  background: currentColor;
}

.dev__states .s-ok   { color: #b8f5cf; }
.dev__states .s-wait { color: #ffd9a8; }
.dev__states .s-done { color: rgba(255, 255, 255, 0.75); }

.dev__sect {
  flex: none;
  padding: 0 1em 0.5em;
  font-size: 0.76em;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.dev__offers {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
  padding: 0 0.7em;
  overflow: hidden;
}

.dev__offer {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.55em 0.6em;
  border-radius: 0.85em;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(17, 18, 26, 0.05);
}

.dev__tile {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.2em;
  height: 2.2em;
  border-radius: 0.6em;
  font-size: 0.8em;
  font-weight: 800;
  color: #fff;
}

.dev__pay {
  flex: none;
  font-size: 0.85em;
  font-weight: 700;
  color: #4d3fd1;
  font-variant-numeric: tabular-nums;
}

/* Bottom navigation. Hustly's real five tabs. */
.dev__nav {
  display: flex;
  justify-content: space-between;
  flex: none;
  margin-top: 0.5em;
  padding: 0.6em 1.1em calc(var(--dw) * 0.055);
  border-top: 1px solid rgba(17, 18, 26, 0.07);
  background: #fff;
  font-size: 0.62em;
  font-weight: 600;
  color: rgba(17, 18, 26, 0.38);
}

.dev__nav b {
  font-weight: 700;
  color: #4d3fd1;
}

/* -------- Empty screen, for a product we cannot show yet -------- */

.dev--empty .dev__screen {
  align-items: center;
  justify-content: center;
  background: #f2f1ee;
  color: rgba(20, 20, 26, 0.34);
  font-size: calc(var(--dw) / 20);
  font-weight: 600;
}

/* ==========================================================================
   Statement, one sentence, centred, filling the width. The counterweight to
   the two-column editorial sections; without something like this the page
   reads as one layout repeated.
   ========================================================================== */

.statement {
  padding-block: clamp(4.5rem, 10vw, 10rem);
  border-top: 1px solid var(--line);
  text-align: center;
}

.statement__text {
  max-width: 26ch;
  margin-inline: auto;
  font-size: clamp(2rem, 1rem + 4.4vw, 5rem);
  font-weight: 500;
  letter-spacing: -0.045em;
  line-height: 1.02;
  color: var(--ink);
}

.statement__note {
  max-width: 44ch;
  margin: clamp(1.5rem, 3vw, 2.25rem) auto 0;
  font-size: var(--step-lead);
  line-height: 1.5;
  letter-spacing: -0.019em;
  color: var(--ink-body);
}

/* ==========================================================================
   Facts strip, a few true numbers on one line, no giant counters. Reads as a
   caption to the page rather than a claim about scale.
   ========================================================================== */

.facts {
  display: flex;
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  flex-wrap: wrap;
  gap: 0.5rem 2.5rem;
  align-items: baseline;
  justify-content: center;
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
  border-top: 1px solid var(--line);
  font-size: var(--step-small);
  color: var(--ink-muted);
}

.facts b {
  font-size: 1.5em;
  font-weight: 500;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.facts span {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
}

@media (max-width: 700px) {
    }

/* ==========================================================================
   Asymmetry

   A two-column grid with both cells starting at the same y is the single most
   template-looking thing a page can do. The second tile drops.
   ========================================================================== */

@media (min-width: 861px) {
  .ptiles {
    align-items: start;
  }

  .ptile:nth-child(2) {
    margin-top: clamp(2rem, 5.5vw, 5rem);
  }
}

/* ==========================================================================
   Rules draw themselves in

   Everything fading up by the same 14px is the other tell. The register rules
   pull out from the left instead, which is the one motion on the site that
   belongs to the concept rather than to a scroll library.
   ========================================================================== */

.js .eyebrow.reveal .eyebrow__rule {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.1s var(--ease) 140ms;
}

.js .eyebrow.reveal.is-in .eyebrow__rule {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .js .eyebrow.reveal .eyebrow__rule,
  .js .eyebrow.reveal.is-in .eyebrow__rule {
    transform: none;
    transition: none;
  }
}



/* ==========================================================================
   Showcase

   The products, at full size, one screen each, with the whole viewport taking
   that product's colour while you are on it. This is the part of the site that
   is supposed to land: a holding company whose entire substance is its products
   should show them at the scale of the screen, not as cards in a grid.

   Structure: a sticky viewport that stays put, driven by tall invisible steps
   scrolling past it. Everything visible cross-fades between steps.
   ========================================================================== */

.showcase {
  position: relative;
}

.showcase__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  display: grid;
  place-items: center;
}

/* Colour floods the screen. Deep and saturated rather than tinted: this is the
   one place the site is allowed to be loud. */
.showcase__wash {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 900ms var(--ease);
  -webkit-mask-image: linear-gradient(
    180deg, transparent 0%, #000 26%, #000 82%, transparent 100%
  );
  mask-image: linear-gradient(
    180deg, transparent 0%, #000 26%, #000 82%, transparent 100%
  );
}

.showcase__wash[data-for="textly"] {
  background:
    radial-gradient(120% 90% at 18% 15%, rgba(255, 107, 69, 0.55), transparent 62%),
    radial-gradient(90% 80% at 85% 90%, rgba(255, 138, 90, 0.3), transparent 60%),
    linear-gradient(160deg, #2a1009 0%, #120805 60%, var(--ground) 100%);
}

.showcase__wash[data-for="hustly"] {
  background:
    radial-gradient(120% 90% at 82% 18%, rgba(77, 63, 209, 0.6), transparent 62%),
    radial-gradient(90% 80% at 12% 88%, rgba(255, 118, 95, 0.22), transparent 58%),
    linear-gradient(160deg, #14103a 0%, #0b0918 60%, var(--ground) 100%);
}

.showcase__wash[data-for="sprezz"] {
  background:
    radial-gradient(120% 90% at 50% 12%, rgba(232, 232, 224, 0.16), transparent 62%),
    linear-gradient(160deg, #1a1a18 0%, #101010 60%, var(--ground) 100%);
}

.showcase[data-active="textly"] .showcase__wash[data-for="textly"],
.showcase[data-active="hustly"] .showcase__wash[data-for="hustly"],
.showcase[data-active="sprezz"] .showcase__wash[data-for="sprezz"] {
  opacity: 1;
}

.showcase__inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(2rem, 6vw, 6rem);
  width: 100%;
  max-width: var(--measure);
  padding-inline: var(--gutter);
}

@media (max-width: 900px) {
  .showcase__inner {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: clamp(1.5rem, 4vw, 2.5rem);
  }
}

/* Each product's words. Only the active one is in place; the others sit just
   below and transparent, so the change reads as a card being dealt. */
.showcase__panel {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity 620ms var(--ease), transform 720ms var(--ease);
  pointer-events: none;
}

.showcase[data-active="textly"] .showcase__panel[data-for="textly"],
.showcase[data-active="hustly"] .showcase__panel[data-for="hustly"],
.showcase[data-active="sprezz"] .showcase__panel[data-for="sprezz"] {
  opacity: 1;
  transform: translateY(var(--drift));
  pointer-events: auto;
}

.showcase__kicker {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.showcase__name {
  margin-top: 0.6rem;
  font-size: clamp(3rem, 1rem + 8vw, 8.5rem);
  font-weight: 500;
  letter-spacing: -0.05em;
  line-height: 0.92;
  color: #fff;
}

.showcase__line {
  max-width: 20ch;
  margin-top: clamp(1rem, 2vw, 1.5rem);
  font-size: clamp(1.0625rem, 0.9rem + 0.7vw, 1.5rem);
  line-height: 1.35;
  letter-spacing: -0.02em;
  color: rgba(255, 255, 255, 0.78);
}

.showcase__line em {
  font-family: var(--serif);
  font-style: italic;
  color: #fff;
}

.showcase__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.62);
}

@media (max-width: 900px) {
  .showcase__facts {
    justify-content: center;
  }
}

/* The device sits in perspective and swings slightly as the product changes,
   which is what stops three screens in a frame from reading as a slideshow. */
.showcase__stage {
  grid-area: 1 / 2;
  display: grid;
  place-items: center;
  perspective: 1400px;
  transform: translateY(calc((0.5 - var(--lp, 0.5)) * 4.5rem));
}

/* The words drift too, a third as far, so the two planes separate slightly as
   you move through a product. */
.showcase__inner {
  --drift: calc((0.5 - var(--lp, 0.5)) * 1.5rem);
}

.showcase__panel {
  transform: translateY(calc(1.5rem + var(--drift)));
}

@media (max-width: 900px) {
  .showcase__stage {
    grid-area: 2 / 1;
  }
}

.showcase__stage .dev {
  --dw: clamp(13rem, 24vw, 20rem);
  grid-area: 1 / 1;
  opacity: 0;
  transform: rotateY(-16deg) rotateX(4deg) translateY(2rem) scale(0.94);
  transform-origin: center;
  transition: opacity 620ms var(--ease), transform 900ms var(--ease);
}

.showcase[data-active="textly"] .dev[data-for="textly"],
.showcase[data-active="hustly"] .dev[data-for="hustly"],
.showcase[data-active="sprezz"] .dev[data-for="sprezz"] {
  opacity: 1;
  transform: rotateY(-9deg) rotateX(2deg) translateY(0) scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .showcase__panel,
  .showcase__stage .dev,
  .showcase__wash {
    transition: none;
  }
  .showcase__stage,
  .showcase__stage .dev,
  .showcase__panel {
    transform: none;
  }
}

/* Steps: invisible, tall, and the only reason the sticky pin has anything to
   do. One screen of scroll per product. */
.showcase__step {
  height: 100svh;
}

/* Which product you are on, and how far through. */
.showcase__ticks {
  position: absolute;
  z-index: 3;
  left: var(--gutter);
  bottom: clamp(1.5rem, 4vw, 3rem);
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.showcase__tick {
  position: relative;
  width: 2.25rem;
  height: 2px;
  background: rgba(255, 255, 255, 0.24);
  overflow: hidden;
}

.showcase__tick::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left;
}

/* Behind the playhead: filled. On it: filling. Ahead: empty. */
.showcase[data-active="hustly"] .showcase__tick:nth-child(1)::after,
.showcase[data-active="sprezz"] .showcase__tick:nth-child(1)::after,
.showcase[data-active="sprezz"] .showcase__tick:nth-child(2)::after {
  transform: scaleX(1);
}

.showcase[data-active="textly"] .showcase__tick:nth-child(1)::after,
.showcase[data-active="hustly"] .showcase__tick:nth-child(2)::after,
.showcase[data-active="sprezz"] .showcase__tick:nth-child(3)::after {
  transform: scaleX(var(--lp, 0));
}

/* Sprezz has no interface to show yet, so its screen says so rather than
   inventing one. */
.dev--soon .dev__screen {
  display: grid;
  place-items: center;
  background: #17171a;
  color: rgba(255, 255, 255, 0.42);
  font-family: var(--mono);
  font-size: calc(var(--dw) / 24);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  padding: 2em;
}

/* ==========================================================================
   The join between the hero and the showcase

   Black type on black, then a full screen of coral one pixel later, reads as
   two different pages stitched together. The hero now carries the first
   product's colour in its lower half, so by the time the pin engages the
   colour has already arrived and the section only turns it up.
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
}

/* The wash the hero hands over to. Its base opens on the ground colour, so the
   join carries no step in brightness. */
.showcase__wash[data-for="textly"] {
  background:
    radial-gradient(95% 70% at 22% 0%, rgba(255, 107, 69, 0.42), transparent 68%),
    radial-gradient(110% 85% at 12% 55%, rgba(255, 107, 69, 0.5), transparent 62%),
    radial-gradient(90% 80% at 88% 96%, rgba(255, 138, 90, 0.28), transparent 62%),
    linear-gradient(170deg, var(--ground) 0%, #24100a 26%, #180a06 62%, var(--ground) 100%);
}

/* ==========================================================================
   Full-screen moments

   The showcase set the page's unit of measure at one screen per idea. The
   sections after it were still a scroll of padded blocks, which is why only
   the products looked like they belonged to this company.
   ========================================================================== */

.statement {
  display: grid;
  place-items: center;
  min-height: 100svh;
  padding-block: clamp(4rem, 10vw, 8rem);
}

.statement__text {
  max-width: 22ch;
  font-size: clamp(2.4rem, 1rem + 6.4vw, 7rem);
  letter-spacing: -0.05em;
  line-height: 0.98;
}

.statement__note {
  max-width: 46ch;
  font-size: clamp(1.0625rem, 0.95rem + 0.6vw, 1.375rem);
}

.closer {
  display: grid;
  place-items: center;
  min-height: 88svh;
}

.closer__title {
  max-width: 20ch;
  font-size: clamp(2.4rem, 1rem + 5.6vw, 6rem);
  letter-spacing: -0.05em;
  line-height: 1;
}

/* The numbered convictions carried the same weight as body copy. At this scale
   they are the argument rather than a list under a heading. */
.props--large .prop {
  grid-template-columns: 4.5rem minmax(0, 1fr);
  padding-block: clamp(2.25rem, 5vw, 4rem);
}

.props--large .prop__num {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.5rem, 1rem + 1.4vw, 2.5rem);
  color: var(--ink-faint);
  padding-top: 0;
}

.props--large .prop__title {
  font-size: clamp(1.6rem, 1rem + 2.2vw, 3rem);
  letter-spacing: -0.04em;
}

.props--large .prop__text {
  max-width: 46ch;
  margin-top: 1rem;
  font-size: clamp(1rem, 0.95rem + 0.4vw, 1.25rem);
  line-height: 1.5;
}

@media (max-width: 560px) {
  .props--large .prop {
    grid-template-columns: 1fr;
  }
}

/* A colour field. Any section can take one; the variant picks the hue. Always
   masked at both edges, because a field that starts at full strength on its
   first pixel draws a line across the page. */
.band--field {
  --field:
    radial-gradient(80% 62% at 88% 26%, rgba(77, 63, 209, 0.34), transparent 66%),
    radial-gradient(70% 55% at 6% 92%, rgba(255, 107, 69, 0.12), transparent 66%),
    linear-gradient(180deg, var(--ground) 0%, #100e1e 18%, #121022 52%, #0d0b14 82%,
      var(--ground) 100%);

  position: relative;
  border-top: 0;
  isolation: isolate;
  overflow: clip;
}

.band--field::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--field);
  -webkit-mask-image: linear-gradient(
    180deg, transparent 0%, #000 20%, #000 80%, transparent 100%
  );
  mask-image: linear-gradient(
    180deg, transparent 0%, #000 20%, #000 80%, transparent 100%
  );
}

/* Textly's coral. */
.band--field--coral {
  --field:
    radial-gradient(80% 62% at 12% 24%, rgba(255, 107, 69, 0.34), transparent 66%),
    radial-gradient(70% 55% at 92% 88%, rgba(255, 138, 90, 0.16), transparent 66%),
    linear-gradient(180deg, var(--ground) 0%, #1d0d07 18%, #23100a 52%, #150a06 82%,
      var(--ground) 100%);
}

/* Hustly's violet, mirrored so two fields on one page do not repeat. */
.band--field--violet {
  --field:
    radial-gradient(80% 62% at 14% 22%, rgba(77, 63, 209, 0.36), transparent 66%),
    radial-gradient(70% 55% at 90% 90%, rgba(120, 100, 255, 0.14), transparent 66%),
    linear-gradient(180deg, var(--ground) 0%, #0f0d1e 18%, #131126 52%, #0c0a13 82%,
      var(--ground) 100%);
}

/* ==========================================================================
   Page openings

   The home page hero is a screen of type. An interior page that starts with a
   small label and a modest heading reads as a different site, so every page
   opens at the same scale.
   ========================================================================== */

.pagehead--full {
  display: grid;
  align-content: center;
  min-height: 86svh;
  padding-block: clamp(3rem, 8vw, 6rem) clamp(2.5rem, 5vw, 4rem);
}

.pagehead--full .pagehead__title {
  max-width: 17ch;
  font-size: clamp(2.6rem, 0.9rem + 7.4vw, 8rem);
  letter-spacing: -0.05em;
  line-height: 0.96;
}

.pagehead--full .lead {
  max-width: 42ch;
  margin-top: clamp(1.5rem, 3.5vw, 2.5rem);
  font-size: clamp(1.0625rem, 0.95rem + 0.7vw, 1.5rem);
}

.pagehead--full .crumb {
  margin-bottom: clamp(1.5rem, 3.5vw, 2.5rem);
}

/* ==========================================================================
   Wordmark

   No mark: the two V's set in the serif italic are the logo. On hover they lean
   into each other and settle.

   Everything moves by transform. The first version animated letter-spacing,
   which re-laid-out the wordmark on every frame and pushed the navigation
   around with it; that reflow is what read as abrupt, not the timing.
   ========================================================================== */

.wordmark__vv {
  display: inline-flex;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.24em;
  font-weight: 400;
  letter-spacing: -0.02em;
  text-transform: none;
}

.wordmark__vv b {
  display: inline-block;
  font-weight: inherit;
  transition: transform 820ms cubic-bezier(0.34, 0.8, 0.28, 1);
}

/* Staggered by 60ms, so the pair reads as one gesture rather than two things
   moving at once. */
.wordmark__vv b:last-child {
  transition-delay: 60ms;
}

.wordmark:hover .wordmark__vv b:first-child {
  transform: translateX(0.055em) rotate(-3.5deg);
}

.wordmark:hover .wordmark__vv b:last-child {
  transform: translateX(-0.055em) rotate(3.5deg);
}

@media (prefers-reduced-motion: reduce) {
  .wordmark__vv b {
    transition: none;
  }
  .wordmark:hover .wordmark__vv b {
    transform: none;
  }
}

/* ==========================================================================
   Headline rise

   Split and driven from savvant.js. Without the script, or with reduced motion,
   the headline is simply there.
   ========================================================================== */

.rw {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* The mask would otherwise clip the descenders off the serif italic, which
     hangs a long way below the baseline. Note that this padding makes the mask
     taller than the word inside it, which the start offset below has to clear. */
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}

.rw > span {
  display: inline-block;
  will-change: transform;
}

/* 105% was not enough: the offset is a percentage of the word's own height,
   while the mask is that plus the 0.16em descender padding, so the tops of the
   letters stayed visible along the bottom edge before the animation ran. */
.js .is-rising .rw > span {
  transform: translateY(125%);
  animation: word-rise 1s cubic-bezier(0.22, 1, 0.3, 1) forwards;
  animation-delay: var(--d, 0ms);
}

@keyframes word-rise {
  to {
    transform: translateY(0);
  }
}

/* Between the script adding .js and the split finishing there is no paint, so
   this never flashes; it only guards against the raw headline showing if the
   split bails out on markup it does not handle. Without the script the rule
   does not apply at all. */
.js [data-rise]:not(.is-rising) {
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .js .is-rising .rw > span {
    transform: none;
    animation: none;
  }
}
