/* ─────────────────────────────────────────────────────────────────────────
   CphVisuals

   The palette is not designed, it is recovered. Every colour here was sampled
   out of the previous cphvisuals.com — #EFDBA3 and #C2BBD3 come from its
   stylesheet, the six bar colours from its artboard PNG. Cph Visuals traded as
   Henrik's VFX supervision company from 2018 to 2020, so the sand ground, the
   brush-script wordmark and the colour bars are inherited equity, not
   decoration, and none of them is ours to redraw.

   The bars are the brand device and carry all the colour on the page. Body
   type stays quiet so they can: one accent (the bars' indigo) for anything
   clickable, and nothing else competes.

   Libre Franklin is here because brush script over Franklin Gothic is the old
   sign-painting pairing — the wordmark already commits to that lineage, so the
   text may as well agree with it. IBM Plex Mono appears only on real measured
   numbers, never as a label.

   Fonts are self-hosted. Never fonts.googleapis.com: hotlinking sends every
   visitor's IP to Google on every load, which would contradict the exact claim
   the tools section spends its whole budget proving.
   ───────────────────────────────────────────────────────────────────────── */

/* ── Fonts ─────────────────────────────────────────────────────────────── */

@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/libre-franklin-400.woff2") format("woff2");
}
@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/libre-franklin-600.woff2") format("woff2");
}
@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/fonts/libre-franklin-800.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/plex-mono-500.woff2") format("woff2");
}

/* ── Tokens ────────────────────────────────────────────────────────────── */

:root {
  /* Recovered from the old site's own stylesheet and artboard. */
  --sand:      #efdba3;   /* the brand ground, verbatim                    */
  --sand-line: #dcc489;   /* a rule that reads on sand                     */
  --paper:     #faf5e9;   /* sand lifted, for long-form sections           */
  --ink:       #17140e;
  --ink-2:     #413b30;
  --muted:     #6e6656;
  --rule:      #ddd0b2;
  --rule-soft: #ece2cb;

  /* The six bars, sampled from tegnebræt 1.png. */
  --bar-brick:  #a93339;
  --bar-ochre:  #ce843f;
  --bar-indigo: #544fa0;
  --bar-powder: #a2b7cf;
  --bar-green:  #77ba6a;
  --lavender:   #c2bbd3;

  /* One accent, taken from the bars rather than chosen alongside them. */
  --accent:      var(--bar-indigo);
  --accent-ink:  #ffffff;
  --accent-wash: #e6e3f2;

  --font-body: "Libre Franklin", "Helvetica Neue", Arial, system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --fs-display: clamp(2.1rem, 5vw, 3.6rem);
  --fs-h2:      clamp(1.5rem, 3vw, 2.05rem);
  --fs-h3:      1.16rem;
  --fs-lead:    1.15rem;
  --fs-small:   0.875rem;
  --fs-micro:   0.78rem;

  --gutter: 28px;
  --measure: 62ch;
}

@media (prefers-color-scheme: dark) {
  :root {
    --sand:      #2b2418;   /* the hero band, warm rather than neutral dark */
    --sand-line: #463c28;
    --paper:     #17130d;
    --ink:       #f7f0de;
    --ink-2:     #d9d0ba;
    --muted:     #9b927d;
    --rule:      #342d20;
    --rule-soft: #241f16;
    --accent:      #a49de9;   /* indigo lifted to carry contrast on dark   */
    --accent-ink:  #14102a;
    --accent-wash: #1f1b33;
  }
}

/* ── Reset ─────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

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

h1, h2, h3 {
  color: var(--ink);
  font-weight: 800;
  letter-spacing: -0.021em;
  line-height: 1.1;
  margin: 0;
  text-wrap: balance;
  overflow-wrap: break-word;
}

p { margin: 0; }
cite { font-style: italic; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

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

.skip { position: absolute; left: -9999px; top: 0; background: var(--accent); color: var(--accent-ink); padding: 10px 16px; z-index: 10; }
.skip:focus { left: 0; }

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

/* ── Frame ─────────────────────────────────────────────────────────────── */

.wrap { width: 100%; max-width: 1150px; margin: 0 auto; padding: 0 var(--gutter); }

.band { border-top: 1px solid var(--rule-soft); }
.band > .wrap { padding-top: clamp(58px, 7vw, 94px); padding-bottom: clamp(58px, 7vw, 94px); }

/* ── The wordmark ──────────────────────────────────────────────────────
   Shipped as an alpha mask rather than a black PNG, so it takes its colour
   from the page and inverts for free in dark mode. Without mask support the
   element would paint as a solid rectangle, so the colour is only applied
   inside @supports and the image is never a background otherwise. */

.wordmark { display: inline-flex; align-items: center; text-decoration: none; }

.wordmark-mark {
  display: block;
  width: 132px;
  aspect-ratio: 801 / 289;   /* the cropped mark's true ratio */
}

@supports ((-webkit-mask-image: url("/wordmark.png")) or (mask-image: url("/wordmark.png"))) {
  .wordmark-mark {
    background-color: var(--ink);
    -webkit-mask: url("/wordmark.png") left center / contain no-repeat;
    mask: url("/wordmark.png") left center / contain no-repeat;
  }
}

/* ── Masthead ──────────────────────────────────────────────────────────── */

.masthead { background: var(--sand); }
.masthead > .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px 28px; flex-wrap: wrap;
  padding-top: 20px; padding-bottom: 20px;
}

.nav { display: flex; align-items: center; gap: 24px; font-size: var(--fs-small); }
.nav a { color: var(--ink-2); text-decoration: none; }
.nav a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 4px; }

/* ── Hero ──────────────────────────────────────────────────────────────── */

.hero { background: var(--sand); }
.hero > .wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
  padding-top: clamp(40px, 5vw, 66px);
  padding-bottom: clamp(52px, 6vw, 84px);
}

.hero h1 { font-size: var(--fs-display); max-width: 17ch; margin-bottom: 22px; }
.hero-body { font-size: var(--fs-lead); color: var(--ink-2); max-width: 50ch; }
.hero-body + .hero-body { margin-top: 16px; }

.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }

.btn {
  display: inline-flex; align-items: center;
  font-size: var(--fs-small); font-weight: 600;
  padding: 11px 20px; border-radius: 2px;
  text-decoration: none; border: 1px solid transparent;
}
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { text-decoration: none; filter: brightness(1.1); }
.btn-quiet { border-color: var(--ink); color: var(--ink); }
.btn-quiet:hover { text-decoration: none; background: rgba(0,0,0,0.05); }

/* ── The bars ──────────────────────────────────────────────────────────
   The device from the original site, redrawn from measurements of its own
   artboard: six bars of identical length, staggered vertically. The stagger
   is the whole idea, so nothing here is tidied into alignment.

   This is the page's one piece of non-user-triggered motion: the bars rise
   into place once, on load, the way colour bars come up on a monitor. */

.bars { display: block; width: 100%; max-width: 290px; margin-inline: auto; height: auto; }
.bars rect { transform-box: fill-box; transform-origin: 50% 100%; }

@media (prefers-reduced-motion: no-preference) {
  .bars rect { animation: bar-rise 620ms cubic-bezier(0.22, 1, 0.36, 1) backwards; }
  .bars rect:nth-child(1) { animation-delay: 40ms; }
  .bars rect:nth-child(2) { animation-delay: 110ms; }
  .bars rect:nth-child(3) { animation-delay: 180ms; }
  .bars rect:nth-child(4) { animation-delay: 250ms; }
  .bars rect:nth-child(5) { animation-delay: 320ms; }
  .bars rect:nth-child(6) { animation-delay: 390ms; }
}

@keyframes bar-rise {
  from { transform: scaleY(0); opacity: 0; }
  to   { transform: scaleY(1); opacity: 1; }
}

/* ── Section headings ──────────────────────────────────────────────────── */

.section-head { max-width: var(--measure); margin-bottom: 40px; }
.section-head h2 { font-size: var(--fs-h2); margin-bottom: 14px; }
.section-head p { color: var(--muted); }

/* ── Credits ───────────────────────────────────────────────────────────
   Set as a credit roll, because that is the form this content already has in
   the world it comes from: the work on the left, the role and any accolade on
   the right, hairline between each. */

.credits { border-top: 1px solid var(--rule); }

.credit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 6px clamp(18px, 4vw, 48px);
  padding: 20px 0;
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}

.credit-work { color: var(--ink); font-weight: 600; }
.credit-work cite { font-style: italic; }
.credit-year { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--muted); margin-left: 8px; }
.credit-role { color: var(--ink-2); font-size: var(--fs-small); }
.credit-note { display: block; color: var(--muted); font-size: var(--fs-micro); margin-top: 3px; }

.credits-more { margin-top: 20px; font-size: var(--fs-small); color: var(--muted); }

/* ── Tools ─────────────────────────────────────────────────────────────── */

.tool {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.55fr);
  gap: clamp(18px, 4vw, 52px);
  padding: 30px 0;
  border-top: 1px solid var(--rule-soft);
  align-items: start;
}
.tool:last-of-type { border-bottom: 1px solid var(--rule-soft); }

.tool h3 { font-size: var(--fs-h3); margin-bottom: 5px; }
.tool h3 a { color: var(--ink); text-decoration: none; }
.tool h3 a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; }
.tool-domain { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--muted); }
.tool-desc { color: var(--ink-2); max-width: 54ch; }

.tool-facts { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.tool-facts li {
  font-size: var(--fs-micro); color: var(--muted);
  border: 1px solid var(--rule); border-radius: 2px; padding: 4px 9px;
}

/* ── The instrument ────────────────────────────────────────────────────
   Not a stat block: it runs a real hash loop in a worker on the visitor's own
   machine and reports what that machine did, next to a network figure read
   from the Performance API rather than asserted in copy. If any part fails,
   .instrument is swapped for the static fallback and nothing broken is shown. */

/* ── The instrument ────────────────────────────────────────────────────
   A vectorscope and a waveform strip, in a dark well inset into the sand
   page. The darkness is not a style choice: these displays are read in a
   grading suite at a few candela, and a glowing additive trace has nothing
   to glow against on a light ground. The chassis keeps hard corners and a
   hairline border for the same reason — instruments are not rounded. */

.instrument {
  margin-top: 44px;
  max-width: 560px;
  background: #0c0f11;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  padding: 18px 18px 16px;
  /* Reserved so swapping the static fallback for the live panel, 0.3-3s
     after load once the network goes quiet, cannot reflow the page below. */
  min-height: 430px;
}

.instrument-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding-bottom: 14px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.instrument-title { font-weight: 600; font-size: var(--fs-small); color: #dfe6e1; }

.state {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em;
  text-transform: uppercase; color: rgba(126, 196, 154, 0.66);
}
.state::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: #4bff8c;
}
.state.is-running::before { animation: pulse 1.15s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.25; } }

.scope-grid {
  display: grid;
  grid-template-columns: minmax(0, 240px) minmax(0, 1fr);
  gap: 20px;
  padding: 18px 0 16px;
  align-items: center;
}

#scope, #wave {
  display: block; width: 100%; height: auto;
  background: #06080a;
  border: 1px solid rgba(255, 255, 255, 0.07);
}
#scope { aspect-ratio: 1; }
#wave { height: 74px; }

.readouts { display: flex; flex-direction: column; gap: 16px; }
.readout-label {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.07em;
  text-transform: uppercase; color: rgba(126, 196, 154, 0.5); margin-bottom: 5px;
}

.readout-value {
  font-family: var(--font-mono); font-weight: 500;
  font-size: 1.5rem; color: #eef3ef; line-height: 1.1;
  /* Tabular figures and a reserved slot, or a live counter shimmers
     sideways on every digit change — the cheapest tell there is. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.02em;
  min-width: 6ch;
}
.readout-value .unit {
  font-size: 0.72rem; color: rgba(126, 196, 154, 0.55);
  margin-left: 6px; letter-spacing: 0.04em;
}
.readout-value.is-zero { color: #4bff8c; }

.instrument-note {
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  font-size: var(--fs-micro); color: #8d968f; line-height: 1.6;
}
/* Must follow .instrument-note: equal specificity, so source order is what
   makes the override land. The fallback has no readout above it to divide. */
.instrument-note-plain { border-top: 0; margin-top: 14px; padding-top: 0; }

@media (max-width: 560px) {
  .scope-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .instrument { min-height: 0; }
}

/* ── Services ──────────────────────────────────────────────────────────── */

.services { display: grid; grid-template-columns: repeat(auto-fit, minmax(225px, 1fr)); gap: 32px 40px; }
/* min-height so a heading that wraps to two lines does not drop its rule
   below the others' — the four rules read as one line across the row. */
.service h3 { font-size: 1rem; min-height: 2.2em; margin-bottom: 8px; padding-bottom: 11px; border-bottom: 2px solid var(--ink); }
.service p { font-size: var(--fs-small); color: var(--muted); }

/* The concrete way in, given the accent treatment the four capability
   columns deliberately do not get — a visitor who is sold needs one obvious
   next step, not a fifth equal option. */
.offer {
  margin-top: 40px;
  padding: 22px 24px;
  background: var(--accent-wash);
  border-left: 2px solid var(--accent);
  max-width: var(--measure);
}
.offer h3 { font-size: 1.05rem; margin-bottom: 8px; }
.offer p { font-size: var(--fs-small); color: var(--ink-2); }

.plain-note {
  margin-top: 26px;
  font-size: var(--fs-small); color: var(--muted); max-width: var(--measure);
}

/* ── Contact ───────────────────────────────────────────────────────────── */

.contact { background: var(--sand); border-top: 1px solid var(--sand-line); }
.contact > .wrap { padding-top: clamp(52px, 6vw, 80px); padding-bottom: clamp(52px, 6vw, 80px); }

.contact-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(26px, 5vw, 60px); align-items: start; }
.contact-head { font-size: var(--fs-h2); margin-bottom: 20px; }

.contact-line { font-size: clamp(1.15rem, 2.6vw, 1.6rem); font-weight: 800; letter-spacing: -0.03em; word-break: break-word; }
.contact-line + .contact-line { margin-top: 8px; }
.contact-line a { color: var(--ink); text-decoration-color: var(--accent); }
.contact-line a:hover { color: var(--accent); }

.contact-meta { font-size: var(--fs-small); color: var(--ink-2); }
.contact-meta p + p { margin-top: 12px; }

/* ── Footer ────────────────────────────────────────────────────────────── */

.foot { background: var(--sand); border-top: 1px solid var(--sand-line); }
.foot > .wrap {
  padding-top: 26px; padding-bottom: 40px;
  display: flex; justify-content: space-between; gap: 16px 36px; flex-wrap: wrap;
  font-size: var(--fs-micro); color: var(--muted);
}
.foot a { color: var(--muted); }
.foot a:hover { color: var(--ink); }
.foot-links { display: flex; gap: 20px; flex-wrap: wrap; }

/* ── 404 ───────────────────────────────────────────────────────────────── */

.stub > .wrap { padding-top: clamp(60px, 9vw, 110px); padding-bottom: clamp(76px, 12vw, 150px); }
.stub h1 { font-size: var(--fs-h2); max-width: 20ch; margin-bottom: 16px; }

/* ── Narrow ────────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  :root { --gutter: 20px; }
  .hero > .wrap { grid-template-columns: minmax(0, 1fr); }
  /* The bars lead on a phone, where there is no room beside the headline. */
  .hero-copy { order: 2; }
  .hero-art { order: 1; }
  .bars { max-width: 210px; margin-inline: 0; }
  .tool { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .repos { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .credit { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .contact-grid { grid-template-columns: minmax(0, 1fr); }
  .nav { gap: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
