/*
 * The palette, transcribed from `Theme.swift` in both apps.
 *
 * Dark first, because that is how the apps were designed — a deep blue-black
 * ground rather than a neutral grey, so the signal colours read as emitted
 * light. The accent is reserved for two things only: *you*, and anything you
 * can press. It is deliberately not one of the semantic colours, so a link is
 * never mistaken for a warning.
 *
 * Light values are not the dark ones dimmed. Both appearances are declared
 * separately in the apps and stay separate here, because at those luminances
 * the dark values fail contrast on white. `Scripts/contrast.mjs` checks every
 * pair in both appearances on every run of the gate.
 */

:root {
  color-scheme: dark light;

  /* Ground and surfaces */
  --ground: #0f131a;
  --card: #171c26;
  --card-raised: #202737;
  --separator: #232a38;

  /* Text. The apps have a third weight, `tertiary`; the site does not carry it.
     It measures 3.02:1 on the light ground and 2.82:1 on a light card — under
     the floor for body text either way. Not declaring it is what stops someone
     reaching for it later when they want smaller grey text. */
  --text-primary: #f0f2f6;
  --text-secondary: #98a2b3;

  /* Interaction, and "you" */
  --accent: #6ba6ff;
  --accent-soft: #101f38;
  --on-accent: #0a1626;

  /* Semantics — meaning only, never decoration */
  --good: #34d399;
  --good-soft: #0e2a20;
  --warn: #fbbf24;
  --warn-soft: #2b2211;
  --bad: #fb7185;
  --bad-soft: #2f1622;

  /* Spectrum hues, for anything that charts one network against another */
  --spectrum-1: #2dd4bf;
  --spectrum-2: #f5a524;
  --spectrum-3: #fb7185;
  --spectrum-4: #a78bfa;

  /* Type. The apps use the system face; so does the site, for the same reason:
     it is already on the machine, so nothing blocks the first paint. */
  --font-body:
    -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Spacing, on a 4px grid */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-24: 6rem;

  --radius: 0.75rem;
  --radius-large: 1.25rem;
  --measure: 38rem;
}

@media (prefers-color-scheme: light) {
  :root {
    --ground: #ffffff;
    --card: #f6f7fa;
    --card-raised: #eceff5;
    --separator: #e6e9f0;

    --text-primary: #0f131a;
    --text-secondary: #59647a;

    --accent: #1f5fd0;
    --accent-soft: #e6eefc;
    --on-accent: #ffffff;

    --good: #0e9f6e;
    --good-soft: #e4f7ef;
    --warn: #b45309;
    --warn-soft: #fdf4e3;
    --bad: #e11d48;
    --bad-soft: #fde9ee;

    --spectrum-1: #0f766e;
    --spectrum-2: #c2740a;
    --spectrum-3: #d9426b;
    --spectrum-4: #7c6be0;
  }
}

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* A token wider than the viewport pushes the whole page sideways. This was
     added for a support address in the footer, which has since been replaced by
     a form; it stays because the pages are full of things that do not break —
     an IPv6 address, a BSSID, an endpoint, a framework name. */
  overflow-wrap: break-word;
}

h1,
h2,
h3 {
  line-height: 1.15;
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 0;
}

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

a {
  color: var(--accent);
  text-underline-offset: 0.2em;
}

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

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

/* ── Layout ─────────────────────────────────────────────────────────────── */

.shell {
  width: 100%;
  max-width: 68rem;
  margin-inline: auto;
  padding-inline: var(--space-6);
}

main > section {
  padding-block: var(--space-16);
  border-top: 1px solid var(--separator);
}

main > section:first-child {
  border-top: 0;
}

/* Keyboard-only escape from the header. Off-screen until focused, rather than
   display:none, which removes it from the tab order and defeats the point. */
.skip {
  position: absolute;
  left: -100vw;
  top: var(--space-2);
  z-index: 2;
  padding: var(--space-2) var(--space-4);
  background: var(--card-raised);
  color: var(--text-primary);
  border-radius: var(--radius);
  text-decoration: none;
}

.skip:focus {
  left: var(--space-4);
}

/* ── Header and footer ──────────────────────────────────────────────────── */

.site-header {
  border-bottom: 1px solid var(--separator);
}

.bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-4);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--text-primary);
  text-decoration: none;
  letter-spacing: -0.01em;
}

.bar nav {
  display: flex;
  gap: var(--space-6);
  font-size: 0.95rem;
}

.site-footer {
  border-top: 1px solid var(--separator);
  padding-block: var(--space-8);
  font-size: 0.95rem;
}

.site-footer p {
  margin-block: var(--space-1);
}

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

.hero {
  padding-block: var(--space-16);
}

.eyebrow {
  color: var(--accent);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: var(--space-4);
}

h1 {
  font-size: clamp(2.4rem, 7vw, 4.2rem);
  font-weight: 700;
  max-width: 16ch;
}

.lead {
  margin-top: var(--space-6);
  max-width: var(--measure);
  font-size: 1.2rem;
  color: var(--text-secondary);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

/* Not links, and deliberately not shaped like buttons — there is nowhere to go
   until the apps are on the store, and a button that does nothing is worse than
   a label that never claimed to. */
.chip {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--text-primary);
  font-size: 0.95rem;
  font-weight: 500;
}

.note {
  margin-top: var(--space-4);
  color: var(--text-secondary);
  font-size: 0.95rem;
}

/* ── Type ───────────────────────────────────────────────────────────────── */

h2 {
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
  max-width: 22ch;
}

h3 {
  font-size: 1.1rem;
  font-weight: 600;
}

.prose {
  max-width: var(--measure);
  margin-top: var(--space-6);
  color: var(--text-secondary);
}

.prose p + p {
  margin-top: var(--space-4);
}

.prose strong,
.terms dd strong {
  color: var(--text-primary);
  font-weight: 600;
}

code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: var(--card-raised);
  color: var(--text-primary);
}

/* ── Cards, events, definitions ─────────────────────────────────────────── */

/* `min(15rem, 100%)` rather than a bare `15rem`, for the same reason `.shots`
   uses `minmax(0, …)`: a track floor wider than its container overflows instead
   of shrinking, and the page starts scrolling sideways. 15rem plus the shell's
   padding needs 288px, which no phone is narrower than — but the guard costs
   one function call and removes the question. */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
  gap: var(--space-4);
  margin-top: var(--space-8);
}

.card {
  padding: var(--space-6);
  border: 1px solid var(--separator);
  border-radius: var(--radius-large);
  background: var(--card);
  overflow: hidden;
}

.card p {
  margin-top: var(--space-2);
  color: var(--text-secondary);
  font-size: 0.98rem;
}

.card-feature {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.events {
  list-style: none;
  padding: 0;
  margin: var(--space-8) 0 0;
  display: grid;
  gap: var(--space-4);
}

.events li {
  padding: var(--space-6);
  border: 1px solid var(--separator);
  border-radius: var(--radius-large);
  background: var(--card);
}

.events h3 {
  margin-top: var(--space-3);
}

.events p {
  margin-top: var(--space-2);
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* The semantic colour is the border and the dot, never the label text. Green,
   amber and red carry small text comfortably in the apps' native controls and
   do not on the web — the amber pairing measures 3.04:1 in the light
   appearance. So the meaning stays in the colour and the legibility stays in
   --text-primary. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.15rem var(--space-3);
  border-radius: 999px;
  border: 1px solid;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-primary);
}

.tag::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
}

.tag-warn {
  border-color: var(--warn);
  background: var(--warn-soft);
}

.tag-warn::before {
  background: var(--warn);
}

.tag-bad {
  border-color: var(--bad);
  background: var(--bad-soft);
}

.tag-bad::before {
  background: var(--bad);
}

.terms {
  margin-top: var(--space-8);
  max-width: 42rem;
}

.terms dt {
  font-weight: 600;
  margin-top: var(--space-6);
}

.terms dd {
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
}

/* ── The iPhone section ─────────────────────────────────────────────────── */

.split {
  display: grid;
  gap: var(--space-12);
  margin-top: var(--space-6);
  align-items: start;
}

@media (min-width: 60rem) {
  .split {
    grid-template-columns: 1fr minmax(0, 22rem);
  }
}

/* Two equal tracks rather than a flex row of fixed-width images. At 15rem each
   plus a gap they came to more than the 22rem column, so the second screenshot
   ran off the side of the page — and an overflowing child is how a page ends up
   scrolling sideways on a phone. `minmax(0, 1fr)` is the load-bearing part: a
   bare `1fr` floors at the image's intrinsic width and overflows anyway. */
.shots {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

.shots img {
  width: 100%;
  border-radius: var(--radius-large);
  border: 1px solid var(--separator);
}

/* ── Privacy and support ────────────────────────────────────────────────── */

.document {
  padding-block: var(--space-16);
  max-width: 52rem;
}

.document h2 {
  margin-top: var(--space-12);
  font-size: 1.45rem;
  max-width: none;
}

.document .lead {
  font-size: 1.15rem;
}

/* Navigation and the footer read as a row of labels, not a paragraph of links;
   the underline comes back on hover and is always there on focus. */
.bar nav a,
.site-footer a {
  text-decoration: none;
}

.bar nav a:hover,
.site-footer a:hover,
.wordmark:hover span {
  text-decoration: underline;
}

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

/* One column on a phone, copy beside the app on a wide screen. The screenshot
   is a real window rather than a device mock-up: the app has no hardware to
   sit inside, and a fake laptop bezel would be the only invented thing here. */
.hero {
  display: grid;
  gap: var(--space-12);
  align-items: center;
}

@media (min-width: 62rem) {
  .hero {
    grid-template-columns: 1fr minmax(0, 24rem);
    gap: var(--space-16);
  }
}

.hero-mark {
  display: block;
  margin-bottom: var(--space-6);
}

/* The class sits on the `img` itself, not on a wrapper. It used to be a
   `<picture>` with light and dark branches, and these selectors read
   `.hero-shot img`; when the branches went the wrapper went with them, and the
   selectors quietly stopped matching — no border, no radius, no cap on width,
   and nothing in the gate that could see it. Validity and contrast checks do
   not notice a rule that applies to nothing. */
.hero-shot,
.pane-shot {
  width: 100%;
  border-radius: var(--radius-large);
  border: 1px solid var(--separator);
}

/* Beside the events rather than under them, so the pane and the entries it
   produces are read together. It is the taller of the two columns on a phone,
   where the split collapses, so it goes second in the source. */
.pane-shot {
  position: sticky;
  top: var(--space-6);
}

/* ── Pull quote ─────────────────────────────────────────────────────────── */

.pull {
  margin: var(--space-8) 0 0;
  padding-left: var(--space-6);
  border-left: 3px solid var(--accent);
  max-width: var(--measure);
}

.pull p {
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.pull footer {
  margin-top: var(--space-3);
  color: var(--text-secondary);
  font-size: 0.98rem;
}

/* The pane thumbnail at the top of each card. Cropped below the window's tab
   bar before shipping, so six cards do not all lead with the same row of tabs —
   what distinguishes them is the pane, and that is what shows. */
.card-shot {
  display: block;
  width: calc(100% + var(--space-6) * 2);
  /* The reset's `img { max-width: 100% }` clamps that calc straight back to the
     card's content box, so the thumbnail stopped a padding's width short of the
     right edge and the bleed silently did nothing. */
  max-width: none;
  margin: calc(var(--space-6) * -1) calc(var(--space-6) * -1) var(--space-4);
  border-radius: var(--radius-large) var(--radius-large) 0 0;
  border-bottom: 1px solid var(--separator);
}

/* ── The support form ───────────────────────────────────────────────────── */

.form {
  margin-top: var(--space-8);
  max-width: 34rem;
}

.field {
  margin-top: var(--space-6);
}

.field label {
  display: block;
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.optional {
  font-weight: 400;
  color: var(--text-secondary);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: var(--space-3);
  font: inherit;
  color: var(--text-primary);
  background: var(--card);
  border: 1px solid var(--separator);
  border-radius: var(--radius);
}

.field textarea {
  resize: vertical;
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  border-color: var(--accent);
}

.field button {
  width: auto;
  padding: var(--space-3) var(--space-8);
  font: inherit;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  cursor: pointer;
}

/* The honeypot. It has to be in the document and reachable by a bot filling
   every field it finds, and never seen or focused by a person — so it is
   hidden here rather than with a style attribute, which the CSP forbids. */
.honeypot {
  position: absolute;
  left: -100vw;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
