/* Landing page styles.
 *
 * A separate file rather than a <style> block, because the Content-Security
 * Policy is style-src 'self' with no 'unsafe-inline' - an inline block would be
 * silently dropped and the page would render unstyled. Deliberately plain CSS
 * with no build step: this file is served to a crawler and to somebody on a bad
 * connection, and it should not depend on the application bundle.
 */
/* Self-hosted, so this page makes NO third-party request.
 *
 * The app loads its typefaces from Google Fonts, which the privacy policy
 * discloses and lists self-hosting as an open item. The landing page is the
 * first thing a stranger loads, and it would be an odd page on which to hand a
 * third party the reader's IP address while telling them how carefully their
 * data is handled. These three files are 109KB total and they ship with the
 * image.
 *
 * font-display: swap so text is readable immediately on a slow connection
 * rather than invisible while the face downloads.
 */
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/fonts/plex-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/fonts/plex-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Bebas Neue';
  src: url('/fonts/bebas-neue.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --bg: #08090b;
  --bg2: #0e1014;
  --ink: #e9ecf1;
  --muted: #8b93a1;
  --faint: #5b626e;
  --edge: #1c2027;
  --accent: #ff7a45;
  --ok: #4ade80;
  --warn: #fbbf24;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) {
    --bg: #ffffff; --bg2: #f6f7f9; --ink: #14171c; --muted: #5b626e;
    --faint: #8b93a1; --edge: #e3e6ea;
  }
}
* { box-sizing: border-box; }
/* IBM Plex Sans for body copy.
 *
 * Third face tried here, and the reasoning narrowed each time. Inter is what
 * the app is set in and it is the default every SaaS landing page arrives in -
 * correct for a dense instrument panel, characterless for a page whose job is
 * to be read. A serif was the opposite swing and read as a document rather
 * than as a product.
 *
 * Plex sits between them and is the better fit for what this actually is: a
 * face drawn for engineering documentation, with enough character in the
 * letterforms to not read as a default, and none of the warmth that would make
 * a compliance tool look like a newsletter. It also holds up at small sizes,
 * which matters because most of this page is 0.95rem.
 *
 * Bebas stays for headings; the contrast between an industrial condensed
 * display face and a technical body face is the type idea. */
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 16.5px/1.6 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* The wing behind the hero.
 *
 * A wing's venation is a graph - a root, veins branching from it, cells
 * between them - which is the same structure as the access path this product
 * draws further down the page, so the mark says something about the product
 * rather than merely decorating it. Generated by cmd/wing; the geometry lives
 * there, not here.
 *
 * A local SVG, because the page's claim to make no third-party request is one
 * it has to keep, and because a wireframe is a few kilobytes of vector rather
 * than a photograph.
 *
 * The right half of the hero is empty at every width above phone size - the h1
 * is capped at 18ch - so this occupies space that was already blank instead of
 * competing with anything. */
body { position: relative; }
body::before {
  content: ""; position: absolute; top: 0; right: 0;
  width: min(62rem, 82vw); aspect-ratio: 1100 / 980;
  background: url('/wing.svg') top right / contain no-repeat;
  opacity: 0.58;
  /* Brightest at the corner it flies out of and gone before it reaches the
   * headline, so the type never has to compete with it. */
  -webkit-mask-image: radial-gradient(122% 116% at 94% 4%, #000 30%, transparent 80%);
  mask-image: radial-gradient(122% 116% at 94% 4%, #000 30%, transparent 80%);
  pointer-events: none;
  z-index: 0;
}
/* Below ~48rem the hero has no empty half left to give, so the wing drops back
 * far enough to be texture rather than an illustration behind the words. */
@media (max-width: 48rem) { body::before { opacity: 0.28; } }
/* Orange on white carries less than orange on near-black, so the light theme
 * needs more of it to register at all. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) body::before { opacity: 0.7; }
}

/* Above the wing. Stated as a stacking order rather than left to source order,
 * because a positioned pseudo-element and static content do not simply stack
 * in document order. */
.wrap { max-width: 56rem; margin: 0 auto; padding: 3rem 1.5rem 4rem; position: relative; z-index: 1; }
header.top { display: flex; align-items: center; justify-content: space-between; }
.mark {
  font: 400 1.75rem/1 'Bebas Neue', ui-sans-serif, system-ui, sans-serif;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent);
  text-decoration: none;
}
.mark span { color: var(--ink); }
.btn {
  display: inline-block; border-radius: 6px; padding: 0.6rem 1.1rem;
  font-size: 0.9rem; text-decoration: none; border: 1px solid var(--edge);
  color: var(--ink);
}
.btn:hover { background: var(--bg2); }
.btn-primary { background: var(--accent); color: #14171c; border-color: var(--accent); font-weight: 600; }
/* The primary button needs its OWN hover, and this is not a nicety.
 *
 * `.btn:hover` is a class plus a pseudo-class, so it outranks `.btn-primary`
 * on specificity. Hovering the primary button therefore took the plain hover
 * background - dark - while keeping the dark text the filled style sets, and
 * the label vanished at exactly the moment somebody was about to click it.
 *
 * Stated as its own rule rather than fixed by reordering, because source order
 * only decides ties and these are not tied. */
.btn-primary:hover { background: #ff9166; color: #14171c; border-color: #ff9166; }
h1 {
  margin: 4rem 0 0; font-family: 'Bebas Neue', ui-sans-serif, sans-serif;
  font-weight: 400; font-size: clamp(2.5rem, 6vw, 3.75rem); line-height: 1.02;
  letter-spacing: 0.02em; max-width: 18ch;
}
h1 .alt { color: var(--accent); }
.lede { margin-top: 1.5rem; max-width: 38rem; color: var(--muted); font-size: 1.15rem; }
.cta { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.cta small { color: var(--muted); }
h2 {
  margin: 3.5rem 0 0; font-family: 'Bebas Neue', ui-sans-serif, sans-serif;
  font-weight: 400; font-size: 1.5rem; letter-spacing: 0.06em;
}
.eyebrow {
  margin: 4rem 0 0; font-family: ui-sans-serif, system-ui, sans-serif;
  font-size: 0.7rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--muted);
  border-top: 1px solid var(--edge); padding-top: 2rem;
}
ul.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 0.75rem 0 0; }
ul.chips li { border: 1px solid var(--edge); background: var(--bg2); border-radius: 6px; padding: 0.4rem 0.75rem; font-size: 0.9rem; }
.grid { display: grid; gap: 1.5rem 2rem; margin-top: 2rem; }
@media (min-width: 40rem) { .grid { grid-template-columns: 1fr 1fr; } }
.grid h3 { margin: 0 0 0.4rem; font-family: 'Bebas Neue', sans-serif; font-weight: 400; font-size: 1.25rem; letter-spacing: 0.05em; }
.grid p { margin: 0; color: var(--muted); font-size: 0.95rem; }
/* Screenshots as figures, not as windows.
 *
 * Each image is a CROP of one region of the interface, captured at 2x by
 * cmd/shots, and it sits on a stage: a framed ground with the accent glowing
 * in from a corner and a faint dot grid, the way a detail is lifted out of a
 * larger instrument for inspection. The bottom edge of every crop dissolves
 * into the stage rather than ending in a hard line - these are details from
 * a page that continues, and a hard edge would read as "this is all there is".
 *
 * A plate above each one names where in the product it was taken, in the same
 * small monospace caps the app uses for its own labels. */
figure { margin: 3rem 0 0; }
.stage {
  position: relative; overflow: hidden;
  border: 1px solid var(--edge); border-radius: 12px;
  padding: 3.25rem 2.5rem 0;
  background:
    radial-gradient(90% 70% at 12% 0%, rgba(255, 122, 69, 0.22), transparent 62%),
    radial-gradient(var(--edge) 1px, transparent 1.5px) 0 0 / 22px 22px,
    var(--bg2);
}
.stage img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--edge); border-radius: 10px;
  box-shadow: 0 40px 80px -30px rgba(0, 0, 0, 0.85);
}
/* The wide crops are cut mid-list, so their bottom edge dissolves into the
 * stage. The hero is a complete card and keeps its edge: its last row is the
 * unverified control the caption is about, and fading it out would lose the
 * point of the picture. */
.shot-wide .stage img {
  border-bottom: 0; border-radius: 10px 10px 0 0;
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}
.shot-hero .stage { padding-bottom: 2.5rem; }
/* A whole card rather than a crop. It gets the closing edge the cropped shots
 * deliberately lack, and keeps the full column width - the graph is the detail,
 * so shrinking it to the hero's 48rem would defeat the point of showing it. */
.shot-full .stage { padding-bottom: 2.5rem; }
.plate {
  position: absolute; top: 1.15rem; left: 2.5rem;
  font: 400 0.65rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--faint);
}
.plate b { font-weight: 400; color: var(--accent); }
/* The hero crop is narrow enough to show at its natural size, centred. */
.shot-hero .stage img { max-width: 48rem; margin: 0 auto; }
/* The wide crops run edge to edge on a stage that is wider than the column,
 * so the interface reads at a usable scale rather than shrinking to fit. */
@media (min-width: 66rem) {
  .shot-wide .stage { margin-left: -3.5rem; margin-right: -3.5rem; }
}
figcaption { margin-top: 0.85rem; }
figcaption b { display: block; font-family: 'Bebas Neue', sans-serif; font-weight: 400; font-size: 1.25rem; letter-spacing: 0.05em; }
figcaption span { display: block; margin-top: 0.35rem; color: var(--muted); font-size: 0.95rem; max-width: 44rem; }
figcaption em { display: block; margin-top: 0.3rem; color: var(--faint); font-size: 0.8rem; font-style: normal; }
.panel { margin-top: 3.5rem; border: 1px solid var(--edge); background: var(--bg2); border-radius: 8px; padding: 1.5rem; }
.panel h2 { margin-top: 0; }
.panel p { color: var(--muted); font-size: 0.95rem; }
.panel .keep { color: var(--ink); }
/* The footer does real work on a page this long.
 *
 * The only call to action is in the header, seven thousand pixels up. A reader
 * who has scrolled the whole way has finished deciding and has nothing to act
 * on, so the footer carries the map of the page, the providers - the first
 * thing a stranger filters on - a way to reach a person, and the sign-in link
 * one last time. */
footer.bot { margin-top: 5rem; border-top: 1px solid var(--edge); padding-top: 2.5rem; }
.bot-cols { display: grid; gap: 2.5rem 2rem; grid-template-columns: 1fr; }
@media (min-width: 38rem) { .bot-cols { grid-template-columns: repeat(2, 1fr); } }
/* The identity column is widest so the wordmark anchors the block rather than
 * sitting as one of four equal columns. */
@media (min-width: 62rem) { .bot-cols { grid-template-columns: 1.7fr 1.15fr 0.9fr 1fr; } }

.bot-id p { margin: 1rem 0 0; color: var(--muted); font-size: 0.85rem; max-width: 24rem; }
.bot-id .op { margin-top: 0.7rem; color: var(--faint); font-size: 0.78rem; }
/* Monospace and letterspaced, matching .plate: both are labels ON something
 * rather than headings IN the document. Deliberately not <h2> - these would
 * land in the heading outline beside the page's real sections, and this page
 * is written to be crawled. */
.col-head {
  margin: 0 0 0.9rem;
  font: 400 0.68rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--faint);
}
footer.bot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
footer.bot li { font-size: 0.85rem; color: var(--muted); }
footer.bot a { color: var(--muted); text-decoration: none; }
footer.bot a:hover, footer.bot a:focus-visible { color: var(--ink); }
/* More specific than `footer.bot a`, which would otherwise mute the wordmark -
 * the same specificity trap that made the primary button invisible on hover. */
footer.bot a.mark { font-size: 1.5rem; color: var(--accent); }

.bot-end {
  margin-top: 2.5rem; border-top: 1px solid var(--edge); padding-top: 1.25rem;
  display: flex; flex-wrap: wrap; gap: 0.6rem 1.5rem; align-items: baseline;
  /* --muted, not --faint: this line is content, and --faint on the light
   * ground is about 2.6:1 - fine for a decorative plate, not for text. */
  color: var(--muted); font-size: 0.78rem;
}
.bot-end a { margin-left: auto; }

a { color: var(--accent); }
/* Anchored sections land under the top edge rather than flush against it. */
[id] { scroll-margin-top: 2rem; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

/* The worked example: one control, its check, and the finding it raises.
 *
 * Laid out as three numbered-feeling steps rather than prose, because the point
 * is that they are a CHAIN. A reader who only looks at this block should come
 * away knowing what the product does. */
.chain { display: grid; gap: 1rem; margin-top: 1.5rem; }
@media (min-width: 52rem) { .chain { grid-template-columns: repeat(3, 1fr); } }
.step {
  border: 1px solid var(--edge); background: var(--bg2);
  border-radius: 8px; padding: 1rem 1.1rem;
}
.step .tag {
  display: block; font-size: 0.65rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 0.5rem;
}
.step p { margin: 0; font-size: 0.95rem; }
.step p.fail { color: var(--warn); }
.step code { font-size: 0.85rem; word-break: break-word; }
.step em { display: block; margin-top: 0.6rem; font-style: normal; font-size: 0.82rem; color: var(--muted); }
.aside { margin-top: 1.25rem; max-width: 48rem; color: var(--muted); font-size: 0.9rem; }

/* Coverage. A table because it IS tabular - five providers compared across the
 * same three dimensions - and because a crawler reads a table as data. */
.cov { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 0.92rem; }
.cov th, .cov td { text-align: left; padding: 0.5rem 0.75rem 0.5rem 0; border-bottom: 1px solid var(--edge); vertical-align: top; }
.cov th { font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); font-weight: 400; }
.cov td:nth-child(2), .cov td:nth-child(3) { font-variant-numeric: tabular-nums; color: var(--ink); }
.cov td:nth-child(4) { color: var(--muted); }
/* The table is the one thing here that can outgrow a narrow screen, so it
 * scrolls inside itself rather than pushing the page sideways. */
.cov-wrap { overflow-x: auto; }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em; color: var(--ink); }
