code wiki / (root) / nx_sclass_kit.nx

nx_sclass_kit.nx source

↩ module page · 396 lines · 41705 B

1// nx_sclass_kit.nx -- reusable S-CLASS page component kit (the "gorgeous by construction" library). 2// Pure emit functions (NO syscalls -> any emitter composes it without a syscall-layer clash). Every 3// page built through this kit passes nx_brand_apply_enforced BY CONSTRUCTION: it ships the canonical 4// --nx- design-token SSOT, a base reset, responsive grid, reduced-motion, :focus-visible, a skip-link, 5// and semantic <header>/<main id=main>/<footer> landmarks. This closes the "fragmented adoption" gap at 6// the ROOT: emitters stop hand-rolling crude CSS and compose ONE tokenized shell + components. 7// Factored out of the proven nx_connect_lds_page (uxcx 7/7, design 9/9, canonical). license_tier: ORIGINAL 8// (no main -- a library, like nx_uxcx_grade) 9 10// append NUL-terminated s to buf at off; return new off. 11func he_puts(buf: *u8, off: i64, s: *u8) -> i64 { 12 var o: i64 = off 13 var i: i64 = 0 14 while s[i] != 0 { buf[o] = s[i]; o = o + 1; i = i + 1 } 15 return o 16} 17 18// doc HEAD: <head> + the full tokenized S-class stylesheet + <body> + skip-link. Factored out of sk_open so the 19// single-page shell (sk_open) and the whole-SITE shell (sk_open_site) share ONE stylesheet SSOT (DRY, no drift). 20func sk_head(buf: *u8, off: i64, title: *u8, theme: i64) -> i64 { 21 var o: i64 = off 22 o = he_puts(buf, o, "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"color-scheme\" content=\"light\"><title>" as *u8) 23 o = he_puts(buf, o, title) 24 o = he_puts(buf, o, "</title><style>" as *u8) 25 o = he_puts(buf, o, ":root{" as *u8) 26 if theme==1 { o = he_puts(buf, o, "--nx-color-bg:#FCFCFD;--nx-color-surface:#ffffff;--nx-color-surface2:#F3F4F6;--nx-color-ink:#141821;--nx-color-ink2:#6B7280;--nx-color-primary:#4338CA;--nx-color-primary2:#5B52D6;--nx-color-accent:#0EA5A0;--nx-color-accent-ink:#04302C;--nx-color-line:#E5E7EB;--glow1:#EAF0FF;--glow2:#F3F4F6;" as *u8) } 27 else { o = he_puts(buf, o, "--nx-color-bg:#FBF7F0;--nx-color-surface:#ffffff;--nx-color-surface2:#F5EFE4;--nx-color-ink:#23283A;--nx-color-ink2:#5B6172;--nx-color-primary:#2C3A66;--nx-color-primary2:#3B4C85;--nx-color-accent:#D99A4E;--nx-color-accent-ink:#3A2A0E;--nx-color-line:#E7DFD1;--glow1:#FFF3E0;--glow2:#EAF0FF;" as *u8) } 28 o = he_puts(buf, o, "--bg:var(--nx-color-bg);--surface:var(--nx-color-surface);--surface2:var(--nx-color-surface2);--ink:var(--nx-color-ink);--ink2:var(--nx-color-ink2);--brand:var(--nx-color-primary);--brand2:var(--nx-color-primary2);--accent:var(--nx-color-accent);--accent-ink:var(--nx-color-accent-ink);--line:var(--nx-color-line);--r:16px;--sh-sm:0 1px 2px rgba(35,40,58,.06),0 1px 3px rgba(35,40,58,.08);--sh:0 16px 40px -18px rgba(44,58,102,.35);--ease:cubic-bezier(.2,.7,.2,1);--fd:ui-serif,Georgia,\"Iowan Old Style\",\"Palatino Linotype\",serif;--fs:ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif}" as *u8) 29 o = he_puts(buf, o, "*{box-sizing:border-box}html{-webkit-text-size-adjust:100%}body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--fs);font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased}h1,h2,h3{font-family:var(--fd);line-height:1.12;letter-spacing:-.01em;margin:0}p{margin:0}a{color:inherit}svg{display:block}.wrap{max-width:1080px;margin:0 auto;padding:0 24px}.skip-link{position:absolute;left:-999px}.skip-link:focus{left:16px;top:16px;background:var(--brand);color:#fff;padding:10px 16px;border-radius:8px;z-index:10}:focus-visible{outline:3px solid var(--brand2);outline-offset:2px;border-radius:6px}" as *u8) 30 o = he_puts(buf, o, "header.site{position:sticky;top:0;background:rgba(251,247,240,.82);backdrop-filter:blur(10px);border-bottom:1px solid var(--line);z-index:5}.bar{display:flex;align-items:center;justify-content:space-between;height:66px}.brand{display:flex;align-items:center;gap:10px;font-family:var(--fd);font-weight:700;font-size:1.2rem;text-decoration:none;color:var(--ink)}.dot{width:26px;height:26px;border-radius:8px;background:linear-gradient(135deg,var(--brand),var(--accent));box-shadow:var(--sh-sm);flex:none}nav.top{display:flex;gap:26px;align-items:center}nav.top a{color:var(--ink2);text-decoration:none;font-size:.95rem;font-weight:500}nav.top a:hover{color:var(--ink)}@media(max-width:640px){nav.top{display:none}}" as *u8) 31 o = he_puts(buf, o, ".btn{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;font:inherit;font-weight:600;font-size:1rem;padding:13px 22px;border-radius:999px;text-decoration:none;transition:transform .18s var(--ease),box-shadow .18s var(--ease),background .18s}.btn:hover{transform:translateY(-2px)}.btn-primary{background:var(--brand);color:#fff;box-shadow:var(--sh)}.btn-primary:hover{background:var(--brand2)}.btn-accent{background:var(--accent);color:var(--accent-ink);box-shadow:0 12px 26px -12px rgba(217,154,78,.7)}.btn-ghost{background:transparent;color:var(--ink);border:1.5px solid var(--line)}.btn-ghost:hover{border-color:var(--brand2);background:#fff}" as *u8) 32 o = he_puts(buf, o, ".hero{position:relative;overflow:hidden;padding:92px 0 76px;text-align:center;background:radial-gradient(1100px 460px at 50% -8%,var(--glow1) 0%,rgba(255,243,224,0) 62%),radial-gradient(720px 360px at 82% 6%,var(--glow2) 0%,rgba(234,240,255,0) 60%)}.eyebrow{display:inline-flex;align-items:center;gap:8px;background:#fff;border:1px solid var(--line);color:var(--brand);font-weight:600;font-size:.82rem;letter-spacing:.02em;padding:7px 14px;border-radius:999px;box-shadow:var(--sh-sm);margin-bottom:22px}.hero h1{font-size:clamp(2.4rem,5.2vw,3.7rem);font-weight:700;max-width:17ch;margin:0 auto}.grad{background:linear-gradient(120deg,var(--brand),var(--accent));-webkit-background-clip:text;background-clip:text;color:transparent}.lede{max-width:57ch;margin:22px auto 0;color:var(--ink2);font-size:1.18rem}.cta-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:32px}.hint{margin-top:16px;color:var(--ink2);font-size:.9rem}" as *u8) 33 o = he_puts(buf, o, ".sec{padding:74px 0}.sec-head{text-align:center;max-width:60ch;margin:0 auto 44px}.sec-head h2{font-size:clamp(1.9rem,3.6vw,2.5rem)}.sec-head p{color:var(--ink2);margin-top:14px;font-size:1.1rem}.tag{display:block;text-transform:uppercase;letter-spacing:.14em;font-size:.76rem;font-weight:700;color:var(--accent);font-family:var(--fs);margin-bottom:12px}.grid3{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:22px}.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:30px;box-shadow:var(--sh-sm);transition:transform .2s var(--ease),box-shadow .2s var(--ease)}.card:hover{transform:translateY(-4px);box-shadow:var(--sh)}.ic{width:52px;height:52px;border-radius:14px;display:grid;place-items:center;background:linear-gradient(135deg,#EAF0FF,#F6EEDF);color:var(--brand);margin-bottom:18px}.card h3{font-size:1.34rem;margin-bottom:10px}.card p{color:var(--ink2)}" as *u8) 34 o = he_puts(buf, o, ".final{text-align:center;padding:86px 0}.final h2{font-size:clamp(2rem,4vw,2.8rem)}.final p{color:var(--ink2);max-width:52ch;margin:16px auto 30px;font-size:1.12rem}footer.site{border-top:1px solid var(--line);background:#fff}footer.site .wrap{padding:40px 24px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;align-items:center}.muted{color:var(--ink2);font-size:.9rem}.scope{color:var(--ink2);font-size:.86rem;max-width:52ch}@media(prefers-reduced-motion:reduce){*{transition:none!important}.btn:hover,.card:hover{transform:none}}" as *u8) 35 o = he_puts(buf, o, ".trust{background:linear-gradient(160deg,var(--brand),var(--brand2));color:#fff}.trust .wrap{padding:64px 24px}.trust h2{color:#fff;text-align:center;font-size:clamp(1.8rem,3.4vw,2.4rem)}.trust .sub{text-align:center;color:rgba(255,255,255,.82);max-width:56ch;margin:14px auto 40px}.tl{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:18px}.tl li{display:flex;gap:13px;align-items:flex-start;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);border-radius:14px;padding:18px}.tl .ck{flex:none;width:26px;height:26px;border-radius:50%;background:var(--accent);color:var(--accent-ink);display:grid;place-items:center}.tl b{display:block;margin-bottom:3px}.tl span{color:rgba(255,255,255,.82);font-size:.92rem}" as *u8) 36 o = he_puts(buf, o, "@keyframes rise{to{opacity:1;transform:none}}.hero .eyebrow{opacity:0;animation:rise .8s var(--ease) .05s forwards}.hero h1{opacity:0;animation:rise .9s var(--ease) .16s forwards}.hero .lede{opacity:0;animation:rise .9s var(--ease) .3s forwards}.hero .cta-row{opacity:0;animation:rise .9s var(--ease) .42s forwards}@supports(animation-timeline:view()){.grid3 .card{opacity:0;transform:translateY(30px);animation:rise linear both;animation-timeline:view();animation-range:entry 5% cover 22%}.grid3 .card:nth-child(2){animation-range:entry 9% cover 26%}.grid3 .card:nth-child(3){animation-range:entry 13% cover 30%}}@media(prefers-reduced-motion:reduce){*{animation:none!important}.hero .eyebrow,.hero h1,.hero .lede,.hero .cta-row,.grid3 .card{opacity:1;transform:none}}" as *u8) 37 o = he_puts(buf, o, "</style></head><body><a class=\"skip-link\" href=\"#main\">Skip to content</a>" as *u8) 38 return o 39} 40 41// header open: <header> + brand (with a caller-supplied href, so the brand links home across pages) + an OPEN 42// <nav class=top>. Caller emits sk_nav_item()* then sk_header_close(). Cross-page nav = the whole-site gap this closes. 43func sk_header_open(buf: *u8, off: i64, brand: *u8, brandhref: *u8) -> i64 { 44 var o: i64 = off 45 o = he_puts(buf, o, "<header class=\"site\"><div class=\"wrap bar\"><a class=\"brand\" href=\"" as *u8) 46 o = he_puts(buf, o, brandhref) 47 o = he_puts(buf, o, "\"><span class=\"dot\"></span>" as *u8) 48 o = he_puts(buf, o, brand) 49 o = he_puts(buf, o, "</a><nav class=\"top\">" as *u8) 50 return o 51} 52// one cross-page nav link. 53func sk_nav_item(buf: *u8, off: i64, label: *u8, href: *u8) -> i64 { 54 var o: i64 = off 55 o = he_puts(buf, o, "<a href=\"" as *u8) 56 o = he_puts(buf, o, href) 57 o = he_puts(buf, o, "\">" as *u8) 58 o = he_puts(buf, o, label) 59 o = he_puts(buf, o, "</a>" as *u8) 60 return o 61} 62// close nav + header, open <main id=main>. 63func sk_header_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</nav></div></header><main id=\"main\">" as *u8) } 64 65// BACKWARD-COMPATIBLE single-page shell: hardcoded #main/#join in-page anchors. Composed from sk_head + 66// sk_header_open + sk_nav_item*2 + sk_header_close so its emitted bytes are IDENTICAL to the pre-refactor sk_open 67// (every existing page -- /finance, /connect, ... -- emits the same bytes; proven by rebuild+diff). 68func sk_open(buf: *u8, off: i64, title: *u8, brand: *u8, theme: i64) -> i64 { 69 var o: i64 = sk_head(buf, off, title, theme) 70 o = sk_header_open(buf, o, brand, "#top" as *u8) 71 o = sk_nav_item(buf, o, "Home" as *u8, "#main" as *u8) 72 o = sk_nav_item(buf, o, "Join" as *u8, "#join" as *u8) 73 o = sk_header_close(buf, o) 74 return o 75} 76// WHOLE-SITE shell: same tokenized stylesheet, but a CROSS-PAGE nav. Usage: 77// o = sk_open_site(buf,o,title,brand,theme,"/"); o = sk_nav_item(buf,o,label,href)* ; o = sk_header_close(buf,o); ... 78func sk_open_site(buf: *u8, off: i64, title: *u8, brand: *u8, theme: i64, brandhref: *u8) -> i64 { 79 var o: i64 = sk_head(buf, off, title, theme) 80 o = sk_header_open(buf, o, brand, brandhref) 81 return o 82} 83 84// hero band. title renders as: <title_pre> <span.grad>title_grad</span> 85func sk_hero(buf: *u8, off: i64, eyebrow: *u8, title_pre: *u8, title_grad: *u8, lede: *u8, cta1: *u8, cta1href: *u8, cta2: *u8, cta2href: *u8, hint: *u8) -> i64 { 86 var o: i64 = off 87 o = he_puts(buf, o, "<section class=\"hero\" id=\"top\"><div class=\"wrap\"><span class=\"eyebrow\">" as *u8) 88 o = he_puts(buf, o, eyebrow) 89 o = he_puts(buf, o, "</span><h1>" as *u8); o = he_puts(buf, o, title_pre) 90 o = he_puts(buf, o, " <span class=\"grad\">" as *u8); o = he_puts(buf, o, title_grad); o = he_puts(buf, o, "</span></h1><p class=\"lede\">" as *u8) 91 o = he_puts(buf, o, lede) 92 o = he_puts(buf, o, "</p><div class=\"cta-row\"><a class=\"btn btn-primary\" href=\"" as *u8); o = he_puts(buf, o, cta1href); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, cta1) 93 o = he_puts(buf, o, "</a><a class=\"btn btn-ghost\" href=\"" as *u8); o = he_puts(buf, o, cta2href); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, cta2); o = he_puts(buf, o, "</a></div><p class=\"hint\">" as *u8) 94 o = he_puts(buf, o, hint) 95 o = he_puts(buf, o, "</p></div></section>" as *u8) 96 return o 97} 98 99// open a section with a centered header + a 3-up card grid. 100func sk_section_open(buf: *u8, off: i64, id: *u8, tag: *u8, h2: *u8, sub: *u8) -> i64 { 101 var o: i64 = off 102 o = he_puts(buf, o, "<section class=\"sec\" id=\"" as *u8); o = he_puts(buf, o, id) 103 o = he_puts(buf, o, "\"><div class=\"wrap\"><div class=\"sec-head\"><span class=\"tag\">" as *u8); o = he_puts(buf, o, tag) 104 o = he_puts(buf, o, "</span><h2>" as *u8); o = he_puts(buf, o, h2); o = he_puts(buf, o, "</h2><p>" as *u8); o = he_puts(buf, o, sub) 105 o = he_puts(buf, o, "</p></div><div class=\"grid3\">" as *u8) 106 return o 107} 108func sk_card(buf: *u8, off: i64, icon_svg: *u8, title: *u8, body: *u8) -> i64 { 109 var o: i64 = off 110 o = he_puts(buf, o, "<article class=\"card\"><div class=\"ic\">" as *u8); o = he_puts(buf, o, icon_svg) 111 o = he_puts(buf, o, "</div><h3>" as *u8); o = he_puts(buf, o, title); o = he_puts(buf, o, "</h3><p>" as *u8); o = he_puts(buf, o, body) 112 o = he_puts(buf, o, "</p></article>" as *u8) 113 return o 114} 115// LINKABLE card -- the whole card is an <a> to href (cross-page internal link + a "Learn more" affordance). This 116// closes the "wb_card/sk_card have no <a href>" gap so emitted pages actually link to each other. 117func sk_card_link(buf: *u8, off: i64, icon_svg: *u8, title: *u8, body: *u8, href: *u8) -> i64 { 118 var o: i64 = off 119 o = he_puts(buf, o, "<a class=\"card\" href=\"" as *u8); o = he_puts(buf, o, href) 120 o = he_puts(buf, o, "\" style=\"display:block;text-decoration:none\"><div class=\"ic\">" as *u8); o = he_puts(buf, o, icon_svg) 121 o = he_puts(buf, o, "</div><h3>" as *u8); o = he_puts(buf, o, title); o = he_puts(buf, o, "</h3><p>" as *u8); o = he_puts(buf, o, body) 122 o = he_puts(buf, o, "</p><span style=\"display:inline-block;margin-top:14px;color:var(--brand);font-weight:600\">Learn more &rarr;</span></a>" as *u8) 123 return o 124} 125func sk_section_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></div></section>" as *u8) } 126 127// ONSITE SEARCH band (satisfies the conversion-floor onsite-search rule; role=search + GET form). Uses existing 128// .sec/.wrap/.btn classes + minimal inline for the input, so no stylesheet change (sk_open stays byte-identical). 129func sk_search(buf: *u8, off: i64, action: *u8, placeholder: *u8) -> i64 { 130 var o: i64 = off 131 o = he_puts(buf, o, "<section class=\"sec\" id=\"search\" style=\"padding:34px 0 0\"><div class=\"wrap\"><form role=\"search\" method=\"get\" action=\"" as *u8) 132 o = he_puts(buf, o, action) 133 o = he_puts(buf, o, "\" style=\"display:flex;gap:10px;max-width:560px;margin:0 auto;flex-wrap:wrap\"><input type=\"search\" name=\"q\" aria-label=\"Search this site\" placeholder=\"" as *u8) 134 o = he_puts(buf, o, placeholder) 135 o = he_puts(buf, o, "\" style=\"flex:1;min-width:220px;padding:13px 18px;border:1.5px solid var(--line);border-radius:999px;font:inherit;background:var(--surface)\"><button class=\"btn btn-primary\" type=\"submit\">Search</button></form></div></section>" as *u8) 136 return o 137} 138 139// final call-to-action band. 140func sk_cta(buf: *u8, off: i64, tag: *u8, h2: *u8, p: *u8, b1: *u8, b1href: *u8, b2: *u8, b2href: *u8) -> i64 { 141 var o: i64 = off 142 o = he_puts(buf, o, "<section class=\"final\" id=\"join\"><div class=\"wrap\"><span class=\"tag\">" as *u8); o = he_puts(buf, o, tag) 143 o = he_puts(buf, o, "</span><h2>" as *u8); o = he_puts(buf, o, h2); o = he_puts(buf, o, "</h2><p>" as *u8); o = he_puts(buf, o, p) 144 o = he_puts(buf, o, "</p><div class=\"cta-row\"><a class=\"btn btn-primary\" href=\"" as *u8); o = he_puts(buf, o, b1href); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, b1) 145 o = he_puts(buf, o, "</a><a class=\"btn btn-ghost\" href=\"" as *u8); o = he_puts(buf, o, b2href); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, b2) 146 o = he_puts(buf, o, "</a></div></div></section>" as *u8) 147 return o 148} 149// close: </main> + footer(scope + brand) + close. 150func sk_close(buf: *u8, off: i64, scope: *u8, brand: *u8) -> i64 { 151 var o: i64 = off 152 o = he_puts(buf, o, "</main><footer class=\"site\"><div class=\"wrap\"><span class=\"brand\" style=\"font-size:1rem\"><span class=\"dot\"></span>" as *u8) 153 o = he_puts(buf, o, brand) 154 o = he_puts(buf, o, "</span><span class=\"scope\">" as *u8); o = he_puts(buf, o, scope) 155 o = he_puts(buf, o, "</span><span class=\"muted\">&copy; 2026 &middot; Built sovereign on Nishi</span></div></footer></body></html>" as *u8) 156 return o 157} 158 159// trust band: a brand-colour section + a check-item grid ("safe by construction" messaging). Reusable. 160func sk_trust_open(buf: *u8, off: i64, tag: *u8, h2: *u8, sub: *u8) -> i64 { 161 var o: i64 = off 162 o = he_puts(buf, o, "<section class=\"trust\" id=\"trust\"><div class=\"wrap\"><span class=\"tag\">" as *u8); o = he_puts(buf, o, tag) 163 o = he_puts(buf, o, "</span><h2>" as *u8); o = he_puts(buf, o, h2); o = he_puts(buf, o, "</h2><p class=\"sub\">" as *u8); o = he_puts(buf, o, sub) 164 o = he_puts(buf, o, "</p><ul class=\"tl\">" as *u8) 165 return o 166} 167func sk_trust_item(buf: *u8, off: i64, title: *u8, body: *u8) -> i64 { 168 var o: i64 = off 169 o = he_puts(buf, o, "<li><span class=\"ck\"><svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12l4 4 10-10\"/></svg></span><div><b>" as *u8); o = he_puts(buf, o, title) 170 o = he_puts(buf, o, "</b><span>" as *u8); o = he_puts(buf, o, body); o = he_puts(buf, o, "</span></div></li>" as *u8) 171 return o 172} 173func sk_trust_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</ul></div></section>" as *u8) } 174 175// ============================================================================================================ 176// CONTENT-TIER (data / reference / dashboard / doc pages -- compare matrices, census scorecards, docs). The 177// hero components above are MARKETING; these are the missing CONTENT tier: a scanned-not-read scorecard + table 178// layout, NO aurora. Emitters COMPOSE these instead of hand-rolling crude CSS (the root cause of the ~223-permil 179// compare fleet). sk_doc_open reuses sk_head's --nx-color token SSOT (so no token drift) and injects a scoped 180// content stylesheet in <body> (sk_head/sk_open stay BYTE-IDENTICAL -- existing hero pages untouched) that ALSO 181// adds prefers-color-scheme dark (the hero themes are light-only). license_tier: ORIGINAL 182// ============================================================================================================ 183 184// the injectable content stylesheet (uses the --nx-color-* tokens from sk_head; adds a dark override + the 185// content-tier classes: .doc, .lede, .scorecard/.score/.stat, .tablewrap/table, .chip-*, .callout, .docfoot). 186func sk_doc_styles(buf: *u8, off: i64) -> i64 { 187 var o: i64 = off 188 o = he_puts(buf, o, "<style>@media(prefers-color-scheme:dark){:root{--nx-color-bg:#0D1015;--nx-color-surface:#161A21;--nx-color-surface2:#1D222B;--nx-color-ink:#E7EAF0;--nx-color-ink2:#97A1B0;--nx-color-line:#252B35}}" as *u8) 189 o = he_puts(buf, o, ".doc{max-width:1000px;margin:0 auto;padding:clamp(10px,3vw,30px) 24px 72px;font-family:var(--fs)}.doc h1{font-family:var(--fs);font-size:clamp(1.9rem,5vw,2.7rem);font-weight:800;letter-spacing:-.025em;text-wrap:balance;margin:.5em 0 .15em}.doc h2{font-family:var(--fs);font-size:clamp(1.15rem,3vw,1.45rem);font-weight:720;letter-spacing:-.01em;margin:2.4em 0 .55em}.doc .lede{color:var(--nx-color-ink2);max-width:64ch;font-size:1.02rem}" as *u8) 190 o = he_puts(buf, o, ".scorecard{display:flex;align-items:baseline;gap:20px;flex-wrap:wrap;margin:20px 0 6px}.score{font-size:clamp(3.2rem,13vw,5.4rem);font-weight:850;line-height:.9;letter-spacing:-.03em;background:linear-gradient(102deg,var(--nx-color-primary),var(--nx-color-primary2));-webkit-background-clip:text;background-clip:text;color:transparent;font-variant-numeric:tabular-nums}.score small{display:block;font-size:.9rem;font-weight:600;letter-spacing:.04em;color:var(--nx-color-ink2);-webkit-text-fill-color:var(--nx-color-ink2)}" as *u8) 191 o = he_puts(buf, o, ".stats{display:flex;flex-wrap:wrap;gap:12px}.stat{background:var(--nx-color-surface);border:1px solid var(--nx-color-line);border-radius:14px;padding:13px 18px;box-shadow:var(--sh-sm);min-width:90px;animation:rise .5s both;transition:transform .18s var(--ease)}.stat:hover{transform:translateY(-2px)}.stat:nth-child(2){animation-delay:.05s}.stat:nth-child(3){animation-delay:.1s}.stat:nth-child(4){animation-delay:.15s}.stat b{display:block;font-size:1.7rem;font-weight:800;font-variant-numeric:tabular-nums;line-height:1.1}.stat span{color:var(--nx-color-ink2);font-size:.74rem;text-transform:uppercase;letter-spacing:.07em}" as *u8) 192 o = he_puts(buf, o, ".tablewrap{overflow-x:auto;border:1px solid var(--nx-color-line);border-radius:14px;box-shadow:var(--sh-sm);margin:12px 0 8px;background:var(--nx-color-surface)}.dtable{border-collapse:collapse;width:100%;min-width:520px}.dtable th,.dtable td{text-align:left;padding:11px 16px;border-bottom:1px solid var(--nx-color-line);font-size:.92rem;vertical-align:top}.dtable th{background:var(--nx-color-surface2);font-weight:640;font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--nx-color-ink2)}.dtable tbody tr:last-child td{border-bottom:0}.dtable tbody tr:hover td{background:var(--nx-color-surface2)}.dtable code{font-family:ui-monospace,\"Cascadia Code\",\"SF Mono\",monospace;font-size:.85em;background:var(--nx-color-surface2);padding:2px 6px;border-radius:6px}" as *u8) 193 o = he_puts(buf, o, ".chip{display:inline-block;padding:3px 11px;border-radius:999px;font-size:.7rem;font-weight:700;letter-spacing:.04em;color:#fff;white-space:nowrap}.chip-have{background:#2c9462}.chip-exceed{background:#b88a30}.chip-partial{background:#4074ba}.chip-gap{background:#768090}.callout{background:var(--nx-color-surface);border-left:3px solid var(--nx-color-primary);border-radius:0 14px 14px 0;padding:14px 18px;margin:18px 0;box-shadow:var(--sh-sm);max-width:70ch}.docfoot{color:var(--nx-color-ink2);font-size:.85rem;border-top:1px solid var(--nx-color-line);margin-top:2.6em;padding-top:1.3em}@media(prefers-reduced-motion:reduce){.stat{animation:none;transition:none}}" as *u8) 194 o = he_puts(buf, o, ".nform{display:flex;flex-direction:column;gap:15px;max-width:33rem;margin:18px 0}.field{display:flex;flex-direction:column;gap:6px}.field label{font-size:.82rem;font-weight:640;letter-spacing:.02em}.field input,.field select,.field textarea{font:inherit;color:var(--nx-color-ink);background:var(--nx-color-surface);border:1px solid var(--nx-color-line);border-radius:10px;padding:11px 13px;transition:border-color .16s var(--ease),box-shadow .16s var(--ease)}.field textarea{min-height:96px;resize:vertical}.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{outline:none;border-color:var(--nx-color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--nx-color-primary) 24%,transparent)}.field .hint{font-size:.74rem;color:var(--nx-color-ink2)}.btnp{align-self:flex-start;font:inherit;font-weight:680;color:#fff;background:linear-gradient(102deg,var(--nx-color-primary),var(--nx-color-primary2));border:0;border-radius:11px;padding:12px 22px;cursor:pointer;box-shadow:var(--sh-sm);transition:transform .16s var(--ease)}.btnp:hover{transform:translateY(-1px)}" as *u8) 195 o = he_puts(buf, o, "</style>" as *u8) 196 return o 197} 198 199// CONTENT-TIER page shell: head (token SSOT) + content styles + cross-page header + <main id=main class=doc>. 200func sk_doc_open(buf: *u8, off: i64, title: *u8, brand: *u8, brandhref: *u8) -> i64 { 201 var o: i64 = sk_head(buf, off, title, 1) 202 o = sk_doc_styles(buf, o) 203 o = sk_header_open(buf, o, brand, brandhref) 204 return o 205} 206// after the header nav items, the caller closes the header (sk_header_close) then opens the doc container: 207func sk_doc_main(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "<div class=\"doc\">" as *u8) } 208 209// page title + lede paragraph. 210func sk_doc_h1(buf: *u8, off: i64, h1: *u8, lede: *u8) -> i64 { 211 var o: i64 = off 212 o = he_puts(buf, o, "<h1>" as *u8); o = he_puts(buf, o, h1); o = he_puts(buf, o, "</h1><p class=\"lede\">" as *u8) 213 o = he_puts(buf, o, lede); o = he_puts(buf, o, "</p>" as *u8) 214 return o 215} 216func sk_h2(buf: *u8, off: i64, text: *u8) -> i64 { var o: i64 = he_puts(buf, off, "<h2>" as *u8); o = he_puts(buf, o, text); return he_puts(buf, o, "</h2>" as *u8) } 217func sk_lede(buf: *u8, off: i64, text: *u8) -> i64 { var o: i64 = he_puts(buf, off, "<p class=\"lede\">" as *u8); o = he_puts(buf, o, text); return he_puts(buf, o, "</p>" as *u8) } 218func sk_callout(buf: *u8, off: i64, html: *u8) -> i64 { var o: i64 = he_puts(buf, off, "<div class=\"callout\">" as *u8); o = he_puts(buf, o, html); return he_puts(buf, o, "</div>" as *u8) } 219 220// scorecard: big gradient score + a row of stat tiles. sk_score_open(big,unit) -> sk_stat(num,label)* -> sk_score_close(). 221func sk_score_open(buf: *u8, off: i64, big: *u8, unit: *u8) -> i64 { 222 var o: i64 = he_puts(buf, off, "<div class=\"scorecard\"><div class=\"score\">" as *u8) 223 o = he_puts(buf, o, big); o = he_puts(buf, o, "<small>" as *u8); o = he_puts(buf, o, unit); o = he_puts(buf, o, "</small></div><div class=\"stats\">" as *u8) 224 return o 225} 226func sk_stat(buf: *u8, off: i64, num: *u8, label: *u8) -> i64 { 227 var o: i64 = he_puts(buf, off, "<div class=\"stat\"><b>" as *u8); o = he_puts(buf, o, num) 228 o = he_puts(buf, o, "</b><span>" as *u8); o = he_puts(buf, o, label); o = he_puts(buf, o, "</span></div>" as *u8) 229 return o 230} 231func sk_score_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></div>" as *u8) } 232 233// data table: sk_dtable_open(h1,h2,h3) -> sk_drow(axis, chipkind, chiplabel, note)* -> sk_dtable_close(). 234// chipkind = "have"|"exceed"|"partial"|"gap" (any other -> gap styling). This is the compare-fleet row shape. 235func sk_dtable_open(buf: *u8, off: i64, h1: *u8, h2: *u8, h3: *u8) -> i64 { 236 var o: i64 = he_puts(buf, off, "<div class=\"tablewrap\"><table class=\"dtable\"><thead><tr><th>" as *u8) 237 o = he_puts(buf, o, h1); o = he_puts(buf, o, "</th><th>" as *u8); o = he_puts(buf, o, h2) 238 o = he_puts(buf, o, "</th><th>" as *u8); o = he_puts(buf, o, h3); o = he_puts(buf, o, "</th></tr></thead><tbody>" as *u8) 239 return o 240} 241func sk_drow(buf: *u8, off: i64, axis: *u8, chipkind: *u8, chiplabel: *u8, note: *u8) -> i64 { 242 var o: i64 = he_puts(buf, off, "<tr><td>" as *u8); o = he_puts(buf, o, axis) 243 o = he_puts(buf, o, "</td><td><span class=\"chip chip-" as *u8); o = he_puts(buf, o, chipkind) 244 o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, chiplabel); o = he_puts(buf, o, "</span></td><td>" as *u8) 245 o = he_puts(buf, o, note); o = he_puts(buf, o, "</td></tr>" as *u8) 246 return o 247} 248// plain 3-column data row (no chip) -- e.g. an organ/gate/capability table. Cells are emitted as-is (the caller 249// may wrap a cell in <code>...</code>). 250func sk_trow(buf: *u8, off: i64, c1: *u8, c2: *u8, c3: *u8) -> i64 { 251 var o: i64 = he_puts(buf, off, "<tr><td>" as *u8); o = he_puts(buf, o, c1) 252 o = he_puts(buf, o, "</td><td>" as *u8); o = he_puts(buf, o, c2) 253 o = he_puts(buf, o, "</td><td>" as *u8); o = he_puts(buf, o, c3); o = he_puts(buf, o, "</td></tr>" as *u8) 254 return o 255} 256func sk_dtable_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</tbody></table></div>" as *u8) } 257 258// close: </div></main> + footer + close. (mirrors sk_close but for the .doc container.) 259func sk_doc_close(buf: *u8, off: i64, foot_html: *u8, brand: *u8) -> i64 { 260 var o: i64 = he_puts(buf, off, "<div class=\"docfoot\">" as *u8) 261 o = he_puts(buf, o, foot_html) 262 o = he_puts(buf, o, "</div></div></main><footer class=\"site\"><div class=\"wrap\"><span class=\"brand\" style=\"font-size:1rem\"><span class=\"dot\"></span>" as *u8) 263 o = he_puts(buf, o, brand) 264 o = he_puts(buf, o, "</span><span class=\"muted\">&copy; 2026 &middot; Built sovereign on Nishi</span></div></footer></body></html>" as *u8) 265 return o 266} 267 268// FORM components (accessible: label[for]/input[id] pairing, :focus-visible ring, gradient submit). Closes the 269// "no form component" gap so an intent like a settings/signup/contact form has real structure to map to. 270// sk_form_open(action) -> sk_field(id,label,type,placeholder)* / sk_field_area(id,label,placeholder)* -> sk_form_submit(text) -> sk_form_close(). 271func sk_form_open(buf: *u8, off: i64, action: *u8) -> i64 { 272 var o: i64 = he_puts(buf, off, "<form class=\"nform\" method=\"post\" action=\"" as *u8) 273 o = he_puts(buf, o, action); return he_puts(buf, o, "\">" as *u8) 274} 275func sk_field(buf: *u8, off: i64, id: *u8, label: *u8, kind: *u8, ph: *u8) -> i64 { 276 var o: i64 = he_puts(buf, off, "<div class=\"field\"><label for=\"" as *u8) 277 o = he_puts(buf, o, id); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, label) 278 o = he_puts(buf, o, "</label><input id=\"" as *u8); o = he_puts(buf, o, id) 279 o = he_puts(buf, o, "\" name=\"" as *u8); o = he_puts(buf, o, id) 280 o = he_puts(buf, o, "\" type=\"" as *u8); o = he_puts(buf, o, kind) 281 o = he_puts(buf, o, "\" placeholder=\"" as *u8); o = he_puts(buf, o, ph) 282 return he_puts(buf, o, "\"></div>" as *u8) 283} 284func sk_field_area(buf: *u8, off: i64, id: *u8, label: *u8, ph: *u8) -> i64 { 285 var o: i64 = he_puts(buf, off, "<div class=\"field\"><label for=\"" as *u8) 286 o = he_puts(buf, o, id); o = he_puts(buf, o, "\">" as *u8); o = he_puts(buf, o, label) 287 o = he_puts(buf, o, "</label><textarea id=\"" as *u8); o = he_puts(buf, o, id) 288 o = he_puts(buf, o, "\" name=\"" as *u8); o = he_puts(buf, o, id) 289 o = he_puts(buf, o, "\" placeholder=\"" as *u8); o = he_puts(buf, o, ph) 290 return he_puts(buf, o, "\"></textarea></div>" as *u8) 291} 292func sk_form_submit(buf: *u8, off: i64, text: *u8) -> i64 { 293 var o: i64 = he_puts(buf, off, "<button class=\"btnp\" type=\"submit\">" as *u8) 294 o = he_puts(buf, o, text); return he_puts(buf, o, "</button>" as *u8) 295} 296func sk_form_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</form>" as *u8) } 297 298// ============================================================================================================ 299// MARKETING/CONTENT band components (template-breadth: pricing, FAQ, testimonial, feature-split, steps). 300// All colors flow through the --nx-color-* tokens so generated palettes (nx_uigen_l1/l3) retheme every band in 301// BOTH themes automatically. Injectable stylesheet (like sk_doc_styles) -- emit once after the header on any 302// page (hero or doc tier) that composes these bands. 303func sk_mkt_styles(buf: *u8, off: i64) -> i64 { 304 var o: i64 = off 305 o = he_puts(buf, o, "<style>.pricing-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:18px;text-align:left}.pricecard{background:var(--nx-color-surface);border:1px solid var(--nx-color-line);border-radius:16px;padding:26px 22px;box-shadow:var(--sh-sm);display:flex;flex-direction:column;gap:10px;position:relative}.pricecard.featured{border-color:var(--nx-color-primary);box-shadow:0 10px 34px -14px var(--nx-color-primary)}.pricecard .plan{font-size:.76rem;font-weight:720;text-transform:uppercase;letter-spacing:.07em;color:var(--nx-color-ink2)}.pricecard .amount{font-size:2.5rem;font-weight:840;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums}.pricecard .amount small{font-size:.85rem;font-weight:600;color:var(--nx-color-ink2)}.pricecard ul{list-style:none;padding:0;margin:6px 0 14px;display:flex;flex-direction:column;gap:7px;font-size:.9rem}.pricecard ul li{padding-left:1.15em;position:relative}.pricecard ul li::before{content:'';position:absolute;left:0;top:.42em;width:.5em;height:.5em;border-radius:50%;background:var(--nx-color-primary)}.pricecard .btn2{margin-top:auto;text-align:center;font-weight:680;color:#fff;background:linear-gradient(102deg,var(--nx-color-primary),var(--nx-color-primary2));border-radius:11px;padding:11px 16px;text-decoration:none}.bestchip{position:absolute;top:-11px;right:14px;background:var(--nx-color-accent);color:#fff;font-size:.64rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;padding:4px 10px;border-radius:999px}" as *u8) 306 o = he_puts(buf, o, ".faqs{max-width:46rem;margin:0 auto;text-align:left}.faqs details{border:1px solid var(--nx-color-line);border-radius:12px;background:var(--nx-color-surface);margin:0 0 10px;overflow:hidden}.faqs summary{cursor:pointer;padding:14px 18px;font-weight:650;list-style:none}.faqs summary::-webkit-details-marker{display:none}.faqs summary::after{content:'+';float:right;font-weight:800;color:var(--nx-color-primary)}.faqs details[open] summary::after{content:'\\2212'}.faqs details p{padding:0 18px 15px;margin:0;color:var(--nx-color-ink2)}" as *u8) 307 o = he_puts(buf, o, ".quoteband{max-width:46rem;margin:0 auto}.quoteband .qmark{color:var(--nx-color-primary);margin-bottom:6px}.quoteband blockquote{font-size:clamp(1.15rem,2.6vw,1.5rem);font-weight:620;letter-spacing:-.012em;margin:0;text-wrap:balance}.quoteband figcaption{margin-top:12px;color:var(--nx-color-ink2);font-size:.9rem}.quoteband figcaption b{color:var(--nx-color-ink)}" as *u8) 308 o = he_puts(buf, o, ".splitrow{display:grid;grid-template-columns:1fr 1fr;gap:36px;align-items:center;text-align:left}@media(max-width:760px){.splitrow{grid-template-columns:1fr}}.splitrow.flip .splitpanel{order:-1}.splitpanel{border-radius:18px;min-height:250px;background:linear-gradient(135deg,var(--nx-color-primary),var(--nx-color-accent));box-shadow:var(--sh-sm);display:flex;align-items:center;justify-content:center;color:#fff}.splitrow .pts{list-style:none;padding:0;margin:14px 0 0;display:flex;flex-direction:column;gap:9px}.splitrow .pts li{display:flex;gap:9px;align-items:flex-start}.splitrow .pts .ck2{flex:none;width:20px;height:20px;border-radius:50%;background:var(--nx-color-primary);color:#fff;display:inline-flex;align-items:center;justify-content:center;margin-top:2px}" as *u8) 309 o = he_puts(buf, o, ".steps{list-style:none;counter-reset:st;padding:0;margin:14px 0 0;display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px;text-align:left}.steps li{counter-increment:st;background:var(--nx-color-surface);border:1px solid var(--nx-color-line);border-radius:14px;padding:18px;box-shadow:var(--sh-sm)}.steps li::before{content:counter(st);display:inline-flex;width:28px;height:28px;border-radius:50%;align-items:center;justify-content:center;background:linear-gradient(102deg,var(--nx-color-primary),var(--nx-color-primary2));color:#fff;font-weight:800;font-size:.85rem;margin-bottom:10px}.steps li b{display:block;margin-bottom:4px}.steps li span{color:var(--nx-color-ink2);font-size:.9rem}</style>" as *u8) 310 return o 311} 312// centered band header (section + wrap + sec-head), with a marker class for structural checks/grammar validity. 313func sk_mband_open(buf: *u8, off: i64, klass: *u8, id: *u8, tag: *u8, h2: *u8, sub: *u8) -> i64 { 314 var o: i64 = he_puts(buf, off, "<section class=\"sec " as *u8) 315 o = he_puts(buf, o, klass); o = he_puts(buf, o, "\" id=\"" as *u8); o = he_puts(buf, o, id) 316 o = he_puts(buf, o, "\"><div class=\"wrap\"><div class=\"sec-head\"><span class=\"tag\">" as *u8); o = he_puts(buf, o, tag) 317 o = he_puts(buf, o, "</span><h2>" as *u8); o = he_puts(buf, o, h2); o = he_puts(buf, o, "</h2><p>" as *u8); o = he_puts(buf, o, sub) 318 o = he_puts(buf, o, "</p></div>" as *u8) 319 return o 320} 321// pricing: sk_pricing_open -> sk_price_tier* -> sk_pricing_close. feats_html = "<li>..</li>..." prebuilt. 322func sk_pricing_open(buf: *u8, off: i64, id: *u8, tag: *u8, h2: *u8, sub: *u8) -> i64 { 323 var o: i64 = sk_mband_open(buf, off, "priceband" as *u8, id, tag, h2, sub) 324 return he_puts(buf, o, "<div class=\"pricing-grid\">" as *u8) 325} 326func sk_price_tier(buf: *u8, off: i64, plan: *u8, amount: *u8, period: *u8, feats_html: *u8, cta: *u8, href: *u8, featured: i64) -> i64 { 327 var o: i64 = he_puts(buf, off, "<div class=\"pricecard" as *u8) 328 if featured == 1 { o = he_puts(buf, o, " featured" as *u8) } 329 o = he_puts(buf, o, "\">" as *u8) 330 if featured == 1 { o = he_puts(buf, o, "<span class=\"bestchip\">Most popular</span>" as *u8) } 331 o = he_puts(buf, o, "<span class=\"plan\">" as *u8); o = he_puts(buf, o, plan) 332 o = he_puts(buf, o, "</span><span class=\"amount\">" as *u8); o = he_puts(buf, o, amount) 333 o = he_puts(buf, o, "<small>" as *u8); o = he_puts(buf, o, period); o = he_puts(buf, o, "</small></span><ul>" as *u8) 334 o = he_puts(buf, o, feats_html) 335 o = he_puts(buf, o, "</ul><a class=\"btn2\" href=\"" as *u8); o = he_puts(buf, o, href); o = he_puts(buf, o, "\">" as *u8) 336 o = he_puts(buf, o, cta); o = he_puts(buf, o, "</a></div>" as *u8) 337 return o 338} 339func sk_pricing_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></div></section>" as *u8) } 340// testimonial quote band (figure/blockquote/figcaption semantics + a quote-mark svg). 341func sk_quote(buf: *u8, off: i64, id: *u8, tag: *u8, quote: *u8, name: *u8, role: *u8) -> i64 { 342 var o: i64 = sk_mband_open(buf, off, "quoteband" as *u8, id, tag, "What people say" as *u8, "" as *u8) 343 o = he_puts(buf, o, "<figure class=\"quoteband\"><div class=\"qmark\"><svg width=\"34\" height=\"26\" viewBox=\"0 0 34 26\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M0 26V14C0 6 4 1 12 0l2 5c-4 1-6 3-6 7h6v14H0zm20 0V14c0-8 4-13 12-14l2 5c-4 1-6 3-6 7h6v14H20z\"/></svg></div><blockquote>" as *u8) 344 o = he_puts(buf, o, quote) 345 o = he_puts(buf, o, "</blockquote><figcaption><b>" as *u8); o = he_puts(buf, o, name) 346 o = he_puts(buf, o, "</b> &middot; " as *u8); o = he_puts(buf, o, role) 347 o = he_puts(buf, o, "</figcaption></figure></div></section>" as *u8) 348 return o 349} 350// FAQ: native details/summary disclosure (interactive with ZERO JS -- on-brand for the sovereign moat). 351func sk_faq_open(buf: *u8, off: i64, id: *u8, tag: *u8, h2: *u8, sub: *u8) -> i64 { 352 var o: i64 = sk_mband_open(buf, off, "faqband" as *u8, id, tag, h2, sub) 353 return he_puts(buf, o, "<div class=\"faqs\">" as *u8) 354} 355func sk_faq_item(buf: *u8, off: i64, q: *u8, a: *u8) -> i64 { 356 var o: i64 = he_puts(buf, off, "<details><summary>" as *u8) 357 o = he_puts(buf, o, q); o = he_puts(buf, o, "</summary><p>" as *u8) 358 o = he_puts(buf, o, a); o = he_puts(buf, o, "</p></details>" as *u8) 359 return o 360} 361func sk_faq_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></div></section>" as *u8) } 362// numbered process steps (ol = real sequence semantics; the counter badge encodes actual order). 363func sk_steps_open(buf: *u8, off: i64, id: *u8, tag: *u8, h2: *u8, sub: *u8) -> i64 { 364 var o: i64 = sk_mband_open(buf, off, "stepsband" as *u8, id, tag, h2, sub) 365 return he_puts(buf, o, "<ol class=\"steps\">" as *u8) 366} 367func sk_step(buf: *u8, off: i64, title: *u8, body: *u8) -> i64 { 368 var o: i64 = he_puts(buf, off, "<li><b>" as *u8) 369 o = he_puts(buf, o, title); o = he_puts(buf, o, "</b><span>" as *u8) 370 o = he_puts(buf, o, body); o = he_puts(buf, o, "</span></li>" as *u8) 371 return o 372} 373func sk_steps_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</ol></div></section>" as *u8) } 374// feature split: text column (h2 + lede + 3 check points) beside a decorative gradient panel (pure CSS+svg, 375// no external imagery). flip=1 puts the panel first. 376func sk_split(buf: *u8, off: i64, id: *u8, tag: *u8, h2: *u8, lede: *u8, p1: *u8, p2: *u8, p3: *u8, flip: i64) -> i64 { 377 var o: i64 = he_puts(buf, off, "<section class=\"sec splitband\" id=\"" as *u8) 378 o = he_puts(buf, o, id) 379 o = he_puts(buf, o, "\"><div class=\"wrap\"><div class=\"splitrow" as *u8) 380 if flip == 1 { o = he_puts(buf, o, " flip" as *u8) } 381 o = he_puts(buf, o, "\"><div><span class=\"tag\">" as *u8); o = he_puts(buf, o, tag) 382 o = he_puts(buf, o, "</span><h2>" as *u8); o = he_puts(buf, o, h2) 383 o = he_puts(buf, o, "</h2><p class=\"lede\">" as *u8); o = he_puts(buf, o, lede) 384 o = he_puts(buf, o, "</p><ul class=\"pts\">" as *u8) 385 var k: i64 = 0 386 while k < 3 { 387 o = he_puts(buf, o, "<li><span class=\"ck2\"><svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12l4 4 10-10\"/></svg></span><span>" as *u8) 388 if k == 0 { o = he_puts(buf, o, p1) } 389 if k == 1 { o = he_puts(buf, o, p2) } 390 if k == 2 { o = he_puts(buf, o, p3) } 391 o = he_puts(buf, o, "</span></li>" as *u8) 392 k = k + 1 393 } 394 o = he_puts(buf, o, "</ul></div><div class=\"splitpanel\"><svg width=\"120\" height=\"120\" viewBox=\"0 0 120 120\" fill=\"none\" stroke=\"currentColor\" stroke-opacity=\".55\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"60\" cy=\"60\" r=\"50\"/><circle cx=\"60\" cy=\"60\" r=\"34\"/><circle cx=\"60\" cy=\"60\" r=\"18\"/></svg></div></div></div></section>" as *u8) 395 return o 396}