natureplot 0.7.0__py3-none-any.whl

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.
natureplot/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """NaturePlot: data-driven SVG charts, rendered interactively in a browser."""
2
+ from ._version import __version__
3
+ from .chart import Chart, chart_types
4
+
5
+ __all__ = ["Chart", "chart_types", "__version__"]
@@ -0,0 +1,450 @@
1
+ {
2
+ "version": "0.7.0",
3
+ "bundle_sha256": "7c325ac04012b034dbe9995361752b33eed1150d80b9bd3030ede782ce727cdb",
4
+ "charts": {
5
+ "rainbow": {
6
+ "name": "Rainbow",
7
+ "subtitle": "A spectrum of small wins",
8
+ "description": "Give seven weeks of daily activity a little color. Every arc is a week; every segment is a day.",
9
+ "encoding": "7 arcs × 7 days. Color intensity represents the value; pale gray means no data.",
10
+ "category": "Calendar"
11
+ },
12
+ "garden": {
13
+ "name": "Garden",
14
+ "subtitle": "Good habits take root",
15
+ "description": "Watch a month of effort grow into a garden. Three plants, thirty leaves, a story in every day.",
16
+ "encoding": "Each plant holds 10 consecutive dates. Leaf intensity represents value. A 31st day adds a sprout.",
17
+ "category": "Calendar"
18
+ },
19
+ "forest": {
20
+ "name": "Forest",
21
+ "subtitle": "See how your ideas grow",
22
+ "description": "Turn comparisons into a little woodland. A taller tree represents a larger value.",
23
+ "encoding": "Tree height represents value on a shared linear scale starting at zero.",
24
+ "category": "Comparison"
25
+ },
26
+ "river": {
27
+ "name": "River",
28
+ "subtitle": "Find the flow in your data",
29
+ "description": "Follow the gentle ebb and flow of your everyday rhythms, one observation at a time.",
30
+ "encoding": "Vertical position represents value. Curves pass through observations without overshoot; gaps remain gaps.",
31
+ "category": "Trend"
32
+ },
33
+ "bloom": {
34
+ "name": "Bloom",
35
+ "subtitle": "A fuller picture, petal by petal",
36
+ "description": "Give every dimension room to blossom. A simple flower reveals the shape of your strengths.",
37
+ "encoding": "Petal length beyond the center represents value. Petal area is decorative, not a part-to-whole measure.",
38
+ "category": "Comparison"
39
+ },
40
+ "mountain": {
41
+ "name": "Mountain",
42
+ "subtitle": "Make every milestone visible",
43
+ "description": "Explore the peaks and quieter stretches of a journey, from daily activity to weekly demand.",
44
+ "encoding": "Each vertex is an observation on a linear scale. Straight slopes connect adjacent values.",
45
+ "category": "Trend"
46
+ },
47
+ "tide": {
48
+ "name": "Tide",
49
+ "subtitle": "Little by little, the tide rises",
50
+ "description": "A single goal, a rising tide. Make progress feel calm, tangible, and within reach.",
51
+ "encoding": "Water height represents the fraction of a target. Text preserves progress beyond 100%.",
52
+ "category": "Progress"
53
+ },
54
+ "rings": {
55
+ "name": "Rings",
56
+ "subtitle": "Every season leaves a mark",
57
+ "description": "Layer your goals like the rings of a tree. Every circle has its own pace and possibility.",
58
+ "encoding": "Arc angle represents percent of each target. Compare angles or percentages, not raw arc lengths.",
59
+ "category": "Progress"
60
+ },
61
+ "seed-ledger": {
62
+ "name": "Seed Ledger",
63
+ "subtitle": "Small things, counted carefully",
64
+ "description": "Count everyday wins as seeds, with an explicit unit and room for fractions.",
65
+ "encoding": "One seed represents a labeled denomination. Filled seed area represents a fractional unit; rows preserve exact totals.",
66
+ "category": "Comparison"
67
+ },
68
+ "waterline": {
69
+ "name": "Waterline",
70
+ "subtitle": "Know when you reach the mark",
71
+ "description": "Read capacity and meaningful thresholds on a calibrated water column.",
72
+ "encoding": "Water height represents value on a linear zero-to-capacity scale. Dashed lines mark supplied thresholds. Fill caps at capacity; exact values remain.",
73
+ "category": "Progress"
74
+ },
75
+ "sundial": {
76
+ "name": "Sundial",
77
+ "subtitle": "Give your day a little sunlight",
78
+ "description": "Arrange daily activities on a clock-time dial, with separate tracks for overlapping plans.",
79
+ "encoding": "Angle represents 24-hour clock time. Arc extent represents duration. This is a schedule dial, not a physical sundial or solar calculation.",
80
+ "category": "Timeline"
81
+ },
82
+ "season-wheel": {
83
+ "name": "Season Wheel",
84
+ "subtitle": "A whole year, in one glance",
85
+ "description": "See real date intervals unfold around a Gregorian year, one season of work at a time.",
86
+ "encoding": "Angle represents date within the selected year; arc extent represents duration. Month lengths and leap days are respected. No astronomical solar terms are calculated.",
87
+ "category": "Calendar"
88
+ },
89
+ "phenology": {
90
+ "name": "Phenology",
91
+ "subtitle": "Readiness has its own rhythm",
92
+ "description": "Follow observed milestones alongside expected windows, leaving unknown dates open.",
93
+ "encoding": "Horizontal position represents date. Sprouts mark observed dates; shaded spans show expected windows; empty circles mean not observed.",
94
+ "category": "Timeline"
95
+ },
96
+ "lunar-cycle": {
97
+ "name": "Lunar Cycle",
98
+ "subtitle": "Make room for a recurring rhythm",
99
+ "description": "Arrange your own recurring routine around an abstract cycle with illustrative moons.",
100
+ "encoding": "Angle represents cycle position; outer stem length represents value. Moon symbols are illustrative and do not encode completion or calculate lunar phase.",
101
+ "category": "Cycle"
102
+ },
103
+ "water-clock": {
104
+ "name": "Water Clock",
105
+ "subtitle": "Let time take a gentler shape",
106
+ "description": "Read elapsed or remaining duration through a calibrated vessel of water.",
107
+ "encoding": "Water height represents remaining duration in a constant-area vessel. Input convention is explicitly elapsed or remaining. This chart is a snapshot, not a timer.",
108
+ "category": "Progress"
109
+ },
110
+ "balance": {
111
+ "name": "Balance",
112
+ "subtitle": "See both sides of the story",
113
+ "description": "Compare two quantities with common-scale bars and a softly tilting balance.",
114
+ "encoding": "Bar lengths use one linear scale. Beam tilt indicates the direction of the difference only; stones are decorative. Difference is right minus left.",
115
+ "category": "Comparison"
116
+ },
117
+ "cord-ledger": {
118
+ "name": "Cord Ledger",
119
+ "subtitle": "Every contribution has a place",
120
+ "description": "Count contributions on grouped cords, with a clear denomination for each knot.",
121
+ "encoding": "One knot represents a labeled denomination. Partial knot area represents a fraction. Parent tracks group contiguous cords. This is not a historical khipu decoder.",
122
+ "category": "Comparison"
123
+ },
124
+ "growth-history": {
125
+ "name": "Growth History",
126
+ "subtitle": "Let every chapter leave a trace",
127
+ "description": "Build a record of contributions as successive growth layers with a linked comparison view.",
128
+ "encoding": "Layer order follows input chronology. Ring thickness or area, as configured, represents period contribution; linked bars use a shared linear scale. Unknown periods have no quantitative layer.",
129
+ "category": "Trend"
130
+ },
131
+ "tidal-rhythm": {
132
+ "name": "Tidal Rhythm",
133
+ "subtitle": "Find what returns, and what changes",
134
+ "description": "Compare repeating patterns as aligned cycles with a shared scale and honest gaps.",
135
+ "encoding": "Horizontal position represents position within each supplied cycle. Vertical position uses a shared numeric scale across cycles. Lines connect observations; this does not predict tides.",
136
+ "category": "Trend"
137
+ },
138
+ "star-cycle": {
139
+ "name": "Star Cycle",
140
+ "subtitle": "A constellation of things to return to",
141
+ "description": "Give recurring checkpoints a place in a clearly labeled cycle.",
142
+ "encoding": "Angular position represents cycle position. Star intensity represents value; exact values and cycle positions remain available. The arrangement is an abstract checkpoint map.",
143
+ "category": "Cycle"
144
+ },
145
+ "honeycomb": {
146
+ "name": "Honeycomb",
147
+ "category": "Comparison",
148
+ "subtitle": "A place for every reading",
149
+ "description": "Compare team activity across categories in a modular hexagonal heatmap.",
150
+ "encoding": "Equal cells; intensity represents value. Rows are tracks; columns are labels.",
151
+ "rule": "Up to 6 tracks × 6 labels, one observation per pair. Nonnegative values or null; absent cells are unmeasured.",
152
+ "inspiration": "The modular hexagonal cells of bee and wasp nests."
153
+ },
154
+ "mycelium": {
155
+ "name": "Mycelium",
156
+ "category": "Relationship",
157
+ "subtitle": "Connections beneath the surface",
158
+ "description": "Inspect dependencies, messages, or exchanges in a directed weighted network.",
159
+ "encoding": "Curved links connect source to destination; line width encodes weight. Nodes are identifiers, not quantities; long dashes mean zero and short dashes mean unknown weight.",
160
+ "rule": "Up to 18 directed links and 10 nodes. source and destination are required, distinct, and unique as a pair. Nonnegative values or null.",
161
+ "inspiration": "Adaptive biological networks, including fungal and slime-mold networks; no optimization is computed."
162
+ },
163
+ "root-tree": {
164
+ "name": "Root Tree",
165
+ "category": "Relationship",
166
+ "subtitle": "Trace it back to the root",
167
+ "description": "Explore ownership, taxonomies, and parent-child dependencies.",
168
+ "encoding": "Depth follows parent links; node area represents its own value, never an inferred subtree total.",
169
+ "rule": "Up to 16 uniquely identified nodes, 4 depth levels, and at most 5 nodes per level. id is required; parent must exist; cycles are rejected. Nonnegative values or null.",
170
+ "inspiration": "Branching root systems and plant transport networks."
171
+ },
172
+ "canopy": {
173
+ "name": "Canopy",
174
+ "category": "Composition",
175
+ "subtitle": "See the whole forest",
176
+ "description": "Understand how departments and their projects contribute to one total.",
177
+ "encoding": "Nested rectangles allocate area first to tracks, then to observations within each track.",
178
+ "rule": "Up to 12 observations in at most 4 tracks. All quantities must be known and nonnegative. Zero-area contributions remain in the inspector.",
179
+ "inspiration": "A canopy partitioned into neighboring crowns; rectangular areas are a modern abstraction."
180
+ },
181
+ "fern": {
182
+ "name": "Fern",
183
+ "category": "Comparison",
184
+ "subtitle": "Find the few that matter",
185
+ "description": "Prioritize issue categories or costs using a Pareto view.",
186
+ "encoding": "Descending frond lengths show values; spores on a second axis show cumulative share.",
187
+ "rule": "Up to 8 known nonnegative quantities, sorted descending for display. Input indexes remain stable for callbacks.",
188
+ "inspiration": "A fern frond arranges repeated leaflets along a central stem."
189
+ },
190
+ "phyllotaxis": {
191
+ "name": "Phyllotaxis",
192
+ "category": "Composition",
193
+ "subtitle": "Many parts, one seed head",
194
+ "description": "Explain a portfolio, time allocation, or resource mix as one whole.",
195
+ "encoding": "120 equal dots follow a golden-angle spiral. Largest-remainder allocation approximates each category share.",
196
+ "rule": "Up to 8 known nonnegative parts. Dots are rounded shares, not literal counts; exact totals remain in the table. All-zero data draws no allocated dots.",
197
+ "inspiration": "Repeated seed placement in plant heads; this is a mathematical packing metaphor, not a claim about every species."
198
+ },
199
+ "leaf-veins": {
200
+ "name": "Leaf Veins",
201
+ "category": "Comparison",
202
+ "subtitle": "Follow the change",
203
+ "description": "Compare before and after for several teams or measures.",
204
+ "encoding": "Paired endpoints share one linear axis: baseline is the old reading and value is the new reading.",
205
+ "rule": "Up to 6 observations. Every baseline must be finite. Signed readings and null new values are supported.",
206
+ "inspiration": "Veins connect two sides of a leaf; here they connect paired observations."
207
+ },
208
+ "lotus": {
209
+ "name": "Lotus",
210
+ "category": "Progress",
211
+ "subtitle": "A profile with room to bloom",
212
+ "description": "Compare performance across metrics with different target scales.",
213
+ "encoding": "Each axis is value / target, capped at 100%; a dashed ring is the goal. Polygon shape depends on axis order.",
214
+ "rule": "3–8 nonnegative readings. Each positive target falls back to chart target or 100. Null readings break the polygon. Over-goal values stay exact.",
215
+ "inspiration": "Radial arrangement of lotus petals; unlike Bloom, each axis has its own target."
216
+ },
217
+ "petal-box": {
218
+ "name": "Petal Box",
219
+ "category": "Distribution",
220
+ "subtitle": "Let the spread unfold",
221
+ "description": "Compare variability and medians across groups using supplied quartiles.",
222
+ "encoding": "Whiskers span low–high; the petal body spans q1–q3; its line is the median (value).",
223
+ "rule": "Up to 6 known summaries satisfying low ≤ q1 ≤ value ≤ q3 ≤ high. These are supplied extrema, not automatically computed outliers.",
224
+ "inspiration": "A seed pod opening around its center inspires the distribution silhouette."
225
+ },
226
+ "raincloud": {
227
+ "name": "Raincloud",
228
+ "category": "Distribution",
229
+ "subtitle": "See where readings gather",
230
+ "description": "Show a histogram from supplied, possibly unequal-width bins.",
231
+ "encoding": "Horizontal width is the bin interval. Height is frequency / bin width, so area represents frequency.",
232
+ "rule": "Up to 12 ordered, non-overlapping bins with low < high and known nonnegative frequencies. Gaps between bins remain gaps.",
233
+ "inspiration": "Rainfall falling from a cloud; frequency bars form the visible cloud profile."
234
+ },
235
+ "dew": {
236
+ "name": "Dew",
237
+ "category": "Relationship",
238
+ "subtitle": "A clearer view of relationships",
239
+ "description": "Compare two numeric measurements and an optional third size variable.",
240
+ "encoding": "x is horizontal; value is vertical; optional weight controls droplet area. Hollow markers mean zero weight; dashed markers mean missing values.",
241
+ "rule": "Up to 40 readings with finite x. Signed values and null are supported. weight defaults to 1 and must be nonnegative. Missing readings sit in a separate rail.",
242
+ "inspiration": "Dew droplets provide small, distinct lenses onto individual observations."
243
+ },
244
+ "wind-rose": {
245
+ "name": "Wind Rose",
246
+ "category": "Spatial",
247
+ "subtitle": "Know which way it leans",
248
+ "description": "Compare directional observations such as wind or arrival frequencies.",
249
+ "encoding": "Equally spaced compass sectors use area proportional to value, with north at the top and clockwise angles.",
250
+ "rule": "4, 8, or 16 observations. angle must equal index × 360 / count. Nonnegative values or null. Values need not sum to 100.",
251
+ "inspiration": "Compass directions and observed winds; no weather predictions are generated."
252
+ },
253
+ "dune": {
254
+ "name": "Dune",
255
+ "category": "Distribution",
256
+ "subtitle": "Distributions across the landscape",
257
+ "description": "Compare supplied frequency profiles across cohorts.",
258
+ "encoding": "Each track has a ridge; position determines horizontal distance and height uses a common value scale.",
259
+ "rule": "Up to 4 tracks with 2–16 strictly increasing positions each. Nonnegative values or null; null breaks a ridge. Heights are supplied frequencies, not fitted densities.",
260
+ "inspiration": "Wind-shaped ridges in dune fields inspire the aligned silhouettes."
261
+ },
262
+ "glacier": {
263
+ "name": "Glacier",
264
+ "category": "Trend",
265
+ "subtitle": "Every change leaves a step",
266
+ "description": "Explain how gains and losses build a final balance.",
267
+ "encoding": "Floating ice steps show signed contributions; their endpoints track the running sum from zero.",
268
+ "rule": "Up to 10 known signed changes. Unknown changes are rejected because later cumulative totals would be unknowable.",
269
+ "inspiration": "A stepped glacier landscape becomes a signed waterfall; it is not a glacier mass model."
270
+ },
271
+ "sediment": {
272
+ "name": "Sediment",
273
+ "category": "Composition",
274
+ "subtitle": "Read the layers over time",
275
+ "description": "Explore how a changing total is composed of several contributions.",
276
+ "encoding": "Layer thickness is value at each position; stacked boundaries show the total on one linear scale.",
277
+ "rule": "Up to 4 tracks sharing 2–16 identical increasing positions. All values must be known and nonnegative. Linear interpolation is visual, not extra observations.",
278
+ "inspiration": "Deposited layers preserve sequences; chart thickness represents supplied contributions."
279
+ },
280
+ "delta": {
281
+ "name": "Delta",
282
+ "category": "Composition",
283
+ "subtitle": "One source, many paths",
284
+ "description": "Show how one budget or supply is allocated to destinations.",
285
+ "encoding": "One incoming ribbon splits into outgoing ribbons. Each width is proportional to its quantity.",
286
+ "rule": "Up to 7 known nonnegative links sharing one source and distinct destinations. Zero links remain inspectable.",
287
+ "inspiration": "River deltas branch a main channel into distributaries."
288
+ },
289
+ "estuary": {
290
+ "name": "Estuary",
291
+ "category": "Relationship",
292
+ "subtitle": "Meet where the currents join",
293
+ "description": "Track transfers between several origins and destinations.",
294
+ "encoding": "Bipartite ribbons have conserved widths; nodes show sums of their incident links.",
295
+ "rule": "Up to 16 known nonnegative links with at most 5 sources and 5 destinations. Directed pairs must be unique. Left and right node sets are separate.",
296
+ "inspiration": "River and sea exchanges inspire a modern many-to-many flow diagram."
297
+ },
298
+ "pitcher": {
299
+ "name": "Pitcher",
300
+ "category": "Progress",
301
+ "subtitle": "Where does the journey narrow?",
302
+ "description": "Locate attrition through a signup, sales, or completion funnel.",
303
+ "encoding": "Successive widths show remaining count relative to the first stage; labels show retention from the preceding stage.",
304
+ "rule": "2–7 known nonnegative counts in non-increasing order. All-zero data has no measurable conversion percentage.",
305
+ "inspiration": "A pitcher plant’s narrowing chamber inspires the funnel silhouette."
306
+ },
307
+ "firefly": {
308
+ "name": "Firefly",
309
+ "category": "Timeline",
310
+ "subtitle": "See the moments light up",
311
+ "description": "Spot bursts, pauses, and coincidences in event logs.",
312
+ "encoding": "Horizontal position is event time; tracks separate event streams; glow intensity is magnitude.",
313
+ "rule": "Up to 60 events in at most 5 tracks. position is required and may be irregular. Nonnegative values or null. Coincident events remain separate in the inspector.",
314
+ "inspiration": "Brief firefly signals inspire discrete events, not an interpolated trend."
315
+ },
316
+ "migration": {
317
+ "name": "Migration",
318
+ "category": "Spatial",
319
+ "subtitle": "A journey, stop by stop",
320
+ "description": "Show an ordered route through supplied coordinates.",
321
+ "encoding": "x and y locate each stop; arrows preserve input order; stop area represents value.",
322
+ "rule": "2–20 known nonnegative readings, each with finite x and y. Coordinates are planar in the same unit, with equal scale on both axes; not projected latitude/longitude.",
323
+ "inspiration": "Animal migration inspires an ordered path; the chart does not infer a real route."
324
+ },
325
+ "murmuration": {
326
+ "name": "Murmuration",
327
+ "category": "Distribution",
328
+ "subtitle": "Keep every individual in view",
329
+ "description": "Compare raw distributions across cohorts without binning away observations.",
330
+ "encoding": "Horizontal position represents value; vertical offsets separate nearby birds within each track and have no numeric meaning.",
331
+ "rule": "Up to 60 signed readings in at most 4 tracks. Null values use a separate rail. Crowded points may overlap; use the inspector for every record.",
332
+ "inspiration": "A flock remains many individual birds; each mark preserves one observation."
333
+ },
334
+ "coral-range": {
335
+ "name": "Coral Range",
336
+ "category": "Comparison",
337
+ "subtitle": "Make uncertainty visible",
338
+ "description": "Compare estimates with supplied lower and upper bounds.",
339
+ "encoding": "Branch endpoints mark low and high; the central polyp marks value on a common numeric scale.",
340
+ "rule": "Up to 6 observations with finite low ≤ value ≤ high. A null center is allowed within a known range. These bounds are not assumed to be confidence intervals.",
341
+ "inspiration": "Branching coral provides a range silhouette; bounds remain application-defined."
342
+ },
343
+ "pebble": {
344
+ "name": "Pebble",
345
+ "category": "Distribution",
346
+ "subtitle": "Find your place in the stream",
347
+ "description": "Read percentiles from a sample without choosing histogram bins.",
348
+ "encoding": "A right-continuous step shows the empirical cumulative distribution; ties share the fraction at or below that value.",
349
+ "rule": "Up to 60 known signed samples. All observations are retained, including ties. This is an empirical sample, not a fitted probability model.",
350
+ "inspiration": "Pebbles accumulate downstream; each reading contributes equally to cumulative probability."
351
+ },
352
+ "nautilus": {
353
+ "name": "Nautilus",
354
+ "category": "Cycle",
355
+ "subtitle": "Remember more than one turn",
356
+ "description": "Compare recurring work across several consecutive cycles.",
357
+ "encoding": "Angle represents position modulo cycleLength; spiral turn retains cycle number; dot area shows value.",
358
+ "rule": "Up to 36 nonnegative readings, strictly increasing positions from 0 to less than 4 × cycleLength. cycleLength defaults to 12.",
359
+ "inspiration": "Successive shell chambers inspire a spiral timeline; no biological growth law is inferred."
360
+ },
361
+ "frost": {
362
+ "name": "Frost",
363
+ "category": "Relationship",
364
+ "subtitle": "Patterns in a crystal lattice",
365
+ "description": "Inspect a supplied correlation matrix with a fixed signed scale.",
366
+ "encoding": "Cells show values from −1 to +1; color distinguishes sign and intensity magnitude. Diagonal cells are one.",
367
+ "rule": "A complete symmetric matrix of 2–6 variables using track for row and label for column. Mirrored values must agree; symmetric null pairs are allowed.",
368
+ "inspiration": "A repeating crystalline lattice suggests symmetry; correlation never implies causation."
369
+ },
370
+ "echo": {
371
+ "name": "Echo",
372
+ "category": "Relationship",
373
+ "subtitle": "Hear the previous reading",
374
+ "description": "Inspect lag-one dependence in an ordered numeric series.",
375
+ "encoding": "Each point pairs the preceding value on x with the current value on y; a diagonal marks equality.",
376
+ "rule": "2–40 signed observations. Nulls break pairs. The first record has no predecessor and is shown in the inspector rail. No significance or causality is inferred.",
377
+ "inspiration": "Returning sounds inspire comparison with what came before; this is not an echolocation model."
378
+ },
379
+ "cairn": {
380
+ "name": "Cairn",
381
+ "category": "Progress",
382
+ "subtitle": "Build toward one shared goal",
383
+ "description": "Track which contributions make up progress toward a common target.",
384
+ "encoding": "Stacked stone heights show contributions on a shared goal scale; the dashed line is the chart-level target, shared by every contributor.",
385
+ "rule": "Up to 8 known nonnegative contributions and a positive chart target (default 100). The scale expands for over-goal totals; exact excess is retained.",
386
+ "inspiration": "A cairn is assembled stone by stone; the stones here record contributions, not dates."
387
+ },
388
+ "daylight": {
389
+ "name": "Daylight",
390
+ "category": "Timeline",
391
+ "subtitle": "Watch the days open and close",
392
+ "description": "Compare supplied sunrise/sunset or opening/closing windows across dates.",
393
+ "encoding": "Each row shares a 24-hour axis; warm bands run from start to exclusive end; labels state duration.",
394
+ "rule": "Up to 7 unique ISO dates with increasing HH:mm start/end in one day. Only end may be 24:00. No location or astronomical calculation is performed.",
395
+ "inspiration": "Historical shadow observations motivated careful attention to changing daylight."
396
+ },
397
+ "isobar": {
398
+ "name": "Isobar",
399
+ "category": "Spatial",
400
+ "subtitle": "Connect equal readings",
401
+ "description": "Explore a measured scalar field such as temperature or pressure.",
402
+ "encoding": "Nodes retain measured values; lines interpolate equal values at 25%, 50%, and 75% of the observed range.",
403
+ "rule": "Complete 2–8 by 2–8 grid of unique finite x/y coordinates and known finite values. Contours use piecewise-linear triangles; they are estimates between nodes.",
404
+ "inspiration": "Isobars connect equal pressure; the same encoding works for other scalar fields."
405
+ },
406
+ "bamboo": {
407
+ "name": "Bamboo",
408
+ "category": "Distribution",
409
+ "subtitle": "The original numbers, intact",
410
+ "description": "Inspect a compact integer distribution without losing exact readings.",
411
+ "encoding": "The stem is tens; each leaf is units. A key explains how to reconstruct the number.",
412
+ "rule": "Up to 60 integer values from 0 to 99, with at most 18 leaves per stem. Values are sorted within stems; repeated digits are distinct observations.",
413
+ "inspiration": "Segmented bamboo stems inspire a modern statistical stem-and-leaf plot, not an ancient invention."
414
+ }
415
+ },
416
+ "themes": [
417
+ "meadow",
418
+ "ocean",
419
+ "autumn",
420
+ "twilight"
421
+ ],
422
+ "options": [
423
+ "title",
424
+ "description",
425
+ "theme",
426
+ "startDate",
427
+ "endDate",
428
+ "days",
429
+ "max",
430
+ "target",
431
+ "unit",
432
+ "xLabel",
433
+ "yLabel",
434
+ "weightLabel",
435
+ "unitsPerMark",
436
+ "thresholds",
437
+ "year",
438
+ "at",
439
+ "cycleLength",
440
+ "cycleUnit",
441
+ "cyclePosition",
442
+ "timeMode",
443
+ "growthMode",
444
+ "detail",
445
+ "artwork",
446
+ "animate",
447
+ "showTable",
448
+ "interactive"
449
+ ]
450
+ }
@@ -0,0 +1,33 @@
1
+ var NaturePlot=(function(ae){"use strict";const Ze={balance:"Choose a pan to inspect its amount. Change the sample to see the beam rebalance.","cord-ledger":"Choose a cord to isolate its knots and read the exact contribution.","star-cycle":"Choose a star to locate its checkpoint within the cycle.",firefly:"Choose a firefly to reveal its event time, track, and intensity.",migration:"Choose a numbered stop to follow the route and inspect its coordinates.",murmuration:"Choose a bird to isolate one observation from the flock.",pebble:"Choose a stone to reveal its exact value and cumulative share.",echo:"Choose a ripple to compare a reading with its predecessor.",isobar:"Choose a survey point to inspect the measured field. Contours interpolate between points.",phenology:"Choose a sprout or an expected window to inspect the observed and planned dates.","seed-ledger":"Choose a row to inspect its total, seed denomination, and fractional remainder.",phyllotaxis:"Choose a seed or a category to isolate its share of the flower.","leaf-veins":"Choose a leaf to compare its before and after values.",bamboo:"Choose a numbered leaf to reconstruct the original score.","petal-box":"Choose a pod to inspect its quartiles, median, and range.",sundial:"Choose an arc or an activity to inspect its start, end, and duration."};function F(e,t,s="leaf"){if(e.options.detail==="essential")return t;const a=`${e.id}-${s}-${t.slice(1)}`;if(!e.svg.querySelector(`[id="${a}"]`)){const n=e.el("defs");if(s==="water"){const o=e.el("radialGradient",{id:a,cx:".3",cy:".25",r:".8"},n);e.el("stop",{offset:"0","stop-color":e.theme.background,"stop-opacity":".9"},o),e.el("stop",{offset:".38","stop-color":t,"stop-opacity":".52"},o),e.el("stop",{offset:"1","stop-color":t,"stop-opacity":".95"},o)}else{const o=e.el("linearGradient",{id:a,x1:"0",y1:"0",x2:"1",y2:"1"},n);e.el("stop",{offset:"0","stop-color":t,"stop-opacity":".92"},o),e.el("stop",{offset:".55","stop-color":t,"stop-opacity":".72"},o),e.el("stop",{offset:"1","stop-color":t,"stop-opacity":".95"},o)}}return`url(#${a})`}function de(e,t,s,a,n,o,r=!1){if(e.el("circle",{cx:s,cy:a,r:Math.max(11,n),fill:"transparent",class:"np-hit-area"},t),r||n===0){e.el("circle",{cx:s,cy:a,r:4,fill:e.theme.background,stroke:e.theme.muted,"stroke-width":1.4,"stroke-dasharray":r?"2 2":"none",class:r?"np-missing-marker":"np-zero-marker"},t),r&&e.el("path",{d:`M${s-2} ${a+2}l4 -4`,stroke:e.theme.muted,"pointer-events":"none"},t);return}e.el("circle",{cx:s,cy:a,r:n,fill:F(e,o,"water"),class:"np-measured-dot"},t),e.el("circle",{cx:s,cy:a,r:Math.max(0,n-.5),fill:"none",stroke:o,"stroke-width":.8,"pointer-events":"none"},t),n>6&&e.el("path",{d:`M${s-n*.55} ${a-n*.12} Q${s-n*.5} ${a-n*.65} ${s-n*.03} ${a-n*.62}`,fill:"none",stroke:e.theme.background,"stroke-width":1.2,"stroke-linecap":"round",opacity:.8,"pointer-events":"none"},t),n>8&&(e.el("ellipse",{cx:s-n*.32,cy:a-n*.48,rx:n*.12,ry:n*.065,transform:`rotate(-36 ${s-n*.32} ${a-n*.48})`,fill:e.theme.background,opacity:.85,"pointer-events":"none"},t),e.el("path",{d:`M${s+n*.56} ${a+n*.05}Q${s+n*.58} ${a+n*.52} ${s+n*.14} ${a+n*.69}`,fill:"none",stroke:o,"stroke-width":.8,opacity:.5,"pointer-events":"none"},t))}function kt(e,t,s,a,n,o){if(e.el("path",{d:`M${s} ${a}H${s+n}`,stroke:o,"stroke-width":1.5,class:"np-frond-measure"},t),!(n<4))for(let r=0;r<11;r++){const l=.06+r*.076,i=s+n*l,d=Math.min(12,n*.15)*Math.sin(Math.PI*(l+.07)),h=Math.min(n*.12,16);for(const p of[-1,1]){const u=Math.min(s+n,i+h),m=a+p*d;e.el("path",{d:`M${i} ${a}Q${i+h*.12} ${a+p*d*.87} ${u} ${m}Q${i+h*.9} ${a+p*d*.23} ${i} ${a}Z`,fill:F(e,o),stroke:o,"stroke-width":.35,class:"np-frond-pinna"},t),d>3&&e.el("path",{d:`M${i+1} ${a}Q${i+h*.55} ${a+p*d*.35} ${u-1} ${m-p}`,stroke:e.theme.background,"stroke-width":.5,fill:"none",opacity:.5,"pointer-events":"none"},t)}}}function Ve(e,t,s,a){const n=e.el("g",{class:"np-ornament","aria-hidden":"true","pointer-events":"none"}),o=Array.from({length:97},(r,l)=>{const i=l*Math.PI/48,d=a+5+1.5*Math.sin(i*13)+.8*Math.cos(i*23);return`${l?"L":"M"}${t+d*Math.cos(i)} ${s+d*Math.sin(i)}`});e.el("path",{d:o.join("")+"Z",fill:"none",stroke:e.theme.muted,"stroke-width":4,opacity:.4},n);for(let r=0;r<48;r++){const l=r*Math.PI/24,i=a+5+1.5*Math.sin(l*13)+.8*Math.cos(l*23);e.el("path",{d:`M${t+(i-1)*Math.cos(l)} ${s+(i-1)*Math.sin(l)}L${t+(i+2)*Math.cos(l+.009)} ${s+(i+2)*Math.sin(l+.009)}`,stroke:e.theme.background,"stroke-width":.7,opacity:.65},n)}}function Mt(e,t,s,a,n,o){const r=o-n;if(r<5)return;const l=e.el("g",{class:"np-ornament","aria-hidden":"true","pointer-events":"none"},t);for(const i of[.25,.55,.8]){const d=n+r*i,h=Math.min(.65,r*.05),p=Array.from({length:97},(u,m)=>{const c=m*Math.PI/48,f=d+h*(Math.sin(c*7)+.35*Math.cos(c*11));return`${m?"L":"M"}${s+f*Math.cos(c)} ${a+f*Math.sin(c)}`}).join("")+"Z";e.el("path",{d:p,fill:"none",stroke:e.theme.ink,"stroke-width":.6,opacity:.2},l)}}function Et(e,t,s,a){const n=e.el("g",{class:"np-ornament","aria-hidden":"true","pointer-events":"none"});e.el("circle",{cx:t,cy:s,r:a,fill:F(e,e.theme.grid,"stone"),stroke:e.theme.muted,"stroke-width":1,opacity:.6},n),e.el("circle",{cx:t,cy:s,r:a-4,fill:"none",stroke:e.theme.muted,"stroke-width":.6,opacity:.35},n);for(let o=0;o<95;o++){const r=o*2.39996323,l=Math.sqrt((o+.5)/95)*(a-7);e.el("path",{d:`M${t+Math.cos(r)*l} ${s+Math.sin(r)*l}l${o%3?.8:1.5} -.5`,stroke:e.theme.muted,"stroke-width":.6,opacity:.16},n)}}function N(e,t,s){const a=r=>{const l=r.slice(1),i=l.length===3?l.split("").map(d=>d+d).join(""):l;return[0,2,4].map(d=>parseInt(i.slice(d,d+2),16))},n=a(e),o=a(t);return"#"+n.map((r,l)=>Math.round(r+(o[l]-r)*s).toString(16).padStart(2,"0")).join("")}function me(e){const t=e.slice(1),s=t.length===3?t.split("").map(n=>n+n).join(""):t,a=[0,2,4].map(n=>parseInt(s.slice(n,n+2),16)/255).map(n=>n<=.04045?n/12.92:((n+.055)/1.055)**2.4);return a[0]*.2126+a[1]*.7152+a[2]*.0722>.179?"#000000":"#ffffff"}function _(e,t,s){if(e.options.detail==="essential")return;const a=`${e.id}-engraving-${s}`;let n=e.svg.querySelector("defs");if(n||(n=e.el("defs")),!e.svg.querySelector(`[id="${a}"]`)){const d=s==="leaf"?32:24,h=e.el("pattern",{id:a,width:d,height:d,patternUnits:"userSpaceOnUse"},n),p={water:"M-6 6Q0 2 6 6T18 6T30 6M-6 18Q0 14 6 18T18 18T30 18",sand:"M-4 5Q7 0 16 5T32 5M-4 12Q7 7 16 12T32 12M-4 20Q7 15 16 20T32 20",rock:"M-2 19L9 5L14 11L24 0M9 5L7 23M14 11L25 16",ice:"M0 0L13 8L24 3M13 8L8 24M13 8L24 22M0 16L10 17",leaf:"M0 32L32 0M8 24Q6 16 0 12M16 16Q13 7 9 0M8 24Q19 25 25 32M16 16Q26 17 32 22",glass:"M5 0V24M8 0V24M20 0V24"};e.el("path",{d:p[s],fill:"none",stroke:e.theme.background,"stroke-width":s==="glass"?1.3:.65,opacity:s==="rock"?.22:.3},h)}const o=`${e.id}-engraving-clip-${n.querySelectorAll("clipPath").length}`,r=e.el("clipPath",{id:o},n),l=t.cloneNode(!1);for(const d of[...l.attributes])["d","points","x","y","width","height","rx","ry","r","cx","cy","transform","fill-rule"].includes(d.name)||l.removeAttribute(d.name);l.setAttribute("fill","#000"),r.append(l);const i=e.el("g",{class:"np-ornament","aria-hidden":"true","pointer-events":"none","clip-path":`url(#${o})`},t.parentElement);e.el("rect",{width:640,height:360,fill:`url(#${a})`},i)}function Be(e,t,s,a,n,o=6){e.el("path",{d:`M${s-o} ${a-2}Q${s-o*.6} ${a-o} ${s} ${a}Q${s+o*.6} ${a-o} ${s+o} ${a-2}M${s} ${a-1}v3`,fill:"none",stroke:n,"stroke-width":1.5,"stroke-linecap":"round","stroke-linejoin":"round"},t)}function Ge(e,t,s,a=1){const n=e.el("g",{transform:`translate(${t} ${s}) scale(${a})`,class:"np-ornament","aria-hidden":"true","pointer-events":"none"});e.el("path",{d:"M-26 6Q-32-5-20-7Q-19-23-5-17Q5-31 17-15Q34-15 32-1Q45 10 27 12H-22Q-31 12-26 6Z",fill:e.theme.grid,opacity:.65},n),e.el("path",{d:"M-19-7Q-9-10-6-4M16-14Q23-8 20-2M-22 5Q0 10 25 5",fill:"none",stroke:e.theme.background,"stroke-width":1.2,opacity:.75},n)}const Ct=["seed","leaf","sprout","bud","bamboo","pod","sunflower","flower","balance-stand","balance-beam","balance-pan","cord","knot","dial","star","firefly","swallow","starling","pebble","pebble-light","ripple"];function j(e,t){return e.options.detail!=="essential"&&Ct.includes(t)}function Lt(e,t,s,a,n){const o=e.el("symbol",{id:a,viewBox:"0 0 100 100",preserveAspectRatio:"none",overflow:"hidden"},s),r=e.theme.ink,l=e.theme.colors[0],i=e.theme.background,d=(c,f,y=!1)=>{const g=`${a}-${c}`;if(s.querySelector(`[id="${g}"]`))return`url(#${g})`;const w=y?e.el("radialGradient",{id:g,cx:".32",cy:".25",r:".8"},s):e.el("linearGradient",{id:g,x1:"0",y1:"0",x2:"1",y2:".25"},s);return(y?[[0,N(f,"#ffffff",.45)],[.45,f],[1,N(f,"#101e18",.52)]]:[[0,N(f,"#101e18",.55)],[.28,f],[.43,N(f,"#ffffff",.5)],[.57,f],[1,N(f,"#101e18",.45)]]).forEach(([E,T])=>e.el("stop",{offset:E,"stop-color":T},w)),`url(#${g})`},h=(c,f="none",y="none",g=1,w=o,b={})=>e.el("path",{d:c,fill:f,stroke:y,"stroke-width":g,"stroke-linejoin":"round","stroke-linecap":"round",...b},w),p=(c,f,y,g,w,b=o,E={})=>e.el("ellipse",{cx:c,cy:f,rx:y,ry:g,fill:w,...E},b),u=()=>d("metal",N(l,"#c49a53",.7)),m=(c,f)=>{h("M3 51C21 3 73 4 98 48C72 91 28 96 3 51Z",f,l,.7,c),h("M5 51Q53 49 95 48","none",N(l,"#fff",.6),1.2,c);for(let y=0;y<7;y++){const g=16+y*10;h(`M${g} 50Q${g+7} ${27-y*.4} ${g+17} ${17+Math.abs(3-y)*3}M${g} 50Q${g+8} ${74+y*.3} ${g+19} ${84-Math.abs(3-y)*3}`,"none",N(l,"#fff",.4),.55,c,{opacity:.65})}};if(t==="leaf")m(o,d("leaf",l,!0));else if(t==="seed"){const c=n??"#847050";h("M4 53C7 12 48 3 95 39Q104 48 94 58C45 98 11 91 4 53Z",d("shell",c,!0),N(c,"#17231a",.3),1);for(let f=0;f<5;f++)h(`M${15+f*3} ${24+f*9}Q${53+f*5} ${13+f*15} 93 48`,"none",N(c,f%2?"#17231a":"#ffffff",.4),1.5,o,{opacity:.8});p(12,51,3,7,N(c,"#ffffff",.35))}else if(t==="sprout"||t==="bud"||t==="flower"){h("M49 89Q53 68 49 36","none",l,3),h("M50 83Q43 90 37 97M49 87L52 99M50 86Q58 89 62 96","none",N(l,"#a7875b",.6),1.1);const c=e.el("g",{transform:"translate(7 28) rotate(26 22 20) scale(.43 .36)"},o);m(c,d("blade",l,!0));const f=e.el("g",{transform:"translate(51 15) rotate(-25 21 20) scale(.43 .38)"},o);if(m(f,`url(#${a}-blade)`),t==="bud"&&(h("M49 41Q31 18 49 3Q69 17 49 41Z",d("bud",l,!0),l,1),h("M49 8Q43 23 49 39","none",i,1,o,{opacity:.6})),t==="flower"){for(let y=0;y<8;y++){const g=e.el("g",{transform:`rotate(${y*45} 49 23)`},o);p(49,11,6,13,d(`petal-${y}`,e.theme.colors[3%e.theme.colors.length],!0),g)}p(49,23,7,7,"#b79143")}}else if(t==="bamboo"){h("M24 1Q50-1 76 1L74 99Q50 100 26 99Z",d("culm",l),l,.8);for(let c=1;c<6;c++){const f=c*16;h(`M24 ${f}Q49 ${f+3} 76 ${f}`,"none",N(l,"#dfdb99",.6),2),h(`M25 ${f+2}Q48 ${f+4} 74 ${f+2}`,"none",N(l,"#14251d",.5),1)}h("M39 3L38 96M61 3L63 96","none",i,.8,o,{opacity:.18})}else if(t==="pod"){h("M1 49C18 3 76 3 99 49C74 96 22 96 1 49Z",d("pod",l,!0),l,1.2),h("M4 49Q49 17 96 49Q51 79 4 49Z",N(l,"#0e2719",.65));for(let c=0;c<5;c++)p(19+c*15.5,50,8,18,d(`pea-${c}`,N(l,"#d7d289",.5),!0));h("M2 48Q50 35 98 48M3 51Q52 80 98 50","none",N(l,"#f1e8b3",.7),1.5)}else if(t==="sunflower"){const c=d("petal",N(e.theme.colors[3%e.theme.colors.length],"#d8ad47",.7),!0);for(let f=0;f<2;f++)for(let y=0;y<24;y++){const g=y*15+f*7.5,w=e.el("g",{transform:`rotate(${g} 50 50)`},o);h(`M45 29Q${39-f} 14 50 ${2+f*5}Q${62+f} 14 55 29Z`,c,"#a88439",.15,w),h(`M50 28Q48 14 50 ${5+f*5}`,"none","#f5df95",.35,w,{opacity:.7})}p(50,50,26,26,d("head","#685538",!0))}else if(t==="balance-stand"){h("M42 4Q50 0 58 4L58 17Q63 29 58 46L57 68Q57 77 68 83L78 88L81 96Q51 102 19 96L22 88L32 83Q43 78 43 68L42 46Q37 28 42 17Z",d("wood","#8b6948"),"#5d4934",.6);for(const c of[19,67,82,88,95])h(`M${c>80?24:42} ${c}Q50 ${c+3} ${c>80?76:58} ${c}`,"none","#ddbc80",.7,o,{opacity:.6});p(50,6,10,6,u())}else if(t==="balance-beam")h("M1 39Q23 37 44 29Q50 7 56 29Q78 37 99 39V64Q78 62 56 67Q50 93 44 67Q22 62 1 64Z",u(),"#887344",.7),p(50,49,7.5,35,u()),p(50,49,4.3,22,d("pivot","#96814b",!0)),h("M3 43L40 37M60 37L97 43","none","#f4dd9d",.7);else if(t==="balance-pan"){for(const c of[8,50,92]){h(`M50 3L${c} 75`,"none","#8b784a",.8);for(let f=0;f<13;f++){const y=f/13;p(50+(c-50)*y,4+70*y,1.1,1.8,"none",o,{stroke:"#c4ae76","stroke-width":.55})}}h("M3 75Q50 88 97 75Q93 96 50 99Q7 96 3 75Z",u(),"#7c683b",.7),p(50,75,47,7,u(),o,{stroke:"#af965b","stroke-width":.8}),h("M9 84Q47 97 91 84","none","#ead7a0",.8,o,{opacity:.8}),p(50,3,3,2,u())}else if(t==="cord"||t==="knot"){const c=d("flax",N(l,"#ae9771",.7));if(t==="cord"){h("M32 0Q27 43 35 100L67 100Q61 53 68 0Z",c);for(let f=0;f<25;f++)h(`M30 ${f*4}Q51 ${f*4+2} 68 ${f*4+1}`,"none","#725f44",.65,o,{opacity:.6});h("M44 0Q40 51 47 100","none","#e4d5af",1.5,o,{opacity:.6})}else{const f="M4 64C24 81 67 9 80 31C95 57 24 86 20 44C15 12 59 15 69 51C77 78 86 70 97 61";h(f,"none","#776549",17),h(f,"none",c,14),h(f,"none","#e3d0a4",1.2,o,{"stroke-dasharray":"2 4"}),h("M24 26Q36 35 41 59","none","#6d5b3d",2,o,{opacity:.8})}}else if(t==="dial"){p(50,50,49,49,d("stone",N(e.theme.grid,"#c4b78e",.5),!0),o,{stroke:e.theme.muted,"stroke-width":.45});for(const c of[43,45,47])p(50,50,c,c,"none",o,{stroke:e.theme.muted,"stroke-width":.2,opacity:.7});for(let c=0;c<90;c++){const f=c*2.4,y=Math.sqrt((c+.5)/90)*43;p(50+Math.cos(f)*y,50+Math.sin(f)*y,.13,.17,e.theme.muted,o,{opacity:.23})}h("M48 51L53 21L56 53Z",u(),"#8b774d",.3)}else if(t==="star"){const c=e.el("radialGradient",{id:`${a}-glow`},s);[[0,.85],[.2,.42],[1,0]].forEach(([f,y])=>e.el("stop",{offset:f,"stop-color":e.theme.colors[3%e.theme.colors.length],"stop-opacity":y},c)),p(50,50,49,49,`url(#${a}-glow)`),h("M50 9L54 44L91 50L54 54L50 91L46 54L9 50L46 45Z",d("star",e.theme.colors[3%e.theme.colors.length],!0)),p(50,50,3,3,i)}else if(t==="firefly"){const c=d("wing",N(l,i,.55),!0);for(const f of[-1,1]){const y=e.el("g",{transform:`translate(50 0) scale(${f} 1)`},o);h("M0 45Q21 12 43 23Q52 47 0 63Z",c,l,.6,y,{opacity:.68});for(let g=0;g<4;g++)h(`M1 54Q${15+g*6} ${42-g*5} ${32+g*3} ${26+g*3}`,"none",l,.45,y,{opacity:.6});h("M2 44L22 58L29 75M2 54L17 70L16 86M2 34L14 31L24 16","none",r,1.2,y)}p(50,59,7,22,d("abdomen",l,!0)),p(50,70,6,11,d("light",e.theme.colors[3%e.theme.colors.length],!0)),p(50,36,8,8,r),p(50,25,5,6,r),h("M47 23L40 10M53 23L60 10","none",r,1)}else if(t==="swallow"||t==="starling"){const c=d("feather",N(l,r,.7),!0);for(const f of[-1,1]){const y=e.el("g",{transform:`translate(50 0) scale(${f} 1)`},o);h("M0 35C13 36 24 24 45 5L37 40L16 60L2 59Z",c,r,.6,y);for(let g=0;g<8;g++)h(`M${7+g*2} ${40+g*1.8}L${40-g*3} ${14+g*4.5}`,"none",N(l,i,.5),.65,y,{opacity:.5})}h(t==="swallow"?"M44 65L35 98L50 83L65 98L55 64Z":"M43 64L36 90Q50 97 65 90L56 64Z",c,r,.4),p(50,48,7,26,c),p(50,22,5,8,r),h("M47 16L50 9L53 16Z",r),h("M47 35Q43 52 49 66","none",i,1,o,{opacity:.4})}else if(t==="pebble"||t==="pebble-light"){const c=t==="pebble"?N(l,"#71888b",.55):N(l,"#c3ad83",.7);h("M3 55C-2 21 39 5 74 15C101 23 105 64 90 80C68 99 11 95 3 55Z",d("pebble",c,!0),N(c,r,.3),.5),h("M13 25Q39 33 54 48T85 85M27 15Q35 41 61 57T91 71","none",N(c,i,.7),1.1,o,{opacity:.75}),h("M15 62Q12 34 40 23","none",i,1.6,o,{opacity:.32})}else if(t==="ripple")for(let c=0;c<5;c++){const f=8+c*9;p(50,50,f,f,"none",o,{stroke:l,"stroke-width":2.3-c*.3,opacity:.85-c*.14}),h(`M${50-f*.78} ${50-f*.2}A${f*.81} ${f*.81} 0 0 1 ${50+f*.25} ${50-f*.78}`,"none",i,.8,o,{opacity:.8-c*.1})}}function H(e,t,s,a,n,o,r,l={},i){if(!j(e,t)||o<=0||r<=0)return null;let d=e.svg.querySelector("defs");d||(d=e.el("defs"));const h=`${e.id}-form-${t}${i?`-${i.slice(1)}`:""}`;e.svg.querySelector(`[id="${h}"]`)||Lt(e,t,d,h,i);const p=e.el("g",{class:"np-specimen","data-specimen":t,"aria-hidden":"true",...l},s);return s&&e.el("rect",{x:a,y:n,width:o,height:r,fill:"transparent","pointer-events":"all",class:"np-specimen-hit"},p),e.el("use",{href:`#${h}`,x:a,y:n,width:o,height:r,"pointer-events":"none"},p),p}function se(e,t,s,a,n){e.el("circle",{cx:s,cy:a,r:n,fill:"none",stroke:e.theme.ink,"stroke-width":1.5,"stroke-dasharray":"3 4",opacity:0,class:"np-focus-halo","aria-hidden":"true","pointer-events":"none"},t)}const _e=["honeycomb","mycelium","root-tree","canopy","fern","phyllotaxis","leaf-veins","lotus","petal-box","raincloud","dew","wind-rose","dune","glacier","sediment","delta","estuary","pitcher","firefly","migration","murmuration","coral-range","pebble","nautilus","frost","echo","cairn","daylight","isobar","bamboo"],St={honeycomb:["Honeycomb","Comparison","A place for every reading","Compare team activity across categories in a modular hexagonal heatmap.","Equal cells; intensity represents value. Rows are tracks; columns are labels.","Up to 6 tracks × 6 labels, one observation per pair. Nonnegative values or null; absent cells are unmeasured.","The modular hexagonal cells of bee and wasp nests."],mycelium:["Mycelium","Relationship","Connections beneath the surface","Inspect dependencies, messages, or exchanges in a directed weighted network.","Curved links connect source to destination; line width encodes weight. Nodes are identifiers, not quantities; long dashes mean zero and short dashes mean unknown weight.","Up to 18 directed links and 10 nodes. source and destination are required, distinct, and unique as a pair. Nonnegative values or null.","Adaptive biological networks, including fungal and slime-mold networks; no optimization is computed."],"root-tree":["Root Tree","Relationship","Trace it back to the root","Explore ownership, taxonomies, and parent-child dependencies.","Depth follows parent links; node area represents its own value, never an inferred subtree total.","Up to 16 uniquely identified nodes, 4 depth levels, and at most 5 nodes per level. id is required; parent must exist; cycles are rejected. Nonnegative values or null.","Branching root systems and plant transport networks."],canopy:["Canopy","Composition","See the whole forest","Understand how departments and their projects contribute to one total.","Nested rectangles allocate area first to tracks, then to observations within each track.","Up to 12 observations in at most 4 tracks. All quantities must be known and nonnegative. Zero-area contributions remain in the inspector.","A canopy partitioned into neighboring crowns; rectangular areas are a modern abstraction."],fern:["Fern","Comparison","Find the few that matter","Prioritize issue categories or costs using a Pareto view.","Descending frond lengths show values; spores on a second axis show cumulative share.","Up to 8 known nonnegative quantities, sorted descending for display. Input indexes remain stable for callbacks.","A fern frond arranges repeated leaflets along a central stem."],phyllotaxis:["Phyllotaxis","Composition","Many parts, one seed head","Explain a portfolio, time allocation, or resource mix as one whole.","120 equal dots follow a golden-angle spiral. Largest-remainder allocation approximates each category share.","Up to 8 known nonnegative parts. Dots are rounded shares, not literal counts; exact totals remain in the table. All-zero data draws no allocated dots.","Repeated seed placement in plant heads; this is a mathematical packing metaphor, not a claim about every species."],"leaf-veins":["Leaf Veins","Comparison","Follow the change","Compare before and after for several teams or measures.","Paired endpoints share one linear axis: baseline is the old reading and value is the new reading.","Up to 6 observations. Every baseline must be finite. Signed readings and null new values are supported.","Veins connect two sides of a leaf; here they connect paired observations."],lotus:["Lotus","Progress","A profile with room to bloom","Compare performance across metrics with different target scales.","Each axis is value / target, capped at 100%; a dashed ring is the goal. Polygon shape depends on axis order.","3–8 nonnegative readings. Each positive target falls back to chart target or 100. Null readings break the polygon. Over-goal values stay exact.","Radial arrangement of lotus petals; unlike Bloom, each axis has its own target."],"petal-box":["Petal Box","Distribution","Let the spread unfold","Compare variability and medians across groups using supplied quartiles.","Whiskers span low–high; the petal body spans q1–q3; its line is the median (value).","Up to 6 known summaries satisfying low ≤ q1 ≤ value ≤ q3 ≤ high. These are supplied extrema, not automatically computed outliers.","A seed pod opening around its center inspires the distribution silhouette."],raincloud:["Raincloud","Distribution","See where readings gather","Show a histogram from supplied, possibly unequal-width bins.","Horizontal width is the bin interval. Height is frequency / bin width, so area represents frequency.","Up to 12 ordered, non-overlapping bins with low < high and known nonnegative frequencies. Gaps between bins remain gaps.","Rainfall falling from a cloud; frequency bars form the visible cloud profile."],dew:["Dew","Relationship","A clearer view of relationships","Compare two numeric measurements and an optional third size variable.","x is horizontal; value is vertical; optional weight controls droplet area. Hollow markers mean zero weight; dashed markers mean missing values.","Up to 40 readings with finite x. Signed values and null are supported. weight defaults to 1 and must be nonnegative. Missing readings sit in a separate rail.","Dew droplets provide small, distinct lenses onto individual observations."],"wind-rose":["Wind Rose","Spatial","Know which way it leans","Compare directional observations such as wind or arrival frequencies.","Equally spaced compass sectors use area proportional to value, with north at the top and clockwise angles.","4, 8, or 16 observations. angle must equal index × 360 / count. Nonnegative values or null. Values need not sum to 100.","Compass directions and observed winds; no weather predictions are generated."],dune:["Dune","Distribution","Distributions across the landscape","Compare supplied frequency profiles across cohorts.","Each track has a ridge; position determines horizontal distance and height uses a common value scale.","Up to 4 tracks with 2–16 strictly increasing positions each. Nonnegative values or null; null breaks a ridge. Heights are supplied frequencies, not fitted densities.","Wind-shaped ridges in dune fields inspire the aligned silhouettes."],glacier:["Glacier","Trend","Every change leaves a step","Explain how gains and losses build a final balance.","Floating ice steps show signed contributions; their endpoints track the running sum from zero.","Up to 10 known signed changes. Unknown changes are rejected because later cumulative totals would be unknowable.","A stepped glacier landscape becomes a signed waterfall; it is not a glacier mass model."],sediment:["Sediment","Composition","Read the layers over time","Explore how a changing total is composed of several contributions.","Layer thickness is value at each position; stacked boundaries show the total on one linear scale.","Up to 4 tracks sharing 2–16 identical increasing positions. All values must be known and nonnegative. Linear interpolation is visual, not extra observations.","Deposited layers preserve sequences; chart thickness represents supplied contributions."],delta:["Delta","Composition","One source, many paths","Show how one budget or supply is allocated to destinations.","One incoming ribbon splits into outgoing ribbons. Each width is proportional to its quantity.","Up to 7 known nonnegative links sharing one source and distinct destinations. Zero links remain inspectable.","River deltas branch a main channel into distributaries."],estuary:["Estuary","Relationship","Meet where the currents join","Track transfers between several origins and destinations.","Bipartite ribbons have conserved widths; nodes show sums of their incident links.","Up to 16 known nonnegative links with at most 5 sources and 5 destinations. Directed pairs must be unique. Left and right node sets are separate.","River and sea exchanges inspire a modern many-to-many flow diagram."],pitcher:["Pitcher","Progress","Where does the journey narrow?","Locate attrition through a signup, sales, or completion funnel.","Successive widths show remaining count relative to the first stage; labels show retention from the preceding stage.","2–7 known nonnegative counts in non-increasing order. All-zero data has no measurable conversion percentage.","A pitcher plant’s narrowing chamber inspires the funnel silhouette."],firefly:["Firefly","Timeline","See the moments light up","Spot bursts, pauses, and coincidences in event logs.","Horizontal position is event time; tracks separate event streams; glow intensity is magnitude.","Up to 60 events in at most 5 tracks. position is required and may be irregular. Nonnegative values or null. Coincident events remain separate in the inspector.","Brief firefly signals inspire discrete events, not an interpolated trend."],migration:["Migration","Spatial","A journey, stop by stop","Show an ordered route through supplied coordinates.","x and y locate each stop; arrows preserve input order; stop area represents value.","2–20 known nonnegative readings, each with finite x and y. Coordinates are planar in the same unit, with equal scale on both axes; not projected latitude/longitude.","Animal migration inspires an ordered path; the chart does not infer a real route."],murmuration:["Murmuration","Distribution","Keep every individual in view","Compare raw distributions across cohorts without binning away observations.","Horizontal position represents value; vertical offsets separate nearby birds within each track and have no numeric meaning.","Up to 60 signed readings in at most 4 tracks. Null values use a separate rail. Crowded points may overlap; use the inspector for every record.","A flock remains many individual birds; each mark preserves one observation."],"coral-range":["Coral Range","Comparison","Make uncertainty visible","Compare estimates with supplied lower and upper bounds.","Branch endpoints mark low and high; the central polyp marks value on a common numeric scale.","Up to 6 observations with finite low ≤ value ≤ high. A null center is allowed within a known range. These bounds are not assumed to be confidence intervals.","Branching coral provides a range silhouette; bounds remain application-defined."],pebble:["Pebble","Distribution","Find your place in the stream","Read percentiles from a sample without choosing histogram bins.","A right-continuous step shows the empirical cumulative distribution; ties share the fraction at or below that value.","Up to 60 known signed samples. All observations are retained, including ties. This is an empirical sample, not a fitted probability model.","Pebbles accumulate downstream; each reading contributes equally to cumulative probability."],nautilus:["Nautilus","Cycle","Remember more than one turn","Compare recurring work across several consecutive cycles.","Angle represents position modulo cycleLength; spiral turn retains cycle number; dot area shows value.","Up to 36 nonnegative readings, strictly increasing positions from 0 to less than 4 × cycleLength. cycleLength defaults to 12.","Successive shell chambers inspire a spiral timeline; no biological growth law is inferred."],frost:["Frost","Relationship","Patterns in a crystal lattice","Inspect a supplied correlation matrix with a fixed signed scale.","Cells show values from −1 to +1; color distinguishes sign and intensity magnitude. Diagonal cells are one.","A complete symmetric matrix of 2–6 variables using track for row and label for column. Mirrored values must agree; symmetric null pairs are allowed.","A repeating crystalline lattice suggests symmetry; correlation never implies causation."],echo:["Echo","Relationship","Hear the previous reading","Inspect lag-one dependence in an ordered numeric series.","Each point pairs the preceding value on x with the current value on y; a diagonal marks equality.","2–40 signed observations. Nulls break pairs. The first record has no predecessor and is shown in the inspector rail. No significance or causality is inferred.","Returning sounds inspire comparison with what came before; this is not an echolocation model."],cairn:["Cairn","Progress","Build toward one shared goal","Track which contributions make up progress toward a common target.","Stacked stone heights show contributions on a shared goal scale; the dashed line is the chart-level target, shared by every contributor.","Up to 8 known nonnegative contributions and a positive chart target (default 100). The scale expands for over-goal totals; exact excess is retained.","A cairn is assembled stone by stone; the stones here record contributions, not dates."],daylight:["Daylight","Timeline","Watch the days open and close","Compare supplied sunrise/sunset or opening/closing windows across dates.","Each row shares a 24-hour axis; warm bands run from start to exclusive end; labels state duration.","Up to 7 unique ISO dates with increasing HH:mm start/end in one day. Only end may be 24:00. No location or astronomical calculation is performed.","Historical shadow observations motivated careful attention to changing daylight."],isobar:["Isobar","Spatial","Connect equal readings","Explore a measured scalar field such as temperature or pressure.","Nodes retain measured values; lines interpolate equal values at 25%, 50%, and 75% of the observed range.","Complete 2–8 by 2–8 grid of unique finite x/y coordinates and known finite values. Contours use piecewise-linear triangles; they are estimates between nodes.","Isobars connect equal pressure; the same encoding works for other scalar fields."],bamboo:["Bamboo","Distribution","The original numbers, intact","Inspect a compact integer distribution without losing exact readings.","The stem is tens; each leaf is units. A key explains how to reconstruct the number.","Up to 60 integer values from 0 to 99, with at most 18 leaves per stem. Values are sorted within stems; repeated digits are distinct observations.","Segmented bamboo stems inspire a modern statistical stem-and-leaf plot, not an ancient invention."]},Pe=Object.fromEntries(_e.map(e=>{const[t,s,a,n,o,r,l]=St[e];return[e,{name:t,category:s,subtitle:a,description:n,encoding:o,rule:r,inspiration:l}]})),z=e=>{throw new Error("NaturePlot: "+e)},J=(e,t)=>{(e[t]===void 0||e[t]==="")&&z(`${t} is required.`)},Ye=e=>[...new Set(e.map(t=>t.track??"Series"))].map(t=>e.filter(s=>(s.track??"Series")===t)),Je={honeycomb:36,mycelium:18,"root-tree":16,canopy:12,fern:8,phyllotaxis:8,"leaf-veins":6,lotus:8,"petal-box":6,raincloud:12,dew:40,"wind-rose":16,dune:64,glacier:10,sediment:64,delta:7,estuary:16,pitcher:7,firefly:60,migration:20,murmuration:60,"coral-range":6,pebble:60,nautilus:36,frost:36,echo:40,cairn:8,daylight:7,isobar:64,bamboo:60};function zt(e,t){if(!_e.includes(e.type))return;const s=e.type;t.length>Je[s]&&z(`${s} supports at most ${Je[s]} observations.`);for(const a of t){for(const n of["x","y","weight","angle","baseline","low","q1","q3","high"])a[n]!==void 0&&!Number.isFinite(a[n])&&z(`${n} must be finite.`);for(const n of["id","parent","source","destination"])a[n]!==void 0&&(typeof a[n]!="string"||!a[n].trim())&&z(`${n} must be a nonempty string.`);for(const n of["value","x","y","weight","position","baseline","low","q1","q3","high"])typeof a[n]=="number"&&(Math.abs(a[n])>1e100||a[n]!==0&&Math.abs(a[n])<1e-100)&&z(`${s} supports zero and magnitudes from 1e-100 to 1e100; rescale units first.`);!["leaf-veins","petal-box","dew","glacier","murmuration","coral-range","pebble","frost","echo","isobar","daylight"].includes(s)&&a.value!==null&&a.value<0&&z(`${s} requires nonnegative values.`),["canopy","fern","phyllotaxis","petal-box","raincloud","glacier","sediment","delta","estuary","pitcher","migration","pebble","cairn","isobar","bamboo"].includes(s)&&a.value===null&&z(`${s} needs known values; missing data would make its derived quantities misleading.`),a.weight!==void 0&&a.weight<0&&z("weight must be nonnegative.")}s==="nautilus"&&(!Number.isFinite(e.cycleLength??12)||(e.cycleLength??12)<=0||(e.cycleLength??12)>1e100)&&z("cycleLength must be positive and no greater than 1e100.");for(const a of["target","max","cycleLength"])e[a]!==void 0&&(e[a]<1e-100||e[a]>1e100)&&z(`${a} must be between 1e-100 and 1e100 for living-system charts.`);if(t.some(a=>a.target!==void 0&&(a.target<1e-100||a.target>1e100))&&z("point targets must be between 1e-100 and 1e100."),!!t.length){if(["honeycomb","frost"].includes(s)){t.forEach(r=>{J(r,"track"),J(r,"label")});const a=[...new Set(t.map(r=>r.track))],n=[...new Set(t.map(r=>r.label))];(a.length>6||n.length>6)&&z("matrix dimensions must not exceed 6.");const o=new Map;t.forEach(r=>{const l=JSON.stringify([r.track,r.label]);o.has(l)&&z("matrix cells must be unique."),o.set(l,r)}),s==="frost"&&((a.length<2||a.length!==n.length||a.some(r=>!n.includes(r))||t.length!==a.length**2)&&z("frost requires a complete square matrix of matching variables."),t.forEach(r=>{r.value!==null&&(r.value<-1||r.value>1)&&z("correlations must lie between -1 and 1."),r.track===r.label&&r.value!==1&&z("correlation diagonal must equal 1."),o.get(JSON.stringify([r.label,r.track])).value!==r.value&&z("correlation matrix must be symmetric.")}))}if(["mycelium","delta","estuary"].includes(s)){const a=new Set;t.forEach(r=>{J(r,"source"),J(r,"destination");const l=JSON.stringify([r.source,r.destination]);a.has(l)&&z("directed link pairs must be unique."),a.add(l),s!=="estuary"&&r.source===r.destination&&z("self-links are not supported.")});const n=new Set(t.map(r=>r.source)),o=new Set(t.map(r=>r.destination));s==="mycelium"&&new Set([...n,...o]).size>10&&z("mycelium supports at most 10 nodes."),s==="delta"&&n.size!==1&&z("delta requires one source."),s==="estuary"&&(n.size>5||o.size>5)&&z("estuary supports at most 5 sources and 5 destinations.")}if(s==="root-tree"){const a=new Map,n=new Map;t.forEach(o=>{J(o,"id"),n.has(o.id)&&z("node ids must be unique."),n.set(o.id,o)}),t.forEach(o=>{let r=o;const l=new Set;for(;r&&(l.has(r.id)&&z("hierarchy must not contain cycles."),l.add(r.id),l.size>4&&z("root-tree supports at most 4 depth levels."),!!r.parent);){const d=n.get(r.parent);d||z("every parent must reference an existing node."),r=d}const i=l.size;a.set(i,(a.get(i)??0)+1)}),[...a.values()].some(o=>o>5)&&z("root-tree supports at most 5 nodes at each depth for readable labels.")}if(["canopy","dune","sediment","firefly","murmuration"].includes(s)&&Ye(t).length>(s==="firefly"?5:4)&&z(`${s} has too many tracks.`),s==="lotus"&&t.length<3&&z("lotus needs at least 3 axes."),s==="leaf-veins"&&t.forEach(a=>J(a,"baseline")),["petal-box","coral-range","raincloud"].includes(s)&&t.forEach((a,n)=>{J(a,"low"),J(a,"high"),a.low>a.high&&z("low must not exceed high."),s==="raincloud"?(a.low===a.high&&z("histogram bins need positive width."),n&&a.low<t[n-1].high&&z("histogram bins must be ordered and non-overlapping.")):a.value!==null&&(a.value<a.low||a.value>a.high)&&z("value must fall inside its range."),s==="petal-box"&&(J(a,"q1"),J(a,"q3"),a.low<=a.q1&&a.q1<=a.value&&a.value<=a.q3&&a.q3<=a.high||z("quartiles must satisfy low ≤ q1 ≤ median ≤ q3 ≤ high."))}),["dew","migration","isobar"].includes(s)&&t.forEach(a=>{J(a,"x"),s!=="dew"&&J(a,"y")}),["migration","echo","pitcher"].includes(s)&&t.length<2&&z(`${s} needs at least 2 observations.`),s==="wind-rose"&&([4,8,16].includes(t.length)||z("wind-rose needs 4, 8, or 16 sectors."),t.forEach((a,n)=>{a.angle!==n*360/t.length&&z("wind angles must equal index × 360 / count.")})),["dune","sediment","firefly","nautilus"].includes(s)&&t.forEach(a=>J(a,"position")),["dune","sediment"].includes(s)){const a=Ye(t);a.forEach(n=>{(n.length<2||n.length>16)&&z("each profile needs 2–16 positions."),n.forEach((o,r)=>{r&&o.position<=n[r-1].position&&z("profile positions must increase strictly.")})}),s==="sediment"&&a.some(n=>n.length!==a[0].length||n.some((o,r)=>o.position!==a[0][r].position))&&z("sediment tracks must share identical positions.")}if(s==="pitcher"&&t.some((a,n)=>n>0&&a.value>t[n-1].value)&&z("funnel counts must be non-increasing."),s==="nautilus"&&t.forEach((a,n)=>{(a.position<0||a.position>=4*(e.cycleLength??12)||n&&a.position<=t[n-1].position)&&z("spiral positions must increase within four cycles.")}),s==="daylight"){const a=new Set;t.forEach(n=>{J(n,"date"),J(n,"start"),J(n,"end"),a.has(n.date)&&z("daylight dates must be unique."),a.add(n.date);const o=(r,l=!1)=>{if(!/^\d{2}:\d{2}$/.test(r))return z("daylight times must use HH:mm.");const[i,d]=r.split(":").map(Number);return(d>59||i>23&&!(l&&i===24&&d===0))&&z("daylight time outside 24-hour day."),i*60+d};o(n.end,!0)<=o(n.start)&&z("daylight end must be after start in one day.")})}if(s==="isobar"){const a=new Set(t.map(o=>o.x)),n=new Set(t.map(o=>o.y));(a.size<2||n.size<2||a.size>8||n.size>8||t.length!==a.size*n.size||new Set(t.map(o=>JSON.stringify([o.x,o.y]))).size!==t.length)&&z("isobar requires a complete 2–8 by 2–8 coordinate grid without duplicates.")}s==="bamboo"&&[...new Set(t.map(a=>Math.floor(a.value/10)))].some(a=>t.filter(n=>Math.floor(n.value/10)===a).length>18)&&z("bamboo supports at most 18 leaves per stem; split dense samples."),s==="bamboo"&&t.some(a=>!Number.isInteger(a.value)||a.value<0||a.value>99)&&z("bamboo accepts integers from 0 to 99.")}}const Ke="http://www.w3.org/2000/svg";function Z(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))throw new Error(`NaturePlot: invalid ISO date "${e}". Use YYYY-MM-DD.`);const t=new Date(e+"T00:00:00Z");if(!Number.isFinite(t.getTime())||t.toISOString().slice(0,10)!==e)throw new Error(`NaturePlot: invalid calendar date "${e}".`);return t}function Pt(e,t){const s=Z(e);return s.setUTCDate(s.getUTCDate()+t),s.toISOString().slice(0,10)}function he(e){return Z(e).toLocaleDateString("en",{month:"short",day:"numeric",timeZone:"UTC"})}function ne(e,t=!1){if(typeof e!="string"||!/^\d{2}:\d{2}$/.test(e))throw new Error("NaturePlot: clock times must use HH:mm.");const[s,a]=e.split(":").map(Number);if(a>59||s>23&&!(t&&s===24&&a===0))throw new Error("NaturePlot: clock time is outside the day; only an interval end may be 24:00.");return s*60+a}function Xe(e,t){return e.year??(t[0]?.start?Number(t[0].start.slice(0,4)):new Date().getUTCFullYear())}function Ne(e){return`${String(e).padStart(4,"0")}-01-01`}function Ae(e){return e.cycleLength??(e.type==="star-cycle"?12:e.type==="tidal-rhythm"?24:30)}function et(e,t){return t.type==="cairn"?t.target??100:e.target??(["tide","rings","waterline","water-clock","lotus","cairn"].includes(t.type)?t.target??100:void 0)}function Ie(e,t=1,s=40){const a=Math.max(0,...e.map(r=>r.value??0));if(a/t<=s)return t;const o=Math.ceil(a/s/t)*t;return Number.isFinite(o)&&o>0?o:a/s}function Nt(e){if(e.detail!==void 0&&!["natural","essential"].includes(e.detail))throw new Error("NaturePlot: detail must be natural or essential.");if(e.artwork!==void 0&&!["illustrated","vector"].includes(e.artwork))throw new Error("NaturePlot: artwork must be illustrated or vector.");if(!Array.isArray(e.data))throw new Error("NaturePlot: data must be an array.");if(e.data.length>1e3)throw new Error("NaturePlot: use at most 1,000 observations; aggregate larger datasets first.");for(const u of["title","description","unit","cycleUnit","xLabel","yLabel","weightLabel"])if(e[u]!==void 0&&typeof e[u]!="string")throw new Error(`NaturePlot: ${u} must be a string.`);const t=["sundial","season-wheel","phenology","daylight"].includes(e.type),s=["rainbow","garden","forest","bloom","tide","rings","seed-ledger","waterline","lunar-cycle","water-clock","balance","cord-ledger","growth-history","star-cycle"].includes(e.type),a=Array.from(e.data,(u,m)=>{if(!u||typeof u!="object")throw new Error(`NaturePlot: data[${m}].value must be a finite number or null.`);const c=t&&u.value===void 0?null:u.value;if(c!==null&&(typeof c!="number"||!Number.isFinite(c)))throw new Error(`NaturePlot: data[${m}].value must be a finite number or null.`);if(s&&c!==null&&c<0)throw new Error(`NaturePlot: ${e.type} requires nonnegative values.`);for(const f of["label","track","cycle","start","end","observedAt","expectedStart","expectedEnd"])if(u[f]!==void 0&&typeof u[f]!="string")throw new Error(`NaturePlot: data[${m}].${f} must be a string.`);if(u.date!==void 0&&Z(u.date),u.target!==void 0&&(!Number.isFinite(u.target)||u.target<=0))throw new Error("NaturePlot: targets must be positive numbers.");if(u.position!==void 0&&!Number.isFinite(u.position))throw new Error("NaturePlot: cycle positions must be finite numbers.");return{...u,value:c}});if(e.max!==void 0&&(!Number.isFinite(e.max)||e.max<=0||a.some(u=>u.value!==null&&u.value>e.max)))throw new Error("NaturePlot: max must be positive and at least the largest value.");if(e.target!==void 0&&(!Number.isFinite(e.target)||e.target<=0))throw new Error("NaturePlot: target must be a positive number.");if(["tide","waterline","water-clock"].includes(e.type)&&a.length>1)throw new Error(`NaturePlot: ${e.type} accepts one observation. Use rings for multiple goals.`);const n={rings:8,forest:24,bloom:12,"seed-ledger":6,"cord-ledger":8,"growth-history":10,sundial:12,"season-wheel":12,phenology:6,"lunar-cycle":24,"star-cycle":24};if(n[e.type]&&a.length>n[e.type])throw new Error(`NaturePlot: ${e.type} supports at most ${n[e.type]} observations.`);if(e.startDate!==void 0&&Z(e.startDate),e.endDate!==void 0&&Z(e.endDate),e.type==="balance"&&a.length&&a.length!==2)throw new Error("NaturePlot: balance requires exactly two observations.");if(["seed-ledger","cord-ledger"].includes(e.type)&&e.unitsPerMark!==void 0&&(!Number.isFinite(e.unitsPerMark)||e.unitsPerMark<=0))throw new Error("NaturePlot: unitsPerMark must be a positive finite number.");if(e.type==="water-clock"&&e.timeMode!==void 0&&!["remaining","elapsed"].includes(e.timeMode))throw new Error("NaturePlot: timeMode must be remaining or elapsed.");if(e.type==="growth-history"&&e.growthMode!==void 0&&!["thickness","area"].includes(e.growthMode))throw new Error("NaturePlot: growthMode must be thickness or area.");if(e.type==="waterline"&&e.thresholds!==void 0){if(!Array.isArray(e.thresholds)||e.thresholds.length>6)throw new Error("NaturePlot: use at most 6 thresholds.");const u=a[0]?.target??e.target??100,m=new Set;Array.from(e.thresholds).forEach(c=>{if(!c||!Number.isFinite(c.value)||c.value<0||c.value>u||typeof c.label!="string"||!c.label.trim())throw new Error("NaturePlot: each threshold needs a label and a value between zero and capacity.");if(m.has(c.value))throw new Error("NaturePlot: threshold values must be unique.");m.add(c.value)})}if(e.type==="sundial"||e.type==="season-wheel"){const u=e.type==="sundial",m=Xe(e,a);if(!u&&(!Number.isInteger(m)||m<1||m>9998))throw new Error("NaturePlot: year must be an integer from 1 to 9998.");const c=u?0:Z(Ne(m)).getTime(),f=u?1440:Z(Ne(m+1)).getTime(),y=a.map((g,w)=>{if(!g.start||!g.end)throw new Error(`NaturePlot: data[${w}] needs start and end for an interval.`);const b=u?ne(g.start):Z(g.start).getTime(),E=u?ne(g.end,!0):Z(g.end).getTime();if(b<c||E>f||E<=b)throw new Error(`NaturePlot: intervals must advance within the selected ${u?"day":"year"}; split intervals that cross its boundary.`);return{start:b,end:E,track:g.track??"Schedule"}});if(new Set(y.map(g=>g.track)).size>4)throw new Error("NaturePlot: use at most 4 interval tracks.");if(y.forEach((g,w)=>{if(y.slice(0,w).some(b=>g.track===b.track&&g.start<b.end&&g.end>b.start))throw new Error("NaturePlot: intervals on the same track must not overlap. Use separate tracks for simultaneous activities.")}),e.at!==void 0){const g=u?ne(e.at):Z(e.at).getTime();if(g<c||g>=f)throw new Error("NaturePlot: at must fall inside the selected interval window.")}}if(e.type==="phenology"){if(e.startDate===void 0!=(e.endDate===void 0))throw new Error("NaturePlot: provide both startDate and endDate for a fixed stage timeline.");if(e.startDate&&e.endDate<=e.startDate)throw new Error("NaturePlot: endDate must be after startDate.");a.forEach(u=>{if(u.expectedStart===void 0!=(u.expectedEnd===void 0))throw new Error("NaturePlot: an expected window requires both expectedStart and expectedEnd.");for(const m of["observedAt","expectedStart","expectedEnd"]){const c=u[m];if(c!==void 0&&(Z(c),e.startDate&&(c<e.startDate||c>e.endDate)))throw new Error("NaturePlot: a stage date falls outside the timeline domain.")}if(u.expectedStart&&u.expectedEnd<u.expectedStart)throw new Error("NaturePlot: an expected window must not end before it starts.")})}if(["lunar-cycle","star-cycle","tidal-rhythm"].includes(e.type)){const u=Ae(e);if(!Number.isFinite(u)||u<=0)throw new Error("NaturePlot: cycleLength must be a positive finite number.");if(e.cycleUnit!==void 0&&typeof e.cycleUnit!="string")throw new Error("NaturePlot: cycleUnit must be a string.");if(e.cyclePosition!==void 0&&(!Number.isFinite(e.cyclePosition)||e.cyclePosition<0||e.cyclePosition>=u))throw new Error("NaturePlot: cyclePosition must be within [0, cycleLength).");const m=new Map;if(a.forEach((c,f)=>{const y=e.type==="tidal-rhythm"?c.cycle??"Cycle 1":"Cycle",g=m.get(y)??[];if(c.position??=e.type==="tidal-rhythm"?g.length:f,c.position<0||(e.type==="tidal-rhythm"?c.position>u:c.position>=u))throw new Error("NaturePlot: an observation position falls outside the cycle.");if(e.type==="tidal-rhythm"&&g.length&&c.position<=g.at(-1))throw new Error("NaturePlot: positions in each tidal cycle must increase strictly.");if(g.includes(c.position))throw new Error("NaturePlot: cycle positions must be unique within each cycle.");g.push(c.position),m.set(y,g),e.type==="tidal-rhythm"&&(c.cycle=y)}),e.type==="tidal-rhythm"&&(m.size>4||[...m.values()].some(c=>c.length>48)))throw new Error("NaturePlot: tidal-rhythm supports at most 4 cycles with 48 observations each.")}if(e.type==="cord-ledger"){const u=new Set;let m;a.forEach(c=>{const f=c.track??"Contributions";if(f!==m){if(u.has(f))throw new Error("NaturePlot: keep each cord track contiguous in input order.");m!==void 0&&u.add(m),m=f}})}if(s||t||["river","mountain","tidal-rhythm"].includes(e.type)){const u=c=>c==null||c===0||Math.abs(c)>=1e-100&&Math.abs(c)<=1e100;if(a.flatMap(c=>[c.value,c.target,c.position]).concat([e.max,e.target,e.unitsPerMark,e.cycleLength,e.cyclePosition],(e.thresholds??[]).map(c=>c.value)).some(c=>!u(c)))throw new Error("NaturePlot: built-in numeric magnitudes must be zero or between 1e-100 and 1e100. Rescale the data and state its unit.")}if(zt(e,a),!["rainbow","garden"].includes(e.type))return a;const r=e.startDate??a.find(u=>u.date)?.date??new Date().toISOString().slice(0,10),l=Z(r),i=new Date(l);i.setUTCMonth(i.getUTCMonth()+1,0);const d=e.type==="rainbow"?49:e.days??i.getUTCDate();if(!Number.isInteger(d)||d<1||d>49||e.type==="garden"&&d>31)throw new Error("NaturePlot: garden days must be an integer between 1 and 31.");const h=Array.from({length:d},(u,m)=>Pt(r,m)),p=new Map;return a.forEach((u,m)=>{const c=u.date??h[m];if(!c||!h.includes(c))throw new Error(`NaturePlot: observation ${m+1} falls outside the calendar window.`);if(p.has(c))throw new Error(`NaturePlot: duplicate calendar date ${c}.`);p.set(c,{...u,date:c})}),h.map(u=>p.get(u)??{date:u,value:null})}function ce(e,t){return t??(Math.max(0,...e.map(s=>s.value??0))||1)}function C(e,t,s,a){return[e+s*Math.cos(a),t+s*Math.sin(a)]}function De(e,t,s,a,n){const[o,r]=C(e,t,s,a),[l,i]=C(e,t,s,n);return`M${o},${r} A${s},${s} 0 ${n-a>Math.PI?1:0} 1 ${l},${i}`}function S(e,t,s,a,n={}){return e.el("text",{x:t,y:s,fill:e.theme.muted,"font-size":12,"text-anchor":"middle",...n},void 0,a)}function q(e,t=13){return e.length>t?e.slice(0,t-1)+"…":e}function Y(e,t){return e.label??(e.date?he(e.date):`Item ${t+1}`)}function $e(e,t){return e===null?.1:.25+Math.min(1,e/t)*.75}function tt(e){const s=ce(e.data,e.options.max);S(e,206,338,e.format(0),{"text-anchor":"end"});for(let a=0;a<5;a++)e.el("rect",{x:221+a*17,y:327,width:12,height:12,rx:3,fill:e.theme.colors[0],opacity:.25+a*.1875});S(e,319,338,e.format(s),{"text-anchor":"start"}),e.el("circle",{cx:423,cy:333,r:5,fill:e.theme.grid,stroke:e.theme.muted,"stroke-dasharray":"2 2"}),S(e,436,338,"No data",{"text-anchor":"start"})}const ie=Math.PI*2,v=(e,t=0)=>e.theme.colors[t%e.theme.colors.length],M=(e,t,s,a,n,o={})=>e.el("text",{x:t,y:s,fill:e.theme.ink,"font-size":14,"text-anchor":"middle",...o},n,a),A=(e,t,s,a,n,o,r={})=>e.el("line",{x1:t,y1:s,x2:a,y2:n,stroke:e.theme.grid,"stroke-width":1.5,...r},o),Q=(e,t,s,a={})=>e.el("path",{d:t,fill:"none",stroke:v(e),"stroke-width":2,...a},s),D=(e,t,s,a,n,o={})=>e.el("circle",{cx:t,cy:s,r:a,fill:v(e),...o},n),k=e=>e.value??0,ke=e=>e.reduce((t,s)=>t+k(s),0),K=e=>e.options.max??(Math.max(0,...e.data.map(k))||1),G=(e,t)=>{const s=e.format(t);return s.length>16?t.toExponential(2)+(e.options.unit?" "+e.options.unit:""):s},le=(e,t,s=16)=>q(Y(e,t),s),I=(e,t,s)=>{const a=e.el("g");return e.mark(a,t,s),a},V=(e,t)=>M(e,320,338,t,void 0,{"font-size":13,fill:e.theme.muted}),B=(e,t,s,a=!1)=>{let n=Math.min(...e),o=Math.max(...e);if(a&&(n=Math.min(0,n),o=Math.max(0,o)),e.length||(n=0,o=1),n===o)if(a&&n===0)o=1;else{const r=Math.abs(n)*.1||1;n-=r,o+=r}return{a:n,b:o,at:r=>t+(r-n)/(o-n)*(s-t)}},at=e=>{const t=e.data.map(h=>h.x),s=e.data.map(h=>h.y),a=Math.min(...t),n=Math.max(...t),o=Math.min(...s),r=Math.max(...s),l=Math.max((n-a)/533,(r-o)/220)||1/220,i=(a+n)/2,d=(o+r)/2;return{xs:B([i-l*533/2,i+l*533/2],65,598),ys:B([d-l*110,d+l*110],275,55)}},Me=e=>[...new Set(e.data.map(t=>t.track??"Series"))].map(t=>({key:t,items:e.data.map((s,a)=>({p:s,i:a})).filter(({p:s})=>(s.track??"Series")===t)})),Ee=(e,t,s=285)=>{A(e,150,s,596,s),[t.a,(t.a+t.b)/2,t.b].forEach(a=>{A(e,t.at(a),s,t.at(a),s+5),M(e,t.at(a),s+24,["firefly","dune"].includes(e.options.type)?be(a):G(e,a))})},be=e=>Math.abs(e)>=1e6||e!==0&&Math.abs(e)<.001?e.toExponential(2):new Intl.NumberFormat("en",{maximumFractionDigits:2}).format(e),fe=(e,t,s,a="x",n="value")=>{A(e,65,280,598,280),A(e,65,45,65,280),[t.a,(t.a+t.b)/2,t.b].forEach(o=>M(e,t.at(o),303,["echo","pebble"].includes(e.options.type)?G(e,o):be(o))),[s.a,(s.a+s.b)/2,s.b].forEach(o=>{A(e,65,s.at(o),598,s.at(o),void 0,{"stroke-dasharray":"3 5"}),M(e,56,s.at(o)+5,["dew","echo","sediment"].includes(e.options.type)?G(e,o):be(o),void 0,{"text-anchor":"end","font-size":12})}),M(e,335,326,e.options.xLabel??a,void 0,{fill:e.theme.muted}),M(e,67,26,e.options.yLabel??n,void 0,{"text-anchor":"start",fill:e.theme.muted})},ot=(e,t,s)=>Array.from({length:6},(a,n)=>C(e,t,s,ie*n/6)).map(a=>a.join(",")).join(" "),At=(e,t,s,a,n)=>`M${e},${t}L${C(e,t,s,a)}A${s},${s} 0 ${n-a>Math.PI?1:0} 1 ${C(e,t,s,n)}Z`,qt=e=>{const t=[...new Set(e.data.map(r=>r.track))],s=[...new Set(e.data.map(r=>r.label))],a=Math.min(42,240/t.length),n=Math.min(69,410/s.length),o=Math.min(25,a/1.8);s.forEach((r,l)=>M(e,181+l*n,31,q(r,9))),t.forEach((r,l)=>M(e,137,65+l*a,q(r,13),void 0,{"text-anchor":"end"})),t.forEach((r,l)=>s.forEach((i,d)=>{const h=e.data.findIndex(f=>f.track===r&&f.label===i),p=e.data[h],u=p?I(e,p,h):e.el("g"),m=181+d*n+(l%2?o*.15:0),c=60+l*a;e.el("polygon",{points:ot(m,c,o),fill:p?v(e):e.theme.grid,opacity:p&&p.value!==null?.18+.82*k(p)/K(e):.12,stroke:e.theme.muted},u),e.el("polygon",{points:ot(m,c,o-2.4),fill:"none",stroke:e.theme.background,"stroke-width":1,opacity:.5,"pointer-events":"none"},u),p&&M(e,m,c+5,p.value===null?"—":q(e.options.formatValue?.(p.value)??be(p.value),6),u,{"font-size":13,fill:me(N(e.theme.background,v(e),p.value===null?.12:.18+.82*k(p)/K(e)))})})),V(e,`Equal cells · intensity = value${e.options.unit?` (${e.options.unit})`:""} · pale cells are unmeasured`)},Qt=e=>{const t=e.data.map(n=>{let o=0,r=n;for(;r.parent;)o++,r=e.data.find(l=>l.id===r.parent);return o}),s=Math.max(...t,1),a=e.data.map((n,o)=>{const r=e.data.filter((i,d)=>t[d]===t[o]),l=r.indexOf(n);return[75+t[o]/s*475,50+(l+1)*240/(r.length+1)]});e.data.forEach((n,o)=>{if(n.parent){const r=e.data.findIndex(d=>d.id===n.parent),l=a[r],i=a[o];Q(e,`M${l}C${l[0]+70},${l[1]} ${i[0]-70},${i[1]} ${i}`,void 0,{stroke:v(e,t[o]),opacity:.6,"stroke-width":3.5}),Q(e,`M${l}C${l[0]+70},${l[1]} ${i[0]-70},${i[1]} ${i}`,void 0,{stroke:e.theme.background,"stroke-width":.75,opacity:.6,"pointer-events":"none"})}}),e.data.forEach((n,o)=>{const[r,l]=a[o],i=I(e,n,o);de(e,i,r,l,12*Math.sqrt(k(n)/K(e)),v(e,t[o]),n.value===null),M(e,r,l+24,q(n.label??n.id,12),i,{"font-size":13})}),V(e,"Depth = ancestry · node area = value · hollow = zero · dashed = missing")},Tt=e=>{const t=Me(e),s=ke(e.data);let a=30;t.forEach(({key:n,items:o},r)=>{const l=o.reduce((h,{p})=>h+k(p),0),i=s?580*l/s:580/t.length;M(e,a+i/2,31,q(n,14));let d=49;o.forEach(({p:h,i:p})=>{const u=me(N(e.theme.background,v(e,r),.35+.5*(p%3)/2)),m=l?235*k(h)/l:0,c=I(e,h,p),f=e.el("rect",{x:a,y:d,width:i,height:m,fill:v(e,r),opacity:.35+.5*(p%3)/2,stroke:e.theme.background,"stroke-width":2,class:"np-canopy-cell"},c);if(_(e,f,"leaf"),i>74&&m>36){const y=a+i/2,g=d+m/2;Q(e,`M${y-i*.3},${g+m*.3}Q${y-i*.25},${g-m*.42} ${y+i*.3},${g-m*.3}Q${y+i*.25},${g+m*.42} ${y-i*.3},${g+m*.3}M${y-i*.3},${g+m*.3}L${y+i*.3},${g-m*.3}`,c,{stroke:e.theme.background,"stroke-width":1.5,opacity:.28}),M(e,a+i/2,d+m/2-3,le(h,p,Math.floor(i/9)),c,{fill:u}),M(e,a+i/2,d+m/2+17,G(e,k(h)),c,{"font-size":13,fill:u})}m||D(e,a+8,300,3,c,{fill:e.theme.muted}),d+=m}),a+=i}),V(e,s?"Rectangle area = contribution to the total":"No positive contributions to allocate")},xt=e=>{const t=e.data.map((n,o)=>({p:n,i:o})).sort((n,o)=>k(o.p)-k(n.p)),s=ke(e.data);let a=0;A(e,168,40,168,291,void 0,{stroke:v(e),"stroke-width":3}),t.forEach(({p:n,i:o},r)=>{const l=52+r*230/Math.max(1,e.data.length-1),i=245*k(n)/K(e),d=I(e,n,o);M(e,150,l+5,le(n,o),d,{"text-anchor":"end"}),kt(e,d,168,l,i,v(e,r)),A(e,168,l,168+Math.max(12,i),l,d,{stroke:"transparent","stroke-width":24,class:"np-hit-area"}),a+=k(n);const h=454+(s?a/s:0)*108;D(e,h,l,6,d,{fill:v(e,2)}),M(e,622,l+5,s?`${Math.round(a/s*100)}%`:"—",d,{"text-anchor":"end","font-size":13})}),M(e,522,26,"Cumulative share",void 0,{"font-size":13}),M(e,290,26,"Amount",void 0,{"font-size":13}),A(e,168,302,413,302),[0,.5,1].forEach(n=>{A(e,168+245*n,302,168+245*n,306),M(e,168+245*n,323,G(e,K(e)*n),void 0,{"font-size":12})}),V(e,"Frond length = ranked amount · spores = cumulative %")},It=e=>{const t=ke(e.data),s=e.data.map(i=>t?k(i)/t*120:0),a=s.map(Math.floor);let n=t?120-a.reduce((i,d)=>i+d,0):0;const o=s.map((i,d)=>({i:d,v:i-a[d]})).sort((i,d)=>d.v-i.v);for(let i=0;i<n;i++)a[o[i].i]++;const r=j(e,"sunflower");r&&(D(e,206,169,85,void 0,{fill:"#493b25"}),H(e,"sunflower",void 0,46,9,320,320));let l=0;e.data.forEach((i,d)=>{const h=I(e,i,d);for(let p=0;p<a[d];p++,l++){const u=l*2.399963229728653,m=(r?7.05:10.7)*Math.sqrt(l+.5);D(e,206+Math.cos(u)*m,169+Math.sin(u)*m,4.5,h,{fill:F(e,v(e,d)),...r?{stroke:v(e,d),"stroke-width":2}:{}});const c=206+Math.cos(u)*m,f=169+Math.sin(u)*m;if(r){H(e,"seed",h,c-4.5,f-3.1,9,6.2,{transform:`rotate(${u*180/Math.PI} ${c} ${f})`},v(e,d));continue}Q(e,`M${c-2},${f}Q${c},${f-2} ${c+2},${f}`,h,{stroke:e.theme.background,"stroke-width":.6,opacity:.6,"pointer-events":"none"})}D(e,383,51+d*30,5,h,{fill:v(e,d)}),M(e,397,56+d*30,`${le(i,d,15)} · ${t?(100*k(i)/t).toFixed(1):"0"}%`,h,{"text-anchor":"start"})}),V(e,"120 dots, rounded shares · exact values in the inspector")},Dt=e=>{const t=B(e.data.flatMap(s=>[s.baseline,...s.value===null?[]:[s.value]]),150,596,!0);Ee(e,t),e.data.forEach((s,a)=>{const n=52+a*210/Math.max(1,e.data.length-1),o=I(e,s,a);M(e,137,n+5,le(s,a),o,{"text-anchor":"end"});const r=t.at(s.baseline),l=D(e,r,n,6,o,{fill:e.theme.background,stroke:v(e),"stroke-width":2});if(s.value!==null){const i=t.at(s.value),d=j(e,"leaf"),h=Q(e,`M${r},${n}Q${(r+i)/2},${n-12} ${i},${n}Q${(r+i)/2},${n+12} ${r},${n}`,o,{fill:v(e,s.value>=s.baseline?0:3),opacity:.3});d&&Math.abs(i-r)>1?(h.setAttribute("opacity","0"),H(e,"leaf",o,Math.min(r,i),n-18,Math.abs(i-r),36,{...i<r?{transform:`translate(${r+i} 0) scale(-1 1)`}:{}}),A(e,r,n,i,n,o,{stroke:e.theme.background,"stroke-width":1,opacity:.6}),se(e,o,i,n,10)):_(e,h,"leaf"),D(e,i,n,6,o,{fill:v(e,s.value>=s.baseline?0:3)}),o.append(l),r===i&&D(e,i,n,2.5,o,{fill:v(e)})}}),V(e,"Hollow = before · solid = after · one shared numeric axis")},Rt=e=>{const t=e.data.length,s=[320,173],a=105,n=e.data.map((o,r)=>C(...s,a*Math.min(1,k(o)/(o.target??e.options.target??100)),-Math.PI/2+r*ie/t));for(let o=0;o<t;o++){const r=-Math.PI/2+o*ie/t,l=C(320,173,a,r),i=C(320,173,a*.72,r-.32),d=C(320,173,a*.72,r+.32);Q(e,`M320,173Q${i} ${l}Q${d} 320,173`,void 0,{stroke:v(e,o),fill:F(e,v(e,o)),"fill-opacity":.035,"stroke-width":1,opacity:.25,"pointer-events":"none"});for(const h of[-.09,.09]){const p=C(320,173,a*.55,r+h),u=C(320,173,a*.91,r);Q(e,`M320,173Q${p} ${u}`,void 0,{stroke:v(e,o),"stroke-width":.6,opacity:.13,"pointer-events":"none"})}}[.25,.5,.75,1].forEach(o=>e.el("polygon",{points:Array.from({length:t},(r,l)=>C(320,173,a*o,-Math.PI/2+l*ie/t).join(",")).join(" "),fill:"none",stroke:e.theme.grid,"stroke-dasharray":o===1?"5 4":"1 0"})),e.data.every(o=>o.value!==null)&&e.el("polygon",{points:n.map(o=>o.join(",")).join(" "),fill:v(e),"fill-opacity":.18,stroke:v(e),"stroke-width":2}),e.data.forEach((o,r)=>{const l=-Math.PI/2+r*ie/t,i=C(320,173,a,l),d=C(320,173,a+35,l),h=I(e,o,r);A(e,320,173,...i,h),D(e,...n[r],6,h,{fill:o.value===null?e.theme.grid:v(e,r)}),M(e,d[0],d[1],le(o,r,12),h,{"font-size":13}),M(e,d[0],d[1]+17,o.value===null?"No data":`${Math.round(k(o)/(o.target??e.options.target??100)*100)}%`,h,{"font-size":13})})},nt=e=>t=>{const s=B(t.data.flatMap(a=>[a.low,a.high]),150,596);Ee(t,s),t.data.forEach((a,n)=>{const o=54+n*210/Math.max(1,t.data.length-1),r=I(t,a,n),l=s.at(a.low),i=s.at(a.high);if(M(t,136,o+5,le(a,n),r,{"text-anchor":"end"}),A(t,l,o,i,o,r,{stroke:v(t,n),"stroke-width":3}),A(t,l,o-9,l,o+9,r,{stroke:v(t,n)}),A(t,i,o-9,i,o+9,r,{stroke:v(t,n)}),e){const d=s.at(a.q1),h=s.at(a.q3),p=Q(t,`M${d},${o}Q${d},${o-17} ${(d+h)/2},${o-17}Q${h},${o-17} ${h},${o}Q${h},${o+17} ${(d+h)/2},${o+17}Q${d},${o+17} ${d},${o}`,r,{fill:v(t,n),"fill-opacity":.35});j(t,"pod")&&h>d?(p.setAttribute("opacity","0"),H(t,"pod",r,d,o-22,h-d,44),A(t,d,o-24,d,o+24,r,{stroke:v(t,n),"stroke-width":1.2}),A(t,h,o-24,h,o+24,r,{stroke:v(t,n),"stroke-width":1.2}),se(t,r,s.at(k(a)),o,25)):_(t,p,"glass"),A(t,s.at(k(a)),o-25,s.at(k(a)),o+25,r,{stroke:t.theme.ink,"stroke-width":2})}else{const d=i-l;if(d>4){Q(t,`M${l},${o}Q${l+d*.3},${o-4} ${i},${o}Q${l+d*.4},${o+4} ${l},${o}Z`,r,{fill:F(t,v(t,n),"stone"),stroke:"none"});for(let h=0;h<7;h++){const p=(h+1)/8,u=l+d*p,m=Math.min(11,d/14),c=(h%2?-1:1)*(9+3*Math.sin(h*2));Q(t,`M${u-m},${o}C${u},${o} ${u-m*.3},${o+c*.75} ${u+m*.35},${o+c}Q${u+m*.8},${o+c*.8} ${u+m*.55},${o+c*.6}L${u+m*.24},${o+c*.4}Q${u+m},${o+c*.55} ${u+m},${o+c*.2}Q${u+m*.5},${o-1} ${u-m},${o}Z`,r,{fill:F(t,v(t,n),"stone"),stroke:"none",class:"np-coral-branch"})}}a.value!==null?de(t,r,s.at(a.value),o,7,v(t,n)):M(t,(l+i)/2,o-14,"No estimate",r,{"font-size":12})}}),V(t,e?"Whiskers = min/max · pod = Q1–Q3 · line = median":"Branch = supplied low/high range · polyp = central estimate")},Ht=e=>{const t=B(e.data.flatMap(n=>[n.low,n.high]),65,598),s=e.data.map(n=>k(n)/(n.high-n.low)),a=B([0,...s],280,65,!0);fe(e,t,a,"Bin interval","Frequency density"),e.data.forEach((n,o)=>{const r=t.at(n.low),l=t.at(n.high),i=a.at(s[o]),d=I(e,n,o),h=e.el("rect",{x:r,y:i,width:l-r,height:280-i,fill:v(e,o),opacity:.6,stroke:e.theme.background,class:"np-bin"},d);_(e,h,"glass");for(let p=0;p<3;p++){const u=r+(p+1)*(l-r)/4,m=i+18+p*7;m+12<280&&Q(e,`M${u},${m-6}Q${u-5},${m+2} ${u},${m+5}Q${u+5},${m+2} ${u},${m-6}`,d,{fill:e.theme.background,stroke:"none",opacity:.45})}l-r>35&&M(e,(r+l)/2,i-8,G(e,k(n)),d,{"font-size":13})})},Ot=e=>{const t=B(e.data.map(n=>n.x),85,572),s=B(e.data.filter(n=>n.value!==null).map(k).concat([0]),264,62,!0),a=Math.max(0,...e.data.map(n=>n.weight??1))||1;fe(e,t,s,"x","Value"),e.data.forEach((n,o)=>{const r=I(e,n,o),l=t.at(n.x),i=n.value===null?345:s.at(n.value);de(e,r,l,i,23*Math.sqrt((n.weight??1)/a),v(e,0),n.value===null)}),[.25,.5,1].forEach((n,o)=>{const r=365+o*84;D(e,r,24,23*Math.sqrt(n),void 0,{fill:"none",stroke:v(e)}),M(e,r+27,28,be(a*n),void 0,{"text-anchor":"start","font-size":12})}),M(e,348,28,e.options.weightLabel??"Weight",void 0,{"text-anchor":"end","font-size":12}),e.data.some(n=>n.value===null)&&M(e,20,350,"Missing",void 0,{"text-anchor":"start","font-size":12})},Ut=e=>{const n=e.data.length;[.25,.5,1].forEach(o=>D(e,320,175,116*Math.sqrt(o),void 0,{fill:"none",stroke:e.theme.grid})),["N","E","S","W"].forEach((o,r)=>{const l=C(320,175,143,-Math.PI/2+r*Math.PI/2);M(e,l[0],l[1]+4,o)}),e.data.forEach((o,r)=>{const l=o.angle*Math.PI/180-Math.PI/2,i=ie/n*.86,d=I(e,o,r),h=116*Math.sqrt(k(o)/K(e));if(h>0){const p=Q(e,At(320,175,h,l-i/2,l+i/2),d,{fill:F(e,v(e,r)),stroke:e.theme.background,opacity:.8,class:"np-wind-sector","data-radius":h});_(e,p,"water")}else de(e,d,...C(320,175,10,l),0,v(e,r),o.value===null)}),[.25,.5,1].forEach((o,r)=>{A(e,490,119+r*29,508,119+r*29,void 0,{stroke:e.theme.muted}),M(e,517,123+r*29,G(e,K(e)*o),void 0,{"text-anchor":"start","font-size":13})}),M(e,490,92,"Ring values",void 0,{"text-anchor":"start","font-size":13}),V(e,"Sector area = value · clockwise from north")},Ft=e=>{const t=Me(e),s=B(e.data.map(o=>o.position),150,596),a=220/t.length,n=K(e);M(e,596,30,`Each ridge: 0–${G(e,n)}`,void 0,{"text-anchor":"end","font-size":13}),t.forEach(({key:o,items:r},l)=>{const i=65+l*a;M(e,137,i+20,q(o,14),void 0,{"text-anchor":"end"}),A(e,150,i+a-10,596,i+a-10);let d=[];const h=()=>{if(d.length){const p=e.el("g",{class:"np-series","data-series":o}),u=Q(e,`M${d[0].x},${i+a-10}L${d.map(m=>`${m.x},${m.y}`).join("L")}L${d.at(-1).x},${i+a-10}Z`,p,{fill:v(e,l),"fill-opacity":.3,stroke:v(e,l)});_(e,u,"sand")}d=[]};r.forEach(({p,i:u})=>{const m=s.at(p.position),c=i+a-10-(a-20)*k(p)/n;p.value===null?h():d.push({x:m,y:c});const f=I(e,p,u);D(e,m,c,4,f,{fill:p.value===null?e.theme.grid:v(e,l)})}),h()}),Ee(e,s),V(e,"Position = bin location · all ridge heights share one scale")},Wt=e=>{let t=0;const s=e.data.map(o=>{const r=t;return t+=k(o),{start:r,end:t}}),a=B([0,...s.flatMap(o=>[o.start,o.end])],281,53,!0),n=500/e.data.length;M(e,68,29,e.options.unit?`Balance (${e.options.unit})`:"Running balance",void 0,{"text-anchor":"start","font-size":13}),[...new Set([a.a,0,a.b])].forEach(o=>{A(e,68,a.at(o),605,a.at(o)),M(e,61,a.at(o)+5,be(o),void 0,{"text-anchor":"end","font-size":12})}),e.data.forEach((o,r)=>{const l=I(e,o,r),i=85+r*n,d=a.at(s[r].start),h=a.at(s[r].end),p=e.el("rect",{x:i,y:Math.min(d,h),width:n*.68,height:Math.abs(h-d),rx:2,fill:v(e,k(o)>=0?0:3),opacity:.75,class:"np-waterfall-step"},l);_(e,p,"ice"),Math.abs(h-d)>24&&Q(e,`M${i+n*.18},${Math.min(d,h)+4}l${n*.12},9l${-n*.1},8`,l,{stroke:e.theme.background,opacity:.55}),r<e.data.length-1&&A(e,i+n*.68,h,i+n,h,void 0,{"stroke-dasharray":"3 3"}),M(e,i+n*.34,304,le(o,r,8),l,{"font-size":13}),M(e,i+n*.34,Math.min(d,h)-8,G(e,k(o)),l,{"font-size":12})}),V(e,`Signed steps from zero · ending balance ${G(e,t)}`)},jt=e=>{const t=Me(e),s=t[0].items.map(({p:l})=>l.position),a=B(s,65,598),n=s.map((l,i)=>t.reduce((d,h)=>d+k(h.items[i].p),0)),o=B([0,...n],275,50,!0);let r=s.map(()=>0);fe(e,a,o,"Position","Stacked total"),t.forEach(({key:l,items:i},d)=>{const h=233+d*98;A(e,h-17,22,h-5,22,void 0,{stroke:v(e,d),"stroke-width":4}),M(e,h,26,q(l,10),void 0,{"text-anchor":"start","font-size":12});const p=r.map((c,f)=>c+k(i[f].p)),u=e.el("g",{class:"np-series","data-series":l}),m=Q(e,`M${s.map((c,f)=>`${a.at(c)},${o.at(p[f])}`).join("L")}L${s.map((c,f)=>`${a.at(c)},${o.at(r[f])}`).reverse().join("L")}Z`,u,{fill:v(e,d),"fill-opacity":.65,stroke:e.theme.background});_(e,m,"sand"),i.forEach(({p:c,i:f},y)=>{const g=I(e,c,f);D(e,a.at(c.position),o.at(r[y]+k(c)/2),5,g,{fill:v(e,d),stroke:e.theme.background})}),r=p})},rt=e=>t=>{const s=[...new Set(t.data.map(p=>p.source))],a=[...new Set(t.data.map(p=>p.destination))],n=ke(t.data),o=30,r=235-o*(Math.max(s.length,a.length)-1),l=n?r/n:0,i=(p,u)=>{let m=49;return new Map(p.map(c=>{const f=t.data.filter(g=>g[u]===c).reduce((g,w)=>g+k(w),0),y={y:m,height:f*l,offset:0,amount:f};return m+=y.height+o,[c,y]}))},d=i(s,"source"),h=i(a,"destination");t.data.forEach((p,u)=>{const m=d.get(p.source),c=h.get(p.destination),f=k(p)*l,y=m.y+m.offset,g=c.y+c.offset,w=I(t,p,u);if(m.offset+=f,c.offset+=f,f){const b=Q(t,`M165,${y}C310,${y} 330,${g} 475,${g}L475,${g+f}C330,${g+f} 310,${y+f} 165,${y+f}Z`,w,{fill:v(t,e?u:s.indexOf(p.source)),stroke:"none","fill-opacity":.5,class:"np-flow-ribbon"});_(t,b,"water")}else Q(t,`M165,${y}C310,${y} 330,${g} 475,${g}`,w,{stroke:t.theme.muted,"stroke-dasharray":"3 4"})});for(const[p,u,m]of[[d,149,"end"],[h,491,"start"]])p.forEach((c,f)=>{t.el("rect",{x:m==="end"?157:475,y:c.y,width:8,height:c.height,fill:t.theme.ink,opacity:.4}),M(t,u,c.y+c.height/2-3,q(f,15),void 0,{"text-anchor":m,"font-size":13}),M(t,u,c.y+c.height/2+15,G(t,c.amount),void 0,{"text-anchor":m,"font-size":12,fill:t.theme.muted})});V(t,e?"One source · ribbon width = allocated quantity":"Source → destination · ribbon widths conserve supplied flow")},Zt=e=>{const t=[...new Set(e.data.flatMap(a=>[a.source,a.destination]))],s=a=>C(320,172,118,-Math.PI/2+t.indexOf(a)*ie/t.length);e.data.forEach((a,n)=>{const o=s(a.source),r=s(a.destination),l=I(e,a,n),i=[(o[0]+r[0])/2,(o[1]+r[1])/2],d=r[0]-o[0],h=r[1]-o[1],p=Math.hypot(d,h)||1,u=[i[0]-h/p*25,i[1]+d/p*25];Q(e,`M${o}Q${u} ${r}`,l,{stroke:"transparent","stroke-width":14,class:"np-hit-area"}),Q(e,`M${o}Q${u} ${r}`,l,{stroke:k(a)?v(e,n):e.theme.muted,"stroke-width":k(a)?10*k(a)/K(e):1,opacity:.7,"stroke-dasharray":a.value===null?"2 5":a.value===0?"7 5":"none",class:"np-thread"}),k(a)>0&&Q(e,`M${o}Q${u} ${r}`,l,{stroke:e.theme.background,"stroke-width":10*k(a)/K(e)*.18,opacity:.45,"pointer-events":"none"});const m=.7,c=(1-m)**2*o[0]+2*m*(1-m)*u[0]+m*m*r[0],f=(1-m)**2*o[1]+2*m*(1-m)*u[1]+m*m*r[1],y=Math.atan2((1-m)*(u[1]-o[1])+m*(r[1]-u[1]),(1-m)*(u[0]-o[0])+m*(r[0]-u[0]));Q(e,`M${C(c,f,7,y+2.5)}L${c},${f}L${C(c,f,7,y-2.5)}`,l,{stroke:v(e,n),"stroke-width":2})}),t.forEach(a=>{const[n,o]=s(a);D(e,n,o,9,void 0,{fill:e.theme.background,stroke:v(e),"stroke-width":2});const r=n-320,l=o-172,i=Math.hypot(r,l);M(e,n+r/i*25,o+l/i*25+4,q(a,11),void 0,{"font-size":13,"text-anchor":r>1?"start":r<-1?"end":"middle"})}),V(e,"Width = weight · long dash = zero · short dash = missing")},Vt=e=>{const t=k(e.data[0]),s=225/e.data.length;e.data.forEach((a,n)=>{const o=I(e,a,n),r=t?300*k(a)/t:0,l=49+n*s;if(r>0){const d=e.el("rect",{x:350-r/2,y:l,width:r,height:s-5,rx:Math.min(8,r/6),fill:F(e,v(e,n)),opacity:.85,class:"np-funnel-stage"},o);_(e,d,"glass"),A(e,350-r*.44,l+3,350+r*.44,l+3,o,{stroke:e.theme.background,"stroke-width":1,opacity:.5,"pointer-events":"none"})}r||de(e,o,350,l+s/2,0,v(e,n)),M(e,175,l+s/2+4,le(a,n),o,{"text-anchor":"end"}),M(e,530,l+s/2,G(e,k(a)),o,{"text-anchor":"start"});const i=n?k(e.data[n-1]):t;M(e,530,l+s/2+17,i?`${Math.round(k(a)/i*100)}% kept`:"—",o,{"text-anchor":"start","font-size":12})}),V(e,"Width = count · retention compares adjacent stages")},Bt=e=>{const t=Me(e),s=B(e.data.map(a=>a.position),150,596);Ee(e,s),t.forEach(({key:a,items:n},o)=>{const r=58+o*200/Math.max(1,t.length-1);M(e,135,r+5,q(a,14),void 0,{"text-anchor":"end"}),A(e,150,r,596,r),n.forEach(({p:l,i})=>{const d=s.at(l.position),h=I(e,l,i);j(e,"firefly")&&l.value!==null?(D(e,d,r,20,h,{fill:"transparent",class:"np-hit-area"}),H(e,"firefly",h,d-20,r-21,40,34,{opacity:.35+.65*k(l)/K(e)}),D(e,d,r,2.5,h,{fill:v(e,o),stroke:e.theme.background,"stroke-width":.7}),se(e,h,d,r,23)):(D(e,d,r,12,h,{fill:v(e,o),opacity:l.value===null?.08:.08+.17*k(l)/K(e)}),Q(e,`M${d},${r-1}Q${d-10},${r-10} ${d-6},${r-1}Q${d-3},${r+1} ${d},${r-1}Q${d+10},${r-10} ${d+6},${r-1}Q${d+3},${r+1} ${d},${r-1}`,h,{stroke:v(e,o),"stroke-width":.6,opacity:.4,fill:"none","pointer-events":"none"}),D(e,d,r,4.5,h,{fill:l.value===null?e.theme.grid:v(e,o),opacity:l.value===null?1:.25+.75*k(l)/K(e)}))})}),V(e,`${e.options.xLabel??"Event position"} · opacity = magnitude${e.options.unit?` (${e.options.unit})`:""}`)},Gt=e=>{const{xs:t,ys:s}=at(e);fe(e,t,s,"x coordinate","y coordinate"),e.data.forEach((a,n)=>{const o=t.at(a.x),r=s.at(a.y);if(n){const i=e.data[n-1],d=[t.at(i.x),s.at(i.y)],h=o-d[0],p=r-d[1],u=Math.atan2(p,h),m=(d[0]+o)/2,c=(d[1]+r)/2;A(e,d[0],d[1],o,r,void 0,{stroke:v(e),"stroke-width":2,"stroke-dasharray":"4 4"});const f=e.el("g",{transform:`translate(${m} ${c}) rotate(${u*180/Math.PI+90})`,"aria-hidden":"true","pointer-events":"none"});H(e,"swallow",f,-17,-13,34,27)||Be(e,f,0,0,v(e),7)}const l=I(e,a,n);de(e,l,o,r,16*Math.sqrt(k(a)/K(e)),v(e,n)),M(e,o,r-20,String(n+1),l),j(e,"swallow")&&se(e,l,o,r,19)})},_t=e=>{const t=Me(e),s=B(e.data.filter(a=>a.value!==null).map(k).concat([0]),150,596);Ee(e,s),t.forEach(({key:a,items:n},o)=>{const r=64+o*195/Math.max(1,t.length-1),l=[];M(e,134,r+5,q(a,13),void 0,{"text-anchor":"end"}),A(e,150,r,596,r),n.forEach(({p:i,i:d})=>{const h=i.value===null?610:s.at(i.value);let p=0;const u=j(e,"starling"),m=u?[0,-17,17,-30,30]:[0,-10,10,-20,20,-30,30];for(const f of m)if(p=f,!l.some(y=>Math.hypot(h-y.x,r+f-y.y)<(u?21:11)))break;l.push({x:h,y:r+p});const c=I(e,i,d);u&&i.value!==null?(H(e,"starling",c,h-12,r+p-9,24,18),D(e,h,r+p,2,c,{fill:v(e,o),stroke:e.theme.background,"stroke-width":.6}),se(e,c,h,r+p,15)):Be(e,c,h,r+p,i.value===null?e.theme.muted:v(e,o)),D(e,h,r+p,9,c,{fill:"transparent",class:"np-hit-area"})})}),V(e,"Each bird = one reading · vertical jitter only separates marks")},Yt=e=>{const t=e.data.map((r,l)=>({p:r,i:l})).sort((r,l)=>k(r.p)-k(l.p)),s=B(e.data.map(k),65,598),a=B([0,1],278,53);fe(e,s,a,"Sample value","Fraction at or below value");let n="M65,278";[...new Set(t.map(({p:r})=>k(r)))].forEach(r=>{const l=s.at(r),i=t.filter(({p:d})=>k(d)<=r).length/t.length;n+=`H${l}V${a.at(i)}`}),n+="H598",Q(e,n,void 0,{stroke:v(e),"stroke-width":2.5,class:"np-ecdf"}),t.forEach(({p:r,i:l})=>{const i=t.filter(({p:u})=>k(u)<=k(r)).length/t.length,d=I(e,r,l),h=s.at(k(r)),p=a.at(i);j(e,"pebble")?(H(e,l%2?"pebble-light":"pebble",d,h-13,p-8,26,16),D(e,h,p,2.1,d,{fill:v(e,l),stroke:e.theme.background,"stroke-width":.8}),se(e,d,h,p,16)):(e.el("ellipse",{cx:h,cy:p,rx:6,ry:4.5,fill:F(e,v(e,l),"stone"),stroke:e.theme.background,"stroke-width":.6},d),Q(e,`M${h-3},${p+1}Q${h-1},${p-2} ${h+3},${p-1}`,d,{stroke:e.theme.background,"stroke-width":.6,opacity:.65,"pointer-events":"none"})),D(e,h,p,14,d,{fill:"transparent",class:"np-hit-area"})})},Jt=e=>{const t=e.options.cycleLength??12,s=Math.max(1,Math.floor(e.data.at(-1).position/t)+1),a=n=>{const o=n/t,r=22+o/s*100;return C(275,175,r,-Math.PI/2+o*ie)};Q(e,Array.from({length:321},(n,o)=>`${o?"L":"M"}${a(s*t*o/320)}`).join(""),void 0,{stroke:e.theme.grid,"stroke-width":2});for(let n=12;n<s*12;n++){const o=n/12*t,[r,l]=a(o),[i,d]=a(Math.max(0,o-t)),h=a(o+t/12),p=a(o+t/12-t),u=Array.from({length:9},(c,f)=>`L${a(o+t/12*f/8)}`).join(""),m=Array.from({length:9},(c,f)=>`L${a(o+t/12-t/12*f/8-t)}`).join("");Q(e,`M${r},${l}${u}Q${(h[0]+p[0])/2+5},${(h[1]+p[1])/2-5} ${p}${m}Q${(i+r)/2+5},${(d+l)/2-5} ${r},${l}Z`,void 0,{fill:F(e,v(e,Math.floor(o/t)),"stone"),"fill-opacity":.09,stroke:"none","pointer-events":"none",class:"np-shell-chamber","aria-hidden":"true"}),Q(e,`M${i},${d}Q${(i+r)/2+5},${(d+l)/2-5} ${r},${l}`,void 0,{stroke:v(e,Math.floor(o/t)),"stroke-width":.9,opacity:.22,"pointer-events":"none"})}for(let n=0;n<s;n++){const[o,r]=a(n*t);M(e,o+20,r,`Turn ${n+1}`,void 0,{"text-anchor":"start","font-size":12})}e.data.forEach((n,o)=>{const r=I(e,n,o),[l,i]=a(n.position);de(e,r,l,i,10*Math.sqrt(k(n)/K(e)),v(e,Math.floor(n.position/t)),n.value===null)}),M(e,462,110,"One turn",void 0,{"text-anchor":"start"}),M(e,462,134,`${t} ${e.options.cycleUnit??"steps"}`,void 0,{"text-anchor":"start"}),M(e,462,171,"Dot area",void 0,{"text-anchor":"start"}),M(e,462,195,"= value",void 0,{"text-anchor":"start"}),V(e,"Angle = cycle position · each new turn retains chronology")},Kt=e=>{const t=[...new Set(e.data.map(o=>o.track))],s=t.length,a=Math.min(47,240/s),n=195;t.forEach((o,r)=>{M(e,n+(r+.5)*a,35,q(o,7),void 0,{"font-size":12}),M(e,n-15,53+(r+.5)*a,q(o,13),void 0,{"text-anchor":"end"})}),e.data.forEach((o,r)=>{const l=t.indexOf(o.label),i=t.indexOf(o.track),d=n+l*a,h=47+i*a,p=I(e,o,r),u=N(e.theme.background,k(o)>=0?"#367d9e":"#b5683e",o.value===null?.06:Math.abs(k(o))*.8);e.el("rect",{x:d,y:h,width:a-3,height:a-3,rx:4,fill:u,stroke:e.theme.grid,"stroke-width":.6},p),[0,1].forEach(m=>{const c=d+(m?a-9:6),f=h+(m?a-9:6);for(let y=0;y<6;y++){const g=y*ie/6,w=C(c,f,4,g),b=C(c,f,2.2,g),E=C(b[0],b[1],1.8,g-.65),T=C(b[0],b[1],1.8,g+.65);Q(e,`M${c},${f}L${w}M${E}L${b}L${T}`,p,{stroke:me(u),"stroke-width":.45,opacity:.3,"pointer-events":"none"})}}),a>=32&&M(e,d+(a-3)/2,h+a/2+3,o.value===null?"—":o.value.toFixed(1),p,{"font-size":12,fill:me(u)})}),[-1,-.5,0,.5,1].forEach((o,r)=>{const l=195+r*47;e.el("rect",{x:l,y:302,width:44,height:9,rx:2,fill:N(e.theme.background,o>=0?"#367d9e":"#b5683e",Math.abs(o)*.8),stroke:e.theme.grid}),M(e,l+22,328,String(o),void 0,{"font-size":12})})},Xt=e=>{const t=e.data.filter(n=>n.value!==null).map(k),s=B(t.concat([0]),65,598),a=B(t.concat([0]),275,55);fe(e,s,a,"Previous reading (t − 1)","Current reading (t)"),A(e,65,275,598,55,void 0,{stroke:e.theme.muted,"stroke-dasharray":"5 5"}),e.data.forEach((n,o)=>{const r=e.data[o-1],l=I(e,n,o),i=n.value!==null&&r?.value!==null&&r?.value!==void 0;if(D(e,i?s.at(r.value):158+o*11,i?a.at(n.value):348,i?6:3,l,{fill:i?v(e,o):e.theme.muted,"fill-opacity":.65}),i){const d=s.at(r.value),h=a.at(n.value);j(e,"ripple")?(H(e,"ripple",l,d-17,h-17,34,34,{opacity:.85}),se(e,l,d,h,20)):[8.5,11].forEach(p=>Q(e,`M${d-p},${h}A${p},${p} 0 0 1 ${d},${h-p}M${d+p},${h}A${p},${p} 0 0 1 ${d},${h+p}`,l,{stroke:v(e,o),"stroke-width":.65,opacity:.4,"pointer-events":"none","aria-hidden":"true"}))}}),M(e,72,353,"No pair",void 0,{"text-anchor":"start","font-size":12})},ea=e=>{const t=e.options.target??100,s=ke(e.data),a=Math.max(t,s),n=230;let o=285;const r=285-t/a*n;A(e,135,r,510,r,void 0,{stroke:e.theme.muted,"stroke-dasharray":"4 4"}),M(e,510,r-9,`Goal ${G(e,t)}`,void 0,{"text-anchor":"end"}),e.data.forEach((l,i)=>{const d=n*k(l)/a,h=I(e,l,i),p=210-i*11,u=e.el("rect",{x:290-p/2,y:o-d,width:p,height:d,rx:Math.min(9,d/4),fill:F(e,v(e,i),"stone"),stroke:e.theme.background,"stroke-width":2,opacity:.8,class:"np-cairn-stone"},h);_(e,u,"rock"),d===0&&de(e,h,160,305+i%2*10,0,v(e,i)),d>23&&M(e,290,o-d/2+5,le(l,i),h,{fill:me(N(e.theme.background,v(e,i),.64))}),o-=d}),M(e,480,160,G(e,s),void 0,{"font-size":27}),M(e,480,187,`${Math.round(s/t*100)}% of goal`),V(e,s>t?`${G(e,s-t)} beyond the shared goal`:`${G(e,t-s)} remaining · stone height = contribution`)},ta=e=>{const t={a:0,b:1440,at:s=>150+s/1440*446};[0,360,720,1080,1440].forEach(s=>{const a=t.at(s);A(e,a,42,a,282,void 0,{"stroke-dasharray":"3 5"}),M(e,a,306,`${String(s/60).padStart(2,"0")}:00`,void 0,{"font-size":13})}),e.data.forEach((s,a)=>{const n=59+a*210/Math.max(1,e.data.length-1),o=I(e,s,a),r=t.at(ne(s.start)),l=t.at(ne(s.end,!0));M(e,136,n+5,s.date,o,{"text-anchor":"end","font-size":13}),e.el("rect",{x:r,y:n-11,width:l-r,height:22,rx:11,fill:F(e,v(e,3)),opacity:.6},o);for(const i of[-1,1])Q(e,`M${r-3},${n+i*7}l-1,${i*2}M${r+3},${n+i*7}l1,${i*2}`,o,{stroke:v(e,3),"stroke-width":.8,opacity:.65,"pointer-events":"none"});D(e,r,n,5,o,{fill:v(e,3)}),D(e,l,n,5,o,{fill:v(e,1)}),M(e,(r+l)/2,n+5,`${((ne(s.end,!0)-ne(s.start))/60).toFixed(1)} h`,o,{"font-size":13,fill:me(N(e.theme.background,v(e,3),.6))})}),V(e,"Supplied opening → closing times · shared 24-hour scale")},aa=e=>{const t=[...new Set(e.data.map(h=>h.x))].sort((h,p)=>h-p),s=[...new Set(e.data.map(h=>h.y))].sort((h,p)=>h-p),{xs:a,ys:n}=at(e),o=B(e.data.map(k),0,1);fe(e,a,n,"x coordinate","y coordinate");const r=Math.min(...e.data.map(k)),l=Math.max(...e.data.map(k)),i=r===l?[]:[.25,.5,.75].map(h=>r+(l-r)*h),d=(h,p)=>e.data.find(u=>u.x===h&&u.y===p);if(e.options.detail!=="essential"){const h=a.at(t[0]),p=a.at(t.at(-1)),u=n.at(s.at(-1)),m=n.at(s[0]);e.el("rect",{x:h,y:u,width:p-h,height:m-u,fill:e.theme.grid,opacity:.4});const c=(f,y,g)=>{const w=[];return f.forEach((b,E)=>{const T=f[(E+1)%f.length],R=g?b.v>=y:b.v<=y,O=g?T.v>=y:T.v<=y;if(R&&w.push(b),R!==O){const ee=(y-b.v)/(T.v-b.v);w.push({x:b.x+ee*(T.x-b.x),y:b.y+ee*(T.y-b.y),v:y})}}),w};for(let f=0;f<s.length-1;f++)for(let y=0;y<t.length-1;y++){const g=[d(t[y],s[f]),d(t[y+1],s[f]),d(t[y+1],s[f+1]),d(t[y],s[f+1])];for(const w of[[g[0],g[1],g[2]],[g[0],g[2],g[3]]]){const b=w.map(E=>({x:a.at(E.x),y:n.at(E.y),v:o.at(k(E))}));for(let E=0;E<4;E++){const T=c(c(b,E/4,!0),(E+1)/4,!1);T.length>=3&&e.el("polygon",{points:T.map(R=>`${R.x},${R.y}`).join(" "),fill:N(e.theme.background,v(e,E),.35+E*.12),class:"np-isoband","data-low":E/4,"data-high":(E+1)/4})}}}}for(let h=0;h<s.length-1;h++)for(let p=0;p<t.length-1;p++){const u=[d(t[p],s[h]),d(t[p+1],s[h]),d(t[p+1],s[h+1]),d(t[p],s[h+1])];for(const m of[[u[0],u[1],u[2]],[u[0],u[2],u[3]]])i.forEach((c,f)=>{const y=[];for(let g=0;g<3;g++){const w=m[g],b=m[(g+1)%3];if(k(w)<c&&k(b)>=c||k(b)<c&&k(w)>=c){const E=(c-k(w))/(k(b)-k(w));y.push([a.at(w.x+E*(b.x-w.x)),n.at(w.y+E*(b.y-w.y))])}}y.length===2&&(A(e,...y[0],...y[1],void 0,{stroke:e.theme.background,"stroke-width":5,opacity:.9,"pointer-events":"none"}),A(e,...y[0],...y[1],void 0,{stroke:v(e,f),"stroke-width":2.5,class:"np-contour","data-level":c}))})}e.data.forEach((h,p)=>{const u=I(e,h,p);se(e,u,a.at(h.x),n.at(h.y),11),D(e,a.at(h.x),n.at(h.y),5,u,{fill:v(e),"fill-opacity":.25+.75*o.at(k(h)),stroke:e.theme.background})}),i.length?i.forEach((h,p)=>{const u=233+p*128;A(e,u,22,u+20,22,void 0,{stroke:v(e,p),"stroke-width":2.5}),M(e,u+26,26,G(e,h),void 0,{"text-anchor":"start","font-size":12})}):M(e,595,25,`Constant field: ${G(e,r)}`,void 0,{"text-anchor":"end","font-size":12})},oa=e=>{const t=[...new Set(e.data.map(n=>Math.floor(k(n)/10)))].sort((n,o)=>n-o),s=240/Math.max(1,t.length),a=j(e,"bamboo");a?H(e,"bamboo",void 0,174,29,23,267):A(e,185,35,185,289,void 0,{stroke:v(e),"stroke-width":5}),A(e,183,35,183,289,void 0,{stroke:e.theme.background,"stroke-width":.8,opacity:.6,"pointer-events":"none"}),t.forEach((n,o)=>{const r=49+o*s;M(e,164,r+5,String(n),void 0,{"font-size":17}),A(e,177,r+s*.5,193,r+s*.5,void 0,{stroke:v(e),"stroke-width":3}),Q(e,`M181,${r+s*.5-3}h9M185,${r+s*.5}q17-15 26-12q-8 14-26 12`,void 0,{stroke:v(e),"stroke-width":.7,fill:v(e),"fill-opacity":.3,"pointer-events":"none"});const l=e.data.map((i,d)=>({p:i,i:d})).filter(({p:i})=>Math.floor(k(i)/10)===n).sort((i,d)=>k(i.p)-k(d.p));l.forEach(({p:i,i:d},h)=>{const p=I(e,i,d);if(a){const u=211+h*Math.min(22,389/Math.max(1,l.length-1));H(e,"leaf",p,u-11,r-11,22,24),D(e,u,r,8,p,{fill:e.theme.background,opacity:.82}),se(e,p,u,r,13)}M(e,211+h*Math.min(22,389/Math.max(1,l.length-1)),r+5,String(k(i)%10),p,{"font-size":17,fill:e.theme.ink})})}),V(e,"Stem | leaf: 4 | 7 means 47 · repeated leaves are distinct")},na={honeycomb:qt,mycelium:Zt,"root-tree":Qt,canopy:Tt,fern:xt,phyllotaxis:It,"leaf-veins":Dt,lotus:Rt,"petal-box":nt(!0),raincloud:Ht,dew:Ot,"wind-rose":Ut,dune:Ft,glacier:Wt,sediment:jt,delta:rt(!0),estuary:rt(!1),pitcher:Vt,firefly:Bt,migration:Gt,murmuration:_t,"coral-range":nt(!1),pebble:Yt,nautilus:Jt,frost:Kt,echo:Xt,cairn:ea,daylight:ta,isobar:aa,bamboo:oa},oe=Math.PI*2,te=-Math.PI/2,st=e=>Math.abs(e)>=1e12||e!==0&&Math.abs(e)<.001?e.toExponential(2):String(Math.round(e*100)/100),P=(e,t)=>e.theme.colors[t%e.theme.colors.length],W=(e,t,s,a,n,o={})=>e.el("text",{x:t,y:s,fill:e.theme.ink,"font-size":11,"text-anchor":"middle",...o},n,a),re=(e,t)=>S(e,320,339,t,{"font-size":10});function ra(e,t,s,a,n){e.el("circle",{cx:t,cy:s,r:a,fill:P(e,3),opacity:.8},n);for(let o=0;o<8;o++){const r=C(t,s,a+5,o*oe/8),l=C(t,s,a+9,o*oe/8);e.el("path",{d:`M${r}L${l}`,stroke:P(e,3),"stroke-width":1.3,opacity:.65},n)}}function sa(e,t,s){return Array.from({length:10},(a,n)=>`${n?"L":"M"}${C(e,t,n%2?s*.43:s,te+n*Math.PI/5)}`).join("")+"Z"}function it(e,t,s,a,n,o){if(o-n>=oe-1e-9)return`M${e},${t-a}A${a},${a} 0 1 1 ${e},${t+a}A${a},${a} 0 1 1 ${e},${t-a}M${e},${t-s}A${s},${s} 0 1 0 ${e},${t+s}A${s},${s} 0 1 0 ${e},${t-s}Z`;const r=o-n>Math.PI?1:0;return`M${C(e,t,a,n)}A${a},${a} 0 ${r} 1 ${C(e,t,a,o)}L${C(e,t,s,o)}A${s},${s} 0 ${r} 0 ${C(e,t,s,n)}Z`}function lt(e,t,s,a,n,o,r=6,l="seed"){const i=e.el("g",{transform:`translate(${s} ${a}) rotate(-28) scale(1 .68)`},t);if(e.el("circle",{r,fill:e.theme.grid},i),n>=.9999999999)e.el("circle",{r,fill:o},i);else if(n>0){const d=te+n*oe;e.el("path",{d:`M0,0L${C(0,0,r,te)}A${r},${r} 0 ${n>.5?1:0} 1 ${C(0,0,r,d)}Z`,fill:o},i)}if(e.el("path",{d:`M${-r*.55},0L${r*.55},0`,stroke:e.theme.background,"stroke-width":.7,opacity:.5},i),e.el("path",{d:`M${-r*.75},0Q0,${-r*.65} ${r*.75},0M${-r*.75},0Q0,${r*.65} ${r*.75},0`,fill:"none",stroke:e.theme.background,"stroke-width":.45,opacity:.35,"pointer-events":"none"},i),j(e,l)){const d=e.el("g",{},i),h=`${e.id}-seed-fill-${e.svg.querySelectorAll("clipPath").length}`,p=e.el("defs"),u=e.el("clipPath",{id:h},p);n>=.9999999999?e.el("circle",{r},u):n>0&&e.el("path",{d:`M0,0L${C(0,0,r,te)}A${r},${r} 0 ${n>.5?1:0} 1 ${C(0,0,r,te+n*oe)}Z`},u),d.setAttribute("clip-path",`url(#${h})`),H(e,l,d,-r,-r,r*2,r*2,{},l==="seed"?o:void 0)}e.el("ellipse",{cx:-r*.65,cy:0,rx:r*.09,ry:r*.2,fill:e.theme.ink,opacity:.2,"pointer-events":"none"},i)}const ia=e=>{const t=Ie(e.data,e.options.unitsPerMark,40);S(e,42,37,"A HARVEST OF SMALL THINGS",{"text-anchor":"start","font-size":10,"letter-spacing":1.8}),e.data.forEach((s,a)=>{const n=Math.min(56,252/e.data.length),o=67+a*n,r=e.el("g");e.el("rect",{x:30,y:o-13,width:580,height:n-4,rx:7,fill:P(e,a),opacity:.055},r),W(e,43,o+7,q(Y(s,a),16),r,{"text-anchor":"start","font-size":11});const l=(s.value??0)/t,i=Math.floor(l),d=Math.max(0,l-i),h=Math.min(40,i+(d>0?1:0));for(let p=0;p<h;p++)lt(e,r,172+p%20*19,o+Math.floor(p/20)*16,p<i?1:d,P(e,a),j(e,"seed")?8:6);h||W(e,175,o+7,s.value===null?"Not recorded":"No seeds yet",r,{"text-anchor":"start",fill:e.theme.muted,"font-size":10}),W(e,596,o+7,e.format(s.value),r,{"text-anchor":"end","font-size":12}),e.mark(r,s,a)}),re(e,`1 seed = ${e.format(t)} · partial seed area = fractional unit${t!==(e.options.unitsPerMark??1)?" · bundled to fit":""}`)},la=e=>{const t=e.data[0],s=t.target??e.options.target??100,a=192,n=57,o=133,r=230,l=r*Math.min(1,(t.value??0)/s),i=e.el("g"),d=e.el("defs"),h=e.el("clipPath",{id:`${e.id}-column`},d);e.el("rect",{x:a,y:n,width:o,height:r,rx:10},h),e.el("rect",{x:a,y:n,width:o,height:r,rx:10,fill:e.theme.grid,opacity:.5},i);const p=e.el("rect",{x:a,y:n+r-l,width:o,height:l,fill:F(e,P(e,0),"water"),opacity:.85,"clip-path":`url(#${e.id}-column)`,class:"np-water-level"},i);e.el("g",{"clip-path":`url(#${e.id}-column)`},i).append(p),_(e,p,"glass"),e.el("path",{d:`M${a+6} ${n+4}H${a+o-6}M${a+6} ${n+r-4}H${a+o-6}`,stroke:e.theme.muted,"stroke-width":.8,opacity:.5,"pointer-events":"none"},i),t.value!==null&&e.el("line",{x1:a,x2:a+o,y1:n+r-l,y2:n+r-l,stroke:P(e,1),"stroke-width":2},i),e.el("rect",{x:a,y:n,width:o,height:r,rx:10,fill:"none",stroke:P(e,0),"stroke-width":1.5,opacity:.5},i);for(let c=0;c<=4;c++){const f=n+r-c*r/4;e.el("path",{d:`M${a-9} ${f}h9`,stroke:e.theme.muted,opacity:.6}),S(e,a-18,f+4,e.format(s*c/4),{"text-anchor":"end","font-size":10})}[...e.options.thresholds??[]].sort((c,f)=>f.value-c.value).forEach((c,f)=>{const y=n+r*(1-c.value/s),g=106+f*29;e.el("path",{d:`M${a-3} ${y}H${a+o+14}L395 ${g-4}H411`,fill:"none",stroke:P(e,f+2),"stroke-width":1,"stroke-dasharray":"4 4"}),S(e,423,g,`${q(c.label,19)} · ${e.format(c.value)}`,{"text-anchor":"start","font-size":11,fill:e.theme.ink})}),W(e,424,57,e.format(t.value),void 0,{"text-anchor":"start","font-size":31}),S(e,424,79,`of ${e.format(s)} capacity`,{"text-anchor":"start","font-size":11}),W(e,a+o/2,311,q(Y(t,0),30),void 0,{"font-size":12}),e.mark(i,t,0),re(e,"Water height = quantity · dashed marks = your thresholds")},da=e=>{const t=e.data[0],s=t.target??e.options.target??100,a=e.options.timeMode==="elapsed",n=t.value===null?null:a?Math.max(0,s-t.value):t.value,o=Math.min(1,(n??0)/s),r=171,l=71,i=168,d=195,h=d*o,p=e.el("defs"),u=e.el("clipPath",{id:`${e.id}-vessel`},p);e.el("rect",{x:r,y:l,width:i,height:d,rx:12},u);const m=e.el("g");e.el("rect",{x:r,y:l,width:i,height:d,rx:12,fill:e.theme.grid,opacity:.4},m);const c=e.el("g",{"clip-path":`url(#${e.id}-vessel)`},m),f=e.el("rect",{x:r,y:l+d-h,width:i,height:h,fill:F(e,P(e,0),"water"),opacity:.8,"clip-path":`url(#${e.id}-vessel)`,class:"np-clock-water"},c);_(e,f,"glass"),e.el("path",{d:`M${r-7} ${l-5}H${r+i+7}M${r-6} ${l+d+5}H${r+i+6}`,stroke:P(e,3),"stroke-width":2,opacity:.5,"pointer-events":"none"},m),n!==null&&e.el("line",{x1:r,x2:r+i,y1:l+d-h,y2:l+d-h,stroke:P(e,1),"stroke-width":2},m),e.el("path",{d:`M${r-10} ${l-9}H${r+i+10}M${r} ${l}V${l+d-12}Q${r} ${l+d} ${r+12} ${l+d}H${r+i-12}Q${r+i} ${l+d} ${r+i} ${l+d-12}V${l}M${r-8} ${l+d+9}H${r+i+8}`,fill:"none",stroke:e.theme.muted,"stroke-width":2.5,"stroke-linecap":"round"},m);for(let y=0;y<=4;y++){const g=l+d-y*d/4;e.el("path",{d:`M${r+10} ${g}h14`,stroke:e.theme.ink,opacity:.3}),S(e,r-18,g+3,e.format(s*y/4),{"text-anchor":"end","font-size":10})}e.mark(m,t,0),W(e,404,126,n===null?"No data":e.format(n),void 0,{"text-anchor":"start","font-size":31}),S(e,405,149,"REMAINING",{"text-anchor":"start","font-size":9,"letter-spacing":1.8}),S(e,405,184,`Input: ${e.format(t.value)} ${a?"elapsed":"remaining"}`,{"text-anchor":"start","font-size":11}),S(e,405,207,`Duration: ${e.format(s)}`,{"text-anchor":"start","font-size":11}),a&&t.value!==null&&t.value>s&&S(e,405,236,`${e.format(t.value-s)} beyond duration`,{"text-anchor":"start","font-size":11,fill:P(e,4)}),S(e,255,311,q(Y(t,0),31),{fill:e.theme.ink,"font-size":12}),re(e,"Calibrated, constant-area vessel · a snapshot, not a running timer")},dt=e=>t=>{const o=!e&&j(t,"dial");!e&&!H(t,"dial",void 0,83,46,260,260)&&Et(t,213,176,119);const r=Xe(t.options,t.data),l=e?Z(Ne(r)).getTime():0,i=e?Z(Ne(r+1)).getTime():1440,d=m=>te+(m-l)/(i-l)*oe,h=(m,c=!1)=>e?Z(m).getTime():ne(m,c),p=[...new Set(t.data.map(m=>m.track??"Schedule"))];for(let m=0;m<(e?12:24);m++){const c=te+m*oe/(e?12:24),f=C(213,176,115,c),y=C(213,176,106+(e&&m%3===0?15:12),c);t.el("path",{d:`M${f}L${y}`,stroke:t.theme.muted,"stroke-width":e&&m%3===0?2:1,opacity:.5,"pointer-events":"none"})}const u=p.length>2?13:19;p.forEach((m,c)=>t.el("circle",{cx:213,cy:176,r:106-c*(u+5),fill:"none",stroke:t.theme.grid,"stroke-width":u,opacity:o?.45:1}));for(let m=0;m<12;m++){const c=e?Z(`${String(r).padStart(4,"0")}-${String(m+1).padStart(2,"0")}-01`).getTime():m*120,f=d(c),y=C(213,176,120,f),g=C(213,176,125,f),w=C(213,176,138,f);t.el("path",{d:`M${y}L${g}`,stroke:t.theme.muted,opacity:.6}),S(t,w[0],w[1]+3,e?["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][m]:String(m*2).padStart(2,"0"),{"font-size":10})}if(t.data.forEach((m,c)=>{const f=p.indexOf(m.track??"Schedule"),y=106-f*(u+5),g=d(h(m.start)),w=d(h(m.end,!0)),b=t.el("g");t.el("path",{d:it(213,176,y-u/2,y+u/2,g,w),fill:P(t,c),opacity:.88,class:"np-interval","data-start-angle":g,"data-end-angle":w},b);const E=58+c*(t.data.length>8?22:34);t.el("circle",{cx:386,cy:E-3,r:3.5,fill:P(t,c)},b),W(t,398,E,q(Y(m,c),24),b,{"text-anchor":"start","font-size":11}),t.data.length<=8&&W(t,398,E+15,e?`${he(m.start)} – ${he(m.end)} · ${q(m.track??"Schedule",15)}`:`${m.start} – ${m.end} · ${q(m.track??"Schedule",15)}`,b,{"text-anchor":"start","font-size":8,fill:t.theme.muted}),t.mark(b,m,c)}),t.options.at!==void 0){const m=d(h(t.options.at)),c=C(213,176,114,m);t.el("path",{d:`M213 176L${c}`,stroke:t.theme.ink,"stroke-width":1.8,opacity:.7,"pointer-events":"none"}),ra(t,c[0],c[1],5)}e?(W(t,213,171,String(r),void 0,{"font-size":27}),S(t,213,192,`${Math.round((i-l)/864e5)} days`,{"font-size":10})):(o||(t.el("path",{d:"M204 187L213 152L222 187Z",fill:F(t,P(t,0),"stone"),opacity:.85}),t.el("path",{d:"M213 152L213 187L222 187Z",fill:t.theme.ink,opacity:.2,"pointer-events":"none"})),S(t,213,213,t.options.at??"24-hour day",{"font-size":11,fill:t.theme.ink})),re(t,e?"Angle = date · arc = duration · tracks run outside in · end dates exclusive":"Clock time, not solar position · arc = duration · tracks run outside in")},ha=e=>{const s=[...e.data.flatMap(i=>[i.observedAt,i.expectedStart,i.expectedEnd].filter(d=>!!d))].sort(),a=e.options.startDate??s[0]??new Date().toISOString().slice(0,10),n=e.options.endDate??s.at(-1)??a,o=Z(a).getTime(),r=Math.max(o+864e5,Z(n).getTime()),l=i=>174+(Z(i).getTime()-o)/(r-o)*345;S(e,39,34,"FOLLOW THE SIGNS OF CHANGE",{"text-anchor":"start","font-size":10,"letter-spacing":1.7}),S(e,581,52,"UNOBSERVED",{"font-size":8,"letter-spacing":.7});for(let i=0;i<=4;i++){const d=174+345*i/4;e.el("line",{x1:d,y1:59,x2:d,y2:278,stroke:e.theme.grid,"stroke-dasharray":"3 5"}),S(e,d,300,he(new Date(o+(r-o)*i/4).toISOString().slice(0,10)),{"font-size":9})}e.data.forEach((i,d)=>{const h=80+d*Math.min(42,200/Math.max(1,e.data.length-1)),p=e.el("g");if(e.el("rect",{x:28,y:h-17,width:584,height:34,rx:5,fill:P(e,d),opacity:.035},p),W(e,39,h+4,q(Y(i,d),18),p,{"text-anchor":"start","font-size":11}),i.expectedStart){const u=l(i.expectedStart),m=l(i.expectedEnd);e.el("rect",{x:u,y:h-7,width:Math.max(2,m-u),height:14,rx:3,fill:P(e,d),opacity:.19,class:"np-expected-window"},p),e.el("path",{d:`M${u} ${h-10}v20M${m} ${h-10}v20`,stroke:P(e,d),opacity:.6},p)}if(i.observedAt){const u=l(i.observedAt),m=(i.label??"").toLowerCase(),c=/sown|sow|seed/.test(m)?"seed":/harvest|fruit|pod/.test(m)?"pod":/flower|bloom/.test(m)?"flower":/bud/.test(m)?"bud":/leaves|leaf/.test(m)?"leaf":"sprout";j(e,c)?(H(e,c,p,u-19,h-28,38,38,{"data-fit":"contain"}),e.el("line",{x1:u,x2:u,y1:h-5,y2:h+13,stroke:P(e,d),"stroke-width":1.5,class:"np-observed-stage","data-x":u},p),e.el("circle",{cx:u,cy:h+13,r:3,fill:e.theme.background,stroke:P(e,d),"stroke-width":1.5},p)):(e.el("path",{d:`M${u} ${h+10}V${h-13}M${u} ${h-3}Q${u-17} ${h-16} ${u-12} ${h-21}Q${u+2} ${h-17} ${u} ${h-3}M${u} ${h-7}Q${u+14} ${h-27} ${u+19} ${h-17}Q${u+12} ${h-7} ${u} ${h-7}`,fill:P(e,d),stroke:P(e,d),"stroke-width":1.4,class:"np-observed-stage","data-x":u},p),e.el("path",{d:`M${u} ${h-4}l-10-14M${u+2} ${h-9}l13-9M${u} ${h+8}q-4 6-9 7m9-7q5 6 10 7`,fill:"none",stroke:e.theme.background,"stroke-width":.7,opacity:.65,"pointer-events":"none"},p),e.el("circle",{cx:u,cy:h,r:3,fill:e.theme.ink},p))}else e.el("circle",{cx:581,cy:h,r:7,fill:e.theme.background,stroke:e.theme.muted,"stroke-dasharray":"2 3"},p);e.mark(p,i,d)}),re(e,"Plant marker = observed date · shaded interval = expected window · empty circle = unknown")};function ca(e,t,s,a,n){const o=e.el("defs"),r=`${e.id}-moon-${Math.round(n*8)}`,l=e.el("clipPath",{id:r},o);if(e.el("circle",{cx:t,cy:s,r:a},l),e.el("circle",{cx:t,cy:s,r:a,fill:e.theme.grid,stroke:P(e,2),"stroke-width":.7}),n>0&&n<1){const d=e.el("g",{"clip-path":`url(#${r})`});e.el("circle",{cx:t,cy:s,r:a,fill:P(e,3),opacity:.9},d),n!==.5&&e.el("circle",{cx:t+(n<.5?-1:1)*a*4*Math.min(n,1-n),cy:s,r:a,fill:e.theme.grid},d)}const i=e.el("g",{"clip-path":`url(#${r})`,"aria-hidden":"true","pointer-events":"none"});[[-.35,-.3,.18],[.3,.25,.25],[-.2,.5,.12]].forEach(([d,h,p])=>e.el("circle",{cx:t+a*d,cy:s+a*h,r:a*p,fill:"none",stroke:e.theme.muted,"stroke-width":.6,opacity:.32},i))}const ht=e=>t=>{const o=Ae(t.options),r=ce(t.data,t.options.max);t.el("circle",{cx:228,cy:177,r:85,fill:"none",stroke:t.theme.grid,"stroke-width":1,"stroke-dasharray":"2 5"}),t.el("circle",{cx:228,cy:177,r:131,fill:"none",stroke:t.theme.grid,"stroke-width":1,opacity:.7});for(let l=0;l<8;l++){const i=te+l*oe/8,d=C(228,177,85,i);if(e)ca(t,d[0],d[1],9,l/8);else{const h=C(228,177,134,i);t.el("circle",{cx:d[0],cy:d[1],r:1.6,fill:t.theme.muted,opacity:.5}),S(t,h[0],h[1]+4,st(o*(l/8)),{"font-size":9})}}if(t.data.forEach((l,i)=>{const d=te+l.position/o*oe,h=C(228,177,102,d),p=C(228,177,102+(e?(l.value??0)/r*29:0),d),u=t.el("g");e?(t.el("path",{d:`M${h}L${p}`,stroke:P(t,i),"stroke-width":3,"stroke-linecap":"round",class:"np-cycle-stem"},u),t.el("circle",{cx:p[0],cy:p[1],r:3.5,fill:l.value===null?t.theme.grid:P(t,i),stroke:l.value===null?t.theme.muted:"none"},u)):j(t,"star")&&l.value!==null?(t.el("circle",{cx:h[0],cy:h[1],r:19,fill:"transparent",class:"np-hit-area"},u),H(t,"star",u,h[0]-22,h[1]-22,44,44,{opacity:$e(l.value,r)}),t.el("circle",{cx:h[0],cy:h[1],r:2.5,fill:P(t,i)},u)):(t.el("path",{d:sa(h[0],h[1],9),fill:P(t,i),opacity:$e(l.value,r),stroke:l.value===null?t.theme.muted:"none","stroke-dasharray":"2 2"},u),t.el("path",{d:`M${h[0]-2} ${h[1]}h4M${h[0]} ${h[1]-2}v4`,stroke:t.theme.background,"stroke-width":.7,opacity:.7,"pointer-events":"none"},u));const m=t.data.length>8,c=Math.ceil(t.data.length/2),f=m?Math.floor(i/c):0,y=m?i%c:i,g=391+f*111,w=54+y*(m?22:34);W(t,g,w,q(Y(l,i),m?13:24),u,{"text-anchor":"start","font-size":12}),m||W(t,g,w+15,`${l.position} ${t.options.cycleUnit??"steps"} · ${t.format(l.value)}`,u,{"text-anchor":"start",fill:t.theme.muted,"font-size":12}),t.mark(u,l,i)}),W(t,228,171,String(o),void 0,{"font-size":35}),S(t,228,193,`${t.options.cycleUnit??"steps"} per cycle`,{"font-size":11}),t.options.cyclePosition!==void 0){const l=te+t.options.cyclePosition/o*oe,i=C(228,177,68,l);t.el("path",{d:`M${C(228,177,49,l)}L${i}`,stroke:t.theme.ink,"stroke-width":1.5,"stroke-dasharray":"3 3"}),t.el("circle",{cx:i[0],cy:i[1],r:3,fill:t.theme.ink})}re(t,e?"Abstract cycle · angle = position · outer stem length = value · moons are illustrative":"Angle = cycle position · star intensity = value · dashed cursor = selected position")},ua=e=>{const t=e.data[0],s=e.data[1],a=t.value===null||s.value===null,n=ce(e.data,e.options.max),o=(t.value??0)/n,r=(s.value??0)/n,l=a?0:(r-o)/Math.max(1e-6,o+r)*.17,i=320,d=116,h=162,p=["balance-stand","balance-beam","balance-pan"].every(c=>j(e,c));p?H(e,"balance-stand",void 0,265,99,110,180):(e.el("path",{d:"M293 270L320 121L347 270Z",fill:P(e,0),opacity:.22}),e.el("path",{d:"M307 257Q317 210 320 153M320 251V184M332 257Q323 227 324 211",fill:"none",stroke:e.theme.muted,"stroke-width":.8,opacity:.5,"pointer-events":"none"}),e.el("path",{d:"M263 275Q320 255 377 275",fill:"none",stroke:e.theme.muted,"stroke-width":2,"stroke-linecap":"round"}));const u=[i-h*Math.cos(l),d-h*Math.sin(l)],m=[i+h*Math.cos(l),d+h*Math.sin(l)];e.el("path",{d:`M${u}L${m}`,fill:"none",stroke:e.theme.ink,"stroke-width":3,"stroke-linecap":"round",class:"np-balance-beam"}),p&&H(e,"balance-beam",void 0,i-177,d-26,354,52,{transform:`rotate(${l*180/Math.PI} ${i} ${d})`}),e.el("circle",{cx:i,cy:d,r:5,fill:P(e,3),stroke:e.theme.ink,"stroke-width":1}),e.data.forEach((c,f)=>{const y=f?m:u,g=y[0],w=y[1]+89,b=e.el("g");if(p?H(e,"balance-pan",b,g-59,y[1]-3,118,130):e.el("path",{d:`M${g} ${y[1]}L${g-51} ${w}M${g} ${y[1]}L${g+51} ${w}M${g-55} ${w}Q${g} ${w+36} ${g+55} ${w}`,fill:"none",stroke:e.theme.muted,"stroke-width":1.5},b),(c.value??0)>0)for(let E=0;E<3;E++){if(p&&j(e,"pebble")){H(e,E===1?"pebble-light":"pebble",b,g+(E-1)*23-19,w-17-(E===1?7:0),38,23);continue}const T=e.el("ellipse",{cx:g+(E-1)*23,cy:w-7-(E===1?7:0),rx:19,ry:10,fill:F(e,P(e,f+E),"stone"),opacity:.85},b);_(e,T,"rock")}W(e,g,52,q(Y(c,f),20),b,{"font-size":12}),W(e,g,80,e.format(c.value),b,{"font-size":23}),e.el("rect",{x:g-65,y:288,width:130,height:6,rx:3,fill:e.theme.grid},b),e.el("rect",{x:g-65,y:288,width:(c.value??0)/n*130,height:6,rx:3,fill:P(e,f),class:"np-balance-amount"},b),e.mark(b,c,f)}),S(e,320,318,a?"Difference unavailable":`Right − left: ${e.format(s.value-t.value)}`,{fill:e.theme.ink,"font-size":12}),re(e,"Bar length = amount on a shared scale · beam tilt indicates direction only")},pa=e=>{const t=Ie(e.data,e.options.unitsPerMark,20),s=536/e.data.length,a=new Map;e.data.forEach((n,o)=>{const r=n.track??"Contributions",l=a.get(r)??[];l.push(o),a.set(r,l)}),a.forEach((n,o)=>{const r=52+s*(n[0]+.2),l=52+s*(n.at(-1)+.8);e.el("path",{d:`M${r} 57V48H${l}V57`,fill:"none",stroke:e.theme.muted,"stroke-width":1.5}),S(e,(r+l)/2,32,q(o,Math.max(8,Math.floor((l-r)/6))),{"font-size":10})}),e.data.forEach((n,o)=>{const r=52+s*(o+.5),l=e.el("g"),i=j(e,"cord")&&j(e,"knot");if(i)H(e,"cord",l,r-4,50,8,224);else{e.el("path",{d:`M${r} 50Q${r-5} 143 ${r+2} 274`,fill:"none",stroke:P(e,o),"stroke-width":2,opacity:.58},l);for(let u=0;u<2;u++){const m=Array.from({length:57},(c,f)=>{const y=f/56,g=r-10*y*(1-y)+2*y*y+Math.sin(f*Math.PI/2+u*Math.PI)*1.3;return`${f?"L":"M"}${g},${50+y*224}`}).join("");e.el("path",{d:m,fill:"none",stroke:P(e,o),"stroke-width":.6,opacity:.55,"pointer-events":"none"},l)}}const d=(n.value??0)/t,h=Math.floor(d),p=Math.max(0,d-h);for(let u=0;u<Math.min(20,h+(p>0?1:0));u++){const m=70+u*9.3+Math.floor(u/10)*9;i&&u<h?(e.el("ellipse",{cx:r,cy:m,rx:9,ry:5,fill:P(e,o),opacity:.35},l),H(e,"knot",l,r-10,m-5.2,20,10.4)):lt(e,l,r,m,u<h?1:p,P(e,o),6.5,i?"knot":"seed")}n.value===null&&W(e,r,170,"?",l,{fill:e.theme.muted,"font-size":18}),W(e,r,298,q(Y(n,o),Math.max(7,Math.floor(s/6))),l,{"font-size":10}),W(e,r,314,e.format(n.value),l,{"font-size":11,fill:e.theme.ink}),e.mark(l,n,o)}),re(e,`1 knot = ${e.format(t)} · groups of ten · partial knot area = fractional unit${t!==(e.options.unitsPerMark??1)?" · bundled":""}`)},ma=e=>{const o=ce(e.data,e.options.max),r=e.data.map(p=>(p.value??0)/o),l=r.reduce((p,u)=>p+u,0),i=e.options.growthMode==="area";let d=0;const h=p=>i?Math.sqrt(169+p*13287):13+p*103;Ve(e,183,173,116),e.el("circle",{cx:183,cy:173,r:13,fill:e.theme.grid}),S(e,465,37,"PERIOD CONTRIBUTIONS",{"font-size":9,"letter-spacing":1.7}),e.data.forEach((p,u)=>{const m=l?d/l:0;d+=r[u];const c=l?d/l:0,f=h(m),y=h(c),g=e.el("g");y>f&&(e.el("path",{d:it(183,173,f,y,te,te+oe),fill:F(e,P(e,u),"stone"),opacity:.88,"fill-rule":"evenodd",class:"np-growth-layer","data-inner-radius":f,"data-outer-radius":y},g),Mt(e,g,183,173,f,y));const w=65+u*Math.min(30,230/e.data.length);W(e,344,w,q(Y(p,u),19),g,{"text-anchor":"start","font-size":10}),e.el("rect",{x:344,y:w+7,width:200,height:5,rx:2.5,fill:e.theme.grid},g),e.el("rect",{x:344,y:w+7,width:r[u]*200,height:5,rx:2.5,fill:P(e,u),class:"np-growth-comparison"},g),W(e,606,w+7,e.format(p.value),g,{"text-anchor":"end","font-size":10,fill:e.theme.muted}),e.mark(g,p,u)}),S(e,183,310,"EARLIER → LATER, INSIDE OUT",{"font-size":9,"letter-spacing":1}),re(e,`Ring ${i?"area":"thickness"} = period contribution · bars share a linear scale${e.data.some(p=>p.value===null)?" · unknown periods have no layer":""}`)},fa=e=>{const t=Ae(e.options),s=[...new Set(e.data.map(i=>i.cycle))],a=Math.max(1,e.options.max??0,...e.data.map(i=>Math.abs(i.value??0))),n=Math.min(0,...e.data.map(i=>(i.value??0)/a)),o=e.options.max!==void 0?e.options.max/a:Math.max(0,...e.data.map(i=>(i.value??0)/a)),r=o-n||1,l=244/s.length;s.forEach((i,d)=>{const h=37+d*l,p=h+l-18,u=l-28,m=b=>p-(b/a-n)/r*u,c=m(0),f=P(e,d);S(e,31,h+18,q(i,13),{"text-anchor":"start",fill:e.theme.ink,"font-size":11}),S(e,116,h+10,e.format(o*a),{"text-anchor":"end","font-size":8}),S(e,116,p+3,e.format(n*a),{"text-anchor":"end","font-size":8}),e.el("line",{x1:135,x2:602,y1:c,y2:c,stroke:e.theme.grid});let y=[];const g=()=>{if(!y.length)return;const b=y.map((T,R)=>`${R?"L":"M"}${T}`).join(""),E=e.el("path",{d:`${b}L${y.at(-1)[0]},${c}L${y[0][0]},${c}Z`,fill:f,opacity:.2,class:"np-cycle-area"});_(e,E,"water"),e.el("path",{d:b,fill:"none",stroke:f,"stroke-width":2,"stroke-linejoin":"round"}),y=[]},w=e.data.map((b,E)=>({point:b,index:E})).filter(b=>b.point.cycle===i);w.forEach(({point:b})=>{const E=135+b.position/t*467;b.value===null?g():y.push([E,m(b.value)])}),g(),w.forEach(({point:b,index:E})=>{const T=135+b.position/t*467,R=b.value===null?c:m(b.value),O=e.el("circle",{cx:T,cy:R,r:b.value===null?3:3.5,fill:b.value===null?e.theme.background:f,stroke:f,"stroke-width":1,"stroke-dasharray":b.value===null?"2 2":"none"});e.mark(O,b,E)})});for(let i=0;i<=4;i++)S(e,135+i*467/4,302,`${st(t*(i/4))}${e.options.cycleUnit?" "+e.options.cycleUnit:""}`,{"font-size":9});re(e,"Aligned cycle positions · one shared scale · gaps stay gaps · no tide prediction")},ga={"seed-ledger":ia,waterline:la,sundial:dt(!1),"season-wheel":dt(!0),phenology:ha,"lunar-cycle":ht(!0),"water-clock":da,balance:ua,"cord-ledger":pa,"growth-history":ma,"tidal-rhythm":fa,"star-cycle":ht(!1)},ya={"seed-ledger":{name:"Seed Ledger",subtitle:"Small things, counted carefully",description:"Count everyday wins as seeds, with an explicit unit and room for fractions.",encoding:"One seed represents a labeled denomination. Filled seed area represents a fractional unit; rows preserve exact totals.",category:"Comparison"},waterline:{name:"Waterline",subtitle:"Know when you reach the mark",description:"Read capacity and meaningful thresholds on a calibrated water column.",encoding:"Water height represents value on a linear zero-to-capacity scale. Dashed lines mark supplied thresholds. Fill caps at capacity; exact values remain.",category:"Progress"},sundial:{name:"Sundial",subtitle:"Give your day a little sunlight",description:"Arrange daily activities on a clock-time dial, with separate tracks for overlapping plans.",encoding:"Angle represents 24-hour clock time. Arc extent represents duration. This is a schedule dial, not a physical sundial or solar calculation.",category:"Timeline"},"season-wheel":{name:"Season Wheel",subtitle:"A whole year, in one glance",description:"See real date intervals unfold around a Gregorian year, one season of work at a time.",encoding:"Angle represents date within the selected year; arc extent represents duration. Month lengths and leap days are respected. No astronomical solar terms are calculated.",category:"Calendar"},phenology:{name:"Phenology",subtitle:"Readiness has its own rhythm",description:"Follow observed milestones alongside expected windows, leaving unknown dates open.",encoding:"Horizontal position represents date. Sprouts mark observed dates; shaded spans show expected windows; empty circles mean not observed.",category:"Timeline"},"lunar-cycle":{name:"Lunar Cycle",subtitle:"Make room for a recurring rhythm",description:"Arrange your own recurring routine around an abstract cycle with illustrative moons.",encoding:"Angle represents cycle position; outer stem length represents value. Moon symbols are illustrative and do not encode completion or calculate lunar phase.",category:"Cycle"},"water-clock":{name:"Water Clock",subtitle:"Let time take a gentler shape",description:"Read elapsed or remaining duration through a calibrated vessel of water.",encoding:"Water height represents remaining duration in a constant-area vessel. Input convention is explicitly elapsed or remaining. This chart is a snapshot, not a timer.",category:"Progress"},balance:{name:"Balance",subtitle:"See both sides of the story",description:"Compare two quantities with common-scale bars and a softly tilting balance.",encoding:"Bar lengths use one linear scale. Beam tilt indicates the direction of the difference only; stones are decorative. Difference is right minus left.",category:"Comparison"},"cord-ledger":{name:"Cord Ledger",subtitle:"Every contribution has a place",description:"Count contributions on grouped cords, with a clear denomination for each knot.",encoding:"One knot represents a labeled denomination. Partial knot area represents a fraction. Parent tracks group contiguous cords. This is not a historical khipu decoder.",category:"Comparison"},"growth-history":{name:"Growth History",subtitle:"Let every chapter leave a trace",description:"Build a record of contributions as successive growth layers with a linked comparison view.",encoding:"Layer order follows input chronology. Ring thickness or area, as configured, represents period contribution; linked bars use a shared linear scale. Unknown periods have no quantitative layer.",category:"Trend"},"tidal-rhythm":{name:"Tidal Rhythm",subtitle:"Find what returns, and what changes",description:"Compare repeating patterns as aligned cycles with a shared scale and honest gaps.",encoding:"Horizontal position represents position within each supplied cycle. Vertical position uses a shared numeric scale across cycles. Lines connect observations; this does not predict tides.",category:"Trend"},"star-cycle":{name:"Star Cycle",subtitle:"A constellation of things to return to",description:"Give recurring checkpoints a place in a clearly labeled cycle.",encoding:"Angular position represents cycle position. Star intensity represents value; exact values and cycle positions remain available. The arrangement is an abstract checkpoint map.",category:"Cycle"}};function ct(e,t){const s=e.options.formatValue?.(t)??new Intl.NumberFormat("en",{maximumFractionDigits:2}).format(t);return s.length>8||t!==0&&Math.abs(t)<.001?t.toExponential(1):s}const ba=e=>{const t=ce(e.data,e.options.max);Ge(e,58,272,.7),Ge(e,582,272,.7),e.data.forEach((n,o)=>{const r=Math.floor(o/7),l=o%7,i=222-r*23,d=Math.PI+l*Math.PI/7+10/i,h=Math.PI+(l+1)*Math.PI/7-10/i,p=De(320,273,i,d,h);e.el("path",{d:p,fill:"none",stroke:e.theme.grid,"stroke-width":17,"stroke-linecap":"round"});const u=e.el("path",{d:p,fill:"none",stroke:e.theme.colors[(6-r)%e.theme.colors.length],"stroke-width":17,"stroke-linecap":"round",opacity:$e(n.value,t)});e.mark(u,n,o)});const s=e.data[0],a=e.data[48];S(e,102,299,he(s.date),{"text-anchor":"start"}),S(e,538,299,he(a.date),{"text-anchor":"end"}),S(e,320,274,"49 little moments",{fill:e.theme.ink,"font-size":13}),tt(e)},wa=e=>{const t=Math.ceil(e.data.length/10),s=ce(e.data,e.options.max);e.el("path",{d:"M48 277 Q175 269 318 279 T592 276",fill:"none",stroke:e.theme.grid,"stroke-width":1.5});for(let a=0;a<t;a++){const n=640/(t+1)*(a+1);e.el("ellipse",{cx:n,cy:280,rx:39,ry:5,fill:e.theme.colors[0],opacity:.07}),e.el("path",{d:`M${n} 279 Q${n-6} 190 ${n+2} 71`,fill:"none",stroke:e.theme.colors[0],"stroke-width":2.5,"stroke-linecap":"round"}),e.el("path",{d:`M${n} 279q-8 7-19 10m19-10q8 8 22 11m-22-11v13`,fill:"none",stroke:e.theme.muted,"stroke-width":.7,opacity:.5,"pointer-events":"none"});for(let l=0;l<10&&a*10+l<e.data.length;l++){const i=a*10+l,d=e.data[i],h=l%2?1:-1,p=255-Math.floor(l/2)*36-l%2*9,u=n+h*65,m=p-38,c=`M${n} ${p} Q${n+h*8} ${p-45} ${u} ${m} Q${n+h*50} ${p+5} ${n} ${p}Z`;e.el("path",{d:c,fill:e.theme.grid});const f=e.el("path",{d:c,fill:e.theme.colors[(a+Math.floor(l/4))%e.theme.colors.length],opacity:$e(d.value,s)});e.mark(f,d,i),e.el("path",{d:`M${n+h*5} ${p-4} L${u-h*12} ${m+5}`,stroke:e.theme.background,opacity:.5,"stroke-width":1,"pointer-events":"none"}),[.3,.55,.75].forEach(y=>{const g=n+h*53*y,w=p-33*y;e.el("path",{d:`M${g} ${w}q${h*4} -8 ${h*11} -12`,fill:"none",stroke:e.theme.background,opacity:.35,"stroke-width":.8,"pointer-events":"none"}),e.el("path",{d:`M${g} ${w}q${h*10} 5 ${h*16} 1`,fill:"none",stroke:e.theme.background,opacity:.3,"stroke-width":.65,"pointer-events":"none"})}),S(e,n+h*39,p-20,String(Number(d.date.slice(-2))),{fill:me(N(e.theme.grid,e.theme.colors[(a+Math.floor(l/4))%e.theme.colors.length],$e(d.value,s))),"font-size":10,"pointer-events":"none"})}const o=e.data[a*10].date,r=e.data[Math.min(a*10+9,e.data.length-1)].date;S(e,n,305,`${he(o)} – ${he(r)}`,{"font-size":11}),e.el("ellipse",{cx:n+2,cy:69,rx:4,ry:8,transform:`rotate(15 ${n+2} 69)`,fill:e.theme.colors[1%e.theme.colors.length]})}tt(e)},va=e=>{const t=ce(e.data,e.options.max),s=276,a=72,n=598;S(e,a,42,e.options.yLabel??e.options.unit??"Value",{"text-anchor":"start","font-size":13});for(let r=0;r<=4;r++){const l=s-r*51;e.el("line",{x1:a-12,y1:l,x2:n,y2:l,stroke:e.theme.grid,"stroke-dasharray":r?"3 6":"none"}),S(e,a-23,l+4,ct(e,t*r/4),{"text-anchor":"end","font-size":12})}const o=(n-a)/Math.max(e.data.length,1);e.data.forEach((r,l)=>{const i=a+o*(l+.5),d=(r.value??0)/t*204,h=Math.min(36,o*.37),p=e.el("g");if(r.value===null||r.value===0)e.el("circle",{cx:i,cy:s,r:4,fill:r.value===null?e.theme.grid:e.theme.colors[l%e.theme.colors.length]},p);else{e.el("path",{d:`M${i} ${s} V${s-d}`,stroke:e.theme.ink,"stroke-width":2,opacity:.45},p);const u=s-d;if(e.el("path",{d:`M${i} ${u} L${i+h*.64} ${u+d*.39} H${i+h*.36} L${i+h*.86} ${u+d*.62} H${i+h*.58} L${i+h} ${u+d*.83} Q${i} ${u+d*.9} ${i-h} ${u+d*.83} L${i-h*.58} ${u+d*.62} H${i-h*.86} L${i-h*.36} ${u+d*.39} H${i-h*.64}Z`,fill:F(e,e.theme.colors[l%e.theme.colors.length])},p),[.4,.6,.8].forEach(m=>e.el("path",{d:`M${i-h*m*.65} ${u+d*m}L${i} ${u+d*(m+.05)}L${i+h*m*.65} ${u+d*m}`,fill:"none",stroke:e.theme.background,opacity:.25,"stroke-width":.9,"pointer-events":"none"},p)),d>20)for(let m=1;m<=8;m++){const c=.16+m*.072,f=h*c*.78;e.el("path",{d:`M${i-f} ${u+d*c}Q${i-f*.45} ${u+d*(c+.035)} ${i} ${u+d*(c+.06)}Q${i+f*.45} ${u+d*(c+.035)} ${i+f} ${u+d*c}`,fill:"none",stroke:e.theme.background,"stroke-width":.55,opacity:.45,"pointer-events":"none"},p)}e.el("path",{d:`M${i} ${u+d*.12} V${s-d*.14}`,stroke:e.theme.background,opacity:.27,"stroke-width":1},p)}e.mark(p,r,l),l%Math.max(1,Math.ceil(e.data.length/9))===0&&S(e,i,301,q(Y(r,l),9),{"font-size":11})}),S(e,335,335,"Tree height = value · all trees share a zero baseline",{"font-size":11})};function ut(e,t){const{data:s,theme:a}=e,n=Math.min(0,...s.map(w=>w.value??0)),r=(e.options.max??Math.max(0,...s.map(w=>w.value??0)))-n||1,l=279,i=68,d=65,h=593,p=w=>l-(w-n)/r*(l-i),u=w=>s.length===1?(d+h)/2:d+w/Math.max(1,s.length-1)*(h-d);S(e,d,40,e.options.yLabel??e.options.unit??"Value",{"text-anchor":"start","font-size":13});for(let w=0;w<=4;w++){const b=n+r*w/4,E=p(b);e.el("line",{x1:d,x2:h,y1:E,y2:E,stroke:a.grid,"stroke-dasharray":"3 6"}),S(e,d-12,E+4,ct(e,b),{"text-anchor":"end","font-size":12})}const m=p(0);e.el("line",{x1:d,x2:h,y1:m,y2:m,stroke:a.muted,opacity:.45});const c=e.el("defs"),f=e.el("linearGradient",{id:`${e.id}-area`,x1:"0",y1:"0",x2:"0",y2:"1"},c);e.el("stop",{offset:"0%","stop-color":a.colors[0],"stop-opacity":t?.5:.65},f),e.el("stop",{offset:"100%","stop-color":a.colors[2%a.colors.length],"stop-opacity":.12},f);const y=[];let g=[];s.forEach((w,b)=>{w.value===null?(g.length&&y.push(g),g=[]):g.push({x:u(b),y:p(w.value)})}),g.length&&y.push(g),y.forEach(w=>{let b=`M${w[0].x},${w[0].y}`;for(let R=1;R<w.length;R++){const O=w[R],ee=w[R-1],ge=(O.x+ee.x)/2;b+=t?` C${ge},${ee.y} ${ge},${O.y} ${O.x},${O.y}`:` L${O.x},${O.y}`}const E=`${b} L${w.at(-1).x},${m} L${w[0].x},${m}Z`,T=e.el("path",{d:E,fill:`url(#${e.id}-area)`});_(e,T,t?"water":"rock"),e.el("path",{d:b,fill:"none",stroke:a.colors[0],"stroke-width":t?3:2,"stroke-linejoin":"round"})}),s.forEach((w,b)=>{const E=u(b),T=w.value===null?m:p(w.value),R=e.el("g");e.el("circle",{cx:E,cy:T,r:11,fill:"transparent"},R),e.el("circle",{cx:E,cy:T,r:w.value===null?3:4,fill:w.value===null?a.grid:a.background,stroke:a.colors[0],"stroke-width":1.7},R),e.mark(R,w,b);const O=Math.max(1,Math.ceil(s.length/7));(b%O===0&&s.length-1-b>=O||b===s.length-1)&&S(e,E,306,q(Y(w,b),9),{"font-size":11})}),S(e,330,338,"Equal spacing = observation order · gaps = missing readings",{"font-size":11})}const qe={rainbow:ba,garden:wa,forest:va,river:e=>ut(e,!0),bloom:e=>{const a=ce(e.data,e.options.max),n=e.data.length;[45,80,115].forEach(o=>e.el("circle",{cx:320,cy:177,r:o,fill:"none",stroke:e.theme.grid,"stroke-dasharray":"2 6"})),e.data.forEach((o,r)=>{const l=-Math.PI/2+r/n*Math.PI*2,i=Math.min(.5,Math.PI/Math.max(3,n)*.8),d=15+(o.value??0)/a*100,h=C(320,177,15,l-i),p=C(320,177,15,l+i),u=C(320,177,d,l),m=C(320,177,d,l-i),c=C(320,177,d,l+i),f=`M${h} Q${m} ${u} Q${c} ${p}Z`,y=e.el("g");if(e.el("path",{d:f,fill:o.value===null?e.theme.grid:F(e,e.theme.colors[r%e.theme.colors.length]),stroke:e.theme.background,"stroke-width":1.4},y),o.value!==null&&o.value>0){const b=C(320,177,17,l),E=C(320,177,d-3,l);e.el("path",{d:`M${b}L${E}`,stroke:e.theme.background,opacity:.5,"stroke-width":.8},y);for(const T of[-1,1]){const R=C(320,177,20,l+T*i*.25),O=C(320,177,d*.75,l+T*i*.4),ee=C(320,177,d-4,l+T*i*.06);e.el("path",{d:`M${R}Q${O} ${ee}`,fill:"none",stroke:e.theme.background,"stroke-width":.6,opacity:.35,"pointer-events":"none"},y)}}e.mark(y,o,r);const[g,w]=C(320,177,142,l);S(e,g,w+4,q(Y(o,r),14),{"font-size":11,fill:e.theme.ink})}),e.el("circle",{cx:320,cy:177,r:13,fill:e.theme.colors[3%e.theme.colors.length],"pointer-events":"none"}),[0,1,2,3,4].forEach(o=>{const r=C(320,177,6,o*1.26);e.el("circle",{cx:r[0],cy:r[1],r:1.1,fill:e.theme.ink,opacity:.45,"pointer-events":"none"})}),S(e,320,347,`Petal length · 0–${e.format(a)}`,{"font-size":10})},mountain:e=>ut(e,!1),tide:e=>{const t=e.data[0],s=320,a=168,n=111,o=t.target??e.options.target??100,r=t.value===null?null:t.value/o*100,l=Math.min(1,Math.max(0,(r??0)/100)),i=a+n-l*n*2,d=e.el("defs"),h=e.el("clipPath",{id:`${e.id}-water`},d);e.el("circle",{cx:s,cy:a,r:n},h),e.el("circle",{cx:s,cy:a,r:n+7,fill:"none",stroke:e.theme.grid,"stroke-width":1}),e.el("circle",{cx:s,cy:a,r:n,fill:e.theme.grid,opacity:.45});const p=e.el("g",{"clip-path":`url(#${e.id}-water)`}),u=l>.02&&l<.98?7:0;e.el("path",{d:`M190 ${i} Q255 ${i-u*2} 320 ${i} T450 ${i} V300 H190Z`,fill:e.theme.colors[1%e.theme.colors.length],opacity:.5},p);const m=e.el("path",{d:`M190 ${i} Q255 ${i+u*2} 320 ${i} T450 ${i} V300 H190Z`,fill:e.theme.colors[0],opacity:.8},p);_(e,m,"water"),[0,.5,1].forEach(f=>{const y=a+n-f*n*2;e.el("path",{d:`M197 ${y}h8`,stroke:e.theme.muted,"stroke-width":1}),S(e,189,y+4,`${f*100}%`,{"text-anchor":"end","font-size":12})});const c=e.el("circle",{cx:s,cy:a,r:n,fill:"transparent"});e.mark(c,t,0),e.el("rect",{x:260,y:132,width:120,height:72,rx:24,fill:e.theme.background,"fill-opacity":.94,"pointer-events":"none"}),S(e,s,173,r===null?"—":`${Math.round(r)}%`,{fill:e.theme.ink,"font-size":39,"font-weight":500,"pointer-events":"none"}),S(e,s,194,"of your goal",{"font-size":10,"pointer-events":"none"}),S(e,s,308,q(Y(t,0),50),{fill:e.theme.ink,"font-size":14}),S(e,s,333,`${e.format(t.value)} / ${e.format(o)}`,{"font-size":11})},rings:e=>{const t=e.data.length,s=Math.min(23,99/Math.max(1,t)),a=250,n=176;Ve(e,a,n,132),e.data.forEach((o,r)=>{const l=120-r*s,i=o.target??e.options.target??100,d=Math.min(1,Math.max(0,(o.value??0)/i)),h=e.theme.colors[r%e.theme.colors.length],p=e.el("g");if(e.el("circle",{cx:a,cy:n,r:l,fill:"none",stroke:e.theme.grid,"stroke-width":Math.max(1,s-7)},p),d>0){const u=-Math.PI/2+Math.PI*2*Math.min(.99999,d);if(e.el("path",{d:De(a,n,l,-Math.PI/2,u),fill:"none",stroke:h,"stroke-width":Math.max(1,s-7),"stroke-linecap":"butt",opacity:.9},p),s>10)for(const m of[-1.5,1.5])e.el("path",{d:De(a,n,l+m,-Math.PI/2,u),fill:"none",stroke:e.theme.background,"stroke-width":.55,opacity:.4,"pointer-events":"none"},p)}if(e.mark(p,o,r),r<10){const u=62+r*Math.min(40,250/t);e.el("circle",{cx:423,cy:u-4,r:4,fill:h}),S(e,437,u,q(Y(o,r),17),{"text-anchor":"start",fill:e.theme.ink,"font-size":12}),S(e,437,u+16,o.value===null?"No data":`${Math.round(o.value/i*100)}% of ${e.format(i)}`,{"text-anchor":"start","font-size":10})}}),S(e,320,337,"Sweep angle = progress · hollow track = unfilled goal",{"font-size":11})},...ga,...na},Qe={rainbow:{name:"Rainbow",subtitle:"A spectrum of small wins",description:"Give seven weeks of daily activity a little color. Every arc is a week; every segment is a day.",encoding:"7 arcs × 7 days. Color intensity represents the value; pale gray means no data.",category:"Calendar"},garden:{name:"Garden",subtitle:"Good habits take root",description:"Watch a month of effort grow into a garden. Three plants, thirty leaves, a story in every day.",encoding:"Each plant holds 10 consecutive dates. Leaf intensity represents value. A 31st day adds a sprout.",category:"Calendar"},forest:{name:"Forest",subtitle:"See how your ideas grow",description:"Turn comparisons into a little woodland. A taller tree represents a larger value.",encoding:"Tree height represents value on a shared linear scale starting at zero.",category:"Comparison"},river:{name:"River",subtitle:"Find the flow in your data",description:"Follow the gentle ebb and flow of your everyday rhythms, one observation at a time.",encoding:"Vertical position represents value. Curves pass through observations without overshoot; gaps remain gaps.",category:"Trend"},bloom:{name:"Bloom",subtitle:"A fuller picture, petal by petal",description:"Give every dimension room to blossom. A simple flower reveals the shape of your strengths.",encoding:"Petal length beyond the center represents value. Petal area is decorative, not a part-to-whole measure.",category:"Comparison"},mountain:{name:"Mountain",subtitle:"Make every milestone visible",description:"Explore the peaks and quieter stretches of a journey, from daily activity to weekly demand.",encoding:"Each vertex is an observation on a linear scale. Straight slopes connect adjacent values.",category:"Trend"},tide:{name:"Tide",subtitle:"Little by little, the tide rises",description:"A single goal, a rising tide. Make progress feel calm, tangible, and within reach.",encoding:"Water height represents the fraction of a target. Text preserves progress beyond 100%.",category:"Progress"},rings:{name:"Rings",subtitle:"Every season leaves a mark",description:"Layer your goals like the rings of a tree. Every circle has its own pace and possibility.",encoding:"Arc angle represents percent of each target. Compare angles or percentages, not raw arc lengths.",category:"Progress"},...ya,...Pe},pt={meadow:{background:"#fafbf5",ink:"#263e32",muted:"#647262",grid:"#dce5d8",colors:["#63866a","#93aa73","#bbca80","#e3c874","#dfa06f","#be8077","#9681a3"]},ocean:{background:"#f3f8fa",ink:"#274a57",muted:"#56717d",grid:"#d6e5e9",colors:["#4e8599","#6ea9b4","#92bfc1","#abd2c7","#d0dcb9","#b1bfd8","#8a9bb9"]},autumn:{background:"#fff8ef",ink:"#604934",muted:"#816b56",grid:"#ebdfcc",colors:["#ab6c48","#cb8850","#e1ac68","#e8c98d","#b6ac6e","#8c9768","#c08276"]},twilight:{background:"#f8f5fc",ink:"#4b4263",muted:"#78698c",grid:"#e4dced",colors:["#87749f","#a48eb4","#c1a4c3","#dbb7cb","#e7c9b6","#a2b2c7","#869cad"]}};function $a(e="meadow"){const t=typeof e=="string"?pt[e]:e;if(!t||!Array.isArray(t.colors)||t.colors.length===0||[t.background,t.ink,t.muted,t.grid,...t.colors].some(s=>typeof s!="string"||!/^#[\da-f]{3}(?:[\da-f]{3})?$/i.test(s)))throw new Error("NaturePlot: theme must be a known theme name or a palette of hex colors.");return{...t,colors:[...t.colors]}}function ka(e,t,s){const a=[{header:"Observation",read:Y}],n=["sundial","season-wheel","phenology","daylight"].includes(e.type);if((!n||t.some(o=>o.date))&&a.push({header:"Date",read:o=>o.date??"—"}),(!n||t.some(o=>o.value!==null))&&a.push({header:"Value",read:o=>s(o.value)}),n||a.push({header:"Target",read:o=>{const r=et(o,e);return r===void 0?"—":s(r)}}),(e.type==="sundial"||e.type==="season-wheel"||e.type==="daylight")&&a.push({header:"Start",read:o=>o.start},{header:"End (exclusive)",read:o=>o.end},{header:"Duration",read:o=>e.type!=="season-wheel"?`${ne(o.end,!0)-ne(o.start)} min`:`${(Date.parse(o.end)-Date.parse(o.start))/864e5} days`},{header:"Track",read:o=>o.track??"Schedule"}),e.type==="phenology"&&a.push({header:"Observed",read:o=>o.observedAt??"Not observed"},{header:"Expected start",read:o=>o.expectedStart??"—"},{header:"Expected end",read:o=>o.expectedEnd??"—"},{header:"Status",read:o=>o.observedAt?"Observed":o.expectedStart?"Expected, not observed":"Not yet scheduled"}),["lunar-cycle","star-cycle","tidal-rhythm"].includes(e.type)&&a.push({header:"Position",read:o=>`${o.position}${e.cycleUnit?" "+e.cycleUnit:""}`}),e.type==="tidal-rhythm"&&a.push({header:"Cycle",read:o=>o.cycle}),e.type==="cord-ledger"&&a.push({header:"Track",read:o=>o.track??"Contributions"}),Object.hasOwn(Pe,e.type)){for(const o of["id","parent","source","destination","track","position","angle","x","y","weight","baseline","low","q1","q3","high"])t.some(r=>r[o]!==void 0)&&!a.some(r=>r.header.toLowerCase()===o)&&a.push({header:o[0].toUpperCase()+o.slice(1),read:r=>r[o]===void 0?"—":String(r[o])});e.type==="echo"&&a.push({header:"Previous value",read:(o,r)=>r?s(t[r-1].value):"No predecessor"})}return a}function Ma(e,t,s){const a=[];return Object.hasOwn(Pe,e.type)&&a.push(Pe[e.type].encoding),["seed-ledger","cord-ledger"].includes(e.type)&&a.push(`One ${e.type==="seed-ledger"?"seed":"knot"} = ${s(Ie(t,e.unitsPerMark,e.type==="cord-ledger"?20:40))}. Partial marks represent fractional units.`),e.type==="waterline"&&a.push(...(e.thresholds??[]).map(n=>`${n.label}: ${s(n.value)}.`)),e.type==="water-clock"&&a.push(`Input is ${e.timeMode??"remaining"} duration. Water height shows remaining duration.`),e.type==="sundial"&&a.push("Clock-time schedule, not solar position. Interval ends are exclusive."),e.type==="season-wheel"&&a.push("Gregorian date intervals. Ends are exclusive."),e.at&&["sundial","season-wheel"].includes(e.type)&&a.push(`Cursor: ${e.at}.`),["star-cycle","lunar-cycle","tidal-rhythm"].includes(e.type)&&a.push(`Cycle length: ${Ae(e)} ${e.cycleUnit??"steps"}.`),e.type==="lunar-cycle"&&a.push("Abstract cycle; moon symbols are illustrative, not computed astronomical phases."),e.cyclePosition!==void 0&&["lunar-cycle","star-cycle"].includes(e.type)&&a.push(`Cycle cursor: ${e.cyclePosition}.`),e.type==="growth-history"&&a.push(`Ring ${e.growthMode??"thickness"} represents each period's contribution; input order is chronological. Null means unknown contribution and receives no quantitative layer.`),e.type==="balance"&&t.length===2&&a.push(t.some(n=>n.value===null)?"Difference unavailable because an observation is missing.":`Difference (right minus left): ${s(t[1].value-t[0].value)}.`),e.xLabel&&a.push(`Horizontal axis: ${e.xLabel}.`),e.yLabel&&a.push(`Vertical axis: ${e.yLabel}.`),e.weightLabel&&e.type==="dew"&&a.push(`Droplet area: ${e.weightLabel}.`),a.join(" ")}const mt=Object.fromEntries(Object.entries({rainbow:["Seven weeks of dispatches","Which days had unusually low dispatch activity?","Inspect quiet dates against staffing and closures before changing a schedule."],garden:["Daily orders this month","How consistently are orders arriving across the month?","Spot sustained changes and check exact daily counts before planning stock."],forest:["Orders by sales channel","Which sales channels bring the most orders?","Rank comparable totals to decide where to investigate capacity or promotion."],river:["Weekly net cash movement","Is the weekly cash movement improving or becoming more volatile?","Review the sequence and negative weeks; use a ledger for reconciliation."],bloom:["Customer service ratings","Which aspects of service need attention?","Compare scores from the same rating scale; keep sample size and uncertainty in the supporting report."],mountain:["Weekly order demand","Which weeks put the most pressure on fulfillment?","Identify peak periods to investigate staffing and supply requirements."],tide:["Invoices collected this month","How much of the collection goal has been reached?","Read paid amount against the target; this does not replace an aging report."],rings:["Team goals this month","Which independent targets are furthest from completion?","Compare percentages and inspect each goal in its original unit."],"seed-ledger":["Follow-up hours by team","How much time has each team spent on customer follow-ups?","Compare recorded hours using the shared seed denomination, including partial hours."],waterline:["Packaging stock on hand","Is stock close to a replenishment threshold?","Compare the current amount with operational thresholds chosen by the business."],sundial:["A day at the service desk","When do appointments and support shifts overlap?","Find scheduling conflicts in the supplied time intervals."],"season-wheel":["Annual campaign calendar","Which campaigns share the same part of the year?","Coordinate annual windows and inspect their exact start and end dates."],phenology:["Customer onboarding milestones","Which milestones happened inside their expected window?","Follow up on pending stages and investigate late observations."],"lunar-cycle":["Recurring equipment checks","Where do checks fall within our maintenance cycle?","Locate upcoming checkpoints in a supplied cycle; the chart does not calculate maintenance needs."],"water-clock":["Time left in an appointment","How much of the allotted session remains?","Use a bounded duration snapshot to pace a session; the application supplies updates."],balance:["Operating budget and spending","Is spending above or below the budget?","Read the exact difference and common-scale bars; the beam only shows direction."],"cord-ledger":["Volunteer hours by crew","How much has each person contributed within their crew?","Count or compare contributions using one shared denomination."],"growth-history":["New members by year","Which years contributed most to today’s membership?","Inspect period contributions using the companion bars, without confusing them with running totals."],"tidal-rhythm":["Hourly visits across store days","Do busy hours recur at similar times on different days?","Compare days on the same clock-position and visitor scales."],"star-cycle":["Customer review checkpoints","Where are customer reviews within our twelve-week cycle?","Find scheduled checkpoints and inspect their priority reading."],honeycomb:["Support tickets by team and weekday","Which team-day combinations have the heaviest load?","Investigate concentrated workload using exact counts, including missing cells."],mycelium:["Ticket transfers between teams","Where are tickets being handed between teams?","Inspect directed handoffs and their counts to find processes worth reviewing."],"root-tree":["Workload and ownership","Who owns each part of the work?","Trace the supplied ownership hierarchy and inspect each team’s own workload."],canopy:["Department spending allocation","How is spending divided between departments and activities?","Compare shares of one positive total and inspect small categories numerically."],fern:["Reasons for customer returns","Which return reasons account for most of the volume?","Prioritize high-volume reasons using ranked counts and cumulative share."],phyllotaxis:["Sales mix by product family","How is our sales volume divided across product families?","Communicate approximate shares; use the exact table for small differences."],"leaf-veins":["Response time before and after","Which teams improved after the process change?","Compare paired measurements on the same scale; the chart alone cannot establish cause."],lotus:["Progress against team targets","Which goals are closest to their individual targets?","Inspect each axis independently rather than treating polygon area as an overall score."],"petal-box":["Delivery times by provider","Which delivery provider is faster and more consistent?","Compare medians and quartiles from supplied summaries; review sample sizes separately."],raincloud:["Distribution of handling times","Are most requests quick, or is there a long tail?","Compare counts by interval, allowing for different bin widths."],dew:["Campaign effort and response","How do campaign effort, response, and reach relate?","Inspect individual campaigns and avoid treating association as proof of impact."],"wind-rose":["Delivery requests by direction","Which compass sectors generate more delivery requests?","Plan a closer route analysis using directional totals from a common origin."],dune:["Order-size profiles by customer group","How do order-size patterns differ between customer groups?","Compare supplied frequencies on a shared scale and inspect unusual cohorts."],glacier:["Weekly changes in the cash balance","Which inflows and outflows changed the running balance?","Trace signed changes and the resulting balance; use exact values for reconciliation."],sediment:["Revenue by product over time","Which products account for changes in total revenue?","Read the total trend and inspect each stacked contribution on its own."],delta:["Routing incoming enquiries","How are incoming enquiries allocated to teams?","Compare the outgoing allocation of one total; no conversion rate is implied."],estuary:["Work handed between departments","How much work moves between each pair of departments?","Inspect supplied transfers and their source and destination totals."],pitcher:["Trial-to-customer funnel","At which stage do the most prospects drop away?","Compare ordered stage totals and conversion rates for the same cohort."],firefly:["Operational events through the day","When did incidents occur across the monitored services?","Inspect event timing, service and severity before investigating a cluster."],migration:["A courier’s recorded stops","In what order were the delivery stops visited?","Inspect supplied planar coordinates and stop amounts; this is not route optimization or a map."],murmuration:["Handling times by team","How much variation is hidden behind each team’s average?","Inspect individual observations, clusters and outliers before aggregating."],"coral-range":["Task duration estimates","Which tasks have the widest planning range?","Compare supplied low, central and high estimates; ranges are not automatically confidence intervals."],pebble:["Customer waiting-time distribution","What share of customers waited at most a given time?","Inspect cumulative proportions and compare them with a service standard."],nautilus:["Three cycles of outreach activity","How did activity differ at the same position across cycles?","Compare checkpoint positions and exact counts while keeping cycle order visible."],frost:["Relationships between service metrics","Which measured metrics have positive or negative associations?","Inspect a supplied correlation matrix; the library does not calculate significance or infer causation."],echo:["Order volume and the previous day","Do high-volume days tend to follow high-volume days?","Inspect lag-one pairs as a diagnostic, not as a forecast."],cairn:["Contributions to a shared fund","How much has each contributor added toward one goal?","Read contributions, total progress and the remaining gap on a common scale."],daylight:["Store opening hours by date","Which dates offer longer or shorter opening windows?","Compare supplied opening and closing times before planning coverage."],isobar:["Temperature across a stockroom","Where do measured temperatures form hotter or cooler zones?","Inspect measured nodes and interpolated contours; do not treat the interpolated field as extra sensor readings."],bamboo:["Training assessment scores","What are the original scores, including repeated values?","Inspect a small integer distribution without grouping observations into bins."]}).map(([e,[t,s,a]])=>[e,{example:t,question:s,decision:a}])),Ea=Object.fromEntries(Object.entries({rainbow:["Find consistent days across a 49-day habit or activity log.","Different arc lengths make area comparisons unreliable. Read intensity within a date, not arc area.","garden"],garden:["Review a month of daily routines, practice, or contributions.","Leaf position follows consecutive dates, not weekday columns. Intensity is better for patterns than close numeric comparisons.","forest"],forest:["Compare a small set of category totals in the same unit.","Read tree height from zero. Crown area and branching do not measure additional quantities.","seed-ledger"],river:["Follow an ordered series with a gentle visual rhythm.","Observations are equally spaced, even when date labels are irregular. The curve is an interpolation, not a forecast.","mountain"],bloom:["Compare a handful of dimensions sharing one unit and scale.","Petal area is decorative. Use Lotus for different targets, or Forest for precise ranking.","forest"],mountain:["Find peaks and troughs in ordered observations.","Horizontal distance means observation order, not elapsed time. Missing values break the slope.","tidal-rhythm"],tide:["Communicate progress toward one positive goal.","Water height, not the area of the circular vessel, encodes progress. Above-goal values require the exact reading.","waterline"],rings:["Track up to eight independent goals with different targets.","Compare sweep angles or percentages. Outer rings have longer circumferences and do not imply more progress.","lotus"],"seed-ledger":["Make small counts and fractional quantities tangible.","Read the stated denomination: large quantities are bundled. These seeds are not always one unit each.","forest"],waterline:["Monitor a capacity against user-defined thresholds.","The vessel saturates at capacity. Exact text retains an overflow; thresholds do not imply automatic alerts.","tide"],sundial:["Plan activities inside one clock day, including simultaneous lanes.","This is a 24-hour clock-time schedule, not a solar calculation. Split intervals that cross midnight.","daylight"],"season-wheel":["Compare annual windows, seasons, or project phases.","Arc distance uses actual Gregorian dates. Split intervals crossing the selected year; this is not an ancient calendar reconstruction.","phenology"],phenology:["Compare observed milestones with expected date windows.","Undated stages remain pending. The chart does not predict when a stage will occur.","season-wheel"],"lunar-cycle":["Place recurring activity within an explicitly defined cycle.","Moon shapes illustrate abstract phase. They do not show observed lunar phase or a medical prediction.","star-cycle"],"water-clock":["Show elapsed or remaining time in a single duration snapshot.","It is a calibrated visual vessel, not a running timer or a physical model of water flow.","tide"],balance:["Compare two nonnegative quantities and their difference.","The bars measure amounts; beam tilt only indicates direction. Decorative stones are not countable units.","leaf-veins"],"cord-ledger":["Count contributions organized into a few named teams.","Keep each team contiguous. Knots use a shared denomination and do not reconstruct a historical writing system.","seed-ledger"],"growth-history":["Show known contributions accumulated in chronological order.","Choose thickness or area explicitly. Later rings have longer circumferences; read the companion bars for direct comparisons.","glacier"],"tidal-rhythm":["Compare multiple observed cycles on a common position axis.","Cycle length and positions are supplied. The chart does not detect periodicity or predict tides.","echo"],"star-cycle":["Place checkpoints around a repeating cycle.","Angle measures cycle position and brightness measures value; star area has no quantitative meaning.","nautilus"],honeycomb:["Compare activity for a small row-by-column matrix.","Absent cells are unmeasured, not zero. Use exact readings when small intensity differences matter.","forest"],mycelium:["Explore a small directed network with weighted connections.","Node positions are a layout, not distance or importance. Large networks need a dedicated graph tool.","estuary"],"root-tree":["Trace a compact taxonomy or ownership hierarchy.","Each node measures its own value, not its subtree. Multiple roots are allowed; layout order does not rank importance.","canopy"],canopy:["Compare positive parts of a total within two grouping levels.","Zero parts have no area. Compare narrow cells with the inspector; this is not an arbitrary-depth treemap.","phyllotaxis"],fern:["Find high-impact categories and their cumulative share.","Display order is descending. The spores use a separate percentage scale from the frond lengths.","forest"],phyllotaxis:["Explain how a whole is divided among several categories.","120 seed positions approximate proportions with rounding. Small shares can receive no dot; the table retains the exact value.","canopy"],"leaf-veins":["Compare before and after across several measures in the same unit.","Both endpoints share a scale. Filled leaf area does not measure the change; missing new values retain the baseline only.","forest"],lotus:["Compare performance against a different target on each axis.","Axis order changes the polygon. It is not an overall score; values beyond 100% are preserved in exact readings.","rings"],"petal-box":["Compare medians and supplied quartile summaries.","Whiskers are supplied bounds, not automatically calculated outliers or confidence intervals.","murmuration"],raincloud:["Compare frequencies across supplied numeric bins, including unequal widths.","Read area as frequency and height as density. Bin choices come from your analysis; no density estimate is fitted.","pebble"],dew:["Explore two measurements and an optional size variable.","Droplet area encodes weight. Overlap can obscure records; correlation does not establish cause.","echo"],"wind-rose":["Compare amounts across 4, 8, or 16 compass directions.","Sector area encodes value, so radius is nonlinear. Directions must be evenly spaced and ordered clockwise.","forest"],dune:["Compare frequency profiles across up to four cohorts.","Ridge heights are supplied frequencies on a shared scale, not fitted densities. Nulls break the ridge.","petal-box"],glacier:["Explain how signed changes produce a final running total.","Changes must be known. Missing steps would make every subsequent total unknowable.","growth-history"],sediment:["Read changing composition across aligned numeric positions.","Only the bottom layer has a flat baseline. Use Dune to compare individual profiles directly.","dune"],delta:["Explain one budget or supply split among destinations.","Ribbon width measures amount; branch position has no geographic meaning. Zero flows have no width.","canopy"],estuary:["Follow transfers from several sources to several destinations.","It is a two-column flow diagram, not a general multi-stage Sankey. Source and destination sets are distinct.","mycelium"],pitcher:["Find where a sequential funnel loses participants.","Counts must not increase. Retention is relative to the preceding stage; a zero denominator gives no percentage.","forest"],firefly:["Locate bursts and gaps in event streams.","Position is supplied event time; glow shows magnitude. Coincident events may overlap and remain in the inspector.","honeycomb"],migration:["Follow an ordered route through planar coordinates.","Coordinates use equal physical scale on both axes. This is not a geographic projection, and arrows do not infer unsupplied stops.","dew"],murmuration:["Compare raw observations across a few cohorts.","Vertical jitter has no numeric meaning. Crowded birds may overlap; use Petal Box for compact distribution summaries.","petal-box"],"coral-range":["Compare estimates with explicitly supplied uncertainty bounds.","Bounds mean what your application defines. A displayed interval does not automatically imply statistical confidence.","petal-box"],pebble:["Read empirical percentiles without choosing bins.","Each sample has equal weight and ties share a cumulative height. This is not a fitted population distribution.","raincloud"],nautilus:["Preserve chronology across up to four repeating cycles.","Angle wraps at cycleLength; the spiral turn carries chronology. Radius itself is not a separate measurement.","tidal-rhythm"],frost:["Compare a supplied symmetric correlation matrix.","Correlation does not imply causation. The chart validates symmetry and range, not whether a matrix is statistically realizable.","dew"],echo:["Look for relationships between adjacent ordered readings.","Pairs use one observation of lag, not a fixed elapsed duration. A diagonal is equality, not a fitted trend.","river"],cairn:["Show individual contributions toward one shared goal.","Stone height measures contribution; width is decorative. All contributors use the chart-level target.","rings"],daylight:["Compare opening and closing times across dated observations.","Times are supplied in a common local clock convention. The chart does not calculate sunrise or timezone shifts.","sundial"],isobar:["Explore equal-value contours in a small complete rectangular grid.","Contours interpolate within triangles and are not new measurements. Coordinate axes preserve equal scale; no extrapolation is made.","dew"],bamboo:["Inspect a small sample of integer scores without hiding original values.","Only integers from 0 to 99 are supported. A stem is tens and a leaf is units; repeated digits are distinct observations.","pebble"]}).map(([e,[t,s,a]])=>[e,{bestFor:t,caution:s,alternative:a,...mt[e]}])),Ca="0.7.0";let La=0;function Sa(e,t,s){if(!/^[a-z][a-z0-9-]*$/.test(e)||e in qe)throw new Error("NaturePlot: chart name must be unique and contain lowercase letters, numbers, or hyphens.");if(typeof t!="function"||!s?.name)throw new Error("NaturePlot: a renderer and metadata are required.");qe[e]=t,Qe[e]={...s}}const za=`
2
+ .np-chart{position:relative;width:100%;max-width:100%;overflow:auto;font:14px/1.6 system-ui,-apple-system,sans-serif;box-sizing:border-box;isolation:isolate}
3
+ .np-chart svg{display:block;width:100%;min-width:640px;height:auto;overflow:visible}
4
+ .np-chart .np-mark{cursor:crosshair;outline:none;transition:filter .18s,opacity .18s}
5
+ .np-chart .np-mark:hover,.np-chart .np-mark:focus{filter:brightness(.86) drop-shadow(0 2px 2px #0002);opacity:1}
6
+ .np-chart .np-mark:focus-visible{outline:2px solid currentColor;outline-offset:4px}
7
+ .np-chart .np-tooltip{position:absolute;z-index:3;box-sizing:border-box;max-width:min(280px,calc(100% - 16px));overflow-wrap:anywhere;padding:10px 14px;border-radius:9px;background:#20382e;color:#fff;font-size:14px;line-height:1.6;box-shadow:0 4px 16px #172b2820;pointer-events:none;white-space:pre-line;transform:translate(-50%,-100%)}
8
+ .np-chart .np-table{border-collapse:collapse;width:100%;margin-top:12px;font-size:14px}
9
+ .np-chart .np-inspector{position:sticky;left:0;box-sizing:border-box;width:100%;padding:12px 15px;border-top:1px solid #8883;background:var(--np-bg);color:var(--np-ink)}
10
+ .np-chart .np-controls{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
11
+ .np-chart .np-controls button{font:inherit;font-size:14px;padding:8px 12px;border:1px solid #8885;border-radius:6px;color:inherit;background:transparent;cursor:pointer;min-height:40px}
12
+ .np-chart .np-controls button:hover{background:#8882}.np-chart .np-controls button:focus-visible{outline:2px solid currentColor;outline-offset:2px}
13
+ .np-chart .np-controls button:disabled{opacity:.4;cursor:default}
14
+ .np-chart .np-reading{font-size:14px;line-height:1.6;margin:9px 0 0;white-space:pre-line;overflow-wrap:anywhere}
15
+ .np-chart.np-has-selection .np-mark:not([aria-pressed=true]){opacity:.22}
16
+ .np-chart .np-series.np-series-muted{opacity:.14}
17
+ .np-chart .np-mark[aria-pressed=true]{filter:drop-shadow(0 0 2px #8888);opacity:1}
18
+ .np-chart .np-table th,.np-chart .np-table td{padding:7px 12px;text-align:left;border-bottom:1px solid #8883}
19
+ .np-chart .np-table caption{text-align:left;font-weight:600;padding:10px 12px}
20
+ .np-chart .np-sr-only{display:block!important;position:absolute!important;top:0!important;left:0!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
21
+ .np-chart.np-essential .np-ornament{display:none}
22
+ .np-chart .np-observation-card{display:flex;align-items:center;gap:16px;margin:2px 0 14px;min-height:78px}
23
+ .np-chart .np-observation-copy{min-width:0;flex:1}.np-chart .np-observation-heading{font-size:12px;text-transform:uppercase;letter-spacing:.08em;opacity:.7;font-weight:600}
24
+ .np-chart .np-observation-card .np-reading{margin:4px 0 0;font-size:15px}
25
+ .np-chart .np-mark:hover .np-focus-halo,.np-chart .np-mark:focus .np-focus-halo,.np-chart .np-mark[aria-pressed=true] .np-focus-halo{opacity:.85}
26
+ @keyframes np-appear{from{opacity:0}to{opacity:1}}
27
+ .np-chart.np-animate>svg{animation:np-appear .5s ease both}
28
+ @media(prefers-reduced-motion:reduce){.np-chart.np-animate>svg{animation:none}.np-chart .np-mark{transition:none}}
29
+ `;class Re{container;id;options;root;svg;destroyed=!1;normalized=[];cleanup;selectObservation;constructor(t,s){const a=typeof t=="string"?document.querySelector(t):t;if(!(a instanceof HTMLElement))throw new Error("NaturePlot: container must be an existing HTML element or a matching selector.");this.container=a,this.id=`natureplot-${++La}`,this.options={...s},this.render(s)}get data(){return this.normalized.map(t=>({...t}))}update(t){if(this.destroyed)throw new Error("NaturePlot: cannot update a destroyed chart.");return this.render({...this.options,...t}),this}setTheme(t){return this.update({theme:t})}select(t){if(this.destroyed)throw new Error("NaturePlot: cannot select on a destroyed chart.");if(!Number.isInteger(t)||t<0||t>=this.normalized.length)throw new Error("NaturePlot: selection index is outside the data.");return this.selectObservation?.(t),this}toSVG(){if(this.destroyed)throw new Error("NaturePlot: cannot export a destroyed chart.");const t=this.svg.cloneNode(!0);return this.options.detail==="essential"&&t.querySelectorAll(".np-ornament").forEach(s=>s.remove()),t.setAttribute("width","640"),t.setAttribute("height","360"),t.setAttribute("role","img"),t.querySelectorAll(".np-focus-halo,.np-specimen-hit").forEach(s=>s.remove()),t.querySelectorAll("[tabindex]").forEach(s=>s.removeAttribute("tabindex")),t.querySelectorAll(".np-mark").forEach(s=>{s.removeAttribute("role"),s.removeAttribute("aria-pressed"),s.removeAttribute("aria-describedby")}),t.querySelectorAll(".np-series-muted").forEach(s=>s.classList.remove("np-series-muted")),new XMLSerializer().serializeToString(t)}download(t=`${this.options.type}.svg`){const s=URL.createObjectURL(new Blob([this.toSVG()],{type:"image/svg+xml;charset=utf-8"})),a=document.createElement("a");a.href=s,a.download=t.endsWith(".svg")?t:`${t}.svg`,document.body.append(a),a.click(),a.remove(),setTimeout(()=>URL.revokeObjectURL(s),1e3)}destroy(){this.destroyed||(this.cleanup?.(),this.root.remove(),this.normalized=[],this.selectObservation=void 0,this.destroyed=!0)}render(t){if(!Object.hasOwn(qe,t.type))throw new Error(`NaturePlot: unknown chart type "${t.type}".`);const s=Nt(t),a=$a(t.theme),n={...t,data:t.data.map($=>({...$})),...t.thresholds?{thresholds:t.thresholds.map($=>({...$}))}:{},theme:a},o=$=>$===null?"No data":(n.formatValue?n.formatValue($):$!==0&&Math.abs($)<.001?String($):new Intl.NumberFormat("en",{maximumSignificantDigits:21}).format($))+(n.unit?` ${n.unit}`:""),r=Ma(n,s,o),l=ka(n,s,o),i=document.createElement("div");i.className=`np-chart${n.detail==="essential"?" np-essential":""}${n.animate===!1?"":" np-animate"}`,i.style.color=a.ink,i.style.setProperty("--np-bg",a.background),i.style.setProperty("--np-ink",a.ink);const d=document.createElement("style");d.textContent=za,i.append(d);const h=document.createElementNS(Ke,"svg");h.setAttribute("viewBox","0 0 640 360"),h.setAttribute("role","group"),h.setAttribute("aria-labelledby",`${this.id}-title`),h.setAttribute("aria-describedby",`${this.id}-desc`),h.setAttribute("font-family","system-ui, -apple-system, sans-serif"),i.append(h);const p=($,L={},x=h,U)=>{const X=document.createElementNS(Ke,$);for(const[ve,pe]of Object.entries(L))X.setAttribute(ve,String($==="text"&&ve==="font-size"&&Number.isFinite(Number(pe))?Math.max(12,Number(pe)):pe));return U!==void 0&&(X.textContent=U),x.append(X),X},u=n.title??`${Qe[n.type].name} chart`;p("title",{id:`${this.id}-title`},h,u),p("desc",{id:`${this.id}-desc`},h,n.description??`${Qe[n.type].encoding} ${r} Use arrow keys to explore observations; Enter to select. An exact data table follows the chart.`),p("rect",{width:640,height:360,rx:4,fill:a.background});const m=document.createElement("div");m.className="np-tooltip",m.id=`${this.id}-tooltip`,m.setAttribute("role","tooltip"),m.hidden=!0,i.append(m);const c=[],f=($,L)=>{const x=$.date?` · ${new Date($.date+"T00:00:00Z").toLocaleDateString("en",{weekday:"short",year:"numeric",month:"short",day:"numeric",timeZone:"UTC"})}`:"",U=et($,n),ve=["sundial","season-wheel","phenology","daylight"].includes(n.type)&&$.value===null?"":`
30
+ ${o($.value)}${U?` / ${o(U)}`:""}`,pe=l.filter(ye=>!["Observation","Date","Value","Target"].includes(ye.header)).map(ye=>`${ye.header}: ${ye.read($,L)}`);return`${Y($,L)}${x}${ve}${pe.length?`
31
+ `+pe.join(`
32
+ `):""}`},y=($,L,x)=>{$.classList.add("np-mark"),$.dataset.index=String(x),$.setAttribute("tabindex",c.length===0?"0":"-1"),$.setAttribute("role","button"),$.setAttribute("aria-pressed","false"),$.setAttribute("aria-label",f(L,x).replace(`
33
+ `,": ")),$.setAttribute("aria-describedby",m.id),c.push($)},g=$=>{const L=o($);return $!==null&&!n.formatValue&&L.length>18?$.toExponential(2)+(n.unit?` ${n.unit}`:""):L},w={svg:h,data:s,options:n,theme:a,id:this.id,width:640,height:360,el:p,mark:y,format:g};s.length?qe[n.type](w):(p("circle",{cx:320,cy:145,r:32,fill:a.grid,opacity:.5}),p("path",{d:"M320 163 V135 Q341 123 336 141 Q328 148 320 143 M320 151 Q298 143 307 132 Q319 134 320 144",fill:"none",stroke:a.colors[0],"stroke-width":2}),p("text",{x:320,y:213,"text-anchor":"middle",fill:a.ink,"font-size":16},h,"Room to grow"),p("text",{x:320,y:240,"text-anchor":"middle",fill:a.muted,"font-size":12},h,"Add your first observation to begin.")),c.sort(($,L)=>Number($.dataset.index)-Number(L.dataset.index)),c.forEach(($,L)=>$.setAttribute("tabindex",L===0?"0":"-1"));const b=document.createElement("table");b.className=`np-table${n.showTable?"":" np-sr-only"}`;const E=b.createCaption();E.textContent=`${u} — exact data`;const T=b.createTHead().insertRow();l.forEach($=>{const L=document.createElement("th");L.scope="col",L.textContent=$.header,T.append(L)});const R=b.createTBody();if(s.forEach(($,L)=>{const x=R.insertRow();l.forEach(U=>{x.insertCell().textContent=U.read($,L)})}),r){const $=b.createTFoot().insertRow().insertCell();$.colSpan=l.length,$.textContent=r}b.id=`${this.id}-table`;const O=document.createElement("div");O.className="np-inspector";const ee=document.createElement("div");ee.className="np-controls",ee.setAttribute("role","group"),ee.setAttribute("aria-label","Chart inspection controls");const ge=($,L)=>{const x=document.createElement("button");return x.type="button",x.textContent=$,x.setAttribute("aria-label",L),ee.append(x),x},He=ge("Previous","Previous observation"),Oe=ge("Next","Next observation"),Ce=ge("Clear","Clear selection"),we=ge(n.showTable?"Hide data":"Show data","Toggle chart data table");we.setAttribute("aria-expanded",String(!!n.showTable)),we.setAttribute("aria-controls",b.id);const ue=document.createElement("p");ue.className="np-reading",ue.setAttribute("role","status"),ue.textContent=c.length?"Select a mark, or use Next to explore. Scroll the chart horizontally on small screens.":"No observations yet.";const Ue=document.createElement("div");Ue.className="np-observation-card";const Fe=document.createElement("div");Fe.className="np-observation-copy";const We=document.createElement("strong");We.className="np-observation-heading",We.textContent="Explore an observation",ue.textContent=c.length?Ze[n.type]??ue.textContent:"No observations yet.",Fe.append(We,ue),Ue.append(Fe),O.append(Ue,ee),n.interactive!==!1&&i.append(O),i.append(b),He.disabled=Oe.disabled=!c.length,Ce.disabled=!0;let Le=-1;const Te=($,L)=>{const x=s[Number($.dataset.index)];m.textContent=f(x,Number($.dataset.index)),m.hidden=!1;const U=i.getBoundingClientRect(),X=$.getBoundingClientRect(),ve=L?L.clientX-U.left:X.left+X.width/2-U.left,pe=L?L.clientY-U.top:X.top-U.top,ye=Math.min(m.offsetWidth/2+8,i.clientWidth/2);m.style.left=`${i.scrollLeft+Math.max(ye,Math.min(i.clientWidth-ye,ve))}px`,m.style.top=`${i.scrollTop+Math.max(m.offsetHeight+8,pe-12)}px`},xe=$=>$ instanceof Element?$.closest(".np-mark"):null,ft=$=>{const L=xe($.target);L?Te(L,$):m.hidden=!0},gt=$=>{const L=xe($.target);L&&(c.forEach(x=>x.setAttribute("tabindex",x===L?"0":"-1")),Te(L))},Se=()=>{m.hidden=!0},ze=$=>{const L=Number($.dataset.index),x=Object.freeze({...s[L]});Le=c.indexOf($),i.classList.add("np-has-selection"),c.forEach(X=>X.setAttribute("aria-pressed",String(X===$))),h.querySelectorAll("[data-series]").forEach(X=>X.classList.toggle("np-series-muted",X.dataset.series!==(x.track??"Series"))),ue.textContent=`${L+1} of ${s.length} · ${f(s[L],L)}`;const U=O.querySelector(".np-observation-heading");U&&(U.textContent="Selected observation"),Ce.disabled=!1,n.onSelect?.(x,L),i.dispatchEvent(new CustomEvent("natureplot:select",{bubbles:!0,detail:{point:x,index:L,chart:this}}))},yt=()=>{c.length&&ze(c[(Le<0?c.length-1:Le-1+c.length)%c.length])};He.addEventListener("click",yt);const bt=()=>{c.length&&ze(c[(Le+1)%c.length])};Oe.addEventListener("click",bt);const je=()=>{Le=-1,i.classList.remove("np-has-selection"),h.querySelectorAll(".np-series-muted").forEach(L=>L.classList.remove("np-series-muted")),c.forEach(L=>L.setAttribute("aria-pressed","false")),ue.textContent=Ze[n.type]??"Selection cleared. Select a mark or use Next to explore.";const $=O.querySelector(".np-observation-heading");$&&($.textContent="Explore an observation"),Ce.disabled=!0,Se()};Ce.addEventListener("click",je);const wt=()=>{const $=b.classList.toggle("np-sr-only")===!1;we.textContent=$?"Hide data":"Show data",we.setAttribute("aria-expanded",String($))};we.addEventListener("click",wt);const vt=$=>{const L=xe($.target);L&&(Te(L),ze(L))},$t=$=>{const L=xe($.target);if(!L)return;const x=c.indexOf(L);let U=x;if(["ArrowRight","ArrowDown"].includes($.key))U=(x+1)%c.length;else if(["ArrowLeft","ArrowUp"].includes($.key))U=(x-1+c.length)%c.length;else if($.key==="Home")U=0;else if($.key==="End")U=c.length-1;else if(["Enter"," "].includes($.key)){$.preventDefault(),Te(L),ze(L);return}else if($.key==="Escape"){je();return}else return;$.preventDefault(),c[U].focus()};i.addEventListener("pointermove",ft),i.addEventListener("pointerleave",Se),i.addEventListener("focusin",gt),i.addEventListener("focusout",Se),i.addEventListener("click",vt),i.addEventListener("keydown",$t),this.cleanup?.(),this.root?this.root.replaceWith(i):this.container.append(i),this.root=i,this.svg=h,this.options=n,this.normalized=s,this.selectObservation=$=>{const L=c.find(x=>Number(x.dataset.index)===$);L&&ze(L)},this.cleanup=()=>{He.removeEventListener("click",yt),Oe.removeEventListener("click",bt),Ce.removeEventListener("click",je),we.removeEventListener("click",wt),i.removeEventListener("pointermove",ft),i.removeEventListener("pointerleave",Se),i.removeEventListener("focusin",gt),i.removeEventListener("focusout",Se),i.removeEventListener("click",vt),i.removeEventListener("keydown",$t)}}}function Pa(e,t){return new Re(e,t)}return ae.NaturePlot=Re,ae.chartGuidance=Ea,ae.chartTypes=Qe,ae.chartUseCases=mt,ae.createChart=Pa,ae.default=Re,ae.registerChart=Sa,ae.themes=pt,ae.version=Ca,Object.defineProperties(ae,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),ae})({});
natureplot/_version.py ADDED
@@ -0,0 +1,2 @@
1
+ # Generated from package.json by scripts/prepare-python.mjs.
2
+ __version__ = "0.7.0"
natureplot/chart.py ADDED
@@ -0,0 +1,158 @@
1
+ """Python data interface to the packaged NaturePlot JavaScript renderer."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Iterable, Mapping
5
+ from datetime import date, datetime
6
+ from functools import lru_cache
7
+ from html import escape
8
+ from importlib.resources import files
9
+ import json
10
+ from pathlib import Path
11
+ import re
12
+ from typing import Any
13
+ from uuid import uuid4
14
+
15
+
16
+ @lru_cache(maxsize=1)
17
+ def _catalog() -> dict[str, Any]:
18
+ return json.loads(files("natureplot").joinpath("_assets/catalog.json").read_text(encoding="utf-8"))
19
+
20
+
21
+ @lru_cache(maxsize=1)
22
+ def _bundle() -> str:
23
+ bundle = files("natureplot").joinpath("_assets/natureplot.global.js").read_text(encoding="utf-8")
24
+ # Keep script boundaries safe even if a future renderer contains this literal.
25
+ return re.sub(r"</script", r"<\\/script", bundle, flags=re.IGNORECASE)
26
+
27
+
28
+ def chart_types() -> dict[str, dict[str, str]]:
29
+ """Return a fresh catalog of all 50 chart names, categories, and encodings."""
30
+ return json.loads(json.dumps(_catalog()["charts"]))
31
+
32
+
33
+ def _json_value(value: Any) -> Any:
34
+ if type(value) is int and abs(value) > 2**53 - 1:
35
+ raise ValueError("Integers must fit JavaScript's exact range (±9007199254740991). Rescale larger quantities.")
36
+ if isinstance(value, datetime):
37
+ raise TypeError("Use a datetime.date or an ISO string for calendar dates; convert datetime with .date().")
38
+ if isinstance(value, date):
39
+ return value.isoformat()
40
+ if isinstance(value, Mapping):
41
+ if any(not isinstance(key, str) for key in value):
42
+ raise TypeError("Chart objects must have string keys.")
43
+ return {key: _json_value(item) for key, item in value.items()}
44
+ if isinstance(value, (list, tuple)):
45
+ return [_json_value(item) for item in value]
46
+ return value
47
+
48
+
49
+ def _script_json(value: Any) -> str:
50
+ text = json.dumps(value, ensure_ascii=False, allow_nan=False, separators=(",", ":"))
51
+ for original, encoded in (("&", "\\u0026"), ("<", "\\u003c"), (">", "\\u003e"), ("\u2028", "\\u2028"), ("\u2029", "\\u2029")):
52
+ text = text.replace(original, encoded)
53
+ return text
54
+
55
+
56
+ class Chart:
57
+ """Create an offline interactive chart from observations and chart options.
58
+
59
+ Options accept JavaScript camelCase or Python snake_case names. Python
60
+ callbacks are not supported: interaction runs inside the browser. Detailed
61
+ measurement validation is performed by the shared JavaScript renderer.
62
+ """
63
+
64
+ def __init__(self, chart_type: str, data: Iterable[Mapping[str, Any]], **options: Any) -> None:
65
+ catalog = _catalog()
66
+ if not isinstance(chart_type, str) or chart_type not in catalog["charts"]:
67
+ raise ValueError(f"Unknown chart type {chart_type!r}. See natureplot.chart_types().")
68
+ aliases = {re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower(): name for name in catalog["options"]}
69
+ normalized: dict[str, Any] = {}
70
+ for name, value in options.items():
71
+ key = aliases.get(name, name)
72
+ if name in ("on_select", "onSelect", "format_value", "formatValue"):
73
+ raise TypeError("Python callbacks cannot run in exported HTML. Use the JavaScript API for browser callbacks.")
74
+ if key not in catalog["options"]:
75
+ raise TypeError(f"Unknown chart option {name!r}.")
76
+ if key in normalized:
77
+ raise TypeError(f"Chart option {key!r} was supplied more than once.")
78
+ normalized[key] = value
79
+ if isinstance(data, (str, bytes, Mapping)):
80
+ raise TypeError("data must be an iterable of observation dictionaries.")
81
+ observations = list(data)
82
+ if any(not isinstance(point, Mapping) for point in observations):
83
+ raise TypeError("Each observation must be a dictionary, such as {'label': 'Mon', 'value': 4}.")
84
+ theme = normalized.get("theme", "meadow")
85
+ if not isinstance(theme, Mapping) and (not isinstance(theme, str) or theme not in catalog["themes"]):
86
+ raise ValueError("theme must be meadow, ocean, autumn, twilight, or a theme dictionary.")
87
+ if normalized.get("detail", "natural") not in ("natural", "essential"):
88
+ raise ValueError("detail must be natural or essential.")
89
+ for name in ("title", "description", "unit", "xLabel", "yLabel", "weightLabel"):
90
+ if name in normalized and not isinstance(normalized[name], str):
91
+ raise TypeError(f"{name} must be a string.")
92
+ # Copy through JSON to isolate the chart from subsequent caller mutations.
93
+ serialized = _json_value({"type": chart_type, "data": observations, **normalized})
94
+ try:
95
+ self._options = json.loads(json.dumps(serialized, allow_nan=False))
96
+ except (ValueError, OverflowError) as error:
97
+ raise ValueError("Chart data must contain finite, JSON-compatible values; use None for missing readings.") from error
98
+
99
+ def to_dict(self) -> dict[str, Any]:
100
+ """Return a defensive copy of the browser options, with camelCase keys."""
101
+ return json.loads(json.dumps(self._options))
102
+
103
+ def to_html(self, *, full_document: bool = True, include_js: bool = True) -> str:
104
+ """Render HTML; JavaScript creates the SVG when the document is opened.
105
+
106
+ By default the complete runtime is embedded, with no CDN or Node.js
107
+ dependency. With include_js=False, the host must load the matching
108
+ natureplot.global.js before the fragment. No JavaScript is executed by
109
+ Python. Inline scripts must be allowed by the host's CSP.
110
+ """
111
+ identifier = "natureplot-" + uuid4().hex
112
+ title = self._options.get("title") or _catalog()["charts"][self._options["type"]]["name"]
113
+ safe_title = escape(title, quote=True)
114
+ runtime = f"<script>{_bundle()}</script>" if include_js else ""
115
+ config = _script_json(self._options)
116
+ fragment = f'''<section class="natureplot-python" aria-label="{safe_title}">
117
+ <div id="{identifier}"></div>
118
+ <p id="{identifier}-error" role="alert" hidden></p>
119
+ <div style="padding:12px 0"><button id="{identifier}-export" type="button" hidden style="font:inherit;padding:8px 14px;border:1px solid #87967d;border-radius:6px;background:transparent;color:inherit;cursor:pointer">Download SVG</button></div>
120
+ <noscript>This interactive chart requires JavaScript.</noscript>
121
+ </section>
122
+ {runtime}
123
+ <script type="application/json" id="{identifier}-data">{config}</script>
124
+ <script>
125
+ (function () {{
126
+ const host = document.getElementById("{identifier}");
127
+ const error = document.getElementById("{identifier}-error");
128
+ const download = document.getElementById("{identifier}-export");
129
+ try {{
130
+ if (!window.NaturePlot) throw new Error("Load natureplot.global.js before this chart.");
131
+ const chart = window.NaturePlot.createChart(host, JSON.parse(document.getElementById("{identifier}-data").textContent));
132
+ host.natureplot = chart;
133
+ download.hidden = false;
134
+ download.addEventListener("click", function () {{ chart.download("natureplot.svg"); }});
135
+ }} catch (cause) {{
136
+ error.hidden = false;
137
+ error.textContent = cause instanceof Error ? cause.message : String(cause);
138
+ }}
139
+ }})();
140
+ </script>'''
141
+ if not full_document:
142
+ return fragment
143
+ return f'''<!doctype html>
144
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
145
+ <title>{safe_title}</title>
146
+ <style>body{{margin:0;padding:24px;background:#f9faf6;color:#2b4033;font:16px/1.6 system-ui,sans-serif}}main{{max-width:960px;margin:auto}}h1{{font-size:24px;line-height:1.3}}[role=alert]{{padding:16px;border:1px solid #b65345;border-radius:8px;overflow-wrap:anywhere}}</style>
147
+ </head><body><main><h1>{safe_title}</h1>{fragment}</main></body></html>'''
148
+
149
+ def write_html(self, path: str | Path) -> Path:
150
+ """Write a standalone HTML file and return its absolute path."""
151
+ target = Path(path).expanduser().resolve()
152
+ target.write_text(self.to_html(), encoding="utf-8")
153
+ return target
154
+
155
+ def _repr_html_(self) -> str:
156
+ """Notebook representation with each chart isolated in its own iframe."""
157
+ title = self._options.get("title") or "NaturePlot chart"
158
+ return f'<iframe title="{escape(title, quote=True)}" sandbox="allow-scripts allow-downloads" srcdoc="{escape(self.to_html(), quote=True)}" width="100%" height="600" style="border:0;display:block"></iframe>'
natureplot/py.typed ADDED
File without changes
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.5
2
+ Name: natureplot
3
+ Version: 0.7.0
4
+ Summary: Create interactive nature-inspired SVG charts from Python, with an offline browser renderer.
5
+ Project-URL: JavaScript package, https://www.npmjs.com/package/natureplot
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: charts,html,jupyter,nature,svg,visualization
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Topic :: Scientific/Engineering :: Visualization
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+
19
+ # NaturePlot for Python
20
+
21
+ Create interactive nature-inspired SVG charts from Python data. All 50 NaturePlot.js chart types are included: growing calendars, forests, rivers, petals, natural instruments, and more.
22
+
23
+ The Python package embeds the NaturePlot.js browser renderer. It generates HTML for reports, notebooks, and web applications. Charts retain hover readings, keyboard selection, exact data tables, and SVG downloads. No Node.js, CDN, or Python runtime dependencies are needed by users.
24
+
25
+ ## Install
26
+
27
+ Choose your Python package manager:
28
+
29
+ ```sh
30
+ python -m pip install natureplot
31
+ uv add natureplot
32
+ poetry add natureplot
33
+ ```
34
+
35
+ Python 3.10 or newer is required.
36
+
37
+ ## Create a chart
38
+
39
+ ```python
40
+ from natureplot import Chart
41
+
42
+ chart = Chart(
43
+ "forest",
44
+ data=[
45
+ {"label": "Online", "value": 42},
46
+ {"label": "Retail", "value": 28},
47
+ {"label": "Wholesale", "value": 18},
48
+ ],
49
+ title="Orders by sales channel",
50
+ unit="orders",
51
+ theme="meadow",
52
+ )
53
+ chart.write_html("orders.html")
54
+ ```
55
+
56
+ Open `orders.html` in a browser. The complete JavaScript runtime is embedded, so the chart works offline. Use the **Download SVG** button to save the rendered SVG.
57
+
58
+ ## Notebooks
59
+
60
+ Leave `chart` as the final expression in a notebook cell, or use `display(chart)`. The rich HTML representation uses an isolated iframe and requires a notebook frontend that permits JavaScript in trusted output. Each chart includes its runtime and does not modify the notebook's global JavaScript environment.
61
+
62
+ ```python
63
+ from datetime import date
64
+ from natureplot import Chart
65
+
66
+ chart = Chart(
67
+ "garden",
68
+ data=[{"date": date(2026, 9, 1), "value": 8}],
69
+ start_date=date(2026, 9, 1),
70
+ days=30,
71
+ title="September orders",
72
+ )
73
+ chart
74
+ ```
75
+
76
+ ## Options and data
77
+
78
+ Options accept both Python `snake_case` and JavaScript `camelCase`: for example, `start_date` or `startDate`, `show_table` or `showTable`. Passing both spellings of one option is an error. Observation dictionary keys use the JavaScript API names, including `observedAt`, `expectedStart`, and `expectedEnd`.
79
+
80
+ Use `None` for missing readings, zero for measured zero, and `datetime.date` for calendar dates. NaN and Infinity are rejected. Integers must be within JavaScript's exact range, ±9,007,199,254,740,991; rescale larger quantities. For pandas, pass `frame.to_dict(orient="records")` with JSON-compatible values and replace missing numeric values with `None` first.
81
+
82
+ ```python
83
+ from natureplot import chart_types
84
+
85
+ for key, metadata in chart_types().items():
86
+ print(key, metadata["category"], metadata["encoding"])
87
+ ```
88
+
89
+ General input validation happens in Python. Chart-specific measurement rules, such as valid timeline intervals and maximum category counts, are checked by the shared JavaScript renderer when the page opens. Rendering errors appear as readable alerts in the document.
90
+
91
+ ## API
92
+
93
+ - `Chart(chart_type, data, **options)` copies and serializes your data. Later mutations to the original list do not change the chart.
94
+ - `chart.to_dict()` returns a fresh dictionary of JavaScript chart options.
95
+ - `chart.to_html()` returns a complete, self-contained HTML document.
96
+ - `chart.to_html(full_document=False)` returns an embeddable HTML fragment. Scripts must be allowed and executed by the host; inserting it with `innerHTML` alone does not execute its scripts.
97
+ - `chart.to_html(full_document=False, include_js=False)` omits the runtime. Load the matching `natureplot.global.js` before the fragment.
98
+ - `chart.write_html(path)` writes UTF-8 HTML and returns the absolute `Path`.
99
+ - `chart_types()` returns metadata for all 50 built-in charts.
100
+
101
+ Interaction runs in the browser. Python `on_select` callbacks, live kernel synchronization, static PNG/PDF generation, server-side SVG rendering, and JavaScript custom renderer registration are not part of this Python API. Use the JavaScript API for client-side callbacks. Rendering allows inline scripts and styles, so a host's Content Security Policy must permit those or provide an integration of its own.
102
+
103
+ ## Build from the monorepo
104
+
105
+ From the repository root:
106
+
107
+ ```sh
108
+ npm run prepare:python
109
+ python3 -m venv .venv-packaging
110
+ .venv-packaging/bin/python -m pip install build twine
111
+ .venv-packaging/bin/python -m build python
112
+ .venv-packaging/bin/python -m twine check python/dist/*
113
+ python3 -m unittest discover -s python/tests
114
+ ```
115
+
116
+ `prepare:python` rebuilds the browser runtime and copies the version, catalog, runtime, and MIT license into this package. Building rejects missing assets, version mismatches, or a mismatched runtime checksum. The source distribution also includes the bundled runtime and can build a wheel without Node.js.
117
+
118
+ See `docs/PUBLISHING.md` in the monorepo for the npm and PyPI publishing steps. This project is MIT licensed.
@@ -0,0 +1,10 @@
1
+ natureplot/__init__.py,sha256=yjDtyfNHn56BLqfVMnVDt0WIg8OxCI3GN92RIevzkzE,202
2
+ natureplot/_version.py,sha256=R_W37f06ydSqGFQNruafn3h6cfc85AMEfFpUYNqcV9Q,83
3
+ natureplot/chart.py,sha256=bz6wG-aTbnkOQ-7JOpVImeUv3a1rnjbsqdn-ZkwQFf4,8441
4
+ natureplot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ natureplot/_assets/catalog.json,sha256=Z-Jj-JmF04GXwG2dBSlq3CpwV0OueXFzEgupezdYIx4,26505
6
+ natureplot/_assets/natureplot.global.js,sha256=fDJawEASsDTb6ZlTYXUrM-7RFQ2Aub0wMO3ngs5yfNs,154481
7
+ natureplot-0.7.0.dist-info/METADATA,sha256=-GTBdQVeS_uBbkcV-MJ93NGMlI5DHD6cTMVgrnQ6QlA,5506
8
+ natureplot-0.7.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
+ natureplot-0.7.0.dist-info/licenses/LICENSE,sha256=_srH4KXwVGW9Hmt84tiCw4bFo8imtOOtpbDUV6b47Ng,1083
10
+ natureplot-0.7.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NaturePlot.js contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.