@orangecheck/app-design 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/brand.d.mts CHANGED
@@ -23,7 +23,7 @@ interface LockupProps {
23
23
  }
24
24
  declare function Lockup({ name, size, className }: LockupProps): react.JSX.Element;
25
25
 
26
- declare const SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)";
26
+ declare const SPHERE_GRADIENT = "radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)";
27
27
  interface HalftoneSphereProps {
28
28
  src: string;
29
29
  alt: string;
@@ -43,4 +43,15 @@ interface IsoCheckProps {
43
43
  }
44
44
  declare function IsoCheck({ size, settle, shadow, title, className }: IsoCheckProps): react.JSX.Element;
45
45
 
46
- export { AppIcon, type AppIconProps, HalftoneSphere, type HalftoneSphereProps, IsoCheck, type IsoCheckProps, Lockup, type LockupProps, MARK_FACES, Mark, type MarkProps, SPHERE_GRADIENT };
46
+ interface CrowdProps {
47
+ cols?: number;
48
+ rows?: number;
49
+ real?: number;
50
+ seed?: number;
51
+ tone?: 'ink' | 'canvas';
52
+ label?: string;
53
+ className?: string;
54
+ }
55
+ declare function Crowd({ cols, rows, real, seed, tone, label, className, }: CrowdProps): react.JSX.Element;
56
+
57
+ export { AppIcon, type AppIconProps, Crowd, type CrowdProps, HalftoneSphere, type HalftoneSphereProps, IsoCheck, type IsoCheckProps, Lockup, type LockupProps, MARK_FACES, Mark, type MarkProps, SPHERE_GRADIENT };
package/dist/brand.d.ts CHANGED
@@ -23,7 +23,7 @@ interface LockupProps {
23
23
  }
24
24
  declare function Lockup({ name, size, className }: LockupProps): react.JSX.Element;
25
25
 
26
- declare const SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)";
26
+ declare const SPHERE_GRADIENT = "radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)";
27
27
  interface HalftoneSphereProps {
28
28
  src: string;
29
29
  alt: string;
@@ -43,4 +43,15 @@ interface IsoCheckProps {
43
43
  }
44
44
  declare function IsoCheck({ size, settle, shadow, title, className }: IsoCheckProps): react.JSX.Element;
45
45
 
46
- export { AppIcon, type AppIconProps, HalftoneSphere, type HalftoneSphereProps, IsoCheck, type IsoCheckProps, Lockup, type LockupProps, MARK_FACES, Mark, type MarkProps, SPHERE_GRADIENT };
46
+ interface CrowdProps {
47
+ cols?: number;
48
+ rows?: number;
49
+ real?: number;
50
+ seed?: number;
51
+ tone?: 'ink' | 'canvas';
52
+ label?: string;
53
+ className?: string;
54
+ }
55
+ declare function Crowd({ cols, rows, real, seed, tone, label, className, }: CrowdProps): react.JSX.Element;
56
+
57
+ export { AppIcon, type AppIconProps, Crowd, type CrowdProps, HalftoneSphere, type HalftoneSphereProps, IsoCheck, type IsoCheckProps, Lockup, type LockupProps, MARK_FACES, Mark, type MarkProps, SPHERE_GRADIENT };
package/dist/brand.js CHANGED
@@ -93,30 +93,25 @@ function Lockup({ name = "orangecheck", size = 28, className }) {
93
93
  }
94
94
  );
95
95
  }
96
- var SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)";
96
+ var SPHERE_GRADIENT = "radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)";
97
+ var SPHERE_DEPTH = "inset -14px -22px 44px rgb(150 40 0 / 0.16), 0 36px 70px -34px rgb(234 88 11 / 0.45)";
98
+ function Ball({ src, alt, float }) {
99
+ return /* @__PURE__ */ jsxRuntime.jsx(
100
+ "div",
101
+ {
102
+ className: cn("relative size-full overflow-hidden rounded-full", float && "oca-animate-float"),
103
+ style: { background: SPHERE_GRADIENT, boxShadow: SPHERE_DEPTH },
104
+ children: /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "absolute inset-0 size-full select-none", draggable: false })
105
+ }
106
+ );
107
+ }
97
108
  function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }) {
98
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative aspect-square w-full", className), style, children: [
99
- stadium ? (
100
- // Measured from the deck's title slide: a rounded square, its corners
101
- // 17% of the sphere, level with the sphere's foot, reaching left behind
102
- // the headline. On phones a ring inside the gutter, since the offset
103
- // would push the page sideways.
104
- /* @__PURE__ */ jsxRuntime.jsx(
105
- "div",
106
- {
107
- "aria-hidden": true,
108
- className: "oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]"
109
- }
110
- )
111
- ) : null,
112
- /* @__PURE__ */ jsxRuntime.jsx(
113
- "div",
114
- {
115
- className: cn("relative size-full overflow-hidden rounded-full", float && "oca-animate-float"),
116
- style: { background: SPHERE_GRADIENT },
117
- children: /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "absolute inset-0 size-full select-none", draggable: false })
118
- }
119
- )
109
+ if (!stadium) {
110
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative aspect-square w-full", className), style, children: /* @__PURE__ */ jsxRuntime.jsx(Ball, { src, alt, float }) });
111
+ }
112
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative aspect-[1.255/1.127] w-full", className), style, children: [
113
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "oca-stadium pointer-events-none absolute left-0 top-0 h-full w-[88.4%] rounded-[32%]" }),
114
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 right-0 aspect-square w-[79.7%]", children: /* @__PURE__ */ jsxRuntime.jsx(Ball, { src, alt, float }) })
120
115
  ] });
121
116
  }
122
117
  function IsoCheck({ size = 360, settle = false, shadow = true, title, className }) {
@@ -132,8 +127,69 @@ function IsoCheck({ size = 360, settle = false, shadow = true, title, className
132
127
  ) : null
133
128
  ] });
134
129
  }
130
+ var CELL_W = 22;
131
+ var CELL_H = 27;
132
+ function picks(count, total, seed) {
133
+ let s = seed >>> 0;
134
+ const out = /* @__PURE__ */ new Set();
135
+ while (out.size < Math.min(count, total)) {
136
+ s = Math.imul(s, 1664525) + 1013904223 >>> 0;
137
+ out.add(s % total);
138
+ }
139
+ return out;
140
+ }
141
+ function Crowd({
142
+ cols = 26,
143
+ rows = 9,
144
+ real = 7,
145
+ seed = 21,
146
+ tone = "ink",
147
+ label = "A crowd of identical accounts. Only a few are real.",
148
+ className
149
+ }) {
150
+ const width = cols * CELL_W;
151
+ const scales = Array.from({ length: rows }, (_, r) => 0.5 + 0.5 * (r / Math.max(1, rows - 1)));
152
+ const height = scales.reduce((h, s) => h + CELL_H * s, 0);
153
+ const nearFrom = Math.floor(rows / 2) * cols;
154
+ const realSet = new Set([...picks(real, rows * cols - nearFrom, seed)].map((i) => i + nearFrom));
155
+ const bot = tone === "ink" ? "#ffffff" : "#0a0a0a";
156
+ let y = 0;
157
+ return /* @__PURE__ */ jsxRuntime.jsx(
158
+ "svg",
159
+ {
160
+ role: "img",
161
+ "aria-label": label,
162
+ viewBox: `0 0 ${width} ${Math.ceil(height)}`,
163
+ className: cn("block h-auto w-full", className),
164
+ children: scales.map((s, r) => {
165
+ const rowY = y;
166
+ y += CELL_H * s;
167
+ const rowW = cols * CELL_W * s;
168
+ const x0 = (width - rowW) / 2;
169
+ return Array.from({ length: cols }, (_, c) => {
170
+ const i = r * cols + c;
171
+ const isReal = realSet.has(i);
172
+ return /* @__PURE__ */ jsxRuntime.jsxs(
173
+ "g",
174
+ {
175
+ transform: `translate(${x0 + c * CELL_W * s} ${rowY}) scale(${s})`,
176
+ fill: isReal ? "var(--oca-orange)" : bot,
177
+ fillOpacity: isReal ? 1 : 0.1 + 0.16 * s,
178
+ children: [
179
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: 11, cy: 8, r: 4.4 }),
180
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 25a8 8.5 0 0 1 16 0Z" })
181
+ ]
182
+ },
183
+ i
184
+ );
185
+ });
186
+ })
187
+ }
188
+ );
189
+ }
135
190
 
136
191
  exports.AppIcon = AppIcon;
192
+ exports.Crowd = Crowd;
137
193
  exports.HalftoneSphere = HalftoneSphere;
138
194
  exports.IsoCheck = IsoCheck;
139
195
  exports.Lockup = Lockup;
package/dist/brand.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["extendTailwindMerge","clsx","jsxs","jsx"],"mappings":"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","file":"brand.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // Measured from the deck's title slide: a rounded square, its corners\n // 17% of the sphere, level with the sphere's foot, reaching left behind\n // the headline. On phones a ring inside the gutter, since the offset\n // would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx","../src/brand/crowd.tsx"],"names":["extendTailwindMerge","clsx","jsxs","jsx"],"mappings":"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","file":"brand.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange ball, lit from the\n * upper left, with a hairline stadium behind it. The dot layer is an image of\n * black dots on transparent, made by scripts/halftone.py, so the ball stays CSS\n * and the dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)';\n\nconst SPHERE_DEPTH = 'inset -14px -22px 44px rgb(150 40 0 / 0.16), 0 36px 70px -34px rgb(234 88 11 / 0.45)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction Ball({ src, alt, float }: { src: string; alt: string; float: boolean }) {\n return (\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT, boxShadow: SPHERE_DEPTH }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n );\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n if (!stadium) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n <Ball src={src} alt={alt} float={float} />\n </div>\n );\n }\n // Measured from the deck's title slide against the ball's width S: the stadium\n // starts 0.255 S left of and 0.127 S above the ball, is 1.11 S wide, and ends\n // level with its foot. Both live inside this box, so nothing reaches the text.\n return (\n <div className={cn('relative aspect-[1.255/1.127] w-full', className)} style={style}>\n <div aria-hidden className=\"oca-stadium pointer-events-none absolute left-0 top-0 h-full w-[88.4%] rounded-[32%]\" />\n <div className=\"absolute bottom-0 right-0 aspect-square w-[79.7%]\">\n <Ball src={src} alt={alt} float={float} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n","import { cn } from '../lib/cn';\n\n/*\n * \"A bot farm can pretend to be a crowd\": a crowd of identical figures in\n * perspective, nearer rows larger, with a few real ones in orange. Identical on\n * purpose: copies are what a bot farm is. Positions come from a fixed seed so\n * server and client draw the same crowd.\n */\n\nexport interface CrowdProps {\n cols?: number;\n rows?: number;\n /** How many figures are real (orange). */\n real?: number;\n seed?: number;\n /** Ground it sits on: figures are light on ink, dark on canvas. */\n tone?: 'ink' | 'canvas';\n /** Spoken by screen readers. */\n label?: string;\n className?: string;\n}\n\nconst CELL_W = 22;\nconst CELL_H = 27;\n\nfunction picks(count: number, total: number, seed: number): Set<number> {\n let s = seed >>> 0;\n const out = new Set<number>();\n while (out.size < Math.min(count, total)) {\n s = (Math.imul(s, 1664525) + 1013904223) >>> 0;\n out.add(s % total);\n }\n return out;\n}\n\nexport function Crowd({\n cols = 26,\n rows = 9,\n real = 7,\n seed = 21,\n tone = 'ink',\n label = 'A crowd of identical accounts. Only a few are real.',\n className,\n}: CrowdProps) {\n const width = cols * CELL_W;\n const scales = Array.from({ length: rows }, (_, r) => 0.5 + 0.5 * (r / Math.max(1, rows - 1)));\n const height = scales.reduce((h, s) => h + CELL_H * s, 0);\n // Real figures only in the nearer half, where they are big enough to see.\n const nearFrom = Math.floor(rows / 2) * cols;\n const realSet = new Set([...picks(real, rows * cols - nearFrom, seed)].map((i) => i + nearFrom));\n const bot = tone === 'ink' ? '#ffffff' : '#0a0a0a';\n let y = 0;\n return (\n <svg\n role=\"img\"\n aria-label={label}\n viewBox={`0 0 ${width} ${Math.ceil(height)}`}\n className={cn('block h-auto w-full', className)}\n >\n {scales.map((s, r) => {\n const rowY = y;\n y += CELL_H * s;\n const rowW = cols * CELL_W * s;\n const x0 = (width - rowW) / 2;\n return Array.from({ length: cols }, (_, c) => {\n const i = r * cols + c;\n const isReal = realSet.has(i);\n return (\n <g\n key={i}\n transform={`translate(${x0 + c * CELL_W * s} ${rowY}) scale(${s})`}\n fill={isReal ? 'var(--oca-orange)' : bot}\n fillOpacity={isReal ? 1 : 0.1 + 0.16 * s}\n >\n <circle cx={11} cy={8} r={4.4} />\n <path d=\"M3 25a8 8.5 0 0 1 16 0Z\" />\n </g>\n );\n });\n })}\n </svg>\n );\n}\n"]}
package/dist/brand.mjs CHANGED
@@ -91,30 +91,25 @@ function Lockup({ name = "orangecheck", size = 28, className }) {
91
91
  }
92
92
  );
93
93
  }
94
- var SPHERE_GRADIENT = "radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)";
94
+ var SPHERE_GRADIENT = "radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)";
95
+ var SPHERE_DEPTH = "inset -14px -22px 44px rgb(150 40 0 / 0.16), 0 36px 70px -34px rgb(234 88 11 / 0.45)";
96
+ function Ball({ src, alt, float }) {
97
+ return /* @__PURE__ */ jsx(
98
+ "div",
99
+ {
100
+ className: cn("relative size-full overflow-hidden rounded-full", float && "oca-animate-float"),
101
+ style: { background: SPHERE_GRADIENT, boxShadow: SPHERE_DEPTH },
102
+ children: /* @__PURE__ */ jsx("img", { src, alt, className: "absolute inset-0 size-full select-none", draggable: false })
103
+ }
104
+ );
105
+ }
95
106
  function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }) {
96
- return /* @__PURE__ */ jsxs("div", { className: cn("relative aspect-square w-full", className), style, children: [
97
- stadium ? (
98
- // Measured from the deck's title slide: a rounded square, its corners
99
- // 17% of the sphere, level with the sphere's foot, reaching left behind
100
- // the headline. On phones a ring inside the gutter, since the offset
101
- // would push the page sideways.
102
- /* @__PURE__ */ jsx(
103
- "div",
104
- {
105
- "aria-hidden": true,
106
- className: "oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]"
107
- }
108
- )
109
- ) : null,
110
- /* @__PURE__ */ jsx(
111
- "div",
112
- {
113
- className: cn("relative size-full overflow-hidden rounded-full", float && "oca-animate-float"),
114
- style: { background: SPHERE_GRADIENT },
115
- children: /* @__PURE__ */ jsx("img", { src, alt, className: "absolute inset-0 size-full select-none", draggable: false })
116
- }
117
- )
107
+ if (!stadium) {
108
+ return /* @__PURE__ */ jsx("div", { className: cn("relative aspect-square w-full", className), style, children: /* @__PURE__ */ jsx(Ball, { src, alt, float }) });
109
+ }
110
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative aspect-[1.255/1.127] w-full", className), style, children: [
111
+ /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "oca-stadium pointer-events-none absolute left-0 top-0 h-full w-[88.4%] rounded-[32%]" }),
112
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 right-0 aspect-square w-[79.7%]", children: /* @__PURE__ */ jsx(Ball, { src, alt, float }) })
118
113
  ] });
119
114
  }
120
115
  function IsoCheck({ size = 360, settle = false, shadow = true, title, className }) {
@@ -130,7 +125,67 @@ function IsoCheck({ size = 360, settle = false, shadow = true, title, className
130
125
  ) : null
131
126
  ] });
132
127
  }
128
+ var CELL_W = 22;
129
+ var CELL_H = 27;
130
+ function picks(count, total, seed) {
131
+ let s = seed >>> 0;
132
+ const out = /* @__PURE__ */ new Set();
133
+ while (out.size < Math.min(count, total)) {
134
+ s = Math.imul(s, 1664525) + 1013904223 >>> 0;
135
+ out.add(s % total);
136
+ }
137
+ return out;
138
+ }
139
+ function Crowd({
140
+ cols = 26,
141
+ rows = 9,
142
+ real = 7,
143
+ seed = 21,
144
+ tone = "ink",
145
+ label = "A crowd of identical accounts. Only a few are real.",
146
+ className
147
+ }) {
148
+ const width = cols * CELL_W;
149
+ const scales = Array.from({ length: rows }, (_, r) => 0.5 + 0.5 * (r / Math.max(1, rows - 1)));
150
+ const height = scales.reduce((h, s) => h + CELL_H * s, 0);
151
+ const nearFrom = Math.floor(rows / 2) * cols;
152
+ const realSet = new Set([...picks(real, rows * cols - nearFrom, seed)].map((i) => i + nearFrom));
153
+ const bot = tone === "ink" ? "#ffffff" : "#0a0a0a";
154
+ let y = 0;
155
+ return /* @__PURE__ */ jsx(
156
+ "svg",
157
+ {
158
+ role: "img",
159
+ "aria-label": label,
160
+ viewBox: `0 0 ${width} ${Math.ceil(height)}`,
161
+ className: cn("block h-auto w-full", className),
162
+ children: scales.map((s, r) => {
163
+ const rowY = y;
164
+ y += CELL_H * s;
165
+ const rowW = cols * CELL_W * s;
166
+ const x0 = (width - rowW) / 2;
167
+ return Array.from({ length: cols }, (_, c) => {
168
+ const i = r * cols + c;
169
+ const isReal = realSet.has(i);
170
+ return /* @__PURE__ */ jsxs(
171
+ "g",
172
+ {
173
+ transform: `translate(${x0 + c * CELL_W * s} ${rowY}) scale(${s})`,
174
+ fill: isReal ? "var(--oca-orange)" : bot,
175
+ fillOpacity: isReal ? 1 : 0.1 + 0.16 * s,
176
+ children: [
177
+ /* @__PURE__ */ jsx("circle", { cx: 11, cy: 8, r: 4.4 }),
178
+ /* @__PURE__ */ jsx("path", { d: "M3 25a8 8.5 0 0 1 16 0Z" })
179
+ ]
180
+ },
181
+ i
182
+ );
183
+ });
184
+ })
185
+ }
186
+ );
187
+ }
133
188
 
134
- export { AppIcon, HalftoneSphere, IsoCheck, Lockup, MARK_FACES, Mark, SPHERE_GRADIENT };
189
+ export { AppIcon, Crowd, HalftoneSphere, IsoCheck, Lockup, MARK_FACES, Mark, SPHERE_GRADIENT };
135
190
  //# sourceMappingURL=brand.mjs.map
136
191
  //# sourceMappingURL=brand.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"brand.mjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange sphere that glows\n * near-white behind the subject and saturates at the rim, with a hairline\n * stadium behind it. The dot layer is an image of black dots on transparent,\n * made by scripts/halftone.py from any photo, so the sphere stays CSS and the\n * dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(circle at 50% 42%, #fff8f3 0%, #ffe2d3 22%, #ffb48d 45%, #ff8a55 68%, #ff6a2c 100%)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n {stadium ? (\n // Measured from the deck's title slide: a rounded square, its corners\n // 17% of the sphere, level with the sphere's foot, reaching left behind\n // the headline. On phones a ring inside the gutter, since the offset\n // would push the page sideways.\n <div\n aria-hidden\n className=\"oca-stadium pointer-events-none absolute inset-[-6%] rounded-full md:[inset:-12%_14%_0%_-26%] md:rounded-[17%]\"\n />\n ) : null}\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/cn.ts","../src/brand/mark.tsx","../src/brand/sphere.tsx","../src/brand/iso-check.tsx","../src/brand/crowd.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"brand.mjs","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\n// tailwind-merge would treat `oca-display-1` and `text-foreground` as the same\n// group and drop one; teach it the package's own type utilities.\nconst merge = extendTailwindMerge<'oca-type'>({\n extend: {\n classGroups: {\n 'oca-type': [\n 'oca-display-1',\n 'oca-display-2',\n 'oca-display-3',\n 'oca-title',\n 'oca-title-sm',\n 'oca-eyebrow',\n 'oca-lead',\n 'oca-body',\n 'oca-numeral',\n ],\n },\n },\n});\n\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs));\n}\n","import type { SVGProps } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The isometric check, measured from Sou's deck (slide 9) onto a 16×8 grid:\n * a chevron with a 3-unit short arm and a 5-unit long arm, one unit thick,\n * extruded two units. Three faces, three oranges.\n */\n\nconst TOP = '0,3 2,2 6,4 14,0 16,1 6,6';\nconst LEFT = '0,3 6,6 6,8 0,5';\nconst RIGHT = '6,6 16,1 16,3 6,8';\n\nexport const MARK_FACES = { top: '#FED8AB', left: '#FB933C', right: '#EA580B' } as const;\n\nexport interface MarkProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n /** Accessible name. Omit to render the mark as decoration. */\n title?: string;\n /** Rendered width in px; height follows the 2:1 grid. */\n size?: number;\n}\n\nexport function Mark({ title, size, className, ...rest }: MarkProps) {\n return (\n <svg\n viewBox=\"0 0 16 8\"\n width={size}\n height={size ? size / 2 : undefined}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </svg>\n );\n}\n\nexport interface AppIconProps extends Omit<SVGProps<SVGSVGElement>, 'children'> {\n title?: string;\n size?: number;\n}\n\n/** The app icon: the mark on an ink squircle, as in the deck's lockup. */\nexport function AppIcon({ title, size = 32, className, ...rest }: AppIconProps) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n width={size}\n height={size}\n role={title ? 'img' : undefined}\n aria-hidden={title ? undefined : true}\n className={cn('shrink-0', className)}\n {...rest}\n >\n {title ? <title>{title}</title> : null}\n <rect\n width=\"24\"\n height=\"24\"\n rx=\"6.25\"\n style={{ fill: 'var(--oca-icon-tile, #0A0A0A)', stroke: 'var(--oca-icon-ring, transparent)' }}\n strokeWidth=\"0.5\"\n />\n <g transform=\"translate(4.25 8.1) scale(0.97)\">\n <polygon points={LEFT} fill={MARK_FACES.left} />\n <polygon points={RIGHT} fill={MARK_FACES.right} />\n <polygon points={TOP} fill={MARK_FACES.top} />\n </g>\n </svg>\n );\n}\n\nexport interface LockupProps {\n /** The word beside the icon. */\n name?: string;\n /** Icon size in px; the word scales with it. */\n size?: number;\n className?: string;\n}\n\n/** Icon and wordmark, lowercase, as the deck sets it. */\nexport function Lockup({ name = 'orangecheck', size = 28, className }: LockupProps) {\n return (\n <span\n className={cn('inline-flex items-center font-medium tracking-[-0.01em]', className)}\n style={{ gap: size * 0.32, fontSize: size * 0.56 }}\n >\n <AppIcon size={size} />\n <span>{name}</span>\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport { cn } from '../lib/cn';\n\n/*\n * The deck's photograph: a halftone portrait on an orange ball, lit from the\n * upper left, with a hairline stadium behind it. The dot layer is an image of\n * black dots on transparent, made by scripts/halftone.py, so the ball stays CSS\n * and the dots stay crisp at any size.\n */\n\nexport const SPHERE_GRADIENT =\n 'radial-gradient(86% 86% at 36% 32%, #fff7f0 0%, #ffdec8 14%, #ffa069 34%, #ff7030 56%, #f65210 78%, #de4006 100%)';\n\nconst SPHERE_DEPTH = 'inset -14px -22px 44px rgb(150 40 0 / 0.16), 0 36px 70px -34px rgb(234 88 11 / 0.45)';\n\nexport interface HalftoneSphereProps {\n /** The dot layer (black dots on transparent, already circular). */\n src: string;\n /** Describe the person or subject; empty string marks it decorative. */\n alt: string;\n /** Draw the hairline stadium behind the sphere, as the hero does. */\n stadium?: boolean;\n /** Let the sphere drift a few pixels. Off under reduced motion. */\n float?: boolean;\n className?: string;\n style?: CSSProperties;\n}\n\nfunction Ball({ src, alt, float }: { src: string; alt: string; float: boolean }) {\n return (\n <div\n className={cn('relative size-full overflow-hidden rounded-full', float && 'oca-animate-float')}\n style={{ background: SPHERE_GRADIENT, boxShadow: SPHERE_DEPTH }}\n >\n {/* eslint-disable-next-line @next/next/no-img-element -- a static dot layer, sized by CSS */}\n <img src={src} alt={alt} className=\"absolute inset-0 size-full select-none\" draggable={false} />\n </div>\n );\n}\n\nexport function HalftoneSphere({ src, alt, stadium = false, float = false, className, style }: HalftoneSphereProps) {\n if (!stadium) {\n return (\n <div className={cn('relative aspect-square w-full', className)} style={style}>\n <Ball src={src} alt={alt} float={float} />\n </div>\n );\n }\n // Measured from the deck's title slide against the ball's width S: the stadium\n // starts 0.255 S left of and 0.127 S above the ball, is 1.11 S wide, and ends\n // level with its foot. Both live inside this box, so nothing reaches the text.\n return (\n <div className={cn('relative aspect-[1.255/1.127] w-full', className)} style={style}>\n <div aria-hidden className=\"oca-stadium pointer-events-none absolute left-0 top-0 h-full w-[88.4%] rounded-[32%]\" />\n <div className=\"absolute bottom-0 right-0 aspect-square w-[79.7%]\">\n <Ball src={src} alt={alt} float={float} />\n </div>\n </div>\n );\n}\n","import { cn } from '../lib/cn';\nimport { Mark } from './mark';\n\nexport interface IsoCheckProps {\n /** Width in px; the check keeps its 2:1 grid. */\n size?: number;\n /** Drop it into place on first paint, the deck's closing gesture. */\n settle?: boolean;\n /** A soft floor shadow, so it reads as an object on the canvas. */\n shadow?: boolean;\n title?: string;\n className?: string;\n}\n\n/** The isometric check as an object: the deck's closing illustration. */\nexport function IsoCheck({ size = 360, settle = false, shadow = true, title, className }: IsoCheckProps) {\n return (\n <div className={cn('relative inline-block', settle && 'oca-animate-settle', className)} style={{ width: size }}>\n <Mark size={size} title={title} className=\"relative z-10 block\" />\n {shadow ? (\n <div\n aria-hidden\n className=\"absolute left-[8%] right-[8%] rounded-[50%] opacity-60 blur-xl\"\n style={{ height: size * 0.08, bottom: -size * 0.04, background: 'rgb(234 88 11 / 0.18)' }}\n />\n ) : null}\n </div>\n );\n}\n","import { cn } from '../lib/cn';\n\n/*\n * \"A bot farm can pretend to be a crowd\": a crowd of identical figures in\n * perspective, nearer rows larger, with a few real ones in orange. Identical on\n * purpose: copies are what a bot farm is. Positions come from a fixed seed so\n * server and client draw the same crowd.\n */\n\nexport interface CrowdProps {\n cols?: number;\n rows?: number;\n /** How many figures are real (orange). */\n real?: number;\n seed?: number;\n /** Ground it sits on: figures are light on ink, dark on canvas. */\n tone?: 'ink' | 'canvas';\n /** Spoken by screen readers. */\n label?: string;\n className?: string;\n}\n\nconst CELL_W = 22;\nconst CELL_H = 27;\n\nfunction picks(count: number, total: number, seed: number): Set<number> {\n let s = seed >>> 0;\n const out = new Set<number>();\n while (out.size < Math.min(count, total)) {\n s = (Math.imul(s, 1664525) + 1013904223) >>> 0;\n out.add(s % total);\n }\n return out;\n}\n\nexport function Crowd({\n cols = 26,\n rows = 9,\n real = 7,\n seed = 21,\n tone = 'ink',\n label = 'A crowd of identical accounts. Only a few are real.',\n className,\n}: CrowdProps) {\n const width = cols * CELL_W;\n const scales = Array.from({ length: rows }, (_, r) => 0.5 + 0.5 * (r / Math.max(1, rows - 1)));\n const height = scales.reduce((h, s) => h + CELL_H * s, 0);\n // Real figures only in the nearer half, where they are big enough to see.\n const nearFrom = Math.floor(rows / 2) * cols;\n const realSet = new Set([...picks(real, rows * cols - nearFrom, seed)].map((i) => i + nearFrom));\n const bot = tone === 'ink' ? '#ffffff' : '#0a0a0a';\n let y = 0;\n return (\n <svg\n role=\"img\"\n aria-label={label}\n viewBox={`0 0 ${width} ${Math.ceil(height)}`}\n className={cn('block h-auto w-full', className)}\n >\n {scales.map((s, r) => {\n const rowY = y;\n y += CELL_H * s;\n const rowW = cols * CELL_W * s;\n const x0 = (width - rowW) / 2;\n return Array.from({ length: cols }, (_, c) => {\n const i = r * cols + c;\n const isReal = realSet.has(i);\n return (\n <g\n key={i}\n transform={`translate(${x0 + c * CELL_W * s} ${rowY}) scale(${s})`}\n fill={isReal ? 'var(--oca-orange)' : bot}\n fillOpacity={isReal ? 1 : 0.1 + 0.16 * s}\n >\n <circle cx={11} cy={8} r={4.4} />\n <path d=\"M3 25a8 8.5 0 0 1 16 0Z\" />\n </g>\n );\n });\n })}\n </svg>\n );\n}\n"]}
@@ -344,6 +344,17 @@ declare function CodeBlock({ code, caption, className, }: {
344
344
  className?: string;
345
345
  }): react.JSX.Element;
346
346
 
347
+ interface SplitSegment {
348
+ pct: number;
349
+ label: ReactNode;
350
+ tone?: 'ink' | 'card';
351
+ }
352
+ declare function SplitBar({ segments, caption, className, }: {
353
+ segments: SplitSegment[];
354
+ caption?: ReactNode;
355
+ className?: string;
356
+ }): react.JSX.Element;
357
+
347
358
  type TruthStatus = 'yes' | 'testing' | 'not-yet' | 'no';
348
359
  interface TruthRow {
349
360
  status: TruthStatus;
@@ -471,4 +482,4 @@ declare function ActivityList({ items, empty, className }: {
471
482
  className?: string;
472
483
  }): react.JSX.Element;
473
484
 
474
- export { Accent, type ActivityItem, ActivityList, CodeBlock, type Column, Container, CopyButton, DataTable, Display, type DisplayProps, EmptyState, Eyebrow, Footnote, HashChip, type KeyValueItem, KeyValueList, Lead, Notice, PackageCard, type PackageItem, PageHeader, type ProgressStep, ProgressSteps, Prose, Sats, SatsFigure, type SatsProps, type SatsTextOptions, Section, SectionIntro, type SectionProps, StatCard, StatGrid, StepGrid, type StepGridProps, type StepItem, type StepState, type TruthRow, type TruthStatus, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
485
+ export { Accent, type ActivityItem, ActivityList, CodeBlock, type Column, Container, CopyButton, DataTable, Display, type DisplayProps, EmptyState, Eyebrow, Footnote, HashChip, type KeyValueItem, KeyValueList, Lead, Notice, PackageCard, type PackageItem, PageHeader, type ProgressStep, ProgressSteps, Prose, Sats, SatsFigure, type SatsProps, type SatsTextOptions, Section, SectionIntro, type SectionProps, SplitBar, type SplitSegment, StatCard, StatGrid, StepGrid, type StepGridProps, type StepItem, type StepState, type TruthRow, type TruthStatus, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
@@ -344,6 +344,17 @@ declare function CodeBlock({ code, caption, className, }: {
344
344
  className?: string;
345
345
  }): react.JSX.Element;
346
346
 
347
+ interface SplitSegment {
348
+ pct: number;
349
+ label: ReactNode;
350
+ tone?: 'ink' | 'card';
351
+ }
352
+ declare function SplitBar({ segments, caption, className, }: {
353
+ segments: SplitSegment[];
354
+ caption?: ReactNode;
355
+ className?: string;
356
+ }): react.JSX.Element;
357
+
347
358
  type TruthStatus = 'yes' | 'testing' | 'not-yet' | 'no';
348
359
  interface TruthRow {
349
360
  status: TruthStatus;
@@ -471,4 +482,4 @@ declare function ActivityList({ items, empty, className }: {
471
482
  className?: string;
472
483
  }): react.JSX.Element;
473
484
 
474
- export { Accent, type ActivityItem, ActivityList, CodeBlock, type Column, Container, CopyButton, DataTable, Display, type DisplayProps, EmptyState, Eyebrow, Footnote, HashChip, type KeyValueItem, KeyValueList, Lead, Notice, PackageCard, type PackageItem, PageHeader, type ProgressStep, ProgressSteps, Prose, Sats, SatsFigure, type SatsProps, type SatsTextOptions, Section, SectionIntro, type SectionProps, StatCard, StatGrid, StepGrid, type StepGridProps, type StepItem, type StepState, type TruthRow, type TruthStatus, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
485
+ export { Accent, type ActivityItem, ActivityList, CodeBlock, type Column, Container, CopyButton, DataTable, Display, type DisplayProps, EmptyState, Eyebrow, Footnote, HashChip, type KeyValueItem, KeyValueList, Lead, Notice, PackageCard, type PackageItem, PageHeader, type ProgressStep, ProgressSteps, Prose, Sats, SatsFigure, type SatsProps, type SatsTextOptions, Section, SectionIntro, type SectionProps, SplitBar, type SplitSegment, StatCard, StatGrid, StepGrid, type StepGridProps, type StepItem, type StepState, type TruthRow, type TruthStatus, TruthTable, UsdApprox, satsText, sectionVariants, useCopy };
@@ -216,6 +216,47 @@ function CodeBlock({
216
216
  /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto p-5 font-mono text-[0.875rem] leading-relaxed text-oca-ink-muted", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) })
217
217
  ] });
218
218
  }
219
+ function SplitBar({
220
+ segments,
221
+ caption,
222
+ className
223
+ }) {
224
+ return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: cn("flex flex-col gap-5", className), children: [
225
+ caption ? /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "text-[0.9375rem]", children: caption }) : null,
226
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 w-full gap-1.5 overflow-hidden rounded-[1.25rem] md:h-20", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
227
+ "div",
228
+ {
229
+ className: cn(
230
+ "flex min-w-0 items-end rounded-[1rem] px-4 pb-3",
231
+ s.tone === "card" ? "bg-white text-oca-ink" : "bg-oca-ink text-white"
232
+ ),
233
+ style: { flexBasis: `${s.pct}%` },
234
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "oca-tabular text-[1.5rem] leading-none tracking-[-0.02em] md:text-[1.875rem]", children: [
235
+ s.pct,
236
+ "%"
237
+ ] })
238
+ },
239
+ i
240
+ )) }),
241
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid gap-x-8 gap-y-2 sm:grid-cols-2", children: segments.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2.5", children: [
242
+ /* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "sr-only", children: [
243
+ s.pct,
244
+ "%"
245
+ ] }),
246
+ /* @__PURE__ */ jsxRuntime.jsx(
247
+ "span",
248
+ {
249
+ "aria-hidden": true,
250
+ className: cn(
251
+ "inline-block size-2.5 shrink-0 translate-y-[-1px] rounded-full",
252
+ s.tone === "card" ? "bg-white" : "bg-oca-ink"
253
+ )
254
+ }
255
+ ),
256
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-[0.9375rem] leading-snug", children: s.label })
257
+ ] }, i)) })
258
+ ] });
259
+ }
219
260
  var LABEL = {
220
261
  yes: "Yes",
221
262
  testing: "In testing",
@@ -607,6 +648,7 @@ exports.Sats = Sats;
607
648
  exports.SatsFigure = SatsFigure;
608
649
  exports.Section = Section;
609
650
  exports.SectionIntro = SectionIntro;
651
+ exports.SplitBar = SplitBar;
610
652
  exports.StatCard = StatCard;
611
653
  exports.StatGrid = StatGrid;
612
654
  exports.StepGrid = StepGrid;