/* Ashbeck — shared design system for the feature and engagement pages.
 *
 * index.html, about.html and writing.html carry their own inline copy of this system
 * and are deliberately left alone: index.html is 82 KB of working, deployed page and
 * rewriting it to save a request is a risk with no reader-facing return. Everything
 * added from here links this file instead, so the pages that will keep growing have
 * one source of truth. If a token changes, change it here and in index.html's <style>
 * — that duplication is real and is the price of not touching a page that works.
 */

*,*::before,*::after{box-sizing:border-box}
:root{
  --bg:#050609; --ink:#f4f6fa; --dim:#9aa4b8; --dim2:#7e8a9e;
  --edge:rgba(255,255,255,.09); --edge2:rgba(255,255,255,.055);
  --pane:rgba(255,255,255,.032); --pane2:rgba(255,255,255,.018);
  --o:#ff7a45; --p:#8b7bff; --c:#4fd8e8; --g:#3ddc8a; --r:#ff5c8a; --R:24px;
}
/* overflow-x on the ROOT, not just on body. `body{overflow-x:hidden}` makes the body a
 * scroll container while <html> keeps expanding, so a fixed aurora blob or an off-screen
 * honeypot field still widened the document — measured at 12px on a 1024 viewport, which
 * is a page that scrolls sideways for no visible reason. `clip` rather than `hidden`
 * because hidden on the root makes it a scroll container and breaks the sticky nav. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip}
/* NO overflow-x here. `html{overflow-x:clip}` already clips the axis, and setting it on
 * body as well BREAKS PAGE SCROLLING: body's overflow only propagates to the viewport
 * while html is `visible` on both axes, so with clip on html the body becomes its own
 * scroll container, html sizes to the full content height, and window.scrollBy does
 * nothing. Measured: scrollHeight == clientHeight == 5403 and document.scrollingElement
 * was no longer <html>. One axis, one place. */
body{margin:0;background:var(--bg);color:var(--ink);position:relative;
  font:400 17px/1.72 "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  -webkit-font-smoothing:antialiased;letter-spacing:-.011em}
h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:-.04em;line-height:1.08}
a{color:inherit;text-decoration:none}

/* ── the details that decide whether a site feels finished ──────────────────
   None of these are visible in a screenshot. All of them are visible in use. */

/* Keyboard focus. The default ring is a browser-blue rectangle that ignores the corner
   radius; on a dark page it looks like a fault. :focus-visible only, so a mouse click
   never draws it — the ring is for people navigating by keyboard, and showing it to
   everyone is what makes designers delete it entirely. */
:focus-visible{outline:2px solid #ffb894;outline-offset:3px;border-radius:4px}
.btn:focus-visible,a.card:focus-visible{outline-offset:4px;border-radius:12px}

/* Selected text. The browser default is a blue that belongs to no palette here. */
::selection{background:rgba(255,122,69,.28);color:#fff}

/* Scrollbars, where the browser lets us. A bright system scrollbar down the edge of a
   near-black page is the single loudest unstyled element on the site. */
*{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.16) transparent}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:99px;
  border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.26);background-clip:content-box}

/* Anchored headings must clear the sticky nav, or following an in-page link lands with
   the target hidden behind it. 66px of nav plus air. */
:target{scroll-margin-top:92px}
section[id]{scroll-margin-top:24px}

/* Long technical strings — quantity names, file names, codes — must not force a
   horizontal scroll on a phone. */
.body p,.body li,.find-b,.qa p,.step p{overflow-wrap:break-word}
.prism{background:linear-gradient(96deg,#ffc79a,#ff7a45 26%,#ff5c8a 52%,#8b7bff 78%,#4fd8e8);
  -webkit-background-clip:text;background-clip:text;color:transparent}

.aurora{position:fixed;inset:-30vh -25vw;z-index:0;pointer-events:none;filter:blur(100px);opacity:.30}
.blob{position:absolute;border-radius:50%;mix-blend-mode:screen}
.ab1{width:58vw;height:58vw;left:-6vw;top:-12vh;
  background:radial-gradient(circle,rgba(255,122,69,.5),transparent 62%);animation:d1 24s ease-in-out infinite alternate}
.ab2{width:50vw;height:50vw;right:-8vw;top:10vh;
  background:radial-gradient(circle,rgba(139,123,255,.45),transparent 64%);animation:d2 30s ease-in-out infinite alternate}
@keyframes d1{to{transform:translate3d(7vw,6vh,0) scale(1.14)}}
@keyframes d2{to{transform:translate3d(-6vw,9vh,0) scale(1.1)}}
.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.15;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='.5'/></svg>")}
.page{position:relative;z-index:2}
.wrap{max-width:1080px;margin:0 auto;padding:0 26px}
.narrow{max-width:760px;margin:0 auto}

nav{position:sticky;top:0;z-index:40;background:rgba(5,6,9,.6);border-bottom:1px solid var(--edge2);
  backdrop-filter:blur(22px) saturate(170%);-webkit-backdrop-filter:blur(22px) saturate(170%)}
nav .wrap{display:flex;align-items:center;justify-content:space-between;height:66px}
/* min-height, because this is the home link and on a phone it was a 31px target — under
 * every tap-size guideline, on the one control a lost reader reaches for. The nav is 66px
 * tall, so the padding costs nothing visually. */
.mark{display:flex;align-items:center;gap:10px;font-size:18px;font-weight:650;
  letter-spacing:-.035em;min-height:44px}
.orb{width:24px;height:24px;border-radius:8px;
  background:conic-gradient(from 210deg,#ff7a45,#ff5c8a,#8b7bff,#4fd8e8,#ff7a45);
  box-shadow:0 0 20px -3px rgba(255,122,69,.8)}
.nl{display:flex;gap:26px;font-size:14.5px;color:var(--dim)}
.nl a:hover{color:var(--ink)}
.nl a.on{color:var(--ink)}
@media(max-width:940px){.nl{display:none}}

/* Mobile sheet. Matched to index.html's, at this file's 940px breakpoint rather than
 * index's 900px — the link row here is one item longer, so it runs out of room sooner. */
.burger{display:none;width:40px;height:40px;border-radius:11px;cursor:pointer;
  background:var(--pane2);border:1px solid var(--edge);align-items:center;
  justify-content:center;padding:0}
.burger span{display:block;width:17px;height:1.5px;background:var(--ink);
  border-radius:2px;position:relative;transition:.25s}
.burger span::before,.burger span::after{content:"";position:absolute;left:0;
  width:17px;height:1.5px;background:var(--ink);border-radius:2px;transition:.25s}
.burger span::before{top:-5.5px} .burger span::after{top:5.5px}
.burger.on span{background:transparent}
.burger.on span::before{top:0;transform:rotate(45deg)}
.burger.on span::after{top:0;transform:rotate(-45deg)}
.sheet{display:none}
@media(max-width:940px){
  .burger{display:flex}
  nav .btn.b1.bs{display:none}
  .sheet{display:block;position:fixed;inset:66px 0 auto 0;z-index:39;
    padding:20px 26px 28px;background:rgba(5,6,9,.97);
    backdrop-filter:blur(22px);-webkit-backdrop-filter:blur(22px);
    border-bottom:1px solid var(--edge);transform:translateY(-12px);opacity:0;
    pointer-events:none;transition:.28s cubic-bezier(.2,.7,.3,1)}
  .sheet.open{transform:none;opacity:1;pointer-events:auto}
  .sheet a{display:block;padding:15px 0;font-size:18px;color:var(--ink);
    border-bottom:1px solid var(--edge2);letter-spacing:-.025em}
  .sheet a.btn{border-bottom:0;width:100%;justify-content:center;margin-top:16px;
    font-size:16px}
}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border-radius:12px;
  font-size:15px;font-weight:550;transition:.22s cubic-bezier(.2,.7,.3,1);border:1px solid transparent}
.b1{color:#1c0a03;background:linear-gradient(100deg,#ffcda5,#ff7a45 55%,#ff5c8a);
  box-shadow:0 14px 40px -14px rgba(255,122,69,.9)}
.b1:hover{transform:translateY(-2px)}
.b2{border-color:var(--edge);color:var(--ink);background:var(--pane2)}
.b2:hover{border-color:rgba(255,255,255,.28)}
.bs{padding:9px 17px;font-size:14px}

.hero{padding:120px 0 78px}
.eyebrow{display:inline-flex;align-items:center;gap:11px;font-size:11.5px;letter-spacing:.17em;
  text-transform:uppercase;color:#ffb894;margin-bottom:26px;font-weight:650}
.eyebrow::before{content:"";width:22px;height:1px;background:currentColor;opacity:.55}
h1{font-size:clamp(40px,6.4vw,70px);line-height:1.02;letter-spacing:-.05em;max-width:18ch}
.lede{font-size:clamp(19px,2.1vw,22.5px);line-height:1.58;color:var(--dim);
  max-width:66ch;margin:34px 0 0;letter-spacing:-.016em}

section{padding:88px 0;border-top:1px solid var(--edge2)}
h2{font-size:clamp(28px,3.8vw,42px);max-width:20ch;letter-spacing:-.044em}
h3{font-size:21px;letter-spacing:-.032em}

/* THE MEASURE IS THE COLUMN. `.narrow` is 760px and the prose used to cap at 64ch
 * (~538px) inside it, so every paragraph stopped 220px short of the tables, finding
 * cards and pull quotes sitting in the same container — nothing shared a right edge and
 * the page read as though the text had been poured in by accident. The cap is gone and
 * the type is up to 18.5px, which puts the line at roughly 78 characters: wider than
 * before, still inside the range prose is comfortable at, and aligned to everything
 * around it. Alignment is most of what "designed" means. */
.body p{font-size:18.5px;line-height:1.76;color:var(--dim)}
.body p b{color:var(--ink);font-weight:550}
.body p+p{margin-top:24px}
.body ul{color:var(--dim);font-size:18px;line-height:1.74;padding-left:22px}
.body li+li{margin-top:12px}
.body li b{color:var(--ink);font-weight:550}
.body h2+p,.body h2+ul{margin-top:26px}

/* Numbered section labels. The rule running off to the right is what stops a small grey
 * caption looking like an afterthought — it turns the label into structure. */
.page{counter-reset:sec}
.kick{display:flex;align-items:center;gap:15px;font-size:12px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim2);font-weight:650;margin-bottom:20px}
.kick::before{counter-increment:sec;content:counter(sec,decimal-leading-zero);
  font-size:12px;letter-spacing:.04em;font-weight:700;
  background:linear-gradient(96deg,#ff7a45,#ff5c8a 60%,#8b7bff);
  -webkit-background-clip:text;background-clip:text;color:transparent}
/* Fades across roughly half its run rather than immediately. At the 9% edge token the
 * gradient was invisible within about thirty pixels, which read as a rendering fault
 * rather than a rule. */
.kick::after{content:"";flex:1;height:1px;
  background:linear-gradient(90deg,rgba(255,255,255,.15),rgba(255,255,255,.04) 45%,transparent)}
.sub{font-size:17px;line-height:1.7;color:var(--dim);margin:20px 0 0}

/* The pull quote carries the one sentence a reader should leave with, so it is set at
 * display weight rather than as a slightly bigger paragraph. No max-width: it fills the
 * column like everything else, which is the whole point of the alignment work above. */
.pull{margin:48px 0;padding:34px 38px;border-radius:var(--R);background:var(--pane);
  border:1px solid var(--edge);border-left:2px solid var(--o);position:relative;
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}
.pull p{margin:0;font-size:22px;line-height:1.52;color:var(--ink);letter-spacing:-.03em;
  font-weight:500}

/* a coded finding, shown the way the register shows it */
.find{margin:26px 0;border-radius:18px;overflow:hidden;background:#0b0e15;
  border:1px solid rgba(255,255,255,.10)}
.find-h{display:flex;align-items:center;gap:12px;padding:14px 20px;background:#0e121a;
  border-bottom:1px solid rgba(255,255,255,.07);flex-wrap:wrap}
.code{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:13px;
  letter-spacing:.04em;color:#cfd6e2;font-weight:650}
.sev{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  padding:4px 9px;border-radius:6px}
.sev.block{color:#ff8fae;background:rgba(255,92,138,.13);border:1px solid rgba(255,92,138,.3)}
.sev.warn{color:#ffc08a;background:rgba(255,122,69,.12);border:1px solid rgba(255,122,69,.28)}
.sev.note{color:#8fe3c0;background:rgba(61,220,138,.10);border:1px solid rgba(61,220,138,.26)}
.find-b{padding:19px 22px;font-size:16px;line-height:1.7;color:#c3cbd9}
.find-b .t{color:#f4f6fa;font-weight:550}

/* A measured figure quoted in running text. Small, monospaced, boxed — so a number that
 * came off a real run is visually distinct from a number in a sentence. */
.q{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:.88em;
  font-variant-numeric:tabular-nums;color:var(--ink);background:rgba(255,255,255,.055);
  border:1px solid var(--edge2);border-radius:6px;padding:2px 7px;white-space:nowrap;
  letter-spacing:-.02em}

.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:40px}
.grid.g3{grid-template-columns:repeat(3,1fr)}
@media(max-width:860px){.grid,.grid.g3{grid-template-columns:1fr}}
.card{padding:30px 28px;border-radius:20px;background:var(--pane);border:1px solid var(--edge);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);position:relative;
  overflow:hidden;transition:border-color .22s,transform .22s cubic-bezier(.2,.7,.3,1)}
/* A prism hairline that arrives on hover. One gradient, top edge only — enough to make
 * the card feel like a control rather than a box, without turning the grid into a
 * light show when eight of them sit together. */
.card::before{content:"";position:absolute;inset:0 0 auto 0;height:1px;opacity:0;
  background:linear-gradient(90deg,transparent,#ff7a45,#8b7bff,transparent);
  transition:opacity .3s}
a.card:hover{border-color:rgba(255,255,255,.24);transform:translateY(-3px)}
a.card:hover::before{opacity:1}
.card h3{font-size:19.5px;margin:0 0 11px}
.card p{margin:0;font-size:16px;color:var(--dim);line-height:1.66}
.card .more{display:inline-block;margin-top:17px;font-size:14.5px;color:#ffb894;font-weight:550}
a.card:hover .more{color:#ffd0b4}

table.spec{width:100%;border-collapse:collapse;margin:36px 0;font-size:15.6px}
table.spec th,table.spec td{text-align:left;padding:15px 16px;border-bottom:1px solid var(--edge2);
  color:var(--dim);vertical-align:top}
table.spec th{color:var(--ink);font-weight:600;font-size:12.5px;letter-spacing:.11em;
  text-transform:uppercase;border-bottom-color:var(--edge);padding-bottom:13px}
table.spec tr:last-child td{border-bottom:0}
table.spec tbody tr{transition:background .18s}
table.spec tbody tr:hover{background:rgba(255,255,255,.022)}
table.spec td b{color:var(--ink);font-weight:550}
table.spec td.num{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  font-variant-numeric:tabular-nums;white-space:nowrap;letter-spacing:-.02em}
.tscroll{overflow-x:auto}

/* A measured percentage, drawn as well as written. The distortion table is the single
 * most persuasive thing on this site — six resolutions, before and after, on a real
 * building — and as plain figures a reader has to do the comparison in their head. The
 * bar does it for them. Width comes from --v (the percentage) via an inline style, so
 * the number in the markup and the length of the bar cannot disagree. */
td.bar{position:relative}
td.bar::after{content:"";position:absolute;left:16px;right:16px;bottom:7px;height:2px;
  border-radius:2px;transform:scaleX(calc(var(--v,0) / 50));transform-origin:left;
  background:linear-gradient(90deg,#ff7a45,#ff5c8a)}
td.bar.good::after{background:linear-gradient(90deg,#3ddc8a,#4fd8e8)}
td.bar.stop::after{background:linear-gradient(90deg,#ff5c8a,#ff2d6f)}
/* NO `both`. With that fill mode the bar holds the keyframe's `from` state — scaleX(0) —
 * until the animation starts, so any document that does not composite, or any browser
 * that skips the animation, renders every bar at zero. A decorative animation would just
 * look wrong; this one CARRIES A MEASUREMENT, so it would quietly report that a 37.1%
 * distortion was 0%. Without a fill mode the resting state is the element's own
 * transform — the correct length — and the animation is purely an enhancement on top.
 *
 * This is the same defect that hid the scroll reveals behind an IntersectionObserver:
 * content whose correct state depends on an animation having run. */
@media(prefers-reduced-motion:no-preference){
  td.bar::after{animation:barin .9s cubic-bezier(.2,.7,.3,1)}
  @keyframes barin{from{transform:scaleX(0)}}
}

.steps{counter-reset:s;margin-top:40px}
.step{position:relative;padding:0 0 34px 62px;border-left:1px solid var(--edge);margin-left:16px}
.step:last-child{border-left-color:transparent;padding-bottom:0}
.step::before{counter-increment:s;content:counter(s);position:absolute;left:-17px;top:-4px;
  width:34px;height:34px;border-radius:11px;display:grid;place-items:center;
  font-size:14px;font-weight:650;color:#1c0a03;
  background:linear-gradient(100deg,#ffcda5,#ff7a45 55%,#ff5c8a)}
.step h3{font-size:19px;margin:0 0 9px}
.step p{margin:0;color:var(--dim);font-size:17px;line-height:1.7}
.step .when{display:inline-block;margin-top:14px;font-size:13px;color:var(--dim2);
  letter-spacing:.02em;padding:5px 11px;border:1px solid var(--edge2);border-radius:99px;
  background:var(--pane2)}

.faq{margin-top:36px}
.qa{border-bottom:1px solid var(--edge2);padding:22px 0}
.qa h3{font-size:19px;margin:0 0 11px;letter-spacing:-.028em}
.qa p{margin:0;color:var(--dim);font-size:17px;line-height:1.72}
.qa a{color:#ffb894;border-bottom:1px solid rgba(255,184,148,.35)}
.qa a:hover{border-bottom-color:#ffb894}
.body p a{color:#ffb894;border-bottom:1px solid rgba(255,184,148,.35)}
.body p a:hover{border-bottom-color:#ffb894}

.close{text-align:center;padding:92px 0}
.close h2{margin:0 auto} .close .lede{margin:20px auto 0}
.acts{display:flex;gap:13px;justify-content:center;margin-top:34px;flex-wrap:wrap}

footer{padding:46px 0;border-top:1px solid var(--edge2);font-size:14px;color:var(--dim2)}
footer .wrap{display:grid;grid-template-columns:1.7fr 1fr 1fr;gap:40px}
@media(max-width:760px){footer .wrap{grid-template-columns:1fr;gap:26px}}
footer a{color:var(--dim)} footer a:hover{color:var(--ink)}
/* h3, not h4. These follow the close section's h2, and h2 -> h4 is a level skip that a
 * screen reader announces as a missing section. Styled small deliberately — heading
 * LEVEL is structure, font size is presentation, and they are not the same decision. */
footer h3{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim2);
  margin:0 0 14px;font-weight:650}
footer .col a{display:block;margin-bottom:9px}
.fine{font-size:13px;line-height:1.7;color:#8b95a8;max-width:56ch;margin-top:14px}
/* ── reduced motion, comprehensively ─────────────────────────────────────────
   This used to be `.blob{animation:none}` and nothing else, which left the table bars
   growing and every transition on the page running. Naming individual selectors means
   each animation added later has to be remembered by whoever adds it, and eventually
   one is not — so this neutralises everything instead.

   Near-zero duration rather than `animation:none`, deliberately: it lets an animation
   settle on its END state. For `barin` that is the bar at its measured length; `none`
   would leave every bar at zero, which misreports the data the bar exists to show.
   That distinction matters more here than anywhere else on the site.

   `.blob` keeps `none` so the aurora stays where it was authored rather than jumping
   to the end of its drift. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms !important;
    animation-delay:0ms !important;
    animation-iteration-count:1 !important;
    transition-duration:1ms !important;
    transition-delay:0ms !important;
  }
  html{scroll-behavior:auto !important}
  .blob{animation:none !important}
}
