Files
workbuddy_skills/oil-ui-pro/assets/cards/gallery.html
T

2212 lines
1.8 MiB
HTML
Raw Normal View History

<!doctype html>
<html lang="zh-CN" data-theme="light">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="icon" href="data:,"><title>看得见的产品力 · oil cards</title><style>/* The host supplies the accent and font. Geometry is intentionally fixed. */
:where(.card, .showcase-card, .card-dialog) {
--_card-accent-2: var(--card-accent-2, oklch(from var(--card-accent) calc(l + .06) c calc(h + 50)));
--_card-accent-3: var(--card-accent-3, oklch(from var(--card-accent) calc(l + .10) calc(c * .8) calc(h + 100)));
--_card-accent-ink: oklch(from var(--card-accent) min(l,.5) c h);
--_card-accent-2-ink: oklch(from var(--_card-accent-2) min(l,.5) c h);
--_card-accent-3-ink: oklch(from var(--_card-accent-3) min(l,.5) c h);
/* A checkmark sits on the chosen color itself, independently of page theme. */
--_card-on-accent: oklch(from var(--card-accent) clamp(0, round(up, calc(.65 - l), 1), 1) 0 h);
--_card-radius: var(--card-radius, 8px);
color-scheme:light;
--card-surface: #fff;
--card-panel: #fff;
--card-ink: #233044;
--card-muted: #7b8798;
--card-line: #e5edf5;
--card-wash: #f5f7fa;
--card-shadow: 0 12px 32px -12px #23364c26, 0 2px 8px #23364c06;
--_card-edge-core: color-mix(in oklab, var(--card-accent) 56%, var(--card-surface));
color: var(--card-ink);
}
:where([data-theme="dark"]) :where(.card, .showcase-card, .card-dialog), :where(.card, .showcase-card, .card-dialog)[data-theme="dark"] {
--_card-accent-ink: oklch(from var(--card-accent) max(l,.8) c h);
--_card-accent-2-ink: oklch(from var(--_card-accent-2) max(l,.8) c h);
--_card-accent-3-ink: oklch(from var(--_card-accent-3) max(l,.8) c h);
color-scheme:dark;
--card-surface: #14171e;
--card-panel: #20242d;
--card-ink: #edf0f5;
--card-muted: #979fad;
--card-line: #ffffff12;
--card-wash: #191e27;
--card-shadow: 0 14px 40px -12px #0008, 0 0 0 1px #ffffff08;
--_card-edge-core: color-mix(in oklab, var(--card-accent) 48%, #fff);
}
/* A local light theme also works inside a dark host. */
:is(.card, .showcase-card, .card-dialog)[data-theme="light"] {
--_card-accent-ink: oklch(from var(--card-accent) min(l,.5) c h);
--_card-accent-2-ink: oklch(from var(--_card-accent-2) min(l,.5) c h);
--_card-accent-3-ink: oklch(from var(--_card-accent-3) min(l,.5) c h);
color-scheme:light;
--card-surface: #fff;
--card-panel: #fff;
--card-ink: #233044;
--card-muted: #7b8798;
--card-line: #e5edf5;
--card-wash: #f5f7fa;
--card-shadow: 0 12px 32px -12px #23364c26, 0 2px 8px #23364c06;
--_card-edge-core: color-mix(in oklab, var(--card-accent) 56%, var(--card-surface));
}
.card {
width:100%; min-width:0;
position: relative;
isolation: isolate;
overflow: hidden;
/* Clip without an internal scroll box when focus/scrollIntoView targets art. */
overflow: clip;
border: 1px solid var(--card-line);
border-radius: var(--_card-radius);
background: var(--card-surface);
aspect-ratio: 1.2;
&.portrait { aspect-ratio: .6; }
&.banner { aspect-ratio: 2.7; }
&.strip { aspect-ratio: 4.7; }
& .card-heading {
position: absolute; z-index: 4; top: 24px; left: 24px; right: 24px;
margin: 0; font-size: clamp(22px, 1.8vw, 26px); font-weight: 400;
line-height: 1.12; letter-spacing: -.045em;
}
& .card-stage { position: absolute; inset: 78px 0 0; overflow: hidden; overflow:clip; }
&.bottom-title {
& .card-stage { inset: 0 0 94px; }
& .card-caption { position: absolute; bottom: 23px; left: 24px; right: 24px; z-index: 4; }
& h2 { margin: 0 0 8px; font-size: 18px; font-weight: 550; letter-spacing: -.035em; }
& .card-caption p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--card-muted); }
}
& canvas { display: block; width: 100%; height: 100%; }
}
.card::after, .showcase-window::before {
content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 9;
padding: 1px; border-radius: inherit;
background: radial-gradient(150px circle at var(--pointer-x, -300px) var(--pointer-y, -300px) in oklab,
var(--_card-edge-core) 0%,
color-mix(in oklab, var(--card-accent) 45%, transparent) 24%,
color-mix(in oklab, var(--card-accent) 16%, transparent) 52%,
transparent 76%);
mask: linear-gradient(in oklab,#fff 0 0) content-box, linear-gradient(in oklab,#fff 0 0);
mask-composite: exclude; opacity: 0;
transition: opacity .45s cubic-bezier(.16,1,.3,1);
}
.card:hover::after, .showcase-card:hover .showcase-window::before { transition-duration:.2s; }
.card[data-effects~="glow"]::after, .showcase-card[data-effects~="glow"] .showcase-window::before { opacity:calc(var(--pointer-on,0) * .85); }
:is(.card,.showcase-card)[data-effects~="tilt"] { transform:perspective(1000px) rotateX(var(--_card-rotate-x,0deg)) rotateY(var(--_card-rotate-y,0deg)); }
.card-dialog {
border: 0; padding: 0; background: transparent; overflow: visible;
width: min(560px, calc((100vh - 48px) * .6), calc(100vw - 40px)); max-height: calc(100vh - 48px);
&.wide { width: min(1100px, calc(100vw - 40px)); }
&::backdrop { background: #10172682; backdrop-filter: blur(8px); }
& .card { width: 100%; max-height: calc(100vh - 48px); }
& :is(.card,.showcase-card)[data-effects~="tilt"] { transform:none; }
& .dialog-close { position: absolute; right: -12px; top: -12px; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--card-line); background: var(--card-panel); color: var(--card-ink); z-index: 20; cursor: pointer; font-size: 20px; }
}
@media (max-width: 650px) {
.card {
& .card-heading { font-size: 24px; }
&.banner { aspect-ratio: 1.2; }
&.strip { aspect-ratio: 1.2; }
}
}
@media (prefers-reduced-motion: reduce) {
.card *, .showcase-card * { transition-duration: 0s !important; animation: none !important; }
.card[data-effects]::after, .showcase-card[data-effects] .showcase-window::before { opacity:0; transition:none; }
:is(.card,.showcase-card)[data-effects~="tilt"] { transform:none; }
}
/* Text occupies a real row; the art keeps its own crop and minimum stage. */
.card:has(> .card-stage):not(.line-ribbons) {
--_card-stage-min:240px;
display:grid;
grid-template-columns:minmax(0,1fr);
grid-template-rows:auto minmax(var(--_card-stage-track-min,var(--_card-stage-min)),1fr);
row-gap:var(--_card-stage-gap,26px);
min-height:min-content;
& > .card-heading, & > .card-caption {
position:relative; inset:auto; min-width:0; order:0;
margin:24px 24px 0; flex:none;
}
& > .card-caption h2 { font-size:22px; line-height:1.15; font-weight:450; margin:0 0 8px; }
& > .card-stage {
position:relative; inset:auto; order:1; min-width:0; min-height:var(--_card-stage-min);
margin:0;
}
&[data-title="bottom"] {
grid-template-rows:minmax(var(--_card-stage-track-min,var(--_card-stage-min)),1fr) auto; row-gap:28px;
&:has(> .card-caption) { row-gap:25px; }
& > .card-stage { order:0; }
& > .card-heading { order:1; margin:0 24px 28px; }
& > .card-caption { order:1; margin:0 24px 23px; }
}
}
.card:is(.ui-slice,.terminal,.beam-graph,.keycaps) {
height:420px; aspect-ratio:auto; --_card-stage-gap:20px;
}
/* Runtime resolves the closest motion mode, speed=0 and reduced motion together. */
:is(.card,.showcase-card)[data-static-motion] *, .card[data-motion="off"] * { transition-duration:0s!important; animation:none!important; }
.card[data-motion="off"]::after, .showcase-card[data-motion="off"] .showcase-window::before { opacity:0; transition:none; }
:is(.card,.showcase-card)[data-motion="off"][data-effects~="tilt"] { transform:none; }
[data-text] { white-space:pre-line; overflow-wrap:anywhere; }
* { box-sizing: border-box; }
[hidden] { display:none !important; }
html { --page-bg:#fff; --page-ink:#283244; --page-muted:#8a929f; --page-line:#e8ecf2; background:var(--page-bg); color:var(--page-ink); color-scheme: light; }
html[data-theme="dark"] { --page-bg:#0e1117; --page-ink:#edf0f5; --page-muted:#7f8795; --page-line:#ffffff12; color-scheme:dark; }
/* A quiet host backdrop makes the glass sample's transmission visible. */
html[data-material="glass"] { background-image:radial-gradient(ellipse at 12% 25%,color-mix(in oklab,var(--gallery-accent) 18%,transparent),transparent 56%),radial-gradient(ellipse at 88% 70%,color-mix(in oklab,var(--gallery-accent) 12%,transparent),transparent 55%); background-attachment:fixed; }
body { margin:0; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB",sans-serif; -webkit-font-smoothing:antialiased; }
button,input,a { -webkit-tap-highlight-color:transparent; }
button { font-family:inherit; }
button:focus-visible,a:focus-visible,input:focus-visible { outline:2px solid var(--page-ink); outline-offset:4px; }
.gallery-page { max-width:1360px; padding:0 40px; margin:auto; }
.gallery-header { height:72px; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--page-line); }
.gallery-mark { text-decoration:none; color:inherit; font-size:25px; font-weight:650; letter-spacing:-.08em; & span { font-size:13px; letter-spacing:0; font-weight:400; margin-left:7px; color:var(--page-muted); } }
.gallery-edition { font-size:11px; color:var(--page-muted); letter-spacing:.02em; }
.gallery-intro { display:flex; align-items:center; justify-content:space-between; padding:33px 0 32px; gap:20px; & h1 { margin:0; font-size:26px; font-weight:450; letter-spacing:-.055em; } }
.gallery-controls { display:flex; flex-wrap:wrap; align-items:center; gap:12px; font-size:11px; }
.theme-options { display:flex; padding:2px; border:1px solid var(--page-line); border-radius:5px; & button { background:transparent; color:var(--page-muted); border:0; padding:4px 7px; font-size:10px; cursor:pointer; border-radius:3px; &.selected { background:var(--page-line); color:var(--page-ink); } } }
.radius-control { display:flex; align-items:center; gap:7px; color:var(--page-muted); white-space:nowrap; & input { width:54px; height:2px; accent-color:var(--page-ink); cursor:pointer; } & output { width:13px; font:10px ui-monospace,monospace; } }
figure { margin:0; min-width:0; }
.replay-motion { font:inherit; color:var(--page-muted); background:transparent; border:1px solid var(--page-line); border-radius:4px; padding:4px 7px; cursor:pointer; }
figcaption .replay-motion { border:0; padding:0; font-size:10px; text-decoration:underline; text-underline-offset:3px; }
figcaption { display:flex; justify-content:space-between; gap:8px; padding:12px 1px 0; color:var(--page-muted); font:10px/1.5 ui-monospace,monospace; & > span:last-child { font-family:inherit; font-size:9px; opacity:.75; } }
.signature-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,290px),1fr)); gap:18px; }
.ribbons-figure { margin-top:30px; }
.promo-figure { margin-top:30px; }
.exploration-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,290px),1fr)); gap:18px; margin-top:36px; }
.renderer-options { display:inline-flex; gap:3px; & button { font-family:inherit; font-size:11px; line-height:1.4; border:1px solid transparent; padding:3px 8px; border-radius:4px; background:transparent; color:var(--page-muted); cursor:pointer; } & button[aria-pressed="true"] { border-color:var(--page-line); color:var(--page-ink); } }
.utility-grid { margin-top:36px; }
.metric-figure { margin-top:30px; }
.utility-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; }
.showcase-intro { display:flex; flex-direction:column; justify-content:center; position:relative; padding:45px 0 5px; & > span { font:10px ui-monospace,monospace; color:var(--page-muted); letter-spacing:.07em; } & h2 { margin:18px 0 15px; font-weight:400; font-size:36px; line-height:1.25; letter-spacing:-.06em; } & p { font-size:13px; color:var(--page-muted); margin:0; } }
.carousel-navigation { position:absolute; bottom:23px; right:0; display:flex; gap:8px; & button { width:34px; height:34px; border:1px solid var(--page-line); background:transparent; border-radius:4px; color:var(--page-ink); font-size:17px; cursor:pointer; &:hover { background:var(--page-line); } } }
.showcase-section { margin-top:35px; }
.gallery-footer { border-top:1px solid var(--page-line); padding:25px 0 35px; margin-top:48px; display:flex; justify-content:space-between; font-size:10px; color:var(--page-muted); }
body.focus-only {
& .gallery-page { width:440px; padding:20px; }
& .gallery-header,& .gallery-intro,& .palette-editor,& .category-controls,& .category-summary,& .gallery-footer,& .showcase-intro { display:none; }
& main { display:block; }
& .signature-grid,& .utility-grid,& .closing-grid,& .exploration-grid { display:contents; }
& .exploration-grid.focused { display:grid; margin:0; & figure { display:block; } }
& figure { display:none; margin:0; }
& figure.focused { display:block; }
& .showcase-section { display:none; margin:0; }
& .showcase-section.focused { display:block; }
& .showcase-card { flex-basis:calc((100% - 48px) / 4); }
&.focus-wide .gallery-page { width:min(1280px,100vw); }
}
@media(max-width:1050px) { .gallery-intro { flex-direction:column; align-items:flex-start; gap:20px; } .gallery-controls { width:100%; } }
@media(max-width:650px) {
.gallery-page { padding:0 20px; }
.gallery-header { height:60px; }
.gallery-edition { font-size:9px; }
.gallery-intro { padding:24px 0; gap:18px; & h1 { font-size:25px; } }
.gallery-controls { flex-wrap:wrap; gap:10px; justify-content:space-between; }
.radius-control { gap:5px; & input { width:40px; } }
.signature-grid { grid-template-columns:1fr; gap:26px; }
.exploration-grid { grid-template-columns:1fr; gap:26px; }
.closing-grid { grid-template-columns:1fr; }
.showcase-intro { grid-column:auto; padding:30px 0 24px; & h2 { font-size:32px; } }
.gallery-footer { font-size:9px; }
body.focus-only .gallery-page { width:100%; }
body.focus-only .showcase-card { flex-basis:min(332px,86vw); }
}
.attribute-control { display:flex; align-items:center; gap:5px; color:var(--page-muted); white-space:nowrap; & select { border:1px solid var(--page-line); border-radius:4px; color:var(--page-ink); background:var(--page-bg); padding:4px; font:inherit; cursor:pointer; } }
@media(min-width:1051px) { .gallery-intro { flex-wrap:wrap; } .gallery-controls { gap:12px; } }
@media(max-width:650px) { .utility-grid { grid-template-columns:1fr; } .showcase-intro { padding:35px 0 25px; } }
.palette-editor { display:flex; flex-wrap:wrap; align-items:center; gap:14px 24px; padding:18px 0; border-top:1px solid var(--page-line); border-bottom:1px solid var(--page-line); }
.palette-editor .attribute-control { font-size:12px; }
.palette-editor .attribute-control select { padding:7px 8px; }
.palette-color { display:flex; align-items:center; gap:8px; min-width:0; }
.palette-color > label { font-size:12px; color:var(--page-muted); }
.palette-color input[type="color"] { appearance:none; width:30px; height:30px; flex:none; border:1px solid var(--page-line); padding:3px; border-radius:6px; background:var(--page-bg); cursor:pointer; }
.palette-color input[type="color"]::-webkit-color-swatch-wrapper { padding:0; }
.palette-color input[type="color"]::-webkit-color-swatch { border:0; border-radius:3px; }
.palette-color input[type="color"]::-moz-color-swatch { border:0; border-radius:3px; }
.palette-hex { width:90px; min-width:0; padding:7px 6px; border:1px solid var(--page-line); border-radius:5px; background:var(--page-bg); color:var(--page-ink); font:12px/1.2 ui-monospace,monospace; }
.palette-hex[aria-invalid="true"] { border-color:#b94e46; }
.palette-reset { margin-left:auto; font-family:inherit; font-size:12px; color:var(--page-muted); background:transparent; border:0; padding:8px 0; cursor:pointer; }
.palette-reset:hover { color:var(--page-ink); }
.palette-note { color:var(--page-muted); font-size:12px; }
.palette-feedback { flex-basis:100%; font-size:12px; color:var(--page-ink); }
.palette-feedback:empty { display:none; }
.category-controls { display:flex; flex-wrap:wrap; gap:8px 20px; padding-top:20px; margin-bottom:24px; }
.category-controls button { display:flex; align-items:baseline; gap:7px; font-family:inherit; font-size:13px; border:0; border-bottom:2px solid transparent; padding:8px 0; color:var(--page-muted); background:transparent; cursor:pointer; }
.category-controls button[aria-pressed="true"] { border-color:var(--page-ink); color:var(--page-ink); }
.category-controls button span { font:11px ui-monospace,monospace; color:var(--page-muted); }
.category-summary { margin:0 0 24px; max-width:70ch; font-size:13px; line-height:1.6; color:var(--page-muted); }
/* Filtered results share tracks; a lone portrait must not stretch into a banner. */
main[data-category]:not([data-category="all"]) {
display:grid; grid-template-columns:repeat(auto-fill,minmax(min(100%,290px),1fr)); gap:30px 18px; align-items:start;
& .signature-grid,& .exploration-grid,& .utility-grid { display:contents; }
& .ribbons-figure,& .promo-figure,& .metric-figure,& .showcase-intro,& .showcase-section { grid-column:1 / -1; margin:0; }
}
@media(max-width:650px) {
.palette-editor { gap:14px 20px; }
.palette-mode { margin-right:calc(100% - 120px); }
.palette-reset { margin-left:0; }
.category-controls { column-gap:18px; }
}
/* New studies keep the first three portrait/feature views together. */
.peer-intro { padding:8px 0 24px; }
.peer-intro > span { font:10px ui-monospace,monospace; letter-spacing:.06em; color:var(--page-muted); }
.peer-intro h2 { font-size:24px; line-height:1.3; font-weight:450; letter-spacing:-.04em; margin:12px 0 8px; }
.peer-intro p { font-size:12px; line-height:1.6; color:var(--page-muted); margin:0; }
.peer-grid { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:28px 18px; margin-bottom:44px; align-items:start; }
.peer-grid > figure { grid-column:span 2; }
.peer-grid > .peer-wide { grid-column:span 3; }
.peer-grid figcaption { flex-wrap:wrap; }
.peer-grid figcaption a { color:inherit; text-decoration:none; font:10px/1.5 -apple-system,BlinkMacSystemFont,sans-serif; }
.peer-grid figcaption a:hover { text-decoration:underline; text-underline-offset:3px; }
main[data-category]:not([data-category="all"]) {
& .peer-grid { display:contents; }
& .peer-grid > figure { grid-column:auto; }
& .peer-intro { grid-column:1 / -1; padding:0; }
}
body.focus-only {
& .peer-intro { display:none; }
& .peer-grid { display:contents; }
& .peer-grid.focused { display:grid; margin:0; & figure { display:block; } }
}
@media(max-width:1050px) {.peer-grid {grid-template-columns:repeat(2,minmax(0,1fr)); & > figure,& > .peer-wide {grid-column:auto;}}}
@media(max-width:650px) {.peer-grid {grid-template-columns:1fr;gap:28px;} .peer-intro h2 {font-size:22px;}}
.particle-ring {
& .ring-mist { position:absolute; inset:0 -15%; background:radial-gradient(ellipse at 37% 33% in oklab,color-mix(in oklab,var(--_card-accent-2) 17%,transparent),transparent 64%); mask-image:linear-gradient(in oklab,transparent,#000 12%,#000 82%,transparent); }
& .ring-canvas { position:absolute; inset:0; }
& .shop-conversation { position:absolute; top:21%; left:10%; width:80%; z-index:2; font-size:11.5px; line-height:1.55; }
& .shop-message { width:88%; margin:0 0 13px; background:var(--card-panel); padding:11px 13px; border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.7); box-shadow:0 5px 14px #3023520a; }
& .customer { margin-left:12%; }
& .shop-products { display:grid; grid-template-columns:1fr 1fr; padding:8px; gap:8px; background:var(--card-panel); border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.85); box-shadow:0 18px 35px -15px #2523422b,0 3px 10px #2523420a; }
& .shop-product { display:flex; flex-direction:column; border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.45); overflow:hidden; padding-bottom:8px;
& strong,& span,& b { padding:0 9px; }
& strong { margin-top:8px; font-size:11px; font-weight:550; }
& span { font-size:9px; color:var(--card-muted); }
& b { margin-top:3px; font-size:10px; font-weight:600; }
}
& .garment { background:var(--card-wash); height:116px; display:grid; place-items:center; & svg { width:87%; height:96%; } }
& .shop-cta { grid-column:1/-1; background:color-mix(in oklab,var(--card-accent) 11%,var(--card-surface)); color:var(--_card-accent-ink); padding:11px; text-align:center; border-radius:calc(var(--_card-radius)*.4); font-weight:550; & > span:last-child { margin-left:10px; } }
}
:where([data-theme="dark"]) .particle-ring .garment, .particle-ring[data-theme="dark"] .garment { background:color-mix(in oklab,var(--card-wash) 35%,#bdcde5); }
.flow-object {
& .card-stage { overflow: visible; perspective: 1100px; }
& .flow-aura { position:absolute; inset:0 -10% -10%; background:radial-gradient(ellipse at 70% 66% in oklab,color-mix(in oklab,var(--card-accent) 17%,transparent),transparent 64%),radial-gradient(ellipse at 24% 82% in oklab,color-mix(in oklab,var(--_card-accent-3) 14%,transparent),transparent 59%); }
& .physical-card { position: absolute; top:21%; left:17.5%; width:65%; aspect-ratio: 685/1078; overflow: hidden; overflow:clip; border-radius: calc(var(--_card-radius) * 1.9); background: linear-gradient(150deg in oklab,var(--_card-accent-2),var(--card-accent),var(--_card-accent-3)); box-shadow: 0 5px 12px -4px #35164536,0 26px 54px -17px color-mix(in oklab,var(--_card-accent-2) 35%,transparent),inset 0 0 0 1px #ffffff50; transform-style: preserve-3d; }
& .flow-surface { position: absolute; inset: 0; }
& .card-edition { position: absolute; top: 7%; left: 10%; font: 9px ui-monospace, monospace; letter-spacing: .18em; color: #fff9; }
& .card-white { position: absolute; inset: 0; width: 100%; height: 100%; }
& .physical-wordmark { position: absolute; right: 10%; bottom: 6%; font-size: 31px; color: #34314c; font-weight: 650; letter-spacing: -.08em; & span { font-size: 9px; position: absolute; right: -8px; top: 5px; letter-spacing: 0; } }
& .physical-number { position: absolute; bottom: 8%; left: 10%; font: 9px ui-monospace, monospace; color: #97929d; letter-spacing: .14em; }
& .token-engraving { position:absolute; inset:17% 10% 27%; color:#fff; text-shadow:0 1px 1px #17214460,0 -1px 0 #ffffff30; font:9px ui-monospace,monospace; }
& .token-status { display:flex; align-items:center; gap:7px; font-size:8px; letter-spacing:.16em; }
& .token-led { flex:none; width:5px; height:5px; border-radius:50%; background:#d4ffec; box-shadow:0 0 3px 1px #e4fffccc,0 0 13px #b3ffdf70; opacity:var(--token-pulse,.8); }
& .token-hash-label { display:block; margin-top:20%; font-size:7px; opacity:.7; letter-spacing:.24em; }
& .token-hash { display:block; margin-top:7px; font-size:clamp(13px,1.6vw,20px); font-weight:500; letter-spacing:.08em; overflow-wrap:anywhere; }
& .token-specs { display:grid; gap:9px; margin-top:18%; padding-top:12px; border-top:1px solid #ffffff38; }
& .token-specs > div { display:flex; flex-wrap:wrap; justify-content:space-between; gap:4px; }
& .token-specs span { opacity:.66; font-size:7px; letter-spacing:.12em; }
& .token-specs b { font-weight:400; font-size:8px; letter-spacing:.04em; }
& .token-registration { display:flex; gap:4px; margin-top:14%; height:13px; opacity:.45; }
& .token-registration i { width:1px; height:100%; background:currentColor; }
& .token-registration i:nth-child(3n) { height:55%; }
&[data-variant="card"] .token-engraving { display:none; }
}
.dot-globe {
& canvas { position:absolute; inset:0; }
& .globe-label { position:absolute; z-index:2; padding:7px 10px; border:1px solid color-mix(in oklab,var(--card-accent) 20%,var(--card-line)); border-radius:24px; background:var(--card-panel); color:var(--_card-accent-ink); box-shadow:0 4px 18px #28254e10; font-size:10px; white-space:nowrap; transform:translate(9px,-50%); }
}
.card.line-ribbons {
aspect-ratio:auto;
display:grid; grid-template-columns:minmax(0,1fr); grid-template-rows:auto auto minmax(140px,1fr); min-height:min-content;
/* A spanning ratio spacer preserves the art format without capping text height. */
&::before { content:""; grid-column:1; grid-row:1 / -1; aspect-ratio:2.7; align-self:start; pointer-events:none; }
& > .card-heading, & > .ribbon-note { grid-column:1; }
& > .card-heading { grid-row:1; position:relative; inset:auto; flex:none; margin:24px 24px 0 24px; }
/* Upper-left space is reserved by the paths, without a horizontal mask. */
&:not([data-title]) > .card-stage, &[data-title="top"] > .card-stage { inset:0; }
& .ribbon-art, & .ribbon-surface { position:absolute; width:100%; height:100%; inset:0; }
&[data-surface-ready="true"] .ribbon-art { visibility:hidden; }
&[data-renderer="dots"] .ribbon-mist, &[data-renderer="grid"] .ribbon-mist { opacity:.13; }
& .ribbon-mist { position:absolute; inset:0; background:
radial-gradient(ellipse 31% 65% at 49% 106% in oklab,color-mix(in oklab,oklch(from var(--_card-accent-2) calc(l - .12) calc(c * 1.08) h) 88%,transparent) 8%,color-mix(in oklab,var(--_card-accent-2) 65%,transparent) 38%,transparent 96%),
radial-gradient(ellipse 39% 70% at 75% 95% in oklab,color-mix(in oklab,color-mix(in oklab,var(--_card-accent-2) 45%,var(--card-accent)) 66%,transparent),transparent 96%),
radial-gradient(ellipse 33% 90% at 98% 40% in oklab,color-mix(in oklab,var(--card-accent) 72%,transparent),transparent 94%); }
& .ribbon-note { grid-row:2; position:relative; z-index:4; margin:24px 24px 0; flex:none; line-height:1.5; font-size:13px; color:var(--card-muted); }
&[data-title="bottom"] {
grid-template-rows:minmax(140px,1fr) auto auto;
& > .card-heading { grid-row:2; margin:24px 24px 0; }
& .ribbon-note { grid-row:3; margin:8px 24px 12px; }
& > .card-stage { position:relative; grid-column:1; grid-row:1; inset:auto; min-height:140px; }
}
@media(max-width:650px) {
&::before { aspect-ratio:1.2; }
& .ribbon-note { max-width:190px; line-height:1.5; }
}
}
.showcase-row { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: thin; scrollbar-color: var(--page-line) transparent; padding: 16px 16px 26px; margin: -12px -16px -8px; }
.showcase-card {
--_card-finish-base:transparent; --_card-material-reflection:0;
position:relative; flex: 0 0 332px; min-width: 0; scroll-snap-align: start;
& .card-finish { z-index:1; }
& .showcase-window { position: relative; width: 100%; aspect-ratio: 332/448; overflow: hidden; border-radius: var(--_card-radius); transform: scaleX(1); transition: transform .8s cubic-bezier(.165,.84,.44,1); }
& .showcase-art { position: absolute; inset: 0 -1.8%; transform: scaleX(1); transition: transform .8s cubic-bezier(.165,.84,.44,1); background: linear-gradient(in oklab,#090b14,color-mix(in oklab,var(--card-accent) 46%,#19112b)); & svg, & img { width: 100%; height: 100%; display: block; object-fit: cover; } }
& .showcase-wordmark { position: absolute; z-index: 3; bottom: 16px; left: 20px; font-size: 25px; font-weight: 650; letter-spacing: -.02em; color: #fff; transform-origin: left center; transition: transform .8s cubic-bezier(.165,.84,.44,1); }
& .showcase-emblem { position:absolute; z-index:2; width:48%; height:auto; left:26%; top:29%; fill:none; stroke:#fff; stroke-width:15; stroke-linecap:round; stroke-linejoin:round; }
& .showcase-art img.showcase-cropped { position:relative; width:140%; height:140%; left:-3%; top:-5%; }
&[data-background="editorial"] .showcase-window::after { content:""; position:absolute; inset:60% 0 0; background:linear-gradient(0deg,#02061060,transparent); pointer-events:none; }
&:hover .showcase-window { transform: scaleX(1.036); }
&:hover .showcase-art, &:hover .showcase-wordmark { transform: scaleX(.965251); }
& p { font-size: 15px; line-height: 1.55; margin: 16px 0 12px; padding-right: 16px; min-height: 47px; letter-spacing: -.02em; }
& .showcase-link { padding: 0; border: 0; background: transparent; color: var(--_card-accent-ink); font: inherit; text-decoration: none; font-size: 14px; font-weight: 550; cursor: pointer; & > span:last-child { display: inline-block; margin-left: 3px; transition: transform .3s; } &:hover > span:last-child { transform: translate(2px,-2px); } }
& small { display: block; margin-top: 20px; color: var(--card-muted); font: 10px/1.5 ui-monospace, monospace; }
&[data-background="iso-object"] .showcase-art { background: color-mix(in oklab,var(--card-accent) 48%,#241824); }
&[data-background="membrane"] { & .showcase-art { background: color-mix(in oklab,var(--card-accent) 12%,#f8f3ef); } & .showcase-wordmark { color: #514451; } }
&[data-background="image"] .showcase-window::after { content: ""; position: absolute; inset: 0; background: linear-gradient(0deg in oklab,color-mix(in oklab,var(--card-accent) 65%,#12141b) 0%,color-mix(in oklab,var(--card-accent) 15%,transparent) 65%,transparent); }
@media(max-width:650px) { flex-basis: min(332px, 86vw); }
}
.card-dialog.showcase-dialog {
width: min(420px, calc((100dvh - 200px) * .741), calc(100vw - 48px));
background: var(--card-surface); border-radius: var(--_card-radius); padding: 12px;
& .showcase-card { width: 100%; }
& .showcase-card p { padding-left: 4px; margin-top: 12px; }
& .showcase-link { display: none; }
& .showcase-wordmark { font-size:clamp(18px,3vh,25px); }
& small { display: none; }
}
.showcase-card { display:flex; flex-direction:column; }
.showcase-card[data-title="top"] { & .showcase-caption { order:-1; margin-bottom:16px; } & .showcase-caption p { margin:0 0 10px; } }
.showcase-card[data-motion="off"] { & .showcase-window,& .showcase-art,& .showcase-wordmark { transition-duration:0s; transform:none; } }
.ui-slice {
& .card-stage { background:radial-gradient(ellipse at 45% 65% in oklab,color-mix(in oklab,var(--card-accent) 10%,transparent),transparent 74%); }
& .slice-back { position:absolute; width:91%; top:0; left:17%; height:98%; overflow:clip; background:var(--card-wash); border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.8); color:var(--card-muted); mask-image:linear-gradient(in oklab,#000 68%,transparent); }
& .slice-toolbar { border-bottom:1px solid var(--card-line); font-size:12px; padding:16px; & > span:last-child { float:right; margin-right:50px; } }
& .slice-columns { display:grid; grid-template-columns:1fr 1fr; padding:14px; gap:12px; font-size:12px; & p { padding:15px 9px; margin:9px 0; background:var(--card-panel); border-radius:calc(var(--_card-radius)*.45); color:var(--card-ink); } }
& .slice-front { position:absolute; width:82%; left:5%; top:25%; background:var(--card-panel); border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.8); box-shadow:0 22px 45px -15px #24324f30,0 3px 9px #24324f0a; padding:20px; font-size:14px; }
& .slice-front-heading { display:flex; align-items:center; gap:7px; font-weight:550; font-size:15px; margin-bottom:11px; }
& .slice-symbol,& .slice-more { color:var(--card-muted); } & .slice-more { margin-left:auto; }
& .slice-task { display:flex; gap:10px; align-items:center; padding:8px 0; margin:0; & i { width:16px; height:16px; border:1px solid var(--card-muted); border-radius:5px; font-size:10px; display:grid; place-items:center; font-style:normal; } & b { margin-left:auto; width:26px; height:26px; border-radius:50%; background:#dce9f7; display:grid; place-items:center; font-size:10px; font-weight:450; color:#385677; } &:nth-of-type(2) b { background:#f4dfdb; color:#7c514b; } &:nth-of-type(3) b { background:#dde9df; color:#48614d; } &.done,&.checked { & i { background:var(--card-accent); color:var(--_card-on-accent); border-color:var(--card-accent); } & > span { color:var(--card-muted); text-decoration:line-through; } } }
& .slice-demo i { background:color-mix(in oklab,var(--card-accent) calc(var(--task-done,0)*100%),transparent);color:color-mix(in oklab,var(--_card-on-accent) calc(var(--task-done,0)*100%),transparent);border-color:color-mix(in oklab,var(--card-accent) calc(var(--task-done,0)*100%),var(--card-muted));transform:scale(calc(1 - sin(var(--task-done,0)*3.14159)*.12)); }
& .slice-demo > span { color:color-mix(in oklab,var(--card-muted) calc(var(--task-done,0)*100%),var(--card-ink)); }
& .slice-progress { display:flex; flex-wrap:wrap; justify-content:space-between; margin-top:15px; font-size:10px; color:var(--card-muted); & > div { width:100%; height:4px; margin-top:9px; background:var(--card-line); border-radius:3px; overflow:clip; & i { display:block; width:100%; height:100%; background:var(--card-accent); border-radius:inherit; transform-origin:left;transform:scaleX(.33333); } } }
@media(max-width:650px) { & .slice-front { padding:18px; font-size:13px; } & .slice-front-heading { font-size:13px; } & .slice-progress { font-size:10px; } }
}
.terminal {
& .card-stage { background:radial-gradient(ellipse at 83% 87% in oklab,color-mix(in oklab,var(--card-accent) 13%,transparent),transparent 62%); mask-image:linear-gradient(in oklab,#000 82%,transparent 100%); }
& .terminal-window { position: absolute; top:3%; left:5%; width:95%; height:calc(100% + 50px); border: 1px solid var(--card-line); border-radius: calc(var(--_card-radius) * .7); background: var(--card-panel); }
& .terminal-toolbar { display: flex; align-items: center; gap: 5px; padding: 12px 14px; border-bottom: 1px solid var(--card-line); color: var(--card-muted); & > span { width: 5px; height: 5px; border-radius: 50%; border: 1px solid color-mix(in oklab,var(--card-muted) 40%,transparent); } & em { margin-left: 12px; font: 9px ui-monospace, monospace; } & b { margin-left: auto; margin-right: 20px; font: 9px ui-monospace, monospace; opacity: .6; } }
& .terminal-lines { padding: 15px 0; font: 13px/1.8 ui-monospace, SFMono-Regular, Consolas, monospace;
& p { padding: 6px 15px; margin: 0; opacity: var(--line-visible,1); transform: translateY(calc((1 - var(--line-visible,1)) * 4px)); white-space: nowrap; }
& p > span:first-child { color: var(--_card-accent-2-ink); }
& .terminal-highlight { background:color-mix(in oklab,var(--card-accent) 9%,var(--card-panel));border-left:2px solid var(--card-accent);padding-left:13px; }
& .terminal-flag { color: var(--_card-accent-3-ink); }
& .terminal-dim { color: var(--card-muted); }
& .terminal-address { color:var(--_card-accent-ink); font-size:15px; font-weight:550; }
& .terminal-result span { color: var(--_card-accent-ink); }
}
}
.terminal .terminal-cursor { opacity:var(--cursor-on,1); }
@media(max-width:650px) { .terminal .terminal-lines { font-size:12px; line-height:1.8; padding:15px 0; & p { padding:7px 12px; } } .terminal .terminal-toolbar { padding:12px; } .terminal .terminal-lines .terminal-address { font-size:14px; } }
.metric {
min-height:calc(var(--_card-heading-height,44px) + 163px);
& .metric-copy { position:absolute; left:30px; right:55%; top:25px; z-index:2; }
& .metric-copy h2 { font-size:19px; margin:0 0 9px; font-weight:550; letter-spacing:-.035em; }
& .metric-copy p { font-size:11px; color:var(--card-muted); margin:0; }
& .metric-number { position:absolute; left:30px; bottom:42px; display:flex; align-items:center; gap:18px; }
& .metric-value { display:flex; align-items:baseline; font-size:72px; line-height:1; font-weight:300; letter-spacing:-.025em; font-variant-numeric:tabular-nums; }
& .metric-readable { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }
& .metric-digit { display:block; width:.58em; height:1em; overflow:clip; mask-image:linear-gradient(transparent,#000 10%,#000 90%,transparent); }
& .metric-reel { display:flex; flex-direction:column; }
& .metric-reel > span { display:block; height:1em; flex:none; }
& .metric-separator { display:block; width:.22em; }
& .metric-delta { font-size:12px; white-space:nowrap; color:var(--_card-accent-ink); padding:6px 8px; background:color-mix(in oklab,var(--card-accent) 7%,var(--card-surface)); border-radius:5px; & small { display:none; } }
& .metric-chart { position:absolute; right:30px; bottom:0; width:45%; height:78%; }
& .metric-bars { height:100%; display:flex; align-items:flex-end; gap:5px; border-bottom:1px solid var(--card-line);
& i { flex:1; height:var(--height); background:color-mix(in oklab,var(--card-muted) 23%,var(--card-surface)); transform-origin:bottom; transform:scaleY(var(--bar-growth,1)); border-radius:2px 2px 0 0; }
& .recent { background:color-mix(in oklab,var(--card-accent) 19%,var(--card-surface)); }
& .current { background:linear-gradient(in oklab,var(--_card-accent-2),var(--card-accent)); }
}
& .metric-today { position:absolute; right:0; top:-16px; font-size:10px; color:var(--_card-accent-ink); opacity:var(--metric-settled,1); }
& .metric-delta { opacity:var(--metric-settled,1); transform:translateY(calc((1 - var(--metric-settled,1))*3px)); }
& .metric-axis { display:none; }
&[data-title="bottom"] { & .metric-copy { top:auto; bottom:23px; } & .metric-number { bottom:auto; top:27px; } & .metric-chart { height:65%; bottom:0; } }
@media(max-width:950px) { & .metric-value { font-size:55px; } & .metric-delta { font-size:10px; } }
@media(max-width:650px) {
min-height:calc(var(--_card-heading-height,44px) + 228px);
& .metric-copy { left:24px; right:24px; top:24px; }
& .metric-copy h2 { font-size:18px; }
& .metric-number { left:24px; top:max(97px,calc(var(--_card-heading-height,44px) + 44px)); bottom:auto; gap:10px; }
& .metric-value { font-size:52px; }
& .metric-chart { width:calc(100% - 48px); right:24px; height:min(36%,calc(100% - var(--_card-heading-height,44px) - 132px)); }
& .metric-bars { gap:4px; }
&[data-title="bottom"] {
min-height:calc(var(--_card-heading-height,44px) + 242px);
& .metric-number { top:32px; }
& .metric-chart { bottom:max(102px,calc(var(--_card-heading-height,44px) + 55px)); height:min(35%,calc(100% - var(--_card-heading-height,44px) - 162px)); }
}
}
}
.beam-graph {
& .card-stage::before { content:""; position:absolute; inset:0; background-image:radial-gradient(color-mix(in oklab,var(--card-accent) 20%,transparent) .7px,transparent .8px); background-size:15px 15px; mask-image:radial-gradient(ellipse in oklab,#000,transparent 72%); }
& .beam-lines { position:absolute; width:100%; height:100%; }
& .beam-base { fill:none; stroke:color-mix(in oklab,var(--card-muted) 35%,var(--card-surface)); stroke-width:1.5; }
& .beam-light,& .beam-glow { fill:none; stroke-linecap:round; stroke-width:2; stroke-dasharray:72 600; }
& .beam-glow { stroke-width:6; opacity:.55; }
& .beam-node { position:absolute; display:flex; align-items:center; gap:7px; font-size:13px; border:1px solid var(--card-line); padding:10px 15px; border-radius:30px; background:var(--card-panel); transform:translate(-50%,-50%); white-space:nowrap; box-shadow:0 3px 10px #29314b07; & i { width:5px; height:5px; border-radius:50%; background:var(--_card-accent-2); } }
& .beam-a { left:18%; top:20%; } & .beam-b { left:18%; top:50%; } & .beam-e { left:18%; top:80%; } & .beam-c { left:82%; top:32%; } & .beam-d { left:82%; top:68%; }
& .beam-center { left:50%; top:50%; font-size:22px; font-weight:600; letter-spacing:-.02em; padding:17px 20px; border-color:color-mix(in oklab,var(--card-accent) 25%,var(--card-line)); box-shadow:0 0 0 9px var(--card-surface),0 0 0 10px color-mix(in oklab,var(--card-accent) 35%,var(--card-surface)),0 0 35px color-mix(in oklab,var(--card-accent) 16%,transparent); }
@media(max-width:650px) { & .beam-node { font-size:10px; padding:7px 10px; gap:4px; } & .beam-center { font-size:15px; padding:12px 13px; } }
}
.keycaps {
& .card-stage { --key-group-top:calc(50% - 107px); }
& .keycap-row { position:absolute; width:100%; top:var(--key-group-top); display:flex; align-items:center; justify-content:center; gap:21px; }
& .keycap {
--_key-radius:calc(var(--_card-radius)*1.6);
width:128px; height:136px; flex-shrink:0; position:relative;
/* A shallow sidewall carries the depth; avoid stacking another dark rim. */
background:linear-gradient(180deg in oklab,color-mix(in oklab,var(--card-panel),var(--card-muted) 4%),color-mix(in oklab,var(--card-panel),var(--card-muted) 15%));
border:1px solid color-mix(in oklab,var(--card-panel),var(--card-muted) 20%);
border-radius:var(--_key-radius);
box-shadow:0 1px 2px #0000000a,0 calc(6px - var(--key-press,0)*2px) calc(10px - var(--key-press,0)*3px) -6px #00000024;
}
& .keycap-top { position:absolute; inset:2px 2px 6px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; background:linear-gradient(180deg in oklab,var(--card-panel),color-mix(in oklab,var(--card-panel),var(--card-muted) 2%)); border:1px solid color-mix(in oklab,var(--card-panel),var(--card-muted) 13%); border-radius:max(0px,calc(var(--_key-radius) - 3px)); box-shadow:0 1px 1px #00000007; transform:translateY(calc(var(--key-press,0)*2.5px));
& > span { font-size:48px; line-height:1.2; font-weight:400; } & small { font-size:11px; color:var(--card-muted); }
}
& .key-letter .keycap-top > span { color:var(--_card-accent-ink); }
& .key-plus { font-size:17px; color:var(--card-muted); }
& .command-panel { position:absolute; left:50%; top:calc(var(--key-group-top) + 164px); width:min(420px,86%); height:50px; transform:translate(-50%,calc((1 - var(--command-visible,0))*8px)); display:flex; gap:12px; align-items:center; padding:13px 15px; border:1px solid var(--card-line); border-radius:calc(var(--_card-radius)*.8); background:var(--card-panel); color:var(--card-muted); box-shadow:0 2px 6px -3px #00000012; font-size:12px; opacity:var(--command-visible,0); & > span:first-child { font-size:22px; color:var(--card-ink); } & kbd { font:9px ui-monospace,monospace; margin-left:auto; border:1px solid var(--card-line); border-radius:3px; padding:3px; } }
@media(max-width:650px) { & .keycap-row { gap:16px; } }
@media(max-width:360px) { & .keycap-row { gap:14px; } & .keycap { width:104px; height:116px; } }
}
.promo-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; }
.promo-band {
display:flex; align-items:flex-start; min-height:180px; aspect-ratio:auto; padding:30px 24px;
text-decoration:none; background:var(--card-wash);
& .promo-copy { position:relative; z-index:2; width:65%; }
& p { margin:0 0 20px; color:var(--card-muted); font-size:16px; line-height:1.5; letter-spacing:-.025em; }
& strong { color:var(--card-ink); font-weight:650; }
& .promo-link { color:color-mix(in oklab,var(--_card-accent-ink) 85%,var(--card-ink)); font-size:16px; font-weight:600; }
& .promo-link > span:last-child { display:inline-block; margin-left:4px; transition:transform .3s ease; }
&:is(:hover,:focus-visible) .promo-link > span:last-child { transform:translateX(4px); }
& .promo-art { position:absolute; right:0; top:0; bottom:0; width:43%; pointer-events:none; }
&[data-art="prism"] .promo-art {
width:44%; clip-path:polygon(43% 0,100% 0,100% 100%,0 100%);
background:
radial-gradient(ellipse at 93% 1% in oklab,var(--_card-accent-2),transparent 69%),
radial-gradient(ellipse at 4% 97% in oklab,color-mix(in oklab,var(--card-accent) 80%,#4812bd),transparent 74%),
linear-gradient(155deg in oklab,var(--_card-accent-2),var(--card-accent) 68%);
&::after { content:""; position:absolute; inset:0; background:linear-gradient(119deg,var(--card-wash) 11%,transparent 37%); }
}
& svg { width:100%; height:100%; display:block; }
& .facet-back { fill:var(--_card-accent-3); }
& .facet-top { fill:oklch(from var(--_card-accent-3) calc(l - .01) calc(c * 1.45) calc(h + 12)); }
& .facet-front { fill:oklch(from var(--_card-accent-3) calc(l + .13) calc(c * .95) calc(h + 36)); }
& .facet-side { fill:oklch(from var(--_card-accent-3) calc(l + .07) calc(c * 1.3) calc(h + 30)); }
@media(max-width:650px) { min-height:204px; padding:25px 20px; & .promo-copy { width:73%; } & p { font-size:15px; } & .promo-art { width:32%; opacity:.85; } &[data-art="prism"] .promo-art { width:34%; } }
}
@media(max-width:850px) { .promo-grid { grid-template-columns:1fr; } }
.vector-space {
height:440px; aspect-ratio:auto; --_card-stage-track-min:min-content;
& .card-stage { display:flex; flex-direction:column; justify-content:space-between; gap:100px; overflow:clip; background:radial-gradient(ellipse at 63% 52% in oklab,color-mix(in oklab,var(--card-accent) 5%,transparent),transparent 70%); }
& .vector-canvas,& .vector-fallback { position:absolute; width:100%; height:100%; inset:0; }
&[data-vector-ready="true"] .vector-fallback { display:none; }
& .vector-query { display:flex; align-items:center; gap:9px; position:relative; z-index:2; align-self:flex-start; margin:4px 24px 0; max-width:calc(100% - 48px); padding:9px 13px; border:1px solid var(--card-line); border-radius:6px; background:var(--card-panel); font-size:12px; box-shadow:0 3px 9px #00000003; }
& .vector-query span { min-width:0; }
& .vector-query svg { flex:none; width:14px; height:14px; fill:none; stroke:var(--card-muted); stroke-width:1.4; }
& .vector-result { display:flex; align-items:center; gap:7px; flex-wrap:wrap; position:relative; z-index:2; margin:0 24px 21px; font-size:11px; }
& .vector-result i { flex:none; width:5px; height:5px; border-radius:50%; background:var(--card-accent); }
& .vector-tag { margin-left:auto; color:var(--card-muted); }
}
.file-mosaic {
height:440px; aspect-ratio:auto; --_card-stage-track-min:min-content;
--file-line:color-mix(in oklab,var(--card-ink) 12%,var(--card-surface));
--file-ink:color-mix(in oklab,var(--card-ink) 80%,var(--card-surface));
--file-label:color-mix(in oklab,var(--card-ink) 66%,var(--card-surface));
& .card-stage { display:grid; grid-template-rows:auto minmax(180px,1fr); mask-image:linear-gradient(#000 90%,transparent 100%); }
& .file-collection { position:relative; z-index:2; display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:10px; padding:6px 24px 22px; min-width:0; font-size:12px; & > span { min-width:0; overflow-wrap:anywhere; } & > span:last-child { color:var(--card-muted);font-size:10px; } & i { color:var(--_card-accent-ink);font-style:normal; } }
& .file-viewport { position:relative; overflow:clip; min-height:180px; }
& .file-grid { position:absolute; top:0; left:24px; width:588px; display:grid; grid-template-columns:repeat(5,108px); grid-auto-rows:106px; gap:12px; }
& .file-tile { display:flex; flex-direction:column; justify-content:center; align-items:center; gap:9px; border:1px solid var(--file-line); border-radius:calc(var(--_card-radius)*.8); background:var(--card-surface); color:var(--file-label); font:9px/12px ui-monospace,monospace; }
& .file-tile > span { max-width:calc(100% - 18px); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
& .file-empty { grid-column:1 / -1; margin:25px 0; color:var(--card-muted); font-size:12px; }
& .file-art { display:flex; align-items:center; justify-content:center; width:78px; height:44px; flex:none; color:var(--file-ink); }
& .file-art svg { width:31px; height:31px; fill:none; stroke:currentColor; stroke-width:1.35; stroke-linecap:round; stroke-linejoin:round; }
& .file-thumbnail { display:flex; height:38px; width:64px; border-radius:2px; overflow:hidden;
& i { flex:1; background:var(--card-accent); }
& i:nth-child(2) { background:var(--_card-accent-2); }
& i:nth-child(3) { background:var(--_card-accent-3); }
& i:nth-child(4) { background:var(--file-ink); }
}
& .file-wave { display:flex; height:35px; gap:3px; align-items:center; & i { width:2px; height:var(--h);border-radius:1px;background:currentColor; } }
}
.stream-window {
height:440px;aspect-ratio:auto; --_card-stage-track-min:min-content;
& .card-stage { display:grid; grid-template-rows:auto minmax(180px,1fr); mask-image:linear-gradient(#000 84%,transparent); }
& .stream-status { display:flex; align-items:center; gap:7px; padding:6px 24px 20px; min-width:0;font-size:11px;color:var(--card-muted); & > i { flex:none; width:5px;height:5px;border-radius:50%;background:var(--card-accent); } & > span:not(.stream-count) { min-width:0; overflow-wrap:anywhere; } & .stream-count { flex:none; white-space:nowrap; margin-left:auto;font:10px ui-monospace,monospace; } }
& .stream-viewport { position:relative; overflow:clip; min-height:180px; }
& .stream-browser { position:absolute;left:24px;top:0;width:470px;height:324px;border:1px solid var(--card-line);border-radius:7px;background:var(--card-panel);box-shadow:0 8px 24px #18273e07; }
& .stream-toolbar { height:33px;border-bottom:1px solid var(--card-line);display:flex;align-items:center;gap:4px;padding:0 13px; & > span { width:5px;height:5px;border-radius:50%;background:var(--card-line); } & > i { overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;font:9px ui-monospace,monospace;color:var(--card-muted);margin-left:10px; } }
& .stream-sidebar { position:absolute;top:33px;bottom:0;left:0;width:46px;border-right:1px solid var(--card-line);display:flex;align-items:center;flex-direction:column;gap:19px;padding-top:21px; & > i { width:11px;height:11px;border:1px solid var(--card-muted);opacity:.3;border-radius:3px; } & b { width:14px;height:14px;border-radius:4px;background:var(--card-accent);opacity:.8; } }
& .stream-content { padding:20px 20px 20px 66px; }
& .stream-chunk { position:relative; --ready:1; }
& .chunk-content { opacity:var(--content-opacity,1);transform:translateY(calc((1 - var(--ready)) * 5px)); }
& .chunk-skeleton { position:absolute;inset:0;opacity:var(--skeleton-opacity,0);display:flex;flex-direction:column;gap:10px; & > i,& > b { height:8px;background:var(--card-line);border-radius:3px;width:68%; } & > b { height:32px;width:85%; } & > i:last-child { width:45%; } }
& .stream-summary { min-height:82px; & small { font-size:10px;color:var(--card-muted); } & strong { display:block;font-size:19px;line-height:1.5;font-weight:550;margin-top:5px; } & p { font-size:10px;margin:4px 0;color:var(--card-muted); } }
& .stream-panels { display:grid;grid-template-columns:155px 150px;gap:15px;margin-top:15px; }
& .stream-visual { height:143px; & .chunk-skeleton > b { width:100%;height:100%; } & .chunk-content { height:100%;overflow:hidden;border-radius:5px;background:color-mix(in oklab,var(--card-accent) 7%,var(--card-panel)); } & span { position:absolute;bottom:12px;left:13px;font-size:11px; } }
& .stream-orbit { width:170px;height:170px;position:absolute;top:-42px;left:28px;transform:rotate(-30deg); & i { position:absolute;inset:0;border:1px solid color-mix(in oklab,var(--card-accent) 45%,transparent);border-radius:50%; } & i:nth-child(2) { inset:22px; } & i:nth-child(3) { inset:44px;background:radial-gradient(circle at 30% 20% in oklab,var(--_card-accent-2),var(--card-accent));border:0; } }
& .stream-list { padding:6px 0; & small { font-size:10px;color:var(--card-muted); } & p { display:flex;align-items:center;gap:8px;font-size:11px;margin:17px 0; } & i { display:inline-block;width:15px;height:15px;border-radius:3px;background:color-mix(in oklab,var(--card-accent) 12%,var(--card-panel));border:1px solid color-mix(in oklab,var(--card-accent) 20%,var(--card-line)); } }
}
.card.portrait-story {
--_card-finish-base:transparent; --_card-material-reflection:0;
position:relative; display:grid; grid-template-columns:minmax(0,1fr); grid-template-rows:auto minmax(170px,1fr) auto;
padding:26px; aspect-ratio:4/5; min-height:auto; color:#fff; background:#171a20; border:0;
& .story-photo { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:50% 32%; filter:grayscale(1); transform:scale(1.025); transition:transform 1s cubic-bezier(.2,.7,.2,1); }
& .story-tint { position:absolute; inset:0; background:var(--card-accent); mix-blend-mode:multiply; opacity:.62; transition:opacity .8s; }
& .story-shade { position:absolute; inset:0; background:linear-gradient(180deg,#0003 0%,transparent 35%,#080a1052 55%,#080a10ed 100%); }
& .story-brand { position:relative; z-index:2; grid-row:1; margin:0; font-size:19px; line-height:1.2; font-weight:550; letter-spacing:-.06em; overflow-wrap:anywhere; }
& .story-copy { position:relative; z-index:2; grid-row:3; min-width:0; }
& > .card-finish { z-index:1; }
& blockquote { margin:0 0 22px; font-family:"Songti SC","Noto Serif CJK SC",Georgia,serif; font-size:clamp(23px,2.2vw,30px); line-height:1.45; letter-spacing:-.03em; }
& .story-copy p { display:flex; flex-wrap:wrap; gap:4px 12px; margin:0; font-size:11px; line-height:1.6; color:#ffffffb8; }
&:is(:hover,:focus-within) .story-photo { transform:scale(1); }
&:is(:hover,:focus-within) .story-tint { opacity:.32; }
}
@media(prefers-reduced-motion:reduce){.portrait-story *{transition:none!important;}}
.card.capture-notes {
--_card-stage-track-min:340px; aspect-ratio:auto; background:color-mix(in oklab,var(--card-surface) 95%,var(--card-ink));
& .card-stage { display:grid; grid-template-rows:minmax(0,1fr) auto; gap:12px; padding:0 22px 22px; }
& .capture-sheet { min-height:0; overflow:hidden; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 8%,transparent); border-radius:10px; box-shadow:0 5px 18px #18243207; padding:23px 19px 0; mask-image:linear-gradient(#000 82%,transparent); }
& .capture-sheet-title { display:flex; align-items:center; gap:8px; margin-bottom:16px; font-size:20px; line-height:1.3; }
& .capture-sheet-title strong { font-weight:550; overflow-wrap:anywhere; }
& .capture-sheet-title svg { width:22px; height:22px; flex:none; fill:none; stroke:var(--_card-accent-ink); stroke-width:1.7; stroke-linecap:round; }
& .capture-tabs { display:flex; gap:12px; align-items:center; color:var(--card-muted); font-size:10px; line-height:1.5; padding-bottom:13px; border-bottom:1px solid var(--card-line); }
& .capture-tabs span:first-child { color:var(--card-ink); background:var(--card-wash); padding:3px 6px; border-radius:4px; }
& .capture-tabs i { font-style:normal; margin-left:auto; }
& .capture-labels { display:flex; justify-content:space-between; font-size:9px; color:var(--card-muted); padding:12px 0 6px; }
& .capture-row { display:flex; align-items:center; gap:8px; width:100%; min-width:0; padding:12px 0; background:transparent; border:0; border-top:1px solid var(--card-line); text-align:left; color:var(--card-ink); cursor:pointer; font-size:11px; line-height:1.3; }
& .capture-row::before { content:''; width:9px; height:12px; flex:none; border:1px solid var(--card-muted); border-radius:1px; background:linear-gradient(var(--card-line),var(--card-line)) 2px 4px/5px 1px no-repeat; }
& .capture-row span { font-size:11px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
& .capture-row .capture-tags { display:flex; flex:none; gap:4px; margin-left:auto; max-width:42%; }
& .capture-row small { flex:0 1 auto; min-width:0; font-size:9px; color:var(--tag-ink); background:color-mix(in oklab,var(--tag-ink) 11%,var(--card-panel)); border-radius:3px; padding:3px 5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
& [data-tone="indigo"] { --tag-ink:light-dark(#5960b5,#a8adf3); }
& [data-tone="teal"] { --tag-ink:light-dark(#347768,#88c9b7); }
& [data-tone="amber"] { --tag-ink:light-dark(#936b32,#d9b277); }
& [data-tone="rose"] { --tag-ink:light-dark(#995c74,#d8a1b5); }
& [data-tone="slate"] { --tag-ink:light-dark(#5f7089,#adbdd3); }
& .capture-row[aria-pressed="true"] span { color:var(--_card-accent-ink); }
& .capture-recorder { display:flex; align-items:center; gap:9px; min-width:0; padding:9px 11px; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 12%,transparent); border-radius:40px; box-shadow:0 6px 16px #1118270a; }
& .record-dot { width:6px; height:6px; flex:none; border-radius:50%; background:var(--card-accent); }
& .capture-now { font-size:11px; min-width:0; flex:1; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
& .capture-wave { display:flex; align-items:center; gap:3px; height:22px; flex:none; }
& .capture-wave i { width:2px; height:20px; background:var(--card-muted); border-radius:2px; transform:scaleY(.3); }
& .capture-pause { display:grid; place-items:center; border:0; border-radius:50%; width:28px; height:28px; flex:none; background:var(--card-wash); color:var(--card-ink); cursor:pointer; }
& .capture-pause svg { width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; }
}
.card.answer-lens {
--_card-stage-track-min:340px; aspect-ratio:auto; background:color-mix(in oklab,var(--card-surface) 95%,var(--card-ink));
& .card-stage { display:grid; grid-template-rows:minmax(210px,1fr) auto; }
& .answer-scene { position:relative; display:flex; flex-direction:column; overflow:hidden; min-height:250px; }
& .answer-dashboard { position:relative; flex:none; margin:0 22px; min-height:224px; padding:22px 22px 110px; border-radius:10px; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 7%,transparent); box-shadow:0 4px 16px #18243206; }
& .answer-dash-kicker { display:block; color:var(--card-muted); font-size:9px; margin-bottom:8px; }
& .answer-dashboard strong { display:block; font-size:19px; line-height:1.3; font-weight:550; overflow-wrap:anywhere; }
& .answer-sync { display:inline-block; font-size:9px; margin-top:8px; padding:3px 6px; color:var(--card-muted); background:var(--card-wash); border-radius:3px; }
& .answer-dash-lines { position:absolute; left:22px; right:22px; bottom:25px; display:grid; gap:16px; & i { height:1px; background:var(--card-line); } }
& .answer-charts { position:relative; flex:none; height:150px; margin-top:-100px; }
& .answer-chart { position:absolute; width:43%; height:178px; padding:13px; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 9%,transparent); border-radius:9px; box-shadow:0 6px 18px #1824320a; transform:translateY(16px) rotate(var(--angle)); transition:transform .65s cubic-bezier(.2,.8,.2,1),border-color .3s; }
& .answer-chart:nth-child(1){left:-4%;--angle:-10deg;--chart-color:var(--card-accent);}
& .answer-chart:nth-child(2){left:29%;--angle:0deg;--chart-color:var(--_card-accent-2);}
& .answer-chart:nth-child(3){left:64%;--angle:10deg;--chart-color:var(--_card-accent-3);}
& .answer-chart[data-selected="true"] { transform:translateY(-7px) rotate(var(--angle)); z-index:2; border-color:color-mix(in oklab,var(--card-accent) 40%,var(--card-line)); }
& .answer-chart svg { width:100%; height:117px; display:block; }
& .answer-chart > span:last-child { font-size:9px; color:var(--card-muted); }
& .chart-symbol { position:absolute; top:-12px; right:18px; width:27px; height:27px; display:grid; place-items:center; border-radius:6px; background:var(--card-panel); box-shadow:0 2px 7px #18243216; color:var(--_card-accent-ink); font-size:19px; }
& .ring-base,& .ring-value { fill:none; stroke:var(--card-line); stroke-width:9; }
& .ring-value { stroke:var(--chart-color); stroke-dasharray:220 302; transform:rotate(-90deg); transform-origin:80px 78px; }
& .chart-grid { fill:none; stroke:var(--card-line); stroke-width:1; }
& .chart-bars { stroke:var(--chart-color); stroke-width:15; opacity:.75; }
& .chart-line { fill:none; stroke:var(--chart-color); stroke-width:2; }
& .answer-response { position:relative; margin:0 22px 22px; padding:14px 15px; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 12%,transparent); border-radius:11px; box-shadow:0 5px 18px #1824320a; }
& .answer-response > p { font-size:12px; line-height:1.5; margin:0 0 12px; }
& .answer-citations { display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px; }
& .answer-citations button { display:flex; align-items:center; gap:5px; color:var(--card-muted); background:var(--card-wash); border:1px solid transparent; border-radius:4px; padding:4px 6px; font-size:9px; cursor:pointer; }
& .answer-citations button[aria-pressed="true"] { color:var(--_card-accent-ink); border-color:color-mix(in oklab,var(--card-accent) 25%,var(--card-line)); background:color-mix(in oklab,var(--card-accent) 7%,var(--card-panel)); }
& .answer-excerpt { font-size:11px; line-height:1.7; color:var(--card-muted); min-height:3.4em; overflow-wrap:anywhere; }
& .answer-reference { display:block; margin-top:8px; padding-top:8px; border-top:1px solid var(--card-line); font:9px ui-monospace,monospace; color:var(--card-muted); overflow-wrap:anywhere; }
& .answer-reference[hidden] { display:none; }
}
@media(prefers-reduced-motion:reduce){.answer-lens *{transition:none!important;}}
.card.responsive-preview {
--_card-stage-track-min:320px; aspect-ratio:auto;
& .card-stage { display:grid; grid-template-rows:minmax(245px,1fr) auto; }
& .size-scene { position:relative; min-height:245px; overflow:hidden; background:radial-gradient(ellipse at 50% 70%,var(--card-wash),transparent 65%); }
& .size-frame { position:absolute; overflow:hidden; border:1px solid color-mix(in oklab,var(--card-ink) 16%,var(--card-panel)); border-radius:10px; background:var(--card-panel); box-shadow:0 6px 14px #18243209; }
& .size-frame-bar { height:29px; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--card-line); color:var(--card-muted); font:9px ui-monospace,monospace; padding:0 10px; }
& .size-frame-bar i { width:4px; height:4px; background:var(--card-line); border-radius:50%; box-shadow:7px 0 var(--card-line),14px 0 var(--card-line); }
& .size-frame > svg { display:block; width:100%; height:calc(100% - 29px); }
& .size-frame-0 { width:59%; height:190px; left:20%; top:10px; transform:translate(calc(var(--spread,1)*12%),calc(var(--spread,1)*-1px)); }
& .size-frame-1 { width:47%; height:154px; left:26%; top:44px; transform:translate(calc(var(--spread,1)*-18%),calc(var(--spread,1)*6px)); opacity:var(--spread,1); }
& .size-frame-2 { width:27%; height:137px; left:36%; top:61px; transform:translate(calc(var(--spread,1)*-87%),calc(var(--spread,1)*33px)); opacity:var(--spread,1); }
& .preview-land-back { fill:color-mix(in oklab,var(--card-accent) 17%,var(--card-panel)); }
& .preview-land-front { fill:color-mix(in oklab,var(--card-accent) 32%,var(--card-panel)); }
& .preview-contours { stroke:color-mix(in oklab,var(--card-accent) 45%,var(--card-panel)); stroke-width:1; }
& .preview-sun { fill:color-mix(in oklab,var(--_card-accent-2) 36%,var(--card-panel)); }
& .size-toolbar { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; padding:18px 24px 24px; font-size:11px; line-height:1.5; color:var(--card-muted); }
& .size-toggle { display:flex; gap:12px; align-items:center; background:transparent; color:var(--_card-accent-ink); border:0; padding:0; font:inherit; cursor:pointer; white-space:nowrap; }
}
.card.deploy-preview {
--_card-stage-track-min:320px; aspect-ratio:auto;
& > .card-heading { text-wrap:balance; }
& .card-stage { display:grid; grid-template-rows:minmax(245px,1fr) auto; padding:0 24px; }
& .deploy-window { align-self:center; width:100%; overflow:hidden; background:var(--card-panel); border:1px solid color-mix(in oklab,var(--card-ink) 14%,var(--card-panel)); border-radius:10px; box-shadow:0 8px 18px #18243206; }
& .deploy-window-bar { display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap:wrap; padding:13px 15px; font:10px/1.4 ui-monospace,monospace; color:var(--card-muted); background:var(--card-wash); border-bottom:1px solid var(--card-line); overflow-wrap:anywhere; }
& .deploy-branch { padding:2px 5px; border:1px solid var(--card-line); border-radius:4px; }
& .deploy-commit { display:grid; grid-template-columns:24px minmax(0,1fr) auto; gap:8px; align-items:center; padding:18px 15px 14px; }
& .deploy-commit > svg { width:22px; height:22px; fill:none; stroke:var(--card-muted); stroke-width:1.3; }
& .deploy-commit strong { font-size:12px; line-height:1.5; font-weight:550; overflow-wrap:anywhere; }
& .deploy-status { color:var(--_card-accent-ink); background:color-mix(in oklab,var(--card-accent) 8%,var(--card-panel)); font-size:9px; padding:4px 6px; border-radius:20px; white-space:nowrap; }
& .deploy-progress { margin:0 16px; height:2px; background:var(--card-line); overflow:hidden; }
& .deploy-progress > span { display:block; width:100%; height:100%; background:var(--card-accent); transform:scaleX(var(--build-progress,1)); transform-origin:left; }
& .deploy-steps { display:flex; justify-content:space-between; gap:8px; padding:9px 16px 15px; color:var(--card-muted); font-size:9px; line-height:1.5; }
& .deploy-steps > span[data-ready="true"] { color:var(--_card-accent-ink); }
& .deploy-result { display:grid; grid-template-columns:86px minmax(0,1fr); gap:15px; padding:16px; border-top:1px solid var(--card-line); align-items:center; opacity:calc(.2 + .8*var(--preview-ready,1)); transform:translateY(calc((1 - var(--preview-ready,1))*6px)); }
& .deploy-mini { height:82px; background:var(--card-wash); border:1px solid var(--card-line); border-radius:4px; padding:9px; }
& .deploy-mini > i { display:block; width:35%; height:3px; background:var(--card-muted); opacity:.35; }
& .deploy-mini > div { display:flex; gap:4px; margin:7px 0; }
& .deploy-mini b { width:50%; height:33px; border-radius:1px; background:color-mix(in oklab,var(--card-accent) 12%,var(--card-panel)); }
& .deploy-mini b:first-child { background:color-mix(in oklab,var(--card-accent) 32%,var(--card-panel)); }
& .deploy-mini > span { display:block; height:2px; width:80%; margin-top:4px; background:var(--card-line); }
& .deploy-result-copy { display:grid; gap:7px; min-width:0; }
& .deploy-result-copy small { color:var(--card-muted); font-size:9px; }
& .deploy-result-copy strong { font:11px/1.4 ui-monospace,monospace; overflow-wrap:anywhere; }
& .deploy-result-copy > span { color:var(--card-muted); font-size:10px; line-height:1.6; overflow-wrap:anywhere; }
& .deploy-history { display:flex; gap:18px; padding:18px 0 24px; }
& .deploy-history button { display:flex; gap:7px; align-items:center; padding:0; border:0; background:transparent; color:var(--card-muted); font-size:11px; cursor:pointer; }
& .deploy-history i { width:7px; height:7px; border:1px solid var(--card-muted); border-radius:50%; }
& .deploy-history button[aria-pressed="true"] { color:var(--card-ink); }
& .deploy-history button[aria-pressed="true"] i { background:var(--card-accent); border-color:var(--card-accent); }
}
/* Light, edge and contact shadow are authored together for each material. */
:where(.card,.showcase-card) {
--_card-shadow-contact:0 1px 2px #18253808,0 3px 6px -3px #18253812;
--_card-shadow-lifted:0 2px 5px -3px #18253812,0 10px 20px -12px #18253820;
--_card-shadow-glass:0 1px 3px #18253805,0 7px 16px -10px #18253814;
--_card-shadow-solid:0 1px 1px #18253809,0 4px 8px -5px #18253816;
--_card-material-shadow:0 0 #0000;
--_card-metal-base:color-mix(in oklab,var(--card-surface) 95%,var(--card-muted));
--_card-metal-band:linear-gradient(180deg,#ffffff66,transparent 36%,#17263b05);
--_card-metal-rim:inset 0 1px 0 #ffffff70,inset 0 -1px 0 #17223514;
--_card-glass-rim:inset 0 1px 0 #ffffff80,inset 1px 0 0 #ffffff29,inset 0 -1px 0 #35496d0d;
--_card-solid-rim:inset 0 1px 0 #ffffff80,inset 0 -1px 0 color-mix(in oklab,var(--card-ink) 11%,var(--card-surface));
--_card-finish-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='.025'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cpath filter='url(%23n)' d='M0 0h160v160H0z'/%3E%3C/svg%3E");
}
:where([data-theme="dark"]) :where(.card,.showcase-card),:where(.card,.showcase-card)[data-theme="dark"] {
--_card-shadow-contact:0 1px 2px #0003,0 3px 6px -3px #0004;
--_card-shadow-lifted:0 2px 5px -3px #0005,0 10px 20px -12px #0008;
--_card-shadow-glass:0 1px 3px #0002,0 7px 16px -10px #0006;
--_card-shadow-solid:0 1px 1px #0004,0 4px 8px -5px #0006;
--_card-metal-base:color-mix(in oklab,var(--card-surface) 93%,var(--card-muted));
--_card-metal-band:linear-gradient(180deg,#ffffff07,transparent 36%,#0000000a);
--_card-metal-rim:inset 0 1px 0 #ffffff1c,inset 0 -1px 0 #0003;
--_card-glass-rim:inset 0 1px 0 #ffffff22,inset 1px 0 0 #ffffff0b,inset 0 -1px 0 #0002;
--_card-solid-rim:inset 0 1px 0 #ffffff16,inset 0 -1px 0 #0005;
}
:is(.card,.showcase-card)[data-shadow="auto"] {--_card-outer-shadow:var(--_card-material-shadow);}
:is(.card,.showcase-card)[data-shadow="none"] {--_card-outer-shadow:0 0 #0000;}
:is(.card,.showcase-card)[data-shadow="contact"] {--_card-outer-shadow:var(--_card-shadow-contact);}
:is(.card,.showcase-card)[data-shadow="lifted"] {--_card-outer-shadow:var(--_card-shadow-lifted);}
.card,.showcase-card > .showcase-window {box-shadow:var(--_card-outer-shadow,var(--_card-material-shadow));}
.card-finish {display:none;position:absolute;inset:0;z-index:0;border-radius:inherit;pointer-events:none;user-select:none;box-shadow:var(--_card-material-rim,none);}
.card-finish::before {content:"";position:absolute;inset:0;border-radius:inherit;background-image:var(--_card-finish-art,none);background-size:var(--_card-finish-size,auto);opacity:var(--_card-material-reflection,1);}
:is(.card,.showcase-card)[data-material="metal"] {
--_card-material-rim:var(--_card-metal-rim);
--_card-material-shadow:var(--_card-shadow-contact);
& .card-finish {display:block;background-color:var(--_card-finish-base,var(--_card-metal-base));--_card-finish-art:var(--_card-metal-band);}
}
:is(.card,.showcase-card)[data-material="glass"] {
--_card-material-rim:var(--_card-glass-rim);
--_card-material-shadow:var(--_card-shadow-glass);
& .card-finish {display:block;--_card-finish-art:linear-gradient(126deg,#ffffff24,transparent 34% 65%,#ffffff0b);}
}
/* Keep a readable fallback when a host cannot blur the backdrop. */
.card[data-material="glass"],.showcase-card[data-material="glass"] > .showcase-window {background:color-mix(in oklab,var(--card-surface) 90%,transparent);}
@supports (backdrop-filter:blur(1px)) {
.card[data-material="glass"],.showcase-card[data-material="glass"] > .showcase-window {background:color-mix(in oklab,var(--card-surface) 62%,transparent);backdrop-filter:blur(18px) saturate(1.12);}
}
:is(.card,.showcase-card)[data-material="solid"] {
--_card-material-rim:var(--_card-solid-rim);
--_card-material-shadow:var(--_card-shadow-solid);
& .card-finish {display:block;background-color:var(--_card-finish-base,color-mix(in oklab,var(--card-surface) 98%,var(--card-accent)));--_card-finish-art:var(--_card-finish-noise),linear-gradient(160deg,#ffffff18,transparent 35%,#15203406);--_card-finish-size:160px 160px,100% 100%;}
}
</style></head>
<body>
<div class="gallery-page">
<header class="gallery-header"><a class="gallery-mark" href="#top" aria-label="回到顶部">oil<span> / cards</span></a><span class="gallery-edition">19 种模板 · 25 个实例</span></header>
<div class="gallery-intro" id="top"><h1>卡片探索。</h1><div class="gallery-controls" aria-label="外观控制">
<label class="attribute-control">内容 <select id="content-scene" aria-label="内容场景"><option value="studio">创作团队</option><option value="developer">开发工具</option></select></label>
<label class="attribute-control">外层效果 <select data-attribute="effects" aria-label="外层卡片效果"><option value="none">关闭</option><option value="tilt">倾斜</option><option value="glow">流光</option><option value="tilt glow">倾斜与流光</option></select></label>
<label class="attribute-control">材质 <select data-attribute="material" aria-label="卡片材质"><option value="plain">原始</option><option value="metal">金属</option><option value="glass">玻璃</option><option value="solid">实体卡</option></select></label>
<label class="attribute-control">阴影 <select data-attribute="shadow" aria-label="卡片阴影"><option value="auto">跟随材质</option><option value="none">无</option><option value="contact">轻贴合</option><option value="lifted">微悬浮</option></select></label>
<div class="theme-options" aria-label="选择主题"><button data-theme-choice="light" class="selected" aria-pressed="true">浅</button><button data-theme-choice="dark" aria-pressed="false">深</button></div>
<label class="attribute-control">标题 <select data-attribute="title" aria-label="标题位置"><option value="default">默认</option><option value="top">在上</option><option value="bottom">在下</option></select></label>
<label class="attribute-control">动效 <select data-attribute="motion" aria-label="动效模式"><option value="auto">自动</option><option value="hover">悬停</option><option value="off">关闭</option></select></label>
<button class="replay-motion" data-replay="all">重播演示</button>
<label class="radius-control">圆角 <input id="radius" type="range" min="0" max="24" value="8" aria-label="卡片圆角"><output for="radius">8</output></label>
</div></div>
<section class="palette-editor" aria-label="自定义配色">
<label class="attribute-control palette-mode">配色 <select id="palette-mode" aria-label="配色数量"><option value="single">单色</option><option value="duo">双色</option><option value="trio">三色</option></select></label>
<div class="palette-color" data-color-slot="0"><label for="color-primary">主色</label><input type="color" id="color-primary" aria-label="选择主色" value="#5865c7"><input class="palette-hex" id="hex-primary" aria-label="主色 HEX" value="#5865C7" maxlength="7" spellcheck="false" autocomplete="off"></div>
<div class="palette-color" data-color-slot="1" hidden><label for="color-secondary">辅色</label><input type="color" id="color-secondary" aria-label="选择辅色" value="#9aa5e8"><input class="palette-hex" id="hex-secondary" aria-label="辅色 HEX" value="#9AA5E8" maxlength="7" spellcheck="false" autocomplete="off"></div>
<div class="palette-color" data-color-slot="2" hidden><label for="color-tertiary">点缀</label><input type="color" id="color-tertiary" aria-label="选择点缀色" value="#d6c9b7"><input class="palette-hex" id="hex-tertiary" aria-label="点缀色 HEX" value="#D6C9B7" maxlength="7" spellcheck="false" autocomplete="off"></div>
<button class="palette-reset" type="button">重置配色</button>
<span class="palette-note">图形跟随配色,封面保留原色</span>
<span class="palette-feedback" role="status" aria-live="polite"></span>
</section>
<nav class="category-controls" aria-label="卡片分类">
<button type="button" data-family-filter="all" aria-pressed="true">全部 <span></span></button>
<button type="button" data-family-filter="visual" aria-pressed="false">视觉意象 <span></span></button>
<button type="button" data-family-filter="product" aria-pressed="false">产品场景 <span></span></button>
<button type="button" data-family-filter="data" aria-pressed="false">数据关系 <span></span></button>
<button type="button" data-family-filter="action" aria-pressed="false">行动引导 <span></span></button>
</nav>
<p class="category-summary" hidden></p>
<main>
<div class="peer-intro" id="peer-cards"><span>形态探索 / 影像 · 知识 · 系统</span><h2>从视觉形态,到产品细节。</h2><p>让取景、层次和每一次变化,解释产品能做什么。</p></div>
<section class="peer-grid" data-template="peer-cards" aria-label="新卡片探索">
<figure data-template="portrait-story" data-family="visual"><article class="card portrait-story" data-card="portrait-story" style="--card-accent:#5865c7">
<img class="story-photo" src="data:image/jpeg;base64,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
<div class="story-tint" aria-hidden="true"></div><div class="story-shade" aria-hidden="true"></div>
<h2 class="story-brand" data-text="brand">morrow / journal</h2>
<div class="story-copy"><blockquote data-text="quote">让工具退后一步,
把空间留给真正的创作。</blockquote><p><span data-text="author">创作手记</span><span data-text="role">关于工具与创作</span></p></div>
</article>
<figcaption><span>15 / portrait-story</span><span>影像叙事</span></figcaption></figure>
<figure data-template="capture-notes" data-family="product"><article class="card capture-notes" data-card="capture-notes" style="--card-accent:#5865c7">
<h2 class="card-heading" data-text="title">把每一次讨论,
留在同一份记录里。</h2>
<div class="card-stage"><div class="capture-sheet"><div class="capture-sheet-title"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M6 10v2a6 6 0 0 0 12 0v-2M12 18v3M8 21h8"/></svg><strong data-text="collection">团队讨论</strong></div><div class="capture-tabs"><span data-text="tab">全部记录</span><span data-text="scope">我的会议</span><i>+</i></div><div class="capture-labels"><span data-text="listLabel">会议主题</span><span data-text="tagLabel">团队</span></div><div class="capture-rows"></div></div>
<div class="capture-recorder"><span class="record-dot"></span><span class="capture-now"></span><span class="capture-wave" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span><button class="capture-pause" aria-label="暂停波形动画"><svg viewBox="0 0 20 20"><path d="M7 5v10M13 5v10"/></svg></button></div>
</div>
</article>
<figcaption><span>16 / capture-notes</span><span>讨论记录</span></figcaption></figure>
<figure data-template="answer-lens" data-family="product"><article class="card answer-lens" data-card="answer-lens" style="--card-accent:#5865c7">
<h2 class="card-heading" data-text="title">找到答案,
也看见它从哪里来。</h2>
<div class="card-stage"><div class="answer-scene"><div class="answer-dashboard"><span class="answer-dash-kicker" data-text="eyebrow">团队知识库</span><strong data-text="dashboard">本周项目概览</strong><span class="answer-sync" data-text="status">内容持续同步</span><div class="answer-dash-lines" aria-hidden="true"><i></i><i></i><i></i></div></div>
<div class="answer-charts" aria-hidden="true"><div class="answer-chart" data-chart="0"><span class="chart-symbol">◷</span><svg viewBox="0 0 160 150"><circle cx="80" cy="78" r="48" class="ring-base"/><circle cx="80" cy="78" r="48" class="ring-value"/></svg><span data-text="sources.0.label">项目进度</span></div><div class="answer-chart" data-chart="1"><span class="chart-symbol">▥</span><svg viewBox="0 0 160 150"><path class="chart-grid" d="M15 40h130M15 75h130M15 110h130"/><path class="chart-bars" d="M34 115V81M67 115V58M100 115V35M132 115V14"/></svg><span data-text="sources.1.label">内容表现</span></div><div class="answer-chart" data-chart="2"><span class="chart-symbol">⌁</span><svg viewBox="0 0 160 150"><path class="chart-grid" d="M15 40h130M15 75h130M15 110h130"/><path class="chart-line" d="M8 120C30 130 30 70 48 78S60 103 77 60s22 26 40-12 20-25 35-42"/></svg><span data-text="sources.2.label">用户反馈</span></div></div></div>
<div class="answer-response"><p data-text="question">这周,什么值得继续投入?</p><div class="answer-citations" aria-label="选择答案来源"></div><div class="answer-excerpt" aria-live="polite"></div><span class="answer-reference"></span></div></div>
</article>
<figcaption><span>17 / answer-lens</span><span>知识透镜</span></figcaption></figure>
<figure class="peer-wide" data-template="responsive-preview" data-family="product"><article class="card responsive-preview" data-card="responsive-preview" style="--card-accent:#5865c7">
<h2 class="card-heading" data-text="title">一份内容,
在不同屏幕上自然展开。</h2>
<div class="card-stage"><div class="size-scene"><div class="size-frame size-frame-0"><div class="size-frame-bar"><i></i><span data-text="labels.0">原始画面</span></div><svg viewBox="0 0 300 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true"><path class="preview-land-back" d="M0 98C54 85 56 30 118 42s76 75 182-10V180H0Z"/><path class="preview-land-front" d="M0 147C62 48 114 127 169 75s83 27 131 0V180H0Z"/><g class="preview-contours" fill="none"><path d="M-10 160C62 61 114 140 169 88s83 27 145-6M-10 174C62 75 114 154 169 102s83 27 145-6M-10 188C62 89 114 168 169 116s83 27 145-6M-10 202C62 103 114 182 169 130s83 27 145-6"/></g><circle cx="244" cy="37" r="14" class="preview-sun"/></svg></div><div class="size-frame size-frame-1"><div class="size-frame-bar"><i></i><span data-text="labels.1">宽屏</span></div><svg viewBox="0 0 300 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true"><path class="preview-land-back" d="M0 98C54 85 56 30 118 42s76 75 182-10V180H0Z"/><path class="preview-land-front" d="M0 147C62 48 114 127 169 75s83 27 131 0V180H0Z"/><g class="preview-contours" fill="none"><path d="M-10 160C62 61 114 140 169 88s83 27 145-6M-10 174C62 75 114 154 169 102s83 27 145-6M-10 188C62 89 114 168 169 116s83 27 145-6M-10 202C62 103 114 182 169 130s83 27 145-6"/></g><circle cx="244" cy="37" r="14" class="preview-sun"/></svg></div><div class="size-frame size-frame-2"><div class="size-frame-bar"><i></i><span data-text="labels.2">手机</span></div><svg viewBox="0 0 300 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true"><path class="preview-land-back" d="M0 98C54 85 56 30 118 42s76 75 182-10V180H0Z"/><path class="preview-land-front" d="M0 147C62 48 114 127 169 75s83 27 131 0V180H0Z"/><g class="preview-contours" fill="none"><path d="M-10 160C62 61 114 140 169 88s83 27 145-6M-10 174C62 75 114 154 169 102s83 27 145-6M-10 188C62 89 114 168 169 116s83 27 145-6M-10 202C62 103 114 182 169 130s83 27 145-6"/></g><circle cx="244" cy="37" r="14" class="preview-sun"/></svg></div></div><div class="size-toolbar"><span data-text="description">保留主体,重新安排取景。</span><button type="button" class="size-toggle" aria-pressed="true">收起尺寸 <span aria-hidden="true">↗</span></button></div></div>
</article>
<figcaption><span>18 / responsive-preview</span><button class="replay-motion" data-replay="responsive-preview">重播尺寸展开</button><span>响应式展开</span></figcaption></figure>
<figure class="peer-wide" data-template="deploy-preview" data-family="product"><article class="card deploy-preview" data-card="deploy-preview" style="--card-accent:#5865c7">
<h2 class="card-heading" data-text="title">每一次改动,
都能先预览,再分享。</h2>
<div class="card-stage"><div class="deploy-window"><div class="deploy-window-bar"><span data-text="project">workspace / website</span><span class="deploy-branch" data-release-field="branch">design/home</span></div><div class="deploy-commit"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="7" cy="5" r="2"/><circle cx="7" cy="19" r="2"/><circle cx="18" cy="5" r="2"/><path d="M7 7v10m11-10c0 6-11 1-11 10"/></svg><strong data-release-field="title">更新作品首页</strong><span class="deploy-status" aria-live="polite"></span></div><div class="deploy-progress"><span></span></div><div class="deploy-steps"><span data-text="steps.0">准备资源</span><span data-text="steps.1">完成构建</span><span data-text="steps.2">生成预览</span></div><div class="deploy-result"><div class="deploy-mini" aria-hidden="true"><i></i><div><b></b><b></b></div><span></span><span></span></div><div class="deploy-result-copy"><small data-text="resultLabel">预览地址</small><strong data-release-field="url">preview.workspace.local</strong><span data-text="resultNote">把这一版,分享给一起创作的人。</span></div></div></div>
<div class="deploy-history" aria-label="选择预览版本"><button type="button" data-version="0" aria-pressed="true"><i></i><span data-text="versionLabels.0">当前版本</span></button><button type="button" data-version="1" aria-pressed="false"><i></i><span data-text="versionLabels.1">上一版本</span></button></div></div>
</article>
<figcaption><span>19 / deploy-preview</span><button class="replay-motion" data-replay="deploy-preview">重播构建过程</button><span>构建流水线</span></figcaption></figure>
</section>
<section class="signature-grid" aria-label="主视觉卡片">
<figure data-template="particle-ring" data-family="visual"><article class="card portrait particle-ring" data-card="particle-ring" data-motion="auto">
<h2 class="card-heading" data-text="title">从一句想法,<br>到一件心仪好物</h2>
<div class="card-stage">
<div class="ring-mist"></div><canvas class="ring-canvas" aria-hidden="true"></canvas>
<div class="shop-conversation">
<p class="shop-message customer" data-text="prompt">周末去海边,想穿得舒服一点。<br>有什么适合我的推荐?</p>
<p class="shop-message assistant" data-text="reply">轻盈、好搭,也能应对海风。<br>这两件刚刚好。</p>
<div class="shop-products">
<div class="shop-product"><div class="garment"><svg viewBox="0 0 100 100" aria-label="海蓝棉质T恤"><path d="M34 15 21 20 8 41 23 50 29 41 29 86Q50 91 71 86L71 41 77 50 92 41 79 20 66 15Q50 26 34 15Z" fill="#096bc7"/><path d="M34 15Q50 35 66 15L63 17Q50 28 37 17Z" fill="#0357a8"/><path d="M30 43 35 39 37 84 30 86Z" fill="#0056aa" opacity=".6"/></svg></div><strong data-text="items.0.name">海岸棉质短袖</strong><span data-text="items.0.detail">海蓝 · M</span><b data-text="items.0.value">¥ 129</b></div>
<div class="shop-product"><div class="garment"><svg viewBox="0 0 100 100" aria-label="藏青连帽衫"><path d="M34 15Q50 4 66 15L69 27 81 32 90 82 76 87 69 51 70 90Q50 94 30 90L31 51 24 87 10 82 19 32 31 27Z" fill="#142746"/><path d="M34 15Q28 40 50 43Q72 40 66 15L61 18Q50 12 39 18Z" fill="#0a172d"/><path d="M37 19Q50 34 63 19M50 43v13" fill="none" stroke="#354b6c" stroke-width="2"/><path d="M34 48 37 87 45 88 41 48Z" fill="#2e4261" opacity=".8"/><path d="M36 68Q50 62 64 68L61 80H39Z" fill="#102039"/></svg></div><strong data-text="items.1.name">轻风连帽外套</strong><span data-text="items.1.detail">藏青 · M</span><b data-text="items.1.value">¥ 249</b></div>
<div class="shop-cta"><span data-text="action">一起带去海边</span> <span>↗</span></div>
</div>
</div>
</div>
</article>
<figcaption><span>01 / particle-ring</span><span>光场与内容</span></figcaption></figure>
<figure data-template="flow-object" data-family="visual"><article class="card portrait flow-object" data-card="flow-object" data-motion="auto">
<h2 class="card-heading" data-text="title">让数字身份,<br>拥有可以触碰的形态</h2>
<div class="card-stage">
<div class="flow-aura" aria-hidden="true"></div>
<div class="physical-card">
<canvas class="flow-surface" aria-hidden="true"></canvas>
<span class="card-edition" data-text="edition">IDENTITY / 01</span>
<div class="token-engraving">
<div class="token-status"><i class="token-led" aria-hidden="true"></i><span data-text="status">VERIFIED</span></div>
<span class="token-hash-label" data-text="hashLabel">SIGNATURE</span>
<strong class="token-hash" data-text="hash">7F2A:9C04</strong>
<div class="token-specs"><div><span data-text="specs.0.label">PROTOCOL</span><b data-text="specs.0.value">OPEN / 01</b></div><div><span data-text="specs.1.label">SCOPE</span><b data-text="specs.1.value">WORKSPACE</b></div><div><span data-text="specs.2.label">REVISION</span><b data-text="specs.2.value">2026.10</b></div></div>
<div class="token-registration" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
</div>
<svg class="card-white" viewBox="0 0 300 470" preserveAspectRatio="none" aria-hidden="true"><path d="M0 344Q160 386 300 324V470H0Z" fill="white"/></svg>
<span class="physical-wordmark" data-text="brand">identity</span>
<span class="physical-number" data-text="identifier">ID / 0842</span>
</div>
</div>
</article>
<figcaption><span>02 / flow-object</span><span class="renderer-options" aria-label="实体通证形态"><button data-flow-variant="token" aria-pressed="true">实体铭牌</button><button data-flow-variant="card" aria-pressed="false">流光卡片</button></span></figcaption></figure>
<figure data-template="dot-globe" data-family="data"><article class="card portrait dot-globe" data-card="dot-globe" data-motion="auto" data-arcs="2">
<h2 class="card-heading" data-text="title">让每一份连接,<br>跨越山海</h2>
<div class="card-stage"><canvas class="globe-wash" aria-hidden="true"></canvas><canvas class="globe-points" aria-hidden="true"></canvas><canvas class="globe-arcs" aria-hidden="true"></canvas><span class="globe-label" data-text="label">节点 · 已连接</span></div>
</article>
<figcaption><span>03 / dot-globe</span><span class="renderer-options" aria-label="空间拓扑形态"><button data-globe-mode="globe" aria-pressed="true">点阵地球</button><button data-globe-mode="mesh" aria-pressed="false">星座拓扑</button></span></figcaption></figure>
</section>
<figure id="line-ribbons" class="ribbons-figure" data-template="line-ribbons" data-family="visual"><article class="card banner line-ribbons" data-card="line-ribbons" data-motion="auto">
<h2 class="card-heading" data-text="title">各自精彩,<br>也能一起向前</h2>
<div class="card-stage"><div class="ribbon-mist"></div><svg class="ribbon-art" viewBox="0 0 1280 474" preserveAspectRatio="xMidYMid slice" aria-hidden="true"></svg></div>
<p class="ribbon-note" data-text="description">让灵感汇合,让协作发生。</p>
</article>
<figcaption><span>04 / line-ribbons</span><span class="renderer-options" aria-label="比较丝带材质"><button data-ribbon-renderer="svg" aria-pressed="true">线织</button><button data-ribbon-renderer="webgl" aria-pressed="false">薄绸</button><button data-ribbon-renderer="dots" aria-pressed="false">点阵</button><button data-ribbon-renderer="grid" aria-pressed="false">网格</button></span></figcaption></figure>
<figure id="promo-band" class="promo-figure" data-template="promo-band" data-family="action"><div class="promo-grid">
<a class="card promo-band" data-card="promo-band" data-art="prism" data-example="promo-prism" data-href="href" data-motion="auto" href="#showcase">
<div class="promo-copy"><p><strong data-text="title">创作者计划。</strong> <span data-text="description">让灵感获得支持,与志同道合的人一起,把好想法变成作品。</span></p><span class="promo-link"><span data-text="action">探索计划</span> <span aria-hidden="true">→</span></span></div>
<div class="promo-art" aria-hidden="true"></div>
</a>
<a class="card promo-band" data-card="promo-band" data-art="facets" data-example="promo-facets" data-href="href" data-motion="auto" href="#showcase">
<div class="promo-copy"><p><strong data-text="title">从这里出发。</strong> <span data-text="description">把创作所需的工具和资源放在一起,专注完成你的下一件作品。</span></p><span class="promo-link"><span data-text="action">开始创作</span> <span aria-hidden="true">→</span></span></div>
<div class="promo-art" aria-hidden="true"><svg viewBox="0 0 400 280" preserveAspectRatio="none"><path class="facet-back" d="M135 0H400V280H0Z"/><path class="facet-top" d="M135 0H400V76L280 280Z"/><path class="facet-front" d="M110 50 280 280H0Z"/><path class="facet-side" d="M400 76V280H280Z"/></svg></div>
</a>
</div>
<figcaption><span>11 / promo-band</span><span>光谱 / 折面</span></figcaption></figure>
<section id="explorations" class="exploration-grid" data-template="explorations" aria-label="更多产品表达">
<figure data-template="vector-space" data-family="data"><article class="card vector-space" data-card="vector-space" data-motion="auto">
<h2 class="card-heading" data-text="title">从海量信息中,<br>找到彼此的关联</h2>
<div class="card-stage">
<div class="vector-query"><svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="8" cy="8" r="5"/><path d="m12 12 5 5"/></svg><span data-text="query">自然的灵感</span></div>
<svg class="vector-fallback" viewBox="0 0 400 300" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-width=".8" opacity=".3"><path d="m200 55 107 58v122l-107 58-107-58V113Z M93 113l107 58 107-58 M200 171v122 M200 55v116"/></g><g fill="var(--card-accent)"><circle cx="195" cy="179" r="5"/><circle cx="228" cy="161" r="5"/><circle cx="212" cy="197" r="5"/><circle cx="250" cy="185" r="5"/></g></svg>
<canvas class="vector-canvas" aria-hidden="true"></canvas>
<div class="vector-result"><i></i><span data-text="result">4 个相关结果</span><span class="vector-tag" data-text="mode">语义搜索</span></div>
</div>
</article>
<figcaption><span>12 / vector-space</span><span>语义空间</span></figcaption></figure>
<figure data-template="file-mosaic" data-family="product"><article class="card file-mosaic" data-card="file-mosaic" data-motion="auto">
<h2 class="card-heading" data-text="title">每一份素材,<br>都有合适的位置</h2>
<div class="card-stage"><div class="file-collection"><span data-text="collection">品牌素材</span><span><span data-text="status">所有文件已同步</span> <i>✓</i></span></div><div class="file-viewport"><div class="file-grid" aria-hidden="true"></div></div></div>
</article>
<figcaption><span>13 / file-mosaic</span><span>资源阵列</span></figcaption></figure>
<figure data-template="stream-window" data-family="product"><article class="card stream-window" data-card="stream-window" data-motion="auto">
<h2 class="card-heading" data-text="title">不必等待全部,<br>先让重要的出现</h2>
<div class="card-stage">
<div class="stream-status"><i></i><span>内容陆续就绪</span><span class="stream-count">3 / 3</span></div>
<div class="stream-viewport"><div class="stream-browser"><div class="stream-toolbar"><span></span><span></span><span></span><i data-text="path">studio / overview</i></div>
<div class="stream-sidebar"><b></b><i></i><i></i><i></i><i></i></div>
<div class="stream-content">
<div class="stream-chunk stream-summary"><div class="chunk-skeleton"><i></i><b></b></div><div class="chunk-content"><small data-text="eyebrow">你的创作空间</small><strong data-text="heading">让想法,开始生长。</strong><p data-text="summary">继续上一件作品,或从新的灵感出发。</p></div></div>
<div class="stream-panels"><div class="stream-chunk stream-visual"><div class="chunk-skeleton"><b></b></div><div class="chunk-content"><div class="stream-orbit"><i></i><i></i><i></i></div><span data-text="visualLabel">探索灵感</span></div></div><div class="stream-chunk stream-list"><div class="chunk-skeleton"><i></i><i></i><i></i></div><div class="chunk-content"><small data-text="listTitle">最近的作品</small><p><i></i><span data-text="items.0">晨间随想</span></p><p><i></i><span data-text="items.1">无界之境</span></p><p><i></i><span data-text="items.2">光的形状</span></p></div></div></div>
</div>
</div></div>
</div>
</article>
<figcaption><span>14 / stream-window</span><button class="replay-motion" data-replay="stream-window">重播渐进呈现</button></figcaption></figure>
</section>
<section class="utility-grid" aria-label="产品体验卡片">
<figure data-template="ui-slice" data-family="product"><article class="card bottom-title ui-slice" data-card="ui-slice" data-motion="auto">
<div class="card-stage">
<div class="slice-back"><div class="slice-toolbar"><span data-text="board">本周计划</span> <span>⋯</span></div><div class="slice-columns"><div><span data-text="columns.0">待开始</span><p data-text="backlog.0">整理首页内容</p><p data-text="backlog.1">确认品牌素材</p></div><div><span data-text="columns.1">进行中</span><p data-text="backlog.2">打磨购买体验</p><p data-text="backlog.3">移动端检查</p></div></div></div>
<div class="slice-front"><div class="slice-front-heading"><span class="slice-symbol">◈</span> <span data-text="listTitle">上线前的最后一公里</span> <span class="slice-more">⋯</span></div><p class="slice-task done"><i>✓</i><span data-text="tasks.0.title">确认首页文案</span><b data-text="tasks.0.owner">林</b></p><p class="slice-task slice-demo"><i></i><span data-text="tasks.1.title">检查移动端体验</span><b data-text="tasks.1.owner">周</b></p><p class="slice-task"><i></i><span data-text="tasks.2.title">准备发布素材</span><b data-text="tasks.2.owner">陈</b></p><div class="slice-progress"><span data-text="progressLabel">发布清单</span><span class="slice-count">1 / 3</span><div><i></i></div></div></div>
</div>
<div class="card-caption"><h2 data-text="title">让好想法,稳稳落地</h2><p data-text="description">从计划到完成,团队始终在同一页。</p></div>
</article>
<figcaption><span>06 / ui-slice</span><button class="replay-motion" data-replay="ui-slice">重播动效</button></figcaption></figure>
<figure data-template="terminal" data-family="product"><article class="card bottom-title terminal" data-card="terminal" data-motion="auto">
<div class="card-stage"><div class="terminal-window"><div class="terminal-toolbar"><span></span><span></span><span></span><em data-text="path">~/morrow</em><b data-text="shell">zsh</b></div><div class="terminal-lines"><p><span class="terminal-prompt">›</span> aura deploy <span class="terminal-flag">--production</span></p><p>读取项目配置 <span class="terminal-dim">aura.config</span></p><p class="terminal-highlight"><span>✓</span> 资源构建完成 <span class="terminal-dim">1.8s</span></p><p><span>✓</span> 连接全球边缘节点</p><p class="terminal-result"><span>✓</span> 已发布,下一站是世界。</p><p class="terminal-address">morrow.aura.local <span class="terminal-cursor">▎</span></p></div></div></div>
<div class="card-caption"><h2 data-text="title">从这里,发布到世界</h2><p data-text="description">一个命令,让每一份创造被看见。</p></div>
</article>
<figcaption><span>07 / terminal</span><button class="replay-motion" data-replay="terminal">重播动效</button></figcaption></figure>
<figure data-template="beam-graph" data-family="data"><article class="card beam-graph bottom-title" data-card="beam-graph" data-motion="auto">
<div class="card-stage"><svg class="beam-lines" viewBox="0 0 400 250" preserveAspectRatio="none" aria-hidden="true"><defs></defs><g class="beam-base"><path d="M72 50H135Q147 50 147 62V113Q147 125 159 125H200"/><path d="M72 125H200"/><path d="M72 200H135Q147 200 147 188V137Q147 125 159 125H200"/><path d="M200 125H255Q267 125 267 113V92Q267 80 279 80H328"/><path d="M200 125H255Q267 125 267 137V158Q267 170 279 170H328"/></g><g class="beam-glow"></g><g class="beam-light"></g></svg><span class="beam-node beam-a"><i></i><span data-text="nodes.0">灵感</span></span><span class="beam-node beam-b"><i></i><span data-text="nodes.1">内容</span></span><span class="beam-node beam-e"><i></i><span data-text="nodes.2">团队</span></span><span class="beam-node beam-center" data-text="hub">morrow</span><span class="beam-node beam-c"><i></i><span data-text="nodes.3">产品</span></span><span class="beam-node beam-d"><i></i><span data-text="nodes.4">世界</span></span></div>
<div class="card-caption"><h2 data-text="title">每一份创造,都能相互连接</h2><p data-text="description">把零散的工具,连成顺畅的工作流。</p></div>
</article>
<figcaption><span>09 / beam-graph</span><span>节点与路径</span></figcaption></figure>
<figure data-template="keycaps" data-family="action"><article class="card bottom-title keycaps" data-card="keycaps" data-motion="auto">
<div class="card-stage"><div class="keycap-row"><div class="keycap key-mod"><div class="keycap-top"><span data-text="modifier">⌘</span><small data-text="modifierName">command</small></div></div><span class="key-plus">+</span><div class="keycap key-letter"><div class="keycap-top"><span data-text="key">K</span><small data-text="keyName">quick action</small></div></div></div><div class="command-panel"><span>⌕</span><span data-text="placeholder">搜索命令或项目…</span><kbd>Esc</kbd></div></div>
<div class="card-caption"><h2 data-text="title">快一步,回到心流</h2><p data-text="description">常用动作,始终触手可及。</p></div>
</article>
<figcaption><span>10 / keycaps</span><button class="replay-motion" data-replay="keycaps">重播动效</button></figcaption></figure>
</section>
<figure class="metric-figure" data-template="metric" data-family="data"><article class="card strip metric" data-card="metric" data-motion="auto">
<div class="metric-copy"><h2 data-text="title">每一步增长,都有迹可循</h2><p data-text="description">过去 30 天 · 工作流运行次数</p></div>
<div class="metric-number"><span class="metric-value">24,860</span><span class="metric-delta" data-text="delta">↗ 18.6%</span></div>
<div class="metric-chart"><span class="metric-today" data-text="latest">今日 1,284</span><div class="metric-bars"><i class="" style="--height:12%"></i><i class="" style="--height:18%"></i><i class="" style="--height:15%"></i><i class="" style="--height:24%"></i><i class="" style="--height:29%"></i><i class="" style="--height:22%"></i><i class="" style="--height:34%"></i><i class="" style="--height:32%"></i><i class="" style="--height:41%"></i><i class="" style="--height:45%"></i><i class="" style="--height:38%"></i><i class="" style="--height:49%"></i><i class="" style="--height:46%"></i><i class="" style="--height:59%"></i><i class="" style="--height:56%"></i><i class="" style="--height:66%"></i><i class="" style="--height:61%"></i><i class="" style="--height:74%"></i><i class="recent" style="--height:70%"></i><i class="recent" style="--height:81%"></i><i class="recent" style="--height:77%"></i><i class="recent" style="--height:87%"></i><i class="recent" style="--height:83%"></i><i class="current" style="--height:96%"></i></div><div class="metric-axis"><span>09.06</span><span>10.05</span></div></div>
</article>
<figcaption><span>08 / metric</span><button class="replay-motion" data-replay="metric">重播动效</button></figcaption></figure>
<div class="showcase-intro"><span>05 / SHOWCASE</span><h2>每个品牌,<br>都有自己的光。</h2><p>从一抹颜色开始,找到属于你的表达。</p><div class="carousel-navigation"><button data-slide="-1" aria-label="向前浏览">←</button><button data-slide="1" aria-label="向后浏览">→</button></div></div>
<section class="showcase-section" id="showcase" data-template="showcase" data-family="visual"><div class="showcase-row" aria-label="品牌视觉展示">
<article class="showcase-card" data-card="showcase" data-example="morrow" data-motion="auto" data-background="editorial"><div class="showcase-window"><div class="showcase-art"><img data-image="image" alt="珊瑚色与紫色的发光粒子,汇成四枚环绕的种子" src="data:image/jpeg;base64,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
<article class="showcase-card" data-card="showcase" data-example="duotone" data-motion="auto" data-background="editorial"><div class="showcase-window"><div class="showcase-art"><img data-image="image" alt="蓝色卷发的创作者抬头望向光,暖橙与青蓝交织" src="data:image/jpeg;base64,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
<article class="showcase-card" data-card="showcase" data-example="serein" data-motion="auto" data-background="editorial"><div class="showcase-window"><div class="showcase-art"><img data-image="image" alt="深青绿色的水流,在银白浪纹中旋转" src="data:image/jpeg;base64,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
<article class="showcase-card" data-card="showcase" data-example="folded" data-motion="auto" data-background="editorial"><div class="showcase-window"><div class="showcase-art"><img data-image="image" class="showcase-cropped" alt="薄荷绿的折叠门形结构,悬浮在深色网格上" src="data:image/jpeg;base64,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
<article class="showcase-card" data-card="showcase" data-example="cubiq" data-motion="auto" data-background="iso-object"><div class="showcase-window"><div class="showcase-art"></div><span class="showcase-wordmark" data-text="brand">cubiq</span></div><div class="showcase-caption"><p data-text="description">好想法,也可以有触手可及的形状。</p><a class="showcase-link" data-href="href" href="#showcase"><span data-text="action">走近 cubiq</span> <span>↗</span></a></div><small>iso-object · 通用身份参数</small></article>
<article class="showcase-card" data-card="showcase" data-example="vellum" data-motion="auto" data-background="membrane"><div class="showcase-window"><div class="showcase-art"></div><span class="showcase-wordmark" data-text="brand">vellum</span></div><div class="showcase-caption"><p data-text="description">轻一些,留出想象的空间。</p><a class="showcase-link" data-href="href" href="#showcase"><span data-text="action">走近 vellum</span> <span>↗</span></a></div><small>membrane · 通用身份参数</small></article>
</div>
</section>
</main>
<footer class="gallery-footer"><span>oil cards / 第一辑</span><span>为有想法的产品,留一方舞台。</span></footer>
</div>
<script>(() => {
const modules = {};
modules["gallery-palette.js"] = (() => {
// The editor owns its committed palette; incomplete HEX input is only a draft.
function createPaletteEditor({query, onChange}) {
const root=document.querySelector('.palette-editor'),modeControl=root.querySelector('#palette-mode');
const slots=[...root.querySelectorAll('[data-color-slot]')],feedback=root.querySelector('.palette-feedback');
const defaults={mode:'single',colors:['#5865c7','#9aa5e8','#d6c9b7']},key='oil.cards.palette.v1';
const modes={single:1,duo:2,trio:3};
const hex=value=>{
if(typeof value!=='string')return null;
const raw=value.trim().replace(/^#/,'');
if(/^[a-f\d]{3}$/i.test(raw))return '#'+[...raw].map(c=>c+c).join('').toLowerCase();
return /^[a-f\d]{6}$/i.test(raw)?'#'+raw.toLowerCase():null;
};
let state={mode:defaults.mode,colors:[...defaults.colors]},frame=0;
try {const saved=JSON.parse(localStorage.getItem(key));if(Object.hasOwn(modes,saved?.mode)&&Array.isArray(saved.colors)&&saved.colors.length===3&&saved.colors.every(hex))state={mode:saved.mode,colors:saved.colors.map(hex)};}catch{}
// Existing screenshot URLs remain valid; explicit URL colors override saved input.
const legacy={dawn:['#7a5cff','#ff4f8b','#ff9a4a'],sage:['#4c9a94','#81bca2','#c7d5aa'],mono:['#44454c','#929399','#dedfe2'],clay:['#ac715d','#cf9d85','#e6caae']};
const requestedPreset=query.get('preset')||query.get('state');
if(Object.hasOwn(legacy,requestedPreset))state={mode:'trio',colors:[...legacy[requestedPreset]]};
const requested=query.get('colors')?.split(',');
if(requested?.length>=1&&requested.length<=3&&requested.every(hex))state={mode:['single','duo','trio'][requested.length-1],colors:[...requested.map(hex),...defaults.colors.slice(requested.length)]};
const controller=new AbortController(),listen=(el,event,fn)=>el.addEventListener(event,fn,{signal:controller.signal});
const save=()=>{try{localStorage.setItem(key,JSON.stringify(state));}catch{}};
const render=()=>{
modeControl.value=state.mode;
slots.forEach((slot,i)=>{slot.hidden=i>=modes[state.mode];slot.querySelector('input[type=color]').value=state.colors[i];const text=slot.querySelector('.palette-hex');text.value=state.colors[i].toUpperCase();text.removeAttribute('aria-invalid');});
feedback.textContent='';
};
const refresh=()=>{save();if(!frame)frame=requestAnimationFrame(()=>{frame=0;onChange();});};
const showErrors=()=>{
const invalid=slots.some((slot,i)=>i<modes[state.mode]&&slot.querySelector('.palette-hex').getAttribute('aria-invalid')==='true');
feedback.textContent=invalid?'请输入 3 或 6 位 HEX 色值。':'';
};
slots.forEach((slot,i)=>{
const color=slot.querySelector('input[type=color]'),text=slot.querySelector('.palette-hex');
listen(color,'input',()=>{state.colors[i]=color.value;text.value=color.value.toUpperCase();text.removeAttribute('aria-invalid');showErrors();refresh();});
listen(text,'input',()=>{const value=hex(text.value);text.setAttribute('aria-invalid',String(!value));if(value){state.colors[i]=value;color.value=value;refresh();}showErrors();});
listen(text,'blur',()=>{const value=hex(text.value);if(value)text.value=value.toUpperCase();});
listen(text,'keydown',e=>{if(e.key==='Escape'){text.value=state.colors[i].toUpperCase();text.removeAttribute('aria-invalid');showErrors();}if(e.key==='Enter')text.blur();});
});
listen(modeControl,'change',()=>{state.mode=modeControl.value;render();refresh();});
listen(root.querySelector('.palette-reset'),'click',()=>{state={mode:defaults.mode,colors:[...defaults.colors]};render();refresh();});
render();
return {
get colors(){const [a,b,c]=state.colors;return state.mode==='single'?[a,a,a]:state.mode==='duo'?[a,b,b]:[a,b,c];},
destroy(){controller.abort();cancelAnimationFrame(frame);}
};
}
return {createPaletteEditor};
})();
modules["gallery-catalog.js"] = (() => {
function createCatalog(query) {
const descriptions={
all:'',
visual:'用光场、曲面和影像建立品牌印象。先选视觉语言,再替换品牌与内容。',
product:'把真实产品的一小段体验放大呈现:任务、文件、运行输出与内容就绪。',
data:'表达数量、范围和连接关系。数据可以替换,图形应保持清楚的阅读顺序。',
action:'引导一个清楚的下一步:快捷操作、资源入口或推广行动。'
};
const items=[...document.querySelectorAll('main [data-family]')],buttons=[...document.querySelectorAll('[data-family-filter]')],summary=document.querySelector('.category-summary');
const groups=[...document.querySelectorAll('.signature-grid,.exploration-grid,.utility-grid,.peer-grid')];
const controller=new AbortController();
buttons.forEach(button=>{const family=button.dataset.familyFilter;button.querySelector('span').textContent=String(items.filter(item=>family==='all'||item.dataset.family===family).length);});
function select(family){
document.querySelector('main').dataset.category=family;
items.forEach(item=>{item.hidden=family!=='all'&&item.dataset.family!==family;});
groups.forEach(group=>{group.hidden=![...group.querySelectorAll('[data-family]')].some(item=>!item.hidden);});
document.querySelector('.peer-intro').hidden=document.querySelector('.peer-grid').hidden;
document.querySelector('.showcase-intro').hidden=document.querySelector('.showcase-section').hidden;
buttons.forEach(button=>button.setAttribute('aria-pressed',String(button.dataset.familyFilter===family)));
summary.textContent=descriptions[family];summary.hidden=!summary.textContent;
}
buttons.forEach(button=>button.addEventListener('click',()=>select(button.dataset.familyFilter),{signal:controller.signal}));
const requested=query.get('category');select(!query.has('focus')&&Object.hasOwn(descriptions,requested)?requested:'all');
return {destroy(){controller.abort();}};
}
return {createCatalog};
})();
modules["shared/content.js"] = (() => {
// Content values are plain text. A line break is content, not injected markup.
function contentValue(content, path) {
return path.split('.').reduce((value,key)=>value!=null && Object.hasOwn(value,key)?value[key]:undefined,content);
}
function url(value, image=false) {
if(typeof value!=='string' || !value.trim())return null;
try {
const protocol=new URL(value,document.baseURI).protocol;
if(['http:','https:','file:'].includes(protocol) || (!image && ['mailto:','tel:'].includes(protocol)) || (image && (protocol==='blob:' || /^data:image\/(png|jpeg|webp|gif|svg\+xml)[;,]/i.test(value))))return value;
} catch {}
return null;
}
function applyContent(element, content={}) {
if(!content || typeof content!=='object')return;
for(const node of element.querySelectorAll('[data-text]')) {
const value=contentValue(content,node.dataset.text);
if(value!==undefined && value!==null)node.textContent=Array.isArray(value)?value.join('\n'):String(value);
}
for(const node of [element,...element.querySelectorAll('[data-href]')]) {
if(!node.hasAttribute('data-href'))continue;
const value=contentValue(content,node.dataset.href);
if(value===undefined)continue;
const href=url(value);if(href)node.setAttribute('href',href);else node.removeAttribute('href');
}
for(const node of element.querySelectorAll('[data-image]')) {
const value=contentValue(content,node.dataset.image);
if(!value)continue;
const src=url(typeof value==='string'?value:value.src,true);
if(src){node.src=src;if(typeof value==='object' && value.alt!==undefined)node.alt=String(value.alt);}
}
}
return {contentValue, applyContent};
})();
modules["shared/runtime.js"] = (() => {
const {applyContent} = modules["shared/content.js"];
const TAU = Math.PI * 2;
const clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));
const mix = (a, b, t) => a + (b - a) * t;
const smooth = t => { t=clamp(t);return t*t*t*(t*(t*6-15)+10); };
function staticMotion(element, options={}) {
const media=matchMedia('(prefers-reduced-motion: reduce)');
return ()=>Number(options.speed)===0 || media.matches || element.closest('[data-motion]')?.dataset.motion==='off';
}
function random(seed = 17) {
return () => { seed |= 0; seed = seed + 0x6D2B79F5 | 0; let t = Math.imul(seed ^ seed >>> 15, 1 | seed); t ^= t + Math.imul(t ^ t >>> 7, 61 | t); return ((t ^ t >>> 14) >>> 0) / 4294967296; };
}
/* Original triangular-lattice 2D simplex implementation, deterministic gradients. */
function simplex(seed = 43) {
const rng = random(seed), p = Array.from({length: 256}, (_, i) => i);
for (let i = 255; i; i--) { const j = Math.floor(rng() * (i + 1)); [p[i], p[j]] = [p[j], p[i]]; }
const perm = [...p, ...p], grads = Array.from({length: 8}, (_, i) => [Math.cos(i * TAU / 8), Math.sin(i * TAU / 8)]);
return (x, y) => {
const s = (x + y) * .3660254038, i = Math.floor(x + s), j = Math.floor(y + s), u = (i + j) * .2113248654;
const x0 = x - i + u, y0 = y - j + u, a = x0 > y0 ? 1 : 0, b = 1 - a;
const xs = [x0, x0 - a + .2113248654, x0 - .5773502692], ys = [y0, y0 - b + .2113248654, y0 - .5773502692];
let n = 0;
for (let k = 0; k < 3; k++) {
const ii = i + (k === 1 ? a : k), jj = j + (k === 1 ? b : k), g = grads[perm[(ii & 255) + perm[jj & 255]] & 7];
const h = Math.max(0, .5 - xs[k] * xs[k] - ys[k] * ys[k]);
n += h ** 4 * (xs[k] * g[0] + ys[k] * g[1]);
}
return n * 70;
};
}
function colors(element) {
const cs = getComputedStyle(element), c = document.createElement('canvas'); c.width = c.height = 1;
const ctx = c.getContext('2d', {willReadFrequently: true});
const read = name => {
ctx.clearRect(0, 0, 1, 1); ctx.fillStyle = cs.getPropertyValue(name).trim(); ctx.fillRect(0, 0, 1, 1);
return [...ctx.getImageData(0, 0, 1, 1).data].slice(0, 3).map(n => n / 255);
};
return {a: read('--card-accent'), b: read('--_card-accent-2'), c: read('--_card-accent-3'), surface: read('--card-surface'), dark: cs.getPropertyValue('--card-surface').trim() === '#14171e'};
}
const rgb = (c, alpha = 1) => `rgba(${c.map(n => Math.round(n * 255)).join(',')},${alpha})`;
function mixColor(a, b, t) {
const linear = c => c.map(n => n <= .04045 ? n / 12.92 : ((n + .055) / 1.055) ** 2.4);
const lab = c => {const [r,g,b]=linear(c),l=Math.cbrt(.412221*r+.536333*g+.051446*b),m=Math.cbrt(.211903*r+.680700*g+.107397*b),s=Math.cbrt(.088302*r+.281719*g+.629979*b);return [.210454*l+.793618*m-.004072*s,1.977998*l-2.428592*m+.450594*s,.025904*l+.782772*m-.808676*s];};
const A=lab(a),B=lab(b),[L,x,y]=A.map((n,i)=>mix(n,B[i],t)),l=(L+.396338*x+.215804*y)**3,m=(L-.105561*x-.063854*y)**3,s=(L-.089484*x-1.291486*y)**3;
return [4.076742*l-3.307711*m+.230970*s,-1.268438*l+2.609757*m-.341319*s,-.004196*l-.703419*m+1.707615*s].map(n=>clamp(n<=.0031308?12.92*n:1.055*Math.max(0,n)**(1/2.4)-.055));
}
/* SVG has no OKLab interpolation mode. Dense OKLab stops preserve the palette. */
function svgStops(stops) {
return stops.flatMap(([x,c,a=1],i)=>{
if(i===stops.length-1)return [`<stop offset="${x}" stop-color="${c}" stop-opacity="${a}"/>`];
const [nx,nc,na=1]=stops[i+1];
return Array.from({length:8},(_,j)=>{const t=j/8,color=j?`color-mix(in oklab,${c} ${(1-t)*100}%,${nc})`:c;return `<stop offset="${x+(nx-x)*t}" stop-color="${color}" stop-opacity="${a+(na-a)*t}"/>`;});
}).join('');
}
const glColor = `
vec3 lab(vec3 c){c=mix(c/12.92,pow((c+.055)/1.055,vec3(2.4)),step(vec3(.04045),c));vec3 lms=vec3(dot(c,vec3(.412221,.536333,.051446)),dot(c,vec3(.211903,.680700,.107397)),dot(c,vec3(.088302,.281719,.629979)));lms=pow(max(lms,0.),vec3(1./3.));return vec3(dot(lms,vec3(.210454,.793618,-.004072)),dot(lms,vec3(1.977998,-2.428592,.450594)),dot(lms,vec3(.025904,.782772,-.808676)));}
vec3 srgb(vec3 c){float l=c.x+.396338*c.y+.215804*c.z,m=c.x-.105561*c.y-.063854*c.z,s=c.x-.089484*c.y-1.291486*c.z;l=l*l*l;m=m*m*m;s=s*s*s;vec3 v=max(vec3(4.076742*l-3.307711*m+.230970*s,-1.268438*l+2.609757*m-.341319*s,-.004196*l-.703419*m+1.707615*s),0.);return clamp(mix(v*12.92,1.055*pow(v,vec3(1./2.4))-.055,step(vec3(.0031308),v)),0.,1.);}
vec3 pigment(vec3 a,vec3 b,float t){return srgb(mix(lab(a),lab(b),t));}
`;
function watchColors(element, fn) {
let signature = '';
const update = () => {
const cs = getComputedStyle(element), next = ['--card-accent','--_card-accent-2','--_card-accent-3','--card-surface'].map(n => cs.getPropertyValue(n)).join('|');
if (next !== signature) { signature = next; fn(colors(element)); }
};
const observer = new MutationObserver(update);
for (let p = element; p; p = p.parentElement) observer.observe(p, {attributes: true, attributeFilter: ['style','data-theme','class']});
element.addEventListener('card-theme-change', update); update();
return () => { observer.disconnect(); element.removeEventListener('card-theme-change', update); };
}
/* Timeline motion eases to rest; pointer damping keeps real dt until settled. */
function loop(element, draw, {continuous = true, speed = 1, duration = Infinity, settled = () => true} = {}) {
let visible=false,hovered=element.matches(':hover'),id=0,elapsed=0,previous=0,destroyed=false,started=false;
const rate=Number.isFinite(Number(speed)) && Number(speed)>=0?Number(speed):1;
const media=matchMedia('(prefers-reduced-motion: reduce)');
const finite=Number.isFinite(duration),mode=()=>element.closest('[data-motion]')?.dataset.motion || 'auto';
let activity=mode()==='hover'?0:1;
const allowed=()=>continuous && rate>0 && visible && !document.hidden && !media.matches && mode()!=='off';
const running=()=>allowed() && elapsed<duration && (mode()!=='hover' || hovered || (finite && started) || (!finite && (activity>0 || !settled())));
const stop=()=>{cancelAnimationFrame(id);id=0;previous=0;};
const tick=now=>{
id=0;
if(destroyed || !running()){stop();element.dataset.paused='true';return;}
const dt=Math.min(now-previous,64)/1000*rate;previous=now;started=true;
const target=mode()!=='hover' || hovered?1:0,before=activity;
activity=mix(activity,target,1-Math.exp(-dt*(target?18:10)));
if(!target && activity<.002)activity=0;
// A short demonstration finishes once started; continuous art slows to a stop.
elapsed=Math.min(duration,elapsed+dt*(finite?1:(before+activity)*.5));
draw(elapsed,dt);
if(running())id=requestAnimationFrame(tick);else{stop();element.dataset.paused='true';}
};
const sync=()=>{
if(destroyed)return;
if(finite && (!rate || media.matches || mode()==='off'))elapsed=duration;
const active=running();element.dataset.paused=String(!active);
if(active){if(!id){previous=performance.now();id=requestAnimationFrame(tick);}}
else{stop();draw(elapsed,0);}
};
const enter=()=>{hovered=true;sync();},leave=()=>{hovered=false;sync();};
const io=new IntersectionObserver(entries=>{visible=entries[0].isIntersecting;sync();});io.observe(element);
const observer=new MutationObserver(sync);
for(let p=element;p;p=p.parentElement)observer.observe(p,{attributes:true,attributeFilter:['data-motion']});
element.addEventListener('pointerenter',enter);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);
document.addEventListener('visibilitychange',sync);media.addEventListener('change',sync);
draw(0,0);
return {invalidate(){if(!destroyed)draw(elapsed,0);},restart(){if(!destroyed){elapsed=0;started=false;sync();}},destroy(){destroyed=true;stop();io.disconnect();observer.disconnect();document.removeEventListener('visibilitychange',sync);media.removeEventListener('change',sync);element.removeEventListener('pointerenter',enter);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);delete element.dataset.paused;}};
}
function fitCanvas(canvas, scale = 1) {
const width=canvas.clientWidth,height=canvas.clientHeight,dpr = Math.min(devicePixelRatio || 1, 2.5) * scale;
const w = Math.max(1, Math.round(width * dpr)), h = Math.max(1, Math.round(height * dpr));
if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h; }
return {w: width, h: height, dpr};
}
function glProgram(canvas, vertex, fragment) {
const gl = canvas.getContext('webgl', {alpha: true, antialias: false, premultipliedAlpha: true});
if (!gl) return null;
gl.getExtension('OES_standard_derivatives');
const program = gl.createProgram(), shaders = [];
try {
for (const [type, source] of [[gl.VERTEX_SHADER,vertex], [gl.FRAGMENT_SHADER,fragment]]) {
const s = gl.createShader(type); shaders.push(s); gl.shaderSource(s, source); gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
gl.attachShader(program, s);
}
gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw new Error(gl.getProgramInfoLog(program));
gl.useProgram(program);
} catch (_) { shaders.forEach(s => gl.deleteShader(s)); gl.deleteProgram(program);gl.getExtension('WEBGL_lose_context')?.loseContext();canvas.width=canvas.height=1;return null; }
const buffers = [];let disposed=false;
return {gl, program, uniform(name) { return gl.getUniformLocation(program, name); }, attribute(name, data, size) {
const b = gl.createBuffer(); buffers.push(b); gl.bindBuffer(gl.ARRAY_BUFFER, b); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
const p = gl.getAttribLocation(program, name); gl.enableVertexAttribArray(p); gl.vertexAttribPointer(p, size, gl.FLOAT, false, 0, 0);
}, destroy() {
if(disposed)return;disposed=true;
buffers.forEach(b => gl.deleteBuffer(b)); shaders.forEach(s => gl.deleteShader(s)); gl.deleteProgram(program);
gl.getExtension('WEBGL_lose_context')?.loseContext();canvas.width=canvas.height=1;
}};
}
const glNoise = `
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
vec2 grad(vec2 p){float a=hash(p)*6.2831853;return vec2(cos(a),sin(a));}
float simplex(vec2 p){
vec2 i=floor(p+(p.x+p.y)*.3660254); vec2 x=p-i+(i.x+i.y)*.2113249;
vec2 j=x.x>x.y?vec2(1,0):vec2(0,1);vec2 y=x-j+.2113249,z=x-.5773503;
vec3 h=max(.5-vec3(dot(x,x),dot(y,y),dot(z,z)),0.);h=h*h*h*h;
return 70.*dot(h,vec3(dot(grad(i),x),dot(grad(i+j),y),dot(grad(i+1.),z)));
}`;
function shell(element, options = {}) {
// Apply the public contract before a component measures, reads colors or draws.
const attributes=new Map();
for(const key of ['theme','motion','variant','effects','shadow','material']){
const value=options[key];
if(value===undefined || (key==='theme' && !['light','dark'].includes(value)) || (key==='motion' && !['auto','hover','off'].includes(value)))continue;
attributes.set(key,element.getAttribute('data-'+key));element.dataset[key]=String(value);
}
applyContent(element,options.content);
const button = options.trigger ?? null;
const rate=Number.isFinite(Number(options.speed ?? 1)) && Number(options.speed ?? 1)>=0?Number(options.speed ?? 1):1;
const media=matchMedia('(prefers-reduced-motion: reduce)');
const still=()=>!rate || element.closest('[data-motion]')?.dataset.motion==='off' || media.matches;
let x=0,y=0,tx=0,ty=0,rx=0,ry=0,trx=0,try_=0,on=false,frame=0,previous=0,destroyed=false,entryRect=null;
let dialog=null,placeholder=null,animation=null,lightX=0,lightY=0;
const surface=element.querySelector('.showcase-window') || element;
// A separate background layer leaves artwork pseudo-elements and text intact.
const finish=document.createElement('span');finish.className='card-finish';finish.setAttribute('aria-hidden','true');surface.prepend(finish);
const effects=()=>new Set((element.dataset.effects || '').split(/\s+/));
const enabled=()=>{const e=effects();return !dialog && !still() && (e.has('glow') || e.has('tilt'));};
const paint = () => {
element.style.setProperty('--pointer-x',`${x-lightX}px`);element.style.setProperty('--pointer-y',`${y-lightY}px`);
element.style.setProperty('--_card-rotate-x',`${rx}deg`);element.style.setProperty('--_card-rotate-y',`${ry}deg`);
};
const stop = () => {cancelAnimationFrame(frame);frame=0;previous=0;};
const reset = () => {on=false;stop();rx=ry=trx=try_=0;entryRect=null;element.style.setProperty('--pointer-on',0);paint();};
const schedule = () => {if(!frame && !destroyed){previous=performance.now();frame=requestAnimationFrame(follow);}};
const leave = () => {
on=false;entryRect=null;trx=try_=0;element.style.setProperty('--pointer-on',0);
if(document.hidden || !enabled()){reset();return;}
if(Math.abs(rx)+Math.abs(ry)>.005)schedule();else stop();
};
const follow = now => {
frame=0;
if(destroyed || document.hidden || !enabled()){reset();return;}
const dt=Math.min(now-previous,64)/1000*rate;previous=now;
const ease=1-Math.exp(-dt*(on?18:10));
if(on){x=mix(x,tx,ease);y=mix(y,ty,ease);}
rx=mix(rx,trx,ease);ry=mix(ry,try_,ease);
const positionDone=!on || Math.abs(x-tx)+Math.abs(y-ty)<.1;
const rotationDone=Math.abs(rx-trx)+Math.abs(ry-try_)<.005;
if(positionDone && on){x=tx;y=ty;}if(rotationDone){rx=trx;ry=try_;}
paint();
if(!positionDone || !rotationDone)frame=requestAnimationFrame(follow);else previous=0;
};
const move = e => {
if(e.pointerType==='touch' || document.hidden || !enabled()){reset();return;}
// A fixed entry rectangle avoids feeding the tilted bounds back into tilt.
const r=entryRect || (entryRect=element.getBoundingClientRect());
tx=e.clientX-r.left-element.clientLeft;ty=e.clientY-r.top-element.clientTop;
const active=effects();
trx=active.has('tilt')?-clamp((e.clientY-r.top)/r.height*2-1,-1,1)*2.4:0;
try_=active.has('tilt')?clamp((e.clientX-r.left)/r.width*2-1,-1,1)*2.4:0;
if(!on){x=tx;y=ty;on=true;paint();}
element.style.setProperty('--pointer-on',active.has('glow')?1:0);
if(Math.abs(x-tx)+Math.abs(y-ty)>=.1 || Math.abs(rx-trx)+Math.abs(ry-try_)>=.005)schedule();
};
const sync = () => {element.toggleAttribute('data-static-motion',still());if(document.hidden || !enabled())reset();else if(on){const active=effects();if(!active.has('tilt'))trx=try_=rx=ry=0;element.style.setProperty('--pointer-on',active.has('glow')?1:0);paint();}};
const observer=new MutationObserver(()=>{measure();sync();});
for(let p=element;p;p=p.parentElement)observer.observe(p,{attributes:true,attributeFilter:['data-motion','data-effects','data-title']});
const io=new IntersectionObserver(entries=>{if(!entries[0].isIntersecting)reset();});io.observe(element);
const heading=element.querySelector(':scope > .card-heading, :scope > .card-caption, :scope > .metric-copy');
const measure=()=>{if(heading)element.style.setProperty('--_card-heading-height',`${heading.offsetHeight}px`);if(surface!==element){lightX=surface.offsetLeft;lightY=surface.offsetTop;}};
const ro=new ResizeObserver(()=>{measure();if(on)leave();});ro.observe(element);if(heading)ro.observe(heading);measure();
element.addEventListener('pointerenter',move);element.addEventListener('pointermove',move);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);
document.addEventListener('visibilitychange',sync);media.addEventListener('change',sync);
sync();
window.addEventListener('scroll',leave,{passive:true,capture:true});
const restore = () => {
if (!dialog) return; reset(); animation?.cancel(); placeholder.replaceWith(element); dialog.remove(); dialog=null; placeholder=null;
element.dispatchEvent(new Event('card-theme-change')); button?.focus({preventScroll:true});
};
const close = () => {
if (!dialog) return;
if (still()) { restore(); return; }
const from=element.getBoundingClientRect(), to=placeholder.getBoundingClientRect();
animation=element.animate([{transform:'none'},{transform:`translate(${to.left-from.left}px,${to.top-from.top}px) scale(${to.width/from.width},${to.height/from.height})`}],{duration:450/rate,easing:'cubic-bezier(.165,.84,.44,1)',fill:'forwards'});
animation.finished.then(restore).catch(()=>{});
};
const open = event => {
if(button?.tagName === 'A') { if(!button.getAttribute('href')?.startsWith('#'))return; event?.preventDefault(); }
if (dialog) return; reset(); const from=element.getBoundingClientRect();
placeholder=document.createElement('div');placeholder.style.cssText=`height:${from.height}px;width:100%`;element.before(placeholder);
dialog=document.createElement('dialog');dialog.className=`card-dialog ${element.classList.contains('showcase-card')?'showcase-dialog':element.classList.contains('banner') || element.classList.contains('strip')?'wide':''}`;
dialog.setAttribute('aria-label',element.querySelector('h2, .showcase-wordmark')?.textContent || '查看卡片');
const cs=getComputedStyle(element);
// The element changes parent; carry its inherited host typography with it.
dialog.style.font=cs.font;
for(const n of ['--card-accent','--card-accent-2','--card-accent-3','--card-radius']) {const value=cs.getPropertyValue(n).trim();if(value)dialog.style.setProperty(n,value);}
dialog.dataset.theme=element.closest('[data-theme]')?.dataset.theme || 'light';
dialog.dataset.motion=element.closest('[data-motion]')?.dataset.motion || 'auto';
const exit=document.createElement('button');exit.className='dialog-close';exit.textContent='×';exit.setAttribute('aria-label','关闭');exit.addEventListener('click',close);
dialog.append(element,exit);document.body.append(dialog);dialog.showModal();
dialog.addEventListener('cancel',e=>{e.preventDefault();close();});dialog.addEventListener('click',e=>{if(e.target===dialog)close();});
const to=element.getBoundingClientRect();
if(!still()) animation=element.animate([{transform:`translate(${from.left-to.left}px,${from.top-to.top}px) scale(${from.width/to.width},${from.height/to.height})`},{transform:'none'}],{duration:700/rate,easing:'cubic-bezier(.165,.84,.44,1)'});
element.dispatchEvent(new Event('card-theme-change'));
};
button?.addEventListener('click',open);
return () => { if(destroyed)return;destroyed=true;restore();stop();finish.remove();observer.disconnect();io.disconnect();ro.disconnect();window.removeEventListener('scroll',leave,true);media.removeEventListener('change',sync);document.removeEventListener('visibilitychange',sync);element.removeEventListener('pointerenter',move);element.removeEventListener('pointermove',move);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);button?.removeEventListener('click',open);element.removeAttribute('data-static-motion');element.style.removeProperty('--pointer-on');element.style.removeProperty('--pointer-x');element.style.removeProperty('--pointer-y');element.style.removeProperty('--_card-rotate-x');element.style.removeProperty('--_card-rotate-y');element.style.removeProperty('--_card-heading-height');
// Keep template canvases for remounting, but release their backing stores.
for(const canvas of element.querySelectorAll('canvas'))canvas.width=canvas.height=1;
for(const [key,value] of attributes){if(value===null)element.removeAttribute('data-'+key);else element.setAttribute('data-'+key,value);}
};
}
return {TAU, clamp, mix, smooth, staticMotion, random, simplex, colors, rgb, mixColor, svgStops, glColor, watchColors, loop, fitCanvas, glProgram, glNoise, shell};
})();
modules["shared/land-mask.js"] = (() => {
// Generated from Natural Earth 110m land (public domain).
const LAND_WIDTH = 360;
const LAND_HEIGHT = 180;
const LAND_BITS = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA/v8/AOD//z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+///D/////5//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAH/j/+//////8PAAAAgB0AAHgAAAAAAD8AAAAAAAAAAAAAAAAAAAAAAAAAAOD//wP8//////8BAACAfx4AAAAAAAAAAP4AAAAAAAAAAAAAAAAAAAAAAAA4+I73//n///////8AAAAAfwAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAOAQACDwP4D///////8BAAAAPAQAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAADzAgPP5H8D//////38AAAAAAAAAAAAA4AEAAAD+DwAAAAAAAAAAAAAAAAAAAIC/g7PfBwAA/v////8AAAAAAAAAAADgBwAAwP//PwAA4B8AAAAAAAAAAAAAAAAMAAD8AgAA+P////8AAAAAAAAAAABwAAAA/P//AwAAAAAAAAAAAAAAAAAAAP8Ahvc5OwAA8P///38AAAAAAAAAAAAcAADg////34cHAAcAAAAAAAAAAAAAgN9/xzf8RwAA4P///z8AAAAAAAAAAAAPAB7g//////8fgB8AAAAAAQAAAAAAAO//B3f8/wsA4P///z8AAAAAAAAAAAAPAG////////8f2f8HAAAAAAD4HwAAAAD8H/D4/38A4P///xMAAAAAAPAHAAAAgN//////////////DwAAAAD+///h/4//H+/Bg/8BwP7//w8AAAAAwP8fAAAAj9///////////////88/AeD///////8DB8TPB/4AAP///wAAAAAA8P//AwDjf77/////////////////DwD////////////vh/kPwP//BwAAAAAA/P//H/P//5//////////////////34H8////////////Afw/gP//AQAAAAAA/v+fH////+///////////////////vD///////////9fAPwYgP8PAOA/AAAA/+N/8P//////////////////////QID4///////////Pw/0DAP8HAMAfAACA//F//v////////////////////8/AAP4///////////BBPAHAP4HAAAAAADg//z///////////////////////9/AID///////////8AQ8ABAPwBAAAAAAD8P/7///////////////////////9fAMD//////////z8AwA8AAPgBAAAAAAD+H/7/////////////////////Of8BAID///z//////z8AwD8AAMABAAAAAAD+P/z///////////////////9/wH8AAAD8MwD//////x8AwH8MAAAAAAAAAAD+P2D///////////////////95cAAAAACAAwD8/////38AwP8eAAAAAAAAgAGcH/D//////////////////wEAOAAAAADADgDA/////38AgP8/AAAAAAAAwANAH/T//////////////////wAAfgAAAAAwAAAA//////8PgP8/AAAAAAAAwAFwD/7/////////////////PwAAfwAAAAAGAAAA//////8/wP//AAAAAAAAwAOwA/7/////////////////DwCAPwAAAIAAAAAA/v//////8///BwAAAAAAOAcgcP//////////////////HwAAHwAAAAAAAACA+P//////4///DwAAAAAAOA74/////////////////////wUADwAAAAAAAAAA8P//////4///DwAAAAAAGD///////////////////////wUAAwAAAAAAAAAA8P//////7///FwAAAAAAAB///////////////////////wUAAgAAAAAAAAAA0P//////////DAAAAAAAgOH//////////////////////w0AAAAAAAAAAAAAYP////////8xLAAAAAAAAPz//////////////////////wwAAAAAAAAAAAAAAP7//////38HfgAAAAAAgP///////////////////////wQAAAAAAAAAAAAAAP3///////8HUAAAAAAAAP7/////////////////////fwQAAAAAAAAAAAAAAP////////+XAAAAAAAAAPz///93/P//////////////PwwAAAAAAAAAAAAAAP////////9/AAAAAAAAAPj//f9j/v//////////////HwAAAAAAAAAAAAAAAP////////8MAAAAAAAAAPj/+P8B/P//////////////DwQAAAAAAAAAAAAAAP///////z8AAAAAAAAAcPzH8/8A8P//////////////Bx4AAAAAAAAAAAAAAP///////x8AAAAAAAAA+H+Aw/8AwP////////////9/AA8AAAAAAAAAAAAAAP///////x8AAAAAAAAA+D8Aj//w4f////////////8/AAAAAAAAAAAAAAAAAP///////wMAAAAAAAAA+B8wuE/+/////////////98fAAMAAAAAAAAAAAAAAP///////wMAAAAAAAAA+A8wEMf//////////////0cOAAMAAAAAAAAAAAAAAP7//////wEAAAAAAAAA+A8AAI7//////////////wMOAAEAAAAAAAAAAAAAAP7//////wAAAAAAAAAA+AcABob//////////////zccwAEAAAAAAAAAAAAAAPz//////wAAAAAAAAAAQOB/AAQ2/////////////x8c8AEAAAAAAAAAAAAAAPj//////wAAAAAAAAAAQPx/AAAA/////////////w8Y/gEAAAAAAAAAAAAAAPD/////fwAAAAAAAAAA4P8/AAAA/////////////w+EGwAAAAAAAAAAAAAAAMD/////HwAAAAAAAAAA8P9/AACA/////////////x/AAwAAAAAAAAAAAAAAAID/////DwAAAAAAAAAA+P//BwaA/////////////x/AAAAAAAAAAAAAAAAAAID9////BwAAAAAAAAAA/P//D3+E/////////////z9AAAAAAAAAAAAAAAAAAAD5////BwAAAAAAAAAA/P//f////////////////x8AAAAAAAAAAAAAAAAAAADy/x8GBgAAAAAAAAAA/P/////v/8///////////z8AAAAAAAAAAAAAAAAAAAD0/w8ABgAAAAAAAAAA/v////+//4///////////z8AAAAAAAAAAAAAAAAAAADu/wcADgAAAAAAAACA//////8f/x/+/////////x8AAAAAAAAAAAAAAAAAAACI/wcALAAAAAAAAADA//////8//z/g/////////w8AAAAAAAAAAAAAAAAAAACQ/wcACAAAAAAAAADg//////9//r+A/////////wcAAAAAAAAAAAAAAAAAAAAQ/wMAAAAAAAAAAADg//////9//n8cgP///////ycAAAAAAAAAAAAAAAAAAAAA/gMAAAAAAAAAAADw////////+P9/AP///////xEAAAAAAAAAAAAAAAAAAAAA/AMAHQAAAAAAAADw////////+P//APz/f///fxAAAAAA
return {LAND_WIDTH, LAND_HEIGHT, LAND_BITS};
})();
modules["line-ribbons/surface.js"] = (() => {
const {fitCanvas,glProgram,glColor,watchColors} = modules["shared/runtime.js"];
// Fixed camera, three translucent ruled surfaces. Thread coverage is filtered in
// screen space; below one pixel it converges to a film instead of a moire pattern.
function createSurface(element, ribbons, invalidate, variant='webgl') {
const canvas=document.createElement('canvas');canvas.className='ribbon-surface';canvas.setAttribute('aria-hidden','true');
element.querySelector('.card-stage').append(canvas);
const vertex=`attribute vec3 a_position;attribute float a_thread,a_along;
uniform vec2 u_size;uniform vec4 u_view;uniform float u_time,u_dpr;varying vec3 v_position;varying float v_thread,v_along;
void main(){vec3 p=a_position;float wave=sin(p.x*.004-u_time*.11);
p.y+=wave*3.*sin(a_thread*3.14159265);p.z+=wave*12.;
v_position=p;v_thread=a_thread;v_along=a_along;float scale=max(u_size.x/u_view.z,u_size.y/u_view.w);
vec2 xy=(p.xy-u_view.xy-u_view.zw*.5)*scale/(u_size*.5);
gl_Position=vec4(xy.x,-xy.y,0.,1.);gl_PointSize=(1.+.65*sin(a_thread*3.14159265))*u_dpr;}`;
const fragment=`#extension GL_OES_standard_derivatives : enable
precision highp float;varying vec3 v_position;varying float v_thread,v_along;
uniform vec3 u_a,u_b;uniform float u_dark,u_style;${glColor}
float wire(float phase){
float footprint=max(fwidth(phase),.001),distance=abs(fract(phase+.5)-.5);
float line=1.-smoothstep(footprint*.18,footprint*.9,distance);
return line*(1.-smoothstep(.28,.85,footprint));
}
void main(){vec3 n=normalize(cross(dFdx(v_position),dFdy(v_position)));
float grazing=1.-abs(n.z);float light=abs(dot(n,normalize(vec3(-.35,-.45,1.))));
float phase=v_thread*128.;float footprint=max(fwidth(phase),.001);
float distance=abs(fract(phase)-.5);
float thread=1.-smoothstep(.18-footprint*.5,.18+footprint*.5,distance);
thread=mix(thread,.36,smoothstep(.45,1.15,footprint));
float edge=exp(-min(v_thread,1.-v_thread)*40.);
float hue=smoothstep(360.,1350.,v_position.x)*.92;
vec3 c=pigment(u_b,u_a,hue);vec3 l=lab(c);
l.x+=.05*light-.08*grazing;c=srgb(l);
float alpha=.15+thread*.39+grazing*.28+edge*.10+u_dark*.08;
alpha*=smoothstep(0.,max(fwidth(v_thread)*1.1,.0001),min(v_thread,1.-v_thread));
if(u_style>1.5){
float grid=max(wire(v_thread*22.),wire(v_along*88.));
alpha=grid*(.40+.24*light)+edge*.055;
}else if(u_style>.5){
float point=1.-smoothstep(.22,.50,length(gl_PointCoord-.5));
alpha=point*(.55+.30*sin(v_thread*3.14159265));c=pigment(u_b,u_a,hue);
}
gl_FragColor=vec4(c*alpha,alpha);}`;
const gpu=glProgram(canvas,vertex,fragment);
if(!gpu){canvas.remove();return null;}
const positions=[],threads=[],alongs=[];
const bezier=(a,t)=>{const q=1-t;return [0,1].map(k=>q*q*q*a[k]+3*q*q*t*a[k+2]+3*q*t*t*a[k+4]+t*t*t*a[k+6]);};
const point=(r,u,v)=>{
const [a,b]=ribbons[r],A=bezier(a,u),B=bezier(b,u),s=variant==='dots'?v:(1-Math.cos(v*Math.PI))/2;
positions.push(A[0]+(B[0]-A[0])*s,A[1]+(B[1]-A[1])*s,Math.sin(v*Math.PI*2+u*3+r)*22);
threads.push(v);alongs.push(u);
};
const U=128,V=40;
if(variant==='dots'){
for(let r=0;r<ribbons.length;r++)for(let u=0;u<=128;u++)for(let v=0;v<=24;v++)point(r,u/128,v/24);
}else for(let r=0;r<ribbons.length;r++)for(let u=0;u<U;u++)for(let v=0;v<V;v++){
for(const [du,dv]of [[0,0],[1,0],[0,1],[1,0],[1,1],[0,1]])point(r,(u+du)/U,(v+dv)/V);
}
gpu.attribute('a_position',positions,3);gpu.attribute('a_thread',threads,1);gpu.attribute('a_along',alongs,1);
let palette,lost=false;
const off=watchColors(element,p=>{palette=p;invalidate();});
const ro=new ResizeObserver(invalidate);ro.observe(canvas);
const lostContext=e=>{e.preventDefault();lost=true;canvas.hidden=true;element.dataset.surfaceReady='false';};
canvas.addEventListener('webglcontextlost',lostContext);
element.dataset.surfaceReady='true';
return {
draw(time){
if(!palette||lost)return;
const {w,h,dpr}=fitCanvas(canvas,1);const {gl}=gpu;
gl.useProgram(gpu.program);gl.viewport(0,0,canvas.width,canvas.height);
gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
gl.uniform2f(gpu.uniform('u_size'),w,h);gl.uniform1f(gpu.uniform('u_time'),time);
gl.uniform1f(gpu.uniform('u_dpr'),dpr);gl.uniform1f(gpu.uniform('u_style'),variant==='dots'?1:variant==='grid'?2:0);
gl.uniform4fv(gpu.uniform('u_view'),element.clientWidth<=650?[100,60,560,474]:[0,0,1280,474]);
gl.uniform3fv(gpu.uniform('u_a'),palette.a);gl.uniform3fv(gpu.uniform('u_b'),palette.b);
gl.uniform1f(gpu.uniform('u_dark'),palette.dark?1:0);
gl.drawArrays(variant==='dots'?gl.POINTS:gl.TRIANGLES,0,threads.length);
},
destroy(){off();ro.disconnect();canvas.removeEventListener('webglcontextlost',lostContext);gpu.destroy();canvas.remove();delete element.dataset.surfaceReady;}
};
}
return {createSurface};
})();
modules["flow-object/material.js"] = (() => {
const {glProgram, glNoise, glColor} = modules["shared/runtime.js"];
// One coated surface, not a stack of ribbons. Paint stays still; light travels.
const vertex = `attribute vec2 a_position;varying vec2 v_uv;
void main(){v_uv=a_position*.5+.5;gl_Position=vec4(a_position,0.,1.);}`;
const fragment = `precision highp float;
varying vec2 v_uv;uniform vec2 u_resolution,u_pointer;
uniform float u_time;uniform vec3 u_a,u_b,u_c;
${glNoise}
${glColor}
float bell(float d,float w){return exp(-d*d/(w*w));}
void main(){
// Fit the field to the short side: a wider host reveals more of the material.
vec2 uv=vec2(v_uv.x,1.-v_uv.y);
vec2 p=(uv-.5)*u_resolution/min(u_resolution.x,u_resolution.y);
float grain=hash(gl_FragCoord.xy)-.5;
float dye=p.y+.42*p.x+.07*sin(p.x*2.1);
vec3 color=mix(lab(u_b),lab(u_a),smoothstep(-.48,.30,dye));
// Warm pigment is a broad off-centre pool, not another repeated band.
float warm=smoothstep(-.01,.43,p.y+.18*p.x-.12*sin(p.x*2.));
color=mix(color,lab(u_c),warm);
color=mix(color,lab(vec3(1.)),.18+.09*bell(dye+.52,.40));
float drift=.055*sin(u_time*.32)+u_pointer.x*.065-u_pointer.y*.035;
float d=p.y+.43*p.x-.065*p.x*p.x+.10-drift;
// One narrow glint nested inside an asymmetric, much wider bloom.
float width=max(1.6/min(u_resolution.x,u_resolution.y),.005);
float glint=bell(d,width*1.5);
float shoulder=bell(d-.012,.035)*.20;
float bloom=bell(d-.055,.14)*.29;
float envelope=.66+.34*bell(p.x-.10-.22*sin(u_time*.19),.85);
float brushed=simplex(vec2(p.x*.9,d*220.));
float echo=bell(d+.038+.008*sin(p.x*4.),.014)*.07;
float grazing=(glint*.29+shoulder+bloom+echo)*envelope;
color=mix(color,lab(vec3(1.)),clamp(grazing,0.,.78));
// A broad reflected softbox moves independently of the pigment boundary.
float softbox=bell(p.x*.70-p.y*.22+.27*sin(u_time*.25)+u_pointer.x*.14,.52);
color.x+=softbox*.035;
color.x+=brushed*.009*bell(d,.14);
vec3 outcolor=srgb(color)+grain*.020;
gl_FragColor=vec4(outcolor,1.);
}`;
function coatedSurface(canvas) {
let gpu, uniforms, lastFrame=null, disposed=false;
// Use the untransformed plane, so tilting the host cannot stretch its grain.
const resize=()=>{
const dpr=Math.min(devicePixelRatio || 1,2.5);
const w=Math.max(1,Math.round(canvas.clientWidth*dpr)),h=Math.max(1,Math.round(canvas.clientHeight*dpr));
if(canvas.width!==w || canvas.height!==h){canvas.width=w;canvas.height=h;}
};
const initialize=()=>{
gpu=glProgram(canvas,vertex,fragment);
canvas.dataset.renderer=gpu?'webgl':'css';
if(!gpu)return;
gpu.attribute('a_position',[-1,-1,1,-1,-1,1,-1,1,1,-1,1,1],2);
uniforms=Object.fromEntries(['resolution','pointer','time','a','b','c'].map(n=>[n,gpu.uniform('u_'+n)]));
resize();
};
const lost=e=>{if(disposed)return;e.preventDefault();gpu=null;canvas.dataset.renderer='css';};
const restored=()=>{if(!disposed){initialize();if(lastFrame)draw(...lastFrame);}};
canvas.addEventListener('webglcontextlost',lost);
canvas.addEventListener('webglcontextrestored',restored);
initialize();
function draw(time,palette,pointer=[0,0]){
lastFrame=[time,palette,pointer];
if(!gpu || !palette || disposed)return;
const {gl}=gpu;gl.useProgram(gpu.program);gl.viewport(0,0,canvas.width,canvas.height);
gl.uniform2f(uniforms.resolution,canvas.width,canvas.height);
gl.uniform2f(uniforms.pointer,...pointer);gl.uniform1f(uniforms.time,time);
for(const n of ['a','b','c'])gl.uniform3fv(uniforms[n],palette[n]);
gl.drawArrays(gl.TRIANGLES,0,6);
}
return {
resize,draw,
destroy(){if(disposed)return;disposed=true;canvas.removeEventListener('webglcontextlost',lost);canvas.removeEventListener('webglcontextrestored',restored);gpu?.destroy();gpu=null;lastFrame=null;canvas.width=canvas.height=1;delete canvas.dataset.renderer;}
};
}
return {coatedSurface};
})();
modules["particle-ring/card.js"] = (() => {
const {random, simplex, loop, fitCanvas, glProgram, glNoise, glColor, watchColors, shell, clamp, mix, mixColor, rgb, TAU,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const cleanShell=shell(element,options);
const garmentGradient=element.querySelector('linearGradient');
if(garmentGradient){const old=garmentGradient.id,id='garment-'+crypto.randomUUID();garmentGradient.id=id;for(const path of element.querySelectorAll('svg [fill]'))if(path.getAttribute('fill')===`url(#${old})`)path.setAttribute('fill',`url(#${id})`);}
let canvas = element.querySelector('canvas'), palette, w = 1, h = 1, dpr = 1;
const rng=random(91), count=matchMedia('(max-width: 650px)').matches?8000:16000;
const data=Array.from({length:count},()=>[rng()*TAU,rng(),.6+Math.pow(rng(),3)*3.4,.25+.75*Math.pow(rng(),1.3)]);
const vertex=`precision highp float;attribute vec4 a_seed;uniform vec2 u_size,u_mouse;uniform float u_time,u_dpr,u_active;varying float v_alpha;varying float v_y;${glNoise}
void main(){vec2 unit=vec2(cos(a_seed.x),sin(a_seed.x));float r=u_size.x*.59;
float fine=simplex(unit*45.+a_seed.y*15.+u_time*.075),coarse=simplex(unit*8.+a_seed.y*4.+u_time*.035);
float coreWidth=mix(.008,.030,smoothstep(-.6,.6,simplex(unit*2.2+vec2(u_time*.14,-u_time*.10))));
float scatter=a_seed.y<.6?(fine*.75+coarse*.25)*coreWidth*r:(fine*150.+coarse*45.)*pow((a_seed.y-.6)/.4,2.4);
vec2 base=vec2(u_size.x*.5,u_size.y*.43)+unit*r;
float recover=(1.-smoothstep(0.,u_size.x*.29,distance(base,u_mouse)))*u_active;
vec2 p=base+unit*scatter*(1.-recover);
gl_Position=vec4(p.x/u_size.x*2.-1.,1.-p.y/u_size.y*2.,0.,1.);gl_PointSize=a_seed.z*u_dpr;v_alpha=a_seed.w;v_y=clamp(p.y/u_size.y,0.,1.);
}`;
const fragment=`precision mediump float;uniform vec3 u_a,u_b;varying float v_alpha,v_y;${glColor} void main(){float r=length(gl_PointCoord-.5);float alpha=(1.-smoothstep(.15,.5,r))*v_alpha;gl_FragColor=vec4(pigment(u_b,u_a,v_y),alpha);}`;
let gpu=glProgram(canvas,vertex,fragment),ctx=null;
if(!gpu){ const replacement=canvas.cloneNode();canvas.replaceWith(replacement);canvas=replacement;ctx=canvas.getContext('2d'); }
else gpu.attribute('a_seed',data.flat(),4);
let mouse=[-1000,-1000],target=[-1000,-1000],active=0,targetActive=0;
const still=staticMotion(element,options);
const noise=simplex(77);
const draw=(time,dt)=>{
if(!palette)return;
if(still())active=targetActive=0;
if(dt){mouse=mouse.map((n,i)=>mix(n,target[i],1-Math.exp(-dt*5.6)));active=mix(active,targetActive,1-Math.exp(-dt*3.2));}
({w,h,dpr}=fitCanvas(canvas));
if(gpu){const {gl}=gpu;gl.viewport(0,0,canvas.width,canvas.height);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.useProgram(gpu.program);gl.enable(gl.BLEND);gl.blendFuncSeparate(gl.SRC_ALPHA,palette.dark?gl.ONE:gl.ONE_MINUS_SRC_ALPHA,gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
gl.uniform2f(gpu.uniform('u_size'),w,h);gl.uniform2f(gpu.uniform('u_mouse'),...mouse);gl.uniform1f(gpu.uniform('u_active'),active);gl.uniform1f(gpu.uniform('u_time'),time+12);gl.uniform1f(gpu.uniform('u_dpr'),dpr);gl.uniform3fv(gpu.uniform('u_a'),palette.a);gl.uniform3fv(gpu.uniform('u_b'),palette.b);gl.drawArrays(gl.POINTS,0,count);
}else{ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,w,h);for(const [a,s,size,alpha]of data){const x=Math.cos(a),y=Math.sin(a),base=w*.59,f=noise(x*45+s*15+1,y*45+s*15+1),c=noise(x*8+s*4,y*8+s*4),n=clamp((noise(x*2.2+1.68,y*2.2-1.2)+.6)/1.2),width=mix(.008,.030,n*n*(3-2*n)),r=base+(s<.6?(f*.75+c*.25)*width*base:(f*150+c*45)*((s-.6)/.4)**2.4);const px=w*.5+x*r,py=h*.43+y*r;ctx.fillStyle=rgb(mixColor(palette.b,palette.a,clamp(py/h)),alpha);ctx.beginPath();ctx.arc(px,py,size*.5,0,TAU);ctx.fill();}}
};
const clock=loop(element,draw,{...options,continuous:!!gpu,settled:()=>Math.abs(active-targetActive)<.001});
const off=watchColors(element,p=>{palette=p;clock.invalidate();});
const ro=new ResizeObserver(()=>clock.invalidate());ro.observe(element.querySelector('.card-stage'));
const move=e=>{if(e.pointerType==='touch' || still())return;const r=canvas.getBoundingClientRect();target=[e.clientX-r.left,e.clientY-r.top];targetActive=1;};
const leave=()=>targetActive=0;element.addEventListener('pointermove',move);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);
return {destroy(){clock.destroy();off();ro.disconnect();gpu?.destroy();cleanShell();element.removeEventListener('pointermove',move);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);}};
}
return {mount};
})();
modules["flow-object/card.js"] = (() => {
const {loop, watchColors, shell, mix, clamp, staticMotion} = modules["shared/runtime.js"];
const {coatedSurface} = modules["flow-object/material.js"];
function mount(element, options = {}) {
const variant=options.variant ?? element.dataset.variant ?? 'token';
const cleanShell=shell(element,{...options,variant:variant==='card'?'card':'token'});
const surface=coatedSurface(element.querySelector('.flow-surface'));
const card=element.querySelector('.physical-card');
let palette,x=0,y=0,tx=0,ty=0;
const still=staticMotion(element,options);
const draw=(time,dt)=>{
if(still())x=y=tx=ty=0;
if(dt || still()){
const ease=1-Math.exp(-dt*7);x=mix(x,tx,ease);y=mix(y,ty,ease);
card.style.transform=`rotateX(${-y*5}deg) rotateY(${x*7}deg)`;
}
surface.draw(time,palette,[x,y]);
element.style.setProperty('--token-pulse',still()?.8:.65+.35*(.5+.5*Math.sin(time*2.2)));
};
const clock=loop(element,draw,{...options,settled:()=>Math.abs(x-tx)+Math.abs(y-ty)<.001});
const off=watchColors(element,p=>{palette=p;clock.invalidate();});
const ro=new ResizeObserver(()=>{surface.resize();clock.invalidate();});ro.observe(card);
const move=e=>{if(e.pointerType==='touch' || still())return;const r=element.getBoundingClientRect();tx=clamp((e.clientX-r.left)/r.width*2-1,-1,1);ty=clamp((e.clientY-r.top)/r.height*2-1,-1,1);};
const leave=()=>{tx=ty=0;};
element.addEventListener('pointermove',move);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);
return {destroy(){clock.destroy();off();ro.disconnect();surface.destroy();cleanShell();element.removeEventListener('pointermove',move);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);card.style.removeProperty('transform');element.style.removeProperty('--token-pulse');}};
}
return {mount};
})();
modules["dot-globe/card.js"] = (() => {
const {random, loop, fitCanvas, glProgram, glColor, watchColors, shell, TAU, mixColor, rgb, clamp, mix, smooth} = modules["shared/runtime.js"];
const {LAND_WIDTH, LAND_HEIGHT, LAND_BITS} = modules["shared/land-mask.js"];
function mount(element, options = {}) {
const cleanShell=shell(element,options),previousMode=element.getAttribute('data-mode');
if(options.mode!==undefined)element.dataset.mode=options.mode==='mesh'?'mesh':'globe';
const isMesh=()=>element.dataset.mode==='mesh';
const label=element.querySelector('.globe-label'),labelText=label.textContent;
let canvas=element.querySelector('.globe-points');const backdrop=element.querySelector('.globe-wash'),arcCanvas=element.querySelector('.globe-arcs');
const wash=backdrop.getContext('2d'),arc=arcCanvas.getContext('2d'),rng=random(301),bits=Uint8Array.from(atob(LAND_BITS),c=>c.charCodeAt(0));
const count=matchMedia('(max-width: 650px)').matches?55000:110000,points=[];
const normalize=p=>{const l=Math.hypot(...p);return p.map(n=>n/l);};
for(let i=0;i<count;i++){
const latitude=Math.asin(-1+2*(i+.5)/count),longitude=i*2.3999632297;
let p=[Math.cos(latitude)*Math.sin(longitude),Math.sin(latitude),Math.cos(latitude)*Math.cos(longitude)];
const v=[rng()-.5,rng()-.5,rng()-.5],dot=v.reduce((s,n,j)=>s+n*p[j],0),t=normalize(v.map((n,j)=>n-dot*p[j]));
p=normalize(p.map((n,j)=>n+t[j]*.007));
const lon=Math.atan2(p[0],p[2])*180/Math.PI,lat=Math.asin(p[1])*180/Math.PI;
const x=Math.min(LAND_WIDTH-1,Math.floor((lon+180)/360*LAND_WIDTH)),y=Math.min(LAND_HEIGHT-1,Math.floor((90-lat)/180*LAND_HEIGHT)),idx=y*LAND_WIDTH+x;
if(bits[idx>>3]&(1<<(idx&7)))points.push([...p,1.2+Math.pow(rng(),2)*1.4,.35+Math.pow(rng(),.4)*.55]);
}
// A second point field shares the renderer, palette and clock with the globe.
const globeCount=points.length,content=options.content || {};
const defaults=[[-.98,.64,.18],[-.47,.83,-.32],[.02,.66,.38],[.59,.82,-.18],[1.03,.40,.12],[-1.12,.10,-.4],[-.66,.22,.4],[-.18,.09,-.27],[.36,.29,.24],[.86,-.13,-.18],[-.95,-.46,.21],[-.45,-.28,-.35],[.07,-.39,.38],[.61,-.53,-.25],[-.57,-.85,.1],[.16,-.91,-.18],[1.08,-.70,.25]];
const nodes=(Array.isArray(content.nodes)?content.nodes.filter(n=>n && typeof n==='object').slice(0,64):defaults.map((position,i)=>({id:String(i),position}))).map((node,i)=>({
id:String(node.id ?? i),label:String(node.label ?? ''),position:Array.isArray(node.position)&&node.position.length===3&&node.position.every(Number.isFinite)?node.position.map(n=>clamp(n,-1.5,1.5)):defaults[i%defaults.length]
}));
const indexes=new Map(nodes.map((node,i)=>[node.id,i]));
const resolve=value=>indexes.get(String(value)) ?? (Number.isInteger(value)&&value>=0&&value<nodes.length?value:undefined);
const links=Array.isArray(content.links)?content.links.map(link=>[resolve(link?.from),resolve(link?.to)]).filter(([a,b])=>a!==undefined&&b!==undefined&&a!==b).slice(0,160):nodes.flatMap((node,i)=>nodes.map((other,j)=>({j,d:Math.hypot(...node.position.map((n,k)=>n-other.position[k]))})).filter(({j})=>j!==i).sort((a,b)=>a.d-b.d).slice(0,2).filter(({j})=>j>i).map(({j})=>[i,j]));
for(let i=0;i<1800;i++)points.push([(rng()-.5)*2.9,(rng()-.5)*2.6,(rng()-.5)*1.4,.7+rng()*1.1,.08+rng()*.22]);
for(const node of nodes){for(let i=0;i<100;i++){const radius=Math.pow(rng(),.65)*.17,angle=rng()*TAU,z=(rng()-.5)*radius;points.push([node.position[0]+Math.cos(angle)*radius,node.position[1]+Math.sin(angle)*radius,node.position[2]+z,.8+rng()*1.3,.14+rng()*.48]);}points.push([...node.position,4,.95]);}
const vertex=`precision highp float;attribute vec3 a_position;attribute vec2 a_detail;uniform vec2 u_size;uniform float u_time,u_dpr,u_mode;varying float v_alpha,v_t;
void main(){float a=-.70+u_time*.069813;vec3 p=vec3(a_position.x*cos(a)+a_position.z*sin(a),a_position.y,-a_position.x*sin(a)+a_position.z*cos(a));
float tilt=.12;vec2 xy=mat2(cos(tilt),-sin(tilt),sin(tilt),cos(tilt))*p.xy;float r=u_size.x*.76;
vec2 screen=vec2(u_size.x*.80,u_size.y*.68)+vec2(xy.x,-xy.y)*r;
v_alpha=a_detail.y*mix(.12,1.,smoothstep(-.1,.35,p.z));if(p.z<0.)v_alpha*=.20+.80*smoothstep(0.,.30,-p.z);
float size=.82+.18*(p.z+1.)*.5;
if(u_mode>.5){a=.14+sin(u_time*.07)*.15;p=vec3(a_position.x*cos(a)+a_position.z*sin(a),a_position.y,-a_position.x*sin(a)+a_position.z*cos(a));xy=mat2(cos(tilt),-sin(tilt),sin(tilt),cos(tilt))*p.xy;float perspective=1./(1.+p.z*.24);screen=vec2(u_size.x*.53,u_size.y*.55)+vec2(xy.x,-xy.y)*u_size.x*.43*perspective;size=perspective;v_alpha=a_detail.y*(.85+.15*sin(u_time*.5+a_position.x*7.));}
gl_Position=vec4(screen.x/u_size.x*2.-1.,1.-screen.y/u_size.y*2.,0.,1.);gl_PointSize=a_detail.x*u_dpr*size;v_t=clamp((xy.x+xy.y+.85)/1.8,0.,1.);}`;
const fragment=`precision mediump float;varying float v_alpha,v_t;uniform vec3 u_a,u_b,u_c;${glColor} void main(){float r=length(gl_PointCoord-.5),soft=1.-smoothstep(.32,.5,r);vec3 c=v_t<.58?pigment(u_a,u_b,v_t/.58):pigment(u_b,u_c,(v_t-.58)/.42);gl_FragColor=vec4(c,v_alpha*soft);}`;
let gpu=glProgram(canvas,vertex,fragment),ctx;
if(gpu){gpu.attribute('a_position',points.flatMap(p=>p.slice(0,3)),3);gpu.attribute('a_detail',points.flatMap(p=>p.slice(3)),2);}
else{const n=canvas.cloneNode();canvas.replaceWith(n);canvas=n;ctx=canvas.getContext('2d');}
let palette,washKey="",arcColors=[],fallbackTints=[];const requested=Number(options.arcs??element.dataset.arcs??2),arcs=Number.isFinite(requested)?Math.round(clamp(requested,1,3)):2;
const rotate=(p,time)=>{const a=-.70+time*TAU/90,x=p[0]*Math.cos(a)+p[2]*Math.sin(a),z=-p[0]*Math.sin(a)+p[2]*Math.cos(a);return [x*Math.cos(.12)+p[1]*Math.sin(.12),-x*Math.sin(.12)+p[1]*Math.cos(.12),z];};
const project=(p,w,h)=>[w*.80+p[0]*w*.76,h*.68-p[1]*w*.76];
const meshProject=(p,time,w,h)=>{const a=.14+Math.sin(time*.07)*.15,x=p[0]*Math.cos(a)+p[2]*Math.sin(a),z=-p[0]*Math.sin(a)+p[2]*Math.cos(a),f=1/(1+z*.24);return [w*.53+(x*Math.cos(.12)+p[1]*Math.sin(.12))*w*.43*f,h*.55-(-x*Math.sin(.12)+p[1]*Math.cos(.12))*w*.43*f];};
const slerp=(a,b,t)=>{const angle=Math.acos(clamp(a.reduce((s,n,i)=>s+n*b[i],0),-1,1)),s=Math.sin(angle);return a.map((n,i)=>(n*Math.sin((1-t)*angle)+b[i]*Math.sin(t*angle))/s);};
const draw=(time)=>{
if(!palette)return;const {w,h,dpr}=fitCanvas(canvas);fitCanvas(backdrop);fitCanvas(arcCanvas);
const mesh=isMesh(),nextWashKey=mesh+":"+canvas.width+":"+canvas.height+":"+JSON.stringify(palette);
if(nextWashKey!==washKey){
wash.setTransform(dpr,0,0,dpr,0,0);wash.clearRect(0,0,w,h);const cx=w*.80,cy=h*.68,r=w*.76;
if(mesh){const g=wash.createRadialGradient(w*.53,h*.55,0,w*.53,h*.55,w*.65);g.addColorStop(0,rgb(palette.b,.10));g.addColorStop(.55,rgb(palette.a,.035));g.addColorStop(1,rgb(palette.a,0));wash.fillStyle=g;wash.fillRect(0,0,w,h);}
else{const g=wash.createRadialGradient(cx-r*.25,cy-r*.35,r*.1,cx,cy,r);for(let i=0;i<=16;i++){const t=i/16;g.addColorStop(t*.85,rgb(mixColor(palette.surface,palette.a,t),.01+.03*t));}g.addColorStop(1,rgb(palette.a,.05));wash.fillStyle=g;wash.beginPath();wash.arc(cx,cy,r,0,TAU);wash.fill();wash.strokeStyle=rgb(palette.a,.075);wash.lineWidth=.6;wash.stroke();}
washKey=nextWashKey;arcColors=Array.from({length:201},(_,i)=>mixColor(palette.b,palette.a,i/200));
if(!gpu)fallbackTints=Array.from({length:129},(_,i)=>{const t=i/128,color=t<.58?mixColor(palette.a,palette.b,t/.58):mixColor(palette.b,palette.c,(t-.58)/.42);return Array.from({length:33},(_,a)=>rgb(color,a/32));});
}
if(gpu){const {gl}=gpu;gl.useProgram(gpu.program);gl.viewport(0,0,canvas.width,canvas.height);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.enable(gl.BLEND);gl.blendFuncSeparate(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA,gl.ONE,gl.ONE_MINUS_SRC_ALPHA);gl.uniform2f(gpu.uniform('u_size'),w,h);gl.uniform1f(gpu.uniform('u_time'),time);gl.uniform1f(gpu.uniform('u_mode'),mesh?1:0);gl.uniform1f(gpu.uniform('u_dpr'),dpr);for(const [n,c]of [['a',palette.a],['b',palette.b],['c',palette.c]])gl.uniform3fv(gpu.uniform('u_'+n),c);gl.drawArrays(gl.POINTS,mesh?globeCount:0,mesh?points.length-globeCount:globeCount);}
else{ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,w,h);for(let i=mesh?globeCount:0;i<(mesh?points.length:globeCount);i++){const p=points[i],q=rotate(p,time),[x,y]=mesh?meshProject(p,time,w,h):project(q,w,h),t=clamp((q[0]+q[1]+.85)/1.8);const rim=clamp(-q[2]/.30),back=q[2]<0?.20+.80*rim*rim*(3-2*rim):1,alpha=mesh?p[4]:p[4]*(q[2]>.1?1:.12)*back;ctx.fillStyle=fallbackTints[Math.round(t*128)][Math.round(alpha*32)];ctx.beginPath();ctx.arc(x,y,p[3]*.45,0,TAU);ctx.fill();}}
arc.setTransform(dpr,0,0,dpr,0,0);arc.clearRect(0,0,w,h);
if(mesh){
const positions=nodes.map(n=>meshProject(n.position,time,w,h));
for(const [i,[a,b]] of links.entries()){
const A=positions[a],B=positions[b],color=mixColor(palette.a,palette.b,(i%7)/6),t=(time*.13+i*.31)%1;
arc.strokeStyle=rgb(color,.23);arc.lineWidth=.65;arc.beginPath();arc.moveTo(...A);arc.lineTo(...B);arc.stroke();
const x=mix(A[0],B[0],t),y=mix(A[1],B[1],t),g=arc.createRadialGradient(x,y,0,x,y,7);g.addColorStop(0,rgb(color,.65));g.addColorStop(1,rgb(color,0));arc.fillStyle=g;arc.beginPath();arc.arc(x,y,7,0,TAU);arc.fill();
}
for(const [i,[x,y]] of positions.entries()){const pulse=.5+.5*Math.sin(time*1.5+i),g=arc.createRadialGradient(x,y,0,x,y,12+pulse*4);g.addColorStop(0,rgb(palette.a,.28));g.addColorStop(1,rgb(palette.a,0));arc.fillStyle=g;arc.beginPath();arc.arc(x,y,16,0,TAU);arc.fill();arc.strokeStyle=rgb(palette.a,.4+pulse*.3);arc.lineWidth=.7;arc.beginPath();arc.arc(x,y,3+pulse*1.2,0,TAU);arc.stroke();}
if(label){const i=Math.min(8,nodes.length-1);label.hidden=i<0;if(i>=0){label.style.left=positions[i][0]+'px';label.style.top=positions[i][1]+'px';label.style.opacity='1';label.textContent=String(content.label ?? (nodes[i].label || labelText));}}return;
}
if(label){label.hidden=false;label.textContent=labelText;}
for(let k=0;k<arcs;k++){
const A=normalize([-.88,.09+k*.17,.42]),B=k===0?normalize([.027,.752,.659]):normalize([.24,.40,.88]);
const cycle=(time+k*3.1+3)%13,head=smooth(cycle/3),fade=cycle>9?1-smooth((cycle-9)/3):1;
const tail=cycle>9?smooth((cycle-9)/3)*.85:0;let prev=null,headXY=null;
for(let i=0;i<=200;i++){
const t=i/200;if(t>head||t<tail)continue;
let p=slerp(A,B,t),lift=1+Math.sin(t*Math.PI)*(.38+k*.04);p=rotate(p.map(n=>n*lift),time);
const xy=project(p,w,h);headXY=xy;
if(prev){arc.strokeStyle=rgb(arcColors[i],fade*Math.pow(t,1.2)*.9);arc.lineWidth=.7+t*.65;arc.beginPath();arc.moveTo(...prev);arc.lineTo(...xy);arc.stroke();}prev=xy;
}
if(headXY&&fade>0){const [x,y]=headXY,g=arc.createRadialGradient(x,y,0,x,y,8);g.addColorStop(0,rgb(palette.a,fade*.7));g.addColorStop(1,rgb(palette.a,0));arc.fillStyle=g;arc.beginPath();arc.arc(x,y,8,0,TAU);arc.fill();arc.fillStyle=rgb(palette.a,fade);arc.beginPath();arc.arc(x,y,1.9,0,TAU);arc.fill();}
const [x,y]=project(rotate(B,time),w,h),pulse=clamp((cycle-3)/1.15);
if(head>.99){arc.strokeStyle=rgb(palette.a,fade*.85);arc.lineWidth=1;arc.beginPath();arc.arc(x,y,3.5,0,TAU);arc.stroke();if(pulse<1){arc.strokeStyle=rgb(palette.a,(1-pulse)*.5);arc.beginPath();arc.arc(x,y,4+pulse*14,0,TAU);arc.stroke();}}
if(k===0&&label){label.style.left=x+'px';label.style.top=y+'px';label.style.opacity=String(smooth((head-.85)/.15)*fade*smooth((x-10)/20)*smooth((w-100-x)/20)*smooth((y-20)/20)*smooth((h-20-y)/20));}
}
};
const clock=loop(element,draw,{...options,continuous:true});const off=watchColors(element,p=>{palette=p;clock.invalidate();});const ro=new ResizeObserver(()=>clock.invalidate());ro.observe(element.querySelector('.card-stage'));
const modes=new MutationObserver(()=>clock.invalidate());modes.observe(element,{attributes:true,attributeFilter:['data-mode']});
return {destroy(){clock.destroy();off();ro.disconnect();modes.disconnect();gpu?.destroy();cleanShell();if(options.mode!==undefined){if(previousMode===null)element.removeAttribute('data-mode');else element.setAttribute('data-mode',previousMode);}const label=element.querySelector('.globe-label');label.removeAttribute('style');label.hidden=false;}};
}
return {mount};
})();
modules["line-ribbons/card.js"] = (() => {
const {shell,loop,svgStops} = modules["shared/runtime.js"];
const {createSurface} = modules["line-ribbons/surface.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const svg=element.querySelector('.ribbon-art'),ns='http://www.w3.org/2000/svg';
// Unique IDs per instance, no shared counters or page-singleton assumptions.
const id='ribbon-'+crypto.randomUUID();
svg.innerHTML=`<defs><linearGradient id="${id}" x1="0" y1="1" x2="1" y2="0">${svgStops([[0.0,"var(--_card-accent-2)",1.0],[0.5,"var(--_card-accent-2)",1.0],[1.0,"var(--card-accent)",1.0]])}</linearGradient></defs>`;
const ribbons=[
[[-160,390,210,285,850,410,1350,-170],[-160,460,430,465,775,250,1390,-50]],
[[-170,455,400,540,800,50,1120,-210],[-170,310,490,640,855,250,1300,-40]],
[[280,650,650,180,1110,500,1420,60],[480,680,730,420,1150,80,1450,320]]
];
for(let r=0;r<ribbons.length;r++)for(let i=0;i<128;i++){
const t=(1-Math.cos(Math.PI*i/127))/2,[a,b]=ribbons[r],p=a.map((n,j)=>n+(b[j]-n)*t);
const path=document.createElementNS(ns,'path');path.setAttribute('d',`M${p[0]} ${p[1]}C${p.slice(2).join(' ')}`);path.setAttribute('fill','none');path.setAttribute('stroke',`url(#${id})`);path.setAttribute('stroke-width','.8');path.setAttribute('opacity',String(r===1?.64:.46));svg.append(path);
}
let surface=null,destroyed=false;
const frame=()=>svg.setAttribute('viewBox',element.clientWidth<=650?'100 60 560 474':'0 0 1280 474');
const framing=new ResizeObserver(frame);framing.observe(element);frame();
const clock=loop(element,t=>{if(surface)surface.draw(t);svg.style.transform=`translate(${Math.sin(t/17)*5}px,${Math.sin(t/19)*3}px)`;},options);
const setRenderer=value=>{if(destroyed)return null;surface?.destroy();surface=null;if(['webgl','dots','grid'].includes(value))surface=createSurface(element,ribbons,()=>clock.invalidate(),value);const actual=surface?value:'svg';element.dataset.renderer=actual;clock.invalidate();return actual;};
if(['webgl','dots','grid'].includes(element.dataset.renderer))setRenderer(element.dataset.renderer);
return {setRenderer,destroy(){if(destroyed)return;destroyed=true;clock.destroy();surface?.destroy();surface=null;framing.disconnect();clean();svg.replaceChildren();svg.style.removeProperty('transform');}};
}
return {mount};
})();
modules["showcase/card.js"] = (() => {
const {loop, shell, svgStops} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,{...options,trigger:options.content?.href!==undefined?null:element.querySelector('.showcase-link')});
const art=element.querySelector('.showcase-art'),type=element.dataset.background||'glow-shapes',id='s'+crypto.randomUUID();
const defs=`<defs>
<filter id="${id}-grain"><feTurbulence type="fractalNoise" baseFrequency=".72" numOctaves="2" seed="8" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
<filter id="${id}-edge" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6"/></filter>
<filter id="${id}-fog" x="-80%" y="-80%" width="260%" height="260%"><feGaussianBlur stdDeviation="16"/></filter>
<radialGradient id="${id}-heart" cx="50%" cy="49%" r="63%">${svgStops([[0.0,"var(--_card-accent-3)",1.0],[0.3,"var(--_card-accent-3)",1.0],[0.56,"var(--_card-accent-2)",1.0],[0.88,"var(--card-accent)",1.0],[1.0,"var(--card-accent)",0.0]])}</radialGradient>
<linearGradient id="${id}-back" x1="0" y1="0" x2="1" y2="1">${svgStops([[0.0,"var(--card-accent)",1.0],[1.0,"color-mix(in oklab,var(--card-accent) 48%,#10121b)",1.0]])}</linearGradient>
<pattern id="${id}-grid" width="24" height="24" patternUnits="userSpaceOnUse"><path d="M24 0H0V24" fill="none" stroke="white" stroke-opacity=".14" stroke-width=".7"/></pattern>
<radialGradient id="${id}-fade">${svgStops([[0.0,"white",1.0],[0.62,"white",1.0],[1.0,"black",1.0]])}</radialGradient><mask id="${id}-mask"><rect width="332" height="448" fill="url(#${id}-fade)"/></mask>
<linearGradient id="${id}-memA" x1="0" y1="0" x2=".4" y2="1">${svgStops([[0.0,"var(--_card-accent-2)",0.04],[0.44,"var(--_card-accent-2)",0.55],[0.51,"white",0.35],[0.62,"var(--card-accent)",0.4],[1.0,"var(--card-accent)",0.04]])}</linearGradient>
<linearGradient id="${id}-memB" x1="0" y1="0" x2="1" y2="1">${svgStops([[0.0,"var(--_card-accent-3)",0.03],[0.45,"var(--_card-accent-3)",0.46],[0.52,"white",0.32],[0.7,"var(--_card-accent-2)",0.26],[1.0,"var(--card-accent)",0.04]])}</linearGradient>
</defs>`;
let content='';
if(type==='glow-shapes'){
const paths={heart:'M0 31C-51 0-38-39-15-29Q0-25 0-13Q0-25 15-29C38-39 51 0 0 31Z',circle:'M32 0A32 32 0 1 0-32 0A32 32 0 1 0 32 0',star:'M0-33 9-10 33-10 14 5 21 30 0 15-21 30-14 5-33-10-9-10Z',petal:'M0-36C21-35 35-21 28-4L9 34Q-24 24-24 1Q-19-31 0-36Z'};
const shape=paths[options.shape||element.dataset.shape]||paths.heart;
for(let i=0;i<5;i++){const a=-Math.PI/2+i*Math.PI*2/5,x=166+75*Math.cos(a),y=170+75*Math.sin(a),rotation=[-5,14,-9,10,-13][i];content+=`<g transform="translate(${x} ${y}) rotate(${rotation})"><path d="${shape}" fill="var(--card-accent)" opacity=".48" filter="url(#${id}-fog)"/><path d="${shape}" fill="url(#${id}-heart)" filter="url(#${id}-edge)"/><path d="${shape}" transform="scale(.88)" fill="url(#${id}-heart)" opacity=".8"/></g>`;}
}else if(type==='grid-solid'){
content=`<rect width="332" height="448" fill="url(#${id}-grid)" mask="url(#${id}-mask)"/><path d="M244 130 70 256 96 303 270 177Z" fill="url(#${id}-back)"/><path d="M70 124 244 130 270 177 96 171Z" fill="var(--card-accent)"/><path d="M70 256 244 262 270 309 96 303Z" fill="var(--card-accent)"/><path d="M244 130 270 177M70 256 96 303" fill="none" stroke="color-mix(in oklab,var(--card-accent) 65%,white)" stroke-width="1"/>`;
}else if(type==='iso-object'){
content=`<path d="M156 252 216 216 276 252 216 288Z" fill="#14101c"/><g stroke="#2b1b38" stroke-width=".85" stroke-linejoin="round"><path d="M108 130 168 94 228 130 168 166Z" fill="var(--_card-accent-2)"/><path d="M108 130 168 166 168 246 108 210Z" fill="var(--_card-accent-3)"/><path d="M168 166 228 130 228 210 168 246Z" fill="var(--card-accent)"/></g>`;
}else if(type==='membrane'){
content=`<g class="membrane-folds"><path d="M-90 392C26 172 164 433 400-80L455 207C270 357 100 453-90 392Z" fill="url(#${id}-memA)"/><path d="M-69 279C124 418 157-2 399 72L392 436C235 376 110 555-69 279Z" fill="url(#${id}-memB)"/><path d="M-25 364C129 166 211 324 373 91L425 271C266 237 166 424-25 364Z" fill="url(#${id}-memA)" opacity=".7"/><g fill="none" stroke="white" stroke-opacity=".75" stroke-width=".8"><path d="M-90 392C26 172 164 433 400-80"/><path d="M-69 279C124 418 157-2 399 72"/><path d="M-25 364C129 166 211 324 373 91"/></g><g fill="none" stroke="white" stroke-opacity=".18" stroke-width=".4"><path d="M-68 401C51 225 174 426 405-56"/><path d="M-53 409C66 258 201 425 414-32"/><path d="M-48 296C133 412 179 22 405 88"/></g></g>`;
}
const hasImage=type==='image'||type==='editorial';
if(!hasImage)art.innerHTML=`<svg viewBox="0 0 332 448" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">${defs}<g class="showcase-subject">${content}</g><rect width="332" height="448" filter="url(#${id}-grain)" opacity=".06"/></svg>`;
const fold=art.querySelector('.membrane-folds'),subject=art.querySelector('.showcase-subject');
const clock=loop(element,t=>{if(fold)fold.setAttribute('transform',`translate(${Math.sin(t/15)*3} ${Math.cos(t/17)*4})`);else if(subject&&type==='glow-shapes')subject.setAttribute('transform',`translate(0 ${Math.sin(t/12)*2})`);},{...options,continuous:!!fold||type==='glow-shapes'});
return {destroy(){clock.destroy();clean();if(!hasImage)art.replaceChildren();}};
}
return {mount};
})();
modules["ui-slice/card.js"] = (() => {
const {loop,shell,smooth,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const row=element.querySelector('.slice-demo'),box=row.querySelector('i'),bar=element.querySelector('.slice-progress i'),count=element.querySelector('.slice-count'),still=staticMotion(element,options);
box.textContent='✓';
const clock=loop(element,time=>{
const staticState=still(),p=staticState?1:smooth((time-1.1)/.32),progress=staticState?1:smooth((time-1.2)/.90);
row.style.setProperty('--task-done',p);row.classList.toggle('checked',p===1);
bar.style.transform=`scaleX(${(1+progress)/3})`;count.textContent=progress===1?'2 / 3':'1 / 3';
},{...options,duration:2.3});
return {replay:()=>clock.restart(),destroy(){clock.destroy();clean();row.classList.remove('checked');row.style.removeProperty('--task-done');box.textContent='';bar.style.removeProperty('transform');count.textContent='1 / 3';}};
}
return {mount};
})();
modules["terminal/card.js"] = (() => {
const {loop,shell,smooth,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const content=options.content || {};
if(Array.isArray(content.lines)){
const container=element.querySelector('.terminal-lines');container.replaceChildren();
const lines=content.lines.length?content.lines:[{text:content.emptyText || '等待运行'}];
for(const item of lines){
const line=typeof item==='string'?{text:item}:item,row=document.createElement('p');
if(['highlight','result','address'].includes(line.tone))row.className='terminal-'+line.tone;
row.append(document.createTextNode(String(line.text ?? '')));
if(line.detail){const detail=document.createElement('span');detail.className='terminal-dim';detail.textContent=' '+line.detail;row.append(detail);}
container.append(row);
}
const cursor=document.createElement('span');cursor.className='terminal-cursor';cursor.textContent=' ▎';container.lastElementChild.append(cursor);
}
const rows=[...element.querySelectorAll('.terminal-lines p')],cursor=element.querySelector('.terminal-cursor'),still=staticMotion(element,options);
const clock=loop(element,time=>{
const staticState=still();
rows.forEach((r,i)=>r.style.setProperty('--line-visible',staticState?1:smooth((time-.12-i*.42)/.52)));
cursor.style.setProperty('--cursor-on',staticState?1:.35+.65*(.5+.5*Math.cos(time*5)));
},{...options,duration:Math.max(1,rows.length*.42+.78)});
return {replay:()=>clock.restart(),destroy(){clock.destroy();clean();rows.forEach(r=>r.style.removeProperty('--line-visible'));cursor.style.removeProperty('--cursor-on');}};
}
return {mount};
})();
modules["metric/card.js"] = (() => {
const {loop,shell,smooth,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const value=element.querySelector('.metric-value'),chart=element.querySelector('.metric-bars');
const content=options.content || {};
if(content.value!==undefined)value.textContent=typeof content.value==='number' && Number.isFinite(content.value)?content.value.toLocaleString('en-US',{maximumFractionDigits:2}):String(content.value);
if(Array.isArray(content.series)){
const series=content.series.filter(n=>typeof n==='number' && Number.isFinite(n) && n>=0),max=Math.max(1,...series);
chart.replaceChildren(...series.map((n,i)=>{const bar=document.createElement('i');bar.style.setProperty('--height',`${n/max*96}%`);if(i===series.length-1)bar.className='current';else if(i>=series.length-6)bar.className='recent';return bar;}));
}
const bars=[...chart.querySelectorAll('i')];
const formatted=value.textContent.trim(),still=staticMotion(element,options);
const digits=[];
value.replaceChildren();
const readable=document.createElement('span');readable.className='metric-readable';readable.textContent=formatted;value.append(readable);
for(const char of formatted){
const slot=document.createElement('span');slot.setAttribute('aria-hidden','true');
if(!/\d/.test(char)){slot.className='metric-separator';slot.textContent=char;value.append(slot);continue;}
slot.className='metric-digit';
const strip=document.createElement('span');strip.className='metric-reel';
const end=Number(char);
for(let i=0;i<=end;i++){const n=document.createElement('span');n.textContent=String(i%10);strip.append(n);}
slot.append(strip);value.append(slot);digits.push({strip,end});
}
const clock=loop(element,time=>{
const staticState=still();
digits.forEach(({strip,end},i)=>{
const p=staticState?1:smooth((time-.12-i*.065)/2.05);
strip.style.transform=`translateY(${-end*p}em)`;
});
bars.forEach((bar,i)=>{
const p=staticState?1:smooth((time-.10-i*.018)/1.85);
bar.style.setProperty('--bar-growth',Math.max(.008,p));
});
element.style.setProperty('--metric-settled',staticState?1:smooth((time-1.70)/.70));
},{...options,duration:Math.max(2.65,bars.length*.018+1.95,digits.length*.065+2.17)});
return {replay:()=>clock.restart(),destroy(){clock.destroy();clean();value.textContent=formatted;element.style.removeProperty('--metric-settled');bars.forEach(b=>b.style.removeProperty('--bar-growth'));}};
}
return {mount};
})();
modules["beam-graph/card.js"] = (() => {
const {loop,shell,smooth} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const svg=element.querySelector('svg'),id='beam-'+crypto.randomUUID(),defs=svg.querySelector('defs');
const source=[...svg.querySelectorAll('.beam-base path')];
defs.innerHTML=`<filter id="${id}-blur" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="3"/></filter>`;
const beams=source.map((path,i)=>{const gid=id+'-'+i;defs.insertAdjacentHTML('beforeend',`<linearGradient id="${gid}" gradientUnits="userSpaceOnUse"><stop stop-color="var(--card-accent)" stop-opacity="0"/><stop offset=".55" stop-color="var(--card-accent)"/><stop offset="1" stop-color="color-mix(in oklab,var(--card-accent) 25%,white)"/></linearGradient>`);const copies=['beam-glow','beam-light'].map(cls=>{const n=path.cloneNode();n.setAttribute('stroke',`url(#${gid})`);if(cls==='beam-glow')n.setAttribute('filter',`url(#${id}-blur)`);svg.querySelector('.'+cls).append(n);return n;});return {path,length:path.getTotalLength(),gradient:defs.querySelector('#'+gid),copies};});
const clock=loop(element,time=>{for(const [i,b]of beams.entries()){const phase=((time+i*.71+1)%4)/4,head=phase*(b.length+72),tail=Math.max(0,head-72),a=b.path.getPointAtLength(tail),z=b.path.getPointAtLength(Math.min(head,b.length));for(const [n,v]of [['x1',a.x],['y1',a.y],['x2',z.x],['y2',z.y]])b.gradient.setAttribute(n,v);for(const p of b.copies){p.style.strokeDashoffset=String(72-head);p.style.opacity=String(smooth(head/28)*(1-smooth((head-b.length)/72)));}}},options);
return {destroy(){clock.destroy();clean();svg.querySelector('.beam-light').replaceChildren();svg.querySelector('.beam-glow').replaceChildren();defs.replaceChildren();}};
}
return {mount};
})();
modules["keycaps/card.js"] = (() => {
const {loop,shell,smooth,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const keys=[...element.querySelectorAll('.keycap')],stage=element.querySelector('.card-stage'),still=staticMotion(element,options);
const clock=loop(element,time=>{
const staticState=still();
keys.forEach((key,i)=>{
const down=smooth((time-.7-i*.085)/.23),up=smooth((time-1.12-i*.045)/.46);
key.style.setProperty('--key-press',staticState?0:down*(1-up));
});
stage.style.setProperty('--command-visible',staticState?1:smooth((time-.98)/.48));
},{...options,duration:1.85});
return {replay:()=>clock.restart(),destroy(){clock.destroy();clean();keys.forEach(k=>k.style.removeProperty('--key-press'));stage.style.removeProperty('--command-visible');}};
}
return {mount};
})();
modules["promo-band/card.js"] = (() => {
const {shell} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
// A real link: content and destination stay in HTML; no extra geometry knobs.
return {destroy(){clean();}};
}
return {mount};
})();
modules["vector-space/card.js"] = (() => {
const {loop,shell,random,glProgram,fitCanvas,watchColors,mix,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const canvas=element.querySelector('canvas');
const vertex=`attribute vec3 a_position;attribute float a_selected;uniform vec2 u_size,u_rotation;uniform float u_dpr,u_halo;varying float v_selected,v_depth;
void main(){float c=cos(u_rotation.x),s=sin(u_rotation.x);vec3 p=a_position;p.xz=mat2(c,-s,s,c)*p.xz;c=cos(u_rotation.y);s=sin(u_rotation.y);p.yz=mat2(c,-s,s,c)*p.yz;
float perspective=1./(1.+p.z*.24);v_depth=perspective;v_selected=a_selected;
gl_Position=vec4(p.x*perspective*.94/(u_size.x/u_size.y),p.y*perspective*.84-.02,0.,1.);
gl_PointSize=mix(2.5,7.,a_selected)*u_dpr*perspective+u_halo*a_selected*15.*u_dpr;}`;
const fragment=`precision highp float;uniform vec3 u_accent,u_ink;uniform float u_points,u_halo,u_lineAlpha;varying float v_selected,v_depth;
void main(){float a=1.;if(u_points>.5){float r=length(gl_PointCoord-.5)*2.;a=1.-smoothstep(.65,1.,r);if(u_halo>.5){a=exp(-r*r*5.)*.23*v_selected;}}
vec3 color=mix(u_ink,u_accent,v_selected);a*=u_points>.5?mix(.18,.95,v_selected)*v_depth:u_lineAlpha;
gl_FragColor=vec4(color*a,a);}`;
const gpu=glProgram(canvas,vertex,fragment),rng=random(29),positions=[],selected=[];
const add=(v,s=0)=>{positions.push(...v);selected.push(s);};
const corners=Array.from({length:8},(_,i)=>[(i&1?.65:-.65),(i&2?.65:-.65),(i&4?.65:-.65)]);
for(let i=0;i<8;i++)for(const bit of [1,2,4])if(!(i&bit)){add(corners[i]);add(corners[i|bit]);}
const related=[[-.08,-.06,.28],[.12,.04,.35],[.29,-.12,.23],[.08,-.27,.29]];
for(let i=0;i<3;i++){add(related[i],1);add(related[i+1],1);}
for(let i=0;i<140;i++)add([(rng()-.5)*2.1,(rng()-.5)*1.75,(rng()-.5)*1.8]);
related.forEach(v=>add(v,1));
if(gpu){gpu.attribute('a_position',positions,3);gpu.attribute('a_selected',selected,1);element.dataset.vectorReady='true';}
else canvas.hidden=true;
let palette,tx=0,ty=0,rx=0,ry=0,lost=false;
const still=staticMotion(element,options);
const draw=(time,dt)=>{if(!gpu||!palette||lost)return;if(still())rx=ry=tx=ty=0;if(dt){rx=mix(rx,tx,1-Math.exp(-dt*2.5));ry=mix(ry,ty,1-Math.exp(-dt*2.5));}const {w,h,dpr}=fitCanvas(canvas);const {gl}=gpu;
gl.useProgram(gpu.program);gl.viewport(0,0,canvas.width,canvas.height);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
gl.uniform2f(gpu.uniform('u_size'),w,h);gl.uniform2f(gpu.uniform('u_rotation'),-.62+Math.sin(time*.12)*.06+rx,.35+ry);
gl.uniform1f(gpu.uniform('u_dpr'),dpr);gl.uniform3fv(gpu.uniform('u_accent'),palette.a);gl.uniform3fv(gpu.uniform('u_ink'),palette.dark?[.8,.84,.9]:[.18,.23,.3]);
gl.uniform1f(gpu.uniform('u_halo'),0);gl.uniform1f(gpu.uniform('u_points'),0);gl.uniform1f(gpu.uniform('u_lineAlpha'),.21);gl.drawArrays(gl.LINES,0,24);
gl.uniform1f(gpu.uniform('u_lineAlpha'),.32);gl.drawArrays(gl.LINES,24,6);
gl.uniform1f(gpu.uniform('u_points'),1);gl.uniform1f(gpu.uniform('u_halo'),1);gl.drawArrays(gl.POINTS,30,144);
gl.uniform1f(gpu.uniform('u_halo'),0);gl.drawArrays(gl.POINTS,30,144);
};
const clock=loop(element,draw,{...options,settled:()=>Math.abs(rx-tx)+Math.abs(ry-ty)<.001}),off=watchColors(element,p=>{palette=p;clock.invalidate();}),ro=new ResizeObserver(()=>clock.invalidate());ro.observe(canvas);
const move=e=>{if(e.pointerType==='touch' || still())return;const r=element.getBoundingClientRect();tx=((e.clientX-r.left)/r.width-.5)*.25;ty=((e.clientY-r.top)/r.height-.5)*.16;};
const leave=()=>{tx=ty=0;},loss=e=>{e.preventDefault();lost=true;canvas.hidden=true;delete element.dataset.vectorReady;};
element.addEventListener('pointermove',move);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);canvas.addEventListener('webglcontextlost',loss);
return{destroy(){clean();clock.destroy();off();ro.disconnect();gpu?.destroy();element.removeEventListener('pointermove',move);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);canvas.removeEventListener('webglcontextlost',loss);delete element.dataset.vectorReady;}};
}
return {mount};
})();
modules["file-mosaic/card.js"] = (() => {
const {shell} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const grid=element.querySelector('.file-grid');
const icons={doc:'<path d="M7 3h13l6 6v21H7Z M20 3v7h6 M12 16h9 M12 21h9"/>',image:'<rect x="3" y="5" width="28" height="24" rx="3"/><circle cx="12" cy="13" r="3"/><path d="m5 26 8-8 5 4 5-7 7 9"/>',video:'<rect x="3" y="7" width="20" height="21" rx="3"/><path d="m23 13 8-4v17l-8-4"/>',code:'<path d="m11 10-8 7 8 7 M23 10l8 7-8 7 M19 5l-5 24"/>'};
const fallback=[['image','cover.png'],['color','palette.fig'],['doc','brief.pdf'],['video','intro.mp4'],['code','tokens.json'],['audio','theme.wav'],['image','texture.jpg'],['doc','notes.md'],['image','poster.png'],['video','reel.mov'],['doc','license.txt'],['code','theme.css'],['image','scene.webp'],['audio','voice.wav'],['doc','guide.pdf']].map(([type,name])=>({type,name}));
const files=Array.isArray(options.content?.files)?options.content.files:fallback;
grid.replaceChildren();
for(const file of files){
const type=file.type || 'doc',tile=document.createElement('div'),art=document.createElement('div'),label=document.createElement('span');
tile.className='file-tile';art.className='file-art';
art.innerHTML=type==='color'?'<div class="file-thumbnail"><i></i><i></i><i></i><i></i></div>':type==='audio'?'<div class="file-wave">'+[12,23,32,17,27,35,22,13,24].map(h=>`<i style="--h:${h}px"></i>`).join('')+'</div>':`<svg viewBox="0 0 34 34">${icons[type] || icons.doc}</svg>`;
label.textContent=String(file.name ?? '');label.title=label.textContent;tile.append(art,label);grid.append(tile);
}
if(!files.length){const empty=document.createElement('p');empty.className='file-empty';empty.textContent=options.content?.emptyText ?? '还没有文件';grid.append(empty);}
element.querySelector('.file-collection i').hidden=!files.length;
if(!files.length && options.content?.status===undefined)element.querySelector('[data-text="status"]').textContent='暂无文件';
return{destroy(){clean();grid.replaceChildren();}};
}
return {mount};
})();
modules["stream-window/card.js"] = (() => {
const {loop,shell,smooth,staticMotion} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
const chunks=[...element.querySelectorAll('.stream-chunk')],count=element.querySelector('.stream-count'),status=element.querySelector('.stream-status > span'),still=staticMotion(element,options);
const total=chunks.length,pending=options.content?.pendingStatus ?? '内容陆续就绪',complete=options.content?.completeStatus ?? '全部内容已就绪';
let previous=-1;
const clock=loop(element,time=>{
const staticState=still();let ready=0;
chunks.forEach((chunk,i)=>{
const v=staticState?1:smooth((time-.15-i*.65)/.72);
chunk.style.setProperty('--ready',v);
chunk.style.setProperty('--skeleton-opacity',1-smooth(v/.38));
chunk.style.setProperty('--content-opacity',smooth((v-.42)/.58));
if(v===1)ready++;
});
if(ready!==previous){count.textContent=`${ready} / ${total}`;status.textContent=ready===total?complete:pending;previous=ready;}
},{...options,duration:2.35});
return{replay:()=>clock.restart(),destroy(){clock.destroy();clean();chunks.forEach(c=>{for(const p of ['--ready','--skeleton-opacity','--content-opacity'])c.style.removeProperty(p);});count.textContent=`${total} / ${total}`;status.textContent=complete;}};
}
return {mount};
})();
modules["portrait-story/card.js"] = (() => {
const {shell} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options);
return {destroy(){clean();}};
}
return {mount};
})();
modules["capture-notes/card.js"] = (() => {
const {shell,loop,staticMotion,clamp} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options),content=options.content || {},rows=element.querySelector('.capture-rows'),now=element.querySelector('.capture-now'),bars=[...element.querySelectorAll('.capture-wave i')],button=element.querySelector('.capture-pause');
const defaults=[{title:'首页设计评审',team:'设计'},{title:'内容与视觉方向',team:'创作'},{title:'本周项目同步',team:'产品'},{title:'新想法的第一轮讨论',team:'团队'},{title:'发布前的最后一次检查',team:'工程'}];
const records=(Array.isArray(content.records)?content.records:defaults).filter(record=>record && typeof record==='object');
const still=staticMotion(element,options),events=new AbortController(),tones=['indigo','teal','amber','rose','slate'];let clock,paused=false,phase=0,selected=0;
rows.replaceChildren();
const select=index=>{selected=index;now.textContent=String(records[index]?.title ?? content.emptyText ?? '还没有讨论记录');now.title=String(records[index]?.detail ?? now.textContent);[...rows.children].forEach((r,i)=>r.setAttribute('aria-pressed',String(i===index)));clock?.invalidate();};
records.forEach((record,index)=>{
const row=document.createElement('button'),name=document.createElement('span'),tags=document.createElement('span');row.type='button';row.className='capture-row';tags.className='capture-tags';name.textContent=String(record.title ?? '');
const values=Array.isArray(record.tags)?record.tags:[{label:record.team ?? '',tone:record.tone}];
values.forEach((value,i)=>{const tag=document.createElement('small'),label=typeof value==='object'?value?.label:value,tone=typeof value==='object'?value?.tone:undefined;tag.textContent=String(label ?? '');tag.dataset.tone=tones.includes(tone)?tone:tones[(index+i)%tones.length];tags.append(tag);});
row.append(name,tags);row.addEventListener('click',()=>select(index),{signal:events.signal});rows.append(row);
});select(0);
const start=()=>{clock?.destroy();clock=loop(element,(time,dt)=>{
if(!paused)phase+=dt;
const waveform=records[selected]?.waveform;
bars.forEach((b,i)=>{const sample=Array.isArray(waveform)&&waveform.length?waveform[i%waveform.length]:undefined,base=typeof sample==='number'&&Number.isFinite(sample)?clamp(sample):undefined;
const value=base!==undefined?clamp(base*(still()?1:.8+.2*Math.sin(phase*3.2+i*.85)),.08,1):still()?.25+.5*Math.abs(Math.sin(i*1.8)):.2+.8*Math.abs(Math.sin(phase*3.2+i*.85)*Math.cos(phase*1.4-i*.32));b.style.transform=`scaleY(${value})`;
});
},{...options,continuous:!paused&&records.length>0});};
const syncButton=()=>{button.setAttribute('aria-label',paused?'播放波形动画':'暂停波形动画');button.setAttribute('aria-pressed',String(paused));button.querySelector('path').setAttribute('d',paused?'m7 4 8 6-8 6Z':'M7 5v10M13 5v10');};
button.disabled=!records.length;syncButton();button.addEventListener('click',()=>{paused=!paused;syncButton();start();},{signal:events.signal});start();
return {destroy(){clock.destroy();events.abort();clean();rows.replaceChildren();bars.forEach(bar=>bar.style.removeProperty('transform'));}};
}
return {mount};
})();
modules["answer-lens/card.js"] = (() => {
const {shell,clamp} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options),events=new AbortController(),list=element.querySelector('.answer-citations'),excerpt=element.querySelector('.answer-excerpt'),reference=element.querySelector('.answer-reference'),charts=[...element.querySelectorAll('[data-chart]')];
const defaults=[{label:'项目进度',answer:'核心页面已完成评审,下一步可以集中打磨移动端体验。',citation:'项目记录 · 本周评审'},{label:'内容表现',answer:'实用教程获得了更多收藏,可以继续拓展成一个系列。',citation:'内容记录 · 系列表现'},{label:'用户反馈',answer:'大家最常提到的是查找素材,优先把检索入口做清楚。',citation:'反馈记录 · 需求整理'}];
const sources=(Array.isArray(options.content?.sources)?options.content.sources:defaults).filter(source=>source && typeof source==='object').slice(0,3);
const select=index=>{[...list.children].forEach((b,i)=>b.setAttribute('aria-pressed',String(i===index)));charts.forEach((c,i)=>c.dataset.selected=String(i===index));excerpt.textContent=String(sources[index]?.answer ?? options.content?.emptyText ?? '还没有可引用的来源。');reference.textContent=String(sources[index]?.citation ?? '');reference.hidden=!reference.textContent;};
list.replaceChildren();
charts.forEach((chart,i)=>{
chart.hidden=!sources[i];if(!sources[i])return;
chart.querySelector('[data-text]').textContent=String(sources[i].label ?? '');
const data=sources[i].chart;if(!data || typeof data!=='object')return;
if(i===0 && typeof data.value==='number' && Number.isFinite(data.value)){
const max=typeof data.max==='number' && Number.isFinite(data.max) && data.max>0?data.max:100,ratio=clamp(data.value/max),length=2*Math.PI*48;
chart.querySelector('.ring-value').style.strokeDasharray=`${length*ratio} ${length*(1-ratio)}`;
}
if(i>0 && Array.isArray(data.series)){
const series=data.series.filter(n=>typeof n==='number' && Number.isFinite(n)).slice(0,32),path=chart.querySelector(i===1?'.chart-bars':'.chart-line');
const low=Math.min(0,...series),high=Math.max(1,...series),y=value=>115-(value-low)/(high-low)*95;
if(i===1){path.setAttribute('d',series.map((n,j)=>`M${20+(j+.5)*120/series.length} 115V${y(n)}`).join(' '));path.style.strokeWidth=String(Math.min(15,72/Math.max(1,series.length)));}
else path.setAttribute('d',series.map((n,j)=>`${j?'L':'M'}${series.length===1?80:15+j*130/(series.length-1)} ${y(n)}`).join(' '));
}
});
sources.forEach((source,index)=>{const b=document.createElement('button');b.type='button';b.textContent=`${index+1} · ${source.label ?? ''}`;b.addEventListener('click',()=>select(index),{signal:events.signal});list.append(b);});select(0);
return {destroy(){events.abort();clean();list.replaceChildren();charts.forEach(chart=>{delete chart.dataset.selected;chart.querySelector('.ring-value')?.style.removeProperty('stroke-dasharray');chart.querySelector('.chart-bars')?.style.removeProperty('stroke-width');});}};
}
return {mount};
})();
modules["responsive-preview/card.js"] = (() => {
const {shell,loop,staticMotion,smooth} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options),still=staticMotion(element,options),button=element.querySelector('.size-toggle');let value=0,target=1,clock,destroyed=false;
const animate=next=>{const from=value;target=next;clock?.destroy();button.setAttribute('aria-pressed',String(!!target));button.firstChild.textContent=target?'收起尺寸 ':'比较尺寸 ';clock=loop(element,time=>{value=still()?target:from+(target-from)*smooth(time/.9);element.style.setProperty('--spread',value);},{...options,duration:.9});};
const toggle=()=>animate(target?0:1);button.addEventListener('click',toggle);animate(1);
return{replay(){if(!destroyed){value=0;animate(1);}},destroy(){if(destroyed)return;destroyed=true;clock.destroy();button.removeEventListener('click',toggle);clean();element.style.removeProperty('--spread');}};
}
return {mount};
})();
modules["deploy-preview/card.js"] = (() => {
const {shell,loop,staticMotion,smooth} = modules["shared/runtime.js"];
function mount(element, options = {}) {
const clean=shell(element,options),still=staticMotion(element,options),buttons=[...element.querySelectorAll('[data-version]')],steps=[...element.querySelectorAll('.deploy-steps > span')],status=element.querySelector('.deploy-status'),events=new AbortController();
const versions=options.content?.versions || [{title:'更新作品首页',branch:'design/home',url:'preview.workspace.local'},{title:'整理作品目录',branch:'design/collection',url:'archive.workspace.local'}];let clock,index=0,last=-1,destroyed=false;
function show(next,animate=false){index=next;const version=versions[index] || {};buttons.forEach((b,i)=>{b.hidden=!versions[i];b.setAttribute('aria-pressed',String(i===index));});element.querySelectorAll('[data-release-field]').forEach(el=>el.textContent=String(version[el.dataset.releaseField] ?? ''));clock?.destroy();last=-1;clock=loop(element,time=>{const t=still()||!animate?2.5:time,progress=smooth(t/2),ready=smooth((t-1.6)/.9);element.style.setProperty('--build-progress',progress);element.style.setProperty('--preview-ready',ready);const stage=t<.65?0:t<1.6?1:2;steps.forEach((s,i)=>s.dataset.ready=String(t>=(i+1)*.6));if(stage!==last){status.textContent=(options.content?.statuses || ['准备中','构建中','预览就绪'])[stage];last=stage;}},{...options,duration:2.5});}
buttons.forEach((b,i)=>b.addEventListener('click',()=>show(i),{signal:events.signal}));show(0,true);
return{replay(){if(!destroyed)show(index,true);},destroy(){if(destroyed)return;destroyed=true;clock.destroy();events.abort();clean();for(const property of ['--build-progress','--preview-ready'])element.style.removeProperty(property);steps.forEach(step=>delete step.dataset.ready);}};
}
return {mount};
})();
// Gallery examples only. Components do not depend on these scenarios.
const contentScenes = {
studio: {
'portrait-story':{brand:'morrow / journal',quote:'让工具退后一步,\n把空间留给真正的创作。',author:'创作手记',role:'关于工具与创作'},
'capture-notes':{title:'把每一次讨论,\n留在同一份记录里。'},
'answer-lens':{title:'找到答案,\n也看见它从哪里来。'},
'responsive-preview':{title:'一份内容,\n在不同屏幕上自然展开。'},
'deploy-preview':{title:'每一次改动,\n都能先预览,再分享。'},
'particle-ring': {title:'把需求说清楚,\n找到合适的选择'},
'flow-object': {title:'让数字身份,\n拥有可以触碰的形态',brand:'identity',identifier:'ID / 0842',hash:'7F2A:9C04',status:'VERIFIED'},
'dot-globe': {title:'连接不同地区,\n保持同一步调',label:'巴黎 · 已连接'},
'line-ribbons': {title:'各有方向,\n也能一起前进',description:'让不同的想法,在这里汇合。'},
'ui-slice': {title:'从计划到完成,进度始终清楚',description:'把任务、负责人和完成状态放在一起。'},
terminal: {title:'把重复的步骤,交给工具',description:'一次运行,完成构建、检查与发布。'},
metric: {title:'用数据看见每一次进展',description:'过去 30 天 · 工作流运行次数'},
'beam-graph': {title:'连接工具,也连接工作',description:'从输入到结果,让信息沿着同一条路径流动。'},
keycaps: {title:'常用操作,一步直达',description:'少一次寻找,多一点专注。'},
'promo-prism': {title:'一起开始。',description:'找到需要的资源,与团队一起推进下一项计划。',action:'查看资源',href:'#explorations'},
'promo-facets': {title:'从这里出发。',description:'把工具和内容准备好,开始你的下一件作品。',action:'浏览作品',href:'#showcase'},
'vector-space': {title:'不只匹配关键词,\n也理解内容的关联',query:'自然的灵感',result:'4 个相关结果',mode:'语义搜索'},
'file-mosaic': {title:'不同类型的文件,\n放在同一个空间',collection:'品牌素材',status:'所有文件已同步'},
'stream-window': {title:'内容逐步就绪,\n先看到重要的部分'},
morrow:{description:'用有辨识度的视觉,介绍你的产品。',action:'查看品牌故事'},
duotone:{description:'让人物和色彩,传达品牌的个性。',action:'查看视觉方向'},
serein:{description:'用真实的纹理,为内容留出呼吸。',action:'查看影像表达'},
folded:{description:'用清楚的形体,说明产品的结构。',action:'查看产品表达'},
cubiq:{description:'把抽象的概念,变成可以理解的形状。',action:'查看空间表达'},
vellum:{description:'保留轻盈的层次,让文字更容易被看见。',action:'查看材质表达'}
},
developer: {
'portrait-story':{brand:'fieldnotes / engineering',quote:'把复杂留给系统,\n把清楚留给使用它的人。',author:'构建笔记',role:'关于工程与体验'},
'capture-notes':{title:'让技术讨论,\n成为可以查阅的上下文。',collection:'工程记录',tab:'所有讨论',scope:'我的项目',records:[{title:'查询接口设计评审',team:'后端'},{title:'状态与加载边界',team:'前端'},{title:'预览环境接入',team:'工程'},{title:'移动端性能复盘',team:'体验'},{title:'下一版本的验收范围',team:'产品'}]},
'answer-lens':{title:'从项目资料里,\n找到有依据的答案。',eyebrow:'工程知识库',dashboard:'项目运行概览',question:'接下来应该先优化哪里?',sources:[{label:'运行日志',answer:'高峰时段集中出现重复查询,先检查缓存与请求合并。'},{label:'性能记录',answer:'首屏最重的是图片资源,可以优先调整加载顺序与尺寸。'},{label:'反馈工单',answer:'更多用户需要看懂失败原因,优先补齐可恢复的错误反馈。'}]},
'responsive-preview':{title:'布局会改变,\n内容的主次依然清楚。',description:'同一份资源,适配不同的显示空间。',labels:['资源原图','桌面视图','移动视图']},
'deploy-preview':{title:'改动可以预览,\n讨论就有了共同的页面。',project:'workspace / application',versions:[{title:'完善查询接口',branch:'feat/query',url:'query.preview.local'},{title:'补齐登录状态',branch:'feat/session',url:'session.preview.local'}],resultNote:'在进入正式环境之前,一起确认细节。'},
'particle-ring': {title:'让自然语言,\n成为产品入口',prompt:'想找适合周末出行的衣服,\n有什么推荐?',reply:'根据你的需求,\n这两件适合轻装出发。',action:'查看推荐结果'},
'flow-object': {title:'把访问权限,\n放进一枚实体通证',brand:'access',identifier:'ID / 1024',edition:'WORKSPACE / 02',hash:'A104:7E2D',status:'AUTHORIZED',specs:[{label:'PROTOCOL',value:'OPEN / 02'},{label:'SCOPE',value:'AGENT'},{label:'REVISION',value:'2026.10'}]},
'dot-globe': {title:'从本地出发,\n服务不同地区',label:'巴黎 · 节点在线'},
'line-ribbons': {title:'独立构建,\n顺畅协作',description:'让数据、服务和团队保持连接。'},
'ui-slice': {title:'让每次交付,都有清楚的进度',description:'把发布前的检查,放进同一份清单。',board:'本次迭代',columns:['待处理','进行中'],backlog:['编写接口文档','补充测试用例','集成登录服务','验证预览环境'],listTitle:'版本发布检查',tasks:[{title:'完成代码评审',owner:'李'},{title:'通过集成测试',owner:'周'},{title:'确认发布窗口',owner:'陈'}],progressLabel:'发布检查'},
terminal: {title:'一条命令,完成环境检查',description:'运行步骤和输出结果,按顺序呈现。',path:'~/workspace',shell:'shell',lines:[{text:'› mono check --target=preview'},{text:'读取项目配置',detail:'mono.config'},{text:'✓ 依赖检查完成',tone:'highlight',detail:'1.2s'},{text:'✓ 连接预览环境'},{text:'✓ 所有检查通过',tone:'result'},{text:'preview.mono.local',tone:'address'}]},
metric: {title:'每一次调用,都有据可查',description:'过去 24 小时 · API 请求量',value:86532,delta:'↗ 12.4%',latest:'最近一小时 4,208',series:[640,760,580,820,910,880,1240,1380,1120,1800,1680,2040,1950,2160,2480,2320,2780,3100,2950,3450,3280,3780,3900,4208]},
'beam-graph': {title:'把服务串起来,让数据流起来',description:'不同来源的事件,在同一条工作流中处理。',nodes:['事件','数据','队列','存储','通知'],hub:'API'},
keycaps: {title:'用快捷键,直接找到下一步',description:'打开命令面板,搜索文件、操作或文档。',modifier:'⌘',modifierName:'command',key:'K',keyName:'command menu',placeholder:'搜索文件、命令或文档…'},
'promo-prism': {title:'快速接入。',description:'从清楚的接口和示例开始,把能力接进现有产品。',action:'查看组件示例',href:'#explorations'},
'promo-facets': {title:'开始构建。',description:'复用已有的视觉与交互,把时间留给产品本身。',action:'浏览视觉模板',href:'#showcase'},
'vector-space': {title:'描述你要找什么,\n找到相关的答案',query:'如何部署预览环境',result:'4 段相关内容',mode:'知识检索'},
'file-mosaic': {title:'代码、文档和资源,\n在同一处管理',collection:'项目资源',status:'已同步到工作区',files:[{type:'code',name:'client.ts'},{type:'doc',name:'API.md'},{type:'color',name:'tokens.fig'},{type:'image',name:'diagram.png'},{type:'code',name:'schema.json'},{type:'doc',name:'README.md'},{type:'code',name:'worker.ts'},{type:'doc',name:'changelog.md'},{type:'image',name:'preview.webp'},{type:'video',name:'walkthrough.mp4'},{type:'doc',name:'LICENSE'},{type:'code',name:'theme.css'},{type:'image',name:'cover.png'},{type:'audio',name:'intro.wav'},{type:'doc',name:'guide.pdf'}]},
'stream-window': {title:'按需加载内容,\n让等待更短一些',path:'workspace / overview',eyebrow:'项目工作区',heading:'从这里,继续构建。',summary:'打开最近的项目,查看运行状态与交付进度。',visualLabel:'服务拓扑',listTitle:'最近的项目',items:['开发者文档','组件工作台','预览环境'],pendingStatus:'正在加载工作区',completeStatus:'工作区已就绪'},
morrow:{brand:'signal',description:'从一个想法开始,把能力接进你的产品。',action:'查看集成方式',href:'#explorations'},
duotone:{brand:'duotone',description:'把复杂的信息,组织成清楚的表达。',action:'查看内容组件',href:'#explorations'},
serein:{brand:'relay',description:'让数据在服务之间,自然地流动。',action:'查看连接组件',href:'#explorations'},
folded:{brand:'layer',description:'从稳定的结构,构建新的功能。',action:'查看界面组件',href:'#explorations'},
cubiq:{brand:'blocks',description:'把独立能力,组合成完整的体验。',action:'查看组合方式',href:'#explorations'},
vellum:{brand:'canvas',description:'保留空间,让产品内容成为主角。',action:'查看视觉模板',href:'#showcase'}
}
};
const query=new URLSearchParams(location.search);
let theme=query.get('theme')==='dark'||query.get('state')==='dark'?'dark':'light';
const cards=[...document.querySelectorAll('[data-card]')];
const title=['top','bottom'].includes(query.get('title'))?query.get('title'):'default',motion=query.get('motion')||'auto';
function setAttribute(attribute,value){if(attribute==='material')document.documentElement.dataset.material=value;for(const card of cards){if(attribute==='title'&&value==='default')delete card.dataset.title;else card.dataset[attribute]=value;}}
setAttribute('title',title);setAttribute('motion',motion);
const effects=['none','tilt','glow','tilt glow'].includes(query.get('effects'))?query.get('effects'):'none';
setAttribute('effects',effects);
const finishChoices={shadow:['auto','none','contact','lifted'],material:['plain','metal','glass','solid']},finishKey='oil.cards.finish.v2';
let savedFinish={};try{savedFinish=JSON.parse(localStorage.getItem(finishKey)) || {};}catch{}
const requestedMaterial=finishChoices.material.includes(query.get('material'))?query.get('material'):null;
const finish={
material:requestedMaterial || (finishChoices.material.includes(savedFinish.material)?savedFinish.material:'plain'),
shadow:finishChoices.shadow.includes(query.get('shadow'))?query.get('shadow'):requestedMaterial?'auto':finishChoices.shadow.includes(savedFinish.shadow)?savedFinish.shadow:'auto'
};
for(const [key,value] of Object.entries(finish))setAttribute(key,value);
for(const control of document.querySelectorAll('[data-attribute]')){
control.value=({title,motion,effects,...finish})[control.dataset.attribute];
control.addEventListener('change',()=>{
const key=control.dataset.attribute;setAttribute(key,control.value);
if(Object.hasOwn(finishChoices,key)){
finish[key]=control.value;
if(key==='material'){finish.shadow='auto';setAttribute('shadow','auto');document.querySelector('[data-attribute=shadow]').value='auto';}
try{localStorage.setItem(finishKey,JSON.stringify(finish));}catch{}
}
});
}
function appearance(){
document.documentElement.dataset.theme=theme;
const [a,b,c]=paletteEditor.colors;
document.documentElement.style.setProperty('--gallery-accent',a);
for(const card of cards){card.style.setProperty('--card-accent',a);card.style.setProperty('--card-accent-2',b);card.style.setProperty('--card-accent-3',c);card.style.setProperty('--card-radius',document.querySelector('#radius').value+'px');card.dispatchEvent(new Event('card-theme-change'));}
for(const button of document.querySelectorAll('[data-theme-choice]')){const active=button.dataset.themeChoice===theme;button.classList.toggle('selected',active);button.setAttribute('aria-pressed',active);}
}
const paletteEditor=modules['gallery-palette.js'].createPaletteEditor({query,onChange:appearance});
const catalog=modules['gallery-catalog.js'].createCatalog(query);
appearance();
const focus=query.get('focus');
if(focus){document.body.classList.add('focus-only');if(['line-ribbons','metric','promo-band','showcase','explorations','peer-cards'].includes(focus))document.body.classList.add('focus-wide');document.querySelector(`[data-template="${CSS.escape(focus)}"]`)?.classList.add('focused');}
const original=cards.map(card=>card.innerHTML);
function flowVariant(value){document.querySelector('.flow-object').dataset.variant=value;for(const b of document.querySelectorAll('[data-flow-variant]'))b.setAttribute('aria-pressed',String(b.dataset.flowVariant===value));}
function globeMode(value){document.querySelector('.dot-globe').dataset.mode=value;for(const b of document.querySelectorAll('[data-globe-mode]'))b.setAttribute('aria-pressed',String(b.dataset.globeMode===value));}
flowVariant(query.get('variant')==='card'?'card':'token');globeMode(query.get('mode')==='mesh'?'mesh':'globe');
for(const b of document.querySelectorAll('[data-flow-variant]'))b.addEventListener('click',()=>flowVariant(b.dataset.flowVariant));
for(const b of document.querySelectorAll('[data-globe-mode]'))b.addEventListener('click',()=>globeMode(b.dataset.globeMode));
let scene=Object.hasOwn(contentScenes,query.get('content'))?query.get('content'):'studio',mounts=[];
function mountScene(){
mounts.forEach(instance=>instance.destroy());
cards.forEach((card,i)=>{card.innerHTML=original[i];});
mounts=cards.map(card=>modules[card.dataset.card+'/card.js'].mount(card,{speed:Number(query.get('speed')??1),content:contentScenes[scene][card.dataset.example || card.dataset.card] || {}}));
ribbonRenderer(selectedRenderer);
}
let selectedRenderer=['webgl','dots','grid'].includes(query.get('ribbons'))?query.get('ribbons'):'svg';
mountScene();
const sceneControl=document.querySelector('#content-scene');sceneControl.value=scene;
sceneControl.addEventListener('change',()=>{scene=sceneControl.value;mountScene();});
for(const button of document.querySelectorAll('[data-replay]'))button.addEventListener('click',()=>{
mounts.forEach((instance,i)=>{if(button.dataset.replay==='all' || cards[i].dataset.card===button.dataset.replay)instance.replay?.();});
});
function ribbonRenderer(value){const ribbon=mounts[cards.findIndex(card=>card.dataset.card==='line-ribbons')];const actual=ribbon.setRenderer(value);selectedRenderer=actual;for(const b of document.querySelectorAll('[data-ribbon-renderer]'))b.setAttribute('aria-pressed',String(b.dataset.ribbonRenderer===actual));}
for(const b of document.querySelectorAll('[data-ribbon-renderer]'))b.addEventListener('click',()=>ribbonRenderer(b.dataset.ribbonRenderer));
for(const button of document.querySelectorAll('[data-theme-choice]'))button.addEventListener('click',()=>{theme=button.dataset.themeChoice;appearance();});
document.querySelector('#radius').addEventListener('input',e=>{document.querySelector('.radius-control output').value=e.target.value;appearance();});
for(const button of document.querySelectorAll('[data-slide]'))button.addEventListener('click',()=>document.querySelector('.showcase-row').scrollBy({left:Number(button.dataset.slide)*348,behavior:matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth'}));
window.addEventListener('pagehide',event=>{if(!event.persisted){mounts.forEach(instance=>instance.destroy());paletteEditor.destroy();catalog.destroy();}});
})();</script>
</body></html>