@fairmint/ui 0.2.31 → 0.2.33

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.
@@ -44998,6 +44998,579 @@ function EquityGrantCard({
44998
44998
  }
44999
44999
  );
45000
45000
  }
45001
+ const DEFAULT_RIBBON_CONFIG = {
45002
+ baseline: 0.82,
45003
+ xEndStagger: 9e-3,
45004
+ xEndDraw: [0.84, 0.849, 0.858, 0.867, 0.876],
45005
+ xEndCurve: 0.914,
45006
+ xStart: [0.05, 0.19, 0.34, 0.5, 0.66],
45007
+ kArr: [1.7, 2.4, 3.1, 3.9, 4.9],
45008
+ V: [0.082, 0.102, 0.132, 0.172, 0.21],
45009
+ vMin: [0.038, 0.04, 0.042, 0.044, 0.046],
45010
+ xStem: [0.05, 0.04, 0.035, 0.03, 0.025],
45011
+ labelX: [0.08, 0.24, 0.4, 0.56, 0.8],
45012
+ labelAboveGap: [0.034, 0.03, 0.028, 0.024],
45013
+ labelLeftGap: 0.055,
45014
+ labelLiquidityGap: 0.016,
45015
+ labelNudge: [
45016
+ [0, 0],
45017
+ [0, 0],
45018
+ [0, 0],
45019
+ [0, 0],
45020
+ [0, -0.012]
45021
+ ],
45022
+ samples: 72
45023
+ };
45024
+ const MARK_RIBBON_RIGHT_EDGE = 0.936;
45025
+ const HERO_WIDE_TOP_END = DEFAULT_RIBBON_CONFIG.xEndDraw[DEFAULT_RIBBON_CONFIG.xEndDraw.length - 1];
45026
+ const RIBBON_END_STAGGER_X = DEFAULT_RIBBON_CONFIG.xEndStagger * 1e3 / (1e3 - 500);
45027
+ const MARK_FIT_HORIZONTAL_COMPRESS = 0.78;
45028
+ const MARK_RIBBON_CONFIG = {
45029
+ ...DEFAULT_RIBBON_CONFIG,
45030
+ baseline: 0.97,
45031
+ xEndDraw: [
45032
+ MARK_RIBBON_RIGHT_EDGE,
45033
+ MARK_RIBBON_RIGHT_EDGE,
45034
+ MARK_RIBBON_RIGHT_EDGE,
45035
+ MARK_RIBBON_RIGHT_EDGE,
45036
+ MARK_RIBBON_RIGHT_EDGE
45037
+ ],
45038
+ xEndCurve: 0.94,
45039
+ xStart: [0.08, 0.2, 0.32, 0.44, 0.56],
45040
+ xStem: [0.028, 0.022, 0.018, 0.015, 0.012]
45041
+ };
45042
+ function labelGapAbove(i, config2) {
45043
+ const lastBand = config2.xStart.length - 1;
45044
+ if (i === lastBand) return config2.labelLiquidityGap;
45045
+ return config2.labelAboveGap[i] ?? config2.labelLiquidityGap;
45046
+ }
45047
+ function mergeBounds(a, b) {
45048
+ return {
45049
+ minX: Math.min(a.minX, b.minX),
45050
+ minY: Math.min(a.minY, b.minY),
45051
+ maxX: Math.max(a.maxX, b.maxX),
45052
+ maxY: Math.max(a.maxY, b.maxY)
45053
+ };
45054
+ }
45055
+ function transformPathD(d, scaleX, scaleY, tx, ty) {
45056
+ return d.replace(/(-?[\d.]+),(-?[\d.]+)/g, (_, x, y) => {
45057
+ const px2 = Number(x) * scaleX + tx;
45058
+ const py = Number(y) * scaleY + ty;
45059
+ return `${px2.toFixed(1)},${py.toFixed(1)}`;
45060
+ });
45061
+ }
45062
+ function computeMarkFitTransform(bounds, width, height, paddingRatio) {
45063
+ const pad = paddingRatio * Math.min(width, height);
45064
+ const bw = bounds.maxX - bounds.minX;
45065
+ const bh = bounds.maxY - bounds.minY;
45066
+ if (bw <= 0 || bh <= 0) {
45067
+ return { scaleX: 1, scaleY: 1, tx: 0, ty: 0 };
45068
+ }
45069
+ const scaleY = (height - pad * 2) / bh;
45070
+ let scaleX = scaleY * MARK_FIT_HORIZONTAL_COMPRESS;
45071
+ if (bw * scaleX > width - pad * 2) {
45072
+ scaleX = (width - pad * 2) / bw;
45073
+ }
45074
+ const tx = (width - bw * scaleX) / 2 - bounds.minX * scaleX;
45075
+ const ty = (height - bh * scaleY) / 2 - bounds.minY * scaleY;
45076
+ return { scaleX, scaleY, tx, ty };
45077
+ }
45078
+ function valAt(i, xf, config2) {
45079
+ const { xStart, xEndCurve, kArr, V, vMin, xStem } = config2;
45080
+ const xLo = xStart[i] - xStem[i];
45081
+ if (xf < xLo || xf > xEndCurve) return 0;
45082
+ if (xf < xStart[i]) return vMin[i];
45083
+ const u = (xf - xStart[i]) / (xEndCurve - xStart[i]);
45084
+ const grow = (Math.exp(kArr[i] * u) - 1) / (Math.exp(kArr[i]) - 1);
45085
+ return vMin[i] + (V[i] - vMin[i]) * grow;
45086
+ }
45087
+ function cumBelow(i, xf, config2) {
45088
+ let sum = 0;
45089
+ for (let j = 0; j < i; j++) sum += valAt(j, xf, config2);
45090
+ return sum;
45091
+ }
45092
+ function buildBandPath(i, width, height, config2) {
45093
+ const { xStart, xEndCurve, xStem, baseline } = config2;
45094
+ const samples = Math.max(1, Math.floor(config2.samples));
45095
+ const xLo = xStart[i] - xStem[i];
45096
+ const top = [];
45097
+ const bot = [];
45098
+ for (let s = 0; s <= samples; s++) {
45099
+ const xf = xLo + (xEndCurve - xLo) * (s / samples);
45100
+ const below = cumBelow(i, xf, config2);
45101
+ const v = valAt(i, xf, config2);
45102
+ const x = xf * width;
45103
+ top.push([x, (baseline - below - v) * height]);
45104
+ bot.push([x, (baseline - below) * height]);
45105
+ }
45106
+ let minX = Number.POSITIVE_INFINITY;
45107
+ let minY = Number.POSITIVE_INFINITY;
45108
+ let maxX = Number.NEGATIVE_INFINITY;
45109
+ let maxY = Number.NEGATIVE_INFINITY;
45110
+ for (const [x, y] of [...top, ...bot]) {
45111
+ minX = Math.min(minX, x);
45112
+ minY = Math.min(minY, y);
45113
+ maxX = Math.max(maxX, x);
45114
+ maxY = Math.max(maxY, y);
45115
+ }
45116
+ let d = `M${top[0][0].toFixed(1)},${top[0][1].toFixed(1)}`;
45117
+ for (let s = 1; s <= samples; s++) {
45118
+ d += ` L${top[s][0].toFixed(1)},${top[s][1].toFixed(1)}`;
45119
+ }
45120
+ d += ` L${bot[samples][0].toFixed(1)},${bot[samples][1].toFixed(1)}`;
45121
+ for (let s = samples - 1; s >= 0; s--) {
45122
+ d += ` L${bot[s][0].toFixed(1)},${bot[s][1].toFixed(1)}`;
45123
+ }
45124
+ return { d: `${d} Z`, bounds: { minX, minY, maxX, maxY } };
45125
+ }
45126
+ function buildLabel(i, text, _width, _height, config2, offsetXNorm) {
45127
+ const { baseline, xStart, xStem, labelNudge } = config2;
45128
+ const xf = xStart[i] - xStem[i] + offsetXNorm;
45129
+ const below = cumBelow(i, xf, config2);
45130
+ const v = valAt(i, xf, config2);
45131
+ const yTop = baseline - below - v;
45132
+ const [nudgeX, nudgeY] = labelNudge[i];
45133
+ return {
45134
+ index: i,
45135
+ text,
45136
+ leftPct: (xf + nudgeX) * 100,
45137
+ topPct: (yTop - labelGapAbove(i, config2) + nudgeY) * 100,
45138
+ placement: "start"
45139
+ };
45140
+ }
45141
+ function clampBandCount(count2, max2 = 5) {
45142
+ if (!Number.isFinite(count2)) return max2;
45143
+ return Math.min(max2, Math.max(1, Math.round(count2)));
45144
+ }
45145
+ function resolveChartGeometryWidth(containerWidth, containerHeight, chartWidth) {
45146
+ const layoutWidth = Number.isFinite(containerWidth) && containerWidth > 0 ? containerWidth : 0;
45147
+ const layoutHeight = Number.isFinite(containerHeight) && containerHeight > 0 ? containerHeight : 0;
45148
+ if (chartWidth != null && Number.isFinite(chartWidth) && chartWidth > 0) {
45149
+ const nominal = Math.max(chartWidth, layoutHeight);
45150
+ if (layoutWidth > 0) {
45151
+ return Math.min(nominal, layoutWidth);
45152
+ }
45153
+ return nominal;
45154
+ }
45155
+ if (layoutWidth > 0) {
45156
+ return layoutWidth;
45157
+ }
45158
+ return layoutHeight;
45159
+ }
45160
+ function nominalChartWidthPx(chartWidth, renderedHeightPx) {
45161
+ const heightPx = Number.isFinite(renderedHeightPx) && renderedHeightPx > 0 ? renderedHeightPx : 0;
45162
+ return Math.max(chartWidth, heightPx);
45163
+ }
45164
+ function ribbonEndStaggerNorm(chartWidth, height) {
45165
+ const w = Math.max(chartWidth, height);
45166
+ if (w <= height) return 0;
45167
+ const intPx = (w - height) * RIBBON_END_STAGGER_X;
45168
+ return intPx / w;
45169
+ }
45170
+ function buildStaggeredEndDraw(bandCount, chartWidth, height) {
45171
+ const validBandCount = clampBandCount(bandCount);
45172
+ const intNorm = ribbonEndStaggerNorm(chartWidth, height);
45173
+ const t = DEFAULT_RIBBON_CONFIG.xEndStagger > 0 ? Math.min(1, intNorm / DEFAULT_RIBBON_CONFIG.xEndStagger) : 0;
45174
+ const rightAnchor = MARK_RIBBON_RIGHT_EDGE - t * (MARK_RIBBON_RIGHT_EDGE - HERO_WIDE_TOP_END);
45175
+ const last = validBandCount - 1;
45176
+ return Array.from({ length: validBandCount }, (_, i) => rightAnchor - (last - i) * intNorm);
45177
+ }
45178
+ function configWithChartDimensions(base, chartWidth, height) {
45179
+ return {
45180
+ ...base,
45181
+ xEndDraw: buildStaggeredEndDraw(base.xStart.length, chartWidth, height)
45182
+ };
45183
+ }
45184
+ function buildRibbonGeometry(width, height, labels, config2 = DEFAULT_RIBBON_CONFIG, bandCount = config2.xStart.length, layout = "hero") {
45185
+ const totalBands = config2.xStart.length;
45186
+ const visibleBands = clampBandCount(bandCount, totalBands);
45187
+ if (width <= 0 || height <= 0) {
45188
+ return null;
45189
+ }
45190
+ const paths = [];
45191
+ const clipWidthPx = [];
45192
+ let bounds = null;
45193
+ for (let i = 0; i < visibleBands; i++) {
45194
+ const band = buildBandPath(i, width, height, config2);
45195
+ paths.push(band.d);
45196
+ clipWidthPx.push(config2.xEndDraw[i] * width);
45197
+ bounds = bounds ? mergeBounds(bounds, band.bounds) : band.bounds;
45198
+ }
45199
+ let fitPaths = paths;
45200
+ let fitClips = clipWidthPx;
45201
+ let offsetXNorm = 0;
45202
+ if (bounds) {
45203
+ if (layout === "mark") {
45204
+ const { scaleX, scaleY, tx, ty } = computeMarkFitTransform(bounds, width, height, 0.05);
45205
+ fitPaths = paths.map((p) => transformPathD(p, scaleX, scaleY, tx, ty));
45206
+ const alignedClipRight = MARK_RIBBON_RIGHT_EDGE * width * scaleX + tx;
45207
+ fitClips = clipWidthPx.map(() => alignedClipRight);
45208
+ offsetXNorm = tx / width;
45209
+ } else if (layout === "hero") {
45210
+ const bw = bounds.maxX - bounds.minX;
45211
+ const tx = (width - bw) / 2 - bounds.minX;
45212
+ fitPaths = paths.map((p) => transformPathD(p, 1, 1, tx, 0));
45213
+ fitClips = clipWidthPx.map((w) => w + tx);
45214
+ offsetXNorm = tx / width;
45215
+ }
45216
+ }
45217
+ const paddedLabels = Array.from({ length: visibleBands }, (_, i) => labels[i] ?? "");
45218
+ const builtLabels = paddedLabels.map(
45219
+ (text, i) => buildLabel(i, text, width, height, config2, offsetXNorm)
45220
+ );
45221
+ return {
45222
+ paths: fitPaths,
45223
+ clipWidthPx: fitClips,
45224
+ labels: builtLabels,
45225
+ width,
45226
+ height,
45227
+ bandCount: visibleBands
45228
+ };
45229
+ }
45230
+ function easePower1InOut(t) {
45231
+ const p = Math.max(0, Math.min(1, t));
45232
+ return p < 0.5 ? 2 * p * p : 1 - (-2 * p + 2) ** 2 / 2;
45233
+ }
45234
+ function easePower2Out(t) {
45235
+ const p = Math.max(0, Math.min(1, t));
45236
+ return 1 - (1 - p) ** 2;
45237
+ }
45238
+ function ribbonLabelRevealTime(labelIndex, duration2, config2 = DEFAULT_RIBBON_CONFIG) {
45239
+ return Math.min(duration2, config2.labelX[labelIndex] * duration2 + 0.25);
45240
+ }
45241
+ const ribbonChartLabelDefault = "#FFFFFF";
45242
+ const ribbonChartSurface = {
45243
+ white: "#FFFFFF",
45244
+ black: "#0D1023"
45245
+ };
45246
+ const ribbonBandGradientStops = [
45247
+ { idSuffix: "0", start: "#33198C", end: "#5B2BF0" },
45248
+ { idSuffix: "1", start: "#4A22C8", end: "#7A3DF5" },
45249
+ { idSuffix: "2", start: "#6A32E8", end: "#9A6BFF" },
45250
+ { idSuffix: "3", start: "#8A52F5", end: "#BD84FF" },
45251
+ { idSuffix: "4", start: "#A86BFF", end: "#E6ABFF" }
45252
+ ];
45253
+ const RIBBON_BACKGROUND_CLASS = {
45254
+ white: "fairmint-equity-ribbon--bg-white",
45255
+ black: "fairmint-equity-ribbon--bg-black",
45256
+ transparent: "fairmint-equity-ribbon--bg-transparent"
45257
+ };
45258
+ const DEFAULT_RIBBON_LABELS = [
45259
+ "Cap Table",
45260
+ "Fundraising",
45261
+ "Grants",
45262
+ "Secondaries",
45263
+ "Liquidity"
45264
+ ];
45265
+ const RIBBON_BAND_GRADIENTS = ribbonBandGradientStops;
45266
+ const RIBBON_SURFACE_CSS_VARS = {
45267
+ "--fairmint-ribbon-surface-white": ribbonChartSurface.white,
45268
+ "--fairmint-ribbon-surface-black": ribbonChartSurface.black
45269
+ };
45270
+ const REVEAL_DURATION_MS = 2300;
45271
+ const REVEAL_DELAY_MS = 420;
45272
+ function labelAnchorTransform(_placement, yOffsetPx) {
45273
+ return `translate(0, calc(-100% + ${yOffsetPx}px))`;
45274
+ }
45275
+ function labelClassName(_placement) {
45276
+ return "fairmint-equity-ribbon__label fairmint-equity-ribbon__label--start";
45277
+ }
45278
+ function resolveGradients(gradients, bandCount) {
45279
+ const source = gradients ?? RIBBON_BAND_GRADIENTS;
45280
+ return Array.from({ length: bandCount }, (_, i) => {
45281
+ const fallback = RIBBON_BAND_GRADIENTS[i] ?? RIBBON_BAND_GRADIENTS[RIBBON_BAND_GRADIENTS.length - 1];
45282
+ const g = source[i] ?? fallback;
45283
+ return {
45284
+ idSuffix: String(i),
45285
+ start: g.start,
45286
+ end: g.end
45287
+ };
45288
+ });
45289
+ }
45290
+ function EquityRibbonChart({
45291
+ labels = DEFAULT_RIBBON_LABELS,
45292
+ bandCount: bandCountProp = 5,
45293
+ gradients,
45294
+ animated = true,
45295
+ hideLabels = false,
45296
+ background = "transparent",
45297
+ gapColor,
45298
+ layout = "hero",
45299
+ labelColor = ribbonChartLabelDefault,
45300
+ className,
45301
+ height,
45302
+ chartWidth,
45303
+ style
45304
+ }) {
45305
+ const bandCount = clampBandCount(bandCountProp);
45306
+ const labelsKey = labels.join("");
45307
+ const labelsRef = useRef(labels);
45308
+ labelsRef.current = labels;
45309
+ const bandGradients = resolveGradients(gradients, bandCount);
45310
+ const uid = useId$2().replace(/:/g, "");
45311
+ const rootRef = useRef(null);
45312
+ const playedRef = useRef(false);
45313
+ const rafRef = useRef(null);
45314
+ const revealGenerationRef = useRef(0);
45315
+ const revealScheduleRef = useRef(0);
45316
+ const cancelRevealAnimation = useCallback(() => {
45317
+ revealGenerationRef.current += 1;
45318
+ if (rafRef.current !== null) {
45319
+ cancelAnimationFrame(rafRef.current);
45320
+ rafRef.current = null;
45321
+ }
45322
+ }, []);
45323
+ const [geometry, setGeometry] = useState(null);
45324
+ const [revealWidth, setRevealWidth] = useState(0);
45325
+ const [labelStyles, setLabelStyles] = useState(
45326
+ () => labels.slice(0, bandCount).map(() => ({ opacity: 0, transform: "translateY(6px)" }))
45327
+ );
45328
+ const rebuild = useCallback(() => {
45329
+ cancelRevealAnimation();
45330
+ const el = rootRef.current;
45331
+ if (!el) return;
45332
+ const containerWidth = el.clientWidth;
45333
+ const heightPx = el.clientHeight;
45334
+ const geometryWidth = resolveChartGeometryWidth(containerWidth, heightPx, chartWidth);
45335
+ const baseConfig = layout === "mark" ? MARK_RIBBON_CONFIG : DEFAULT_RIBBON_CONFIG;
45336
+ const config2 = configWithChartDimensions(baseConfig, geometryWidth, heightPx);
45337
+ const built = buildRibbonGeometry(
45338
+ geometryWidth,
45339
+ heightPx,
45340
+ labelsRef.current,
45341
+ config2,
45342
+ bandCount,
45343
+ layout
45344
+ );
45345
+ setGeometry(built);
45346
+ if (built && (playedRef.current || !animated)) {
45347
+ setRevealWidth(built.width);
45348
+ setLabelStyles(
45349
+ built.labels.map((lbl) => ({
45350
+ opacity: 1,
45351
+ transform: labelAnchorTransform(lbl.placement, 0)
45352
+ }))
45353
+ );
45354
+ }
45355
+ }, [animated, bandCount, cancelRevealAnimation, chartWidth, labelsKey, layout]);
45356
+ useLayoutEffect(() => {
45357
+ playedRef.current = false;
45358
+ revealScheduleRef.current = 0;
45359
+ cancelRevealAnimation();
45360
+ if (animated) {
45361
+ setRevealWidth(0);
45362
+ setLabelStyles(
45363
+ labelsRef.current.slice(0, bandCount).map(() => ({
45364
+ opacity: 0,
45365
+ transform: "translateY(6px)"
45366
+ }))
45367
+ );
45368
+ }
45369
+ rebuild();
45370
+ }, [animated, bandCount, cancelRevealAnimation, rebuild]);
45371
+ useEffect(() => () => cancelRevealAnimation(), [cancelRevealAnimation]);
45372
+ useEffect(() => {
45373
+ const el = rootRef.current;
45374
+ if (!el) return;
45375
+ if (typeof ResizeObserver === "undefined") {
45376
+ rebuild();
45377
+ return;
45378
+ }
45379
+ const ro = new ResizeObserver(() => {
45380
+ rebuild();
45381
+ });
45382
+ ro.observe(el);
45383
+ return () => ro.disconnect();
45384
+ }, [rebuild]);
45385
+ const runReveal = useCallback(() => {
45386
+ if (!geometry || playedRef.current) return;
45387
+ if (!animated) {
45388
+ playedRef.current = true;
45389
+ setRevealWidth(geometry.width);
45390
+ setLabelStyles(
45391
+ geometry.labels.map((lbl) => ({
45392
+ opacity: 1,
45393
+ transform: labelAnchorTransform(lbl.placement, 0)
45394
+ }))
45395
+ );
45396
+ return;
45397
+ }
45398
+ const reduceMotion = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
45399
+ if (reduceMotion) {
45400
+ playedRef.current = true;
45401
+ setRevealWidth(geometry.width);
45402
+ setLabelStyles(
45403
+ geometry.labels.map((lbl) => ({
45404
+ opacity: 1,
45405
+ transform: labelAnchorTransform(lbl.placement, 0)
45406
+ }))
45407
+ );
45408
+ return;
45409
+ }
45410
+ playedRef.current = true;
45411
+ setRevealWidth(0);
45412
+ setLabelStyles(
45413
+ geometry.labels.map((lbl) => ({
45414
+ opacity: 0,
45415
+ transform: labelAnchorTransform(lbl.placement, 6)
45416
+ }))
45417
+ );
45418
+ const generation = revealGenerationRef.current;
45419
+ const start = performance.now();
45420
+ const tick = (now) => {
45421
+ if (generation !== revealGenerationRef.current) return;
45422
+ const elapsed = now - start;
45423
+ const t = Math.min(1, elapsed / REVEAL_DURATION_MS);
45424
+ const eased = easePower1InOut(t);
45425
+ setRevealWidth(geometry.width * eased);
45426
+ setLabelStyles(
45427
+ geometry.labels.map((lbl, i) => {
45428
+ const labelStart = ribbonLabelRevealTime(i, REVEAL_DURATION_MS / 1e3) * 1e3;
45429
+ const labelDur = 550;
45430
+ const lt2 = Math.min(1, Math.max(0, (elapsed - labelStart) / labelDur));
45431
+ const lo = easePower2Out(lt2);
45432
+ const y = 6 * (1 - lo);
45433
+ return { opacity: lo, transform: labelAnchorTransform(lbl.placement, y) };
45434
+ })
45435
+ );
45436
+ if (t < 1) {
45437
+ rafRef.current = requestAnimationFrame(tick);
45438
+ } else {
45439
+ setRevealWidth(geometry.width);
45440
+ setLabelStyles(
45441
+ geometry.labels.map((lbl) => ({
45442
+ opacity: 1,
45443
+ transform: labelAnchorTransform(lbl.placement, 0)
45444
+ }))
45445
+ );
45446
+ }
45447
+ };
45448
+ rafRef.current = requestAnimationFrame(tick);
45449
+ }, [animated, geometry]);
45450
+ useEffect(() => {
45451
+ if (!geometry || playedRef.current) return;
45452
+ const scheduleId = ++revealScheduleRef.current;
45453
+ const delay = scheduleId === 1 ? REVEAL_DELAY_MS : 0;
45454
+ const timer = window.setTimeout(() => {
45455
+ if (scheduleId !== revealScheduleRef.current) return;
45456
+ runReveal();
45457
+ }, delay);
45458
+ return () => window.clearTimeout(timer);
45459
+ }, [geometry, runReveal]);
45460
+ const suppressLabels = hideLabels || layout === "mark";
45461
+ const activeGeometry = geometry && geometry.bandCount === bandCount ? geometry : null;
45462
+ const hasExplicitWidth = chartWidth != null && Number.isFinite(chartWidth) && chartWidth > 0;
45463
+ const rootClass = [
45464
+ "fairmint-equity-ribbon",
45465
+ RIBBON_BACKGROUND_CLASS[background],
45466
+ layout === "mark" ? "fairmint-equity-ribbon--layout-mark" : "",
45467
+ hasExplicitWidth ? "fairmint-equity-ribbon--has-chart-width" : "",
45468
+ suppressLabels ? "fairmint-equity-ribbon--hidden-labels" : "",
45469
+ className ?? ""
45470
+ ].filter(Boolean).join(" ");
45471
+ const resolvedGapColor = gapColor ?? (background === "white" ? ribbonChartSurface.white : background === "black" ? ribbonChartSurface.black : ribbonChartSurface.black);
45472
+ const nominalWidthPx = hasExplicitWidth && chartWidth ? nominalChartWidthPx(chartWidth, activeGeometry?.height ?? 0) : null;
45473
+ const rootStyle = {
45474
+ ...RIBBON_SURFACE_CSS_VARS,
45475
+ "--fairmint-ribbon-gap-color": resolvedGapColor,
45476
+ ...style,
45477
+ ...height ? { height } : {},
45478
+ ...nominalWidthPx != null ? { "--fairmint-ribbon-nominal-width": `${nominalWidthPx}px` } : {}
45479
+ };
45480
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: rootClass, style: rootStyle, children: [
45481
+ /* @__PURE__ */ jsxs(
45482
+ "svg",
45483
+ {
45484
+ className: "fairmint-equity-ribbon__svg",
45485
+ preserveAspectRatio: "none",
45486
+ "aria-hidden": "true",
45487
+ viewBox: activeGeometry ? `0 0 ${activeGeometry.width} ${activeGeometry.height}` : "0 0 1 1",
45488
+ children: [
45489
+ /* @__PURE__ */ jsxs("defs", { children: [
45490
+ bandGradients.map((g) => /* @__PURE__ */ jsxs(
45491
+ "linearGradient",
45492
+ {
45493
+ id: `${uid}-bg${g.idSuffix}`,
45494
+ x1: "0",
45495
+ y1: "1",
45496
+ x2: "1",
45497
+ y2: "0",
45498
+ children: [
45499
+ /* @__PURE__ */ jsx("stop", { offset: "0", stopColor: g.start }),
45500
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: g.end })
45501
+ ]
45502
+ },
45503
+ g.idSuffix
45504
+ )),
45505
+ /* @__PURE__ */ jsx("clipPath", { id: `${uid}-revealclip`, children: /* @__PURE__ */ jsx("rect", { x: 0, y: -1, width: revealWidth, height: (activeGeometry?.height ?? 1) + 2 }) }),
45506
+ activeGeometry ? activeGeometry.paths.map((_, i) => /* @__PURE__ */ jsx("clipPath", { id: `${uid}-bandclip${i}`, children: /* @__PURE__ */ jsx(
45507
+ "rect",
45508
+ {
45509
+ x: 0,
45510
+ y: -1,
45511
+ width: activeGeometry.clipWidthPx[i],
45512
+ height: activeGeometry.height + 2
45513
+ }
45514
+ ) }, `bandclip-${bandGradients[i].idSuffix}`)) : null
45515
+ ] }),
45516
+ /* @__PURE__ */ jsx("g", { clipPath: `url(#${uid}-revealclip)`, children: activeGeometry?.paths.map((d, i) => /* @__PURE__ */ jsx(
45517
+ "path",
45518
+ {
45519
+ className: "fairmint-equity-ribbon__band",
45520
+ fill: `url(#${uid}-bg${bandGradients[i].idSuffix})`,
45521
+ clipPath: `url(#${uid}-bandclip${i})`,
45522
+ d
45523
+ },
45524
+ bandGradients[i].idSuffix
45525
+ )) })
45526
+ ]
45527
+ }
45528
+ ),
45529
+ !suppressLabels && activeGeometry?.labels.map((lbl, i) => /* @__PURE__ */ jsx(
45530
+ "div",
45531
+ {
45532
+ className: labelClassName(lbl.placement),
45533
+ style: {
45534
+ left: `${lbl.leftPct}%`,
45535
+ top: `${lbl.topPct}%`,
45536
+ color: labelColor,
45537
+ opacity: labelStyles[i]?.opacity ?? 0,
45538
+ transform: labelStyles[i]?.transform
45539
+ },
45540
+ children: lbl.text
45541
+ },
45542
+ `${lbl.text}-${lbl.index}`
45543
+ ))
45544
+ ] });
45545
+ }
45546
+ function FairmintRibbonMark({
45547
+ size: size2 = 64,
45548
+ bandCount = 5,
45549
+ gradients,
45550
+ background = "black",
45551
+ gapColor,
45552
+ labelColor,
45553
+ className,
45554
+ style
45555
+ }) {
45556
+ const dimension = `${size2}px`;
45557
+ return /* @__PURE__ */ jsx(
45558
+ EquityRibbonChart,
45559
+ {
45560
+ bandCount,
45561
+ gradients,
45562
+ background,
45563
+ gapColor,
45564
+ labelColor,
45565
+ layout: "mark",
45566
+ animated: false,
45567
+ hideLabels: true,
45568
+ className: ["fairmint-ribbon-mark", className].filter(Boolean).join(" "),
45569
+ height: dimension,
45570
+ style: { width: dimension, height: dimension, ...style }
45571
+ }
45572
+ );
45573
+ }
45001
45574
  function InvestmentCard({
45002
45575
  companyName,
45003
45576
  logoUrl,
@@ -45768,6 +46341,8 @@ export {
45768
46341
  ConfirmDialog,
45769
46342
  Container,
45770
46343
  Currency,
46344
+ DEFAULT_RIBBON_CONFIG,
46345
+ DEFAULT_RIBBON_LABELS,
45771
46346
  DataRow,
45772
46347
  DataTable,
45773
46348
  DateDisplay,
@@ -45780,12 +46355,14 @@ export {
45780
46355
  EmailField,
45781
46356
  EmptyState,
45782
46357
  EquityGrantCard,
46358
+ EquityRibbonChart,
45783
46359
  ErrorFallback,
45784
46360
  ErrorPage,
45785
46361
  EthereumAddressField,
45786
46362
  Eyebrow,
45787
46363
  FairmintLogo,
45788
46364
  FairmintProvider,
46365
+ FairmintRibbonMark,
45789
46366
  Flex,
45790
46367
  Form,
45791
46368
  FormField,
@@ -45796,6 +46373,8 @@ export {
45796
46373
  ListRow,
45797
46374
  Loader,
45798
46375
  LoadingSpinner,
46376
+ MARK_RIBBON_CONFIG,
46377
+ MARK_RIBBON_RIGHT_EDGE,
45799
46378
  MOBILE_BREAKPOINT,
45800
46379
  Menu,
45801
46380
  Modal,
@@ -45815,6 +46394,8 @@ export {
45815
46394
  PhotoCropModal,
45816
46395
  PinInput,
45817
46396
  Progress,
46397
+ RIBBON_BACKGROUND_CLASS,
46398
+ RIBBON_BAND_GRADIENTS,
45818
46399
  RadioCard,
45819
46400
  RedactedValue,
45820
46401
  ScrollArea,
@@ -45848,6 +46429,7 @@ export {
45848
46429
  ZERO_ETHEREUM_ADDRESS,
45849
46430
  ZipField,
45850
46431
  brandColors,
46432
+ clampBandCount,
45851
46433
  colors,
45852
46434
  createFairmintMantineProviderProps,
45853
46435
  createZipSchema,