@digilogiclabs/saas-factory-ui 1.30.0 → 1.30.2
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/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +128 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -58
- package/dist/index.mjs.map +1 -1
- package/dist/web/index.d.mts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +128 -58
- package/dist/web/index.js.map +1 -1
- package/dist/web/index.mjs +128 -58
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -5293,7 +5293,7 @@ declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIco
|
|
|
5293
5293
|
* delta: it keeps connector strokes crisp under the pan/zoom transform's
|
|
5294
5294
|
* `scale()` (otherwise sub-1 fit-zoom renders them sub-pixel and invisible).
|
|
5295
5295
|
*/
|
|
5296
|
-
declare const FLOW_TREE_CSS = "\n@import url(\"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap\");\n\n.ft-shell {\n --ft-bg: oklch(0.165 0.008 80);\n --ft-panel: oklch(0.21 0.008 80);\n --ft-panel-2: oklch(0.25 0.01 80);\n --ft-fg: oklch(0.96 0.005 85);\n --ft-muted-fg: oklch(0.68 0.008 80);\n --ft-dim-fg: oklch(0.5 0.008 80);\n --ft-line: oklch(0.28 0.008 80);\n --ft-line-strong: oklch(0.4 0.008 80);\n --ft-grid-strong: oklch(0.24 0.008 80);\n --ft-tier-orange: #f16c25;\n --ft-tier-teal: #5fb7b8;\n --ft-tier-blue: oklch(0.72 0.12 240);\n --ft-tier-amber: oklch(0.8 0.14 75);\n --ft-tier-green: oklch(0.74 0.14 155);\n --ft-accent: var(--ft-tier-orange);\n --ft-serif: \"Instrument Serif\", Georgia, serif;\n --ft-sans: \"Inter\", system-ui, sans-serif;\n --ft-mono: \"JetBrains Mono\", ui-monospace, Menlo, monospace;\n\n /* Defensive shell sizing \u2014 fills flex parents (flex: 1) and block\n parents with definite dimensions (width/height: 100%). */\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n min-height: 420px;\n display: flex;\n flex-direction: column;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1200px 700px at 15% -10%, oklch(0.22 0.012 60), transparent 60%),\n radial-gradient(900px 600px at 110% 110%, oklch(0.2 0.02 200 / 0.5), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-tier-orange); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-tier-orange);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n/* edges */\n.ft-edge {\n fill: none;\n /* Keep stroke widths crisp under the SVG <g scale(z)> pan/zoom transform.\n Without this, fit-to-viewport (z \u2248 0.6-0.9) renders 1.6px strokes as\n sub-pixel and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms;\n}\n.ft-edge--off { stroke: var(--ft-line); stroke-width: 1; opacity: 0.55; }\n.ft-edge--on {\n stroke: var(--ft-tier-orange);\n stroke-width: 2.4;\n stroke-linecap: round;\n /* 0.1px \"dot\" + 9px gap \u2192 renders as discrete round beads. Animating\n stroke-dashoffset on this pattern produces the unmistakable\n \"flowing dots\" effect (vs the previous dashed look). */\n stroke-dasharray: 0.1 9;\n opacity: 1;\n filter: drop-shadow(0 0 4px oklch(0.7 0.18 50 / 0.45));\n animation: ftDashFlow 1.6s linear infinite;\n}\n.ft-edge--on.ft-edge--solid {\n /* Fallback for any consumer that explicitly opts out of the bead\n pattern \u2014 keeps the legacy solid orange line. */\n stroke-dasharray: none;\n animation: none;\n}\n@keyframes ftDashFlow {\n /* -18 == 2 \u00D7 (0.1 + 9) so each cycle advances the bead exactly two\n gap-units, which reads as smooth perpetual flow at 1.6s. */\n to { stroke-dashoffset: -18; }\n}\n@media (prefers-reduced-motion: reduce) {\n .ft-edge--on { animation: none; }\n}\n\n/* card */\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 0;\n align-items: stretch;\n background: oklch(0.21 0.008 80);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: oklch(0.23 0.01 80 / 0.6); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: oklch(0.17 0.008 80);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: oklch(0.19 0.008 80);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover { background: oklch(0.23 0.01 80); color: var(--tier-local); }\n.ft-card__mini.is-on { color: var(--tier-local); background: oklch(0.23 0.01 80); }\n\n/* states */\n.ft-card--active {\n background: oklch(0.25 0.015 60);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-tier-orange);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-tier-orange);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n}\n\n/* detail pop */\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: oklch(0.16 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-tier-orange);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n /* Clear room for the two stacked rail buttons in the top-right\n (expand + close). Bumped from 28px \u2192 58px to fit both. */\n margin: -2px 58px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close,\n.ft-detail__expand {\n position: absolute;\n top: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close { right: 8px; }\n/* Expand button sits immediately to the left of close. */\n.ft-detail__expand { right: 36px; }\n.ft-detail__close:hover,\n.ft-detail__expand:hover {\n color: var(--ft-fg);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n /* Inter (sans) at slightly larger size + medium weight reads\n dramatically better than serif italic on dark stages, while still\n looking distinct from the body paragraph. The orange accent\n border on the left preserves the \"pull-quote\" rhythm. */\n font-family: var(--ft-sans);\n font-size: 14.5px;\n line-height: 1.45;\n font-style: normal;\n font-weight: 500;\n color: var(--ft-fg);\n padding-left: 10px;\n border-left: 2px solid var(--ft-tier-orange);\n margin-bottom: 12px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-tier-orange); }\n.ft-btn--primary {\n background: var(--ft-tier-orange);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-tier-orange);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n\n/* \u2500\u2500\u2500 Fullscreen detail overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Triggered by the expand button in any inline DetailPop. Renders\n inside .ft-stage as an absolutely-positioned scrim + scrollable\n panel. The inline pop stays mounted (cheaper React tree) but the\n user's focus and the pointer-events both transfer to the overlay\n while it's visible.\n*/\n.ft-detail-overlay {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: flex;\n align-items: stretch;\n justify-content: center;\n /* Translucent backdrop blurs the flow tree behind so the focused\n content reads cleanly without losing the user's \"where am I\"\n spatial anchor. */\n background: oklch(0.12 0.008 80 / 0.78);\n backdrop-filter: blur(6px);\n animation: ftOverlayIn 200ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n.ft-detail-overlay__panel {\n position: relative;\n margin: 36px;\n flex: 1 1 auto;\n max-width: 760px;\n background: oklch(0.18 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 3px solid var(--ft-tier-orange);\n border-radius: 4px;\n overflow: auto;\n padding: 26px 32px 28px;\n box-shadow: 0 30px 80px -10px oklch(0 0 0 / 0.7);\n animation: ftOverlayPanelIn 240ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayPanelIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail-overlay__panel .ft-detail__title {\n font-size: 11px;\n}\n.ft-detail-overlay__panel .ft-detail__lead {\n font-size: 18px;\n line-height: 1.5;\n padding-left: 14px;\n}\n.ft-detail-overlay__panel .ft-detail__body {\n font-size: 14px;\n color: var(--ft-fg);\n line-height: 1.65;\n}\n.ft-detail-overlay__panel .ft-detail__bullets {\n font-size: 14px;\n line-height: 1.55;\n padding-left: 20px;\n}\n.ft-detail-overlay__panel .ft-detail__bullets li { margin-bottom: 6px; }\n.ft-detail-overlay__panel .ft-detail__meta-row {\n font-size: 13px;\n grid-template-columns: 140px 1fr;\n}\n.ft-detail-overlay__panel .ft-detail__meta-row dt { font-size: 10.5px; }\n.ft-detail-overlay__panel .ft-btn { font-size: 11px; padding: 9px 14px; }\n.ft-detail-overlay__head {\n display: flex;\n align-items: center;\n margin: 0 0 14px;\n padding-right: 70px;\n}\n.ft-detail-overlay__title {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-detail-overlay__close,\n.ft-detail-overlay__collapse {\n position: absolute;\n top: 16px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line-strong);\n border-radius: 2px;\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail-overlay__close { right: 16px; }\n.ft-detail-overlay__collapse { right: 52px; }\n.ft-detail-overlay__close:hover,\n.ft-detail-overlay__collapse:hover {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n\n@media (max-width: 760px) {\n .ft-detail-overlay__panel {\n margin: 12px;\n padding: 18px 18px 22px;\n }\n .ft-detail-overlay__panel .ft-detail__lead { font-size: 16px; }\n}\n";
|
|
5296
|
+
declare const FLOW_TREE_CSS = "\n@import url(\"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap\");\n\n.ft-shell {\n --ft-bg: oklch(0.165 0.008 80);\n --ft-panel: oklch(0.21 0.008 80);\n --ft-panel-2: oklch(0.25 0.01 80);\n --ft-fg: oklch(0.96 0.005 85);\n --ft-muted-fg: oklch(0.68 0.008 80);\n --ft-dim-fg: oklch(0.5 0.008 80);\n --ft-line: oklch(0.28 0.008 80);\n --ft-line-strong: oklch(0.4 0.008 80);\n --ft-grid-strong: oklch(0.24 0.008 80);\n --ft-tier-orange: #f16c25;\n --ft-tier-teal: #5fb7b8;\n --ft-tier-blue: oklch(0.72 0.12 240);\n --ft-tier-amber: oklch(0.8 0.14 75);\n --ft-tier-green: oklch(0.74 0.14 155);\n --ft-accent: var(--ft-tier-orange);\n --ft-serif: \"Instrument Serif\", Georgia, serif;\n --ft-sans: \"Inter\", system-ui, sans-serif;\n --ft-mono: \"JetBrains Mono\", ui-monospace, Menlo, monospace;\n\n /* Defensive shell sizing \u2014 fills flex parents (flex: 1) and block\n parents with definite dimensions (width/height: 100%). */\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n min-height: 420px;\n display: flex;\n flex-direction: column;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1200px 700px at 15% -10%, oklch(0.22 0.012 60), transparent 60%),\n radial-gradient(900px 600px at 110% 110%, oklch(0.2 0.02 200 / 0.5), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-tier-orange); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-tier-orange);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n/* edges */\n.ft-edge {\n fill: none;\n /* Keep stroke widths crisp under the SVG <g scale(z)> pan/zoom transform.\n Without this, fit-to-viewport (z \u2248 0.6-0.9) renders 1.6px strokes as\n sub-pixel and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms;\n}\n.ft-edge--off { stroke: var(--ft-line); stroke-width: 1; opacity: 0.55; }\n.ft-edge--on {\n stroke: var(--ft-tier-orange);\n /* Verbatim restore of the reference design (local-docs/styles.css\n line 133-140). The 4px dash + 5px gap reads as continuous\n marching-ant flow \u2014 every frame, dash edges visibly sweep across\n the path because the gap (5px) is shorter than the dash itself.\n This is what gives the focused branches their \"alive\" feel; the\n wider gap variants (1/12 in 1.30.1, 0.1/9 in 1.30.0) read as\n isolated dots and look static at typical fit-zoom levels. */\n stroke-width: 1.6;\n stroke-dasharray: 4 5;\n opacity: 1;\n animation: ftDashFlow 1.4s linear infinite;\n /* Visibility-under-zoom fix from 1.29.1 \u2014 without this, fit-zoom\n transforms (z \u2248 0.4-0.7) render the 1.6px stroke as sub-pixel\n and the line becomes invisible. Keeps stroke-width and\n dasharray in screen pixels regardless of the SVG transform.\n Not in the reference because the reference demo runs at z=1. */\n vector-effect: non-scaling-stroke;\n}\n@keyframes ftDashFlow {\n /* -36 == 4 \u00D7 (4+5) so the pattern advances exactly four cycles\n per period at 1.4s \u2014 perceptibly fast flow without strobing. */\n to { stroke-dashoffset: -36; }\n}\n@media (prefers-reduced-motion: reduce) {\n /* a11y improvement not in the reference \u2014 users who suppress\n motion get a clean solid line instead of a static frozen dash. */\n .ft-edge--on {\n animation: none;\n stroke-dasharray: none;\n }\n}\n\n/* card */\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 0;\n align-items: stretch;\n background: oklch(0.21 0.008 80);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: oklch(0.23 0.01 80 / 0.6); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: oklch(0.17 0.008 80);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: oklch(0.19 0.008 80);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover { background: oklch(0.23 0.01 80); color: var(--tier-local); }\n.ft-card__mini.is-on { color: var(--tier-local); background: oklch(0.23 0.01 80); }\n\n/* states */\n.ft-card--active {\n background: oklch(0.25 0.015 60);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-tier-orange);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-tier-orange);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n}\n\n/* detail pop */\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: oklch(0.16 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-tier-orange);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n /* Clear room for the two stacked rail buttons in the top-right\n (expand + close). Bumped from 28px \u2192 58px to fit both. */\n margin: -2px 58px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close,\n.ft-detail__expand {\n position: absolute;\n top: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close { right: 8px; }\n/* Expand button sits immediately to the left of close. */\n.ft-detail__expand { right: 36px; }\n.ft-detail__close:hover,\n.ft-detail__expand:hover {\n color: var(--ft-fg);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n /* Inter (sans) at slightly larger size + medium weight reads\n dramatically better than serif italic on dark stages, while still\n looking distinct from the body paragraph. The orange accent\n border on the left preserves the \"pull-quote\" rhythm. */\n font-family: var(--ft-sans);\n font-size: 14.5px;\n line-height: 1.45;\n font-style: normal;\n font-weight: 500;\n color: var(--ft-fg);\n padding-left: 10px;\n border-left: 2px solid var(--ft-tier-orange);\n margin-bottom: 12px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-tier-orange); }\n.ft-btn--primary {\n background: var(--ft-tier-orange);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-tier-orange);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n /* Larger touch targets on phones \u2014 bumps zoom controls from 26px\n to 36px to comfortably clear the 44px Apple HIG / Material 48dp\n minimum once you account for visual padding. */\n .ft-controls { padding: 6px 8px; }\n .ft-controls button { width: 36px; height: 36px; font-size: 16px; }\n .ft-controls__reset { padding: 0 14px !important; height: 36px; }\n /* Inline detail popup buttons are intentionally compact in the\n inline footprint \u2014 but on touch the user can promote to the\n fullscreen overlay which has 28px buttons by default. */\n .ft-detail__close, .ft-detail__expand { width: 28px; height: 28px; }\n .ft-detail__expand { right: 42px; }\n .ft-detail__head { margin-right: 70px; }\n}\n@media (max-width: 480px) {\n /* Phone-sized: shrink card width so 5+ depth levels still fit\n reasonably without forcing the user to scroll horizontally to\n read the labels. Cards still ellipsis-truncate when needed. */\n .ft-card { font-size: 12px; }\n .ft-card__label { font-size: 14px; }\n .ft-card__sub { font-size: 9px; }\n .ft-controls__hint { display: none; }\n}\n\n/* \u2500\u2500\u2500 Fullscreen detail overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Triggered by the expand button in any inline DetailPop. Renders\n inside .ft-stage as an absolutely-positioned scrim + scrollable\n panel. The inline pop stays mounted (cheaper React tree) but the\n user's focus and the pointer-events both transfer to the overlay\n while it's visible.\n*/\n.ft-detail-overlay {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: flex;\n align-items: stretch;\n justify-content: center;\n /* Translucent backdrop blurs the flow tree behind so the focused\n content reads cleanly without losing the user's \"where am I\"\n spatial anchor. */\n background: oklch(0.12 0.008 80 / 0.78);\n backdrop-filter: blur(6px);\n animation: ftOverlayIn 200ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n.ft-detail-overlay__panel {\n position: relative;\n margin: 36px;\n flex: 1 1 auto;\n max-width: 760px;\n background: oklch(0.18 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 3px solid var(--ft-tier-orange);\n border-radius: 4px;\n overflow: auto;\n padding: 26px 32px 28px;\n box-shadow: 0 30px 80px -10px oklch(0 0 0 / 0.7);\n animation: ftOverlayPanelIn 240ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayPanelIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail-overlay__panel .ft-detail__title {\n font-size: 11px;\n}\n.ft-detail-overlay__panel .ft-detail__lead {\n font-size: 18px;\n line-height: 1.5;\n padding-left: 14px;\n}\n.ft-detail-overlay__panel .ft-detail__body {\n font-size: 14px;\n color: var(--ft-fg);\n line-height: 1.65;\n}\n.ft-detail-overlay__panel .ft-detail__bullets {\n font-size: 14px;\n line-height: 1.55;\n padding-left: 20px;\n}\n.ft-detail-overlay__panel .ft-detail__bullets li { margin-bottom: 6px; }\n.ft-detail-overlay__panel .ft-detail__meta-row {\n font-size: 13px;\n grid-template-columns: 140px 1fr;\n}\n.ft-detail-overlay__panel .ft-detail__meta-row dt { font-size: 10.5px; }\n.ft-detail-overlay__panel .ft-btn { font-size: 11px; padding: 9px 14px; }\n.ft-detail-overlay__head {\n display: flex;\n align-items: center;\n margin: 0 0 14px;\n padding-right: 70px;\n}\n.ft-detail-overlay__title {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-detail-overlay__close,\n.ft-detail-overlay__collapse {\n position: absolute;\n top: 16px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line-strong);\n border-radius: 2px;\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail-overlay__close { right: 16px; }\n.ft-detail-overlay__collapse { right: 52px; }\n.ft-detail-overlay__close:hover,\n.ft-detail-overlay__collapse:hover {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n\n@media (max-width: 760px) {\n .ft-detail-overlay__panel {\n margin: 12px;\n padding: 18px 18px 22px;\n }\n .ft-detail-overlay__panel .ft-detail__lead { font-size: 16px; }\n}\n";
|
|
5297
5297
|
|
|
5298
5298
|
type GameTimerMode = "countdown" | "countup";
|
|
5299
5299
|
type GameTimerThemePreset = "green" | "red" | "blue" | "amber" | "purple" | "pink" | "teal";
|
package/dist/index.d.ts
CHANGED
|
@@ -5293,7 +5293,7 @@ declare function FlowTree({ data, title, eyebrow, tagline, renderIcon: renderIco
|
|
|
5293
5293
|
* delta: it keeps connector strokes crisp under the pan/zoom transform's
|
|
5294
5294
|
* `scale()` (otherwise sub-1 fit-zoom renders them sub-pixel and invisible).
|
|
5295
5295
|
*/
|
|
5296
|
-
declare const FLOW_TREE_CSS = "\n@import url(\"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap\");\n\n.ft-shell {\n --ft-bg: oklch(0.165 0.008 80);\n --ft-panel: oklch(0.21 0.008 80);\n --ft-panel-2: oklch(0.25 0.01 80);\n --ft-fg: oklch(0.96 0.005 85);\n --ft-muted-fg: oklch(0.68 0.008 80);\n --ft-dim-fg: oklch(0.5 0.008 80);\n --ft-line: oklch(0.28 0.008 80);\n --ft-line-strong: oklch(0.4 0.008 80);\n --ft-grid-strong: oklch(0.24 0.008 80);\n --ft-tier-orange: #f16c25;\n --ft-tier-teal: #5fb7b8;\n --ft-tier-blue: oklch(0.72 0.12 240);\n --ft-tier-amber: oklch(0.8 0.14 75);\n --ft-tier-green: oklch(0.74 0.14 155);\n --ft-accent: var(--ft-tier-orange);\n --ft-serif: \"Instrument Serif\", Georgia, serif;\n --ft-sans: \"Inter\", system-ui, sans-serif;\n --ft-mono: \"JetBrains Mono\", ui-monospace, Menlo, monospace;\n\n /* Defensive shell sizing \u2014 fills flex parents (flex: 1) and block\n parents with definite dimensions (width/height: 100%). */\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n min-height: 420px;\n display: flex;\n flex-direction: column;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1200px 700px at 15% -10%, oklch(0.22 0.012 60), transparent 60%),\n radial-gradient(900px 600px at 110% 110%, oklch(0.2 0.02 200 / 0.5), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-tier-orange); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-tier-orange);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n/* edges */\n.ft-edge {\n fill: none;\n /* Keep stroke widths crisp under the SVG <g scale(z)> pan/zoom transform.\n Without this, fit-to-viewport (z \u2248 0.6-0.9) renders 1.6px strokes as\n sub-pixel and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms;\n}\n.ft-edge--off { stroke: var(--ft-line); stroke-width: 1; opacity: 0.55; }\n.ft-edge--on {\n stroke: var(--ft-tier-orange);\n stroke-width: 2.4;\n stroke-linecap: round;\n /* 0.1px \"dot\" + 9px gap \u2192 renders as discrete round beads. Animating\n stroke-dashoffset on this pattern produces the unmistakable\n \"flowing dots\" effect (vs the previous dashed look). */\n stroke-dasharray: 0.1 9;\n opacity: 1;\n filter: drop-shadow(0 0 4px oklch(0.7 0.18 50 / 0.45));\n animation: ftDashFlow 1.6s linear infinite;\n}\n.ft-edge--on.ft-edge--solid {\n /* Fallback for any consumer that explicitly opts out of the bead\n pattern \u2014 keeps the legacy solid orange line. */\n stroke-dasharray: none;\n animation: none;\n}\n@keyframes ftDashFlow {\n /* -18 == 2 \u00D7 (0.1 + 9) so each cycle advances the bead exactly two\n gap-units, which reads as smooth perpetual flow at 1.6s. */\n to { stroke-dashoffset: -18; }\n}\n@media (prefers-reduced-motion: reduce) {\n .ft-edge--on { animation: none; }\n}\n\n/* card */\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 0;\n align-items: stretch;\n background: oklch(0.21 0.008 80);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: oklch(0.23 0.01 80 / 0.6); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: oklch(0.17 0.008 80);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: oklch(0.19 0.008 80);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover { background: oklch(0.23 0.01 80); color: var(--tier-local); }\n.ft-card__mini.is-on { color: var(--tier-local); background: oklch(0.23 0.01 80); }\n\n/* states */\n.ft-card--active {\n background: oklch(0.25 0.015 60);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-tier-orange);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-tier-orange);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n}\n\n/* detail pop */\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: oklch(0.16 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-tier-orange);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n /* Clear room for the two stacked rail buttons in the top-right\n (expand + close). Bumped from 28px \u2192 58px to fit both. */\n margin: -2px 58px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close,\n.ft-detail__expand {\n position: absolute;\n top: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close { right: 8px; }\n/* Expand button sits immediately to the left of close. */\n.ft-detail__expand { right: 36px; }\n.ft-detail__close:hover,\n.ft-detail__expand:hover {\n color: var(--ft-fg);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n /* Inter (sans) at slightly larger size + medium weight reads\n dramatically better than serif italic on dark stages, while still\n looking distinct from the body paragraph. The orange accent\n border on the left preserves the \"pull-quote\" rhythm. */\n font-family: var(--ft-sans);\n font-size: 14.5px;\n line-height: 1.45;\n font-style: normal;\n font-weight: 500;\n color: var(--ft-fg);\n padding-left: 10px;\n border-left: 2px solid var(--ft-tier-orange);\n margin-bottom: 12px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-tier-orange); }\n.ft-btn--primary {\n background: var(--ft-tier-orange);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-tier-orange);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n}\n\n/* \u2500\u2500\u2500 Fullscreen detail overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Triggered by the expand button in any inline DetailPop. Renders\n inside .ft-stage as an absolutely-positioned scrim + scrollable\n panel. The inline pop stays mounted (cheaper React tree) but the\n user's focus and the pointer-events both transfer to the overlay\n while it's visible.\n*/\n.ft-detail-overlay {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: flex;\n align-items: stretch;\n justify-content: center;\n /* Translucent backdrop blurs the flow tree behind so the focused\n content reads cleanly without losing the user's \"where am I\"\n spatial anchor. */\n background: oklch(0.12 0.008 80 / 0.78);\n backdrop-filter: blur(6px);\n animation: ftOverlayIn 200ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n.ft-detail-overlay__panel {\n position: relative;\n margin: 36px;\n flex: 1 1 auto;\n max-width: 760px;\n background: oklch(0.18 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 3px solid var(--ft-tier-orange);\n border-radius: 4px;\n overflow: auto;\n padding: 26px 32px 28px;\n box-shadow: 0 30px 80px -10px oklch(0 0 0 / 0.7);\n animation: ftOverlayPanelIn 240ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayPanelIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail-overlay__panel .ft-detail__title {\n font-size: 11px;\n}\n.ft-detail-overlay__panel .ft-detail__lead {\n font-size: 18px;\n line-height: 1.5;\n padding-left: 14px;\n}\n.ft-detail-overlay__panel .ft-detail__body {\n font-size: 14px;\n color: var(--ft-fg);\n line-height: 1.65;\n}\n.ft-detail-overlay__panel .ft-detail__bullets {\n font-size: 14px;\n line-height: 1.55;\n padding-left: 20px;\n}\n.ft-detail-overlay__panel .ft-detail__bullets li { margin-bottom: 6px; }\n.ft-detail-overlay__panel .ft-detail__meta-row {\n font-size: 13px;\n grid-template-columns: 140px 1fr;\n}\n.ft-detail-overlay__panel .ft-detail__meta-row dt { font-size: 10.5px; }\n.ft-detail-overlay__panel .ft-btn { font-size: 11px; padding: 9px 14px; }\n.ft-detail-overlay__head {\n display: flex;\n align-items: center;\n margin: 0 0 14px;\n padding-right: 70px;\n}\n.ft-detail-overlay__title {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-detail-overlay__close,\n.ft-detail-overlay__collapse {\n position: absolute;\n top: 16px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line-strong);\n border-radius: 2px;\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail-overlay__close { right: 16px; }\n.ft-detail-overlay__collapse { right: 52px; }\n.ft-detail-overlay__close:hover,\n.ft-detail-overlay__collapse:hover {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n\n@media (max-width: 760px) {\n .ft-detail-overlay__panel {\n margin: 12px;\n padding: 18px 18px 22px;\n }\n .ft-detail-overlay__panel .ft-detail__lead { font-size: 16px; }\n}\n";
|
|
5296
|
+
declare const FLOW_TREE_CSS = "\n@import url(\"https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap\");\n\n.ft-shell {\n --ft-bg: oklch(0.165 0.008 80);\n --ft-panel: oklch(0.21 0.008 80);\n --ft-panel-2: oklch(0.25 0.01 80);\n --ft-fg: oklch(0.96 0.005 85);\n --ft-muted-fg: oklch(0.68 0.008 80);\n --ft-dim-fg: oklch(0.5 0.008 80);\n --ft-line: oklch(0.28 0.008 80);\n --ft-line-strong: oklch(0.4 0.008 80);\n --ft-grid-strong: oklch(0.24 0.008 80);\n --ft-tier-orange: #f16c25;\n --ft-tier-teal: #5fb7b8;\n --ft-tier-blue: oklch(0.72 0.12 240);\n --ft-tier-amber: oklch(0.8 0.14 75);\n --ft-tier-green: oklch(0.74 0.14 155);\n --ft-accent: var(--ft-tier-orange);\n --ft-serif: \"Instrument Serif\", Georgia, serif;\n --ft-sans: \"Inter\", system-ui, sans-serif;\n --ft-mono: \"JetBrains Mono\", ui-monospace, Menlo, monospace;\n\n /* Defensive shell sizing \u2014 fills flex parents (flex: 1) and block\n parents with definite dimensions (width/height: 100%). */\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n min-height: 420px;\n display: flex;\n flex-direction: column;\n background: var(--ft-bg);\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n overflow: hidden;\n color: var(--ft-fg);\n font-family: var(--ft-sans);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n box-shadow: 0 30px 60px -30px oklch(0 0 0 / 0.5);\n}\n.ft-shell * { box-sizing: border-box; }\n.ft-shell .ft-mono {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n\n.ft-header {\n padding: 22px 28px 20px;\n border-bottom: 1px solid var(--ft-line);\n background: linear-gradient(180deg, oklch(0.21 0.01 60) 0%, oklch(0.19 0.008 70) 100%);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.ft-header__eyebrow {\n font-family: var(--ft-mono);\n font-size: 10.5px;\n letter-spacing: 0.22em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-header__title {\n margin: 0;\n font-family: var(--ft-serif);\n font-weight: 400;\n font-size: 44px;\n line-height: 1;\n letter-spacing: -0.015em;\n color: var(--ft-fg);\n}\n.ft-header__tagline {\n margin: 6px 0 0;\n max-width: 76ch;\n color: var(--ft-muted-fg);\n font-size: 13px;\n}\n\n.ft-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background:\n radial-gradient(1200px 700px at 15% -10%, oklch(0.22 0.012 60), transparent 60%),\n radial-gradient(900px 600px at 110% 110%, oklch(0.2 0.02 200 / 0.5), transparent 65%);\n}\n\n.ft-svg {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n cursor: grab;\n touch-action: none;\n user-select: none;\n}\n.ft-svg.is-dragging { cursor: grabbing; }\n\n.ft-controls {\n position: absolute;\n top: 14px;\n right: 14px;\n z-index: 5;\n display: flex;\n align-items: center;\n gap: 6px;\n background: oklch(0.2 0.008 80 / 0.94);\n border: 1px solid var(--ft-line-strong);\n padding: 5px 8px;\n backdrop-filter: blur(8px);\n border-radius: 2px;\n}\n.ft-controls button {\n font-family: var(--ft-mono);\n font-size: 12px;\n width: 26px;\n height: 26px;\n background: transparent;\n border: 1px solid var(--ft-line);\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n}\n.ft-controls button:hover { border-color: var(--ft-tier-orange); }\n.ft-controls__reset {\n width: auto !important;\n padding: 0 10px !important;\n font-size: 9.5px !important;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.ft-controls__hint {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n color: var(--ft-muted-fg);\n letter-spacing: 0.04em;\n padding-left: 8px;\n margin-left: 2px;\n border-left: 1px solid var(--ft-line);\n}\n\n.ft-legend {\n position: absolute;\n left: 18px;\n bottom: 14px;\n z-index: 5;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--ft-mono);\n font-size: 10px;\n color: var(--ft-muted-fg);\n background: oklch(0.2 0.008 80 / 0.88);\n border: 1px solid var(--ft-line);\n padding: 6px 10px;\n backdrop-filter: blur(6px);\n max-width: calc(100% - 220px);\n}\n.ft-legend__chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 2px 6px;\n margin: -2px 0;\n background: transparent;\n border: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n cursor: pointer;\n border-radius: 2px;\n transition: background 120ms, color 120ms;\n}\n.ft-legend__chip:hover {\n background: oklch(0.24 0.01 80);\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled {\n cursor: default;\n color: var(--ft-fg);\n}\n.ft-legend__chip:disabled:hover { background: transparent; }\n.ft-legend__dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--ft-tier-orange);\n flex-shrink: 0;\n}\n.ft-legend__sep {\n color: var(--ft-line-strong);\n margin: 0 2px;\n pointer-events: none;\n user-select: none;\n}\n\n/* edges */\n.ft-edge {\n fill: none;\n /* Keep stroke widths crisp under the SVG <g scale(z)> pan/zoom transform.\n Without this, fit-to-viewport (z \u2248 0.6-0.9) renders 1.6px strokes as\n sub-pixel and they visually disappear. */\n vector-effect: non-scaling-stroke;\n transition: stroke 260ms, opacity 260ms;\n}\n.ft-edge--off { stroke: var(--ft-line); stroke-width: 1; opacity: 0.55; }\n.ft-edge--on {\n stroke: var(--ft-tier-orange);\n /* Verbatim restore of the reference design (local-docs/styles.css\n line 133-140). The 4px dash + 5px gap reads as continuous\n marching-ant flow \u2014 every frame, dash edges visibly sweep across\n the path because the gap (5px) is shorter than the dash itself.\n This is what gives the focused branches their \"alive\" feel; the\n wider gap variants (1/12 in 1.30.1, 0.1/9 in 1.30.0) read as\n isolated dots and look static at typical fit-zoom levels. */\n stroke-width: 1.6;\n stroke-dasharray: 4 5;\n opacity: 1;\n animation: ftDashFlow 1.4s linear infinite;\n /* Visibility-under-zoom fix from 1.29.1 \u2014 without this, fit-zoom\n transforms (z \u2248 0.4-0.7) render the 1.6px stroke as sub-pixel\n and the line becomes invisible. Keeps stroke-width and\n dasharray in screen pixels regardless of the SVG transform.\n Not in the reference because the reference demo runs at z=1. */\n vector-effect: non-scaling-stroke;\n}\n@keyframes ftDashFlow {\n /* -36 == 4 \u00D7 (4+5) so the pattern advances exactly four cycles\n per period at 1.4s \u2014 perceptibly fast flow without strobing. */\n to { stroke-dashoffset: -36; }\n}\n@media (prefers-reduced-motion: reduce) {\n /* a11y improvement not in the reference \u2014 users who suppress\n motion get a clean solid line instead of a static frozen dash. */\n .ft-edge--on {\n animation: none;\n stroke-dasharray: none;\n }\n}\n\n/* card */\n.ft-card {\n --tier-local: var(--tier, var(--ft-line-strong));\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 0;\n align-items: stretch;\n background: oklch(0.21 0.008 80);\n border: 1px solid var(--ft-line);\n border-left: 3px solid var(--tier-local);\n border-radius: 2px;\n transition:\n background 260ms cubic-bezier(.2,.8,.2,1),\n border-color 260ms, box-shadow 260ms,\n transform 260ms cubic-bezier(.2,.8,.2,1),\n filter 260ms, opacity 260ms, color 260ms;\n overflow: visible;\n animation: ftCardIn 320ms cubic-bezier(.2,.8,.2,1) backwards;\n}\n@keyframes ftCardIn {\n from { opacity: 0; transform: translateX(-6px); }\n to { opacity: 1; transform: translateX(0); }\n}\n.ft-card__main {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 10px;\n align-items: center;\n padding: 0 10px 0 12px;\n background: transparent;\n border: none;\n color: inherit;\n font-family: var(--ft-sans);\n font-size: 13px;\n text-align: left;\n cursor: pointer;\n min-width: 0;\n}\n.ft-card__main:hover { background: oklch(0.23 0.01 80 / 0.6); }\n.ft-card__icon {\n width: 24px;\n height: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--tier-local);\n border: 1px solid var(--ft-line-strong);\n background: oklch(0.17 0.008 80);\n flex-shrink: 0;\n}\n.ft-card__body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }\n.ft-card__label {\n font-family: var(--ft-serif);\n font-size: 15px;\n line-height: 1.15;\n color: var(--ft-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.ft-card__sub {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.02em;\n color: var(--ft-muted-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ft-card__lock { color: var(--ft-muted-fg); display: inline-flex; }\n.ft-card__caret {\n width: 18px;\n height: 18px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--ft-mono);\n font-size: 12px;\n color: var(--ft-muted-fg);\n border: 1px solid var(--ft-line);\n}\n.ft-card--active .ft-card__caret {\n color: var(--tier-local);\n border-color: var(--tier-local);\n}\n\n.ft-card__rail {\n display: flex;\n flex-direction: column;\n border-left: 1px solid var(--ft-line);\n background: oklch(0.19 0.008 80);\n}\n.ft-card__mini {\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n min-height: 20px;\n background: transparent;\n border: none;\n color: var(--ft-muted-fg);\n cursor: pointer;\n transition: background 150ms, color 150ms;\n border-bottom: 1px solid var(--ft-line);\n padding: 0;\n text-decoration: none;\n}\n.ft-card__mini:last-child { border-bottom: none; }\n.ft-card__mini:hover { background: oklch(0.23 0.01 80); color: var(--tier-local); }\n.ft-card__mini.is-on { color: var(--tier-local); background: oklch(0.23 0.01 80); }\n\n/* states */\n.ft-card--active {\n background: oklch(0.25 0.015 60);\n box-shadow: 0 0 0 1px var(--tier-local), 0 14px 34px -16px oklch(0.5 0.18 50 / 0.55);\n transform: scale(1.02);\n}\n.ft-card--dim {\n filter: grayscale(0.85) brightness(0.82);\n opacity: 0.7;\n transform: scale(0.88);\n transform-origin: center left;\n}\n.ft-card--dim .ft-card__label { color: var(--ft-dim-fg); }\n.ft-card--dim:hover {\n filter: grayscale(0.3) brightness(0.96);\n opacity: 1;\n transform: scale(0.94);\n}\n.ft-card--cta {\n border-left-color: var(--ft-tier-orange);\n background: linear-gradient(180deg, oklch(0.23 0.02 55) 0%, oklch(0.2 0.015 60) 100%);\n}\n.ft-card--root {\n border-left-color: var(--ft-tier-orange);\n background: oklch(0.26 0.015 55);\n}\n.ft-card--root .ft-card__icon {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n}\n\n/* detail pop */\n.ft-detail {\n position: relative;\n margin-top: 0;\n padding: 16px 18px 14px;\n background: oklch(0.16 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 2px solid var(--ft-tier-orange);\n border-radius: 3px;\n animation: ftDetailIn 220ms cubic-bezier(.2,.8,.2,1) backwards;\n color: var(--ft-fg);\n width: 320px;\n box-shadow: 0 18px 40px -8px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);\n}\n.ft-detail__head {\n display: flex;\n align-items: center;\n /* Clear room for the two stacked rail buttons in the top-right\n (expand + close). Bumped from 28px \u2192 58px to fit both. */\n margin: -2px 58px 10px 0;\n}\n.ft-detail__title {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n}\n.ft-detail__close,\n.ft-detail__expand {\n position: absolute;\n top: 8px;\n width: 22px;\n height: 22px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line);\n border-radius: 2px;\n color: var(--ft-muted-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail__close { right: 8px; }\n/* Expand button sits immediately to the left of close. */\n.ft-detail__expand { right: 36px; }\n.ft-detail__close:hover,\n.ft-detail__expand:hover {\n color: var(--ft-fg);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n@keyframes ftDetailIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail__lead {\n /* Inter (sans) at slightly larger size + medium weight reads\n dramatically better than serif italic on dark stages, while still\n looking distinct from the body paragraph. The orange accent\n border on the left preserves the \"pull-quote\" rhythm. */\n font-family: var(--ft-sans);\n font-size: 14.5px;\n line-height: 1.45;\n font-style: normal;\n font-weight: 500;\n color: var(--ft-fg);\n padding-left: 10px;\n border-left: 2px solid var(--ft-tier-orange);\n margin-bottom: 12px;\n}\n.ft-detail__body {\n margin: 0 0 10px;\n font-size: 12px;\n color: var(--ft-muted-fg);\n line-height: 1.5;\n}\n.ft-detail__bullets {\n margin: 0 0 10px;\n padding-left: 16px;\n font-size: 12px;\n color: var(--ft-fg);\n}\n.ft-detail__bullets li { margin-bottom: 3px; }\n.ft-detail__meta {\n margin: 0 0 10px;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.ft-detail__meta-row {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n font-size: 11px;\n}\n.ft-detail__meta-row dt {\n font-family: var(--ft-mono);\n font-size: 9.5px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--ft-muted-fg);\n padding-top: 1px;\n}\n.ft-detail__meta-row dd { margin: 0; color: var(--ft-fg); }\n.ft-detail__actions {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 8px;\n}\n\n.ft-btn {\n font-family: var(--ft-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n padding: 7px 10px;\n background: transparent;\n color: var(--ft-fg);\n border: 1px solid var(--ft-line-strong);\n text-decoration: none;\n cursor: pointer;\n display: inline-flex;\n gap: 5px;\n align-items: center;\n transition: border-color 150ms, background 150ms;\n}\n.ft-btn:hover { border-color: var(--ft-tier-orange); }\n.ft-btn--primary {\n background: var(--ft-tier-orange);\n color: oklch(0.18 0.01 50);\n border-color: var(--ft-tier-orange);\n}\n\n@media (max-width: 760px) {\n .ft-header { padding: 16px 18px 14px; }\n .ft-header__title { font-size: 32px; }\n .ft-controls__hint { display: none; }\n .ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }\n /* Larger touch targets on phones \u2014 bumps zoom controls from 26px\n to 36px to comfortably clear the 44px Apple HIG / Material 48dp\n minimum once you account for visual padding. */\n .ft-controls { padding: 6px 8px; }\n .ft-controls button { width: 36px; height: 36px; font-size: 16px; }\n .ft-controls__reset { padding: 0 14px !important; height: 36px; }\n /* Inline detail popup buttons are intentionally compact in the\n inline footprint \u2014 but on touch the user can promote to the\n fullscreen overlay which has 28px buttons by default. */\n .ft-detail__close, .ft-detail__expand { width: 28px; height: 28px; }\n .ft-detail__expand { right: 42px; }\n .ft-detail__head { margin-right: 70px; }\n}\n@media (max-width: 480px) {\n /* Phone-sized: shrink card width so 5+ depth levels still fit\n reasonably without forcing the user to scroll horizontally to\n read the labels. Cards still ellipsis-truncate when needed. */\n .ft-card { font-size: 12px; }\n .ft-card__label { font-size: 14px; }\n .ft-card__sub { font-size: 9px; }\n .ft-controls__hint { display: none; }\n}\n\n/* \u2500\u2500\u2500 Fullscreen detail overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Triggered by the expand button in any inline DetailPop. Renders\n inside .ft-stage as an absolutely-positioned scrim + scrollable\n panel. The inline pop stays mounted (cheaper React tree) but the\n user's focus and the pointer-events both transfer to the overlay\n while it's visible.\n*/\n.ft-detail-overlay {\n position: absolute;\n inset: 0;\n z-index: 20;\n display: flex;\n align-items: stretch;\n justify-content: center;\n /* Translucent backdrop blurs the flow tree behind so the focused\n content reads cleanly without losing the user's \"where am I\"\n spatial anchor. */\n background: oklch(0.12 0.008 80 / 0.78);\n backdrop-filter: blur(6px);\n animation: ftOverlayIn 200ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n.ft-detail-overlay__panel {\n position: relative;\n margin: 36px;\n flex: 1 1 auto;\n max-width: 760px;\n background: oklch(0.18 0.008 80);\n border: 1px solid var(--ft-line-strong);\n border-left: 3px solid var(--ft-tier-orange);\n border-radius: 4px;\n overflow: auto;\n padding: 26px 32px 28px;\n box-shadow: 0 30px 80px -10px oklch(0 0 0 / 0.7);\n animation: ftOverlayPanelIn 240ms cubic-bezier(.2,.8,.2,1);\n}\n@keyframes ftOverlayPanelIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.ft-detail-overlay__panel .ft-detail__title {\n font-size: 11px;\n}\n.ft-detail-overlay__panel .ft-detail__lead {\n font-size: 18px;\n line-height: 1.5;\n padding-left: 14px;\n}\n.ft-detail-overlay__panel .ft-detail__body {\n font-size: 14px;\n color: var(--ft-fg);\n line-height: 1.65;\n}\n.ft-detail-overlay__panel .ft-detail__bullets {\n font-size: 14px;\n line-height: 1.55;\n padding-left: 20px;\n}\n.ft-detail-overlay__panel .ft-detail__bullets li { margin-bottom: 6px; }\n.ft-detail-overlay__panel .ft-detail__meta-row {\n font-size: 13px;\n grid-template-columns: 140px 1fr;\n}\n.ft-detail-overlay__panel .ft-detail__meta-row dt { font-size: 10.5px; }\n.ft-detail-overlay__panel .ft-btn { font-size: 11px; padding: 9px 14px; }\n.ft-detail-overlay__head {\n display: flex;\n align-items: center;\n margin: 0 0 14px;\n padding-right: 70px;\n}\n.ft-detail-overlay__title {\n font-family: var(--ft-mono);\n font-size: 11px;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: var(--ft-tier-orange);\n}\n.ft-detail-overlay__close,\n.ft-detail-overlay__collapse {\n position: absolute;\n top: 16px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--ft-line-strong);\n border-radius: 2px;\n color: var(--ft-fg);\n cursor: pointer;\n padding: 0;\n transition: color 120ms, border-color 120ms, background 120ms;\n}\n.ft-detail-overlay__close { right: 16px; }\n.ft-detail-overlay__collapse { right: 52px; }\n.ft-detail-overlay__close:hover,\n.ft-detail-overlay__collapse:hover {\n color: var(--ft-tier-orange);\n border-color: var(--ft-tier-orange);\n background: oklch(0.22 0.008 80);\n}\n\n@media (max-width: 760px) {\n .ft-detail-overlay__panel {\n margin: 12px;\n padding: 18px 18px 22px;\n }\n .ft-detail-overlay__panel .ft-detail__lead { font-size: 16px; }\n}\n";
|
|
5297
5297
|
|
|
5298
5298
|
type GameTimerMode = "countdown" | "countup";
|
|
5299
5299
|
type GameTimerThemePreset = "green" | "red" | "blue" | "amber" | "purple" | "pink" | "teal";
|
package/dist/index.js
CHANGED
|
@@ -35500,15 +35500,7 @@ var PATHS = {
|
|
|
35500
35500
|
)
|
|
35501
35501
|
] }),
|
|
35502
35502
|
seedling: /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(import_jsx_runtime119.Fragment, { children: [
|
|
35503
|
-
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
|
|
35504
|
-
"path",
|
|
35505
|
-
{
|
|
35506
|
-
d: "M8 14 v-5",
|
|
35507
|
-
fill: "none",
|
|
35508
|
-
stroke: "currentColor",
|
|
35509
|
-
strokeWidth: "1.2"
|
|
35510
|
-
}
|
|
35511
|
-
),
|
|
35503
|
+
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)("path", { d: "M8 14 v-5", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
35512
35504
|
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
|
|
35513
35505
|
"path",
|
|
35514
35506
|
{
|
|
@@ -35662,24 +35654,8 @@ var PATHS = {
|
|
|
35662
35654
|
strokeWidth: "1.2"
|
|
35663
35655
|
}
|
|
35664
35656
|
),
|
|
35665
|
-
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
|
|
35666
|
-
|
|
35667
|
-
{
|
|
35668
|
-
d: "M9 5 v6",
|
|
35669
|
-
fill: "none",
|
|
35670
|
-
stroke: "currentColor",
|
|
35671
|
-
strokeWidth: "1.2"
|
|
35672
|
-
}
|
|
35673
|
-
),
|
|
35674
|
-
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
|
|
35675
|
-
"path",
|
|
35676
|
-
{
|
|
35677
|
-
d: "M9 8 h4",
|
|
35678
|
-
fill: "none",
|
|
35679
|
-
stroke: "currentColor",
|
|
35680
|
-
strokeWidth: "1.2"
|
|
35681
|
-
}
|
|
35682
|
-
)
|
|
35657
|
+
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)("path", { d: "M9 5 v6", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
35658
|
+
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)("path", { d: "M9 8 h4", fill: "none", stroke: "currentColor", strokeWidth: "1.2" })
|
|
35683
35659
|
] }),
|
|
35684
35660
|
score: /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(import_jsx_runtime119.Fragment, { children: [
|
|
35685
35661
|
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
|
|
@@ -36124,29 +36100,36 @@ var FLOW_TREE_CSS = `
|
|
|
36124
36100
|
.ft-edge--off { stroke: var(--ft-line); stroke-width: 1; opacity: 0.55; }
|
|
36125
36101
|
.ft-edge--on {
|
|
36126
36102
|
stroke: var(--ft-tier-orange);
|
|
36127
|
-
|
|
36128
|
-
|
|
36129
|
-
|
|
36130
|
-
|
|
36131
|
-
|
|
36132
|
-
|
|
36103
|
+
/* Verbatim restore of the reference design (local-docs/styles.css
|
|
36104
|
+
line 133-140). The 4px dash + 5px gap reads as continuous
|
|
36105
|
+
marching-ant flow \u2014 every frame, dash edges visibly sweep across
|
|
36106
|
+
the path because the gap (5px) is shorter than the dash itself.
|
|
36107
|
+
This is what gives the focused branches their "alive" feel; the
|
|
36108
|
+
wider gap variants (1/12 in 1.30.1, 0.1/9 in 1.30.0) read as
|
|
36109
|
+
isolated dots and look static at typical fit-zoom levels. */
|
|
36110
|
+
stroke-width: 1.6;
|
|
36111
|
+
stroke-dasharray: 4 5;
|
|
36133
36112
|
opacity: 1;
|
|
36134
|
-
|
|
36135
|
-
|
|
36136
|
-
|
|
36137
|
-
.
|
|
36138
|
-
|
|
36139
|
-
|
|
36140
|
-
|
|
36141
|
-
animation: none;
|
|
36113
|
+
animation: ftDashFlow 1.4s linear infinite;
|
|
36114
|
+
/* Visibility-under-zoom fix from 1.29.1 \u2014 without this, fit-zoom
|
|
36115
|
+
transforms (z \u2248 0.4-0.7) render the 1.6px stroke as sub-pixel
|
|
36116
|
+
and the line becomes invisible. Keeps stroke-width and
|
|
36117
|
+
dasharray in screen pixels regardless of the SVG transform.
|
|
36118
|
+
Not in the reference because the reference demo runs at z=1. */
|
|
36119
|
+
vector-effect: non-scaling-stroke;
|
|
36142
36120
|
}
|
|
36143
36121
|
@keyframes ftDashFlow {
|
|
36144
|
-
/* -
|
|
36145
|
-
|
|
36146
|
-
to { stroke-dashoffset: -
|
|
36122
|
+
/* -36 == 4 \xD7 (4+5) so the pattern advances exactly four cycles
|
|
36123
|
+
per period at 1.4s \u2014 perceptibly fast flow without strobing. */
|
|
36124
|
+
to { stroke-dashoffset: -36; }
|
|
36147
36125
|
}
|
|
36148
36126
|
@media (prefers-reduced-motion: reduce) {
|
|
36149
|
-
|
|
36127
|
+
/* a11y improvement not in the reference \u2014 users who suppress
|
|
36128
|
+
motion get a clean solid line instead of a static frozen dash. */
|
|
36129
|
+
.ft-edge--on {
|
|
36130
|
+
animation: none;
|
|
36131
|
+
stroke-dasharray: none;
|
|
36132
|
+
}
|
|
36150
36133
|
}
|
|
36151
36134
|
|
|
36152
36135
|
/* card */
|
|
@@ -36440,6 +36423,27 @@ var FLOW_TREE_CSS = `
|
|
|
36440
36423
|
.ft-header__title { font-size: 32px; }
|
|
36441
36424
|
.ft-controls__hint { display: none; }
|
|
36442
36425
|
.ft-legend { max-width: calc(100% - 24px); left: 10px; bottom: 10px; }
|
|
36426
|
+
/* Larger touch targets on phones \u2014 bumps zoom controls from 26px
|
|
36427
|
+
to 36px to comfortably clear the 44px Apple HIG / Material 48dp
|
|
36428
|
+
minimum once you account for visual padding. */
|
|
36429
|
+
.ft-controls { padding: 6px 8px; }
|
|
36430
|
+
.ft-controls button { width: 36px; height: 36px; font-size: 16px; }
|
|
36431
|
+
.ft-controls__reset { padding: 0 14px !important; height: 36px; }
|
|
36432
|
+
/* Inline detail popup buttons are intentionally compact in the
|
|
36433
|
+
inline footprint \u2014 but on touch the user can promote to the
|
|
36434
|
+
fullscreen overlay which has 28px buttons by default. */
|
|
36435
|
+
.ft-detail__close, .ft-detail__expand { width: 28px; height: 28px; }
|
|
36436
|
+
.ft-detail__expand { right: 42px; }
|
|
36437
|
+
.ft-detail__head { margin-right: 70px; }
|
|
36438
|
+
}
|
|
36439
|
+
@media (max-width: 480px) {
|
|
36440
|
+
/* Phone-sized: shrink card width so 5+ depth levels still fit
|
|
36441
|
+
reasonably without forcing the user to scroll horizontally to
|
|
36442
|
+
read the labels. Cards still ellipsis-truncate when needed. */
|
|
36443
|
+
.ft-card { font-size: 12px; }
|
|
36444
|
+
.ft-card__label { font-size: 14px; }
|
|
36445
|
+
.ft-card__sub { font-size: 9px; }
|
|
36446
|
+
.ft-controls__hint { display: none; }
|
|
36443
36447
|
}
|
|
36444
36448
|
|
|
36445
36449
|
/* \u2500\u2500\u2500 Fullscreen detail overlay \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -37241,15 +37245,16 @@ function FlowTree({
|
|
|
37241
37245
|
},
|
|
37242
37246
|
[zoom]
|
|
37243
37247
|
);
|
|
37244
|
-
|
|
37245
|
-
|
|
37248
|
+
function pickInitialBoost(viewW) {
|
|
37249
|
+
if (viewW <= 480) return { boost: 1.15, cap: 1.2 };
|
|
37250
|
+
if (viewW <= 900) return { boost: 1.3, cap: 1.35 };
|
|
37251
|
+
return { boost: 1.5, cap: 1.5 };
|
|
37252
|
+
}
|
|
37246
37253
|
const recenterOnFocused = React87.useCallback(() => {
|
|
37247
37254
|
const fit = computeFitZoom();
|
|
37248
37255
|
if (!fit) return;
|
|
37249
|
-
const
|
|
37250
|
-
|
|
37251
|
-
Math.min(MAX_INITIAL_ZOOM, fit.baseZ * INITIAL_ZOOM_BOOST)
|
|
37252
|
-
);
|
|
37256
|
+
const { boost, cap } = pickInitialBoost(fit.viewW);
|
|
37257
|
+
const z = Math.max(0.5, Math.min(cap, fit.baseZ * boost));
|
|
37253
37258
|
setZoom(z);
|
|
37254
37259
|
if (focusedTailNode) {
|
|
37255
37260
|
setPan({
|
|
@@ -37306,15 +37311,68 @@ function FlowTree({
|
|
|
37306
37311
|
ro.observe(containerRef.current);
|
|
37307
37312
|
return () => ro.disconnect();
|
|
37308
37313
|
}, [recenterOnFocused]);
|
|
37314
|
+
const ZOOM_MIN = 0.35;
|
|
37315
|
+
const ZOOM_MAX = 2.5;
|
|
37316
|
+
const pointersRef = React87.useRef(
|
|
37317
|
+
/* @__PURE__ */ new Map()
|
|
37318
|
+
);
|
|
37319
|
+
const pinchRef = React87.useRef(null);
|
|
37309
37320
|
const onPointerDown = (e) => {
|
|
37310
37321
|
if (e.target.closest("button, a")) return;
|
|
37311
37322
|
userInteractedRef.current = true;
|
|
37312
37323
|
markInteracted();
|
|
37313
|
-
|
|
37324
|
+
pointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
37314
37325
|
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
37315
|
-
|
|
37326
|
+
if (pointersRef.current.size === 2) {
|
|
37327
|
+
const [a, b] = Array.from(pointersRef.current.values());
|
|
37328
|
+
const dist = Math.hypot(a.x - b.x, a.y - b.y) || 1;
|
|
37329
|
+
const host = e.currentTarget.getBoundingClientRect();
|
|
37330
|
+
pinchRef.current = {
|
|
37331
|
+
initialDist: dist,
|
|
37332
|
+
initialZoom: zoom,
|
|
37333
|
+
initialPan: { x: pan.x, y: pan.y },
|
|
37334
|
+
mid: {
|
|
37335
|
+
x: (a.x + b.x) / 2 - host.left,
|
|
37336
|
+
y: (a.y + b.y) / 2 - host.top
|
|
37337
|
+
}
|
|
37338
|
+
};
|
|
37339
|
+
dragRef.current = null;
|
|
37340
|
+
e.currentTarget.classList.remove("is-dragging");
|
|
37341
|
+
} else if (pointersRef.current.size === 1) {
|
|
37342
|
+
dragRef.current = {
|
|
37343
|
+
sx: e.clientX,
|
|
37344
|
+
sy: e.clientY,
|
|
37345
|
+
px: pan.x,
|
|
37346
|
+
py: pan.y
|
|
37347
|
+
};
|
|
37348
|
+
e.currentTarget.classList.add("is-dragging");
|
|
37349
|
+
}
|
|
37316
37350
|
};
|
|
37317
37351
|
const onPointerMove = (e) => {
|
|
37352
|
+
const tracked = pointersRef.current.get(e.pointerId);
|
|
37353
|
+
if (tracked) {
|
|
37354
|
+
tracked.x = e.clientX;
|
|
37355
|
+
tracked.y = e.clientY;
|
|
37356
|
+
}
|
|
37357
|
+
if (pinchRef.current && pointersRef.current.size >= 2) {
|
|
37358
|
+
const [a, b] = Array.from(pointersRef.current.values()).slice(0, 2);
|
|
37359
|
+
const dist = Math.hypot(a.x - b.x, a.y - b.y) || 1;
|
|
37360
|
+
const ratio = dist / pinchRef.current.initialDist;
|
|
37361
|
+
const newZ = Math.max(
|
|
37362
|
+
ZOOM_MIN,
|
|
37363
|
+
Math.min(ZOOM_MAX, pinchRef.current.initialZoom * ratio)
|
|
37364
|
+
);
|
|
37365
|
+
const z = pinchRef.current.initialZoom;
|
|
37366
|
+
const m = pinchRef.current.mid;
|
|
37367
|
+
const p0 = pinchRef.current.initialPan;
|
|
37368
|
+
const ratioZ = newZ / z;
|
|
37369
|
+
setPan({
|
|
37370
|
+
x: m.x - (m.x - p0.x) * ratioZ,
|
|
37371
|
+
y: m.y - (m.y - p0.y) * ratioZ
|
|
37372
|
+
});
|
|
37373
|
+
setZoom(newZ);
|
|
37374
|
+
return;
|
|
37375
|
+
}
|
|
37318
37376
|
if (!dragRef.current) return;
|
|
37319
37377
|
setPan({
|
|
37320
37378
|
x: dragRef.current.px + (e.clientX - dragRef.current.sx),
|
|
@@ -37322,11 +37380,23 @@ function FlowTree({
|
|
|
37322
37380
|
});
|
|
37323
37381
|
};
|
|
37324
37382
|
const onPointerUp = (e) => {
|
|
37325
|
-
|
|
37326
|
-
|
|
37383
|
+
pointersRef.current.delete(e.pointerId);
|
|
37384
|
+
if (pointersRef.current.size < 2) {
|
|
37385
|
+
pinchRef.current = null;
|
|
37386
|
+
}
|
|
37387
|
+
if (pointersRef.current.size === 0) {
|
|
37388
|
+
dragRef.current = null;
|
|
37389
|
+
e.currentTarget.classList.remove("is-dragging");
|
|
37390
|
+
} else if (pointersRef.current.size === 1) {
|
|
37391
|
+
const [remaining] = Array.from(pointersRef.current.values());
|
|
37392
|
+
dragRef.current = {
|
|
37393
|
+
sx: remaining.x,
|
|
37394
|
+
sy: remaining.y,
|
|
37395
|
+
px: pan.x,
|
|
37396
|
+
py: pan.y
|
|
37397
|
+
};
|
|
37398
|
+
}
|
|
37327
37399
|
};
|
|
37328
|
-
const ZOOM_MIN = 0.35;
|
|
37329
|
-
const ZOOM_MAX = 2.5;
|
|
37330
37400
|
const onWheel = (e) => {
|
|
37331
37401
|
if (!e.ctrlKey && !e.metaKey) return;
|
|
37332
37402
|
e.preventDefault();
|
|
@@ -37450,7 +37520,7 @@ function FlowTree({
|
|
|
37450
37520
|
children: "Fit"
|
|
37451
37521
|
}
|
|
37452
37522
|
),
|
|
37453
|
-
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-controls__hint", children: "drag
|
|
37523
|
+
/* @__PURE__ */ (0, import_jsx_runtime120.jsx)("span", { className: "ft-controls__hint", children: "drag \xB7 double-click \xB7 pinch \xB7 \u2318/ctrl + scroll" })
|
|
37454
37524
|
] }) : null,
|
|
37455
37525
|
/* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(
|
|
37456
37526
|
"svg",
|