/* Shared components used on every page. Tokens only (see DESIGN.md). */

/* ---------- Top nav · attached to the top of the viewport, rounded at the bottom (Clerk-style) ----------
   Translucent because page content scrolls underneath it. */
.cr-nav{position:fixed;z-index:50;top:0;left:50%;transform:translateX(-50%);width:min(calc(var(--w-content) + 2 * var(--sp-6)),100% - 2 * var(--sp-4));height:var(--nav-h);display:flex;align-items:center;gap:var(--sp-1);padding:0 var(--sp-3) 0 var(--sp-6);background:color-mix(in srgb,var(--white-base) 86%,transparent);-webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);border-radius:0 0 var(--radius-xl) var(--radius-xl);box-shadow:var(--elevation-nav);font-family:var(--font-sans)}
.cr-nav a{display:inline-flex;align-items:center;height:40px;padding:0 var(--sp-3);border-radius:var(--radius-md);font-size:var(--fs-label);line-height:1;letter-spacing:var(--tr-label);font-weight:var(--fw-medium);color:var(--zinc-700);text-decoration:none;transition:background-color var(--dur-hover) var(--ease-hover),color var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){.cr-nav a:hover{background:var(--zinc-100);color:var(--foreground)}}
.cr-nav a[aria-current="page"]{background:var(--zinc-100);color:var(--foreground)}
.cr-nav a:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.cr-nav .cr-nav-logo{padding:0;margin-right:var(--sp-2)}
@media(hover:hover) and (pointer:fine){.cr-nav .cr-nav-logo:hover{background:none}}
.cr-nav-logo img{display:block;width:103px;height:20px}
/* Early-access status beside the logo: the .cr-pill surface with the nav's corner radius (--radius-md, like the links and button).
   A label, not a link. The mint light pulses: the dot breathes and a soft ring spreads out from it (--dur-live, ease-in-out, loops). */
.cr .cr-nav .cr-nav-pill{display:inline-flex;align-items:center;gap:var(--sp-2);margin-left:var(--sp-2);padding:var(--sp-1) 10px var(--sp-1) var(--sp-2);border-radius:var(--radius-md);cursor:default;user-select:none}
.cr-live{position:relative;width:8px;height:8px;flex:none;border-radius:var(--radius-full);background:var(--mint-500);animation:cr-live-dot var(--dur-live) var(--ease-in-out) infinite}
.cr-live::after{content:"";position:absolute;inset:0;border-radius:inherit;background:var(--mint-400);animation:cr-live-ring var(--dur-live) var(--ease-out) infinite}
@keyframes cr-live-dot{0%,100%{opacity:1}50%{opacity:.45}}
@keyframes cr-live-ring{0%{transform:scale(1);opacity:.55}70%,100%{transform:scale(2.6);opacity:0}}
@media(prefers-reduced-motion:reduce){.cr-live,.cr-live::after{animation:none}.cr-live::after{opacity:0}}
.cr-nav-sep{width:1px;height:20px;margin:0 var(--sp-2);background:var(--zinc-200)}
.cr-nav-end{margin-left:auto;display:flex;align-items:center;gap:var(--sp-1)}
/* The nav button is .cr-btn.is-sm: 12px in from the top, bottom and right edges (56 − 32 = 24, split evenly), so the right padding matches */
.cr .cr-nav .cr-btn{margin-left:var(--sp-2)}
html{scroll-padding-top:calc(var(--nav-h) + var(--sp-6))}

/* ---------- Button · the one button on the site (see DESIGN.md › Button) ----------
   <a|button class="cr-btn">Request early access</a>                  primary: one per view
   <button class="cr-btn is-secondary">…</button>                      secondary: white, hairline ring; aria-pressed="true" for a chosen option
   Modifiers: is-sm (32px), is-block (full width). Icons are inline SVG, 16px (14px at is-sm), before or after the label.
   <a class="cr-btn-link" href="…">See every feature <span aria-hidden="true">→</span></a>   text action beside a button, or on its own */
.cr .cr-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);height:40px;margin:0;padding:0 var(--sp-4);border:0;border-radius:var(--radius-md);background:var(--primary);box-shadow:var(--elevation-button);font-family:var(--font-sans);font-size:var(--fs-label);line-height:1;letter-spacing:var(--tr-label);font-weight:var(--fw-medium);color:var(--primary-foreground);text-decoration:none;white-space:nowrap;cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;
  transition:background-color var(--dur-hover) var(--ease-hover),color var(--dur-hover) var(--ease-hover),box-shadow var(--dur-hover) var(--ease-hover),scale var(--dur-hover) var(--ease-out)}
@media(hover:hover) and (pointer:fine){.cr .cr-btn:hover{background:var(--violet-800);color:var(--primary-foreground)}}
.cr .cr-btn:active{scale:.96}
.cr .cr-btn:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.cr .cr-btn:disabled,.cr .cr-btn[aria-disabled="true"]{opacity:.5;pointer-events:none}
.cr .cr-btn svg{width:16px;height:16px;flex:none}
/* optical balance: the icon side gets 2px less room than the text side */
.cr .cr-btn>svg:first-child{margin-left:-2px}
.cr .cr-btn>svg:last-child{margin-right:-2px}
.cr .cr-btn.is-sm{height:32px;padding:0 var(--sp-3);gap:6px}
.cr .cr-btn.is-sm svg{width:14px;height:14px}
.cr .cr-btn.is-block{display:flex;width:100%}
.cr .cr-btn.is-secondary{background:var(--card);box-shadow:var(--elevation-button-secondary);color:var(--zinc-800)}
@media(hover:hover) and (pointer:fine){.cr .cr-btn.is-secondary:hover{background:var(--zinc-50);color:var(--foreground)}}
.cr .cr-btn.is-secondary[aria-pressed="true"]{background:var(--secondary);box-shadow:inset 0 0 0 1px var(--violet-300);color:var(--primary)}
.cr .cr-btn-link{display:inline-flex;align-items:center;white-space:nowrap;gap:var(--sp-1);height:40px;padding:0 var(--sp-2);border-radius:var(--radius-md);font-family:var(--font-sans);font-size:var(--fs-label);line-height:1;letter-spacing:var(--tr-label);font-weight:var(--fw-medium);color:var(--zinc-800);text-decoration:none;transition:color var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){.cr .cr-btn-link:hover{color:var(--primary)}}
.cr .cr-btn-link:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.cr .cr-btn-link svg{width:16px;height:16px;flex:none}
.cr-btn-link>span,.cr-btn-link>svg{transition:translate var(--dur-hover) var(--ease-out)}
@media(hover:hover) and (pointer:fine){.cr-btn-link:hover>span{translate:2px 0}}
@media(prefers-reduced-motion:reduce){.cr .cr-btn{transition:background-color var(--dur-hover) var(--ease-hover),color var(--dur-hover) var(--ease-hover)}.cr .cr-btn:active{scale:none}.cr-btn-link:hover>span{translate:none}}

/* ---------- Pill badge · Figma hero "Now in Early Access" ----------
   <span class="cr-pill"><img src="assets/status-dot.svg" alt="" width="8" height="8">Now in Early Access</span>
   The leading dot or icon is optional. Micro, Medium, zinc-800 on white with --elevation-pill. */
.cr-pill{display:inline-flex;align-items:center;gap:var(--sp-1);padding:var(--sp-1) var(--sp-3);border-radius:var(--radius-full);background:var(--background);box-shadow:var(--elevation-pill);font-family:var(--font-sans);font-size:var(--fs-micro);line-height:1.4;letter-spacing:var(--tr-label);font-weight:var(--fw-medium);color:var(--zinc-800);white-space:nowrap}
.cr-pill>img,.cr-pill>svg{width:8px;height:8px;flex:none}
a.cr-pill{text-decoration:none;transition:color var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){a.cr-pill:hover{color:var(--primary)}}
a.cr-pill:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.cr-pill .cr-pill-go{color:var(--zinc-400);transition:translate var(--dur-hover) var(--ease-out),color var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){a.cr-pill:hover .cr-pill-go{translate:2px 0;color:var(--primary)}}

/* ---------- Footer · from design screenshot (Figma MCP limit hit) ---------- */
.cr-foot{position:relative;overflow:hidden;isolation:isolate;height:543px;padding-top:148px;background:var(--background);font-family:var(--font-sans)}
.cr-foot::before{content:"";position:absolute;z-index:-1;inset:0 0 auto;height:560px;
  background:linear-gradient(90deg,var(--violet-50) 6%,var(--violet-200) 30%,color-mix(in srgb,var(--violet-200) 55%,var(--mint-200)) 50%,var(--mint-100) 72%,var(--mint-50) 94%);
  -webkit-mask-image:radial-gradient(ellipse 48% 92% at 50% 0,#000 28%,transparent 100%);mask-image:radial-gradient(ellipse 48% 92% at 50% 0,#000 28%,transparent 100%)}
/* Recess: drawn above the glow so the page above appears to overhang the footer */
.cr-foot::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;box-shadow:var(--elevation-inset-top)}
.cr-foot-mark{position:absolute;left:50%;top:-24px;width:48px;height:48px;transform:translateX(-50%);opacity:.5}
.cr-foot-inner{width:var(--w-content);margin-inline:auto}
.cr-foot-top{display:flex;justify-content:space-between;gap:var(--sp-8);padding-bottom:var(--sp-9);border-bottom:.5px solid var(--border)}
.cr-foot-brand img{display:block;width:122.913px;height:24px}
.cr .cr-foot-brand p{margin:var(--sp-6) 0 0;max-width:280px;font-size:var(--fs-label);line-height:var(--lh-label);color:var(--zinc-600)}
.cr-foot-cols{display:flex;gap:var(--sp-15)}
.cr-foot-col{display:grid;gap:var(--sp-2);align-content:start}
.cr .cr-foot-col .cr-foot-h{margin:0;max-width:none;font-size:var(--fs-label);line-height:var(--lh-label);font-weight:var(--fw-regular);letter-spacing:0;color:var(--zinc-500)}
.cr .cr-foot a{color:var(--zinc-900);text-decoration:none;font-size:var(--fs-label);line-height:var(--lh-label);font-weight:var(--fw-medium);transition:color var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){.cr .cr-foot a:hover{color:var(--primary)}}
.cr .cr-foot a:focus-visible{outline:2px solid var(--ring);outline-offset:3px;border-radius:var(--radius-xs)}
.cr-foot-bottom{display:flex;justify-content:space-between;align-items:center;padding-top:var(--sp-8);font-size:var(--fs-label);line-height:var(--lh-label);color:var(--zinc-500)}
.cr-foot-legal{display:flex;gap:var(--sp-7)}
.cr .cr-foot-legal a{color:var(--zinc-700)}
/* Wordmark: inline SVG, gradient fill drifting through the letters (SMIL gradientTransform, --dur-ambient = 12s).
   Paused off-screen and for reduced motion by the footer script. */
.cr-foot-word{position:absolute;left:50%;top:425px;opacity:.5;width:886px;height:166px;margin-left:-443px;display:block;overflow:visible;pointer-events:none;user-select:none}

/* ---------- Connector (.cr-conn) · drawn by components.js from data-conn / data-conn-to pairs; see DESIGN.md ----------
   1px violet-300 line: straight when its ends line up, otherwise a soft S-curve. A 5px violet-500 pin with a 2px white halo
   marks where it starts (data-conn-pin="off" for short links in a sequence). Hidden lines draw themselves in over --dur-note.
   Pages retint a single link with --conn-stroke on its group (g[data-k]), e.g. on hover. */
.cr-conn{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none;z-index:1}
.cr-conn path{fill:none;stroke:var(--conn-stroke,var(--violet-300));stroke-width:1;stroke-linecap:round;stroke-dasharray:1;stroke-dashoffset:0;
  transition:stroke-dashoffset var(--dur-note) var(--ease-out),stroke var(--dur-hover) var(--ease-hover)}
.cr-conn circle{fill:var(--conn-stroke,var(--violet-500));stroke:var(--white-base);stroke-width:2;paint-order:stroke;transition:opacity var(--dur-open) var(--ease-out),fill var(--dur-hover) var(--ease-hover)}
.cr-conn g:not(.is-on) path{stroke-dashoffset:1;transition-duration:var(--dur-exit)}
.cr-conn g:not(.is-on) circle{opacity:0;transition-duration:var(--dur-exit)}
@media(prefers-reduced-motion:reduce){.cr-conn path,.cr-conn circle{transition:none}}

/* ---------- Nav · phones (≤760px) ----------
   The bar keeps the logo, the early-access pill and a menu button. The links, "Log in" and the button move into a
   panel that drops out of the bar on the same frosted surface. Everything stays in the DOM; closed = visibility:hidden.
   Opens over --dur-open with --ease-out-strong (fade, 6px drop, scale .98 from the top); closes over --dur-exit. */
.cr-nav-menu{display:none}
@media(max-width:760px){
  .cr-nav{padding:0 var(--sp-2) 0 var(--sp-5)}
  .cr .cr-nav .cr-nav-pill{margin-left:var(--sp-1)}
  .cr-nav-menu{position:relative;display:grid;place-items:center;align-content:center;gap:5px;width:44px;height:44px;margin-left:auto;padding:0;border:0;border-radius:var(--radius-md);background:none;cursor:pointer;-webkit-tap-highlight-color:transparent}
  .cr-nav-menu span{display:block;width:16px;height:1.5px;border-radius:1px;background:var(--zinc-800);transition:transform var(--dur-open) var(--ease-in-out)} /* lines already on screen turning into a cross: in-out */
  .cr-nav.is-open .cr-nav-menu span:first-child{transform:translateY(3.25px) rotate(45deg)}
  .cr-nav.is-open .cr-nav-menu span:last-child{transform:translateY(-3.25px) rotate(-45deg)}
  .cr-nav-menu:focus-visible{outline:2px solid var(--ring);outline-offset:-2px}
  .cr-nav-end{position:absolute;top:calc(100% + var(--sp-2));left:0;right:0;display:grid;gap:0;padding:var(--sp-2);border-radius:var(--radius-xl);
    background:color-mix(in srgb,var(--white-base) 94%,transparent);-webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);box-shadow:var(--elevation-popover);
    transform-origin:top center;opacity:0;visibility:hidden;transform:translateY(-6px) scale(.98);
    transition:opacity var(--dur-exit) var(--ease-out),transform var(--dur-exit) var(--ease-out),visibility 0s linear var(--dur-exit)}
  .cr-nav.is-open .cr-nav-end{opacity:1;visibility:visible;transform:none;transition:opacity var(--dur-open) var(--ease-out-strong),transform var(--dur-open) var(--ease-out-strong),visibility 0s}
  .cr-nav .cr-nav-end a{height:48px;padding:0 var(--sp-3);font-size:var(--fs-body)}
  .cr-nav-sep{width:auto;height:1px;margin:var(--sp-1) var(--sp-3)}
  .cr .cr-nav .cr-nav-end .cr-btn{height:44px;margin:var(--sp-2) 0 0}
}
@media(max-width:359px){.cr .cr-nav .cr-nav-pill{display:none}}
@media(max-width:760px) and (prefers-reduced-motion:reduce){.cr-nav-end,.cr-nav.is-open .cr-nav-end,.cr-nav-menu span{transition:none}.cr-nav-end{transform:none}}

/* ---------- Footer · tablet and phone ----------
   The fixed 543px footer becomes a stack: brand, link columns, then the legal row. The wordmark shrinks with the width. */
@media(max-width:760px){
  .cr-foot{height:auto;padding:var(--sp-14) 0 0}
  .cr-foot-top{flex-direction:column;gap:var(--sp-9);padding-bottom:var(--sp-8)}
  .cr-foot-cols{gap:var(--sp-12)}
  .cr-foot-bottom{flex-direction:column;align-items:flex-start;gap:var(--sp-4);padding-bottom:calc(var(--sp-13) + 19vw + env(safe-area-inset-bottom))}
  .cr-foot-word{top:auto;bottom:calc(-4vw);left:50%;width:92vw;height:auto;margin-left:-46vw}
}

/* ---------- Touch targets ----------
   On touch screens every small control reaches 44px without changing how it looks: text links get a taller box,
   32px buttons get an invisible 6px bleed on each side. */
@media(pointer:coarse){
  .cr .cr-foot-col a,.cr .cr-foot-legal a{display:inline-flex;align-items:center;min-height:44px}
  .cr-foot-col{gap:0}
  .cr .cr-btn.is-sm{position:relative}
  .cr .cr-btn.is-sm::before{content:"";position:absolute;inset:-6px}
}

/* ---------- Page transitions ----------
   Moving between pages cross-fades the content (the browser's default root transition, --dur-open) while the nav,
   named cr-nav, stays exactly where it is. Browsers without cross-document view transitions just navigate. */
@view-transition{navigation:auto}
.cr-nav{view-transition-name:cr-nav}
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-state);animation-timing-function:var(--ease-out)}
@media(prefers-reduced-motion:reduce){@view-transition{navigation:none}}

/* ---------- Illustration fit (phones) ----------
   Illustrations are drawn at a fixed pixel size. Anything with data-fit="<design width>" is zoomed down by
   components.js to fit its container, so the drawing keeps its proportions instead of being cropped. */
[data-fit]{transform-origin:top left}

/* Hand-set line breaks in running text are for desktop measures; on phones the text reflows naturally.
   Headings keep theirs (they are short and deliberate); illustrations keep theirs. */
@media(max-width:760px){.cr p br{display:none}.cr [role="img"] p br{display:revert}}

/* ---------- Print ----------
   One clean document: no fixed nav repeating on every page, no replay controls or decoration, and no card, frame
   or illustration split across a page break. Illustrations print in their finished state. */
/* ---------- Replay control (`.cr-replay`) ----------
   One control for every illustration on the site. It lives inside the illustration's own panel
   (`.cr-replay-host`) rather than beside it, and stays invisible until you hover the panel or tab to
   it — the video-player convention. A page with eight illustrations then reads as eight pictures
   instead of eight control panels. Where the illustration has nothing else to click, the panel
   itself replays on click (`data-replay-click`), which is how the control is reached on a phone.
   Touch has no hover, so the button shows by default there; `.is-quiet` keeps it hidden on the
   repeating Features cards, where eight visible buttons would be the problem this solves.
   Reduced motion and print remove every one of them in a single rule. */
.cr-replay-host{position:relative}
.cr [data-replay-click]{cursor:pointer}
.cr .cr-replay{position:absolute;z-index:4;right:var(--sp-4);bottom:var(--sp-4);opacity:0;
  transition:opacity var(--dur-hover) var(--ease-hover),background-color var(--dur-hover) var(--ease-hover),color var(--dur-hover) var(--ease-hover),box-shadow var(--dur-hover) var(--ease-hover),scale var(--dur-hover) var(--ease-out)}
/* left-hand placement for panels that bleed off the right edge of the page */
.cr .cr-replay.is-start{right:auto;left:var(--sp-4)}
@media(hover:hover) and (pointer:fine){
  .cr .cr-replay-host:hover>.cr-replay{opacity:1}
}
/* keyboard reaches it at any width, and outside the hover query, so it is never a hidden tab stop */
.cr .cr-replay-host:focus-within>.cr-replay,.cr .cr-replay:focus,.cr .cr-replay:focus-visible{opacity:1}
@media(hover:none){
  .cr .cr-replay{opacity:1}
  .cr .cr-replay.is-quiet{opacity:0}
}
@media(prefers-reduced-motion:reduce){
  .cr .cr-replay{display:none}
  .cr [data-replay-click]{cursor:auto}
}

@media print{
  .cr-nav,.cr-skip,.cr-nav-menu,.cr-replay,.cr-fx-next,.cr-fx-toc,.cr-foot-word,.cr-foot-mark,.cr-hero-glow,.cr-live{display:none!important}
  .cr-foot{height:auto;padding:var(--sp-9) 0 0}
  .cr-foot::before,.cr-foot::after{display:none}
  .cr-fx-sticky{position:static}
  .cr-fx-frame,.cr-work-card,.cr-ab-frame,.cr-ab-card,.cr-jobs-card,.cr-why-panel,.cr-nf-frame,figure{break-inside:avoid}
  .cr :is(h1,h2,h3){break-after:avoid}
  .cr *{transition:none!important;animation:none!important}
}

/* ---------- Close · the light close, the last thing on every page ----------
   After the dark testimonial on the homepage the page opens out: mint-50 fading to the page background (the only
   mint surface on the site), everything centred, nothing else competing. The early-access pill, the biggest headline
   on the site, one line of lead, then the primary button and the email link. No card, no illustration.
   <section class="cr-close"><div class="cr-close-inner"><span class="cr-pill">…</span><h2>…</h2><p>…</p>
   <div class="cr-close-cta">…</div></div></section> */
.cr-close{padding:var(--sp-15) 0 var(--sp-16);background:linear-gradient(180deg in oklab,var(--mint-50),var(--background) 62%)}
.cr-close-inner{width:var(--w-content);margin-inline:auto;display:grid;justify-items:center;gap:var(--sp-5);text-align:center}
.cr .cr-close .cr-pill{gap:var(--sp-2);height:26px;padding:0 12px}
.cr-close-dot{width:7px;height:7px;flex:none;border-radius:var(--radius-full);background:var(--mint-500)}
.cr .cr-close h2{margin:0;max-width:none;font-size:var(--fs-h1);line-height:var(--lh-h1);letter-spacing:var(--tr-h1);font-weight:var(--fw-medium);color:var(--foreground);text-wrap:balance}
.cr .cr-close p{margin:0;max-width:520px;font-size:var(--fs-lead);line-height:var(--lh-lead);letter-spacing:var(--tr-lead);color:var(--zinc-600);text-wrap:pretty}
.cr-close-cta{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--sp-4);margin-top:var(--sp-2)}
/* Flat variants. The gradient only works where the section above it is dark or strongly coloured
   (the homepage's quote surface). Where the close follows another pale band it disappears into it, so
   About and Features take a flat fill stepped against whatever precedes them, plus a real top rule:
   About's last section is zinc-50, so its close is white; Features' last section is white, so its close
   is zinc-50. Content is identical — this is separation only. See DESIGN.md › Close. */
.cr-about-page .cr-close{background:var(--background);border-top:.5px solid var(--zinc-300)}
.cr-features-page .cr-close{background:var(--zinc-50);border-top:.5px solid var(--zinc-300)}
@media(max-width:760px){.cr-close{padding:var(--sp-12) 0 var(--sp-13)}.cr .cr-close h2{font-size:var(--fs-h2);line-height:var(--lh-h2);letter-spacing:var(--tr-h2)}}

/* ---------- Trust strip · facts a public-sector buyer checks first (above the footer) ----------
   Ships hidden: each item stays `hidden` until the claim is confirmed true, and the strip shows once one item is.
   Micro zinc-600 on white, a 16px zinc-500 icon, items spaced by --sp-8, hairline rules above and below. */
.cr-trust{width:var(--w-content);margin:0 auto;padding:var(--sp-6) 0;display:flex;flex-wrap:wrap;justify-content:center;gap:var(--sp-3) var(--sp-8);border-top:.5px solid var(--border);font-size:var(--fs-label);line-height:var(--lh-label);color:var(--zinc-600)}
.cr-trust:not(:has(li:not([hidden]))){display:none}
.cr-trust li{display:flex;align-items:center;gap:var(--sp-2)}
.cr-trust li[hidden]{display:none}
.cr-trust svg{width:16px;height:16px;flex:none;color:var(--zinc-500)}
.cr .cr-trust{list-style:none}

/* ---------- Request early access · "a postcard to Jing" (see DESIGN.md) ----------
   Every early-access link opens a native <dialog> (focus trap, Escape, inert page) that reads like writing a postcard
   to Jing: the note on ruled lines on the left, a hairline down the middle, and on the right a perforated stamp with
   Jing's photo, a violet postmark and the address lines (To Jing · From you · Reply to). House frame around a white card.
   Opens over --dur-open (fade + scale .97, --ease-out-strong); the stamp lands a beat later (--dur-note) and the
   postmark is struck after it (--dur-state). Closes over --dur-exit. Without script the links stay plain mailto: links. */
.cr-req{width:min(580px,100vw - 2 * var(--sp-4));max-height:calc(100dvh - 2 * var(--sp-4));margin:auto;padding:var(--sp-2);border:0;border-radius:var(--radius-2xl);background:var(--zinc-100);box-shadow:inset 0 0 0 1px var(--zinc-200),var(--elevation-popover);color:var(--foreground);font-family:var(--font-sans);overflow:auto;
  opacity:0;transform:scale(.97);transition:opacity var(--dur-exit) var(--ease-out),transform var(--dur-exit) var(--ease-out),overlay var(--dur-exit) allow-discrete,display var(--dur-exit) allow-discrete}
.cr-req[open]{opacity:1;transform:none;transition:opacity var(--dur-open) var(--ease-out-strong),transform var(--dur-open) var(--ease-out-strong),overlay var(--dur-open) allow-discrete,display var(--dur-open) allow-discrete}
@starting-style{.cr-req[open]{opacity:0;transform:scale(.97)}}
.cr-req::backdrop{background:rgb(3 1 57 / 0);-webkit-backdrop-filter:blur(0);backdrop-filter:blur(0);transition:background-color var(--dur-exit) var(--ease-out),backdrop-filter var(--dur-exit) var(--ease-out),overlay var(--dur-exit) allow-discrete,display var(--dur-exit) allow-discrete}
.cr-req[open]::backdrop{background:rgb(3 1 57 / .28);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);transition-duration:var(--dur-open)}
@starting-style{.cr-req[open]::backdrop{background:rgb(3 1 57 / 0);backdrop-filter:blur(0)}}
html:has(.cr-req[open]){overflow:hidden}
.cr-req [hidden]{display:none!important}
.cr-req-card{position:relative;overflow:hidden;border-radius:var(--radius-lg);background:var(--card);box-shadow:var(--elevation-card)}
.cr-req-x{position:absolute;z-index:3;top:var(--sp-3);right:var(--sp-3);display:grid;place-items:center;width:32px;height:32px;padding:0;border:0;border-radius:var(--radius-md);background:none;color:var(--zinc-500);cursor:pointer;transition:background-color var(--dur-hover) var(--ease-hover),color var(--dur-hover) var(--ease-hover)}
.cr-req-x::before{content:"";position:absolute;inset:-6px}
.cr-req-x svg{width:16px;height:16px}
@media(hover:hover) and (pointer:fine){.cr-req-x:hover{background:var(--zinc-100);color:var(--foreground)}}
.cr-req-x:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
.cr-req-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* spam trap: a field people never see or reach; bots fill it and the server quietly drops the card */
.cr-req-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.cr .cr-req-err a{color:inherit;text-decoration:underline;text-underline-offset:.18em}

/* the card is a letter to Jing: the envelope first — who it is to, the stamp and postmark, LinkedIn — so you
   meet the person before you are asked to write anything; then one column to fill, then send */
.cr-req-env{position:relative;display:flex;align-items:flex-end;min-height:156px;padding:var(--sp-6) var(--sp-7) var(--sp-5);background:var(--violet-50);border-bottom:1px solid var(--zinc-200)}
.cr-req-who{width:min(260px,58%)}
.cr-req-stampbox{position:absolute;top:22px;right:60px;width:88px;height:104px}
.cr-req-pc{display:block;padding:var(--sp-6) var(--sp-7) var(--sp-7)}
.cr-req-msg{display:flex;flex-direction:column}
.cr-req-fields{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4);margin-top:var(--sp-5)}
.cr-req-fields .cr-req-field{margin-top:0}
.cr .cr-req h2{margin:0;font-size:var(--fs-h5);line-height:var(--lh-h5);letter-spacing:var(--tr-h5);font-weight:var(--fw-medium);color:var(--foreground)}
.cr .cr-req-lead{margin:var(--sp-2) 0 0;max-width:none;font-size:var(--fs-label);line-height:var(--lh-label);color:var(--zinc-600)}
/* fields: every one is a labelled box, so it is obvious what to fill in and where.
   The note keeps its ruled lines, inside the box. */
.cr-req-field{display:grid;gap:6px;margin-top:var(--sp-4)}
.cr-req-field>span{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:var(--fw-medium);color:var(--zinc-800)}
.cr .cr-req :is(input,textarea){width:100%;box-sizing:border-box;margin:0;border:1px solid var(--input);border-radius:var(--radius-md);background:var(--card);font:inherit;font-size:var(--fs-body);font-weight:var(--fw-regular);color:var(--foreground);transition:border-color var(--dur-hover) var(--ease-hover)}
.cr .cr-req input{height:40px;padding:0 var(--sp-3)}
.cr .cr-req textarea{min-height:176px;padding:6px var(--sp-3) 0;resize:none;line-height:28px;
  background:var(--card) repeating-linear-gradient(transparent 0 27px,var(--zinc-100) 27px 28px) 0 34px/100% 28px local}
.cr .cr-req :is(input,textarea)::placeholder{color:var(--zinc-400)}
@media(hover:hover) and (pointer:fine){.cr .cr-req :is(input,textarea):hover{border-color:var(--zinc-300)}}
.cr .cr-req :is(input,textarea):focus-visible{outline:2px solid var(--ring);outline-offset:1px;border-color:var(--zinc-300)}
.cr .cr-req :is(input,textarea):user-invalid{border-color:var(--violet-400)}
.cr-req-note-field{flex:1;display:grid;grid-template-rows:auto 1fr;margin-top:var(--sp-4)}
/* LinkedIn, in LinkedIn's own blue: the one colour on the site outside zinc, violet and mint, because it is a brand mark */
.cr .cr-req-li{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);height:32px;margin-top:var(--sp-3);padding:0 var(--sp-3);border-radius:var(--radius-md);background:#0A66C2;box-shadow:inset 0 0 0 1px rgb(0 0 0 / .08);color:#fff;font-size:var(--fs-label);line-height:1;letter-spacing:var(--tr-label);font-weight:var(--fw-medium);text-decoration:none;white-space:nowrap;transition:background-color var(--dur-hover) var(--ease-hover),scale var(--dur-hover) var(--ease-out)}
.cr .cr-req-li svg{width:16px;height:16px;flex:none;margin-left:-2px}
@media(hover:hover) and (pointer:fine){.cr .cr-req-li:hover{background:#004182}}
.cr .cr-req-li:active{scale:.96}
.cr .cr-req-li:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
/* the stamp: perforated edge (mask), 5px white margin, Jing in violet duotone, a hairline frame, value and name */
.cr-req-stamp{position:absolute;top:0;right:0;width:88px;height:104px;transform:rotate(3deg);filter:drop-shadow(0 0 .5px rgb(3 1 57 / .4)) drop-shadow(0 2px 3px rgb(3 1 57 / .12))}
.cr-req-stamp-p{width:100%;height:100%;padding:6px;box-sizing:border-box;background:var(--card);
  -webkit-mask:linear-gradient(#000 0 0) content-box,radial-gradient(circle at 0 0,#0000 2.7px,#000 3.1px) 0 0/8px 8px padding-box;mask:linear-gradient(#000 0 0) content-box,radial-gradient(circle at 0 0,#0000 2.7px,#000 3.1px) 0 0/8px 8px padding-box}
.cr-req-stamp-in{position:relative;width:100%;height:100%;overflow:hidden;background:var(--violet-100);box-shadow:inset 0 0 0 .5px var(--violet-300)}
.cr-req-stamp img{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 30%;filter:grayscale(1) contrast(1.08);mix-blend-mode:multiply}
.cr-req-stamp-in::after{content:"";position:absolute;inset:0;background:var(--violet-500);mix-blend-mode:screen;opacity:.28}
/* the stamp reacts to the pointer: the light runs faster and brighter, and the tilt lifts a touch */
.cr-req-stamp{--shim-dur:7s;--shim-op:.85}
@media(hover:hover) and (pointer:fine){.cr-req-stamp{pointer-events:auto;transition:transform var(--dur-state) var(--ease-out)}.cr-req-stamp:hover{--shim-dur:1.9s;--shim-op:1;transform:rotate(3deg) scale(1.03)}}
/* shimmer: a violet-to-mint light drifting across the stamp, like the footer wordmark (--dur-ambient, linear, loops while the card is open) */
.cr-req-stamp-in::before{content:"";position:absolute;z-index:1;inset:-20%;opacity:var(--shim-op,.85);background:linear-gradient(105deg in oklab,transparent 26%,color-mix(in srgb,var(--violet-500) 85%,transparent) 40%,color-mix(in srgb,var(--violet-300) 95%,transparent) 48%,color-mix(in srgb,var(--mint-400) 95%,transparent) 58%,color-mix(in srgb,var(--mint-200) 80%,transparent) 66%,transparent 78%) 0 0/260% 100% no-repeat;mix-blend-mode:screen;pointer-events:none;animation:cr-req-shim var(--shim-dur,7s) linear infinite;transition:opacity var(--dur-state) var(--ease-hover)}
@keyframes cr-req-shim{from{background-position:180% 0}to{background-position:-80% 0}}
.cr-req-stamp-v{position:absolute;z-index:1;top:4px;left:5px;font-size:11px;line-height:1;font-weight:var(--fw-medium);color:var(--card);letter-spacing:.02em}
/* the postmark: double ring, lettering on the ring and wavy cancel lines, struck over the stamp's lower-left corner */
.cr-req-pm{position:absolute;z-index:2;top:50px;right:44px;width:170px;height:80px;color:var(--violet-500);opacity:.55;transform:rotate(-10deg);pointer-events:none}
/* address lines: printed, not typed */
.cr-req-to{display:grid;border-top:1px solid var(--zinc-200)}
.cr-req-to>*{display:flex;align-items:flex-end;height:30px;padding-bottom:5px;box-sizing:border-box;border-bottom:1px solid var(--zinc-200);font-size:var(--fs-label);line-height:1.2;color:var(--zinc-700)}
.cr-req-to b{font-weight:var(--fw-medium);color:var(--foreground)}
.cr-req-to small{margin-right:var(--sp-2);font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--zinc-400)}
/* foot: the promise on the left, send on the right */
.cr-req-foot{grid-area:foot;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-5);margin-top:var(--sp-5);padding-top:var(--sp-5);border-top:1px solid var(--zinc-200)}
.cr-req-promise{flex:1;min-width:0}
.cr-req-send{display:flex;align-items:center;gap:var(--sp-3);flex:none;margin-left:auto}
.cr .cr-req-note{margin:0;max-width:none;font-size:var(--fs-micro);line-height:var(--lh-micro);color:var(--zinc-500)}
.cr .cr-req-err{margin:0;max-width:none;font-size:var(--fs-label);color:var(--review)}
/* sent */
.cr-req-done{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--sp-2) var(--sp-7);padding:var(--sp-7) var(--sp-8) var(--sp-7) var(--sp-6)}
/* sent: the stamp again, franked with just the ring (no cancel lines) over its lower-right corner */
.cr-req-done-stamp{position:relative;grid-row:1 / span 3;width:88px;height:104px;margin:var(--sp-2) 0 var(--sp-2) var(--sp-2)}
.cr-req-done .cr-req-stamp{top:0;right:auto;left:0}
.cr-req-done .cr-req-pm{top:50px;right:auto;left:-50px}
.cr-req-done .cr-req-pm .cr-req-pm-w{display:none}
.cr .cr-req-done h2{margin:0}
.cr .cr-req-done p{margin:0;max-width:none;font-size:var(--fs-body);line-height:var(--lh-body);color:var(--zinc-600)}
.cr-req-done-cta{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;margin-top:var(--sp-3)}
.cr-req-done:not([hidden]){animation:cr-req-in var(--dur-note) var(--ease-out) both}
/* arrival: the stamp lands (settling from 1.06 and -4deg to its 3deg tilt), then the postmark is struck */
@keyframes cr-req-in{from{opacity:0;transform:translateY(6px);filter:blur(3px)}}
/* posting: the written side lifts, tilts and slides out of the top, then the thank-you arrives in its place */
@keyframes cr-req-post{40%{opacity:1;transform:translateY(-8px) rotate(-1.5deg) scale(1.01)}to{opacity:0;transform:translateY(-120%) rotate(-5deg) scale(.96)}}
.cr-req-card.is-posting{overflow:hidden}
.cr-req-card.is-posting .cr-req-pc{animation:cr-req-post 420ms var(--ease-fall) both;pointer-events:none}
@keyframes cr-req-stamp{from{opacity:0;transform:rotate(-4deg) scale(1.06)}}
.cr-req[open] .cr-req-stamp{animation:cr-req-stamp var(--dur-note) var(--ease-out-strong) 120ms backwards} /* backwards, not both: the stamp keeps its own transform afterwards so hover can lift it */
/* the postmark is struck when every field is filled in: the card is ready to post */
.cr-req-pm{opacity:0;transform:rotate(-10deg) scale(1.12);transition:opacity var(--dur-state) var(--ease-out),transform var(--dur-state) var(--ease-out)}
.cr-req.is-ready .cr-req-pm,.cr-req-done .cr-req-pm{opacity:.55;transform:rotate(-10deg) scale(1)}
@media(max-width:560px){.cr-req-fields{grid-template-columns:minmax(0,1fr)}}
@media(max-width:620px){
  /* too narrow for To lines, LinkedIn and the stamp side by side: the To lines sit beside the stamp and
     LinkedIn takes its own row under both. The right padding keeps the tilted stamp clear of the close button. */
  .cr-req-env{display:grid;grid-template-columns:minmax(0,1fr) 96px;gap:0 var(--sp-3);align-items:start;min-height:0;padding:var(--sp-5) 52px var(--sp-4) var(--sp-5)}
  .cr-req-who{display:contents}
  .cr-req-to{grid-column:1;grid-row:1;align-self:center}
  .cr .cr-req-li{grid-column:1 / -1;grid-row:2;display:flex;margin-top:var(--sp-4)}
  .cr-req-stampbox{position:relative;top:auto;right:auto;grid-column:2;grid-row:1}
  .cr-req-pm .cr-req-pm-w{display:none}
  .cr-req-pc{padding:var(--sp-5) var(--sp-5) var(--sp-5)}
  .cr-req-fields{grid-template-columns:minmax(0,1fr)}
  .cr .cr-req textarea{min-height:140px}
  .cr-req-foot{flex-direction:column-reverse;align-items:stretch;text-align:center}
  .cr-req-send{flex:none;display:block;margin-left:0} /* the desktop auto margin would stop it stretching */
  .cr .cr-req-submit{width:100%}
  .cr-req-done-cta{justify-content:center}
  .cr-req-done{grid-template-columns:minmax(0,1fr);padding:var(--sp-2)}
  .cr-req-done-stamp{grid-row:auto;margin-bottom:var(--sp-4)}
}
@media(prefers-reduced-motion:reduce){.cr-req,.cr-req[open],.cr-req::backdrop{transition:none}.cr-req-done:not([hidden]),.cr-req[open] .cr-req-stamp,.cr-req[open] .cr-req-pm,.cr-req-stamp-in::before{animation:none}}

/* ---------------------------------------------------------------
   POLISH PASS · Sep 2026. See DESIGN.md › Polish.
   --------------------------------------------------------------- */

/* 8 · Cross-page continuity. The nav, the close and the footer wordmark are the same object on every
   page, so they persist across a navigation instead of being torn down and rebuilt. Progressive
   enhancement: browsers without view transitions get the ordinary hard cut, and reduced motion opts out
   below. Keep the names unique per page — two elements sharing a name on one page breaks the transition. */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-open);animation-timing-function:var(--ease-out)}
.cr-nav{view-transition-name:cr-nav}
.cr-close{view-transition-name:cr-close}
.cr-foot{view-transition-name:cr-foot}

/* 9 · The nav earns its surface. Over the hero it is just the logo and the links on the page's own
   background; past 24px of scroll it picks up the white fill, the blur and the ring. `.is-stuck` is set
   by components.js from a sentinel at the top of the page, so there is no scroll listener. */
.cr-nav{background:rgb(255 255 255 / 0);-webkit-backdrop-filter:blur(0px) saturate(1);backdrop-filter:blur(0px) saturate(1);box-shadow:none;
  transition:background-color var(--dur-hover) var(--ease-hover),box-shadow var(--dur-hover) var(--ease-hover),backdrop-filter var(--dur-hover) var(--ease-hover)}
.cr-nav.is-stuck{background:color-mix(in srgb,var(--white-base) 86%,transparent);
  -webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);box-shadow:var(--elevation-nav)}
/* the phone menu panel always needs the surface under it, scrolled or not */
.cr-nav.is-open{background:color-mix(in srgb,var(--white-base) 86%,transparent);
  -webkit-backdrop-filter:blur(12px) saturate(1.4);backdrop-filter:blur(12px) saturate(1.4);box-shadow:var(--elevation-nav)}

/* 13 · Any counter that animates gets equal-width digits, so the number never jiggles as it counts.
   On the attribute, not per component, so a new counter inherits it. */
.cr [data-count],.cr [data-total],.cr [data-tally-tot]{font-variant-numeric:tabular-nums}

/* 16 · Photographs carry a 1px inset edge. Pure black at 10% — never a palette neutral, which picks up
   whatever sits behind the image and reads as grime along the edge. Drawn with outline and pulled inside,
   so it costs no layout space.
   Rasters only. An SVG logo or a line-art mark has no photographic edge to define, and drawing one round
   it just boxes the artwork; the full-bleed .webp washes behind an illustration are surfaces, not pictures,
   and are clipped by their panel anyway. Both opt out, as does anything marked .is-bare. */
.cr img:not(.is-bare):not([src$=".svg"]){outline:var(--outline-image);outline-offset:-1px}
.cr :is(.cr-work-panel,.cr-why-panel,.cr-nf-panel,.cr-rul,.cr-tally-panel,.cr-f-panel,.cr-fy-panel,.cr-req-stamp)
  >img:not(.is-bare):not([src$=".svg"]){outline:none}

/* 23 · Hit areas. Footer links are 20px of text, under the 40px desktop / 44px touch minimum.
   Stacked column links sit only 8px apart, so growing them with a pseudo-element would make two
   neighbouring targets overlap — the one thing a hit area must never do. They become real 40px rows
   instead, which tile with the 8px gap intact and cost the footer ~16px per link.
   The legal links run horizontally 32px apart, so there a pseudo-element is safe: ±10px vertical and
   ±8px horizontal keeps 16px of clear space between neighbours. */
.cr :is(.cr-foot-col a,.cr-foot-brand a){display:flex;align-items:center;min-height:40px}
.cr .cr-foot-legal a{position:relative;display:inline-flex;align-items:center;min-height:24px}
.cr .cr-foot-legal a::before{content:"";position:absolute;inset:-10px -8px}
@media(pointer:coarse){
  .cr :is(.cr-foot-col a,.cr-foot-brand a){min-height:44px}
  .cr .cr-foot-legal a::before{inset:-12px -8px}
}

/* 24 · Notches and rounded corners. The nav is fixed to the top edge and the gutter runs to the sides,
   so both have to clear the inset on a phone held in landscape. */
.cr-nav{top:env(safe-area-inset-top,0px)}
.cr-nav-panel,.cr-nav-end{padding-left:max(0px,env(safe-area-inset-left));padding-right:max(0px,env(safe-area-inset-right))}

@media(prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
  .cr-nav{transition:none}
}

/* ---------------------------------------------------------------
   HERO BAND (.cr-hero-band)  ·  see DESIGN.md › Hero band
   A 200px band of the footer's colour across the foot of a hero, behind whatever illustration stands
   there, so the drawing sits on ground rather than floating on a flat field. Used on Features and About.

     <section class="cr-…-hero">            the section, position:relative
       <span class="cr-hero-band" aria-hidden="true"><i></i></span>   first child, before the content
       …

   Same ramp as the footer wordmark (violet-100 · violet-200 · mint-100 · mint-50 · violet-100) on the same
   --dur-ambient loop, but carried on a transform rather than background-position: the band is viewport
   wide, and animating the paint of one costs a repaint a frame. The inner strip is twice as wide and the
   gradient ends on the colour it starts on, so translating it by half its width loops seamlessly.

   The mask holds full strength across the margins. An illustration is usually opaque and covers the middle
   of the viewport, so the only place the band shows is the strip either side of it — fading the horizontal
   mask early kills it exactly where it is visible. Vertically it fades in from the top and runs full into
   the section's own border, the same call as the homepage hero glow.

   No z-index: a negative index escapes past the section's own background, because a section with
   overflow:hidden is not a stacking context, and the band disappears. It is first in the DOM and the
   content after it is positioned, so order alone puts it behind. --------------------------------------- */
.cr-hero-band{position:absolute;left:0;right:0;bottom:0;height:200px;pointer-events:none;overflow:hidden;
  -webkit-mask-image:linear-gradient(to right,transparent 0%,rgb(0 0 0 / .7) 5%,#000 12%,#000 88%,rgb(0 0 0 / .7) 95%,transparent 100%),linear-gradient(to bottom,transparent 0%,rgb(0 0 0 / .3) 22%,rgb(0 0 0 / .72) 48%,#000 72%,#000 100%);
  -webkit-mask-composite:source-in;
  mask-image:linear-gradient(to right,transparent 0%,rgb(0 0 0 / .7) 5%,#000 12%,#000 88%,rgb(0 0 0 / .7) 95%,transparent 100%),linear-gradient(to bottom,transparent 0%,rgb(0 0 0 / .3) 22%,rgb(0 0 0 / .72) 48%,#000 72%,#000 100%);
  mask-composite:intersect}
.cr-hero-band i{position:absolute;top:0;bottom:0;left:0;width:200%;
  background:linear-gradient(90deg in oklab,var(--violet-100) 0%,var(--violet-200) 12.5%,var(--mint-100) 25%,var(--mint-50) 34%,var(--violet-100) 50%,var(--violet-200) 62.5%,var(--mint-100) 75%,var(--mint-50) 84%,var(--violet-100) 100%);
  animation:cr-hero-band-drift var(--dur-ambient) linear infinite;will-change:transform}
@keyframes cr-hero-band-drift{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@media(prefers-reduced-motion:reduce){.cr-hero-band i{animation:none}}
