/* ==========================================================================
   Euphoricquartz — euphoricquartz.com
   Ported from the design canvas document. No build step; edit directly.
   ========================================================================== */

/* --- Fonts (self-hosted, latin subset, variable) --------------------------- */

@font-face {
  font-family: 'Archivo';
  src: url('/assets/fonts/archivo-latin-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('/assets/fonts/jetbrains-mono-latin-var.woff2') format('woff2');
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- Tokens ---------------------------------------------------------------
   The --t-* text ramp is tuned so every step clears WCAG AA (4.5:1) against
   its background; --field clears 3:1 as the sole visual boundary of the form
   inputs. Keep those floors if you retune the palette.
   -------------------------------------------------------------------------- */

:root,
[data-theme='dark'] {
  /* Tells the browser to paint its own chrome — the textarea resize grip,
     scrollbars, the caret — in the dark variant. The grip is drawn by the UA and
     can't be reached with a color token, so without this it stays dark-on-dark. */
  color-scheme: dark;
  --bg: #060607;
  --fg: #e8e8ea;
  --fg-strong: #ffffff;
  --t-0: #c9c9d1;
  --line: #17171a;
  --line-2: #3a3a44;
  --field: #5c5c66;
  --t-1: #b0b0ba;
  --t-2: #9c9ca6;
  --t-3: #8e8e98;
  --t-4: #80808a;
  --t-5: #8a8a94;
  --surface-hover: #0c0c0e;
  --bullet: #5c5c66;
  --header-bg: rgba(6, 6, 7, 0.82);
  --focus: #ffffff;
}

[data-theme='light'] {
  color-scheme: light;
  --bg: #f6f6f3;
  --fg: #0d0d10;
  --fg-strong: #000000;
  --t-0: #2b2b31;
  --line: #e2e2dc;
  --line-2: #c0c0b8;
  --field: #8f8f88;
  --t-1: #3f3f46;
  --t-2: #55555c;
  --t-3: #63636a;
  --t-4: #6d6d72;
  --t-5: #66666d;
  --surface-hover: #eeeeea;
  --bullet: #8f8f88;
  --header-bg: rgba(246, 246, 243, 0.82);
  --focus: #0d0d10;
}

/* --- Base ------------------------------------------------------------------ */

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

html,
body {
  margin: 0;
  padding: 0;
}

/* No colour transitions on the theme switch, here or anywhere else: the tokens
   flip instantly, the canvas and CSS masks cannot interpolate, so a fade on the
   page background alone just makes everything else look like it changed early. */
html {
  background: var(--bg);
  scroll-behavior: smooth;
  /* The header is sticky, so a fragment target aligned to the viewport edge
     ends up underneath it. Offset every in-page jump (#what, #work, #contact,
     #message-sent) by the header's height: .header-inner padding-block (22px
     twice) + --control-h (34px) + the 1px bottom border. Keep in step with
     those if they change. With that cleared, a section's own top padding is
     what shows above its eyebrow, so it lands looking the way it does in flow. */
  scroll-padding-top: 79px;
}

body {
  position: relative;
  min-height: 100vh;
  overflow-x: hidden;
  background: var(--bg);
  color: var(--fg);
  font-family: Archivo, Helvetica, Arial, sans-serif;
}

::selection {
  background: var(--fg);
  color: var(--bg);
}

a {
  color: var(--fg);
  text-decoration: none;
}

a:hover {
  color: var(--fg-strong);
}

h1,
h2,
h3,
p {
  margin: 0;
}

img,
canvas {
  max-width: 100%;
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 18px;
  background: var(--fg);
  color: var(--bg);
  font-size: 14px;
  font-weight: 600;
}

.skip-link:focus {
  left: 12px;
  top: 12px;
  color: var(--bg);
}

.container {
  width: 100%;
  padding-inline: clamp(20px, 5vw, 48px);
}

.section {
  position: relative;
  z-index: 1;
  border-bottom: 1px solid var(--line);
}

/* Same tone as the hero's figure caption, the numbering and the stack tags:
   every mono label on the page sits at --t-4. */
.eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--t-4);
}

.section-title {
  font-size: clamp(27px, 3.6vw, 34px);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-weight: 600;
}

/* --- Background canvas ----------------------------------------------------- */

/* Desktop: promoted out of the hero figure into a viewport-fixed layer, so the
   cursor trail can roam the whole screen. */
.bg-field {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Touch devices: a viewport-fixed canvas has to be repositioned and redrawn on
   every scroll frame, which iOS Safari throttles during momentum scrolling —
   and its collapsing URL bar keeps changing innerHeight, reallocating the
   canvas buffer mid-scroll. Both show up as stutter. Pinned to the figure it is
   an ordinary layer that the compositor scrolls for free. The cursor trail is
   already disabled here, so nothing needs the full viewport. */
@media (pointer: coarse) {
  .bg-field {
    position: absolute;
    inset: 0;
  }
}

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--header-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

/* Three tracks: brand, the section links, and the CTA + controls. The outer
   two are equal (1fr each) so the middle track is centred on the header, not
   on whatever is left over after the brand. */
.header-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 20px;
  padding-block: 22px;
}

.nav-side {
  justify-self: end;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand-name {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.mark {
  /* The brand symbol, same geometry as favicon.svg: a 6x9 grid whose edges are
     all axis-aligned or at exactly 45 degrees. It is applied as a mask rather
     than an <img> so the fill stays var(--fg) and follows the theme on its own,
     exactly as the shape it replaced did — no per-theme asset, no swap logic. */
  --shape: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%206%209%22%3E%3Cpath%20d=%22M2%200%204%202%206%202%206%204%204%204%202%202ZM0%202%202%202%202%204%200%204ZM4%209%202%207%200%207%200%205%202%205%204%207ZM6%207%204%207%204%205%206%205Z%22/%3E%3C/svg%3E");
  height: 26px;
  aspect-ratio: 6 / 9;
  flex: none;
  background: var(--fg);
  -webkit-mask: var(--shape) center / contain no-repeat;
  mask: var(--shape) center / contain no-repeat;
}

.nav {
  /* Both header controls take their height from this, never from their own text.
     The CTA used to be sized by its line box, so its height moved with the font
     metrics — noticeably while Archivo was still loading — and stopped matching
     the .controls box beside it, which is sized by its icon. */
  --control-h: 34px;
  display: flex;
  align-items: center;
  gap: 34px;
  /* Same face as .brand-name (Archivo, semibold, tight tracking), one size
     down so the wordmark keeps the lead. The language switch inside .controls
     sets its own mono style and is not affected. */
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.nav-link {
  color: var(--t-0);
}

.nav-link:hover {
  color: var(--fg-strong);
}

.nav-cta {
  display: flex;
  align-items: center;
  height: var(--control-h);
  padding: 0 16px;
  border: 1px solid var(--line-2);
  color: var(--fg);
}

.nav-cta:hover {
  border-color: var(--fg);
}

/* Theme and language sit in one bordered box with a hairline between them, so
   the header reads as a single control cluster instead of two loose buttons.
   The group owns the border; the children have none of their own. */
.controls {
  display: flex;
  align-items: stretch;
  height: var(--control-h);
  border: 1px solid var(--line-2);
}

.controls > * + * {
  border-left: 1px solid var(--line-2);
}

.theme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 11px;
  background: transparent;
  border: 0;
  color: var(--t-2);
  cursor: pointer;
}

/* Sun while the theme is dark, moon while it is light: the icon shows the theme
   you would switch *to*, which is what the language switch beside it does as
   well. Swapped here off <html data-theme> so no script has to own it — the
   button carries no visible text, and its accessible name is the aria-label
   that oscilloscope.js keeps in step. */
.theme-icon {
  --sun: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%3E%3Ccircle%20cx=%2212%22%20cy=%2212%22%20r=%224.6%22/%3E%3Cpath%20d=%22M12%201.6v3.1M12%2019.3v3.1M22.4%2012h-3.1M4.7%2012H1.6M19.35%204.65l-2.2%202.2M6.85%2017.15l-2.2%202.2M19.35%2019.35l-2.2-2.2M6.85%206.85l-2.2-2.2%22/%3E%3C/svg%3E");
  --moon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2024%2024%22%20fill=%22none%22%20stroke=%22%23000%22%20stroke-width=%222%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22M20.4%2014.6A8.9%208.9%200%200%201%209.4%203.6a8.9%208.9%200%201%200%2011%2011Z%22/%3E%3C/svg%3E");
  width: 15px;
  height: 15px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--sun) center / contain no-repeat;
  mask: var(--sun) center / contain no-repeat;
}

[data-theme='light'] .theme-icon {
  -webkit-mask-image: var(--moon);
  mask-image: var(--moon);
}

/* A plain link, not a toggle: it points at the other language's page, so it
   works with JavaScript off and search engines follow it like any other link.
   It shows the language you would switch to, hence lang/hreflang for the
   target rather than for the current page. */
.lang-switch {
  display: flex;
  align-items: center;
  padding: 0 11px;
  color: var(--t-2);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.theme-toggle:hover,
.lang-switch:hover {
  color: var(--fg);
}

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

.hero-grid {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: 64px;
  align-items: center;
  padding-block: 56px 120px;
}

.hero-copy {
  max-width: 620px;
}

.hero-copy .eyebrow {
  margin-bottom: 28px;
}

.hero-title {
  font-size: clamp(42px, 5.2vw, 74px);
  line-height: 0.98;
  letter-spacing: -0.035em;
  font-weight: 600;
  margin-bottom: 30px;
}

.lede {
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
  max-width: 52ch;
  text-wrap: pretty;
  margin-bottom: 20px;
}

.lede:last-of-type {
  margin-bottom: 44px;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.btn {
  display: inline-block;
  padding: 15px 26px;
  font-family: Archivo, Helvetica, Arial, sans-serif;
  font-size: 14px;
  letter-spacing: -0.01em;
  cursor: pointer;
}

.btn-solid {
  background: var(--fg);
  color: var(--bg);
  border: none;
  font-weight: 600;
}

.btn-solid:hover {
  background: var(--fg-strong);
  color: var(--bg);
}

.btn-outline {
  border: 1px solid var(--line-2);
  color: var(--fg);
  font-weight: 500;
}

.btn-outline:hover {
  border-color: var(--t-3);
}

.hero-figure {
  position: relative;
  height: 500px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wave-slot {
  width: 100%;
  height: 100%;
}

.fig-caption {
  position: absolute;
  bottom: 0;
  left: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--t-4);
}

/* --- What we do ------------------------------------------------------------ */

.section-what,
.section-faq,
.section-method,
.section-team {
  padding-block: 110px;
}

/* Intro above, list below. It used to be a sticky intro column beside a 3x2
   card grid, which put three columns of text side by side; a single column
   reads top to bottom like the work list under it. */
.what-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 56px;
  align-items: start;
}

.what-intro {
  max-width: 640px;
}

.what-intro .eyebrow {
  margin-bottom: 18px;
}

.what-intro .section-title {
  margin-bottom: 18px;
}

/* Secondary text is one style everywhere — .section-sub, .cap-body, .team-list,
   .faq-a, .contact-sub and the Enterprise page's descriptions all use the same
   18px / 1.6 / --t-1 triple, stepping to 17px at ≤560px. Change it here and
   there together. */
.section-sub {
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
  text-wrap: pretty;
}

.what-actions {
  margin-top: 26px;
}

/* Rows separated by hairlines, the number in a narrow left track and
   everything else stacked in the right one. The hairlines are borders on the
   rows themselves, not a 1px background showing through a gap: the rows stay
   transparent so the cursor trail on the fixed canvas underneath shows
   through them. No hover state either — these are not links. */
.cap-grid {
  display: flex;
  flex-direction: column;
  border-bottom: 1px solid var(--line);
}

.cap {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  column-gap: 40px;
  align-items: start;
  padding: 34px 8px 36px;
  border-top: 1px solid var(--line);
}

.cap > :not(.cap-num) {
  grid-column: 2;
}

/* Two per row. Still a list, not blocks: no vertical
   rule between the pair, only the horizontal hairlines that separate rows.
   The space between the columns is padding on the items rather than a grid
   gap, so each row's hairline runs unbroken across both. */
.cap-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.cap-grid-2 .cap {
  grid-template-columns: 64px minmax(0, 1fr);
  column-gap: 28px;
}

.cap-grid-2 .cap:nth-child(odd) {
  padding-right: 36px;
}

.cap-grid-2 .cap:nth-child(even) {
  padding-left: 36px;
}

.cap-num {
  grid-row: 1 / span 3;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.1em;
  color: var(--t-4);
  padding-top: 6px;
}

.cap-title {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 12px;
}

.cap-body {
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
  max-width: 62ch;
  text-wrap: pretty;
  margin-bottom: 18px;
}

.cap-stack {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--t-4);
}

/* --- Before you go ahead (objections) ------------------------------------- */

/* One question and its answer per row, stacked, in a block that takes half the
   row: odd rows start at the left edge, even rows start at the middle. The
   same hairline rows as the capability list. */
/* No bottom rule on the list and less room under it: the section's own
   border closes the last row, so the gap before it only needs the row's own
   padding. */
.section-faq {
  padding-bottom: 56px;
}

.faq-list {
  margin: 0;
}

.faq-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 48px;
  padding: 32px 8px 34px;
  border-top: 1px solid var(--line);
}

.faq-row > * {
  grid-column: 1;
}

.faq-row:nth-child(even) > * {
  grid-column: 2;
}

.faq-q {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.35;
  margin-bottom: 10px;
  text-wrap: balance;
}

.faq-a {
  margin: 0;
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
  text-wrap: pretty;
}

/* --- How we work ------------------------------------------------------------ */

/* The steps reuse the capability list; this only strips the <ol> defaults. */
.method-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.method-list .cap-body {
  margin-bottom: 0;
}




/* --- Selected work --------------------------------------------------------- */

.work-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  margin-bottom: 56px;
}


.work-count {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  color: var(--t-4);
}









.team-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.team-list li {
  display: flex;
  gap: 12px;
  align-items: baseline;
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
}

/* A square — the site has no curves — centred on the first line of the text
   beside it: the line box is 1.6em tall, so its middle sits at 0.8em, minus
   half the square. Em-relative so it follows the text when it steps down on
   small screens. */
.bullet {
  width: 6px;
  height: 6px;
  flex: none;
  align-self: flex-start;
  margin-top: calc(0.8em - 3px);
  background: var(--bullet);
}







/* --- Team ------------------------------------------------------------------ */

/* Title on top, then two columns: the photograph on the left and everything
   read about the person on the right, with the projects underneath as the same
   two-per-row hairline list the rest of the site uses. The bio is bullets and
   not prose on purpose: it is read by someone deciding whether to trust a
   one-person company, and they scan before they read. */
.team-grid {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 56px;
  align-items: start;
  padding: 40px 38px 44px;
  border: 1px solid var(--line);
}

/* The only photograph on the site, and the only curve on it: the circle is
   masked into the file itself, on transparent corners, so there is no radius
   and no ring to draw here and the shape survives with CSS off. It is left in
   colour deliberately — the one human element on a monochrome page. Adding
   `filter: grayscale(1)` here is the whole change if that is ever reversed. */
.team-portrait img {
  display: block;
  width: 100%;
  max-width: 300px;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
}

.team-bio {
  display: flex;
  flex-direction: column;
  gap: 36px;
}

/* Name and role open the text column, level with the top of the photograph. */
.team-id {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.team-name {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.025em;
}

.team-block .field-label {
  margin-bottom: 16px;
  color: var(--t-4);
}

/* The projects live in the same box, under both columns rather than beside
   the portrait: a 2x2 card grid squeezed into the text track would be four
   narrow columns of prose. */
.team-work {
  grid-column: 1 / -1;
}

/* Tighter than the portfolio section's own head: the label sits directly on
   top of the list instead of opening a section. */
.team-work .work-head {
  margin-bottom: 26px;
}

/* --- Contact --------------------------------------------------------------- */

.section-contact {
  border-bottom: none;
}

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
  align-items: start;
  padding-block: 110px 120px;
}

.contact-copy .eyebrow {
  margin-bottom: 18px;
}

.contact-title {
  font-size: clamp(32px, 3.4vw, 46px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-weight: 600;
  margin-bottom: 22px;
}

.contact-sub {
  font-size: 18px;
  line-height: 1.6;
  color: var(--t-1);
  max-width: 40ch;
  text-wrap: pretty;
  margin-bottom: 44px;
}

.contact-details {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.field-label {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--t-3);
}

.contact-details .field-label {
  color: var(--t-4);
  margin-bottom: 8px;
}

.contact-value {
  display: inline-block;
  font-size: clamp(17px, 2.4vw, 21px);
  letter-spacing: -0.02em;
  border-bottom: 1px solid var(--line-2);
  padding-bottom: 2px;
  overflow-wrap: anywhere;
}

.contact-value:hover {
  border-color: var(--fg);
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 38px 36px 40px;
  border: 1px solid var(--line);
}

.contact-form .field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The rule above sets display, which beats the browser's own [hidden] rule —
   without this the selected-modules block would sit there empty, with its
   label showing, for every visitor who has JavaScript off. */
.contact-form .field[hidden] {
  display: none;
}

.contact-form input,
.contact-form textarea {
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--field);
  color: var(--fg);
  font-family: inherit;
  font-size: 16px;
  padding: 8px 0;
  outline: none;
}

.contact-form textarea {
  resize: vertical;
}

.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: var(--t-4);
}

/* The underline carries the focus state on its own: --field to --fg, thickened to
   2px with a box-shadow so nothing reflows. That replaces the global focus ring,
   which drew a rectangle around the whole field. Both rules are needed — the
   :focus-visible one has to override the :focus-visible outline set globally. */
.contact-form input:focus,
.contact-form textarea:focus {
  border-bottom-color: var(--fg);
  box-shadow: 0 1px 0 0 var(--fg);
}

.contact-form input:focus-visible,
.contact-form textarea:focus-visible {
  outline: none;
}

#contact-submit {
  margin-top: 8px;
  align-self: flex-start;
  padding: 14px 26px;
}

#contact-submit[disabled] {
  opacity: 0.55;
  cursor: default;
}

/* Honeypot. Off-screen rather than display:none — a bot that skips hidden
   fields still fills this one. Absolutely positioned so it stays out of the
   form's flex flow, and off to the left so it never widens the page. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Hidden until one of two things reveals it: :target, when the Worker
   redirects a no-JS submit to /#message-sent or /#message-failed, or
   .is-shown, which contact.js adds after a fetch. Same element either way. */
.form-status {
  display: none;
  font-size: 15px;
  line-height: 1.55;
  padding: 12px 0 12px 14px;
  border-left: 2px solid var(--line-2);
  color: var(--t-2);
  text-wrap: pretty;
}

.form-status:target,
.form-status.is-shown {
  display: block;
}

/* The palette is monochrome, so the two states separate by weight rather than
   by hue: success stays quiet, failure comes forward to full contrast. */
.form-status-error {
  border-left-color: var(--fg);
  color: var(--fg);
}

.form-status-error a {
  border-bottom: 1px solid var(--line-2);
}

.form-status-error a:hover {
  border-color: var(--fg);
}

/* The simulator selection, mirrored into the form so the visitor can see what
   travels with the message. Written by pricing.js; the block carries `hidden`
   in the markup and only loses it once there is something to list, so with
   JavaScript off — no simulator, no selection — nothing shows at all. A
   .field, so it keeps the form's own label-over-content rhythm. */
.form-modules-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.form-modules-list li {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 15px;
  line-height: 1.5;
  color: var(--t-1);
}

.form-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--t-4);
  text-wrap: pretty;
}

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

.site-footer {
  position: relative;
  z-index: 1;
  background: var(--bg);
  border-top: 1px solid var(--line);
  padding-block: 56px 44px;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 48px;
  margin-bottom: 56px;
}

.footer-brand {
  margin-bottom: 14px;
}

.footer-brand .mark {
  height: 24px;
}

.footer-blurb {
  font-size: 15px;
  line-height: 1.6;
  color: var(--t-3);
  max-width: 34ch;
}

.footer-col {
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-size: 14px;
}

.footer-col .field-label {
  color: var(--t-4);
  margin-bottom: 4px;
}

.footer-col a {
  color: var(--t-2);
  overflow-wrap: anywhere;
}

.footer-col a:hover {
  color: var(--fg-strong);
}

.footer-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--t-4);
}

/* --- Responsive ------------------------------------------------------------ */

@media (max-width: 1100px) {
  .nav {
    gap: 22px;
  }

  .cap {
    grid-template-columns: 72px minmax(0, 1fr);
    column-gap: 28px;
  }

  .hero-grid {
    gap: 44px;
  }

  .contact-grid {
    gap: 52px;
  }

  .team-grid {
    grid-template-columns: 240px minmax(0, 1fr);
    gap: 40px;
  }
}

@media (max-width: 820px) {
  .nav {
    gap: 16px;
  }

  /* The section links are gone here, so the middle track goes with them. */
  .header-inner {
    grid-template-columns: 1fr auto;
  }

  .nav-center,
  .nav-sections {
    display: none;
  }

  .hero-grid {
    grid-template-columns: 1fr;
    padding-block: 40px 80px;
  }

  .hero-figure {
    order: 2;
    height: min(56vw, 320px);
  }

  .what-grid,
  .contact-grid {
    grid-template-columns: 1fr;
  }

  .section-what,
  .section-faq,
  .section-method,
  .section-team {
    padding-block: 76px;
  }

  .team-grid {
    grid-template-columns: 1fr;
    gap: 30px;
    padding: 30px 24px 34px;
  }

  /* Stacked, the portrait and the name under it read as one centred unit —
     a 220px circle pinned to the left edge of a full-width column looks like
     it slipped. Only the portrait and the identity: the bullet lists below
     stay left-aligned, because centred body text is unreadable at any width. */
  .team-portrait img {
    max-width: 220px;
    margin-inline: auto;
  }

  .team-id {
    align-items: center;
    text-align: center;
  }

  .section-faq {
    padding-bottom: 40px;
  }

  .faq-row {
    grid-template-columns: 1fr;
    padding: 28px 8px 30px;
  }

  .faq-row:nth-child(even) > * {
    grid-column: 1;
  }

  .contact-grid {
    padding-block: 76px 88px;
  }

  .cap-num {
    grid-row: auto;
    padding-top: 0;
  }

  /* Stacked above the title now, so it needs its own breathing room — the
     work rows get theirs from row-gap, the cap lists have none. */
  .cap-num {
    margin-bottom: 14px;
  }

  .cap-grid-2 {
    grid-template-columns: 1fr;
  }

  .cap-grid-2 .cap:nth-child(odd),
  .cap-grid-2 .cap:nth-child(even) {
    padding-inline: 8px;
  }

  .cap > :not(.cap-num) {
    grid-column: 1;
  }

  /* .cap-grid-2 .cap is more specific than .cap, so it has to be repeated
     here or the two-per-row lists keep their 64px number column on phones
     and the text, now sent to column 1, gets squeezed into it. */
  .cap,
  .cap-grid-2 .cap {
    grid-template-columns: 1fr;
    column-gap: 0;
    padding: 30px 8px 32px;
  }

  .footer-grid {
    grid-template-columns: 1fr;
    gap: 36px;
    margin-bottom: 40px;
  }

  .contact-form {
    padding: 30px 24px 32px;
  }
}

@media (max-width: 560px) {
  .hero-title br {
    display: none;
  }

  /* Secondary text follows the lede down a step on small screens. */
  .lede,
  .section-sub,
  .cap-body,
  .team-list li,
  .faq-a,
  .contact-sub {
    font-size: 17px;
  }

  .hero-actions .btn {
    flex: 1 1 100%;
    text-align: center;
  }

  .footer-bar {
    justify-content: flex-start;
    gap: 8px;
  }
}

/* Narrowest supported viewport (320px): the header still has to fit brand +
   Contact + theme toggle on one line. The toggle keeps its aria-label, so
   dropping the visible word costs nothing to assistive tech. */
@media (max-width: 420px) {
  .header-inner {
    gap: 12px;
  }

  .brand-name {
    font-size: 14px;
  }

  .brand {
    gap: 10px;
  }

  .nav {
    gap: 10px;
  }

  .nav-cta {
    padding: 0 12px;
  }

  .theme-toggle {
    padding: 0 8px;
  }

  .lang-switch {
    padding: 0 8px;
  }
}

/* --- Touch scroll performance ----------------------------------------------
   backdrop-filter on a sticky header is a second, independent source of scroll
   jank on iOS (the canvas being viewport-fixed was the first — see .bg-field).
   The header colour is opaque enough that dropping the blur costs almost
   nothing visually. Must stay after the .site-header rule above to win.
   -------------------------------------------------------------------------- */

@media (pointer: coarse) {
  .site-header {
    background: var(--bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* --- Reduced motion --------------------------------------------------------
   The canvas animation is frozen in oscilloscope.js via the same media query.
   -------------------------------------------------------------------------- */

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

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
