/* ============================================================================
   Halation site styles

   The palette, the typefaces and the metrics are lifted from the application's
   own theme (Halation.App/Themes/Theme.xaml) so the site and the tool read as
   one thing. Where a value appears here it should agree with the key of the
   same name there; the severity ramp especially, because those colours carry
   meaning rather than decoration.
   ========================================================================= */

/* ---- Typefaces ---------------------------------------------------------- */
/* Self-hosted rather than fetched from a font CDN. This site belongs to a tool
   whose whole claim is that nothing leaves your machine, so it does not hand a
   third party a record of everyone who reads about it. Both faces are SIL Open
   Font Licence; the licences ship beside them. */

@font-face {
  font-family: 'Barlow';
  src: url('../fonts/Barlow-Light.ttf') format('truetype');
  font-weight: 300;
  font-display: swap;
}

@font-face {
  font-family: 'Barlow';
  src: url('../fonts/Barlow-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Barlow';
  src: url('../fonts/Barlow-SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}

@font-face {
  font-family: 'Oxanium';
  src: url('../fonts/Oxanium-VariableFont_wght.ttf') format('truetype-variations');
  font-weight: 200 800;
  font-display: swap;
}

/* ---- Palette ------------------------------------------------------------ */

:root {
  --bg: #050505;
  --panel: #0d0d0b;
  --panel-raised: #16160a;
  --edge: rgba(138, 230, 110, 0.36);
  --edge-soft: rgba(138, 230, 110, 0.16);
  --text: #ededed;
  --muted: #b5ae7a;
  --accent: #f9f002;
  --accent-wash: rgba(249, 240, 2, 0.12);
  --hover: #231f05;
  --lime: #8ae66e;

  /* Severity ramp. Read left to right as worsening. High is scarlet rather
     than orange so it does not sit on top of the theme's own yellow. */
  --critical: #ff003c;
  --high: #ff5a28;
  --medium: #f9f002;
  --low: #2fb6d4;
  --good: #39ff14;
  --unknown: #6b7280;

  /* --mono is a system stack, so anything set in it is text the site does not
     control the appearance of. That is correct for code, where a monospace is
     expected and the reader's own is fine, and wrong for a label, where it was
     the one thing on the page not set in the site's own faces. Labels are
     Barlow; --mono is now reached only by code, kbd, pre and .rule__id, the
     last because a rule identifier is a machine token somebody copies into a
     search box and monospace keeps 0 apart from O.

     Every label that moved states its own font-weight. Body is 300 and Barlow
     ships 300, 400 and 600 only, so an unstated weight either renders as Light
     or gets synthesised: th in particular had no weight at all and was taking
     the browser's bold. */
  --ui: 'Barlow', 'Segoe UI', system-ui, sans-serif;
  --display: 'Oxanium', 'Barlow', system-ui, sans-serif;
  --mono: ui-monospace, 'Cascadia Mono', 'Cascadia Code', Consolas, monospace;

  --notch: 18px;
  --shell: 1180px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
}

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

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 17px;
  font-weight: 300;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* height:auto matters here. Every screenshot carries width and height
   attributes so the space is reserved before it loads, and without this the
   attribute height survives the CSS width and the picture is stretched. */
img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--accent);
  text-decoration-color: rgba(249, 240, 2, 0.35);
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-color: var(--accent);
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 0.6em;
  text-wrap: balance;
}

h1 { font-size: clamp(2.6rem, 7vw, 4.6rem); }
h2 { font-size: clamp(1.8rem, 3.6vw, 2.7rem); }
h3 { font-size: clamp(1.15rem, 2vw, 1.45rem); }
h4 { font-size: 1.02rem; }

p { margin: 0 0 1.1em; }

p:last-child { margin-bottom: 0; }

code, kbd, pre {
  font-family: var(--mono);
  font-size: 0.88em;
}

code {
  color: var(--lime);
  background: rgba(138, 230, 110, 0.08);
  padding: 0.1em 0.36em;
  border-radius: 2px;
}

pre {
  background: var(--panel);
  border: 1px solid var(--edge-soft);
  border-left: 2px solid var(--accent);
  padding: 1rem 1.15rem;
  overflow-x: auto;
  margin: 0 0 1.4rem;
  line-height: 1.55;
}

pre code {
  background: none;
  padding: 0;
  color: var(--text);
}

::selection {
  background: var(--accent);
  color: #000;
}

/* Present for a screen reader, absent for everyone else. */
.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;
}

/* Skip link, visible only once it has focus. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: #000;
  padding: 0.6rem 1rem;
  font-weight: 600;
}

.skip:focus {
  left: 0;
}

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

/* ============================================================================
   The animated backdrop

   Four fixed layers behind everything: a grid, a slow diagonal sweep, scan
   lines, and a vignette. The application's own backdrop does the same thing
   with the same 9 second sweep, which is where that number comes from.
   ========================================================================= */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}

.backdrop__grid {
  position: absolute;
  inset: -2px;
  background-image:
    linear-gradient(rgba(138, 230, 110, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(138, 230, 110, 0.055) 1px, transparent 1px);
  background-size: 88px 88px;
  mask-image: radial-gradient(ellipse 120% 90% at 50% 0%, #000 20%, transparent 78%);
}

.backdrop__sweep {
  position: absolute;
  top: -30%;
  left: -60%;
  width: 55%;
  height: 160%;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(249, 240, 2, 0.05) 45%,
    rgba(249, 240, 2, 0.09) 50%,
    rgba(249, 240, 2, 0.05) 55%,
    transparent 100%
  );
  transform: rotate(8deg);
  animation: sweep 9s linear infinite;
}

@keyframes sweep {
  from { transform: translateX(0) rotate(8deg); }
  to   { transform: translateX(340%) rotate(8deg); }
}

.backdrop__scan {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0) 0px,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.28) 3px,
    rgba(0, 0, 0, 0.28) 4px
  );
  opacity: 0.45;
}

.backdrop__vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 90% 70% at 50% 40%, transparent 30%, rgba(0, 0, 0, 0.75) 100%);
}

/* ============================================================================
   Notched surfaces

   A card is a filled panel with the top-right corner cut away, drawn as two
   clipped layers: the outer one is the border colour, the inner one sits a
   pixel inside it and is the fill. A border property cannot be used because
   clip-path cuts it off with everything else.
   ========================================================================= */

.card {
  position: relative;
  z-index: 0;
  padding: 1.6rem 1.75rem;
  background: transparent;
}

.card::before,
.card::after {
  content: '';
  position: absolute;
  z-index: -1;
  clip-path: polygon(
    0 0,
    calc(100% - var(--notch)) 0,
    100% var(--notch),
    100% 100%,
    0 100%
  );
}

.card::before {
  inset: 0;
  background: var(--edge);
}

.card::after {
  inset: 1px;
  background: var(--panel);
}

/* Cut on the opposite corner, for alternating rows. */
.card--flip::before,
.card--flip::after {
  clip-path: polygon(
    0 0,
    100% 0,
    100% 100%,
    var(--notch) 100%,
    0 calc(100% - var(--notch))
  );
}

.card--raised::after { background: var(--panel-raised); }

.card--glow::before {
  background: var(--accent);
  filter: drop-shadow(0 0 16px rgba(249, 240, 2, 0.4));
}

/* Lifts on hover. The glow is a separate layer faded in rather than an
   animated shadow, the same reason the application does it that way. */
.card--lift {
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.card--lift::before {
  transition: background 0.35s ease, filter 0.35s ease;
}

.card--lift:hover {
  transform: translateY(-4px);
}

.card--lift:hover::before {
  background: rgba(249, 240, 2, 0.55);
  filter: drop-shadow(0 0 14px rgba(249, 240, 2, 0.3));
}

/* ---- Reticle: the drop zone's four corner brackets ---------------------- */

.reticle {
  position: relative;
  padding: clamp(2rem, 6vw, 4rem) clamp(1.5rem, 5vw, 3.5rem);
  text-align: center;
}

.reticle__corner {
  position: absolute;
  width: 34px;
  height: 34px;
  border: 2px solid var(--accent);
  animation: breathe 4.5s ease-in-out infinite;
}

.reticle__corner:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.reticle__corner:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; }
.reticle__corner:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; }
.reticle__corner:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; }

@keyframes breathe {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

/* The faint rule that joins the brackets, so the zone reads as one region. */
.reticle::before {
  content: '';
  position: absolute;
  inset: 16px;
  border: 1px solid rgba(249, 240, 2, 0.1);
}

/* ============================================================================
   Layout
   ========================================================================= */

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

.shell--narrow { width: min(820px, 100%); }

section {
  padding-block: clamp(3.5rem, 8vw, 6.5rem);
}

.section-label {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.section-label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--edge), transparent);
}

/* Runs the full width of the content it introduces. Capped at a comfortable
   measure it sat in the left half of the column with the cards beneath it
   spanning the whole width, which read as a mistake rather than as a measure. */
.lede {
  font-size: clamp(1.05rem, 1.7vw, 1.22rem);
  color: var(--muted);
}

/* For the places a narrow measure is still right: a paragraph with nothing
   full-width under it to line up against. */
.lede--measure { max-width: 62ch; }

/* The smaller print that follows a table or a grid and qualifies it. Full
   width for the same reason as .lede: it sits directly under something that
   spans the column, and a short measure makes it look misplaced rather than
   readable. */
.note {
  color: var(--muted);
  font-size: 0.95rem;
  margin-top: 1.4rem;
}

.grid {
  display: grid;
  gap: 1.25rem;
}

.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* ============================================================================
   Site chrome
   ========================================================================= */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(5, 5, 5, 0.82);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--edge-soft);
  transition: border-color 0.3s ease;
}

.nav.is-scrolled { border-bottom-color: var(--edge); }

.nav__inner {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  height: 62px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  color: var(--text);
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.22rem;
  letter-spacing: 0.01em;
  margin-right: auto;
}

.brand img { width: 26px; height: 26px; }

.brand span { color: var(--text); }

.nav__links {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav__links a {
  display: block;
  padding: 0.42rem 0.8rem;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.94rem;
  border: 1px solid transparent;
  transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}

.nav__links a:hover {
  color: var(--text);
  background: var(--hover);
  border-color: var(--edge-soft);
}

.nav__links a[aria-current='page'] {
  color: var(--accent);
  border-color: var(--edge);
}

.nav__toggle {
  display: none;
  background: none;
  border: 1px solid var(--edge);
  color: var(--accent);
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.8rem;
  padding: 0.4rem 0.7rem;
  cursor: pointer;
}

@media (max-width: 820px) {
  .nav__toggle { display: block; }

  .nav__links {
    display: none;
    position: absolute;
    top: 62px;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--panel);
    border-bottom: 1px solid var(--edge);
    padding: 0.5rem;
  }

  .nav__links.is-open { display: flex; }

  .nav__links a { padding: 0.7rem 0.9rem; }
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.78rem 1.5rem;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.98rem;
  text-decoration: none;
  border: 1px solid var(--accent);
  color: var(--accent);
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: color 0.25s ease, box-shadow 0.25s ease;
  clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
}

.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.3, 1);
  z-index: -1;
}

.btn:hover {
  color: #000;
  box-shadow: 0 0 22px rgba(249, 240, 2, 0.28);
}

.btn:hover::before { transform: scaleX(1); }

.btn--primary {
  background: var(--accent);
  color: #000;
}

.btn--primary:hover { color: #000; }

.btn--ghost {
  border-color: var(--edge);
  color: var(--text);
}

.btn--ghost::before { background: var(--lime); }

.btn--ghost:hover { color: #000; box-shadow: 0 0 22px rgba(138, 230, 110, 0.25); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  align-items: center;
}

/* ============================================================================
   Hero
   ========================================================================= */

.hero {
  position: relative;
  padding-block: clamp(3.5rem, 10vw, 7rem) clamp(3rem, 7vw, 5rem);
}

.hero__inner {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

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

.hero__eyebrow {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  border: 1px solid var(--edge);
  padding: 0.3rem 0.8rem;
  margin-bottom: 1.5rem;
}

.hero__eyebrow::before {
  content: '';
  width: 6px;
  height: 6px;
  background: var(--good);
  border-radius: 50%;
  box-shadow: 0 0 8px var(--good);
  animation: breathe 2.4s ease-in-out infinite;
}

.hero h1 {
  margin-bottom: 0.35em;
}

.hero h1 .accent {
  color: var(--accent);
  text-shadow: 0 0 34px rgba(249, 240, 2, 0.35);
}

.hero__sub {
  font-size: clamp(1.05rem, 1.9vw, 1.28rem);
  color: var(--muted);
  max-width: 46ch;
  margin-bottom: 2rem;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin-top: 2.2rem;
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.82rem;
  color: var(--muted);
}

.hero__meta span { display: flex; align-items: center; gap: 0.45rem; }

.hero__meta b {
  color: var(--text);
  font-weight: 600;
}

/* ---- The hero's drop target -------------------------------------------- */

.dropzone {
  font-family: var(--display);
}

.dropzone__icon {
  width: 46px;
  height: 58px;
  margin: 0 auto 1.1rem;
  border: 2px solid var(--muted);
  position: relative;
}

.dropzone__icon::after {
  content: '';
  position: absolute;
  top: -2px;
  right: -2px;
  width: 16px;
  height: 16px;
  background: var(--bg);
  border-left: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
}

.dropzone__title {
  font-size: 1.5rem;
  font-weight: 600;
  margin-bottom: 0.3rem;
}

.dropzone__hint {
  font-family: var(--ui);
  color: var(--muted);
  font-size: 0.95rem;
  margin: 0;
}

/* ============================================================================
   The pipeline animation

   The six stages a scan actually runs, lighting up in order. Started by an
   IntersectionObserver so it plays when it is on screen rather than before.
   ========================================================================= */

.pipeline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.6rem;
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.8rem;
}

.pipeline__stage {
  flex: 1 1 auto;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--edge-soft);
  color: var(--unknown);
  text-align: center;
  white-space: nowrap;
  transition: color 0.4s ease, border-color 0.4s ease, background 0.4s ease;
}

.pipeline__stage.is-live {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-wash);
}

.pipeline__stage.is-done {
  color: var(--lime);
  border-color: var(--edge);
}

/* ---- The score readout -------------------------------------------------- */

.score {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
}

.score__value {
  font-family: var(--display);
  font-size: clamp(3.2rem, 8vw, 5rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--critical);
  text-shadow: 0 0 30px rgba(255, 0, 60, 0.35);
}

.score__band {
  font-family: var(--display);
  font-size: 1.2rem;
  color: var(--critical);
}

/* ---- Severity bars ------------------------------------------------------ */

.ramp {
  display: grid;
  gap: 0.55rem;
  margin-top: 1.5rem;
}

.ramp__row {
  display: grid;
  grid-template-columns: 5.5rem 1fr 2.5rem;
  align-items: center;
  gap: 0.85rem;
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.78rem;
}

.ramp__label { text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600; }

.ramp__track {
  height: 8px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}

.ramp__fill {
  height: 100%;
  width: 0;
  transition: width 1.1s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.ramp__count { text-align: right; color: var(--muted); }

/* ============================================================================
   Content components
   ========================================================================= */

.feature {
  height: 100%;
}

.feature__icon {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  color: var(--accent);
  display: block;
  margin-bottom: 0.85rem;
}

.feature h3 { margin-bottom: 0.5rem; }

.feature p {
  color: var(--muted);
  font-size: 0.97rem;
  margin: 0;
}

/* A claim the tool refuses to make, set apart from the ones it does. */
.guarantee {
  border-left: 2px solid var(--good);
  padding-left: 1.15rem;
  margin-bottom: 1.75rem;
}

.guarantee:last-child { margin-bottom: 0; }

.guarantee h3 {
  font-size: 1.08rem;
  margin-bottom: 0.35rem;
}

.guarantee p {
  color: var(--muted);
  margin: 0;
  font-size: 0.97rem;
}

/* ---- Tables ------------------------------------------------------------- */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--edge-soft);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.94rem;
  min-width: 540px;
}

th, td {
  text-align: left;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--edge-soft);
  vertical-align: top;
}

th {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--panel);
  white-space: nowrap;
}

tbody tr:last-child td { border-bottom: 0; }

tbody tr:hover { background: rgba(249, 240, 2, 0.03); }

/* ---- Screenshots -------------------------------------------------------- */

/* Every screenshot is rendered at 2x, so its natural CSS size is half its
   pixel width. Each one carries that figure as --natural and is never drawn
   larger, because past 1:1 a window capture only gets blurrier. Squeezed into
   a half-width column instead, a capture of a whole application window is
   illegible, which is what these were doing before. */
.shot {
  position: relative;
  border: 1px solid var(--edge);
  background: var(--panel);
  overflow: hidden;
  max-width: var(--natural, 100%);
  margin-inline: auto;
}

.shot img {
  width: 100%;
  height: auto;
}

/* Crops a long capture rather than shrinking it, so what is shown stays
   readable. The fade says the picture continues. */
.shot--crop {
  max-height: var(--crop, 640px);
}

.shot--crop::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 130px;
  background: linear-gradient(transparent, rgba(5, 5, 5, 0.96));
  pointer-events: none;
}

.shot__caption {
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: 0.7rem;
  text-align: center;
}

figure {
  margin: 0 0 2rem;
}

figure .shot__caption { margin-top: 0.8rem; }

/* ---- Callout ------------------------------------------------------------ */

.callout {
  border: 1px solid var(--edge);
  border-left: 3px solid var(--accent);
  background: var(--panel);
  padding: 1.15rem 1.35rem;
  margin: 0 0 1.5rem;
}

.callout p { margin: 0; }

.callout p + p { margin-top: 0.7rem; }

.callout--warn { border-left-color: var(--high); }

/* Barlow rather than --mono, which resolves to whatever monospace the reader's
   system supplies and is the one place on the site not set in its own faces.
   These titles are also sentences rather than labels, up to fifteen words, and
   0.16em of tracking on a sentence in caps takes word shapes apart. Wider size,
   a third of the tracking, and a real semibold face instead of the weight the
   browser synthesises. */
.callout__title {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--accent);
  display: block;
  margin-bottom: 0.5rem;
}

.callout--warn .callout__title { color: var(--high); }

/* ---- Severity chips ----------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.16rem 0.5rem;
  border: 1px solid currentColor;
  white-space: nowrap;
}

.chip--critical { color: var(--critical); }
.chip--high     { color: var(--high); }
.chip--medium   { color: var(--medium); }
.chip--low      { color: var(--low); }
.chip--info     { color: var(--unknown); }
.chip--good     { color: var(--good); }

/* ============================================================================
   Documentation layout
   ========================================================================= */

.page-head {
  padding-block: clamp(2.5rem, 6vw, 4rem) clamp(1.5rem, 3vw, 2.5rem);
  border-bottom: 1px solid var(--edge-soft);
}

.page-head h1 {
  font-size: clamp(2.1rem, 5vw, 3.2rem);
  margin-bottom: 0.35em;
}

.doc {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  padding-block: clamp(2.5rem, 5vw, 4rem);
}

.toc {
  position: sticky;
  top: 5rem;
  max-height: calc(100vh - 7rem);
  overflow-y: auto;
  font-size: 0.9rem;
}

.toc h4 {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.7rem;
}

.toc ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.1rem;
  border-left: 1px solid var(--edge-soft);
}

.toc a {
  display: block;
  padding: 0.32rem 0 0.32rem 0.9rem;
  margin-left: -1px;
  border-left: 2px solid transparent;
  color: var(--muted);
  text-decoration: none;
  line-height: 1.4;
}

.toc a:hover { color: var(--text); }

.toc a.is-current {
  color: var(--accent);
  border-left-color: var(--accent);
}

.doc__body > section {
  padding-block: 0 clamp(2.5rem, 5vw, 3.5rem);
  scroll-margin-top: 5.5rem;
}

.doc__body > section:first-child { padding-top: 0; }

/* After the rules it overrides, not before them. Declared earlier it lost the
   cascade to .toc's own position and the sidebar stayed stuck to the top of a
   phone screen with the article scrolling underneath it. */
@media (max-width: 900px) {
  .doc {
    grid-template-columns: 1fr;
  }

  .toc {
    position: static;
    max-height: none;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid var(--edge-soft);
  }
}

.doc__body h2 {
  font-size: clamp(1.5rem, 3vw, 2rem);
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--edge-soft);
}

.doc__body h3 {
  margin-top: 2rem;
  color: var(--text);
}

.doc__body ul,
.doc__body ol {
  margin: 0 0 1.2rem;
  padding-left: 1.3rem;
  color: var(--muted);
}

.doc__body li { margin-bottom: 0.5rem; }

.doc__body li strong { color: var(--text); font-weight: 600; }

.doc__body p { color: var(--muted); }

.doc__body p strong { color: var(--text); font-weight: 600; }

/* ============================================================================
   Rule reference
   ========================================================================= */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  padding-block: 1.5rem;
  border-bottom: 1px solid var(--edge-soft);
  position: sticky;
  top: 62px;
  background: rgba(5, 5, 5, 0.9);
  backdrop-filter: blur(12px);
  z-index: 20;
}

.filters__search {
  flex: 1 1 220px;
  min-width: 180px;
  background: var(--panel);
  border: 1px solid var(--edge);
  color: var(--text);
  font-family: var(--ui);
  font-size: 0.95rem;
  padding: 0.55rem 0.85rem;
}

.filters__search::placeholder { color: var(--unknown); }

.filters__search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.pill {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--edge-soft);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}

.pill:hover { color: var(--text); border-color: var(--edge); }

.pill[aria-pressed='true'] {
  color: #000;
  background: var(--accent);
  border-color: var(--accent);
}

.filters__count {
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.78rem;
  color: var(--muted);
  margin-left: auto;
}

.rules {
  display: grid;
  gap: 0.9rem;
  padding-block: 1.75rem 3rem;
}

.rule {
  border: 1px solid var(--edge-soft);
  border-left: 3px solid var(--unknown);
  background: var(--panel);
  padding: 1.1rem 1.3rem;
}

.rule[data-severity='Critical'] { border-left-color: var(--critical); }
.rule[data-severity='High']     { border-left-color: var(--high); }
.rule[data-severity='Medium']   { border-left-color: var(--medium); }
.rule[data-severity='Low']      { border-left-color: var(--low); }
.rule[data-severity='Info']     { border-left-color: var(--unknown); }

.rule__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem 0.9rem;
  margin-bottom: 0.6rem;
}

.rule__id {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--accent);
}

.rule__title {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.05rem;
  margin: 0;
  flex: 1 1 auto;
}

.rule__body p {
  margin: 0 0 0.7rem;
  color: var(--muted);
  font-size: 0.95rem;
}

.rule__body p:last-child { margin-bottom: 0; }

.rule__reader {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--edge-soft);
}

.rule__reader h4 {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.rule__flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.8rem;
}

.flag {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.14rem 0.45rem;
  border: 1px dashed var(--edge);
  color: var(--lime);
}

.flag--block { border-style: solid; border-color: var(--critical); color: var(--critical); }
.flag--cap { border-color: var(--low); color: var(--low); }

.empty {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--muted);
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.9rem;
}

/* ============================================================================
   Changelog
   ========================================================================= */

.release {
  position: relative;
  padding-left: 1.75rem;
  padding-bottom: 2.5rem;
  border-left: 1px solid var(--edge-soft);
}

.release:last-child { border-left-color: transparent; padding-bottom: 0; }

.release::before {
  content: '';
  position: absolute;
  left: -5px;
  top: 0.55rem;
  width: 9px;
  height: 9px;
  background: var(--accent);
  box-shadow: 0 0 10px rgba(249, 240, 2, 0.55);
}

.release--old::before { background: var(--unknown); box-shadow: none; }

.release__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.8rem;
  margin-bottom: 0.9rem;
}

.release__version {
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 600;
  margin: 0;
}

.release__date {
  font-family: var(--ui);
  font-weight: 400;
  font-size: 0.8rem;
  color: var(--muted);
}

.release ul {
  padding-left: 1.15rem;
  color: var(--muted);
}

/* ============================================================================
   Footer
   ========================================================================= */

.footer {
  border-top: 1px solid var(--edge-soft);
  padding-block: 3rem 2.5rem;
  margin-top: 2rem;
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  justify-content: space-between;
}

.footer h4 {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.8rem;
}

.footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.footer a {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.93rem;
}

.footer a:hover { color: var(--accent); }

.footer__note {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--edge-soft);
  font-size: 0.86rem;
  color: var(--unknown);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
}

/* ============================================================================
   Reveal on scroll
   ========================================================================= */

/* Scoped to a class the script adds to <html> once it is running. Without
   that class these rules do nothing, so a browser with scripting off, or one
   where site.js fails to load, gets the whole page rather than a blank one. */

.js-reveal .reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}

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

/* ============================================================================
   Motion preferences

   Everything above is decoration. Someone who has asked their system for less
   motion gets the page with none of it, rather than a slower version.
   ========================================================================= */

@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;
  }

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

  .backdrop__sweep { display: none; }
  .backdrop__scan { opacity: 0.2; }
}
