/*
 * The public site.
 *
 * Two rules shape everything here.
 *
 * Colour carries severity and nothing else — the same rule the product runs on.
 * So the drama comes from scale, contrast and motion instead of a brand colour.
 * The only saturated pixels on the page are inside screenshots of the product,
 * where red really does mean a check failed.
 *
 * And every animation is decoration. Content is in the DOM and visible before a
 * line of JavaScript runs; the reveal only adds the transition. A company that
 * sells accessibility cannot ship a landing page that is blank until a script
 * finishes, and `prefers-reduced-motion` is honoured for real, not with a
 * shortened duration.
 */

/* ---------- fonts ---------- */
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:100 700;font-display:swap;src:url(/fonts/plex-sans-var-latin.woff2) format('woff2-variations');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:'IBM Plex Sans';font-style:normal;font-weight:100 700;font-display:swap;src:url(/fonts/plex-sans-var-latin-ext.woff2) format('woff2-variations');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400-latin.woff2) format('woff2');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:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-500-latin.woff2) format('woff2');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}

:root {
  /* White is the ground. Sections alternate onto a tint two steps away from it,
     so a section change reads as a breath rather than a cut. */
  --bg: #FFFFFF; --tint: #F7F8F9; --panel: #FFFFFF; --panel-2: #FAFBFB; --panel-3: #F0F2F3;
  /* ink-3 was 2.82:1 on panel-3, against a 4.5:1 threshold. A site selling
     contrast auditing cannot fail it in its own eyebrows and captions. The
     replacement is what our own suggester returned against the darkest
     section tint, since these sit on all four grounds. */
  --ink: #14161A; --ink-2: #5B6068; --ink-3: #686D75;
  --line: #E6E9EB; --line-2: #CFD4D8;
  --line-soft: #EEF0F1;
  --fail: #A82A20; --fail-bg: #FAEBE9; --fail-line: #EBC4BF;
  --warn: #805408; --warn-bg: #FAF1DF; --warn-line: #E8D4A8;
  --pass: #15633F; --pass-bg: #E7F3EC; --pass-line: #BDDCC9;
  --sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --ease: cubic-bezier(.22, .61, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }
/* The hero glow is deliberately wider than the page; clip rather than scroll.
   `clip` instead of `hidden` so no scroll container is created and
   position: sticky on the masthead keeps working. */
html { scroll-behavior: smooth; overflow-x: clip; }
/* No overflow-x here on purpose. `hidden` on the body makes it a scroll
   container, which silently killed position: sticky on the masthead — the very
   thing the `clip` on html above was chosen to avoid. html clips the wide hero
   glow already; the body does not need to clip it a second time. */
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 17px/1.6 var(--sans); -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration-color: var(--line-2); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; border-radius: 3px; }

.wrap { max-width: 1160px; margin: 0 auto; padding: 0 28px; }
.narrow { max-width: 760px; }

/* ---------- reveal on scroll ----------
   The element is fully visible without JS. `.js` is set by the script, and only
   then does anything start hidden — so a failed script leaves a readable page
   rather than an empty one. */
.js .reveal { opacity: 0; transform: translateY(22px); }
.js .reveal.in { opacity: 1; transform: none; transition: opacity .7s var(--ease), transform .7s var(--ease); }
.js .reveal.d1.in { transition-delay: .08s }
.js .reveal.d2.in { transition-delay: .16s }
.js .reveal.d3.in { transition-delay: .24s }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal, .js .reveal.in { opacity: 1; transform: none; transition: none; }
  * { animation: none !important; }
}

/* ---------- masthead ---------- */
.top {
  position: sticky; top: 0; z-index: 20;
  background: rgba(255, 255, 255, .78); backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid transparent; transition: border-color .3s var(--ease);
}
.top.stuck { border-bottom-color: var(--line); }
.progress { position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; }
.progress span {
  display: block; height: 100%; width: 100%; background: var(--ink);
  transform: scaleX(var(--p, 0)); transform-origin: 0 50%;
}
.top .wrap { display: flex; align-items: center; gap: 20px; height: 60px; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 600; text-decoration: none; letter-spacing: -.01em; }
.glyph {
  width: 22px; height: 22px; border: 1.5px solid currentColor; border-radius: 6px;
  display: grid; place-items: center; flex: none;
}
.glyph::after { content: ''; width: 9px; height: 9px; background: currentColor; border-radius: 2px; }
.top nav { margin-left: auto; display: flex; gap: 22px; font-size: 14.5px; }
/* nowrap, because a masthead link that breaks over two lines doubles the height
   of a 60px bar and the whole thing jumps. If the set no longer fits, the answer
   is fewer links, not smaller ones. */
.top nav a { color: var(--ink-2); text-decoration: none; white-space: nowrap; transition: color .2s; }
.top nav a:hover { color: var(--ink); }
@media (max-width: 900px) { .top nav { display: none; } }

/* ---------- type ---------- */
h1, h2, h3 { font-weight: 600; letter-spacing: -.028em; margin: 0; }
h1 { font-size: clamp(36px, 5.4vw, 66px); line-height: 1.04; }
h2 { font-size: clamp(31px, 4.4vw, 54px); line-height: 1.06; }
h3 { font-size: 18px; line-height: 1.3; letter-spacing: -.015em; }
p { margin: 0 0 16px; }
.lede { font-size: clamp(17px, 1.75vw, 22px); line-height: 1.5; color: var(--ink-2); }
.eyebrow {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-3); margin: 0 0 14px;
}
.muted { color: var(--ink-2); }
.tiny { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }
.mono { font-family: var(--mono); }
section { padding: clamp(68px, 8vw, 124px) 0; }
/* The masthead is sticky, so an anchor jump would otherwise park the heading
   underneath it. Now that sticky actually works, this matters. */
section[id] { scroll-margin-top: 72px; }
/* The only device separating sections: a tint, and a hairline where two tints
   would otherwise meet. No hard cut, no band of another colour. */
.tint { background: var(--tint); }
.tint + .tint { border-top: 1px solid var(--line-soft); }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: 15.5px; font-weight: 500;
  padding: 12px 22px; border-radius: 100px; text-decoration: none;
  border: 1px solid var(--ink); background: var(--ink); color: #fff;
  transition: transform .22s var(--ease), box-shadow .22s var(--ease), background .2s;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 24px -10px rgba(13, 15, 18, .55); }
.btn:active { transform: translateY(0); }
.btn.ghost { background: transparent; color: var(--ink); border-color: var(--line-2); }
.btn.ghost:hover { border-color: var(--ink); box-shadow: none; }

.actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
/* Brand marks sit in their own files so the official asset can be dropped in
   without touching markup. Full colour on purpose: they are somebody else's
   trademark, not our palette. */
.btn .logo { width: 18px; height: 18px; flex: none; }
.btn .logo.tall { width: 14px; }

/* ---------- hero ---------- */
/* The date badge. A pill rather than the plain eyebrow, because it is a fact
   with a deadline in it and it has to read as one. */
.badge {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-2); background: var(--panel); border: 1px solid var(--line-2);
  border-radius: 100px; padding: 8px 16px; margin: 0 auto 20px;
}
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--fail); flex: none; }

/* ---------- the scan box ---------- */
.scanbox {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center;
  max-width: 520px; margin: 0 auto; padding: 7px; background: var(--panel);
  border: 1px solid var(--line-2); border-radius: 100px;
  box-shadow: 0 18px 40px -28px rgba(13, 15, 18, .5);
}
.scanbox input {
  flex: 1 1 220px; min-width: 0; font: inherit; font-size: 15.5px;
  border: 0; background: none; color: var(--ink); padding: 10px 8px 10px 16px;
}
.scanbox input::placeholder { color: var(--ink-3); }
.scanbox input:focus { outline: none; }
/* The ring goes on the whole box: the input has no border of its own, so a ring
   on the field alone reads as a stray line inside a pill. */
.scanbox:focus-within { border-color: var(--ink); box-shadow: 0 0 0 3px rgba(13, 15, 18, .12); }
.scanbox .btn { flex: none; }
.scanbox[aria-busy="true"] .btn { opacity: .6; pointer-events: none; }
/* Narrow: the pill stops being a pill. A button floating in the middle of a
   520px lozenge reads as a mistake, and a full-width target is easier to hit
   on the device where it matters. */
@media (max-width: 560px) {
  .scanbox { border-radius: 14px; padding: 10px; }
  .scanbox input { flex: 1 1 100%; padding: 8px 6px; }
  .scanbox .btn { width: 100%; justify-content: center; }
}
.scanmeta { font-size: 13px; color: var(--ink-3); margin: 12px 0 26px; }

.scanout { max-width: 520px; margin: 18px auto 0; text-align: left; }
.scanstatus { font-size: 14.5px; color: var(--ink-2); margin: 0; }
.scanout.bad .scanstatus { color: var(--fail); }
.scanlist { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 8px; }
.scanlist:empty { display: none; }
.scanlist li {
  display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: baseline;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 11px 14px;
}
.scanlist .sc { font-family: var(--mono); font-size: 12px; color: var(--fail); }
.scanlist .st { font-size: 14px; }
.scanlist .sn { font-family: var(--mono); font-size: 12px; color: var(--ink-3); }
.scanout .tiny:empty { display: none; }

/* ---------- the stakes ---------- */
.stakes { margin: clamp(34px, 4.5vw, 56px) auto 0; max-width: 1000px; }
.stakes .eyebrow { margin-bottom: 18px; }
.stakegrid { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
@media (min-width: 700px) { .stakegrid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .stakegrid { grid-template-columns: repeat(4, 1fr); } }
.stake { background: var(--panel); padding: 22px 20px; text-align: left; }
.stake b { display: block; font-size: clamp(24px, 2.6vw, 31px); letter-spacing: -.02em; line-height: 1.1; margin-bottom: 9px; }
.stake p { font-size: 13.5px; line-height: 1.5; color: var(--ink-2); margin: 0; }
.stake em { font-style: normal; font-weight: 600; color: var(--ink); }
/* The one a reader is most likely to have been told wrong. */
.stake.mark { background: var(--panel-3); }
.stake.mark b { color: var(--ink); }
.sources { max-width: 78ch; margin: 16px auto 0; text-align: left; }

.hero { padding: clamp(44px, 6vw, 78px) 0 0; text-align: center; }
.hero .eyebrow { margin-bottom: 18px; }
.hero h1 { max-width: 19ch; margin: 0 auto 20px; }
.hero .lede { max-width: 54ch; margin: 0 auto 26px; }
.hero .actions { justify-content: center; }
.hero-meta {
  display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: center;
  font-size: 13px; color: var(--ink-3); margin: 22px 0 0;
}
.hero-meta li { list-style: none; }
.hero-stage { margin-top: clamp(26px, 3.4vw, 44px); position: relative; }
/* The popup floats above a soft ground rather than sitting in a box. */
.hero-stage::before {
  content: ''; position: absolute; inset: 10% -10% -18%;
  background: radial-gradient(58% 46% at 50% 55%, rgba(13,15,18,.11), transparent 70%);
  filter: blur(8px); pointer-events: none;
}
.float { position: relative; margin: 0 auto; width: min(360px, 84vw); z-index: 2; }
.float img {
  width: 100%; border-radius: 14px 14px 0 0; border: 1px solid var(--line-2); border-bottom: 0;
  box-shadow: 0 40px 80px -40px rgba(13, 15, 18, .5), 0 4px 12px -6px rgba(13, 15, 18, .18);
  mask-image: linear-gradient(to bottom, #000 82%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent 100%);
}

/* ---------- the pieces around it ----------
   Real surfaces from the product, rebuilt in markup so they stay sharp and can
   drift. They are aria-hidden: every claim they make is written out in the
   sections below, and fragments read aloud out of context help nobody. */
.orbit { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.orbit .piece {
  position: absolute; background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 13px 15px; text-align: left;
  box-shadow: 0 20px 44px -26px rgba(13, 15, 18, .45);
  width: 232px;
}
.js .orbit .piece { opacity: 0; transform: translateY(14px); }
.js .in .orbit .piece { opacity: 1; transform: none; transition: opacity .8s var(--ease), transform .8s var(--ease); }
.js .in .orbit .piece:nth-child(1) { transition-delay: .30s }
.js .in .orbit .piece:nth-child(2) { transition-delay: .42s }
.js .in .orbit .piece:nth-child(3) { transition-delay: .54s }
.js .in .orbit .piece:nth-child(4) { transition-delay: .66s }

.piece .pt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); display: block; margin-bottom: 8px; }
.piece .pb { font-size: 13px; line-height: 1.45; color: var(--ink); margin: 0; }
.piece .pn { font-family: var(--mono); font-size: 12px; color: var(--ink-2); }

/* left, upper — the measurement with its threshold */
.p-meter { left: 0; top: 4%; }
.p-meter .bar { height: 8px; border-radius: 100px; background: var(--panel-3); position: relative; margin: 10px 0 7px; }
.p-meter .bar i { display: block; height: 100%; width: 9%; border-radius: 100px; background: var(--fail); }
.p-meter .bar u { position: absolute; left: 21%; top: -4px; bottom: -4px; width: 2px; background: var(--ink); }
.p-meter b { font-family: var(--mono); font-weight: 500; color: var(--fail); }

/* right, upper — a finding row */
.p-finding { right: 0; top: 12%; }
.p-finding .row { display: flex; align-items: center; gap: 9px; }
.p-finding .code { font-family: var(--mono); font-size: 11.5px; padding: 3px 7px; border-radius: 5px; background: var(--fail-bg); border: 1px solid var(--fail-line); color: var(--fail); }

/* left, lower — the alert that actually gets sent */
.p-alert { left: 3%; top: 46%; }
.p-alert .from { display: flex; align-items: center; gap: 7px; margin-bottom: 7px; }
.p-alert .dot { width: 16px; height: 16px; border-radius: 5px; border: 1.5px solid var(--ink); display: grid; place-items: center; flex: none; }
.p-alert .dot::after { content: ''; width: 6px; height: 6px; background: var(--ink); border-radius: 1.5px; }

/* right, lower — what monitoring watches */
.p-watch { right: 3%; top: 54%; }
.p-watch .line { display: flex; align-items: center; gap: 9px; padding: 4px 0; }
.p-watch .n { font-family: var(--mono); font-size: 11px; min-width: 22px; text-align: center; padding: 2px 0; border-radius: 5px; }
.p-watch .n.ok { background: var(--pass-bg); border: 1px solid var(--pass-line); color: var(--pass); }
.p-watch .n.bad { background: var(--fail-bg); border: 1px solid var(--fail-line); color: var(--fail); }
.p-watch .u { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); }

/* Below 1080px there is no room to orbit anything without collision. */
@media (max-width: 1080px) { .orbit { display: none; } }

/* A drift of a few pixels, so the scene is not a still photograph. */
@keyframes drift { 0%, 100% { translate: 0 0 } 50% { translate: 0 -7px } }
.js .in .p-meter { animation: drift 7s var(--ease) infinite .8s }
.js .in .p-finding { animation: drift 8s var(--ease) infinite 1.4s }
.js .in .p-alert { animation: drift 7.5s var(--ease) infinite 1.1s }
.js .in .p-watch { animation: drift 8.5s var(--ease) infinite 1.7s }

/* ---------- the meter: the whole argument, at full size ----------
   This is the product's one idea, so it gets the section rather than a card in
   the corner of it. The number is set large enough to be the thing you read
   from across a room; everything else on the screen is its caption. */
.showpiece { text-align: center; margin-top: clamp(40px, 4.5vw, 64px); }
.showpiece .tag {
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); display: block; margin-bottom: 22px;
}
.bignum {
  font-family: var(--mono); font-weight: 500; letter-spacing: -.045em;
  font-size: clamp(64px, 12vw, 148px); line-height: .92; color: var(--fail);
  display: block;
}
.against { font-size: clamp(16px, 1.6vw, 20px); color: var(--ink-2); margin: 18px 0 0; }
.against b { color: var(--ink); font-weight: 600; }

.gauge { max-width: 780px; margin: clamp(32px, 4vw, 48px) auto 0; }
.gauge-track { position: relative; height: 14px; border-radius: 100px; background: var(--panel-3); }
.gauge-fill {
  height: 100%; border-radius: 100px; background: var(--fail); width: 3%;
  transition: width .5s var(--ease), background-color .25s linear;
}
/* The fill grows on entry; once the visitor drives it, --fill is what moves. */
.in .gauge-fill { width: var(--fill, 4.5%); }
.gauge-th { position: absolute; top: -10px; bottom: -10px; left: var(--th, 17.5%); width: 2px; background: var(--ink); transition: left .5s var(--ease); }
.gauge-th::after {
  content: attr(data-l); position: absolute; left: 50%; top: -30px; transform: translateX(-50%);
  font-family: var(--mono); font-size: 12px; white-space: nowrap;
}
.gauge-scale { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 12px; color: var(--ink-3); margin-top: 16px; }
.showpiece .note { max-width: 56ch; margin: clamp(32px, 4vw, 48px) auto 0; }

/* ---------- the meter, driven by the visitor ----------
   This section claims a measurement is worth nothing without the threshold
   beside it. Letting someone move the measurement and watch the threshold hold
   still argues that better than a paragraph does. The controls are hidden until
   `ix` is set, so a page without JavaScript shows the static example instead of
   dead inputs — and unlike `js`, `ix` is set even under reduced motion, because
   turning motion down is not a request to be given less to read.

   Colour still carries severity and nothing else: the number and the fill turn
   green only when the pair actually clears its threshold. */
.sr {
  position: absolute; width: 1px; height: 1px; margin: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.showpiece.pass .bignum { color: var(--pass); }
.showpiece.pass .gauge-fill { background: var(--pass); }

.verdict {
  display: inline-block; margin-left: 10px; vertical-align: 2px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .08em;
  text-transform: uppercase; padding: 5px 11px; border-radius: 100px;
  background: var(--fail-bg); border: 1px solid var(--fail-line); color: var(--fail);
}
.showpiece.pass .verdict { background: var(--pass-bg); border-color: var(--pass-line); color: var(--pass); }

.tryit { display: none; }
.ix .tryit {
  display: block; text-align: left; max-width: 760px;
  margin: clamp(34px, 4vw, 48px) auto 0; padding: clamp(20px, 2.4vw, 28px);
  background: var(--panel); border: 1px solid var(--line); border-radius: 18px;
}
.tryit-head h3 { font-size: 17px; margin: 0 0 6px; }
.tryit-head p { margin: 0; max-width: 56ch; }

.tryit-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; margin-top: 22px; }
@media (max-width: 720px) { .tryit-grid { grid-template-columns: 1fr; } }
.pick { display: grid; gap: 8px; align-content: start; }
.pick label, .pick .lab { font-size: 13px; color: var(--ink-2); }
.pickrow { display: flex; gap: 8px; }
.pickrow input[type="color"] {
  inline-size: 46px; block-size: 42px; flex: none; padding: 3px; cursor: pointer;
  border: 1px solid var(--line-2); border-radius: 10px; background: var(--panel);
}
.pickrow input[type="text"] {
  flex: 1; min-width: 0; font-family: var(--mono); font-size: 14px; color: var(--ink);
  padding: 10px 12px; background: var(--panel); border: 1px solid var(--line-2); border-radius: 10px;
}
.pickrow input[type="text"][aria-invalid="true"] { border-color: var(--fail-line); background: var(--fail-bg); }
.chip {
  block-size: 42px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 10px; font-size: 15px; font-weight: 600;
}

.sizes { border: 0; margin: 22px 0 0; padding: 20px 0 0; border-top: 1px solid var(--line-soft); }
.sizes legend { padding: 0; font-size: 13px; color: var(--ink-2); }
.sizes .opts { display: grid; gap: 10px; margin-top: 2px; }
.sizes label { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--ink-2); cursor: pointer; }
.sizes input { accent-color: var(--ink); margin: 0; }

.presets { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--line-soft); }
/* The label takes its own row so the three chips line up as one set
   instead of leaving the last one stranded under the sentence. */
.presets .lab { flex-basis: 100%; font-size: 13px; color: var(--ink-2); margin-bottom: 2px; }
.presets button {
  font: inherit; font-size: 13px; padding: 7px 13px; border-radius: 100px; cursor: pointer;
  color: var(--ink); background: var(--panel-2); border: 1px solid var(--line-2);
  transition: background-color .2s, border-color .2s;
}
.presets button:hover { background: var(--panel-3); border-color: var(--ink-3); }

@media (prefers-reduced-motion: reduce) {
  .gauge-fill, .gauge-th { transition: none; }
}

/* ---------- bento ---------- */
.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.tile {
  grid-column: span 2; background: var(--panel); border: 1px solid var(--line);
  border-radius: 18px; padding: 26px; overflow: hidden; position: relative;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s;
}
.tile:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -26px rgba(13, 15, 18, .5); border-color: var(--line-2); }
.tile h3 { margin-bottom: 8px; }
.tile p { font-size: 14.5px; color: var(--ink-2); margin: 0; }
.tile.w3 { grid-column: span 3; }
.tile.w4 { grid-column: span 4; }
.tile.w6 { grid-column: span 6; }
.tile { display: flex; flex-direction: column; }
.tile.tall { min-height: 340px; }
.tile-shot.s { max-height: 200px; }
.tile-shot.m { max-height: 240px; }
.tile .grow { flex: 1; }
/* A popup is 360px wide and looks like one at 360px. Scaling it to fill a
   700px tile turns a screenshot into a blurry zoom, so it is shown at its own
   size, centred, with the tile deciding how much of it to reveal. */
.tile-shot {
  margin: 24px -26px -26px; border-top: 1px solid var(--line);
  background: var(--panel-2); padding: 20px 20px 0; overflow: hidden;
  display: flex; justify-content: center;
}
.tile-shot img {
  width: 360px; max-width: 100%; border-radius: 10px 10px 0 0;
  border: 1px solid var(--line); border-bottom: 0;
  box-shadow: 0 -1px 0 #fff inset, 0 12px 28px -18px rgba(13,15,18,.4);
  mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
}

@media (max-width: 900px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
  .tile, .tile.w3, .tile.w4, .tile.w6 { grid-column: span 2; }
  .tile.tall { min-height: 0; }
}

/* The permission list, shown as the short list it is. */
.perms { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 18px; }
.perms span {
  font-family: var(--mono); font-size: 12px; padding: 5px 10px; border-radius: 100px;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--ink-2);
}
.perms .none { border-style: dashed; color: var(--ink-3); }

/* ---------- what each tile shows ----------
   Four tiles used to carry the same screenshot component and four carried
   nothing, which made a nine-tile grid read as two ideas. Each one now shows
   the thing it is actually talking about, built in CSS rather than captured, so
   the artwork stays legible at any width and cannot drift out of date with a
   screen it is no longer a picture of. Two real screenshots remain, at opposite
   ends of the grid, because a product page that never shows the product is
   asking for a lot of faith.

   All of it is aria-hidden: each one restates the paragraph above it, and a
   screen reader should not hear the same sentence twice. */

/* The overlay notice, in the product's own warning colours. */
.widgetnote {
  margin-top: 20px; padding: 14px 16px; border-radius: 12px;
  background: var(--warn-bg); border: 1px solid var(--warn-line);
  border-left: 3px solid var(--warn); display: grid; gap: 7px;
}
.widgetnote b { font-size: 13.5px; color: var(--warn); }
.widgetnote code { font-family: var(--mono); font-size: 12px; color: var(--ink-2); word-break: break-all; }
.wn-done { font-size: 12.5px; color: var(--ink-2); }
.wn-done::before { content: '✓ '; color: var(--pass); }

/* The crawler's side of the conversation with robots.txt. */
.robots {
  margin-top: 20px; padding: 16px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--line);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.9; color: var(--ink);
}
.r-line.dim { color: var(--ink-3); }
.r-verb { color: var(--ink-2); }
.r-ok { color: var(--pass); }
.r-bar { height: 6px; border-radius: 100px; background: var(--panel-3); margin: 12px 0 10px; overflow: hidden; }
.r-bar span { display: block; height: 100%; width: 47%; border-radius: 100px; background: var(--ink); }

/* The alert, as it lands in an inbox. */
.mailcard {
  margin-top: 20px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--panel-2); overflow: hidden;
}
.m-head {
  display: flex; justify-content: space-between; gap: 12px; padding: 11px 14px;
  border-bottom: 1px solid var(--line); font-family: var(--mono); font-size: 12px; color: var(--ink-2);
}
.m-when { color: var(--ink-3); }
.m-sub { padding: 13px 14px 0; font-size: 14px; font-weight: 600; }
.m-list { list-style: none; margin: 0; padding: 10px 14px 14px; display: grid; gap: 8px; font-size: 13px; color: var(--ink-2); }
.m-list li { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.m-code {
  font-family: var(--mono); font-size: 11.5px; padding: 3px 7px; border-radius: 6px;
  background: var(--fail-bg); border: 1px solid var(--fail-line); color: var(--fail);
}
.m-at { font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); margin-left: auto; }

/* Two counts, side by side, because the point is that they stay apart. */
.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 20px; }
.tier { padding: 14px; border-radius: 12px; background: var(--panel-2); border: 1px solid var(--line); }
.tier b { display: block; font-family: var(--mono); font-size: 30px; letter-spacing: -.03em; line-height: 1; }
.tier span { display: block; font-size: 12.5px; margin-top: 8px; }
.tier em { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-3); margin-top: 3px; }

/* Numbered flags dropped on a page, which is what the overlay does. */
.flags {
  position: relative; margin-top: 20px; padding: 16px; min-height: 130px;
  border-radius: 12px; background: var(--panel-2); border: 1px solid var(--line);
  display: grid; align-content: start; gap: 10px;
}
.flags .sk { display: block; height: 8px; border-radius: 100px; background: var(--panel-3); }
.flags .w80 { width: 80%; } .flags .w70 { width: 70%; } .flags .w55 { width: 55%; }
.flags .box { height: 34px; border-radius: 8px; width: 100%; }
.flags .fl {
  position: absolute; left: var(--x); top: var(--y); transform: translate(-50%, -50%);
  width: 22px; height: 22px; border-radius: 100px; background: var(--ink); color: #fff;
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 3px var(--panel-2);
}

/* What an account buys, which is very little. */
.needs { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 20px; }
.needs > div { padding: 14px; border-radius: 12px; background: var(--panel-2); border: 1px solid var(--line); }
.needs .lab {
  display: block; font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 9px;
}
.needs ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; font-size: 12.5px; color: var(--ink-2); }
@media (max-width: 420px) { .tiers, .needs { grid-template-columns: 1fr; } }

/* ---------- the chain from the law to the criteria ----------
   This was a small tile in the grid, which was the wrong size for it: which
   version of WCAG a client is measured on is the thing that decides whether a
   report is worth anything, and buyers routinely get it wrong in the expensive
   direction. It gets a band of its own.

   An ordered list because the order is the argument — each link is only
   meaningful as the thing the one before it points at. */
.chain {
  list-style: none; margin: clamp(40px, 5vw, 64px) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px;
}
.chain li {
  position: relative; padding: 24px; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--line);
}
.chain li.c-end { border-color: var(--ink); border-width: 1.5px; }
/* The arrow sits in the gutter, drawn rather than typed, so it turns to point
   downwards when the cards stack without swapping any glyphs. */
.chain li + li::before {
  content: ''; position: absolute; left: -14px; top: 50%; width: 14px; height: 1px;
  background: var(--line-2);
}
.chain li + li::after {
  content: ''; position: absolute; left: -6px; top: 50%; width: 6px; height: 6px;
  border-top: 1px solid var(--line-2); border-right: 1px solid var(--line-2);
  transform: translateY(-50%) rotate(45deg);
}
.c-n {
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em; color: var(--ink-3);
  display: block; margin-bottom: 14px;
}
.chain b { display: block; font-size: 18px; letter-spacing: -.015em; }
.c-sub { display: block; font-size: 13px; color: var(--ink-3); margin-top: 4px; }
.chain p { margin: 14px 0 0; font-size: 14px; color: var(--ink-2); }

@media (max-width: 860px) {
  .chain { grid-template-columns: 1fr; gap: 26px; }
  .chain li + li::before { left: 50%; top: -20px; width: 1px; height: 12px; }
  .chain li + li::after { left: 50%; top: -10px; transform: translateX(-50%) rotate(135deg); }
}

/* The same two-count component as in the grid, at the size this section needs. */
.tiers.big { max-width: 780px; margin: clamp(28px, 3vw, 40px) auto 0; gap: 14px; }
.tiers.big .tier { padding: 22px 24px; }
.tiers.big .tier b { font-size: clamp(38px, 5vw, 56px); }
.tiers.big .tier span { font-size: 14px; margin-top: 12px; }
.tiers.big .tier em { font-size: 12.5px; margin-top: 6px; }

/* ---------- dark bands ---------- */

.figures { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
@media (max-width: 860px) { .figures { grid-template-columns: repeat(2, 1fr); } }
.figure { border-top: 1px solid var(--line-2); padding-top: 20px; }
.figure b { display: block; font-family: var(--mono); font-size: clamp(34px, 4.2vw, 54px); font-weight: 400; letter-spacing: -.035em; }
.figure span { font-size: 13.5px; color: var(--ink-2); display: block; margin-top: 8px; line-height: 1.45; }

/* ---------- refusals ---------- */
/* Two columns once there is room: six single-file rows leave half the band
   empty and make the section read as an afterthought. */
.refuse { display: grid; gap: 0 48px; margin-top: 28px; }
@media (min-width: 900px) { .refuse { grid-template-columns: 1fr 1fr; } .refuse li:nth-child(2) { border-top: 1px solid var(--line-2); } }
.refuse li { list-style: none; border-top: 1px solid var(--line-2); padding: 26px 0; display: grid; grid-template-columns: 40px 1fr; gap: 4px 18px; align-items: start; }
.refuse li:last-child { border-bottom: 1px solid var(--line-2); }
.refuse .no { font-family: var(--mono); font-size: 13px; color: var(--ink-3); padding-top: 5px; }
.refuse h3 { font-size: 19px; margin-bottom: 6px; }
.refuse p { margin: 0; font-size: 14.5px; max-width: 62ch; }
@media (max-width: 620px) { .refuse li { grid-template-columns: 1fr; } .refuse .no { display: none; } }

/* ---------- pricing ---------- */
/* Free against paid, before the tiers.
   Two columns rather than a table: a table invites a feature-by-feature
   comparison, and the point here is not that one side has more rows — it is
   that the two sides are different kinds of thing. One is a tool you run, the
   other is a job that runs without you. */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 820px) { .split { grid-template-columns: 1fr; } }
.side {
  padding: clamp(20px, 2.4vw, 28px); border-radius: 16px;
  background: var(--panel); border: 1px solid var(--line);
}
.side.paid { border-color: var(--ink); border-width: 1.5px; }
.side .lab {
  display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 16px;
}
.side.paid .lab { color: var(--ink); }
.side ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
/* The text is wrapped in a span rather than left loose in the li: a bold lead-in
   followed by a bare text node makes two anonymous grid items, and the sentence
   ends up in the dash column, one word per line. */
.side li { display: grid; grid-template-columns: 14px 1fr; gap: 10px; align-items: start; font-size: 14.5px; color: var(--ink-2); line-height: 1.5; }
.side li::before { content: '—'; font-family: var(--mono); font-size: 11px; color: var(--ink-3); padding-top: 5px; }
.side li b { color: var(--ink); font-weight: 600; }
.plans { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 900px) { .plans { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .plans { grid-template-columns: 1fr; } }
.plan {
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 24px;
  display: flex; flex-direction: column; gap: 10px;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.plan:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -26px rgba(13, 15, 18, .45); }
.plan.live { border-color: var(--ink); border-width: 1.5px; }
.plan .tag { font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.plan .name { font-weight: 600; font-size: 17px; letter-spacing: -.015em; }
.plan .price { font-family: var(--mono); font-size: 24px; letter-spacing: -.03em; }
.plan .price small { font-size: 12.5px; color: var(--ink-3); font-family: var(--sans); }
.plan ul { list-style: none; margin: 6px 0 0; padding: 0; font-size: 14px; color: var(--ink-2); display: grid; gap: 7px; }
.plan li { display: flex; gap: 9px; }
.plan li::before { content: '—'; color: var(--ink-3); font-family: var(--mono); font-size: 11px; padding-top: 4px; }

/* ---------- two-up ---------- */
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .two { grid-template-columns: 1fr; } }
.card { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 24px; }
.card ul { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 9px; font-size: 14.5px; color: var(--ink-2); }
.card li { display: flex; gap: 10px; }
.card li::before { content: attr(data-m); font-family: var(--mono); font-size: 12px; color: var(--ink-3); padding-top: 3px; }

/* ---------- get it: one card per surface ----------
   Two products, two states. The state is part of the card rather than a
   footnote under it — a call to action that hides what it actually does when
   you press it is the same lie as a badge nobody earned. */
.getit { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: clamp(40px, 5vw, 64px); }
@media (max-width: 760px) { .getit { grid-template-columns: 1fr; } }
.surface {
  background: var(--panel); border: 1px solid var(--line); border-radius: 18px;
  padding: 30px; display: flex; flex-direction: column; gap: 14px; text-decoration: none;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s;
}
.surface:hover { transform: translateY(-4px); box-shadow: 0 22px 48px -28px rgba(13,15,18,.45); border-color: var(--line-2); }
.surface .mark {
  width: 44px; height: 44px; border-radius: 12px; border: 1px solid var(--line-2);
  display: grid; place-items: center; background: var(--panel-2); flex: none;
}
.surface .mark svg { width: 22px; height: 22px; }
.surface h3 { font-size: 21px; }
.surface p { font-size: 14.5px; color: var(--ink-2); margin: 0; }
.surface .state {
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-3); border: 1px dashed var(--line-2); border-radius: 100px;
  padding: 5px 11px; align-self: flex-start;
}
.surface .go { margin-top: auto; padding-top: 8px; font-weight: 500; font-size: 15px; display: flex; align-items: center; gap: 8px; }
.surface .go::after { content: '→'; transition: transform .25s var(--ease); }
.surface:hover .go::after { transform: translateX(4px); }

/* ---------- closing ---------- */
.cta { text-align: center; }
.cta h2 { max-width: 18ch; margin: 0 auto 16px; }
.cta .lede { max-width: 54ch; margin: 0 auto 26px; }
.cta .actions { justify-content: center; }

/* ---------- document pages ---------- */
.doc h1 { font-size: clamp(32px, 5vw, 46px); margin-bottom: 16px; }
.doc h2 { font-size: 22px; margin: 0 0 12px; }
.doc section { padding: 34px 0; border-top: 1px solid var(--line); }
.doc section:first-of-type { border-top: 0; padding-top: 44px; }
.doc ul.plain { list-style: none; padding: 0; margin: 0; display: grid; gap: 11px; }
.doc ul.plain li { display: flex; gap: 11px; align-items: baseline; }
.doc .tick { flex: none; font-family: var(--mono); font-size: 12px; color: var(--ink-3); }
table { border-collapse: collapse; width: 100%; font-size: 14.5px; }
th, td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-weight: 600; }
thead th { color: var(--ink-2); font-weight: 500; font-size: 12.5px; }
.scroll { overflow-x: auto; }
code { font-family: var(--mono); font-size: .88em; background: var(--panel-3); padding: 2px 6px; border-radius: 4px; }
pre { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 16px 18px; overflow-x: auto; margin: 0 0 16px; font-size: 13.5px; line-height: 1.55; }
pre code { background: none; padding: 0; }
.notice { border: 1px solid var(--line); border-left: 3px solid var(--ink-3); border-radius: 0 10px 10px 0; background: var(--panel-2); padding: 14px 16px; }

/* ---------- footer ---------- */
footer { border-top: 1px solid var(--line); background: var(--panel); padding: 44px 0 56px; }
footer nav { display: flex; flex-wrap: wrap; gap: 20px; font-size: 14.5px; margin-bottom: 20px; }
footer .limits { font-size: 13.5px; color: var(--ink-2); max-width: 62ch; margin: 0 0 8px; }
footer .motion { font-size: 12.5px; color: var(--ink-3); margin: 0; }

/* ---------- scroll-linked drift ----------
   A scroll timeline rather than a scroll listener: the browser drives it off
   the compositor, so it costs nothing and cannot jank the page. Only the two
   screenshots move, and only by a few pixels — enough for the tile to feel like
   a window onto something rather than a picture pasted into it.

   Wrapped in @supports and in the reduced-motion guard, so browsers without
   view timelines and people who asked for stillness simply get the static
   layout that was there all along. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .tile-shot img {
      animation: shot-drift linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 80%;
    }
    @keyframes shot-drift {
      from { transform: translateY(26px); }
      to   { transform: translateY(-14px); }
    }
    /* The bento tiles arrive with a touch of scale as they enter, on top of the
       reveal the observer already does. */
    .figure b { animation: fig-rise linear both; animation-timeline: view(); animation-range: entry 10% entry 90%; }
    @keyframes fig-rise {
      from { opacity: .25; transform: translateY(14px); }
      to   { opacity: 1; transform: none; }
    }
  }
}
