@krizaka/ui 3.32.0 → 3.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/map.js CHANGED
@@ -711,15 +711,21 @@ function Map({
711
711
  latest.current = { onMoveEnd, onPress };
712
712
  });
713
713
  const styleOf = React2.useCallback((sc) => mapStyle({ scheme: sc, brand, lang, ...region !== void 0 ? { region } : {}, ...world !== void 0 ? { world } : {} }), [brand, lang, region, world]);
714
+ const wanted = React2.useRef({ scheme, styleOf });
715
+ React2.useEffect(() => {
716
+ wanted.current = { scheme, styleOf };
717
+ });
718
+ const applied = React2.useRef(null);
714
719
  React2.useEffect(() => {
715
720
  if (!host.current) return;
716
721
  let alive = true;
717
722
  let created = null;
718
723
  (hasWebGL() ? loadEngine() : Promise.reject(new Error("no WebGL"))).then((ml) => {
719
724
  if (!alive || !host.current) return;
725
+ applied.current = wanted.current;
720
726
  created = new ml.Map({
721
727
  container: host.current,
722
- style: styleOf(scheme),
728
+ style: wanted.current.styleOf(wanted.current.scheme),
723
729
  center: [...center],
724
730
  zoom,
725
731
  interactive,
@@ -750,13 +756,10 @@ function Map({
750
756
  created?.remove();
751
757
  };
752
758
  }, []);
753
- const first = React2.useRef(true);
754
759
  React2.useEffect(() => {
755
760
  if (!instance) return;
756
- if (first.current) {
757
- first.current = false;
758
- return;
759
- }
761
+ if (applied.current?.scheme === scheme && applied.current.styleOf === styleOf) return;
762
+ applied.current = { scheme, styleOf };
760
763
  instance.setStyle(styleOf(scheme));
761
764
  }, [instance, scheme, styleOf]);
762
765
  React2.useEffect(() => {
package/dist/map.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/map/assets.ts","../src/map/geo.ts","../src/map/location-picker.tsx","../src/tv.ts","../src/cn.ts","../src/map/map.tsx","../src/media.ts","../../tokens/dist/native.js","../src/map/style.ts","../src/map/static-map.tsx"],"sourcesContent":["// Where the basemap lives: Protomaps PMTiles extracts on the Krizaka CDN (Bunny), uploaded by tools/map-assets.mjs.\n// No API key, no metered service: one file per region, read by HTTP range requests. A new extract is a new file (its\n// build date in the name) and a new default here.\n\nconst CDN = \"https://krizaka-assets.b-cdn.net/maps/v1\";\n\n/** The basemap extracts: OpenStreetMap data built by Protomaps (ODbL). `world` up to zoom 6, `quebec` (Montréal–Québec) up to 14. */\nexport const MAP_TILES = {\n world: `${CDN}/tiles/world-20261012.pmtiles`,\n quebec: `${CDN}/tiles/quebec-20261012.pmtiles`,\n} as const;\n\n/** The glyphs of the labels (Noto Sans, SIL OFL): `{fontstack}` and `{range}` filled by MapLibre. */\nexport const MAP_GLYPHS = `${CDN}/fonts/{fontstack}/{range}.pbf`;\n\n/** The words every map shows, as the licences ask: OpenStreetMap's contributors (ODbL) and Protomaps. */\nexport const MAP_ATTRIBUTION = { osm: \"© OpenStreetMap contributors\", osmUrl: \"https://www.openstreetmap.org/copyright\", protomaps: \"Protomaps\", protomapsUrl: \"https://protomaps.com\" } as const;\n\nlet base: string | null = null;\n\n/**\n * Where the maps read the tiles and the glyphs, when not from the CDN itself: a same-origin path that proxies it\n * (`https://www.krizaka.com/maps/v1`, a rewrite of the site) — no cross-origin request at all. Absolute URL, no trailing slash.\n */\nexport function setMapAssetsBase(url: string | null) {\n base = url ? url.replace(/\\/+$/, \"\") : null;\n}\n\n/** The tiles and glyphs the maps read now: the CDN's, or under the base set by `setMapAssetsBase`. */\nexport function mapAssets() {\n const at = (u: string) => (base ? u.replace(CDN, base) : u);\n return { tiles: { world: at(MAP_TILES.world), quebec: at(MAP_TILES.quebec) }, glyphs: at(MAP_GLYPHS) };\n}\n","/// <reference types=\"geojson\" />\n\n// Plain geometry for the maps, web and native (no DOM, no React): distances, circles, the Web Mercator projection.\n\n/** A position: longitude, latitude (GeoJSON's order, MapLibre's `LngLat`). */\nexport type LngLat = readonly [lng: number, lat: number];\n\nconst R = 6_371_008.8;\nconst rad = (d: number) => (d * Math.PI) / 180;\n\n/** The great-circle distance between two positions, in metres (haversine). */\nexport function distanceMeters(a: LngLat, b: LngLat): number {\n const dLat = rad(b[1] - a[1]);\n const dLng = rad(b[0] - a[0]);\n const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a[1])) * Math.cos(rad(b[1])) * Math.sin(dLng / 2) ** 2;\n return 2 * R * Math.asin(Math.min(1, Math.sqrt(h)));\n}\n\n/** A circle of `radius` metres around `center` as a GeoJSON polygon (`steps` points): a delivery zone, a search radius. */\nexport function circlePolygon(center: LngLat, radius: number, steps = 64): GeoJSON.Feature<GeoJSON.Polygon> {\n const [lng, lat] = center;\n const d = radius / R;\n const ring: [number, number][] = [];\n for (let i = 0; i <= steps; i++) {\n const brg = (2 * Math.PI * i) / steps;\n const lat2 = Math.asin(Math.sin(rad(lat)) * Math.cos(d) + Math.cos(rad(lat)) * Math.sin(d) * Math.cos(brg));\n const lng2 = rad(lng) + Math.atan2(Math.sin(brg) * Math.sin(d) * Math.cos(rad(lat)), Math.cos(d) - Math.sin(rad(lat)) * Math.sin(lat2));\n ring.push([(lng2 * 180) / Math.PI, (lat2 * 180) / Math.PI]);\n }\n return { type: \"Feature\", properties: {}, geometry: { type: \"Polygon\", coordinates: [ring] } };\n}\n\n/** The Web Mercator pixel of a position at a zoom (256-px tiles): what a static map draws without an engine. */\nexport function project([lng, lat]: LngLat, zoom: number): [number, number] {\n const scale = 256 * 2 ** zoom;\n const s = Math.sin(rad(Math.max(-85.0511, Math.min(85.0511, lat))));\n return [((lng + 180) / 360) * scale, (0.5 - Math.log((1 + s) / (1 - s)) / (4 * Math.PI)) * scale];\n}\n\n/** A distance written the locale's way: metres under 1 km, kilometres (one decimal under 10) above. */\nexport function formatDistance(meters: number, locale: string): string {\n if (meters < 1000) return new Intl.NumberFormat(locale, { style: \"unit\", unit: \"meter\", maximumFractionDigits: 0 }).format(Math.round(meters / 10) * 10);\n return new Intl.NumberFormat(locale, { style: \"unit\", unit: \"kilometer\", maximumFractionDigits: meters < 10_000 ? 1 : 0 }).format(meters / 1000);\n}\n\n/** The length of a path of positions, in metres (the sum of its great-circle legs). */\nexport function pathLength(path: readonly LngLat[]): number {\n let total = 0;\n for (let i = 1; i < path.length; i++) total += distanceMeters(path[i - 1]!, path[i]!);\n return total;\n}\n\n/** The position at `fraction` (0–1, clamped) of the path's length, with the heading of the leg it is on (degrees clockwise from north). */\nexport function pointAlong(path: readonly LngLat[], fraction: number): { position: LngLat; heading: number } {\n const first = path[0] ?? [0, 0];\n if (path.length < 2) return { position: first, heading: 0 };\n const target = Math.max(0, Math.min(1, fraction)) * pathLength(path);\n let walked = 0;\n for (let i = 1; i < path.length; i++) {\n const a = path[i - 1]!;\n const b = path[i]!;\n const leg = distanceMeters(a, b);\n if (walked + leg >= target || i === path.length - 1) {\n const t = leg === 0 ? 0 : Math.max(0, Math.min(1, (target - walked) / leg));\n const heading = (Math.atan2(b[0] - a[0], b[1] - a[1]) * 180) / Math.PI;\n return { position: [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t], heading: (heading + 360) % 360 };\n }\n walked += leg;\n }\n return { position: first, heading: 0 };\n}\n\n/** The first `fraction` (0–1) of the path, as a GeoJSON line: what a route has drawn so far. */\nexport function sliceLine(path: readonly LngLat[], fraction: number): GeoJSON.Feature<GeoJSON.LineString> {\n const f = Math.max(0, Math.min(1, fraction));\n const total = pathLength(path);\n const coordinates: [number, number][] = [];\n if (path.length) coordinates.push([...path[0]!] as [number, number]);\n let walked = 0;\n for (let i = 1; i < path.length && f > 0; i++) {\n const a = path[i - 1]!;\n const b = path[i]!;\n const leg = distanceMeters(a, b);\n if (walked + leg <= f * total) coordinates.push([...b] as [number, number]);\n else {\n const t = leg === 0 ? 0 : (f * total - walked) / leg;\n coordinates.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n break;\n }\n walked += leg;\n }\n if (coordinates.length === 1) coordinates.push([...coordinates[0]!]);\n return { type: \"Feature\", properties: {}, geometry: { type: \"LineString\", coordinates } };\n}\n","// Client. A place chosen by moving the map under a fixed pin — a meeting point, a delivery address, the centre of a\n// search — with its distance from where the person is (a chip) and \"near me\": the browser asks for the position only when\n// the button is pressed, and every answer has its words (locating, refused, unavailable). The value is a position.\nimport * as React from \"react\";\n\nimport { tv } from \"../tv\";\nimport { distanceMeters, formatDistance, type LngLat } from \"./geo\";\nimport { Map, type MapProps, RadiusCircle, UserPosition } from \"./map\";\n\nexport const locationPicker = tv({\n slots: {\n root: \"flex flex-col gap-2\",\n frame: \"relative\",\n pin: \"pointer-events-none absolute left-1/2 top-1/2 z-10 -translate-x-1/2 -translate-y-full text-accent drop-shadow-md\",\n bar: \"absolute inset-x-3 bottom-8 z-10 flex flex-wrap items-center justify-between gap-2\",\n chip: \"rounded-full border border-border-default bg-surface-1 px-3 py-1.5 text-xs font-semibold tabular-nums text-fg shadow-floating\",\n near:\n \"inline-flex h-10 items-center gap-2 rounded-full border border-border-default bg-surface-1 bg-sheen px-4 text-sm font-semibold text-fg shadow-floating inset-shadow-edge \" +\n \"transition-colors hover:border-border-strong focus-visible:outline-hidden focus-visible:focus-ring disabled:opacity-60\",\n status: \"text-xs text-fg-secondary data-[tone=danger]:text-fg-danger\",\n },\n});\n\nexport type LocationPickerLabels = {\n /** The button that centres the map on the person (\"Near me\"). */\n nearMe: string;\n /** While the position is asked (\"Finding you…\"). */\n locating: string;\n /** The person refused (\"Location is off for this site. Move the map instead.\"). */\n denied: string;\n /** No position (no GPS, a timeout) (\"We could not find you. Move the map instead.\"). */\n unavailable: string;\n /** The person's place in the list of places (\"You are here\"). */\n you: string;\n /** The distance chip, from a written distance (\"1.2 km from you\"). */\n distance: (distance: string) => string;\n};\n\nexport const locationPickerLabels: LocationPickerLabels = {\n nearMe: \"Near me\",\n locating: \"Finding you…\",\n denied: \"Location is off for this site. Move the map instead.\",\n unavailable: \"We could not find you. Move the map instead.\",\n you: \"You are here\",\n distance: (d) => `${d} from you`,\n};\n\nexport type LocationPickerProps = Pick<MapProps, \"scheme\" | \"brand\" | \"lang\" | \"region\" | \"world\" | \"fallback\" | \"className\"> & {\n /** The picker's accessible name (\"Meeting point\"), passed translated. */\n label: string;\n /** The chosen position (controlled): the centre of the map. */\n value: LngLat;\n /** Called when the map rests on a new position. */\n onValueChange: (value: LngLat) => void;\n /** The BCP 47 locale of the distance. */\n locale: string;\n /** The zoom at first. Default 14. */\n zoom?: number;\n /** A radius in metres drawn around the chosen position (a delivery zone, a search radius). */\n radius?: number;\n /** Where the person is, when the app already knows it (the distance chip shows at once). */\n origin?: LngLat | null;\n /** The words, passed translated; English by default. */\n labels?: Partial<LocationPickerLabels>;\n};\n\ntype Status = \"idle\" | \"locating\" | \"denied\" | \"unavailable\";\n\n/** A position picked by moving the map under a pin; its distance from the person; \"near me\" on request. */\nexport function LocationPicker({ label, value, onValueChange, locale, zoom = 14, radius, origin: originProp = null, labels: given, className, ...map }: LocationPickerProps) {\n const labels = { ...locationPickerLabels, ...given };\n const s = locationPicker();\n const [origin, setOrigin] = React.useState<LngLat | null>(originProp);\n const [camera, setCamera] = React.useState<LngLat>(value);\n const [status, setStatus] = React.useState<Status>(\"idle\");\n const statusId = React.useId();\n const nearMe = () => {\n if (typeof navigator === \"undefined\" || !navigator.geolocation) return setStatus(\"unavailable\");\n setStatus(\"locating\");\n navigator.geolocation.getCurrentPosition(\n (p) => {\n const here: LngLat = [p.coords.longitude, p.coords.latitude];\n setOrigin(here);\n setCamera(here);\n onValueChange(here);\n setStatus(\"idle\");\n },\n (e) => setStatus(e.code === 1 ? \"denied\" : \"unavailable\"),\n { enableHighAccuracy: false, timeout: 10_000, maximumAge: 60_000 },\n );\n };\n const distance = origin ? formatDistance(distanceMeters(origin, value), locale) : null;\n const message = status === \"locating\" ? labels.locating : status === \"denied\" ? labels.denied : status === \"unavailable\" ? labels.unavailable : \"\";\n return (\n <div className={s.root({ className })} role=\"group\" aria-label={label}>\n <div className={s.frame()}>\n <Map {...map} label={label} center={camera} zoom={zoom} controlled onMoveEnd={(v) => onValueChange(v.center)} className=\"h-80\">\n {radius ? <RadiusCircle center={value} radius={radius} /> : null}\n {origin ? <UserPosition position={origin} label={labels.you} /> : null}\n </Map>\n <svg aria-hidden viewBox=\"0 0 24 32\" className={s.pin()} width=\"30\" height=\"40\">\n <path d=\"M12 0C5.4 0 0 5.2 0 11.7 0 20.4 12 32 12 32s12-11.6 12-20.3C24 5.2 18.6 0 12 0Z\" fill=\"currentColor\" />\n <circle cx=\"12\" cy=\"11.5\" r=\"4.5\" fill=\"var(--kz-on-accent)\" />\n </svg>\n <div className={s.bar()}>\n {distance ? (\n <span className={s.chip()} aria-live=\"polite\">\n {labels.distance(distance)}\n </span>\n ) : (\n <span />\n )}\n <button type=\"button\" className={s.near()} onClick={nearMe} disabled={status === \"locating\"} aria-describedby={message ? statusId : undefined}>\n <svg aria-hidden viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"3.5\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n </svg>\n {status === \"locating\" ? labels.locating : labels.nearMe}\n </button>\n </div>\n </div>\n <p id={statusId} className={s.status()} data-tone={status === \"denied\" || status === \"unavailable\" ? \"danger\" : undefined} aria-live=\"polite\">\n {message}\n </p>\n </div>\n );\n}\n","import { createTV } from \"tailwind-variants\";\n\nimport { twMergeConfig } from \"./cn\";\n\n/** `tv` with the preset's utilities known to the merge engine (`twMergeConfig`): what a primitive wearing the material is styled with. */\nexport const tv = createTV({ twMergeConfig });\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnMerge, type TWMergeConfig } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * The preset's own utilities, told to the merge engine: the material (BRAND.md §6: `bg-sheen`, `bg-fill-shade`,\n * `bg-halo` are background images; `shadow-raised` · `-floating` · `-overlay` · `-glow` are shadows; `inset-shadow-edge`\n * · `-edge-fill` · `-well` are inset shadows). Unknown, tailwind-merge would take `bg-sheen` for a colour and drop the\n * `bg-surface-2` it lies on.\n */\nexport const twMergeConfig: TWMergeConfig = {\n extend: {\n classGroups: {\n \"bg-image\": [\"bg-sheen\", \"bg-fill-shade\", \"bg-halo\"],\n shadow: [\"shadow-raised\", \"shadow-floating\", \"shadow-overlay\", \"shadow-glow\", \"shadow-glow-lg\"],\n \"inset-shadow\": [\"inset-shadow-edge\", \"inset-shadow-edge-fill\", \"inset-shadow-well\"],\n },\n },\n};\n\nconst mergeConfig = { twMergeConfig };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return cnMerge(clsx(inputs))(mergeConfig) ?? \"\";\n}\n\n","// Client. A map of the Krizaka family on MapLibre GL JS (open source) over Protomaps tiles on our CDN — no API key, no\n// metered service. MapLibre and PMTiles are optional peers, loaded when a map is shown (a dynamic import: a page without\n// a map never pays for them). The style comes from the tokens (`mapStyle`), in the page's scheme, and follows it.\n//\n// What every map does: the canvas is decorative for assistive technologies — the map is a region named by `label`, its\n// places a list read in order (the text alternative, `MapPlaces`), each marker a button when it does something; the\n// attribution of OpenStreetMap (ODbL) and Protomaps is always shown; under reduced motion the camera jumps, never flies;\n// without WebGL (or while the engine loads) `fallback` stands in its place.\n/// <reference types=\"geojson\" />\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { usePrefersReducedMotion } from \"../media\";\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION, setMapAssetsBase } from \"./assets\";\nimport { circlePolygon, type LngLat, pointAlong, sliceLine } from \"./geo\";\nimport { type MapBrand, mapPalette, type MapRegion, mapStyle } from \"./style\";\n\ntype MapLibre = typeof import(\"maplibre-gl\");\ntype MapInstance = import(\"maplibre-gl\").Map;\n\nexport const map = tv({\n slots: {\n root: \"relative isolate overflow-hidden rounded-2xl border border-border-default bg-surface-2 shadow-raised\",\n canvas: \"absolute inset-0 [&_.maplibregl-canvas]:outline-hidden\",\n fallback: \"absolute inset-0\",\n controls: \"absolute right-3 top-3 z-10 flex flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-floating\",\n control:\n \"inline-flex h-10 w-10 items-center justify-center text-lg font-semibold text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring-inset disabled:opacity-40 [&+&]:border-t [&+&]:border-border-subtle\",\n attribution:\n \"absolute bottom-1.5 right-1.5 z-10 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary backdrop-blur-sm [&_a]:underline-offset-2 hover:[&_a]:underline\",\n places: \"sr-only\",\n marker:\n \"group relative flex -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-2 border-on-accent bg-accent shadow-floating outline-hidden \" +\n \"transition-transform hover:scale-110 focus-visible:focus-ring motion-reduce:transition-none motion-reduce:hover:scale-100 data-[selected]:scale-125\",\n markerLabel:\n \"pointer-events-none absolute bottom-full mb-2 whitespace-nowrap rounded-lg border border-border-default bg-surface-1 px-2 py-1 text-xs font-semibold text-fg shadow-floating\",\n },\n});\n\ntype Registry = { add: (id: string, text: string) => void; remove: (id: string) => void };\ntype Ctx = { map: MapInstance | null; lib: MapLibre | null; ready: boolean; reduce: boolean; places: Registry };\nconst MapContext = React.createContext<Ctx | null>(null);\n\n/** The map a child is drawn on (`Marker`, `RadiusCircle`, `Cluster`…): the MapLibre instance once it is ready. */\nexport function useMap(): Ctx {\n const ctx = React.useContext(MapContext);\n if (!ctx) throw new Error(\"useMap: inside a <Map> only\");\n return ctx;\n}\n\n/** The scheme of the page (`html.light`), followed as it changes. */\nfunction usePageScheme(): \"dark\" | \"light\" {\n const read = () => (typeof document !== \"undefined\" && document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\");\n const [scheme, setScheme] = React.useState<\"dark\" | \"light\">(read);\n React.useEffect(() => {\n const observer = new MutationObserver(() => setScheme(read()));\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n return scheme;\n}\n\nlet protocolAdded = false;\nlet workerUrl: string | null = null;\n\n/**\n * Where MapLibre's worker comes from. A bundler (Next.js, Vite) renames MapLibre's module and leaves its worker behind:\n * by default the worker of the very version the app installed is imported from jsDelivr (free, open source, no key)\n * through a module worker of the page's own origin. An app with a strict CSP serves it itself —\n * `configureMaps({ workerUrl: \"/maplibre-gl-worker.mjs\" })` (with `maplibre-gl-shared.mjs` beside it), once, before a map.\n * `assetsBase` reads the tiles and glyphs through a same-origin proxy of the CDN (`setMapAssetsBase`).\n */\nexport function configureMaps(options: { workerUrl?: string; assetsBase?: string }) {\n if (options.workerUrl) workerUrl = options.workerUrl;\n if (options.assetsBase) setMapAssetsBase(options.assetsBase);\n}\n\n/** MapLibre, its worker and the PMTiles protocol, loaded once for the page. */\nasync function loadEngine(): Promise<MapLibre> {\n const [lib, pmtiles] = await Promise.all([import(\"maplibre-gl\"), import(\"pmtiles\")]);\n const ml = ((lib as unknown as { default?: MapLibre }).default ?? lib) as MapLibre;\n if (!protocolAdded) {\n protocolAdded = true;\n const remote = `https://cdn.jsdelivr.net/npm/maplibre-gl@${ml.getVersion()}/dist/maplibre-gl-worker.mjs`;\n ml.setWorkerUrl(workerUrl ?? URL.createObjectURL(new Blob([`import ${JSON.stringify(remote)};`], { type: \"text/javascript\" })));\n ml.addProtocol(\"pmtiles\", new pmtiles.Protocol().tile);\n }\n return ml;\n}\n\nconst hasWebGL = () => {\n try {\n const canvas = document.createElement(\"canvas\");\n return Boolean(canvas.getContext(\"webgl2\") ?? canvas.getContext(\"webgl\"));\n } catch {\n return false;\n }\n};\n\nexport type MapLabels = {\n /** The zoom-in button (\"Zoom in\"). */\n zoomIn: string;\n /** The zoom-out button (\"Zoom out\"). */\n zoomOut: string;\n /** Before the list of places read in place of the map (\"Places on the map\"). */\n places: string;\n};\n\nexport const mapLabels: MapLabels = { zoomIn: \"Zoom in\", zoomOut: \"Zoom out\", places: \"Places on the map\" };\n\nexport type MapProps = {\n /** The map's accessible name (\"Pick-up points near you\"), passed translated. */\n label: string;\n /** Where the map looks at first: longitude, latitude. */\n center: LngLat;\n /** The zoom at first (0 the world, 12 a city, 16 a street). Default 12. */\n zoom?: number;\n /** The camera follows `center` and `zoom` when they change (flies, or jumps under reduced motion). */\n controlled?: boolean;\n /** The scheme. Default: the page's (`html.light`), followed. */\n scheme?: \"dark\" | \"light\";\n /** The product whose accent the map carries. Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`). Default `en`. */\n lang?: string;\n /** The detailed region (PMTiles) drawn from zoom 6.5. Default: our Montréal–Québec extract (to zoom 14). */\n region?: MapRegion | null;\n /** The world's low zooms (PMTiles). Default: our world extract (to zoom 6). */\n world?: MapRegion | null;\n /** The zoom buttons (default) — the keyboard also zooms with + and − on the focused map. */\n controls?: boolean;\n /** Moves with the hand (default); `false` makes a still picture of a place. */\n interactive?: boolean;\n /** Shown in place of the map while its engine loads, and for good without WebGL: a `StaticMap`, a picture. */\n fallback?: React.ReactNode;\n /** Called when the camera rests: its centre and zoom. */\n onMoveEnd?: (view: { center: LngLat; zoom: number }) => void;\n /** Called with the position pressed on the map (not on a marker). */\n onPress?: (position: LngLat) => void;\n /** Markers, circles, clusters, the user's position — and anything drawn over the map. */\n children?: React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<MapLabels>;\n /** Classes merged on the root (its height: `h-80`, `aspect-video`). */\n className?: string;\n};\n\n/** A map: MapLibre over our Protomaps tiles, styled from the tokens, with its places as a list for screen readers. */\nexport function Map({\n label,\n center,\n zoom = 12,\n controlled,\n scheme: schemeProp,\n brand,\n lang,\n region,\n world,\n controls = true,\n interactive = true,\n fallback,\n onMoveEnd,\n onPress,\n children,\n labels: given,\n className,\n}: MapProps) {\n const labels = { ...mapLabels, ...given };\n const s = map();\n const pageScheme = usePageScheme();\n const scheme = schemeProp ?? pageScheme;\n const reduce = usePrefersReducedMotion();\n const host = React.useRef<HTMLDivElement>(null);\n const [instance, setInstance] = React.useState<MapInstance | null>(null);\n const [lib, setLib] = React.useState<MapLibre | null>(null);\n const [ready, setReady] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [places, setPlaces] = React.useState<Record<string, string>>({});\n const registry = React.useMemo<Registry>(\n () => ({\n add: (id, text) => setPlaces((p) => (p[id] === text ? p : { ...p, [id]: text })),\n remove: (id) =>\n setPlaces((p) => {\n const { [id]: _gone, ...rest } = p;\n return rest;\n }),\n }),\n [],\n );\n const latest = React.useRef({ onMoveEnd, onPress });\n React.useEffect(() => {\n latest.current = { onMoveEnd, onPress };\n });\n const styleOf = React.useCallback((sc: \"dark\" | \"light\") => mapStyle({ scheme: sc, brand, lang, ...(region !== undefined ? { region } : {}), ...(world !== undefined ? { world } : {}) }), [brand, lang, region, world]);\n\n // The engine and the map, once (the style follows the scheme below).\n React.useEffect(() => {\n if (!host.current) return;\n let alive = true;\n let created: MapInstance | null = null;\n (hasWebGL() ? loadEngine() : Promise.reject(new Error(\"no WebGL\")))\n .then((ml) => {\n if (!alive || !host.current) return;\n created = new ml.Map({\n container: host.current,\n style: styleOf(scheme) as never,\n center: [...center] as [number, number],\n zoom,\n interactive,\n attributionControl: false,\n fadeDuration: reduce ? 0 : 300,\n cooperativeGestures: false,\n });\n created.getCanvas().setAttribute(\"aria-hidden\", \"true\");\n created.getCanvas().setAttribute(\"tabindex\", \"-1\");\n created.on(\"load\", () => alive && setReady(true));\n // The basemap cannot be read (offline, a blocked request): the fallback comes back rather than an empty map.\n created.on(\"error\", (e) => {\n const source = (e as { sourceId?: string }).sourceId;\n if (alive && (source === \"world\" || source === \"region\")) setFailed(true);\n });\n created.on(\"moveend\", () => {\n const c = created!.getCenter();\n latest.current.onMoveEnd?.({ center: [c.lng, c.lat], zoom: created!.getZoom() });\n });\n created.on(\"click\", (e) => {\n if ((e.originalEvent.target as Element | null)?.closest?.(\"[data-kz-marker]\")) return;\n latest.current.onPress?.([e.lngLat.lng, e.lngLat.lat]);\n });\n setLib(ml);\n setInstance(created);\n })\n .catch(() => alive && setFailed(true));\n return () => {\n alive = false;\n created?.remove();\n };\n // Created once: the props that change afterwards are followed by the effects below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The scheme (and the brand, the language) change the style, not the map.\n const first = React.useRef(true);\n React.useEffect(() => {\n if (!instance) return;\n if (first.current) {\n first.current = false;\n return;\n }\n instance.setStyle(styleOf(scheme) as never);\n }, [instance, scheme, styleOf]);\n\n // A controlled camera: it flies (or jumps) to the new view.\n React.useEffect(() => {\n if (!instance || !controlled) return;\n const view = { center: [...center] as [number, number], zoom };\n if (reduce) instance.jumpTo(view);\n else instance.flyTo({ ...view, speed: 1.4 });\n }, [instance, controlled, center[0], center[1], zoom, reduce]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const zoomBy = (by: number) => instance && (reduce ? instance.setZoom(instance.getZoom() + by) : instance.zoomTo(instance.getZoom() + by, { duration: 250 }));\n const ctx = React.useMemo<Ctx>(() => ({ map: instance, lib, ready, reduce, places: registry }), [instance, lib, ready, reduce, registry]);\n const listed = Object.entries(places);\n return (\n <MapContext.Provider value={ctx}>\n <section\n aria-label={label}\n className={s.root({ className })}\n data-ready={ready ? \"\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget || !instance) return;\n if (e.key === \"+\" || e.key === \"=\") zoomBy(1);\n else if (e.key === \"-\") zoomBy(-1);\n else if (e.key.startsWith(\"Arrow\")) {\n e.preventDefault();\n const step = 80;\n const by: Record<string, [number, number]> = { ArrowUp: [0, -step], ArrowDown: [0, step], ArrowLeft: [-step, 0], ArrowRight: [step, 0] };\n instance.panBy(by[e.key]!, { duration: reduce ? 0 : 200 });\n }\n }}\n >\n {!ready || failed ? <div className={s.fallback()}>{fallback}</div> : null}\n <div ref={host} className={s.canvas()} style={{ position: \"absolute\", inset: 0, visibility: failed ? \"hidden\" : undefined }} />\n {controls && interactive && !failed ? (\n <div className={s.controls()}>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomIn} disabled={!instance} onClick={() => zoomBy(1)}>\n +\n </button>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomOut} disabled={!instance} onClick={() => zoomBy(-1)}>\n −\n </button>\n </div>\n ) : null}\n <p className={s.attribution()}>\n <a href={MAP_ATTRIBUTION.protomapsUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.protomaps}\n </a>\n {\" · \"}\n <a href={MAP_ATTRIBUTION.osmUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.osm}\n </a>\n </p>\n {listed.length ? (\n <div className={s.places()}>\n <p>{labels.places}</p>\n <ul>\n {listed.map(([id, text]) => (\n <li key={id}>{text}</li>\n ))}\n </ul>\n </div>\n ) : null}\n {children}\n </section>\n </MapContext.Provider>\n );\n}\n\nexport type MarkerProps = {\n /** Where it stands. */\n position: LngLat;\n /** Its words: the place's name, read in the list of places and as the button's name (\"Café Olimpico, 350 m\"). */\n label: string;\n /** Pressing it (details in a panel, a sheet): the marker becomes a button. */\n onPress?: () => void;\n /** The chosen one: larger, its label shown above it. */\n selected?: boolean;\n /** Its size in pixels. Default 18. */\n size?: number;\n /** What it draws instead of the accent dot (an avatar, an icon). */\n children?: React.ReactNode;\n /** Drops onto its place once, after this many milliseconds (stagger several: `index * 90`). Still under reduced motion. */\n drop?: number;\n};\n\nconst noSubscription = () => () => undefined;\n\n/** A place on the map: an accent dot (or your content) that is a button when it does something. */\nexport function Marker({ position, label, onPress, selected, size = 18, children, drop }: MarkerProps) {\n const { map: m, lib, places } = useMap();\n const s = map();\n const id = React.useId();\n // The marker's host element exists only in a browser: the server and the hydration pass render nothing for it, then the\n // client swaps in the element — `useSyncExternalStore`'s server snapshot, so hydration never sees a difference.\n const host = React.useRef<HTMLElement | null>(null);\n const el = React.useSyncExternalStore(\n noSubscription,\n () => (host.current ??= document.createElement(\"div\")),\n () => null,\n );\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib || !el) return;\n el.setAttribute(\"data-kz-marker\", \"\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, el, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n if (!el) return null;\n const Tag = onPress ? \"button\" : \"span\";\n return createPortal(\n <Tag\n type={onPress ? \"button\" : undefined}\n aria-label={onPress ? label : undefined}\n aria-hidden={onPress ? undefined : true}\n aria-pressed={onPress && selected !== undefined ? selected : undefined}\n data-selected={selected ? \"\" : undefined}\n data-drop={drop === undefined ? undefined : \"\"}\n className={s.marker()}\n style={{ ...(children ? null : { width: size, height: size }), ...(drop === undefined ? null : ({ \"--kz-map-drop-delay\": `${drop}ms` } as React.CSSProperties)) }}\n onClick={onPress}\n >\n {children}\n {selected ? <span className={s.markerLabel()}>{label}</span> : null}\n </Tag>,\n el,\n );\n}\n\nexport type UserPositionProps = {\n /** Where the person is. */\n position: LngLat;\n /** Its words (\"You are here\"), read in the list of places. */\n label: string;\n /** The accuracy in metres: a soft disc around the dot. */\n accuracy?: number;\n};\n\n/** The person's position: a dot that pulses (still under reduced motion), its accuracy as a soft disc. */\nexport function UserPosition({ position, label, accuracy }: UserPositionProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-user\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return accuracy ? <RadiusCircle center={position} radius={accuracy} tone=\"soft\" /> : null;\n}\n\nexport type PulseProps = {\n /** Where it beats. */\n position: LngLat;\n /** Its words (\"High demand in Mile End\"), read in the list of places. */\n label: string;\n /** The rings' reach in pixels. Default 56. */\n size?: number;\n /** `accent` (a zone that matters) or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A heat pulse: rings that swell and fade around a place (a hot spot, a demand peak). Still rings under reduced motion. */\nexport function Pulse({ position, label, size = 56, tone = \"accent\" }: PulseProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-pulse\";\n el.setAttribute(\"data-tone\", tone);\n el.setAttribute(\"aria-hidden\", \"true\");\n el.style.setProperty(\"--kz-map-pulse-size\", `${size}px`);\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, tone, size, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return null;\n}\n\nexport type RouteProps = {\n /** The path, as positions in order (a polyline: a delivery, a walk, a flight). */\n path: readonly LngLat[];\n /** Its words (\"Delivery from the studio to Mile End, 2.4 km\"), read in the list of places. */\n label: string;\n /** The line draws itself once, from its start, when the map is on screen; `false` shows it whole. Default `true`. */\n draw?: boolean;\n /** How long the drawing lasts, in milliseconds. Default 1400. */\n duration?: number;\n /** A dot that travels the route once drawn (`true`, or your own content: an avatar, an icon). */\n courier?: boolean | React.ReactNode;\n /** How long a trip takes, in milliseconds. Default 3200. */\n tripDuration?: number;\n /** Start the trips again after a pause (milliseconds), only while the map is on screen. Without it the courier makes one trip. */\n repeatAfter?: number;\n /** Changing it replays the drawing and the trip. */\n replayKey?: string | number;\n /** `accent` or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\ntype RouteSource = { setData?: (d: GeoJSON.GeoJSON) => void };\n\n/** A route on the map: a line that draws itself, and a courier that travels it. Under reduced motion the line is whole and the courier rests at the end. */\nexport function Route({ path, label, draw = true, duration = 1400, courier, tripDuration = 3200, repeatAfter, replayKey, tone = \"accent\" }: RouteProps) {\n const { map: m, lib, ready, reduce, places } = useMap();\n const id = `kz-route-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const animated = draw && !reduce;\n // The drawing is done for this `replayKey` (a new key draws again); without animation the line is whole at once.\n const token = replayKey ?? 0;\n const [doneFor, setDoneFor] = React.useState<string | number | null>(null);\n const drawn = !animated || doneFor === token;\n const whole = React.useMemo(() => sliceLine(path, 1), [path]);\n const start = React.useMemo(() => sliceLine(path, 0), [path]);\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n useGeoLayer(id, drawn ? whole : start, (c) => [\n { id: `${id}-casing`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": 9, \"line-opacity\": tone === \"soft\" ? 0.08 : 0.16, \"line-blur\": 3 } },\n { id: `${id}-line`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 2 : 3.5, \"line-opacity\": tone === \"soft\" ? 0.6 : 1 } },\n ]);\n // Is the map on screen? The drawing waits for it, and a repeating trip does not start while it is not.\n const [visible, setVisible] = React.useState(() => typeof IntersectionObserver === \"undefined\");\n React.useEffect(() => {\n const el = m?.getContainer?.();\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(([entry]) => setVisible(!!entry?.isIntersecting), { threshold: 0.25 });\n io.observe(el);\n return () => io.disconnect();\n }, [m]);\n // The drawing: a finite animation, one frame per paint for `duration` ms — no loop, nothing runs afterwards.\n React.useEffect(() => {\n if (!m || !ready || !animated || drawn || !visible) return;\n const source = m.getSource(id) as RouteSource | undefined;\n let frame = 0;\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, duration));\n source?.setData?.(sliceLine(path, 1 - (1 - t) ** 3));\n if (t < 1) frame = requestAnimationFrame(step);\n else setDoneFor(token);\n };\n frame = requestAnimationFrame(step);\n return () => cancelAnimationFrame(frame);\n }, [m, ready, animated, drawn, visible, id, path, duration, token]);\n // The courier: one trip once the line is drawn (finite frames), again after `repeatAfter` while on screen.\n const [trip, setTrip] = React.useState(0);\n React.useEffect(() => {\n if (!courier || !m || !lib || !ready || !drawn) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-courier\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...(path[reduce ? path.length - 1 : 0] ?? [0, 0])] as [number, number]).addTo(m);\n let frame = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (!reduce && visible) {\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, tripDuration));\n const eased = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n marker.setLngLat([...pointAlong(path, eased).position] as [number, number]);\n if (t < 1) frame = requestAnimationFrame(step);\n else if (repeatAfter !== undefined) timer = setTimeout(() => setTrip((n) => n + 1), repeatAfter);\n };\n frame = requestAnimationFrame(step);\n } else if (!reduce) marker.setLngLat([...(path[path.length - 1] ?? [0, 0])] as [number, number]);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(timer);\n marker.remove();\n };\n }, [courier, m, lib, ready, drawn, visible, reduce, path, tripDuration, repeatAfter, trip, token]);\n return null;\n}\n\n/** Adds a GeoJSON source and its layers while the map has a style (again after a change of scheme). */\nfunction useGeoLayer(id: string, data: GeoJSON.GeoJSON, layers: (palette: ReturnType<typeof mapPalette>) => object[], source: object = {}) {\n const { map: m, ready } = useMap();\n const dataRef = React.useRef(data);\n React.useEffect(() => {\n dataRef.current = data;\n const src = m?.getSource(id) as { setData?: (d: GeoJSON.GeoJSON) => void } | undefined;\n src?.setData?.(data);\n }, [m, id, data]);\n React.useEffect(() => {\n if (!m || !ready) return;\n const add = () => {\n if (m.getSource(id)) return;\n const scheme = document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\";\n m.addSource(id, { type: \"geojson\", data: dataRef.current, ...source } as never);\n for (const layer of layers(mapPalette(scheme))) m.addLayer(layer as never);\n };\n add();\n m.on(\"styledata\", add);\n return () => {\n m.off(\"styledata\", add);\n if (!m.getStyle()) return;\n for (const layer of m.getStyle().layers.filter((l) => \"source\" in l && l.source === id)) m.removeLayer(layer.id);\n if (m.getSource(id)) m.removeSource(id);\n };\n }, [m, ready, id]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nexport type RadiusCircleProps = {\n /** Its centre. */\n center: LngLat;\n /** Its radius in metres. */\n radius: number;\n /** `accent` (a zone that matters: a delivery area, a search radius) or `soft` (an accuracy). */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A circle on the ground: a delivery zone, a search radius — filled with the accent, its edge drawn. */\nexport function RadiusCircle({ center, radius, tone = \"accent\" }: RadiusCircleProps) {\n const id = `kz-circle-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo(() => circlePolygon(center, radius), [center[0], center[1], radius]); // eslint-disable-line react-hooks/exhaustive-deps\n useGeoLayer(id, data, (c) => [\n { id: `${id}-fill`, type: \"fill\", source: id, paint: { \"fill-color\": c.accent, \"fill-opacity\": tone === \"soft\" ? 0.1 : 0.14 } },\n { id: `${id}-line`, type: \"line\", source: id, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 1 : 2, \"line-opacity\": tone === \"soft\" ? 0.4 : 0.9, ...(tone === \"accent\" ? { \"line-dasharray\": [2, 1.5] } : {}) } },\n ]);\n return null;\n}\n\nexport type ClusterPoint = { id: string; position: LngLat; label: string };\n\nexport type ClusterProps = {\n /** The points: many of them (creators, pick-up points, events). */\n points: readonly ClusterPoint[];\n /** Pressing a single point. A group zooms in to open itself. */\n onPointPress?: (point: ClusterPoint) => void;\n /** The radius, in pixels, within which points group. Default 48. */\n radius?: number;\n};\n\n/** Many points grouped while they are close: a disc with the count, opened by zooming in; each point is listed for screen readers. */\nexport function Cluster({ points, onPointPress, radius = 48 }: ClusterProps) {\n const { map: m, ready, reduce, places } = useMap();\n const id = `kz-cluster-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo<GeoJSON.FeatureCollection>(\n () => ({ type: \"FeatureCollection\", features: points.map((p) => ({ type: \"Feature\", properties: { id: p.id, label: p.label }, geometry: { type: \"Point\", coordinates: [...p.position] } })) }),\n [points],\n );\n React.useEffect(() => {\n for (const p of points) places.add(`${id}-${p.id}`, p.label);\n return () => points.forEach((p) => places.remove(`${id}-${p.id}`));\n }, [points, places, id]);\n useGeoLayer(\n id,\n data,\n (c) => [\n { id: `${id}-groups`, type: \"circle\", source: id, filter: [\"has\", \"point_count\"], paint: { \"circle-color\": c.accent, \"circle-opacity\": 0.92, \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 16, 10, 20, 50, 26], \"circle-stroke-width\": 3, \"circle-stroke-color\": c.halo } },\n { id: `${id}-count`, type: \"symbol\", source: id, filter: [\"has\", \"point_count\"], layout: { \"text-field\": [\"get\", \"point_count_abbreviated\"], \"text-font\": [\"Noto Sans Medium\"], \"text-size\": 13 }, paint: { \"text-color\": c.onAccent } },\n { id: `${id}-points`, type: \"circle\", source: id, filter: [\"!\", [\"has\", \"point_count\"]], paint: { \"circle-color\": c.accent, \"circle-radius\": 7, \"circle-stroke-width\": 2.5, \"circle-stroke-color\": c.halo } },\n ],\n { cluster: true, clusterRadius: radius, clusterMaxZoom: 15 },\n );\n React.useEffect(() => {\n if (!m || !ready) return;\n const openGroup = async (e: { features?: { properties: Record<string, unknown>; geometry: GeoJSON.Geometry }[] }) => {\n const f = e.features?.[0];\n if (!f) return;\n const src = m.getSource(id) as unknown as { getClusterExpansionZoom: (id: number) => Promise<number> };\n const z = await src.getClusterExpansionZoom(f.properties.cluster_id as number);\n const center = (f.geometry as GeoJSON.Point).coordinates as [number, number];\n if (reduce) m.jumpTo({ center, zoom: z });\n else m.easeTo({ center, zoom: z });\n };\n const pressPoint = (e: { features?: { properties: Record<string, unknown> }[] }) => {\n const pid = e.features?.[0]?.properties.id;\n const point = points.find((p) => p.id === pid);\n if (point) onPointPress?.(point);\n };\n m.on(\"click\", `${id}-groups`, openGroup as never);\n m.on(\"click\", `${id}-points`, pressPoint as never);\n return () => {\n m.off(\"click\", `${id}-groups`, openGroup as never);\n m.off(\"click\", `${id}-points`, pressPoint as never);\n };\n }, [m, ready, id, points, onPointPress, reduce]);\n return null;\n}\n","// Shared by the data and media primitives (client): the clock format, the reduced-motion preference, the\n// in-view observer, a debounced callback. Never an entry of its own.\nimport * as React from \"react\";\n\n/** Seconds as a clock: `75` → `1:15`, `3725` → `1:02:05`. Negative or unknown values read `0:00`. */\nexport function formatClock(seconds: number): string {\n const s = Number.isFinite(seconds) && seconds > 0 ? Math.floor(seconds) : 0;\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const rest = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${rest}` : `${m}:${rest}`;\n}\n\n/** Clamps `value` between `min` and `max`. */\nexport const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst REDUCE = \"(prefers-reduced-motion: reduce)\";\nconst subscribeReduce = (notify: () => void) => {\n if (typeof window === \"undefined\" || !window.matchMedia) return () => undefined;\n const query = window.matchMedia(REDUCE);\n query.addEventListener?.(\"change\", notify);\n return () => query.removeEventListener?.(\"change\", notify);\n};\n\n/** `true` when the person asked for less motion (`prefers-reduced-motion: reduce`); follows a change of the setting. */\nexport function usePrefersReducedMotion(): boolean {\n return React.useSyncExternalStore(\n subscribeReduce,\n () => (typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(REDUCE).matches : false),\n () => false,\n );\n}\n\n/** The latest value of a callback, for an effect or an event that must not re-subscribe when it changes. */\nexport function useLatest<T>(value: T) {\n const ref = React.useRef(value);\n React.useLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\n/** Calls `onChange(visible)` when at least `threshold` of the element enters or leaves the viewport. */\nexport function useInView(ref: React.RefObject<Element | null>, onChange: (visible: boolean) => void, enabled = true, threshold = 0.25) {\n const latest = useLatest(onChange);\n React.useEffect(() => {\n const element = ref.current;\n if (!enabled || !element || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => entry && latest.current(entry.isIntersecting), { threshold });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, threshold, latest]);\n}\n\n/** A value controlled by a prop when it is given, held inside otherwise; `set` calls `onChange` either way. */\nexport function useControllable<T>(value: T | undefined, defaultValue: T, onChange?: (next: T) => void) {\n const [inner, setInner] = React.useState(defaultValue);\n const controlled = value !== undefined;\n const current = controlled ? value : inner;\n const set = React.useCallback(\n (next: T) => {\n if (!controlled) setInner(next);\n onChange?.(next);\n },\n [controlled, onChange],\n );\n return [current, set] as const;\n}\n\n/** Starts a media element without an unhandled rejection (autoplay refused, the source replaced meanwhile). */\nexport function playSafely(media: HTMLMediaElement | null | undefined) {\n try {\n const started = media?.play() as Promise<void> | undefined;\n started?.catch?.(() => undefined);\n } catch {\n // Not playable here (no media support): the controls stay as they are.\n }\n}\n","// @krizaka/tokens — GÉNÉRÉ depuis src/tokens/*.tokens.json par scripts/build.mjs : ne pas éditer.\n\n/** Colour roles per theme, resolved for React Native (`#rrggbb`, `rgba()` when translucent). */\nexport const themes = /* @__PURE__ */ Object.freeze({ dark: /* @__PURE__ */ Object.freeze({\n \"surface0\": \"#0c0c0e\",\n \"surface1\": \"#161618\",\n \"surface2\": \"#1f1f23\",\n \"surface3\": \"#2a2a2d\",\n \"media\": \"#1b1b1d\",\n \"borderSubtle\": \"rgba(255,255,255,0.06)\",\n \"borderDefault\": \"rgba(255,255,255,0.1)\",\n \"borderStrong\": \"rgba(255,255,255,0.16)\",\n \"textPrimary\": \"#f5f5f5\",\n \"textSecondary\": \"#a0a0a7\",\n \"textMuted\": \"#92929b\",\n \"textOnMedia\": \"#ffffff\",\n \"accent\": \"#3e76a8\",\n \"accentHover\": \"#346793\",\n \"accentSoft\": \"rgba(62,118,168,0.12)\",\n \"accentText\": \"#8ebde1\",\n \"accent2\": \"#3e76a8\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3e76a8\",\n \"brandGradientFrom\": \"#121a21\",\n \"brandGradientVia\": \"#0f1114\",\n \"brandGradientTo\": \"#0c0c0e\",\n \"success\": \"#10b77f\",\n \"successText\": \"#36d399\",\n \"successSoft\": \"rgba(16,183,127,0.14)\",\n \"warning\": \"#f59f0a\",\n \"warningText\": \"#f6a823\",\n \"warningSoft\": \"rgba(245,159,10,0.14)\",\n \"danger\": \"#ef4343\",\n \"dangerText\": \"#f87272\",\n \"dangerSoft\": \"rgba(239,67,67,0.14)\",\n \"info\": \"#3e76a8\",\n \"infoText\": \"#8ebde1\",\n \"scrim\": \"rgba(0,0,0,0.6)\",\n \"scrimStrong\": \"rgba(0,0,0,0.8)\",\n \"overlay\": \"rgba(0,0,0,0.7)\",\n \"surfaceSheen\": \"rgba(255,255,255,0.045)\",\n \"fillShade\": \"rgba(0,0,0,0.12)\",\n \"edgeLight\": \"rgba(255,255,255,0.07)\",\n \"edgeOnFill\": \"rgba(255,255,255,0.22)\",\n \"edgeWell\": \"rgba(4,4,7,0.5)\",\n \"glassBg\": \"rgba(29,29,32,0.88)\",\n \"glassBorder\": \"rgba(255,255,255,0.1)\"\n}), light: /* @__PURE__ */ Object.freeze({\n \"surface0\": \"#fafafa\",\n \"surface1\": \"#ffffff\",\n \"surface2\": \"#f1f1f3\",\n \"surface3\": \"#e4e4e7\",\n \"media\": \"#e9e9ec\",\n \"borderSubtle\": \"rgba(0,0,0,0.06)\",\n \"borderDefault\": \"rgba(0,0,0,0.1)\",\n \"borderStrong\": \"rgba(0,0,0,0.16)\",\n \"textPrimary\": \"#18181b\",\n \"textSecondary\": \"#56565d\",\n \"textMuted\": \"#64646d\",\n \"textOnMedia\": \"#ffffff\",\n \"accent\": \"#2a5f8d\",\n \"accentHover\": \"#1f4d75\",\n \"accentSoft\": \"rgba(42,95,141,0.08)\",\n \"accentText\": \"#275782\",\n \"accent2\": \"#2a5f8d\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#2a5f8d\",\n \"brandGradientFrom\": \"#e7eef4\",\n \"brandGradientVia\": \"#f5f7fa\",\n \"brandGradientTo\": \"#fafafa\",\n \"success\": \"#10b77f\",\n \"successText\": \"#0a714e\",\n \"successSoft\": \"rgba(16,183,127,0.1)\",\n \"warning\": \"#f59f0a\",\n \"warningText\": \"#955104\",\n \"warningSoft\": \"rgba(245,159,10,0.12)\",\n \"danger\": \"#ef4343\",\n \"dangerText\": \"#af1d1d\",\n \"dangerSoft\": \"rgba(239,67,67,0.1)\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"scrim\": \"rgba(0,0,0,0.6)\",\n \"scrimStrong\": \"rgba(0,0,0,0.8)\",\n \"overlay\": \"rgba(0,0,0,0.7)\",\n \"surfaceSheen\": \"rgba(255,255,255,0.7)\",\n \"fillShade\": \"rgba(0,0,0,0.12)\",\n \"edgeLight\": \"rgba(255,255,255,0.9)\",\n \"edgeOnFill\": \"rgba(255,255,255,0.22)\",\n \"edgeWell\": \"rgba(48,52,80,0.08)\",\n \"glassBg\": \"rgba(255,255,255,0.88)\",\n \"glassBorder\": \"rgba(0,0,0,0.08)\"\n}) });\n\n/** Corner radii, in dp. */\nexport const radius = /* @__PURE__ */ Object.freeze({\n \"xs\": 6,\n \"sm\": 8,\n \"md\": 12,\n \"lg\": 16,\n \"xl\": 20,\n \"2xl\": 28,\n \"full\": 9999\n});\n\n/** Font families: the first family of each web stack (React Native takes a single name). */\nexport const typography = /* @__PURE__ */ Object.freeze({\n \"sans\": \"Inter\",\n \"text\": \"Inter\",\n \"display\": \"Plus Jakarta Sans\",\n \"mono\": \"JetBrains Mono\"\n});\n\n/**\n * Motion: the cubic-bezier control points (`Easing.bezier(...motion.ease)`), the short spring of what answers the hand\n * (`motion.spring`) and the micro-interaction durations in ms (`motion.duration.fast`) — 0 when the system reduces motion.\n */\nexport const motion = /* @__PURE__ */ Object.freeze({ ease: /* @__PURE__ */ Object.freeze([0.16,1,0.3,1]), spring: /* @__PURE__ */ Object.freeze([0.34,1.36,0.64,1]), duration: /* @__PURE__ */ Object.freeze({\"instant\":90,\"fast\":150,\"base\":200}) });\n\n/**\n * The type scale in dp: fontSize, lineHeight and letterSpacing absolute (React Native has no em), fontWeight as a string,\n * and the role whose family sets it — with @krizaka/fonts: `{ ...typeScale.bodyMd, fontFamily: fontFamily(role, weight) }`.\n */\nexport const typeScale = /* @__PURE__ */ Object.freeze({\n \"display2xl\": {\n \"role\": \"display\",\n \"fontSize\": 72,\n \"lineHeight\": 73,\n \"fontWeight\": \"800\",\n \"letterSpacing\": -2.52\n },\n \"displayXl\": {\n \"role\": \"display\",\n \"fontSize\": 56,\n \"lineHeight\": 59,\n \"fontWeight\": \"800\",\n \"letterSpacing\": -1.68\n },\n \"displayLg\": {\n \"role\": \"display\",\n \"fontSize\": 44,\n \"lineHeight\": 48,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -1.1\n },\n \"displayMd\": {\n \"role\": \"display\",\n \"fontSize\": 34,\n \"lineHeight\": 39,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.68\n },\n \"displaySm\": {\n \"role\": \"display\",\n \"fontSize\": 26,\n \"lineHeight\": 31,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.39\n },\n \"titleLg\": {\n \"role\": \"display\",\n \"fontSize\": 20,\n \"lineHeight\": 27,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.2\n },\n \"titleMd\": {\n \"role\": \"text\",\n \"fontSize\": 17,\n \"lineHeight\": 24,\n \"fontWeight\": \"600\",\n \"letterSpacing\": -0.2\n },\n \"bodyLg\": {\n \"role\": \"text\",\n \"fontSize\": 18,\n \"lineHeight\": 29,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.25\n },\n \"bodyMd\": {\n \"role\": \"text\",\n \"fontSize\": 16,\n \"lineHeight\": 26,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.18\n },\n \"bodySm\": {\n \"role\": \"text\",\n \"fontSize\": 14,\n \"lineHeight\": 21,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.08\n },\n \"caption\": {\n \"role\": \"text\",\n \"fontSize\": 12,\n \"lineHeight\": 17,\n \"fontWeight\": \"500\",\n \"letterSpacing\": 0\n },\n \"overline\": {\n \"role\": \"mono\",\n \"fontSize\": 11,\n \"lineHeight\": 14,\n \"fontWeight\": \"600\",\n \"letterSpacing\": 1.32\n },\n \"monoMd\": {\n \"role\": \"mono\",\n \"fontSize\": 14,\n \"lineHeight\": 22,\n \"fontWeight\": \"400\",\n \"letterSpacing\": 0\n },\n \"monoSm\": {\n \"role\": \"mono\",\n \"fontSize\": 12,\n \"lineHeight\": 18,\n \"fontWeight\": \"400\",\n \"letterSpacing\": 0\n }\n});\n\n/**\n * The shadows per theme as React Native `boxShadow` strings (RN ≥ 0.76): `raised` (a button, a card), `floating`\n * (a menu, a popover), `overlay` (a sheet, a toast) — ink-tinted, several layers, as on the web.\n */\nexport const shadows = /* @__PURE__ */ Object.freeze({ dark: /* @__PURE__ */ Object.freeze({\n \"sm\": \"0 1px 3px 0 rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.06)\",\n \"md\": \"0 4px 12px 0 rgba(0,0,0,0.15), 0 2px 4px 0 rgba(0,0,0,0.08)\",\n \"lg\": \"0 8px 24px 0 rgba(0,0,0,0.18), 0 4px 8px 0 rgba(0,0,0,0.08)\",\n \"raised\": \"0 1px 2px 0 rgba(4,4,7,0.45), 0 2px 6px -2px rgba(4,4,7,0.3)\",\n \"floating\": \"0 2px 4px 0 rgba(4,4,7,0.35), 0 12px 24px -6px rgba(4,4,7,0.45), 0 24px 48px -12px rgba(4,4,7,0.35)\",\n \"overlay\": \"0 4px 8px 0 rgba(4,4,7,0.4), 0 24px 48px -12px rgba(4,4,7,0.5), 0 48px 96px -24px rgba(4,4,7,0.45)\"\n}), light: /* @__PURE__ */ Object.freeze({\n \"sm\": \"0 1px 3px 0 rgba(0,0,0,0.06), 0 1px 2px 0 rgba(0,0,0,0.04)\",\n \"md\": \"0 4px 12px 0 rgba(0,0,0,0.08), 0 2px 4px 0 rgba(0,0,0,0.04)\",\n \"lg\": \"0 8px 24px 0 rgba(0,0,0,0.1), 0 4px 8px 0 rgba(0,0,0,0.05)\",\n \"raised\": \"0 1px 2px 0 rgba(48,52,80,0.08), 0 2px 6px -2px rgba(48,52,80,0.08)\",\n \"floating\": \"0 2px 4px 0 rgba(48,52,80,0.06), 0 12px 24px -6px rgba(48,52,80,0.1), 0 24px 48px -12px rgba(48,52,80,0.1)\",\n \"overlay\": \"0 4px 8px 0 rgba(48,52,80,0.08), 0 24px 48px -12px rgba(48,52,80,0.14), 0 48px 96px -24px rgba(48,52,80,0.14)\"\n}) });\n\n/** The griffe (BRAND.md §6.1): the size of the 45° cut corner of a control, in dp. */\nexport const cut = /* @__PURE__ */ Object.freeze({\n \"size\": 10\n});\n\n/** The brand themes as ThemeProvider overrides (`overrides={brands.orazaka}`): the accent family per theme. */\nexport const brands = /* @__PURE__ */ Object.freeze({\n \"krizaka\": {\n \"dark\": {\n \"accent\": \"#3e76a8\",\n \"accentHover\": \"#346793\",\n \"accentSoft\": \"rgba(62,118,168,0.12)\",\n \"accentText\": \"#8ebde1\",\n \"accent2\": \"#3e76a8\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3e76a8\",\n \"info\": \"#3e76a8\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#121a21\",\n \"brandGradientVia\": \"#0f1114\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#2a5f8d\",\n \"accentHover\": \"#1f4d75\",\n \"accentSoft\": \"rgba(42,95,141,0.08)\",\n \"accentText\": \"#275782\",\n \"accent2\": \"#2a5f8d\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#2a5f8d\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e7eef4\",\n \"brandGradientVia\": \"#f5f7fa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"orazaka\": {\n \"dark\": {\n \"accent\": \"#f67e23\",\n \"accentHover\": \"#f79245\",\n \"accentSoft\": \"rgba(246,126,35,0.12)\",\n \"accentText\": \"#f78c3b\",\n \"accent2\": \"#f59e0b\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#f67e23\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#28170b\",\n \"brandGradientVia\": \"#17130c\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#b45309\",\n \"accentHover\": \"#964608\",\n \"accentSoft\": \"rgba(180,83,9,0.08)\",\n \"accentText\": \"#9b4808\",\n \"accent2\": \"#a45904\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#b45309\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#ffebd6\",\n \"brandGradientVia\": \"#fdf7ed\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"orochia\": {\n \"dark\": {\n \"accent\": \"#7c3aed\",\n \"accentHover\": \"#6d28d9\",\n \"accentSoft\": \"rgba(139,92,246,0.12)\",\n \"accentText\": \"#a78bfa\",\n \"accent2\": \"#db2777\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#a78bfa\",\n \"info\": \"#7c3aed\",\n \"infoText\": \"#a78bfa\",\n \"brandGradientFrom\": \"#1a0e2a\",\n \"brandGradientVia\": \"#1c0d17\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#7c3aed\",\n \"accentHover\": \"#6d28d9\",\n \"accentSoft\": \"rgba(124,58,237,0.08)\",\n \"accentText\": \"#6d28d9\",\n \"accent2\": \"#db2777\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#7c3aed\",\n \"info\": \"#7c3aed\",\n \"infoText\": \"#6d28d9\",\n \"brandGradientFrom\": \"#efe7fe\",\n \"brandGradientVia\": \"#fdedf5\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yasakia\": {\n \"dark\": {\n \"accent\": \"#24dbc9\",\n \"accentHover\": \"#47e1d2\",\n \"accentSoft\": \"rgba(36,219,201,0.12)\",\n \"accentText\": \"#3ee0cf\",\n \"accent2\": \"#22c378\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#24dbc9\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#0a241e\",\n \"brandGradientVia\": \"#0c1818\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#097169\",\n \"accentHover\": \"#085e57\",\n \"accentSoft\": \"rgba(9,113,105,0.08)\",\n \"accentText\": \"#096d64\",\n \"accent2\": \"#107546\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#097169\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e1faf1\",\n \"brandGradientVia\": \"#e9fbfa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yororis\": {\n \"dark\": {\n \"accent\": \"#98b587\",\n \"accentHover\": \"#adc69f\",\n \"accentSoft\": \"rgba(152,181,135,0.12)\",\n \"accentText\": \"#a7c497\",\n \"accent2\": \"#c3a579\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#98b587\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#1c1812\",\n \"brandGradientVia\": \"#11140f\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#456237\",\n \"accentHover\": \"#39532d\",\n \"accentSoft\": \"rgba(69,98,55,0.08)\",\n \"accentText\": \"#415b33\",\n \"accent2\": \"#745834\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#456237\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#f5efe5\",\n \"brandGradientVia\": \"#f1f6ee\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yomizaka\": {\n \"dark\": {\n \"accent\": \"#8888f2\",\n \"accentHover\": \"#a2a2f6\",\n \"accentSoft\": \"rgba(136,136,242,0.12)\",\n \"accentText\": \"#9797f7\",\n \"accent2\": \"#8d9aec\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#8888f2\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#121321\",\n \"brandGradientVia\": \"#111019\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#3c38b2\",\n \"accentHover\": \"#322e9e\",\n \"accentSoft\": \"rgba(60,56,178,0.08)\",\n \"accentText\": \"#3a36ab\",\n \"accent2\": \"#2d3c9f\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3c38b2\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#ebecfa\",\n \"brandGradientVia\": \"#f0f0fa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"kachizaka\": {\n \"dark\": {\n \"accent\": \"#fbcf23\",\n \"accentHover\": \"#fddb53\",\n \"accentSoft\": \"rgba(251,207,35,0.12)\",\n \"accentText\": \"#fbd84b\",\n \"accent2\": \"#7695f9\",\n \"onAccent\": \"#0c0e17\",\n \"ring\": \"#fbcf23\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#101628\",\n \"brandGradientVia\": \"#0f111a\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#fac905\",\n \"accentHover\": \"#dcb104\",\n \"accentSoft\": \"rgba(250,193,5,0.14)\",\n \"accentText\": \"#1d3faf\",\n \"accent2\": \"#6285f8\",\n \"onAccent\": \"#0c0e17\",\n \"ring\": \"#1743d3\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e8edfc\",\n \"brandGradientVia\": \"#fcf8e8\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"akinaza\": {\n \"dark\": {\n \"accent\": \"#65c374\",\n \"accentHover\": \"#7fd28d\",\n \"accentSoft\": \"rgba(101,195,116,0.12)\",\n \"accentText\": \"#7dd48b\",\n \"accent2\": \"#dc946a\",\n \"onAccent\": \"#0a0f0c\",\n \"ring\": \"#65c374\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#111d15\",\n \"brandGradientVia\": \"#181410\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#21692f\",\n \"accentHover\": \"#195726\",\n \"accentSoft\": \"rgba(33,105,47,0.08)\",\n \"accentText\": \"#20652e\",\n \"accent2\": \"#954923\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#21692f\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e9f7eb\",\n \"brandGradientVia\": \"#faf1eb\",\n \"brandGradientTo\": \"#fafafa\"\n }\n }\n});\n/** The marks' emblem gradients: [offset, #rrggbb] stops, the same in both themes. */\nexport const marks = /* @__PURE__ */ Object.freeze({\n \"krizaka\": [\n [\n \"0%\",\n \"#3e76a8\"\n ],\n [\n \"100%\",\n \"#8ebde1\"\n ]\n ],\n \"orazaka\": [\n [\n \"0%\",\n \"#b45309\"\n ],\n [\n \"100%\",\n \"#dc6509\"\n ]\n ],\n \"orochia\": [\n [\n \"0%\",\n \"#7c3aed\"\n ],\n [\n \"55%\",\n \"#d946ef\"\n ],\n [\n \"100%\",\n \"#f472b6\"\n ]\n ],\n \"yasakia\": [\n [\n \"0%\",\n \"#079d61\"\n ],\n [\n \"100%\",\n \"#089b96\"\n ]\n ],\n \"yororis\": [\n [\n \"0%\",\n \"#a4773d\"\n ],\n [\n \"100%\",\n \"#5d894d\"\n ]\n ],\n \"yomizaka\": [\n [\n \"0%\",\n \"#5362c6\"\n ],\n [\n \"100%\",\n \"#6d5ec9\"\n ]\n ],\n \"kachizaka\": [\n [\n \"0%\",\n \"#355fe9\"\n ],\n [\n \"100%\",\n \"#a27807\"\n ]\n ],\n \"akinaza\": [\n [\n \"0%\",\n \"#2d8049\"\n ],\n [\n \"100%\",\n \"#bc632f\"\n ]\n ]\n});\n","// The Krizaka map style, generated from the tokens (no hand-written colour): Protomaps' basemap layers\n// (@protomaps/basemaps) painted with a flavour drawn from `@krizaka/tokens` — the surfaces for the land, the accent\n// softened for the water, the text roles for the labels — in dark or light, the product's accent where a brand asks.\n// Platform-neutral: the web `Map` and the native `Map` both read it.\nimport { brands, themes } from \"@krizaka/tokens/native\";\nimport { type Flavor, layers, namedFlavor } from \"@protomaps/basemaps\";\n\nimport { MAP_ATTRIBUTION, mapAssets } from \"./assets\";\n\n/** A product of the family: its accent paints the map. */\nexport type MapBrand = keyof typeof brands;\n\n/** A colour mixed into another (`#rrggbb` only): `t` = 0 gives `a`, 1 gives `b`. */\nexport function mix(a: string, b: string, t: number): string {\n const p = (h: string) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));\n const [x, y] = [p(a), p(b)];\n return `#${x.map((v, i) => Math.round(v + (y[i]! - v) * t).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/** The colours of the map in a scheme (and a brand): what the style, the markers and the static map share. */\nexport function mapPalette(scheme: \"dark\" | \"light\", brand: MapBrand = \"krizaka\") {\n const t = { ...themes[scheme], ...(brands as Record<string, Partial<Record<\"dark\" | \"light\", Partial<typeof themes.dark>>>>)[brand]?.[scheme] };\n const dark = scheme === \"dark\";\n const land = dark ? t.surface1 : t.surface0;\n return {\n land,\n background: dark ? t.surface0 : t.surface2,\n water: mix(land, t.accent, dark ? 0.18 : 0.14),\n park: mix(land, t.success, dark ? 0.1 : 0.12),\n road: dark ? mix(land, t.textPrimary, 0.12) : \"#ffffff\",\n roadMajor: dark ? mix(land, t.textPrimary, 0.2) : mix(land, t.textPrimary, 0.06),\n casing: mix(land, t.textPrimary, dark ? 0.05 : 0.12),\n building: mix(land, t.textPrimary, dark ? 0.06 : 0.07),\n boundary: mix(land, t.textPrimary, 0.3),\n label: t.textSecondary,\n labelStrong: t.textPrimary,\n halo: land,\n accent: t.accent,\n onAccent: t.onAccent,\n };\n}\n\n/** The basemap flavour of a palette: every role of Protomaps' layers filled from the tokens. */\nexport function mapFlavor(scheme: \"dark\" | \"light\", brand?: MapBrand): Flavor {\n const c = mapPalette(scheme, brand);\n const base = namedFlavor(scheme);\n return {\n ...base,\n background: c.background,\n earth: c.land,\n park_a: c.park,\n park_b: c.park,\n wood_a: c.park,\n wood_b: c.park,\n scrub_a: c.park,\n scrub_b: c.park,\n glacier: c.land,\n sand: c.land,\n beach: c.land,\n zoo: c.park,\n hospital: c.land,\n industrial: c.land,\n school: c.land,\n military: c.land,\n aerodrome: c.land,\n pedestrian: c.land,\n runway: c.building,\n water: c.water,\n buildings: c.building,\n pier: c.building,\n boundaries: c.boundary,\n railway: c.casing,\n other: c.road,\n minor_service: c.road,\n minor_a: c.road,\n minor_b: c.road,\n link: c.road,\n major: c.roadMajor,\n highway: c.roadMajor,\n bridges_other: c.road,\n bridges_minor: c.road,\n bridges_link: c.road,\n bridges_major: c.roadMajor,\n bridges_highway: c.roadMajor,\n tunnel_other: c.road,\n tunnel_minor: c.road,\n tunnel_link: c.road,\n tunnel_major: c.roadMajor,\n tunnel_highway: c.roadMajor,\n minor_service_casing: c.casing,\n minor_casing: c.casing,\n link_casing: c.casing,\n major_casing_late: c.casing,\n major_casing_early: c.casing,\n highway_casing_late: c.casing,\n highway_casing_early: c.casing,\n bridges_other_casing: c.casing,\n bridges_minor_casing: c.casing,\n bridges_link_casing: c.casing,\n bridges_major_casing: c.casing,\n bridges_highway_casing: c.casing,\n tunnel_other_casing: c.casing,\n tunnel_minor_casing: c.casing,\n tunnel_link_casing: c.casing,\n tunnel_major_casing: c.casing,\n tunnel_highway_casing: c.casing,\n roads_label_minor: c.label,\n roads_label_minor_halo: c.halo,\n roads_label_major: c.label,\n roads_label_major_halo: c.halo,\n ocean_label: c.label,\n subplace_label: c.label,\n subplace_label_halo: c.halo,\n city_label: c.labelStrong,\n city_label_halo: c.halo,\n state_label: c.label,\n state_label_halo: c.halo,\n country_label: c.label,\n address_label: c.label,\n address_label_halo: c.halo,\n regular: \"Noto Sans Regular\",\n bold: \"Noto Sans Medium\",\n italic: \"Noto Sans Italic\",\n // No point-of-interest icons (no sprite to host): the app's own markers carry the places that matter.\n pois: undefined,\n landcover: undefined,\n };\n}\n\n/** A region of the basemap: its PMTiles file and the zoom up to which it has tiles. */\nexport type MapRegion = { url: string; maxzoom: number };\n\nexport type MapStyleOptions = {\n /** `dark` or `light`: the scheme of the page or the app. */\n scheme: \"dark\" | \"light\";\n /** The product whose accent the map carries (its markers, its water's tint). Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`): the place names in it where OpenStreetMap has them. Default `en`. */\n lang?: string;\n /** The low zooms of the whole world. Default: our world extract (zoom 0–6). */\n world?: MapRegion | null;\n /** The detailed region drawn from zoom 7. Default: Montréal–Québec (up to 14, overzoomed beyond). */\n region?: MapRegion | null;\n /** The glyphs' URL template. Default: our Noto Sans on the CDN. */\n glyphs?: string;\n};\n\n/** The zoom where the detailed region takes over from the world. */\nexport const REGION_FROM_ZOOM = 6.5;\n\n/**\n * The MapLibre style of a Krizaka map (a `StyleSpecification`): the world's low zooms, then the detailed region,\n * painted from the tokens. The `pmtiles://` sources need the PMTiles protocol (the web `Map` registers it; MapLibre\n * Native reads it as it is).\n */\nexport function mapStyle({ scheme, brand, lang = \"en\", world = { url: mapAssets().tiles.world, maxzoom: 6 }, region = { url: mapAssets().tiles.quebec, maxzoom: 14 }, glyphs = mapAssets().glyphs }: MapStyleOptions) {\n const flavor = mapFlavor(scheme, brand);\n const attribution = `<a href=\"${MAP_ATTRIBUTION.protomapsUrl}\">${MAP_ATTRIBUTION.protomaps}</a> · <a href=\"${MAP_ATTRIBUTION.osmUrl}\">${MAP_ATTRIBUTION.osm}</a>`;\n const sources: Record<string, unknown> = {};\n const all: { id: string; minzoom?: number; maxzoom?: number; [k: string]: unknown }[] = [];\n if (world) {\n sources.world = { type: \"vector\", url: `pmtiles://${world.url}`, attribution };\n for (const l of layers(\"world\", flavor, { lang }) as typeof all) {\n if (l.id === \"background\") all.push(l);\n else all.push({ ...l, id: `world-${l.id}`, maxzoom: region ? Math.min(l.maxzoom ?? 24, REGION_FROM_ZOOM) : l.maxzoom });\n }\n }\n if (region) {\n sources.region = { type: \"vector\", url: `pmtiles://${region.url}`, attribution };\n for (const l of layers(\"region\", flavor, { lang }) as typeof all) {\n if (l.id === \"background\") {\n if (!world) all.push(l);\n continue;\n }\n all.push({ ...l, id: `region-${l.id}`, minzoom: world ? Math.max(l.minzoom ?? 0, REGION_FROM_ZOOM) : l.minzoom });\n }\n }\n // No sprite is hosted: a layer that needs an icon (points of interest) is left out — the app's markers carry the places.\n const drawn = all.filter((l) => !(l.layout as Record<string, unknown> | undefined)?.[\"icon-image\"]);\n return { version: 8 as const, glyphs, sources, layers: drawn.map((l) => (l.maxzoom === undefined ? withoutKey(l, \"maxzoom\") : l)) };\n}\n\nconst withoutKey = <T extends Record<string, unknown>>(o: T, key: string) => Object.fromEntries(Object.entries(o).filter(([k]) => k !== key)) as T;\n","// Server-safe. A still map drawn without an engine: the places projected (Web Mercator) on the map's paper — or on a\n// picture of the area (`image`, a snapshot of the real map) — in the page's tokens. What a `Map` shows while its engine\n// loads and for good without WebGL; a picture for an e-mail, a card, a list item. An image (`role=\"img\"`) named by its\n// places.\n\n\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION } from \"./assets\";\nimport { type LngLat, project } from \"./geo\";\n\nexport const staticMap = tv({\n slots: {\n root: \"relative block h-full w-full overflow-hidden bg-surface-2\",\n svg: \"absolute inset-0 h-full w-full\",\n attribution: \"absolute bottom-1.5 right-1.5 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary\",\n },\n});\n\nexport type StaticMapPlace = { position: LngLat; label: string };\n\nexport type StaticMapProps = {\n /** What the picture shows, in words (\"Three pick-up points in the Plateau\"), passed translated: its accessible name. */\n label: string;\n /** The centre of the picture. */\n center: LngLat;\n /** The zoom of the picture. Default 12. */\n zoom?: number;\n /** The places drawn on it (an accent dot each), listed in its name. */\n places?: readonly StaticMapPlace[];\n /** A snapshot of the real map of the area (same centre and zoom), drawn under the places; the attribution then shows. */\n image?: string;\n /** Classes merged on the root (its size). */\n className?: string;\n};\n\nconst W = 640;\nconst H = 400;\n\n/** A still map: the places on the map's paper (or on a snapshot), named in words. */\nexport function StaticMap({ label, center, zoom = 12, places = [], image, className }: StaticMapProps) {\n const s = staticMap();\n const [cx, cy] = project(center, zoom);\n const at = (p: LngLat) => {\n const [x, y] = project(p, zoom);\n return [W / 2 + x - cx, H / 2 + y - cy] as const;\n };\n const name = places.length ? `${label}: ${places.map((p) => p.label).join(\", \")}` : label;\n return (\n <span role=\"img\" aria-label={name} className={s.root({ className })}>\n <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio=\"xMidYMid slice\" className={s.svg()} aria-hidden>\n <defs>\n <pattern id=\"kz-map-paper\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M48 0H0V48\" fill=\"none\" stroke=\"var(--kz-border-subtle)\" strokeWidth=\"1\" />\n </pattern>\n </defs>\n <rect width={W} height={H} fill=\"var(--kz-surface-2)\" />\n {image ? <image href={image} x=\"0\" y=\"0\" width={W} height={H} preserveAspectRatio=\"xMidYMid slice\" /> : <rect width={W} height={H} fill=\"url(#kz-map-paper)\" />}\n {places.map((p) => {\n const [x, y] = at(p.position);\n return (\n <g key={`${p.position[0]},${p.position[1]}`}>\n <circle cx={x} cy={y} r=\"12\" fill=\"var(--kz-accent)\" opacity=\"0.18\" />\n <circle cx={x} cy={y} r=\"7\" fill=\"var(--kz-accent)\" stroke=\"var(--kz-on-accent)\" strokeWidth=\"2.5\" />\n </g>\n );\n })}\n </svg>\n {image ? (\n <span aria-hidden className={s.attribution()}>\n {MAP_ATTRIBUTION.protomaps} · {MAP_ATTRIBUTION.osm}\n </span>\n ) : null}\n </span>\n );\n}\n\n"],"mappings":"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","names":["React","React","base","layers","jsx","jsxs","map","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/map/assets.ts","../src/map/geo.ts","../src/map/location-picker.tsx","../src/tv.ts","../src/cn.ts","../src/map/map.tsx","../src/media.ts","../../tokens/dist/native.js","../src/map/style.ts","../src/map/static-map.tsx"],"sourcesContent":["// Where the basemap lives: Protomaps PMTiles extracts on the Krizaka CDN (Bunny), uploaded by tools/map-assets.mjs.\n// No API key, no metered service: one file per region, read by HTTP range requests. A new extract is a new file (its\n// build date in the name) and a new default here.\n\nconst CDN = \"https://krizaka-assets.b-cdn.net/maps/v1\";\n\n/** The basemap extracts: OpenStreetMap data built by Protomaps (ODbL). `world` up to zoom 6, `quebec` (Montréal–Québec) up to 14. */\nexport const MAP_TILES = {\n world: `${CDN}/tiles/world-20261012.pmtiles`,\n quebec: `${CDN}/tiles/quebec-20261012.pmtiles`,\n} as const;\n\n/** The glyphs of the labels (Noto Sans, SIL OFL): `{fontstack}` and `{range}` filled by MapLibre. */\nexport const MAP_GLYPHS = `${CDN}/fonts/{fontstack}/{range}.pbf`;\n\n/** The words every map shows, as the licences ask: OpenStreetMap's contributors (ODbL) and Protomaps. */\nexport const MAP_ATTRIBUTION = { osm: \"© OpenStreetMap contributors\", osmUrl: \"https://www.openstreetmap.org/copyright\", protomaps: \"Protomaps\", protomapsUrl: \"https://protomaps.com\" } as const;\n\nlet base: string | null = null;\n\n/**\n * Where the maps read the tiles and the glyphs, when not from the CDN itself: a same-origin path that proxies it\n * (`https://www.krizaka.com/maps/v1`, a rewrite of the site) — no cross-origin request at all. Absolute URL, no trailing slash.\n */\nexport function setMapAssetsBase(url: string | null) {\n base = url ? url.replace(/\\/+$/, \"\") : null;\n}\n\n/** The tiles and glyphs the maps read now: the CDN's, or under the base set by `setMapAssetsBase`. */\nexport function mapAssets() {\n const at = (u: string) => (base ? u.replace(CDN, base) : u);\n return { tiles: { world: at(MAP_TILES.world), quebec: at(MAP_TILES.quebec) }, glyphs: at(MAP_GLYPHS) };\n}\n","/// <reference types=\"geojson\" />\n\n// Plain geometry for the maps, web and native (no DOM, no React): distances, circles, the Web Mercator projection.\n\n/** A position: longitude, latitude (GeoJSON's order, MapLibre's `LngLat`). */\nexport type LngLat = readonly [lng: number, lat: number];\n\nconst R = 6_371_008.8;\nconst rad = (d: number) => (d * Math.PI) / 180;\n\n/** The great-circle distance between two positions, in metres (haversine). */\nexport function distanceMeters(a: LngLat, b: LngLat): number {\n const dLat = rad(b[1] - a[1]);\n const dLng = rad(b[0] - a[0]);\n const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a[1])) * Math.cos(rad(b[1])) * Math.sin(dLng / 2) ** 2;\n return 2 * R * Math.asin(Math.min(1, Math.sqrt(h)));\n}\n\n/** A circle of `radius` metres around `center` as a GeoJSON polygon (`steps` points): a delivery zone, a search radius. */\nexport function circlePolygon(center: LngLat, radius: number, steps = 64): GeoJSON.Feature<GeoJSON.Polygon> {\n const [lng, lat] = center;\n const d = radius / R;\n const ring: [number, number][] = [];\n for (let i = 0; i <= steps; i++) {\n const brg = (2 * Math.PI * i) / steps;\n const lat2 = Math.asin(Math.sin(rad(lat)) * Math.cos(d) + Math.cos(rad(lat)) * Math.sin(d) * Math.cos(brg));\n const lng2 = rad(lng) + Math.atan2(Math.sin(brg) * Math.sin(d) * Math.cos(rad(lat)), Math.cos(d) - Math.sin(rad(lat)) * Math.sin(lat2));\n ring.push([(lng2 * 180) / Math.PI, (lat2 * 180) / Math.PI]);\n }\n return { type: \"Feature\", properties: {}, geometry: { type: \"Polygon\", coordinates: [ring] } };\n}\n\n/** The Web Mercator pixel of a position at a zoom (256-px tiles): what a static map draws without an engine. */\nexport function project([lng, lat]: LngLat, zoom: number): [number, number] {\n const scale = 256 * 2 ** zoom;\n const s = Math.sin(rad(Math.max(-85.0511, Math.min(85.0511, lat))));\n return [((lng + 180) / 360) * scale, (0.5 - Math.log((1 + s) / (1 - s)) / (4 * Math.PI)) * scale];\n}\n\n/** A distance written the locale's way: metres under 1 km, kilometres (one decimal under 10) above. */\nexport function formatDistance(meters: number, locale: string): string {\n if (meters < 1000) return new Intl.NumberFormat(locale, { style: \"unit\", unit: \"meter\", maximumFractionDigits: 0 }).format(Math.round(meters / 10) * 10);\n return new Intl.NumberFormat(locale, { style: \"unit\", unit: \"kilometer\", maximumFractionDigits: meters < 10_000 ? 1 : 0 }).format(meters / 1000);\n}\n\n/** The length of a path of positions, in metres (the sum of its great-circle legs). */\nexport function pathLength(path: readonly LngLat[]): number {\n let total = 0;\n for (let i = 1; i < path.length; i++) total += distanceMeters(path[i - 1]!, path[i]!);\n return total;\n}\n\n/** The position at `fraction` (0–1, clamped) of the path's length, with the heading of the leg it is on (degrees clockwise from north). */\nexport function pointAlong(path: readonly LngLat[], fraction: number): { position: LngLat; heading: number } {\n const first = path[0] ?? [0, 0];\n if (path.length < 2) return { position: first, heading: 0 };\n const target = Math.max(0, Math.min(1, fraction)) * pathLength(path);\n let walked = 0;\n for (let i = 1; i < path.length; i++) {\n const a = path[i - 1]!;\n const b = path[i]!;\n const leg = distanceMeters(a, b);\n if (walked + leg >= target || i === path.length - 1) {\n const t = leg === 0 ? 0 : Math.max(0, Math.min(1, (target - walked) / leg));\n const heading = (Math.atan2(b[0] - a[0], b[1] - a[1]) * 180) / Math.PI;\n return { position: [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t], heading: (heading + 360) % 360 };\n }\n walked += leg;\n }\n return { position: first, heading: 0 };\n}\n\n/** The first `fraction` (0–1) of the path, as a GeoJSON line: what a route has drawn so far. */\nexport function sliceLine(path: readonly LngLat[], fraction: number): GeoJSON.Feature<GeoJSON.LineString> {\n const f = Math.max(0, Math.min(1, fraction));\n const total = pathLength(path);\n const coordinates: [number, number][] = [];\n if (path.length) coordinates.push([...path[0]!] as [number, number]);\n let walked = 0;\n for (let i = 1; i < path.length && f > 0; i++) {\n const a = path[i - 1]!;\n const b = path[i]!;\n const leg = distanceMeters(a, b);\n if (walked + leg <= f * total) coordinates.push([...b] as [number, number]);\n else {\n const t = leg === 0 ? 0 : (f * total - walked) / leg;\n coordinates.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n break;\n }\n walked += leg;\n }\n if (coordinates.length === 1) coordinates.push([...coordinates[0]!]);\n return { type: \"Feature\", properties: {}, geometry: { type: \"LineString\", coordinates } };\n}\n","// Client. A place chosen by moving the map under a fixed pin — a meeting point, a delivery address, the centre of a\n// search — with its distance from where the person is (a chip) and \"near me\": the browser asks for the position only when\n// the button is pressed, and every answer has its words (locating, refused, unavailable). The value is a position.\nimport * as React from \"react\";\n\nimport { tv } from \"../tv\";\nimport { distanceMeters, formatDistance, type LngLat } from \"./geo\";\nimport { Map, type MapProps, RadiusCircle, UserPosition } from \"./map\";\n\nexport const locationPicker = tv({\n slots: {\n root: \"flex flex-col gap-2\",\n frame: \"relative\",\n pin: \"pointer-events-none absolute left-1/2 top-1/2 z-10 -translate-x-1/2 -translate-y-full text-accent drop-shadow-md\",\n bar: \"absolute inset-x-3 bottom-8 z-10 flex flex-wrap items-center justify-between gap-2\",\n chip: \"rounded-full border border-border-default bg-surface-1 px-3 py-1.5 text-xs font-semibold tabular-nums text-fg shadow-floating\",\n near:\n \"inline-flex h-10 items-center gap-2 rounded-full border border-border-default bg-surface-1 bg-sheen px-4 text-sm font-semibold text-fg shadow-floating inset-shadow-edge \" +\n \"transition-colors hover:border-border-strong focus-visible:outline-hidden focus-visible:focus-ring disabled:opacity-60\",\n status: \"text-xs text-fg-secondary data-[tone=danger]:text-fg-danger\",\n },\n});\n\nexport type LocationPickerLabels = {\n /** The button that centres the map on the person (\"Near me\"). */\n nearMe: string;\n /** While the position is asked (\"Finding you…\"). */\n locating: string;\n /** The person refused (\"Location is off for this site. Move the map instead.\"). */\n denied: string;\n /** No position (no GPS, a timeout) (\"We could not find you. Move the map instead.\"). */\n unavailable: string;\n /** The person's place in the list of places (\"You are here\"). */\n you: string;\n /** The distance chip, from a written distance (\"1.2 km from you\"). */\n distance: (distance: string) => string;\n};\n\nexport const locationPickerLabels: LocationPickerLabels = {\n nearMe: \"Near me\",\n locating: \"Finding you…\",\n denied: \"Location is off for this site. Move the map instead.\",\n unavailable: \"We could not find you. Move the map instead.\",\n you: \"You are here\",\n distance: (d) => `${d} from you`,\n};\n\nexport type LocationPickerProps = Pick<MapProps, \"scheme\" | \"brand\" | \"lang\" | \"region\" | \"world\" | \"fallback\" | \"className\"> & {\n /** The picker's accessible name (\"Meeting point\"), passed translated. */\n label: string;\n /** The chosen position (controlled): the centre of the map. */\n value: LngLat;\n /** Called when the map rests on a new position. */\n onValueChange: (value: LngLat) => void;\n /** The BCP 47 locale of the distance. */\n locale: string;\n /** The zoom at first. Default 14. */\n zoom?: number;\n /** A radius in metres drawn around the chosen position (a delivery zone, a search radius). */\n radius?: number;\n /** Where the person is, when the app already knows it (the distance chip shows at once). */\n origin?: LngLat | null;\n /** The words, passed translated; English by default. */\n labels?: Partial<LocationPickerLabels>;\n};\n\ntype Status = \"idle\" | \"locating\" | \"denied\" | \"unavailable\";\n\n/** A position picked by moving the map under a pin; its distance from the person; \"near me\" on request. */\nexport function LocationPicker({ label, value, onValueChange, locale, zoom = 14, radius, origin: originProp = null, labels: given, className, ...map }: LocationPickerProps) {\n const labels = { ...locationPickerLabels, ...given };\n const s = locationPicker();\n const [origin, setOrigin] = React.useState<LngLat | null>(originProp);\n const [camera, setCamera] = React.useState<LngLat>(value);\n const [status, setStatus] = React.useState<Status>(\"idle\");\n const statusId = React.useId();\n const nearMe = () => {\n if (typeof navigator === \"undefined\" || !navigator.geolocation) return setStatus(\"unavailable\");\n setStatus(\"locating\");\n navigator.geolocation.getCurrentPosition(\n (p) => {\n const here: LngLat = [p.coords.longitude, p.coords.latitude];\n setOrigin(here);\n setCamera(here);\n onValueChange(here);\n setStatus(\"idle\");\n },\n (e) => setStatus(e.code === 1 ? \"denied\" : \"unavailable\"),\n { enableHighAccuracy: false, timeout: 10_000, maximumAge: 60_000 },\n );\n };\n const distance = origin ? formatDistance(distanceMeters(origin, value), locale) : null;\n const message = status === \"locating\" ? labels.locating : status === \"denied\" ? labels.denied : status === \"unavailable\" ? labels.unavailable : \"\";\n return (\n <div className={s.root({ className })} role=\"group\" aria-label={label}>\n <div className={s.frame()}>\n <Map {...map} label={label} center={camera} zoom={zoom} controlled onMoveEnd={(v) => onValueChange(v.center)} className=\"h-80\">\n {radius ? <RadiusCircle center={value} radius={radius} /> : null}\n {origin ? <UserPosition position={origin} label={labels.you} /> : null}\n </Map>\n <svg aria-hidden viewBox=\"0 0 24 32\" className={s.pin()} width=\"30\" height=\"40\">\n <path d=\"M12 0C5.4 0 0 5.2 0 11.7 0 20.4 12 32 12 32s12-11.6 12-20.3C24 5.2 18.6 0 12 0Z\" fill=\"currentColor\" />\n <circle cx=\"12\" cy=\"11.5\" r=\"4.5\" fill=\"var(--kz-on-accent)\" />\n </svg>\n <div className={s.bar()}>\n {distance ? (\n <span className={s.chip()} aria-live=\"polite\">\n {labels.distance(distance)}\n </span>\n ) : (\n <span />\n )}\n <button type=\"button\" className={s.near()} onClick={nearMe} disabled={status === \"locating\"} aria-describedby={message ? statusId : undefined}>\n <svg aria-hidden viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"3.5\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n </svg>\n {status === \"locating\" ? labels.locating : labels.nearMe}\n </button>\n </div>\n </div>\n <p id={statusId} className={s.status()} data-tone={status === \"denied\" || status === \"unavailable\" ? \"danger\" : undefined} aria-live=\"polite\">\n {message}\n </p>\n </div>\n );\n}\n","import { createTV } from \"tailwind-variants\";\n\nimport { twMergeConfig } from \"./cn\";\n\n/** `tv` with the preset's utilities known to the merge engine (`twMergeConfig`): what a primitive wearing the material is styled with. */\nexport const tv = createTV({ twMergeConfig });\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnMerge, type TWMergeConfig } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * The preset's own utilities, told to the merge engine: the material (BRAND.md §6: `bg-sheen`, `bg-fill-shade`,\n * `bg-halo` are background images; `shadow-raised` · `-floating` · `-overlay` · `-glow` are shadows; `inset-shadow-edge`\n * · `-edge-fill` · `-well` are inset shadows). Unknown, tailwind-merge would take `bg-sheen` for a colour and drop the\n * `bg-surface-2` it lies on.\n */\nexport const twMergeConfig: TWMergeConfig = {\n extend: {\n classGroups: {\n \"bg-image\": [\"bg-sheen\", \"bg-fill-shade\", \"bg-halo\"],\n shadow: [\"shadow-raised\", \"shadow-floating\", \"shadow-overlay\", \"shadow-glow\", \"shadow-glow-lg\"],\n \"inset-shadow\": [\"inset-shadow-edge\", \"inset-shadow-edge-fill\", \"inset-shadow-well\"],\n },\n },\n};\n\nconst mergeConfig = { twMergeConfig };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return cnMerge(clsx(inputs))(mergeConfig) ?? \"\";\n}\n\n","// Client. A map of the Krizaka family on MapLibre GL JS (open source) over Protomaps tiles on our CDN — no API key, no\n// metered service. MapLibre and PMTiles are optional peers, loaded when a map is shown (a dynamic import: a page without\n// a map never pays for them). The style comes from the tokens (`mapStyle`), in the page's scheme, and follows it.\n//\n// What every map does: the canvas is decorative for assistive technologies — the map is a region named by `label`, its\n// places a list read in order (the text alternative, `MapPlaces`), each marker a button when it does something; the\n// attribution of OpenStreetMap (ODbL) and Protomaps is always shown; under reduced motion the camera jumps, never flies;\n// without WebGL (or while the engine loads) `fallback` stands in its place.\n/// <reference types=\"geojson\" />\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { usePrefersReducedMotion } from \"../media\";\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION, setMapAssetsBase } from \"./assets\";\nimport { circlePolygon, type LngLat, pointAlong, sliceLine } from \"./geo\";\nimport { type MapBrand, mapPalette, type MapRegion, mapStyle } from \"./style\";\n\ntype MapLibre = typeof import(\"maplibre-gl\");\ntype MapInstance = import(\"maplibre-gl\").Map;\n\nexport const map = tv({\n slots: {\n root: \"relative isolate overflow-hidden rounded-2xl border border-border-default bg-surface-2 shadow-raised\",\n canvas: \"absolute inset-0 [&_.maplibregl-canvas]:outline-hidden\",\n fallback: \"absolute inset-0\",\n controls: \"absolute right-3 top-3 z-10 flex flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-floating\",\n control:\n \"inline-flex h-10 w-10 items-center justify-center text-lg font-semibold text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring-inset disabled:opacity-40 [&+&]:border-t [&+&]:border-border-subtle\",\n attribution:\n \"absolute bottom-1.5 right-1.5 z-10 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary backdrop-blur-sm [&_a]:underline-offset-2 hover:[&_a]:underline\",\n places: \"sr-only\",\n marker:\n \"group relative flex -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-2 border-on-accent bg-accent shadow-floating outline-hidden \" +\n \"transition-transform hover:scale-110 focus-visible:focus-ring motion-reduce:transition-none motion-reduce:hover:scale-100 data-[selected]:scale-125\",\n markerLabel:\n \"pointer-events-none absolute bottom-full mb-2 whitespace-nowrap rounded-lg border border-border-default bg-surface-1 px-2 py-1 text-xs font-semibold text-fg shadow-floating\",\n },\n});\n\ntype Registry = { add: (id: string, text: string) => void; remove: (id: string) => void };\ntype Ctx = { map: MapInstance | null; lib: MapLibre | null; ready: boolean; reduce: boolean; places: Registry };\nconst MapContext = React.createContext<Ctx | null>(null);\n\n/** The map a child is drawn on (`Marker`, `RadiusCircle`, `Cluster`…): the MapLibre instance once it is ready. */\nexport function useMap(): Ctx {\n const ctx = React.useContext(MapContext);\n if (!ctx) throw new Error(\"useMap: inside a <Map> only\");\n return ctx;\n}\n\n/** The scheme of the page (`html.light`), followed as it changes. */\nfunction usePageScheme(): \"dark\" | \"light\" {\n const read = () => (typeof document !== \"undefined\" && document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\");\n const [scheme, setScheme] = React.useState<\"dark\" | \"light\">(read);\n React.useEffect(() => {\n const observer = new MutationObserver(() => setScheme(read()));\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n return scheme;\n}\n\nlet protocolAdded = false;\nlet workerUrl: string | null = null;\n\n/**\n * Where MapLibre's worker comes from. A bundler (Next.js, Vite) renames MapLibre's module and leaves its worker behind:\n * by default the worker of the very version the app installed is imported from jsDelivr (free, open source, no key)\n * through a module worker of the page's own origin. An app with a strict CSP serves it itself —\n * `configureMaps({ workerUrl: \"/maplibre-gl-worker.mjs\" })` (with `maplibre-gl-shared.mjs` beside it), once, before a map.\n * `assetsBase` reads the tiles and glyphs through a same-origin proxy of the CDN (`setMapAssetsBase`).\n */\nexport function configureMaps(options: { workerUrl?: string; assetsBase?: string }) {\n if (options.workerUrl) workerUrl = options.workerUrl;\n if (options.assetsBase) setMapAssetsBase(options.assetsBase);\n}\n\n/** MapLibre, its worker and the PMTiles protocol, loaded once for the page. */\nasync function loadEngine(): Promise<MapLibre> {\n const [lib, pmtiles] = await Promise.all([import(\"maplibre-gl\"), import(\"pmtiles\")]);\n const ml = ((lib as unknown as { default?: MapLibre }).default ?? lib) as MapLibre;\n if (!protocolAdded) {\n protocolAdded = true;\n const remote = `https://cdn.jsdelivr.net/npm/maplibre-gl@${ml.getVersion()}/dist/maplibre-gl-worker.mjs`;\n ml.setWorkerUrl(workerUrl ?? URL.createObjectURL(new Blob([`import ${JSON.stringify(remote)};`], { type: \"text/javascript\" })));\n ml.addProtocol(\"pmtiles\", new pmtiles.Protocol().tile);\n }\n return ml;\n}\n\nconst hasWebGL = () => {\n try {\n const canvas = document.createElement(\"canvas\");\n return Boolean(canvas.getContext(\"webgl2\") ?? canvas.getContext(\"webgl\"));\n } catch {\n return false;\n }\n};\n\nexport type MapLabels = {\n /** The zoom-in button (\"Zoom in\"). */\n zoomIn: string;\n /** The zoom-out button (\"Zoom out\"). */\n zoomOut: string;\n /** Before the list of places read in place of the map (\"Places on the map\"). */\n places: string;\n};\n\nexport const mapLabels: MapLabels = { zoomIn: \"Zoom in\", zoomOut: \"Zoom out\", places: \"Places on the map\" };\n\nexport type MapProps = {\n /** The map's accessible name (\"Pick-up points near you\"), passed translated. */\n label: string;\n /** Where the map looks at first: longitude, latitude. */\n center: LngLat;\n /** The zoom at first (0 the world, 12 a city, 16 a street). Default 12. */\n zoom?: number;\n /** The camera follows `center` and `zoom` when they change (flies, or jumps under reduced motion). */\n controlled?: boolean;\n /** The scheme. Default: the page's (`html.light`), followed. */\n scheme?: \"dark\" | \"light\";\n /** The product whose accent the map carries. Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`). Default `en`. */\n lang?: string;\n /** The detailed region (PMTiles) drawn from zoom 6.5. Default: our Montréal–Québec extract (to zoom 14). */\n region?: MapRegion | null;\n /** The world's low zooms (PMTiles). Default: our world extract (to zoom 6). */\n world?: MapRegion | null;\n /** The zoom buttons (default) — the keyboard also zooms with + and − on the focused map. */\n controls?: boolean;\n /** Moves with the hand (default); `false` makes a still picture of a place. */\n interactive?: boolean;\n /** Shown in place of the map while its engine loads, and for good without WebGL: a `StaticMap`, a picture. */\n fallback?: React.ReactNode;\n /** Called when the camera rests: its centre and zoom. */\n onMoveEnd?: (view: { center: LngLat; zoom: number }) => void;\n /** Called with the position pressed on the map (not on a marker). */\n onPress?: (position: LngLat) => void;\n /** Markers, circles, clusters, the user's position — and anything drawn over the map. */\n children?: React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<MapLabels>;\n /** Classes merged on the root (its height: `h-80`, `aspect-video`). */\n className?: string;\n};\n\n/** A map: MapLibre over our Protomaps tiles, styled from the tokens, with its places as a list for screen readers. */\nexport function Map({\n label,\n center,\n zoom = 12,\n controlled,\n scheme: schemeProp,\n brand,\n lang,\n region,\n world,\n controls = true,\n interactive = true,\n fallback,\n onMoveEnd,\n onPress,\n children,\n labels: given,\n className,\n}: MapProps) {\n const labels = { ...mapLabels, ...given };\n const s = map();\n const pageScheme = usePageScheme();\n const scheme = schemeProp ?? pageScheme;\n const reduce = usePrefersReducedMotion();\n const host = React.useRef<HTMLDivElement>(null);\n const [instance, setInstance] = React.useState<MapInstance | null>(null);\n const [lib, setLib] = React.useState<MapLibre | null>(null);\n const [ready, setReady] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [places, setPlaces] = React.useState<Record<string, string>>({});\n const registry = React.useMemo<Registry>(\n () => ({\n add: (id, text) => setPlaces((p) => (p[id] === text ? p : { ...p, [id]: text })),\n remove: (id) =>\n setPlaces((p) => {\n const { [id]: _gone, ...rest } = p;\n return rest;\n }),\n }),\n [],\n );\n const latest = React.useRef({ onMoveEnd, onPress });\n React.useEffect(() => {\n latest.current = { onMoveEnd, onPress };\n });\n const styleOf = React.useCallback((sc: \"dark\" | \"light\") => mapStyle({ scheme: sc, brand, lang, ...(region !== undefined ? { region } : {}), ...(world !== undefined ? { world } : {}) }), [brand, lang, region, world]);\n\n // The style the map wears now: the engine loads asynchronously, so the scheme it is created with is read when it is ready\n // (the page's theme may have flipped since the first render), and the effect below applies whatever differs.\n const wanted = React.useRef({ scheme, styleOf });\n React.useEffect(() => {\n wanted.current = { scheme, styleOf };\n });\n const applied = React.useRef<{ scheme: \"dark\" | \"light\"; styleOf: typeof styleOf } | null>(null);\n\n // The engine and the map, once (the style follows the scheme below).\n React.useEffect(() => {\n if (!host.current) return;\n let alive = true;\n let created: MapInstance | null = null;\n (hasWebGL() ? loadEngine() : Promise.reject(new Error(\"no WebGL\")))\n .then((ml) => {\n if (!alive || !host.current) return;\n applied.current = wanted.current;\n created = new ml.Map({\n container: host.current,\n style: wanted.current.styleOf(wanted.current.scheme) as never,\n center: [...center] as [number, number],\n zoom,\n interactive,\n attributionControl: false,\n fadeDuration: reduce ? 0 : 300,\n cooperativeGestures: false,\n });\n created.getCanvas().setAttribute(\"aria-hidden\", \"true\");\n created.getCanvas().setAttribute(\"tabindex\", \"-1\");\n created.on(\"load\", () => alive && setReady(true));\n // The basemap cannot be read (offline, a blocked request): the fallback comes back rather than an empty map.\n created.on(\"error\", (e) => {\n const source = (e as { sourceId?: string }).sourceId;\n if (alive && (source === \"world\" || source === \"region\")) setFailed(true);\n });\n created.on(\"moveend\", () => {\n const c = created!.getCenter();\n latest.current.onMoveEnd?.({ center: [c.lng, c.lat], zoom: created!.getZoom() });\n });\n created.on(\"click\", (e) => {\n if ((e.originalEvent.target as Element | null)?.closest?.(\"[data-kz-marker]\")) return;\n latest.current.onPress?.([e.lngLat.lng, e.lngLat.lat]);\n });\n setLib(ml);\n setInstance(created);\n })\n .catch(() => alive && setFailed(true));\n return () => {\n alive = false;\n created?.remove();\n };\n // Created once: the props that change afterwards are followed by the effects below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The scheme (and the brand, the language) change the style, not the map.\n React.useEffect(() => {\n if (!instance) return;\n if (applied.current?.scheme === scheme && applied.current.styleOf === styleOf) return;\n applied.current = { scheme, styleOf };\n instance.setStyle(styleOf(scheme) as never);\n }, [instance, scheme, styleOf]);\n\n // A controlled camera: it flies (or jumps) to the new view.\n React.useEffect(() => {\n if (!instance || !controlled) return;\n const view = { center: [...center] as [number, number], zoom };\n if (reduce) instance.jumpTo(view);\n else instance.flyTo({ ...view, speed: 1.4 });\n }, [instance, controlled, center[0], center[1], zoom, reduce]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const zoomBy = (by: number) => instance && (reduce ? instance.setZoom(instance.getZoom() + by) : instance.zoomTo(instance.getZoom() + by, { duration: 250 }));\n const ctx = React.useMemo<Ctx>(() => ({ map: instance, lib, ready, reduce, places: registry }), [instance, lib, ready, reduce, registry]);\n const listed = Object.entries(places);\n return (\n <MapContext.Provider value={ctx}>\n <section\n aria-label={label}\n className={s.root({ className })}\n data-ready={ready ? \"\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget || !instance) return;\n if (e.key === \"+\" || e.key === \"=\") zoomBy(1);\n else if (e.key === \"-\") zoomBy(-1);\n else if (e.key.startsWith(\"Arrow\")) {\n e.preventDefault();\n const step = 80;\n const by: Record<string, [number, number]> = { ArrowUp: [0, -step], ArrowDown: [0, step], ArrowLeft: [-step, 0], ArrowRight: [step, 0] };\n instance.panBy(by[e.key]!, { duration: reduce ? 0 : 200 });\n }\n }}\n >\n {!ready || failed ? <div className={s.fallback()}>{fallback}</div> : null}\n <div ref={host} className={s.canvas()} style={{ position: \"absolute\", inset: 0, visibility: failed ? \"hidden\" : undefined }} />\n {controls && interactive && !failed ? (\n <div className={s.controls()}>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomIn} disabled={!instance} onClick={() => zoomBy(1)}>\n +\n </button>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomOut} disabled={!instance} onClick={() => zoomBy(-1)}>\n −\n </button>\n </div>\n ) : null}\n <p className={s.attribution()}>\n <a href={MAP_ATTRIBUTION.protomapsUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.protomaps}\n </a>\n {\" · \"}\n <a href={MAP_ATTRIBUTION.osmUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.osm}\n </a>\n </p>\n {listed.length ? (\n <div className={s.places()}>\n <p>{labels.places}</p>\n <ul>\n {listed.map(([id, text]) => (\n <li key={id}>{text}</li>\n ))}\n </ul>\n </div>\n ) : null}\n {children}\n </section>\n </MapContext.Provider>\n );\n}\n\nexport type MarkerProps = {\n /** Where it stands. */\n position: LngLat;\n /** Its words: the place's name, read in the list of places and as the button's name (\"Café Olimpico, 350 m\"). */\n label: string;\n /** Pressing it (details in a panel, a sheet): the marker becomes a button. */\n onPress?: () => void;\n /** The chosen one: larger, its label shown above it. */\n selected?: boolean;\n /** Its size in pixels. Default 18. */\n size?: number;\n /** What it draws instead of the accent dot (an avatar, an icon). */\n children?: React.ReactNode;\n /** Drops onto its place once, after this many milliseconds (stagger several: `index * 90`). Still under reduced motion. */\n drop?: number;\n};\n\nconst noSubscription = () => () => undefined;\n\n/** A place on the map: an accent dot (or your content) that is a button when it does something. */\nexport function Marker({ position, label, onPress, selected, size = 18, children, drop }: MarkerProps) {\n const { map: m, lib, places } = useMap();\n const s = map();\n const id = React.useId();\n // The marker's host element exists only in a browser: the server and the hydration pass render nothing for it, then the\n // client swaps in the element — `useSyncExternalStore`'s server snapshot, so hydration never sees a difference.\n const host = React.useRef<HTMLElement | null>(null);\n const el = React.useSyncExternalStore(\n noSubscription,\n () => (host.current ??= document.createElement(\"div\")),\n () => null,\n );\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib || !el) return;\n el.setAttribute(\"data-kz-marker\", \"\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, el, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n if (!el) return null;\n const Tag = onPress ? \"button\" : \"span\";\n return createPortal(\n <Tag\n type={onPress ? \"button\" : undefined}\n aria-label={onPress ? label : undefined}\n aria-hidden={onPress ? undefined : true}\n aria-pressed={onPress && selected !== undefined ? selected : undefined}\n data-selected={selected ? \"\" : undefined}\n data-drop={drop === undefined ? undefined : \"\"}\n className={s.marker()}\n style={{ ...(children ? null : { width: size, height: size }), ...(drop === undefined ? null : ({ \"--kz-map-drop-delay\": `${drop}ms` } as React.CSSProperties)) }}\n onClick={onPress}\n >\n {children}\n {selected ? <span className={s.markerLabel()}>{label}</span> : null}\n </Tag>,\n el,\n );\n}\n\nexport type UserPositionProps = {\n /** Where the person is. */\n position: LngLat;\n /** Its words (\"You are here\"), read in the list of places. */\n label: string;\n /** The accuracy in metres: a soft disc around the dot. */\n accuracy?: number;\n};\n\n/** The person's position: a dot that pulses (still under reduced motion), its accuracy as a soft disc. */\nexport function UserPosition({ position, label, accuracy }: UserPositionProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-user\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return accuracy ? <RadiusCircle center={position} radius={accuracy} tone=\"soft\" /> : null;\n}\n\nexport type PulseProps = {\n /** Where it beats. */\n position: LngLat;\n /** Its words (\"High demand in Mile End\"), read in the list of places. */\n label: string;\n /** The rings' reach in pixels. Default 56. */\n size?: number;\n /** `accent` (a zone that matters) or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A heat pulse: rings that swell and fade around a place (a hot spot, a demand peak). Still rings under reduced motion. */\nexport function Pulse({ position, label, size = 56, tone = \"accent\" }: PulseProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-pulse\";\n el.setAttribute(\"data-tone\", tone);\n el.setAttribute(\"aria-hidden\", \"true\");\n el.style.setProperty(\"--kz-map-pulse-size\", `${size}px`);\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, tone, size, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return null;\n}\n\nexport type RouteProps = {\n /** The path, as positions in order (a polyline: a delivery, a walk, a flight). */\n path: readonly LngLat[];\n /** Its words (\"Delivery from the studio to Mile End, 2.4 km\"), read in the list of places. */\n label: string;\n /** The line draws itself once, from its start, when the map is on screen; `false` shows it whole. Default `true`. */\n draw?: boolean;\n /** How long the drawing lasts, in milliseconds. Default 1400. */\n duration?: number;\n /** A dot that travels the route once drawn (`true`, or your own content: an avatar, an icon). */\n courier?: boolean | React.ReactNode;\n /** How long a trip takes, in milliseconds. Default 3200. */\n tripDuration?: number;\n /** Start the trips again after a pause (milliseconds), only while the map is on screen. Without it the courier makes one trip. */\n repeatAfter?: number;\n /** Changing it replays the drawing and the trip. */\n replayKey?: string | number;\n /** `accent` or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\ntype RouteSource = { setData?: (d: GeoJSON.GeoJSON) => void };\n\n/** A route on the map: a line that draws itself, and a courier that travels it. Under reduced motion the line is whole and the courier rests at the end. */\nexport function Route({ path, label, draw = true, duration = 1400, courier, tripDuration = 3200, repeatAfter, replayKey, tone = \"accent\" }: RouteProps) {\n const { map: m, lib, ready, reduce, places } = useMap();\n const id = `kz-route-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const animated = draw && !reduce;\n // The drawing is done for this `replayKey` (a new key draws again); without animation the line is whole at once.\n const token = replayKey ?? 0;\n const [doneFor, setDoneFor] = React.useState<string | number | null>(null);\n const drawn = !animated || doneFor === token;\n const whole = React.useMemo(() => sliceLine(path, 1), [path]);\n const start = React.useMemo(() => sliceLine(path, 0), [path]);\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n useGeoLayer(id, drawn ? whole : start, (c) => [\n { id: `${id}-casing`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": 9, \"line-opacity\": tone === \"soft\" ? 0.08 : 0.16, \"line-blur\": 3 } },\n { id: `${id}-line`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 2 : 3.5, \"line-opacity\": tone === \"soft\" ? 0.6 : 1 } },\n ]);\n // Is the map on screen? The drawing waits for it, and a repeating trip does not start while it is not.\n const [visible, setVisible] = React.useState(() => typeof IntersectionObserver === \"undefined\");\n React.useEffect(() => {\n const el = m?.getContainer?.();\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(([entry]) => setVisible(!!entry?.isIntersecting), { threshold: 0.25 });\n io.observe(el);\n return () => io.disconnect();\n }, [m]);\n // The drawing: a finite animation, one frame per paint for `duration` ms — no loop, nothing runs afterwards.\n React.useEffect(() => {\n if (!m || !ready || !animated || drawn || !visible) return;\n const source = m.getSource(id) as RouteSource | undefined;\n let frame = 0;\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, duration));\n source?.setData?.(sliceLine(path, 1 - (1 - t) ** 3));\n if (t < 1) frame = requestAnimationFrame(step);\n else setDoneFor(token);\n };\n frame = requestAnimationFrame(step);\n return () => cancelAnimationFrame(frame);\n }, [m, ready, animated, drawn, visible, id, path, duration, token]);\n // The courier: one trip once the line is drawn (finite frames), again after `repeatAfter` while on screen.\n const [trip, setTrip] = React.useState(0);\n React.useEffect(() => {\n if (!courier || !m || !lib || !ready || !drawn) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-courier\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...(path[reduce ? path.length - 1 : 0] ?? [0, 0])] as [number, number]).addTo(m);\n let frame = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (!reduce && visible) {\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, tripDuration));\n const eased = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n marker.setLngLat([...pointAlong(path, eased).position] as [number, number]);\n if (t < 1) frame = requestAnimationFrame(step);\n else if (repeatAfter !== undefined) timer = setTimeout(() => setTrip((n) => n + 1), repeatAfter);\n };\n frame = requestAnimationFrame(step);\n } else if (!reduce) marker.setLngLat([...(path[path.length - 1] ?? [0, 0])] as [number, number]);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(timer);\n marker.remove();\n };\n }, [courier, m, lib, ready, drawn, visible, reduce, path, tripDuration, repeatAfter, trip, token]);\n return null;\n}\n\n/** Adds a GeoJSON source and its layers while the map has a style (again after a change of scheme). */\nfunction useGeoLayer(id: string, data: GeoJSON.GeoJSON, layers: (palette: ReturnType<typeof mapPalette>) => object[], source: object = {}) {\n const { map: m, ready } = useMap();\n const dataRef = React.useRef(data);\n React.useEffect(() => {\n dataRef.current = data;\n const src = m?.getSource(id) as { setData?: (d: GeoJSON.GeoJSON) => void } | undefined;\n src?.setData?.(data);\n }, [m, id, data]);\n React.useEffect(() => {\n if (!m || !ready) return;\n const add = () => {\n if (m.getSource(id)) return;\n const scheme = document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\";\n m.addSource(id, { type: \"geojson\", data: dataRef.current, ...source } as never);\n for (const layer of layers(mapPalette(scheme))) m.addLayer(layer as never);\n };\n add();\n m.on(\"styledata\", add);\n return () => {\n m.off(\"styledata\", add);\n if (!m.getStyle()) return;\n for (const layer of m.getStyle().layers.filter((l) => \"source\" in l && l.source === id)) m.removeLayer(layer.id);\n if (m.getSource(id)) m.removeSource(id);\n };\n }, [m, ready, id]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nexport type RadiusCircleProps = {\n /** Its centre. */\n center: LngLat;\n /** Its radius in metres. */\n radius: number;\n /** `accent` (a zone that matters: a delivery area, a search radius) or `soft` (an accuracy). */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A circle on the ground: a delivery zone, a search radius — filled with the accent, its edge drawn. */\nexport function RadiusCircle({ center, radius, tone = \"accent\" }: RadiusCircleProps) {\n const id = `kz-circle-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo(() => circlePolygon(center, radius), [center[0], center[1], radius]); // eslint-disable-line react-hooks/exhaustive-deps\n useGeoLayer(id, data, (c) => [\n { id: `${id}-fill`, type: \"fill\", source: id, paint: { \"fill-color\": c.accent, \"fill-opacity\": tone === \"soft\" ? 0.1 : 0.14 } },\n { id: `${id}-line`, type: \"line\", source: id, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 1 : 2, \"line-opacity\": tone === \"soft\" ? 0.4 : 0.9, ...(tone === \"accent\" ? { \"line-dasharray\": [2, 1.5] } : {}) } },\n ]);\n return null;\n}\n\nexport type ClusterPoint = { id: string; position: LngLat; label: string };\n\nexport type ClusterProps = {\n /** The points: many of them (creators, pick-up points, events). */\n points: readonly ClusterPoint[];\n /** Pressing a single point. A group zooms in to open itself. */\n onPointPress?: (point: ClusterPoint) => void;\n /** The radius, in pixels, within which points group. Default 48. */\n radius?: number;\n};\n\n/** Many points grouped while they are close: a disc with the count, opened by zooming in; each point is listed for screen readers. */\nexport function Cluster({ points, onPointPress, radius = 48 }: ClusterProps) {\n const { map: m, ready, reduce, places } = useMap();\n const id = `kz-cluster-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo<GeoJSON.FeatureCollection>(\n () => ({ type: \"FeatureCollection\", features: points.map((p) => ({ type: \"Feature\", properties: { id: p.id, label: p.label }, geometry: { type: \"Point\", coordinates: [...p.position] } })) }),\n [points],\n );\n React.useEffect(() => {\n for (const p of points) places.add(`${id}-${p.id}`, p.label);\n return () => points.forEach((p) => places.remove(`${id}-${p.id}`));\n }, [points, places, id]);\n useGeoLayer(\n id,\n data,\n (c) => [\n { id: `${id}-groups`, type: \"circle\", source: id, filter: [\"has\", \"point_count\"], paint: { \"circle-color\": c.accent, \"circle-opacity\": 0.92, \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 16, 10, 20, 50, 26], \"circle-stroke-width\": 3, \"circle-stroke-color\": c.halo } },\n { id: `${id}-count`, type: \"symbol\", source: id, filter: [\"has\", \"point_count\"], layout: { \"text-field\": [\"get\", \"point_count_abbreviated\"], \"text-font\": [\"Noto Sans Medium\"], \"text-size\": 13 }, paint: { \"text-color\": c.onAccent } },\n { id: `${id}-points`, type: \"circle\", source: id, filter: [\"!\", [\"has\", \"point_count\"]], paint: { \"circle-color\": c.accent, \"circle-radius\": 7, \"circle-stroke-width\": 2.5, \"circle-stroke-color\": c.halo } },\n ],\n { cluster: true, clusterRadius: radius, clusterMaxZoom: 15 },\n );\n React.useEffect(() => {\n if (!m || !ready) return;\n const openGroup = async (e: { features?: { properties: Record<string, unknown>; geometry: GeoJSON.Geometry }[] }) => {\n const f = e.features?.[0];\n if (!f) return;\n const src = m.getSource(id) as unknown as { getClusterExpansionZoom: (id: number) => Promise<number> };\n const z = await src.getClusterExpansionZoom(f.properties.cluster_id as number);\n const center = (f.geometry as GeoJSON.Point).coordinates as [number, number];\n if (reduce) m.jumpTo({ center, zoom: z });\n else m.easeTo({ center, zoom: z });\n };\n const pressPoint = (e: { features?: { properties: Record<string, unknown> }[] }) => {\n const pid = e.features?.[0]?.properties.id;\n const point = points.find((p) => p.id === pid);\n if (point) onPointPress?.(point);\n };\n m.on(\"click\", `${id}-groups`, openGroup as never);\n m.on(\"click\", `${id}-points`, pressPoint as never);\n return () => {\n m.off(\"click\", `${id}-groups`, openGroup as never);\n m.off(\"click\", `${id}-points`, pressPoint as never);\n };\n }, [m, ready, id, points, onPointPress, reduce]);\n return null;\n}\n","// Shared by the data and media primitives (client): the clock format, the reduced-motion preference, the\n// in-view observer, a debounced callback. Never an entry of its own.\nimport * as React from \"react\";\n\n/** Seconds as a clock: `75` → `1:15`, `3725` → `1:02:05`. Negative or unknown values read `0:00`. */\nexport function formatClock(seconds: number): string {\n const s = Number.isFinite(seconds) && seconds > 0 ? Math.floor(seconds) : 0;\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const rest = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${rest}` : `${m}:${rest}`;\n}\n\n/** Clamps `value` between `min` and `max`. */\nexport const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst REDUCE = \"(prefers-reduced-motion: reduce)\";\nconst subscribeReduce = (notify: () => void) => {\n if (typeof window === \"undefined\" || !window.matchMedia) return () => undefined;\n const query = window.matchMedia(REDUCE);\n query.addEventListener?.(\"change\", notify);\n return () => query.removeEventListener?.(\"change\", notify);\n};\n\n/** `true` when the person asked for less motion (`prefers-reduced-motion: reduce`); follows a change of the setting. */\nexport function usePrefersReducedMotion(): boolean {\n return React.useSyncExternalStore(\n subscribeReduce,\n () => (typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(REDUCE).matches : false),\n () => false,\n );\n}\n\n/** The latest value of a callback, for an effect or an event that must not re-subscribe when it changes. */\nexport function useLatest<T>(value: T) {\n const ref = React.useRef(value);\n React.useLayoutEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\n/** Calls `onChange(visible)` when at least `threshold` of the element enters or leaves the viewport. */\nexport function useInView(ref: React.RefObject<Element | null>, onChange: (visible: boolean) => void, enabled = true, threshold = 0.25) {\n const latest = useLatest(onChange);\n React.useEffect(() => {\n const element = ref.current;\n if (!enabled || !element || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(([entry]) => entry && latest.current(entry.isIntersecting), { threshold });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, threshold, latest]);\n}\n\n/** A value controlled by a prop when it is given, held inside otherwise; `set` calls `onChange` either way. */\nexport function useControllable<T>(value: T | undefined, defaultValue: T, onChange?: (next: T) => void) {\n const [inner, setInner] = React.useState(defaultValue);\n const controlled = value !== undefined;\n const current = controlled ? value : inner;\n const set = React.useCallback(\n (next: T) => {\n if (!controlled) setInner(next);\n onChange?.(next);\n },\n [controlled, onChange],\n );\n return [current, set] as const;\n}\n\n/** Starts a media element without an unhandled rejection (autoplay refused, the source replaced meanwhile). */\nexport function playSafely(media: HTMLMediaElement | null | undefined) {\n try {\n const started = media?.play() as Promise<void> | undefined;\n started?.catch?.(() => undefined);\n } catch {\n // Not playable here (no media support): the controls stay as they are.\n }\n}\n","// @krizaka/tokens — GÉNÉRÉ depuis src/tokens/*.tokens.json par scripts/build.mjs : ne pas éditer.\n\n/** Colour roles per theme, resolved for React Native (`#rrggbb`, `rgba()` when translucent). */\nexport const themes = /* @__PURE__ */ Object.freeze({ dark: /* @__PURE__ */ Object.freeze({\n \"surface0\": \"#0c0c0e\",\n \"surface1\": \"#161618\",\n \"surface2\": \"#1f1f23\",\n \"surface3\": \"#2a2a2d\",\n \"media\": \"#1b1b1d\",\n \"borderSubtle\": \"rgba(255,255,255,0.06)\",\n \"borderDefault\": \"rgba(255,255,255,0.1)\",\n \"borderStrong\": \"rgba(255,255,255,0.16)\",\n \"textPrimary\": \"#f5f5f5\",\n \"textSecondary\": \"#a0a0a7\",\n \"textMuted\": \"#92929b\",\n \"textOnMedia\": \"#ffffff\",\n \"accent\": \"#3e76a8\",\n \"accentHover\": \"#346793\",\n \"accentSoft\": \"rgba(62,118,168,0.12)\",\n \"accentText\": \"#8ebde1\",\n \"accent2\": \"#3e76a8\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3e76a8\",\n \"brandGradientFrom\": \"#121a21\",\n \"brandGradientVia\": \"#0f1114\",\n \"brandGradientTo\": \"#0c0c0e\",\n \"success\": \"#10b77f\",\n \"successText\": \"#36d399\",\n \"successSoft\": \"rgba(16,183,127,0.14)\",\n \"warning\": \"#f59f0a\",\n \"warningText\": \"#f6a823\",\n \"warningSoft\": \"rgba(245,159,10,0.14)\",\n \"danger\": \"#ef4343\",\n \"dangerText\": \"#f87272\",\n \"dangerSoft\": \"rgba(239,67,67,0.14)\",\n \"info\": \"#3e76a8\",\n \"infoText\": \"#8ebde1\",\n \"scrim\": \"rgba(0,0,0,0.6)\",\n \"scrimStrong\": \"rgba(0,0,0,0.8)\",\n \"overlay\": \"rgba(0,0,0,0.7)\",\n \"surfaceSheen\": \"rgba(255,255,255,0.045)\",\n \"fillShade\": \"rgba(0,0,0,0.12)\",\n \"edgeLight\": \"rgba(255,255,255,0.07)\",\n \"edgeOnFill\": \"rgba(255,255,255,0.22)\",\n \"edgeWell\": \"rgba(4,4,7,0.5)\",\n \"glassBg\": \"rgba(29,29,32,0.88)\",\n \"glassBorder\": \"rgba(255,255,255,0.1)\"\n}), light: /* @__PURE__ */ Object.freeze({\n \"surface0\": \"#fafafa\",\n \"surface1\": \"#ffffff\",\n \"surface2\": \"#f1f1f3\",\n \"surface3\": \"#e4e4e7\",\n \"media\": \"#e9e9ec\",\n \"borderSubtle\": \"rgba(0,0,0,0.06)\",\n \"borderDefault\": \"rgba(0,0,0,0.1)\",\n \"borderStrong\": \"rgba(0,0,0,0.16)\",\n \"textPrimary\": \"#18181b\",\n \"textSecondary\": \"#56565d\",\n \"textMuted\": \"#64646d\",\n \"textOnMedia\": \"#ffffff\",\n \"accent\": \"#2a5f8d\",\n \"accentHover\": \"#1f4d75\",\n \"accentSoft\": \"rgba(42,95,141,0.08)\",\n \"accentText\": \"#275782\",\n \"accent2\": \"#2a5f8d\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#2a5f8d\",\n \"brandGradientFrom\": \"#e7eef4\",\n \"brandGradientVia\": \"#f5f7fa\",\n \"brandGradientTo\": \"#fafafa\",\n \"success\": \"#10b77f\",\n \"successText\": \"#0a714e\",\n \"successSoft\": \"rgba(16,183,127,0.1)\",\n \"warning\": \"#f59f0a\",\n \"warningText\": \"#955104\",\n \"warningSoft\": \"rgba(245,159,10,0.12)\",\n \"danger\": \"#ef4343\",\n \"dangerText\": \"#af1d1d\",\n \"dangerSoft\": \"rgba(239,67,67,0.1)\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"scrim\": \"rgba(0,0,0,0.6)\",\n \"scrimStrong\": \"rgba(0,0,0,0.8)\",\n \"overlay\": \"rgba(0,0,0,0.7)\",\n \"surfaceSheen\": \"rgba(255,255,255,0.7)\",\n \"fillShade\": \"rgba(0,0,0,0.12)\",\n \"edgeLight\": \"rgba(255,255,255,0.9)\",\n \"edgeOnFill\": \"rgba(255,255,255,0.22)\",\n \"edgeWell\": \"rgba(48,52,80,0.08)\",\n \"glassBg\": \"rgba(255,255,255,0.88)\",\n \"glassBorder\": \"rgba(0,0,0,0.08)\"\n}) });\n\n/** Corner radii, in dp. */\nexport const radius = /* @__PURE__ */ Object.freeze({\n \"xs\": 6,\n \"sm\": 8,\n \"md\": 12,\n \"lg\": 16,\n \"xl\": 20,\n \"2xl\": 28,\n \"full\": 9999\n});\n\n/** Font families: the first family of each web stack (React Native takes a single name). */\nexport const typography = /* @__PURE__ */ Object.freeze({\n \"sans\": \"Inter\",\n \"text\": \"Inter\",\n \"display\": \"Plus Jakarta Sans\",\n \"mono\": \"JetBrains Mono\"\n});\n\n/**\n * Motion: the cubic-bezier control points (`Easing.bezier(...motion.ease)`), the short spring of what answers the hand\n * (`motion.spring`) and the micro-interaction durations in ms (`motion.duration.fast`) — 0 when the system reduces motion.\n */\nexport const motion = /* @__PURE__ */ Object.freeze({ ease: /* @__PURE__ */ Object.freeze([0.16,1,0.3,1]), spring: /* @__PURE__ */ Object.freeze([0.34,1.36,0.64,1]), duration: /* @__PURE__ */ Object.freeze({\"instant\":90,\"fast\":150,\"base\":200}) });\n\n/**\n * The type scale in dp: fontSize, lineHeight and letterSpacing absolute (React Native has no em), fontWeight as a string,\n * and the role whose family sets it — with @krizaka/fonts: `{ ...typeScale.bodyMd, fontFamily: fontFamily(role, weight) }`.\n */\nexport const typeScale = /* @__PURE__ */ Object.freeze({\n \"display2xl\": {\n \"role\": \"display\",\n \"fontSize\": 72,\n \"lineHeight\": 73,\n \"fontWeight\": \"800\",\n \"letterSpacing\": -2.52\n },\n \"displayXl\": {\n \"role\": \"display\",\n \"fontSize\": 56,\n \"lineHeight\": 59,\n \"fontWeight\": \"800\",\n \"letterSpacing\": -1.68\n },\n \"displayLg\": {\n \"role\": \"display\",\n \"fontSize\": 44,\n \"lineHeight\": 48,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -1.1\n },\n \"displayMd\": {\n \"role\": \"display\",\n \"fontSize\": 34,\n \"lineHeight\": 39,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.68\n },\n \"displaySm\": {\n \"role\": \"display\",\n \"fontSize\": 26,\n \"lineHeight\": 31,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.39\n },\n \"titleLg\": {\n \"role\": \"display\",\n \"fontSize\": 20,\n \"lineHeight\": 27,\n \"fontWeight\": \"700\",\n \"letterSpacing\": -0.2\n },\n \"titleMd\": {\n \"role\": \"text\",\n \"fontSize\": 17,\n \"lineHeight\": 24,\n \"fontWeight\": \"600\",\n \"letterSpacing\": -0.2\n },\n \"bodyLg\": {\n \"role\": \"text\",\n \"fontSize\": 18,\n \"lineHeight\": 29,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.25\n },\n \"bodyMd\": {\n \"role\": \"text\",\n \"fontSize\": 16,\n \"lineHeight\": 26,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.18\n },\n \"bodySm\": {\n \"role\": \"text\",\n \"fontSize\": 14,\n \"lineHeight\": 21,\n \"fontWeight\": \"400\",\n \"letterSpacing\": -0.08\n },\n \"caption\": {\n \"role\": \"text\",\n \"fontSize\": 12,\n \"lineHeight\": 17,\n \"fontWeight\": \"500\",\n \"letterSpacing\": 0\n },\n \"overline\": {\n \"role\": \"mono\",\n \"fontSize\": 11,\n \"lineHeight\": 14,\n \"fontWeight\": \"600\",\n \"letterSpacing\": 1.32\n },\n \"monoMd\": {\n \"role\": \"mono\",\n \"fontSize\": 14,\n \"lineHeight\": 22,\n \"fontWeight\": \"400\",\n \"letterSpacing\": 0\n },\n \"monoSm\": {\n \"role\": \"mono\",\n \"fontSize\": 12,\n \"lineHeight\": 18,\n \"fontWeight\": \"400\",\n \"letterSpacing\": 0\n }\n});\n\n/**\n * The shadows per theme as React Native `boxShadow` strings (RN ≥ 0.76): `raised` (a button, a card), `floating`\n * (a menu, a popover), `overlay` (a sheet, a toast) — ink-tinted, several layers, as on the web.\n */\nexport const shadows = /* @__PURE__ */ Object.freeze({ dark: /* @__PURE__ */ Object.freeze({\n \"sm\": \"0 1px 3px 0 rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.06)\",\n \"md\": \"0 4px 12px 0 rgba(0,0,0,0.15), 0 2px 4px 0 rgba(0,0,0,0.08)\",\n \"lg\": \"0 8px 24px 0 rgba(0,0,0,0.18), 0 4px 8px 0 rgba(0,0,0,0.08)\",\n \"raised\": \"0 1px 2px 0 rgba(4,4,7,0.45), 0 2px 6px -2px rgba(4,4,7,0.3)\",\n \"floating\": \"0 2px 4px 0 rgba(4,4,7,0.35), 0 12px 24px -6px rgba(4,4,7,0.45), 0 24px 48px -12px rgba(4,4,7,0.35)\",\n \"overlay\": \"0 4px 8px 0 rgba(4,4,7,0.4), 0 24px 48px -12px rgba(4,4,7,0.5), 0 48px 96px -24px rgba(4,4,7,0.45)\"\n}), light: /* @__PURE__ */ Object.freeze({\n \"sm\": \"0 1px 3px 0 rgba(0,0,0,0.06), 0 1px 2px 0 rgba(0,0,0,0.04)\",\n \"md\": \"0 4px 12px 0 rgba(0,0,0,0.08), 0 2px 4px 0 rgba(0,0,0,0.04)\",\n \"lg\": \"0 8px 24px 0 rgba(0,0,0,0.1), 0 4px 8px 0 rgba(0,0,0,0.05)\",\n \"raised\": \"0 1px 2px 0 rgba(48,52,80,0.08), 0 2px 6px -2px rgba(48,52,80,0.08)\",\n \"floating\": \"0 2px 4px 0 rgba(48,52,80,0.06), 0 12px 24px -6px rgba(48,52,80,0.1), 0 24px 48px -12px rgba(48,52,80,0.1)\",\n \"overlay\": \"0 4px 8px 0 rgba(48,52,80,0.08), 0 24px 48px -12px rgba(48,52,80,0.14), 0 48px 96px -24px rgba(48,52,80,0.14)\"\n}) });\n\n/** The griffe (BRAND.md §6.1): the size of the 45° cut corner of a control, in dp. */\nexport const cut = /* @__PURE__ */ Object.freeze({\n \"size\": 10\n});\n\n/** The brand themes as ThemeProvider overrides (`overrides={brands.orazaka}`): the accent family per theme. */\nexport const brands = /* @__PURE__ */ Object.freeze({\n \"krizaka\": {\n \"dark\": {\n \"accent\": \"#3e76a8\",\n \"accentHover\": \"#346793\",\n \"accentSoft\": \"rgba(62,118,168,0.12)\",\n \"accentText\": \"#8ebde1\",\n \"accent2\": \"#3e76a8\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3e76a8\",\n \"info\": \"#3e76a8\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#121a21\",\n \"brandGradientVia\": \"#0f1114\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#2a5f8d\",\n \"accentHover\": \"#1f4d75\",\n \"accentSoft\": \"rgba(42,95,141,0.08)\",\n \"accentText\": \"#275782\",\n \"accent2\": \"#2a5f8d\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#2a5f8d\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e7eef4\",\n \"brandGradientVia\": \"#f5f7fa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"orazaka\": {\n \"dark\": {\n \"accent\": \"#f67e23\",\n \"accentHover\": \"#f79245\",\n \"accentSoft\": \"rgba(246,126,35,0.12)\",\n \"accentText\": \"#f78c3b\",\n \"accent2\": \"#f59e0b\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#f67e23\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#28170b\",\n \"brandGradientVia\": \"#17130c\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#b45309\",\n \"accentHover\": \"#964608\",\n \"accentSoft\": \"rgba(180,83,9,0.08)\",\n \"accentText\": \"#9b4808\",\n \"accent2\": \"#a45904\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#b45309\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#ffebd6\",\n \"brandGradientVia\": \"#fdf7ed\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"orochia\": {\n \"dark\": {\n \"accent\": \"#7c3aed\",\n \"accentHover\": \"#6d28d9\",\n \"accentSoft\": \"rgba(139,92,246,0.12)\",\n \"accentText\": \"#a78bfa\",\n \"accent2\": \"#db2777\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#a78bfa\",\n \"info\": \"#7c3aed\",\n \"infoText\": \"#a78bfa\",\n \"brandGradientFrom\": \"#1a0e2a\",\n \"brandGradientVia\": \"#1c0d17\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#7c3aed\",\n \"accentHover\": \"#6d28d9\",\n \"accentSoft\": \"rgba(124,58,237,0.08)\",\n \"accentText\": \"#6d28d9\",\n \"accent2\": \"#db2777\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#7c3aed\",\n \"info\": \"#7c3aed\",\n \"infoText\": \"#6d28d9\",\n \"brandGradientFrom\": \"#efe7fe\",\n \"brandGradientVia\": \"#fdedf5\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yasakia\": {\n \"dark\": {\n \"accent\": \"#24dbc9\",\n \"accentHover\": \"#47e1d2\",\n \"accentSoft\": \"rgba(36,219,201,0.12)\",\n \"accentText\": \"#3ee0cf\",\n \"accent2\": \"#22c378\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#24dbc9\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#0a241e\",\n \"brandGradientVia\": \"#0c1818\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#097169\",\n \"accentHover\": \"#085e57\",\n \"accentSoft\": \"rgba(9,113,105,0.08)\",\n \"accentText\": \"#096d64\",\n \"accent2\": \"#107546\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#097169\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e1faf1\",\n \"brandGradientVia\": \"#e9fbfa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yororis\": {\n \"dark\": {\n \"accent\": \"#98b587\",\n \"accentHover\": \"#adc69f\",\n \"accentSoft\": \"rgba(152,181,135,0.12)\",\n \"accentText\": \"#a7c497\",\n \"accent2\": \"#c3a579\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#98b587\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#1c1812\",\n \"brandGradientVia\": \"#11140f\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#456237\",\n \"accentHover\": \"#39532d\",\n \"accentSoft\": \"rgba(69,98,55,0.08)\",\n \"accentText\": \"#415b33\",\n \"accent2\": \"#745834\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#456237\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#f5efe5\",\n \"brandGradientVia\": \"#f1f6ee\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"yomizaka\": {\n \"dark\": {\n \"accent\": \"#8888f2\",\n \"accentHover\": \"#a2a2f6\",\n \"accentSoft\": \"rgba(136,136,242,0.12)\",\n \"accentText\": \"#9797f7\",\n \"accent2\": \"#8d9aec\",\n \"onAccent\": \"#0c0c0e\",\n \"ring\": \"#8888f2\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#121321\",\n \"brandGradientVia\": \"#111019\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#3c38b2\",\n \"accentHover\": \"#322e9e\",\n \"accentSoft\": \"rgba(60,56,178,0.08)\",\n \"accentText\": \"#3a36ab\",\n \"accent2\": \"#2d3c9f\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#3c38b2\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#ebecfa\",\n \"brandGradientVia\": \"#f0f0fa\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"kachizaka\": {\n \"dark\": {\n \"accent\": \"#fbcf23\",\n \"accentHover\": \"#fddb53\",\n \"accentSoft\": \"rgba(251,207,35,0.12)\",\n \"accentText\": \"#fbd84b\",\n \"accent2\": \"#7695f9\",\n \"onAccent\": \"#0c0e17\",\n \"ring\": \"#fbcf23\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#101628\",\n \"brandGradientVia\": \"#0f111a\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#fac905\",\n \"accentHover\": \"#dcb104\",\n \"accentSoft\": \"rgba(250,193,5,0.14)\",\n \"accentText\": \"#1d3faf\",\n \"accent2\": \"#6285f8\",\n \"onAccent\": \"#0c0e17\",\n \"ring\": \"#1743d3\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e8edfc\",\n \"brandGradientVia\": \"#fcf8e8\",\n \"brandGradientTo\": \"#fafafa\"\n }\n },\n \"akinaza\": {\n \"dark\": {\n \"accent\": \"#65c374\",\n \"accentHover\": \"#7fd28d\",\n \"accentSoft\": \"rgba(101,195,116,0.12)\",\n \"accentText\": \"#7dd48b\",\n \"accent2\": \"#dc946a\",\n \"onAccent\": \"#0a0f0c\",\n \"ring\": \"#65c374\",\n \"info\": \"#5e99c9\",\n \"infoText\": \"#8ebde1\",\n \"brandGradientFrom\": \"#111d15\",\n \"brandGradientVia\": \"#181410\",\n \"brandGradientTo\": \"#0c0c0e\"\n },\n \"light\": {\n \"accent\": \"#21692f\",\n \"accentHover\": \"#195726\",\n \"accentSoft\": \"rgba(33,105,47,0.08)\",\n \"accentText\": \"#20652e\",\n \"accent2\": \"#954923\",\n \"onAccent\": \"#ffffff\",\n \"ring\": \"#21692f\",\n \"info\": \"#2a5f8d\",\n \"infoText\": \"#275782\",\n \"brandGradientFrom\": \"#e9f7eb\",\n \"brandGradientVia\": \"#faf1eb\",\n \"brandGradientTo\": \"#fafafa\"\n }\n }\n});\n/** The marks' emblem gradients: [offset, #rrggbb] stops, the same in both themes. */\nexport const marks = /* @__PURE__ */ Object.freeze({\n \"krizaka\": [\n [\n \"0%\",\n \"#3e76a8\"\n ],\n [\n \"100%\",\n \"#8ebde1\"\n ]\n ],\n \"orazaka\": [\n [\n \"0%\",\n \"#b45309\"\n ],\n [\n \"100%\",\n \"#dc6509\"\n ]\n ],\n \"orochia\": [\n [\n \"0%\",\n \"#7c3aed\"\n ],\n [\n \"55%\",\n \"#d946ef\"\n ],\n [\n \"100%\",\n \"#f472b6\"\n ]\n ],\n \"yasakia\": [\n [\n \"0%\",\n \"#079d61\"\n ],\n [\n \"100%\",\n \"#089b96\"\n ]\n ],\n \"yororis\": [\n [\n \"0%\",\n \"#a4773d\"\n ],\n [\n \"100%\",\n \"#5d894d\"\n ]\n ],\n \"yomizaka\": [\n [\n \"0%\",\n \"#5362c6\"\n ],\n [\n \"100%\",\n \"#6d5ec9\"\n ]\n ],\n \"kachizaka\": [\n [\n \"0%\",\n \"#355fe9\"\n ],\n [\n \"100%\",\n \"#a27807\"\n ]\n ],\n \"akinaza\": [\n [\n \"0%\",\n \"#2d8049\"\n ],\n [\n \"100%\",\n \"#bc632f\"\n ]\n ]\n});\n","// The Krizaka map style, generated from the tokens (no hand-written colour): Protomaps' basemap layers\n// (@protomaps/basemaps) painted with a flavour drawn from `@krizaka/tokens` — the surfaces for the land, the accent\n// softened for the water, the text roles for the labels — in dark or light, the product's accent where a brand asks.\n// Platform-neutral: the web `Map` and the native `Map` both read it.\nimport { brands, themes } from \"@krizaka/tokens/native\";\nimport { type Flavor, layers, namedFlavor } from \"@protomaps/basemaps\";\n\nimport { MAP_ATTRIBUTION, mapAssets } from \"./assets\";\n\n/** A product of the family: its accent paints the map. */\nexport type MapBrand = keyof typeof brands;\n\n/** A colour mixed into another (`#rrggbb` only): `t` = 0 gives `a`, 1 gives `b`. */\nexport function mix(a: string, b: string, t: number): string {\n const p = (h: string) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));\n const [x, y] = [p(a), p(b)];\n return `#${x.map((v, i) => Math.round(v + (y[i]! - v) * t).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\n/** The colours of the map in a scheme (and a brand): what the style, the markers and the static map share. */\nexport function mapPalette(scheme: \"dark\" | \"light\", brand: MapBrand = \"krizaka\") {\n const t = { ...themes[scheme], ...(brands as Record<string, Partial<Record<\"dark\" | \"light\", Partial<typeof themes.dark>>>>)[brand]?.[scheme] };\n const dark = scheme === \"dark\";\n const land = dark ? t.surface1 : t.surface0;\n return {\n land,\n background: dark ? t.surface0 : t.surface2,\n water: mix(land, t.accent, dark ? 0.18 : 0.14),\n park: mix(land, t.success, dark ? 0.1 : 0.12),\n road: dark ? mix(land, t.textPrimary, 0.12) : \"#ffffff\",\n roadMajor: dark ? mix(land, t.textPrimary, 0.2) : mix(land, t.textPrimary, 0.06),\n casing: mix(land, t.textPrimary, dark ? 0.05 : 0.12),\n building: mix(land, t.textPrimary, dark ? 0.06 : 0.07),\n boundary: mix(land, t.textPrimary, 0.3),\n label: t.textSecondary,\n labelStrong: t.textPrimary,\n halo: land,\n accent: t.accent,\n onAccent: t.onAccent,\n };\n}\n\n/** The basemap flavour of a palette: every role of Protomaps' layers filled from the tokens. */\nexport function mapFlavor(scheme: \"dark\" | \"light\", brand?: MapBrand): Flavor {\n const c = mapPalette(scheme, brand);\n const base = namedFlavor(scheme);\n return {\n ...base,\n background: c.background,\n earth: c.land,\n park_a: c.park,\n park_b: c.park,\n wood_a: c.park,\n wood_b: c.park,\n scrub_a: c.park,\n scrub_b: c.park,\n glacier: c.land,\n sand: c.land,\n beach: c.land,\n zoo: c.park,\n hospital: c.land,\n industrial: c.land,\n school: c.land,\n military: c.land,\n aerodrome: c.land,\n pedestrian: c.land,\n runway: c.building,\n water: c.water,\n buildings: c.building,\n pier: c.building,\n boundaries: c.boundary,\n railway: c.casing,\n other: c.road,\n minor_service: c.road,\n minor_a: c.road,\n minor_b: c.road,\n link: c.road,\n major: c.roadMajor,\n highway: c.roadMajor,\n bridges_other: c.road,\n bridges_minor: c.road,\n bridges_link: c.road,\n bridges_major: c.roadMajor,\n bridges_highway: c.roadMajor,\n tunnel_other: c.road,\n tunnel_minor: c.road,\n tunnel_link: c.road,\n tunnel_major: c.roadMajor,\n tunnel_highway: c.roadMajor,\n minor_service_casing: c.casing,\n minor_casing: c.casing,\n link_casing: c.casing,\n major_casing_late: c.casing,\n major_casing_early: c.casing,\n highway_casing_late: c.casing,\n highway_casing_early: c.casing,\n bridges_other_casing: c.casing,\n bridges_minor_casing: c.casing,\n bridges_link_casing: c.casing,\n bridges_major_casing: c.casing,\n bridges_highway_casing: c.casing,\n tunnel_other_casing: c.casing,\n tunnel_minor_casing: c.casing,\n tunnel_link_casing: c.casing,\n tunnel_major_casing: c.casing,\n tunnel_highway_casing: c.casing,\n roads_label_minor: c.label,\n roads_label_minor_halo: c.halo,\n roads_label_major: c.label,\n roads_label_major_halo: c.halo,\n ocean_label: c.label,\n subplace_label: c.label,\n subplace_label_halo: c.halo,\n city_label: c.labelStrong,\n city_label_halo: c.halo,\n state_label: c.label,\n state_label_halo: c.halo,\n country_label: c.label,\n address_label: c.label,\n address_label_halo: c.halo,\n regular: \"Noto Sans Regular\",\n bold: \"Noto Sans Medium\",\n italic: \"Noto Sans Italic\",\n // No point-of-interest icons (no sprite to host): the app's own markers carry the places that matter.\n pois: undefined,\n landcover: undefined,\n };\n}\n\n/** A region of the basemap: its PMTiles file and the zoom up to which it has tiles. */\nexport type MapRegion = { url: string; maxzoom: number };\n\nexport type MapStyleOptions = {\n /** `dark` or `light`: the scheme of the page or the app. */\n scheme: \"dark\" | \"light\";\n /** The product whose accent the map carries (its markers, its water's tint). Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`): the place names in it where OpenStreetMap has them. Default `en`. */\n lang?: string;\n /** The low zooms of the whole world. Default: our world extract (zoom 0–6). */\n world?: MapRegion | null;\n /** The detailed region drawn from zoom 7. Default: Montréal–Québec (up to 14, overzoomed beyond). */\n region?: MapRegion | null;\n /** The glyphs' URL template. Default: our Noto Sans on the CDN. */\n glyphs?: string;\n};\n\n/** The zoom where the detailed region takes over from the world. */\nexport const REGION_FROM_ZOOM = 6.5;\n\n/**\n * The MapLibre style of a Krizaka map (a `StyleSpecification`): the world's low zooms, then the detailed region,\n * painted from the tokens. The `pmtiles://` sources need the PMTiles protocol (the web `Map` registers it; MapLibre\n * Native reads it as it is).\n */\nexport function mapStyle({ scheme, brand, lang = \"en\", world = { url: mapAssets().tiles.world, maxzoom: 6 }, region = { url: mapAssets().tiles.quebec, maxzoom: 14 }, glyphs = mapAssets().glyphs }: MapStyleOptions) {\n const flavor = mapFlavor(scheme, brand);\n const attribution = `<a href=\"${MAP_ATTRIBUTION.protomapsUrl}\">${MAP_ATTRIBUTION.protomaps}</a> · <a href=\"${MAP_ATTRIBUTION.osmUrl}\">${MAP_ATTRIBUTION.osm}</a>`;\n const sources: Record<string, unknown> = {};\n const all: { id: string; minzoom?: number; maxzoom?: number; [k: string]: unknown }[] = [];\n if (world) {\n sources.world = { type: \"vector\", url: `pmtiles://${world.url}`, attribution };\n for (const l of layers(\"world\", flavor, { lang }) as typeof all) {\n if (l.id === \"background\") all.push(l);\n else all.push({ ...l, id: `world-${l.id}`, maxzoom: region ? Math.min(l.maxzoom ?? 24, REGION_FROM_ZOOM) : l.maxzoom });\n }\n }\n if (region) {\n sources.region = { type: \"vector\", url: `pmtiles://${region.url}`, attribution };\n for (const l of layers(\"region\", flavor, { lang }) as typeof all) {\n if (l.id === \"background\") {\n if (!world) all.push(l);\n continue;\n }\n all.push({ ...l, id: `region-${l.id}`, minzoom: world ? Math.max(l.minzoom ?? 0, REGION_FROM_ZOOM) : l.minzoom });\n }\n }\n // No sprite is hosted: a layer that needs an icon (points of interest) is left out — the app's markers carry the places.\n const drawn = all.filter((l) => !(l.layout as Record<string, unknown> | undefined)?.[\"icon-image\"]);\n return { version: 8 as const, glyphs, sources, layers: drawn.map((l) => (l.maxzoom === undefined ? withoutKey(l, \"maxzoom\") : l)) };\n}\n\nconst withoutKey = <T extends Record<string, unknown>>(o: T, key: string) => Object.fromEntries(Object.entries(o).filter(([k]) => k !== key)) as T;\n","// Server-safe. A still map drawn without an engine: the places projected (Web Mercator) on the map's paper — or on a\n// picture of the area (`image`, a snapshot of the real map) — in the page's tokens. What a `Map` shows while its engine\n// loads and for good without WebGL; a picture for an e-mail, a card, a list item. An image (`role=\"img\"`) named by its\n// places.\n\n\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION } from \"./assets\";\nimport { type LngLat, project } from \"./geo\";\n\nexport const staticMap = tv({\n slots: {\n root: \"relative block h-full w-full overflow-hidden bg-surface-2\",\n svg: \"absolute inset-0 h-full w-full\",\n attribution: \"absolute bottom-1.5 right-1.5 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary\",\n },\n});\n\nexport type StaticMapPlace = { position: LngLat; label: string };\n\nexport type StaticMapProps = {\n /** What the picture shows, in words (\"Three pick-up points in the Plateau\"), passed translated: its accessible name. */\n label: string;\n /** The centre of the picture. */\n center: LngLat;\n /** The zoom of the picture. Default 12. */\n zoom?: number;\n /** The places drawn on it (an accent dot each), listed in its name. */\n places?: readonly StaticMapPlace[];\n /** A snapshot of the real map of the area (same centre and zoom), drawn under the places; the attribution then shows. */\n image?: string;\n /** Classes merged on the root (its size). */\n className?: string;\n};\n\nconst W = 640;\nconst H = 400;\n\n/** A still map: the places on the map's paper (or on a snapshot), named in words. */\nexport function StaticMap({ label, center, zoom = 12, places = [], image, className }: StaticMapProps) {\n const s = staticMap();\n const [cx, cy] = project(center, zoom);\n const at = (p: LngLat) => {\n const [x, y] = project(p, zoom);\n return [W / 2 + x - cx, H / 2 + y - cy] as const;\n };\n const name = places.length ? `${label}: ${places.map((p) => p.label).join(\", \")}` : label;\n return (\n <span role=\"img\" aria-label={name} className={s.root({ className })}>\n <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio=\"xMidYMid slice\" className={s.svg()} aria-hidden>\n <defs>\n <pattern id=\"kz-map-paper\" width=\"48\" height=\"48\" patternUnits=\"userSpaceOnUse\">\n <path d=\"M48 0H0V48\" fill=\"none\" stroke=\"var(--kz-border-subtle)\" strokeWidth=\"1\" />\n </pattern>\n </defs>\n <rect width={W} height={H} fill=\"var(--kz-surface-2)\" />\n {image ? <image href={image} x=\"0\" y=\"0\" width={W} height={H} preserveAspectRatio=\"xMidYMid slice\" /> : <rect width={W} height={H} fill=\"url(#kz-map-paper)\" />}\n {places.map((p) => {\n const [x, y] = at(p.position);\n return (\n <g key={`${p.position[0]},${p.position[1]}`}>\n <circle cx={x} cy={y} r=\"12\" fill=\"var(--kz-accent)\" opacity=\"0.18\" />\n <circle cx={x} cy={y} r=\"7\" fill=\"var(--kz-accent)\" stroke=\"var(--kz-on-accent)\" strokeWidth=\"2.5\" />\n </g>\n );\n })}\n </svg>\n {image ? (\n <span aria-hidden className={s.attribution()}>\n {MAP_ATTRIBUTION.protomaps} · {MAP_ATTRIBUTION.osm}\n </span>\n ) : null}\n </span>\n );\n}\n\n"],"mappings":"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","names":["React","React","base","layers","jsx","jsxs","map","jsx","jsxs"]}
package/dist/motion.css CHANGED
@@ -687,6 +687,25 @@ svg[data-kz-icon="ExternalIcon"] {
687
687
  }
688
688
  .kz-flow-detail a { color: var(--kz-accent-text, var(--kz-accent)); font-weight: 600; text-underline-offset: 3px; }
689
689
 
690
+ /* ─── Arrival and growth (the data demos of Krizaka Motion) — CSS only, held by the conductor like every loop ───
691
+ `[data-kz-arrive]` on an element (or on a cell: its table row, `tr:has(…)`, its list item) plays an arrival once when it
692
+ is added: `slide` (default) falls into place, `fade`, `flash` (an accent wash that fades) — chosen on any ancestor by
693
+ `data-kz-arrival`. `--kz-arrive-duration` sets the length. `.kz-grow` around a Sparkline (or any SVG chart) wipes it in from the left over `--kz-grow-duration` with `--kz-grow-ease`. No movement of layout:
694
+ `translate`/`scale`/`opacity` only. Off screen they pause (`[data-offscreen]`), under reduced motion they rest done. */
695
+ [data-kz-arrive], tr:has([data-kz-arrive]), li:has(> [data-kz-arrive]) {
696
+ animation: kz-arrive-slide var(--kz-arrive-duration, 520ms) var(--kz-ease) both;
697
+ }
698
+ [data-kz-arrival="fade"] :is([data-kz-arrive], tr:has([data-kz-arrive]), li:has(> [data-kz-arrive])) { animation-name: kz-arrive-fade; }
699
+ [data-kz-arrival="flash"] :is([data-kz-arrive], tr:has([data-kz-arrive]), li:has(> [data-kz-arrive])) { animation-name: kz-arrive-flash; animation-duration: calc(var(--kz-arrive-duration, 520ms) * 2); }
700
+ @keyframes kz-arrive-slide { from { opacity: 0; translate: 0 -14px; } to { opacity: 1; translate: 0 0; } }
701
+ @keyframes kz-arrive-fade { from { opacity: 0; } to { opacity: 1; } }
702
+ @keyframes kz-arrive-flash { from { background: color-mix(in oklab, var(--kz-accent) 24%, transparent); } to { background: transparent; } }
703
+ .kz-grow svg {
704
+ animation: kz-grow-wipe var(--kz-grow-duration, 900ms) var(--kz-grow-ease, var(--kz-ease)) backwards;
705
+ }
706
+ /* A wipe from the left: the whole chart (line, area, bars) is revealed as one — no per-path trick, so it works on any SVG chart. */
707
+ @keyframes kz-grow-wipe { from { clip-path: inset(-4px 100% -4px -4px); } to { clip-path: inset(-4px -4px -4px -4px); } }
708
+
690
709
  @media (prefers-reduced-motion: reduce) {
691
710
  .kz-page, .kz-word, .kz-gradient-text, .kz-marquee, .kz-backdrop-drift, .kz-overlay, .kz-dialog, .kz-pop, .kz-fade, .kz-progress,
692
711
  .kz-skeleton, .kz-skeleton-shape *, .kz-toast::after,
@@ -703,4 +722,5 @@ svg[data-kz-icon="ExternalIcon"] {
703
722
  .kz-count-digit > span { transition: none; }
704
723
  /* The still frame is a composed picture, not a cut animation: particles rest along their link, labels show. */
705
724
  .kz-iso-item > .kz-iso-lift, .kz-iso-tip { transition: none; }
725
+ [data-kz-arrive], tr:has([data-kz-arrive]), li:has(> [data-kz-arrive]), .kz-grow svg { animation: none !important; }
706
726
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "3.32.0",
3
+ "version": "3.33.1",
4
4
  "description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
5
5
  "keywords": [
6
6
  "krizaka",
@@ -721,7 +721,7 @@
721
721
  ],
722
722
  "peerDependencies": {
723
723
  "@krizaka/query": ">=0.2.1",
724
- "@krizaka/tailwind": ">=3.32.0",
724
+ "@krizaka/tailwind": ">=3.33.1",
725
725
  "@maplibre/maplibre-react-native": ">=11",
726
726
  "@shopify/flash-list": ">=2",
727
727
  "expo-camera": ">=16",
@@ -2464,8 +2464,8 @@
2464
2464
  "radix-ui": "^1.7.0",
2465
2465
  "sonner": "^2.0.8",
2466
2466
  "tailwind-variants": "^3.3.1",
2467
+ "@krizaka/tokens": "^3.33.1",
2467
2468
  "@krizaka/intl": "^0.2.1",
2468
- "@krizaka/tokens": "^3.32.0",
2469
2469
  "@krizaka/pacer": "^0.1.0"
2470
2470
  },
2471
2471
  "scripts": {
@@ -339,7 +339,7 @@
339
339
  "description": "`sm`, `md` (default) and `lg`.",
340
340
  "path": "examples/button/sizes.tsx",
341
341
  "module": "@krizaka/ui/registry/examples/button/sizes",
342
- "code": "import { Button } from \"@krizaka/ui/button\";\n\nexport default function ButtonSizes() {\n return (\n <div className=\"flex items-center gap-3\">\n <Button variant=\"primary\" size=\"sm\">\n Continue\n </Button>\n <Button variant=\"primary\" size=\"md\">\n Continue\n </Button>\n <Button variant=\"primary\" size=\"lg\">\n Continue\n </Button>\n </div>\n );\n}\n"
342
+ "code": "import { Button } from \"@krizaka/ui/button\";\n\nexport default function ButtonSizes() {\n return (\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button variant=\"primary\" size=\"sm\">\n Continue\n </Button>\n <Button variant=\"primary\" size=\"md\">\n Continue\n </Button>\n <Button variant=\"primary\" size=\"lg\">\n Continue\n </Button>\n </div>\n );\n}\n"
343
343
  },
344
344
  {
345
345
  "name": "pill",
@@ -4,7 +4,7 @@
4
4
  import { Button } from "@krizaka/ui/button";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  function ButtonSizes() {
7
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
7
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
8
8
  /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", children: "Continue" }),
9
9
  /* @__PURE__ */ jsx(Button, { variant: "primary", size: "md", children: "Continue" }),
10
10
  /* @__PURE__ */ jsx(Button, { variant: "primary", size: "lg", children: "Continue" })
@@ -2,7 +2,7 @@ import { Button } from "@krizaka/ui/button";
2
2
 
3
3
  export default function ButtonSizes() {
4
4
  return (
5
- <div className="flex items-center gap-3">
5
+ <div className="flex flex-wrap items-center gap-3">
6
6
  <Button variant="primary" size="sm">
7
7
  Continue
8
8
  </Button>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "3.32.0",
3
+ "version": "3.33.1",
4
4
  "templatePurposes": {
5
5
  "convince": {
6
6
  "title": "Convince",
@@ -3017,7 +3017,7 @@
3017
3017
  "native": 2
3018
3018
  },
3019
3019
  "dependencies": [
3020
- "@krizaka/tokens@^3.32.0",
3020
+ "@krizaka/tokens@^3.33.1",
3021
3021
  "@protomaps/basemaps@5.7.2",
3022
3022
  "tailwind-variants@^3.3.1"
3023
3023
  ],
@@ -3456,7 +3456,7 @@
3456
3456
  "native": 0
3457
3457
  },
3458
3458
  "dependencies": [
3459
- "@krizaka/tokens@^3.32.0",
3459
+ "@krizaka/tokens@^3.33.1",
3460
3460
  "radix-ui@^1.7.0",
3461
3461
  "tailwind-variants@^3.3.1"
3462
3462
  ],
@@ -4088,7 +4088,7 @@
4088
4088
  "native": 0
4089
4089
  },
4090
4090
  "dependencies": [
4091
- "@krizaka/tokens@^3.32.0",
4091
+ "@krizaka/tokens@^3.33.1",
4092
4092
  "radix-ui@^1.7.0",
4093
4093
  "tailwind-variants@^3.3.1"
4094
4094
  ],
@@ -4431,7 +4431,7 @@
4431
4431
  "native": 0
4432
4432
  },
4433
4433
  "dependencies": [
4434
- "@krizaka/tokens@^3.32.0",
4434
+ "@krizaka/tokens@^3.33.1",
4435
4435
  "radix-ui@^1.7.0",
4436
4436
  "tailwind-variants@^3.3.1"
4437
4437
  ],
package/registry/map.json CHANGED
@@ -1032,7 +1032,7 @@
1032
1032
  },
1033
1033
  {
1034
1034
  "path": "map/map.tsx",
1035
- "content": "// Client. A map of the Krizaka family on MapLibre GL JS (open source) over Protomaps tiles on our CDN — no API key, no\n// metered service. MapLibre and PMTiles are optional peers, loaded when a map is shown (a dynamic import: a page without\n// a map never pays for them). The style comes from the tokens (`mapStyle`), in the page's scheme, and follows it.\n//\n// What every map does: the canvas is decorative for assistive technologies — the map is a region named by `label`, its\n// places a list read in order (the text alternative, `MapPlaces`), each marker a button when it does something; the\n// attribution of OpenStreetMap (ODbL) and Protomaps is always shown; under reduced motion the camera jumps, never flies;\n// without WebGL (or while the engine loads) `fallback` stands in its place.\n/// <reference types=\"geojson\" />\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { usePrefersReducedMotion } from \"../media\";\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION, setMapAssetsBase } from \"./assets\";\nimport { circlePolygon, type LngLat, pointAlong, sliceLine } from \"./geo\";\nimport { type MapBrand, mapPalette, type MapRegion, mapStyle } from \"./style\";\n\ntype MapLibre = typeof import(\"maplibre-gl\");\ntype MapInstance = import(\"maplibre-gl\").Map;\n\nexport const map = tv({\n slots: {\n root: \"relative isolate overflow-hidden rounded-2xl border border-border-default bg-surface-2 shadow-raised\",\n canvas: \"absolute inset-0 [&_.maplibregl-canvas]:outline-hidden\",\n fallback: \"absolute inset-0\",\n controls: \"absolute right-3 top-3 z-10 flex flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-floating\",\n control:\n \"inline-flex h-10 w-10 items-center justify-center text-lg font-semibold text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring-inset disabled:opacity-40 [&+&]:border-t [&+&]:border-border-subtle\",\n attribution:\n \"absolute bottom-1.5 right-1.5 z-10 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary backdrop-blur-sm [&_a]:underline-offset-2 hover:[&_a]:underline\",\n places: \"sr-only\",\n marker:\n \"group relative flex -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-2 border-on-accent bg-accent shadow-floating outline-hidden \" +\n \"transition-transform hover:scale-110 focus-visible:focus-ring motion-reduce:transition-none motion-reduce:hover:scale-100 data-[selected]:scale-125\",\n markerLabel:\n \"pointer-events-none absolute bottom-full mb-2 whitespace-nowrap rounded-lg border border-border-default bg-surface-1 px-2 py-1 text-xs font-semibold text-fg shadow-floating\",\n },\n});\n\ntype Registry = { add: (id: string, text: string) => void; remove: (id: string) => void };\ntype Ctx = { map: MapInstance | null; lib: MapLibre | null; ready: boolean; reduce: boolean; places: Registry };\nconst MapContext = React.createContext<Ctx | null>(null);\n\n/** The map a child is drawn on (`Marker`, `RadiusCircle`, `Cluster`…): the MapLibre instance once it is ready. */\nexport function useMap(): Ctx {\n const ctx = React.useContext(MapContext);\n if (!ctx) throw new Error(\"useMap: inside a <Map> only\");\n return ctx;\n}\n\n/** The scheme of the page (`html.light`), followed as it changes. */\nfunction usePageScheme(): \"dark\" | \"light\" {\n const read = () => (typeof document !== \"undefined\" && document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\");\n const [scheme, setScheme] = React.useState<\"dark\" | \"light\">(read);\n React.useEffect(() => {\n const observer = new MutationObserver(() => setScheme(read()));\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n return scheme;\n}\n\nlet protocolAdded = false;\nlet workerUrl: string | null = null;\n\n/**\n * Where MapLibre's worker comes from. A bundler (Next.js, Vite) renames MapLibre's module and leaves its worker behind:\n * by default the worker of the very version the app installed is imported from jsDelivr (free, open source, no key)\n * through a module worker of the page's own origin. An app with a strict CSP serves it itself —\n * `configureMaps({ workerUrl: \"/maplibre-gl-worker.mjs\" })` (with `maplibre-gl-shared.mjs` beside it), once, before a map.\n * `assetsBase` reads the tiles and glyphs through a same-origin proxy of the CDN (`setMapAssetsBase`).\n */\nexport function configureMaps(options: { workerUrl?: string; assetsBase?: string }) {\n if (options.workerUrl) workerUrl = options.workerUrl;\n if (options.assetsBase) setMapAssetsBase(options.assetsBase);\n}\n\n/** MapLibre, its worker and the PMTiles protocol, loaded once for the page. */\nasync function loadEngine(): Promise<MapLibre> {\n const [lib, pmtiles] = await Promise.all([import(\"maplibre-gl\"), import(\"pmtiles\")]);\n const ml = ((lib as unknown as { default?: MapLibre }).default ?? lib) as MapLibre;\n if (!protocolAdded) {\n protocolAdded = true;\n const remote = `https://cdn.jsdelivr.net/npm/maplibre-gl@${ml.getVersion()}/dist/maplibre-gl-worker.mjs`;\n ml.setWorkerUrl(workerUrl ?? URL.createObjectURL(new Blob([`import ${JSON.stringify(remote)};`], { type: \"text/javascript\" })));\n ml.addProtocol(\"pmtiles\", new pmtiles.Protocol().tile);\n }\n return ml;\n}\n\nconst hasWebGL = () => {\n try {\n const canvas = document.createElement(\"canvas\");\n return Boolean(canvas.getContext(\"webgl2\") ?? canvas.getContext(\"webgl\"));\n } catch {\n return false;\n }\n};\n\nexport type MapLabels = {\n /** The zoom-in button (\"Zoom in\"). */\n zoomIn: string;\n /** The zoom-out button (\"Zoom out\"). */\n zoomOut: string;\n /** Before the list of places read in place of the map (\"Places on the map\"). */\n places: string;\n};\n\nexport const mapLabels: MapLabels = { zoomIn: \"Zoom in\", zoomOut: \"Zoom out\", places: \"Places on the map\" };\n\nexport type MapProps = {\n /** The map's accessible name (\"Pick-up points near you\"), passed translated. */\n label: string;\n /** Where the map looks at first: longitude, latitude. */\n center: LngLat;\n /** The zoom at first (0 the world, 12 a city, 16 a street). Default 12. */\n zoom?: number;\n /** The camera follows `center` and `zoom` when they change (flies, or jumps under reduced motion). */\n controlled?: boolean;\n /** The scheme. Default: the page's (`html.light`), followed. */\n scheme?: \"dark\" | \"light\";\n /** The product whose accent the map carries. Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`). Default `en`. */\n lang?: string;\n /** The detailed region (PMTiles) drawn from zoom 6.5. Default: our Montréal–Québec extract (to zoom 14). */\n region?: MapRegion | null;\n /** The world's low zooms (PMTiles). Default: our world extract (to zoom 6). */\n world?: MapRegion | null;\n /** The zoom buttons (default) — the keyboard also zooms with + and − on the focused map. */\n controls?: boolean;\n /** Moves with the hand (default); `false` makes a still picture of a place. */\n interactive?: boolean;\n /** Shown in place of the map while its engine loads, and for good without WebGL: a `StaticMap`, a picture. */\n fallback?: React.ReactNode;\n /** Called when the camera rests: its centre and zoom. */\n onMoveEnd?: (view: { center: LngLat; zoom: number }) => void;\n /** Called with the position pressed on the map (not on a marker). */\n onPress?: (position: LngLat) => void;\n /** Markers, circles, clusters, the user's position — and anything drawn over the map. */\n children?: React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<MapLabels>;\n /** Classes merged on the root (its height: `h-80`, `aspect-video`). */\n className?: string;\n};\n\n/** A map: MapLibre over our Protomaps tiles, styled from the tokens, with its places as a list for screen readers. */\nexport function Map({\n label,\n center,\n zoom = 12,\n controlled,\n scheme: schemeProp,\n brand,\n lang,\n region,\n world,\n controls = true,\n interactive = true,\n fallback,\n onMoveEnd,\n onPress,\n children,\n labels: given,\n className,\n}: MapProps) {\n const labels = { ...mapLabels, ...given };\n const s = map();\n const pageScheme = usePageScheme();\n const scheme = schemeProp ?? pageScheme;\n const reduce = usePrefersReducedMotion();\n const host = React.useRef<HTMLDivElement>(null);\n const [instance, setInstance] = React.useState<MapInstance | null>(null);\n const [lib, setLib] = React.useState<MapLibre | null>(null);\n const [ready, setReady] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [places, setPlaces] = React.useState<Record<string, string>>({});\n const registry = React.useMemo<Registry>(\n () => ({\n add: (id, text) => setPlaces((p) => (p[id] === text ? p : { ...p, [id]: text })),\n remove: (id) =>\n setPlaces((p) => {\n const { [id]: _gone, ...rest } = p;\n return rest;\n }),\n }),\n [],\n );\n const latest = React.useRef({ onMoveEnd, onPress });\n React.useEffect(() => {\n latest.current = { onMoveEnd, onPress };\n });\n const styleOf = React.useCallback((sc: \"dark\" | \"light\") => mapStyle({ scheme: sc, brand, lang, ...(region !== undefined ? { region } : {}), ...(world !== undefined ? { world } : {}) }), [brand, lang, region, world]);\n\n // The engine and the map, once (the style follows the scheme below).\n React.useEffect(() => {\n if (!host.current) return;\n let alive = true;\n let created: MapInstance | null = null;\n (hasWebGL() ? loadEngine() : Promise.reject(new Error(\"no WebGL\")))\n .then((ml) => {\n if (!alive || !host.current) return;\n created = new ml.Map({\n container: host.current,\n style: styleOf(scheme) as never,\n center: [...center] as [number, number],\n zoom,\n interactive,\n attributionControl: false,\n fadeDuration: reduce ? 0 : 300,\n cooperativeGestures: false,\n });\n created.getCanvas().setAttribute(\"aria-hidden\", \"true\");\n created.getCanvas().setAttribute(\"tabindex\", \"-1\");\n created.on(\"load\", () => alive && setReady(true));\n // The basemap cannot be read (offline, a blocked request): the fallback comes back rather than an empty map.\n created.on(\"error\", (e) => {\n const source = (e as { sourceId?: string }).sourceId;\n if (alive && (source === \"world\" || source === \"region\")) setFailed(true);\n });\n created.on(\"moveend\", () => {\n const c = created!.getCenter();\n latest.current.onMoveEnd?.({ center: [c.lng, c.lat], zoom: created!.getZoom() });\n });\n created.on(\"click\", (e) => {\n if ((e.originalEvent.target as Element | null)?.closest?.(\"[data-kz-marker]\")) return;\n latest.current.onPress?.([e.lngLat.lng, e.lngLat.lat]);\n });\n setLib(ml);\n setInstance(created);\n })\n .catch(() => alive && setFailed(true));\n return () => {\n alive = false;\n created?.remove();\n };\n // Created once: the props that change afterwards are followed by the effects below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The scheme (and the brand, the language) change the style, not the map.\n const first = React.useRef(true);\n React.useEffect(() => {\n if (!instance) return;\n if (first.current) {\n first.current = false;\n return;\n }\n instance.setStyle(styleOf(scheme) as never);\n }, [instance, scheme, styleOf]);\n\n // A controlled camera: it flies (or jumps) to the new view.\n React.useEffect(() => {\n if (!instance || !controlled) return;\n const view = { center: [...center] as [number, number], zoom };\n if (reduce) instance.jumpTo(view);\n else instance.flyTo({ ...view, speed: 1.4 });\n }, [instance, controlled, center[0], center[1], zoom, reduce]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const zoomBy = (by: number) => instance && (reduce ? instance.setZoom(instance.getZoom() + by) : instance.zoomTo(instance.getZoom() + by, { duration: 250 }));\n const ctx = React.useMemo<Ctx>(() => ({ map: instance, lib, ready, reduce, places: registry }), [instance, lib, ready, reduce, registry]);\n const listed = Object.entries(places);\n return (\n <MapContext.Provider value={ctx}>\n <section\n aria-label={label}\n className={s.root({ className })}\n data-ready={ready ? \"\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget || !instance) return;\n if (e.key === \"+\" || e.key === \"=\") zoomBy(1);\n else if (e.key === \"-\") zoomBy(-1);\n else if (e.key.startsWith(\"Arrow\")) {\n e.preventDefault();\n const step = 80;\n const by: Record<string, [number, number]> = { ArrowUp: [0, -step], ArrowDown: [0, step], ArrowLeft: [-step, 0], ArrowRight: [step, 0] };\n instance.panBy(by[e.key]!, { duration: reduce ? 0 : 200 });\n }\n }}\n >\n {!ready || failed ? <div className={s.fallback()}>{fallback}</div> : null}\n <div ref={host} className={s.canvas()} style={{ position: \"absolute\", inset: 0, visibility: failed ? \"hidden\" : undefined }} />\n {controls && interactive && !failed ? (\n <div className={s.controls()}>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomIn} disabled={!instance} onClick={() => zoomBy(1)}>\n +\n </button>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomOut} disabled={!instance} onClick={() => zoomBy(-1)}>\n −\n </button>\n </div>\n ) : null}\n <p className={s.attribution()}>\n <a href={MAP_ATTRIBUTION.protomapsUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.protomaps}\n </a>\n {\" · \"}\n <a href={MAP_ATTRIBUTION.osmUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.osm}\n </a>\n </p>\n {listed.length ? (\n <div className={s.places()}>\n <p>{labels.places}</p>\n <ul>\n {listed.map(([id, text]) => (\n <li key={id}>{text}</li>\n ))}\n </ul>\n </div>\n ) : null}\n {children}\n </section>\n </MapContext.Provider>\n );\n}\n\nexport type MarkerProps = {\n /** Where it stands. */\n position: LngLat;\n /** Its words: the place's name, read in the list of places and as the button's name (\"Café Olimpico, 350 m\"). */\n label: string;\n /** Pressing it (details in a panel, a sheet): the marker becomes a button. */\n onPress?: () => void;\n /** The chosen one: larger, its label shown above it. */\n selected?: boolean;\n /** Its size in pixels. Default 18. */\n size?: number;\n /** What it draws instead of the accent dot (an avatar, an icon). */\n children?: React.ReactNode;\n /** Drops onto its place once, after this many milliseconds (stagger several: `index * 90`). Still under reduced motion. */\n drop?: number;\n};\n\nconst noSubscription = () => () => undefined;\n\n/** A place on the map: an accent dot (or your content) that is a button when it does something. */\nexport function Marker({ position, label, onPress, selected, size = 18, children, drop }: MarkerProps) {\n const { map: m, lib, places } = useMap();\n const s = map();\n const id = React.useId();\n // The marker's host element exists only in a browser: the server and the hydration pass render nothing for it, then the\n // client swaps in the element — `useSyncExternalStore`'s server snapshot, so hydration never sees a difference.\n const host = React.useRef<HTMLElement | null>(null);\n const el = React.useSyncExternalStore(\n noSubscription,\n () => (host.current ??= document.createElement(\"div\")),\n () => null,\n );\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib || !el) return;\n el.setAttribute(\"data-kz-marker\", \"\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, el, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n if (!el) return null;\n const Tag = onPress ? \"button\" : \"span\";\n return createPortal(\n <Tag\n type={onPress ? \"button\" : undefined}\n aria-label={onPress ? label : undefined}\n aria-hidden={onPress ? undefined : true}\n aria-pressed={onPress && selected !== undefined ? selected : undefined}\n data-selected={selected ? \"\" : undefined}\n data-drop={drop === undefined ? undefined : \"\"}\n className={s.marker()}\n style={{ ...(children ? null : { width: size, height: size }), ...(drop === undefined ? null : ({ \"--kz-map-drop-delay\": `${drop}ms` } as React.CSSProperties)) }}\n onClick={onPress}\n >\n {children}\n {selected ? <span className={s.markerLabel()}>{label}</span> : null}\n </Tag>,\n el,\n );\n}\n\nexport type UserPositionProps = {\n /** Where the person is. */\n position: LngLat;\n /** Its words (\"You are here\"), read in the list of places. */\n label: string;\n /** The accuracy in metres: a soft disc around the dot. */\n accuracy?: number;\n};\n\n/** The person's position: a dot that pulses (still under reduced motion), its accuracy as a soft disc. */\nexport function UserPosition({ position, label, accuracy }: UserPositionProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-user\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return accuracy ? <RadiusCircle center={position} radius={accuracy} tone=\"soft\" /> : null;\n}\n\nexport type PulseProps = {\n /** Where it beats. */\n position: LngLat;\n /** Its words (\"High demand in Mile End\"), read in the list of places. */\n label: string;\n /** The rings' reach in pixels. Default 56. */\n size?: number;\n /** `accent` (a zone that matters) or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A heat pulse: rings that swell and fade around a place (a hot spot, a demand peak). Still rings under reduced motion. */\nexport function Pulse({ position, label, size = 56, tone = \"accent\" }: PulseProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-pulse\";\n el.setAttribute(\"data-tone\", tone);\n el.setAttribute(\"aria-hidden\", \"true\");\n el.style.setProperty(\"--kz-map-pulse-size\", `${size}px`);\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, tone, size, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return null;\n}\n\nexport type RouteProps = {\n /** The path, as positions in order (a polyline: a delivery, a walk, a flight). */\n path: readonly LngLat[];\n /** Its words (\"Delivery from the studio to Mile End, 2.4 km\"), read in the list of places. */\n label: string;\n /** The line draws itself once, from its start, when the map is on screen; `false` shows it whole. Default `true`. */\n draw?: boolean;\n /** How long the drawing lasts, in milliseconds. Default 1400. */\n duration?: number;\n /** A dot that travels the route once drawn (`true`, or your own content: an avatar, an icon). */\n courier?: boolean | React.ReactNode;\n /** How long a trip takes, in milliseconds. Default 3200. */\n tripDuration?: number;\n /** Start the trips again after a pause (milliseconds), only while the map is on screen. Without it the courier makes one trip. */\n repeatAfter?: number;\n /** Changing it replays the drawing and the trip. */\n replayKey?: string | number;\n /** `accent` or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\ntype RouteSource = { setData?: (d: GeoJSON.GeoJSON) => void };\n\n/** A route on the map: a line that draws itself, and a courier that travels it. Under reduced motion the line is whole and the courier rests at the end. */\nexport function Route({ path, label, draw = true, duration = 1400, courier, tripDuration = 3200, repeatAfter, replayKey, tone = \"accent\" }: RouteProps) {\n const { map: m, lib, ready, reduce, places } = useMap();\n const id = `kz-route-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const animated = draw && !reduce;\n // The drawing is done for this `replayKey` (a new key draws again); without animation the line is whole at once.\n const token = replayKey ?? 0;\n const [doneFor, setDoneFor] = React.useState<string | number | null>(null);\n const drawn = !animated || doneFor === token;\n const whole = React.useMemo(() => sliceLine(path, 1), [path]);\n const start = React.useMemo(() => sliceLine(path, 0), [path]);\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n useGeoLayer(id, drawn ? whole : start, (c) => [\n { id: `${id}-casing`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": 9, \"line-opacity\": tone === \"soft\" ? 0.08 : 0.16, \"line-blur\": 3 } },\n { id: `${id}-line`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 2 : 3.5, \"line-opacity\": tone === \"soft\" ? 0.6 : 1 } },\n ]);\n // Is the map on screen? The drawing waits for it, and a repeating trip does not start while it is not.\n const [visible, setVisible] = React.useState(() => typeof IntersectionObserver === \"undefined\");\n React.useEffect(() => {\n const el = m?.getContainer?.();\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(([entry]) => setVisible(!!entry?.isIntersecting), { threshold: 0.25 });\n io.observe(el);\n return () => io.disconnect();\n }, [m]);\n // The drawing: a finite animation, one frame per paint for `duration` ms — no loop, nothing runs afterwards.\n React.useEffect(() => {\n if (!m || !ready || !animated || drawn || !visible) return;\n const source = m.getSource(id) as RouteSource | undefined;\n let frame = 0;\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, duration));\n source?.setData?.(sliceLine(path, 1 - (1 - t) ** 3));\n if (t < 1) frame = requestAnimationFrame(step);\n else setDoneFor(token);\n };\n frame = requestAnimationFrame(step);\n return () => cancelAnimationFrame(frame);\n }, [m, ready, animated, drawn, visible, id, path, duration, token]);\n // The courier: one trip once the line is drawn (finite frames), again after `repeatAfter` while on screen.\n const [trip, setTrip] = React.useState(0);\n React.useEffect(() => {\n if (!courier || !m || !lib || !ready || !drawn) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-courier\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...(path[reduce ? path.length - 1 : 0] ?? [0, 0])] as [number, number]).addTo(m);\n let frame = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (!reduce && visible) {\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, tripDuration));\n const eased = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n marker.setLngLat([...pointAlong(path, eased).position] as [number, number]);\n if (t < 1) frame = requestAnimationFrame(step);\n else if (repeatAfter !== undefined) timer = setTimeout(() => setTrip((n) => n + 1), repeatAfter);\n };\n frame = requestAnimationFrame(step);\n } else if (!reduce) marker.setLngLat([...(path[path.length - 1] ?? [0, 0])] as [number, number]);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(timer);\n marker.remove();\n };\n }, [courier, m, lib, ready, drawn, visible, reduce, path, tripDuration, repeatAfter, trip, token]);\n return null;\n}\n\n/** Adds a GeoJSON source and its layers while the map has a style (again after a change of scheme). */\nfunction useGeoLayer(id: string, data: GeoJSON.GeoJSON, layers: (palette: ReturnType<typeof mapPalette>) => object[], source: object = {}) {\n const { map: m, ready } = useMap();\n const dataRef = React.useRef(data);\n React.useEffect(() => {\n dataRef.current = data;\n const src = m?.getSource(id) as { setData?: (d: GeoJSON.GeoJSON) => void } | undefined;\n src?.setData?.(data);\n }, [m, id, data]);\n React.useEffect(() => {\n if (!m || !ready) return;\n const add = () => {\n if (m.getSource(id)) return;\n const scheme = document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\";\n m.addSource(id, { type: \"geojson\", data: dataRef.current, ...source } as never);\n for (const layer of layers(mapPalette(scheme))) m.addLayer(layer as never);\n };\n add();\n m.on(\"styledata\", add);\n return () => {\n m.off(\"styledata\", add);\n if (!m.getStyle()) return;\n for (const layer of m.getStyle().layers.filter((l) => \"source\" in l && l.source === id)) m.removeLayer(layer.id);\n if (m.getSource(id)) m.removeSource(id);\n };\n }, [m, ready, id]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nexport type RadiusCircleProps = {\n /** Its centre. */\n center: LngLat;\n /** Its radius in metres. */\n radius: number;\n /** `accent` (a zone that matters: a delivery area, a search radius) or `soft` (an accuracy). */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A circle on the ground: a delivery zone, a search radius — filled with the accent, its edge drawn. */\nexport function RadiusCircle({ center, radius, tone = \"accent\" }: RadiusCircleProps) {\n const id = `kz-circle-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo(() => circlePolygon(center, radius), [center[0], center[1], radius]); // eslint-disable-line react-hooks/exhaustive-deps\n useGeoLayer(id, data, (c) => [\n { id: `${id}-fill`, type: \"fill\", source: id, paint: { \"fill-color\": c.accent, \"fill-opacity\": tone === \"soft\" ? 0.1 : 0.14 } },\n { id: `${id}-line`, type: \"line\", source: id, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 1 : 2, \"line-opacity\": tone === \"soft\" ? 0.4 : 0.9, ...(tone === \"accent\" ? { \"line-dasharray\": [2, 1.5] } : {}) } },\n ]);\n return null;\n}\n\nexport type ClusterPoint = { id: string; position: LngLat; label: string };\n\nexport type ClusterProps = {\n /** The points: many of them (creators, pick-up points, events). */\n points: readonly ClusterPoint[];\n /** Pressing a single point. A group zooms in to open itself. */\n onPointPress?: (point: ClusterPoint) => void;\n /** The radius, in pixels, within which points group. Default 48. */\n radius?: number;\n};\n\n/** Many points grouped while they are close: a disc with the count, opened by zooming in; each point is listed for screen readers. */\nexport function Cluster({ points, onPointPress, radius = 48 }: ClusterProps) {\n const { map: m, ready, reduce, places } = useMap();\n const id = `kz-cluster-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo<GeoJSON.FeatureCollection>(\n () => ({ type: \"FeatureCollection\", features: points.map((p) => ({ type: \"Feature\", properties: { id: p.id, label: p.label }, geometry: { type: \"Point\", coordinates: [...p.position] } })) }),\n [points],\n );\n React.useEffect(() => {\n for (const p of points) places.add(`${id}-${p.id}`, p.label);\n return () => points.forEach((p) => places.remove(`${id}-${p.id}`));\n }, [points, places, id]);\n useGeoLayer(\n id,\n data,\n (c) => [\n { id: `${id}-groups`, type: \"circle\", source: id, filter: [\"has\", \"point_count\"], paint: { \"circle-color\": c.accent, \"circle-opacity\": 0.92, \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 16, 10, 20, 50, 26], \"circle-stroke-width\": 3, \"circle-stroke-color\": c.halo } },\n { id: `${id}-count`, type: \"symbol\", source: id, filter: [\"has\", \"point_count\"], layout: { \"text-field\": [\"get\", \"point_count_abbreviated\"], \"text-font\": [\"Noto Sans Medium\"], \"text-size\": 13 }, paint: { \"text-color\": c.onAccent } },\n { id: `${id}-points`, type: \"circle\", source: id, filter: [\"!\", [\"has\", \"point_count\"]], paint: { \"circle-color\": c.accent, \"circle-radius\": 7, \"circle-stroke-width\": 2.5, \"circle-stroke-color\": c.halo } },\n ],\n { cluster: true, clusterRadius: radius, clusterMaxZoom: 15 },\n );\n React.useEffect(() => {\n if (!m || !ready) return;\n const openGroup = async (e: { features?: { properties: Record<string, unknown>; geometry: GeoJSON.Geometry }[] }) => {\n const f = e.features?.[0];\n if (!f) return;\n const src = m.getSource(id) as unknown as { getClusterExpansionZoom: (id: number) => Promise<number> };\n const z = await src.getClusterExpansionZoom(f.properties.cluster_id as number);\n const center = (f.geometry as GeoJSON.Point).coordinates as [number, number];\n if (reduce) m.jumpTo({ center, zoom: z });\n else m.easeTo({ center, zoom: z });\n };\n const pressPoint = (e: { features?: { properties: Record<string, unknown> }[] }) => {\n const pid = e.features?.[0]?.properties.id;\n const point = points.find((p) => p.id === pid);\n if (point) onPointPress?.(point);\n };\n m.on(\"click\", `${id}-groups`, openGroup as never);\n m.on(\"click\", `${id}-points`, pressPoint as never);\n return () => {\n m.off(\"click\", `${id}-groups`, openGroup as never);\n m.off(\"click\", `${id}-points`, pressPoint as never);\n };\n }, [m, ready, id, points, onPointPress, reduce]);\n return null;\n}\n"
1035
+ "content": "// Client. A map of the Krizaka family on MapLibre GL JS (open source) over Protomaps tiles on our CDN — no API key, no\n// metered service. MapLibre and PMTiles are optional peers, loaded when a map is shown (a dynamic import: a page without\n// a map never pays for them). The style comes from the tokens (`mapStyle`), in the page's scheme, and follows it.\n//\n// What every map does: the canvas is decorative for assistive technologies — the map is a region named by `label`, its\n// places a list read in order (the text alternative, `MapPlaces`), each marker a button when it does something; the\n// attribution of OpenStreetMap (ODbL) and Protomaps is always shown; under reduced motion the camera jumps, never flies;\n// without WebGL (or while the engine loads) `fallback` stands in its place.\n/// <reference types=\"geojson\" />\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { usePrefersReducedMotion } from \"../media\";\nimport { tv } from \"../tv\";\nimport { MAP_ATTRIBUTION, setMapAssetsBase } from \"./assets\";\nimport { circlePolygon, type LngLat, pointAlong, sliceLine } from \"./geo\";\nimport { type MapBrand, mapPalette, type MapRegion, mapStyle } from \"./style\";\n\ntype MapLibre = typeof import(\"maplibre-gl\");\ntype MapInstance = import(\"maplibre-gl\").Map;\n\nexport const map = tv({\n slots: {\n root: \"relative isolate overflow-hidden rounded-2xl border border-border-default bg-surface-2 shadow-raised\",\n canvas: \"absolute inset-0 [&_.maplibregl-canvas]:outline-hidden\",\n fallback: \"absolute inset-0\",\n controls: \"absolute right-3 top-3 z-10 flex flex-col overflow-hidden rounded-xl border border-border-default bg-surface-1 shadow-floating\",\n control:\n \"inline-flex h-10 w-10 items-center justify-center text-lg font-semibold text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring-inset disabled:opacity-40 [&+&]:border-t [&+&]:border-border-subtle\",\n attribution:\n \"absolute bottom-1.5 right-1.5 z-10 rounded-md bg-surface-1/85 px-1.5 py-0.5 text-[10px] text-fg-secondary backdrop-blur-sm [&_a]:underline-offset-2 hover:[&_a]:underline\",\n places: \"sr-only\",\n marker:\n \"group relative flex -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-2 border-on-accent bg-accent shadow-floating outline-hidden \" +\n \"transition-transform hover:scale-110 focus-visible:focus-ring motion-reduce:transition-none motion-reduce:hover:scale-100 data-[selected]:scale-125\",\n markerLabel:\n \"pointer-events-none absolute bottom-full mb-2 whitespace-nowrap rounded-lg border border-border-default bg-surface-1 px-2 py-1 text-xs font-semibold text-fg shadow-floating\",\n },\n});\n\ntype Registry = { add: (id: string, text: string) => void; remove: (id: string) => void };\ntype Ctx = { map: MapInstance | null; lib: MapLibre | null; ready: boolean; reduce: boolean; places: Registry };\nconst MapContext = React.createContext<Ctx | null>(null);\n\n/** The map a child is drawn on (`Marker`, `RadiusCircle`, `Cluster`…): the MapLibre instance once it is ready. */\nexport function useMap(): Ctx {\n const ctx = React.useContext(MapContext);\n if (!ctx) throw new Error(\"useMap: inside a <Map> only\");\n return ctx;\n}\n\n/** The scheme of the page (`html.light`), followed as it changes. */\nfunction usePageScheme(): \"dark\" | \"light\" {\n const read = () => (typeof document !== \"undefined\" && document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\");\n const [scheme, setScheme] = React.useState<\"dark\" | \"light\">(read);\n React.useEffect(() => {\n const observer = new MutationObserver(() => setScheme(read()));\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\"] });\n return () => observer.disconnect();\n }, []);\n return scheme;\n}\n\nlet protocolAdded = false;\nlet workerUrl: string | null = null;\n\n/**\n * Where MapLibre's worker comes from. A bundler (Next.js, Vite) renames MapLibre's module and leaves its worker behind:\n * by default the worker of the very version the app installed is imported from jsDelivr (free, open source, no key)\n * through a module worker of the page's own origin. An app with a strict CSP serves it itself —\n * `configureMaps({ workerUrl: \"/maplibre-gl-worker.mjs\" })` (with `maplibre-gl-shared.mjs` beside it), once, before a map.\n * `assetsBase` reads the tiles and glyphs through a same-origin proxy of the CDN (`setMapAssetsBase`).\n */\nexport function configureMaps(options: { workerUrl?: string; assetsBase?: string }) {\n if (options.workerUrl) workerUrl = options.workerUrl;\n if (options.assetsBase) setMapAssetsBase(options.assetsBase);\n}\n\n/** MapLibre, its worker and the PMTiles protocol, loaded once for the page. */\nasync function loadEngine(): Promise<MapLibre> {\n const [lib, pmtiles] = await Promise.all([import(\"maplibre-gl\"), import(\"pmtiles\")]);\n const ml = ((lib as unknown as { default?: MapLibre }).default ?? lib) as MapLibre;\n if (!protocolAdded) {\n protocolAdded = true;\n const remote = `https://cdn.jsdelivr.net/npm/maplibre-gl@${ml.getVersion()}/dist/maplibre-gl-worker.mjs`;\n ml.setWorkerUrl(workerUrl ?? URL.createObjectURL(new Blob([`import ${JSON.stringify(remote)};`], { type: \"text/javascript\" })));\n ml.addProtocol(\"pmtiles\", new pmtiles.Protocol().tile);\n }\n return ml;\n}\n\nconst hasWebGL = () => {\n try {\n const canvas = document.createElement(\"canvas\");\n return Boolean(canvas.getContext(\"webgl2\") ?? canvas.getContext(\"webgl\"));\n } catch {\n return false;\n }\n};\n\nexport type MapLabels = {\n /** The zoom-in button (\"Zoom in\"). */\n zoomIn: string;\n /** The zoom-out button (\"Zoom out\"). */\n zoomOut: string;\n /** Before the list of places read in place of the map (\"Places on the map\"). */\n places: string;\n};\n\nexport const mapLabels: MapLabels = { zoomIn: \"Zoom in\", zoomOut: \"Zoom out\", places: \"Places on the map\" };\n\nexport type MapProps = {\n /** The map's accessible name (\"Pick-up points near you\"), passed translated. */\n label: string;\n /** Where the map looks at first: longitude, latitude. */\n center: LngLat;\n /** The zoom at first (0 the world, 12 a city, 16 a street). Default 12. */\n zoom?: number;\n /** The camera follows `center` and `zoom` when they change (flies, or jumps under reduced motion). */\n controlled?: boolean;\n /** The scheme. Default: the page's (`html.light`), followed. */\n scheme?: \"dark\" | \"light\";\n /** The product whose accent the map carries. Default `krizaka`. */\n brand?: MapBrand;\n /** The language of the labels (`fr`, `en`). Default `en`. */\n lang?: string;\n /** The detailed region (PMTiles) drawn from zoom 6.5. Default: our Montréal–Québec extract (to zoom 14). */\n region?: MapRegion | null;\n /** The world's low zooms (PMTiles). Default: our world extract (to zoom 6). */\n world?: MapRegion | null;\n /** The zoom buttons (default) — the keyboard also zooms with + and − on the focused map. */\n controls?: boolean;\n /** Moves with the hand (default); `false` makes a still picture of a place. */\n interactive?: boolean;\n /** Shown in place of the map while its engine loads, and for good without WebGL: a `StaticMap`, a picture. */\n fallback?: React.ReactNode;\n /** Called when the camera rests: its centre and zoom. */\n onMoveEnd?: (view: { center: LngLat; zoom: number }) => void;\n /** Called with the position pressed on the map (not on a marker). */\n onPress?: (position: LngLat) => void;\n /** Markers, circles, clusters, the user's position — and anything drawn over the map. */\n children?: React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<MapLabels>;\n /** Classes merged on the root (its height: `h-80`, `aspect-video`). */\n className?: string;\n};\n\n/** A map: MapLibre over our Protomaps tiles, styled from the tokens, with its places as a list for screen readers. */\nexport function Map({\n label,\n center,\n zoom = 12,\n controlled,\n scheme: schemeProp,\n brand,\n lang,\n region,\n world,\n controls = true,\n interactive = true,\n fallback,\n onMoveEnd,\n onPress,\n children,\n labels: given,\n className,\n}: MapProps) {\n const labels = { ...mapLabels, ...given };\n const s = map();\n const pageScheme = usePageScheme();\n const scheme = schemeProp ?? pageScheme;\n const reduce = usePrefersReducedMotion();\n const host = React.useRef<HTMLDivElement>(null);\n const [instance, setInstance] = React.useState<MapInstance | null>(null);\n const [lib, setLib] = React.useState<MapLibre | null>(null);\n const [ready, setReady] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [places, setPlaces] = React.useState<Record<string, string>>({});\n const registry = React.useMemo<Registry>(\n () => ({\n add: (id, text) => setPlaces((p) => (p[id] === text ? p : { ...p, [id]: text })),\n remove: (id) =>\n setPlaces((p) => {\n const { [id]: _gone, ...rest } = p;\n return rest;\n }),\n }),\n [],\n );\n const latest = React.useRef({ onMoveEnd, onPress });\n React.useEffect(() => {\n latest.current = { onMoveEnd, onPress };\n });\n const styleOf = React.useCallback((sc: \"dark\" | \"light\") => mapStyle({ scheme: sc, brand, lang, ...(region !== undefined ? { region } : {}), ...(world !== undefined ? { world } : {}) }), [brand, lang, region, world]);\n\n // The style the map wears now: the engine loads asynchronously, so the scheme it is created with is read when it is ready\n // (the page's theme may have flipped since the first render), and the effect below applies whatever differs.\n const wanted = React.useRef({ scheme, styleOf });\n React.useEffect(() => {\n wanted.current = { scheme, styleOf };\n });\n const applied = React.useRef<{ scheme: \"dark\" | \"light\"; styleOf: typeof styleOf } | null>(null);\n\n // The engine and the map, once (the style follows the scheme below).\n React.useEffect(() => {\n if (!host.current) return;\n let alive = true;\n let created: MapInstance | null = null;\n (hasWebGL() ? loadEngine() : Promise.reject(new Error(\"no WebGL\")))\n .then((ml) => {\n if (!alive || !host.current) return;\n applied.current = wanted.current;\n created = new ml.Map({\n container: host.current,\n style: wanted.current.styleOf(wanted.current.scheme) as never,\n center: [...center] as [number, number],\n zoom,\n interactive,\n attributionControl: false,\n fadeDuration: reduce ? 0 : 300,\n cooperativeGestures: false,\n });\n created.getCanvas().setAttribute(\"aria-hidden\", \"true\");\n created.getCanvas().setAttribute(\"tabindex\", \"-1\");\n created.on(\"load\", () => alive && setReady(true));\n // The basemap cannot be read (offline, a blocked request): the fallback comes back rather than an empty map.\n created.on(\"error\", (e) => {\n const source = (e as { sourceId?: string }).sourceId;\n if (alive && (source === \"world\" || source === \"region\")) setFailed(true);\n });\n created.on(\"moveend\", () => {\n const c = created!.getCenter();\n latest.current.onMoveEnd?.({ center: [c.lng, c.lat], zoom: created!.getZoom() });\n });\n created.on(\"click\", (e) => {\n if ((e.originalEvent.target as Element | null)?.closest?.(\"[data-kz-marker]\")) return;\n latest.current.onPress?.([e.lngLat.lng, e.lngLat.lat]);\n });\n setLib(ml);\n setInstance(created);\n })\n .catch(() => alive && setFailed(true));\n return () => {\n alive = false;\n created?.remove();\n };\n // Created once: the props that change afterwards are followed by the effects below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The scheme (and the brand, the language) change the style, not the map.\n React.useEffect(() => {\n if (!instance) return;\n if (applied.current?.scheme === scheme && applied.current.styleOf === styleOf) return;\n applied.current = { scheme, styleOf };\n instance.setStyle(styleOf(scheme) as never);\n }, [instance, scheme, styleOf]);\n\n // A controlled camera: it flies (or jumps) to the new view.\n React.useEffect(() => {\n if (!instance || !controlled) return;\n const view = { center: [...center] as [number, number], zoom };\n if (reduce) instance.jumpTo(view);\n else instance.flyTo({ ...view, speed: 1.4 });\n }, [instance, controlled, center[0], center[1], zoom, reduce]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const zoomBy = (by: number) => instance && (reduce ? instance.setZoom(instance.getZoom() + by) : instance.zoomTo(instance.getZoom() + by, { duration: 250 }));\n const ctx = React.useMemo<Ctx>(() => ({ map: instance, lib, ready, reduce, places: registry }), [instance, lib, ready, reduce, registry]);\n const listed = Object.entries(places);\n return (\n <MapContext.Provider value={ctx}>\n <section\n aria-label={label}\n className={s.root({ className })}\n data-ready={ready ? \"\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget || !instance) return;\n if (e.key === \"+\" || e.key === \"=\") zoomBy(1);\n else if (e.key === \"-\") zoomBy(-1);\n else if (e.key.startsWith(\"Arrow\")) {\n e.preventDefault();\n const step = 80;\n const by: Record<string, [number, number]> = { ArrowUp: [0, -step], ArrowDown: [0, step], ArrowLeft: [-step, 0], ArrowRight: [step, 0] };\n instance.panBy(by[e.key]!, { duration: reduce ? 0 : 200 });\n }\n }}\n >\n {!ready || failed ? <div className={s.fallback()}>{fallback}</div> : null}\n <div ref={host} className={s.canvas()} style={{ position: \"absolute\", inset: 0, visibility: failed ? \"hidden\" : undefined }} />\n {controls && interactive && !failed ? (\n <div className={s.controls()}>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomIn} disabled={!instance} onClick={() => zoomBy(1)}>\n +\n </button>\n <button type=\"button\" className={s.control()} aria-label={labels.zoomOut} disabled={!instance} onClick={() => zoomBy(-1)}>\n −\n </button>\n </div>\n ) : null}\n <p className={s.attribution()}>\n <a href={MAP_ATTRIBUTION.protomapsUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.protomaps}\n </a>\n {\" · \"}\n <a href={MAP_ATTRIBUTION.osmUrl} target=\"_blank\" rel=\"noreferrer\">\n {MAP_ATTRIBUTION.osm}\n </a>\n </p>\n {listed.length ? (\n <div className={s.places()}>\n <p>{labels.places}</p>\n <ul>\n {listed.map(([id, text]) => (\n <li key={id}>{text}</li>\n ))}\n </ul>\n </div>\n ) : null}\n {children}\n </section>\n </MapContext.Provider>\n );\n}\n\nexport type MarkerProps = {\n /** Where it stands. */\n position: LngLat;\n /** Its words: the place's name, read in the list of places and as the button's name (\"Café Olimpico, 350 m\"). */\n label: string;\n /** Pressing it (details in a panel, a sheet): the marker becomes a button. */\n onPress?: () => void;\n /** The chosen one: larger, its label shown above it. */\n selected?: boolean;\n /** Its size in pixels. Default 18. */\n size?: number;\n /** What it draws instead of the accent dot (an avatar, an icon). */\n children?: React.ReactNode;\n /** Drops onto its place once, after this many milliseconds (stagger several: `index * 90`). Still under reduced motion. */\n drop?: number;\n};\n\nconst noSubscription = () => () => undefined;\n\n/** A place on the map: an accent dot (or your content) that is a button when it does something. */\nexport function Marker({ position, label, onPress, selected, size = 18, children, drop }: MarkerProps) {\n const { map: m, lib, places } = useMap();\n const s = map();\n const id = React.useId();\n // The marker's host element exists only in a browser: the server and the hydration pass render nothing for it, then the\n // client swaps in the element — `useSyncExternalStore`'s server snapshot, so hydration never sees a difference.\n const host = React.useRef<HTMLElement | null>(null);\n const el = React.useSyncExternalStore(\n noSubscription,\n () => (host.current ??= document.createElement(\"div\")),\n () => null,\n );\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib || !el) return;\n el.setAttribute(\"data-kz-marker\", \"\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, el, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n if (!el) return null;\n const Tag = onPress ? \"button\" : \"span\";\n return createPortal(\n <Tag\n type={onPress ? \"button\" : undefined}\n aria-label={onPress ? label : undefined}\n aria-hidden={onPress ? undefined : true}\n aria-pressed={onPress && selected !== undefined ? selected : undefined}\n data-selected={selected ? \"\" : undefined}\n data-drop={drop === undefined ? undefined : \"\"}\n className={s.marker()}\n style={{ ...(children ? null : { width: size, height: size }), ...(drop === undefined ? null : ({ \"--kz-map-drop-delay\": `${drop}ms` } as React.CSSProperties)) }}\n onClick={onPress}\n >\n {children}\n {selected ? <span className={s.markerLabel()}>{label}</span> : null}\n </Tag>,\n el,\n );\n}\n\nexport type UserPositionProps = {\n /** Where the person is. */\n position: LngLat;\n /** Its words (\"You are here\"), read in the list of places. */\n label: string;\n /** The accuracy in metres: a soft disc around the dot. */\n accuracy?: number;\n};\n\n/** The person's position: a dot that pulses (still under reduced motion), its accuracy as a soft disc. */\nexport function UserPosition({ position, label, accuracy }: UserPositionProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-user\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return accuracy ? <RadiusCircle center={position} radius={accuracy} tone=\"soft\" /> : null;\n}\n\nexport type PulseProps = {\n /** Where it beats. */\n position: LngLat;\n /** Its words (\"High demand in Mile End\"), read in the list of places. */\n label: string;\n /** The rings' reach in pixels. Default 56. */\n size?: number;\n /** `accent` (a zone that matters) or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A heat pulse: rings that swell and fade around a place (a hot spot, a demand peak). Still rings under reduced motion. */\nexport function Pulse({ position, label, size = 56, tone = \"accent\" }: PulseProps) {\n const { map: m, lib, places } = useMap();\n const id = React.useId();\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n React.useEffect(() => {\n if (!m || !lib) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-pulse\";\n el.setAttribute(\"data-tone\", tone);\n el.setAttribute(\"aria-hidden\", \"true\");\n el.style.setProperty(\"--kz-map-pulse-size\", `${size}px`);\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...position] as [number, number]).addTo(m);\n return () => void marker.remove();\n }, [m, lib, tone, size, position[0], position[1]]); // eslint-disable-line react-hooks/exhaustive-deps\n return null;\n}\n\nexport type RouteProps = {\n /** The path, as positions in order (a polyline: a delivery, a walk, a flight). */\n path: readonly LngLat[];\n /** Its words (\"Delivery from the studio to Mile End, 2.4 km\"), read in the list of places. */\n label: string;\n /** The line draws itself once, from its start, when the map is on screen; `false` shows it whole. Default `true`. */\n draw?: boolean;\n /** How long the drawing lasts, in milliseconds. Default 1400. */\n duration?: number;\n /** A dot that travels the route once drawn (`true`, or your own content: an avatar, an icon). */\n courier?: boolean | React.ReactNode;\n /** How long a trip takes, in milliseconds. Default 3200. */\n tripDuration?: number;\n /** Start the trips again after a pause (milliseconds), only while the map is on screen. Without it the courier makes one trip. */\n repeatAfter?: number;\n /** Changing it replays the drawing and the trip. */\n replayKey?: string | number;\n /** `accent` or `soft`. Default `accent`. */\n tone?: \"accent\" | \"soft\";\n};\n\ntype RouteSource = { setData?: (d: GeoJSON.GeoJSON) => void };\n\n/** A route on the map: a line that draws itself, and a courier that travels it. Under reduced motion the line is whole and the courier rests at the end. */\nexport function Route({ path, label, draw = true, duration = 1400, courier, tripDuration = 3200, repeatAfter, replayKey, tone = \"accent\" }: RouteProps) {\n const { map: m, lib, ready, reduce, places } = useMap();\n const id = `kz-route-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const animated = draw && !reduce;\n // The drawing is done for this `replayKey` (a new key draws again); without animation the line is whole at once.\n const token = replayKey ?? 0;\n const [doneFor, setDoneFor] = React.useState<string | number | null>(null);\n const drawn = !animated || doneFor === token;\n const whole = React.useMemo(() => sliceLine(path, 1), [path]);\n const start = React.useMemo(() => sliceLine(path, 0), [path]);\n React.useEffect(() => {\n places.add(id, label);\n return () => places.remove(id);\n }, [id, label, places]);\n useGeoLayer(id, drawn ? whole : start, (c) => [\n { id: `${id}-casing`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": 9, \"line-opacity\": tone === \"soft\" ? 0.08 : 0.16, \"line-blur\": 3 } },\n { id: `${id}-line`, type: \"line\", source: id, layout: { \"line-cap\": \"round\", \"line-join\": \"round\" }, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 2 : 3.5, \"line-opacity\": tone === \"soft\" ? 0.6 : 1 } },\n ]);\n // Is the map on screen? The drawing waits for it, and a repeating trip does not start while it is not.\n const [visible, setVisible] = React.useState(() => typeof IntersectionObserver === \"undefined\");\n React.useEffect(() => {\n const el = m?.getContainer?.();\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(([entry]) => setVisible(!!entry?.isIntersecting), { threshold: 0.25 });\n io.observe(el);\n return () => io.disconnect();\n }, [m]);\n // The drawing: a finite animation, one frame per paint for `duration` ms — no loop, nothing runs afterwards.\n React.useEffect(() => {\n if (!m || !ready || !animated || drawn || !visible) return;\n const source = m.getSource(id) as RouteSource | undefined;\n let frame = 0;\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, duration));\n source?.setData?.(sliceLine(path, 1 - (1 - t) ** 3));\n if (t < 1) frame = requestAnimationFrame(step);\n else setDoneFor(token);\n };\n frame = requestAnimationFrame(step);\n return () => cancelAnimationFrame(frame);\n }, [m, ready, animated, drawn, visible, id, path, duration, token]);\n // The courier: one trip once the line is drawn (finite frames), again after `repeatAfter` while on screen.\n const [trip, setTrip] = React.useState(0);\n React.useEffect(() => {\n if (!courier || !m || !lib || !ready || !drawn) return;\n const el = document.createElement(\"div\");\n el.className = \"kz-map-courier\";\n el.setAttribute(\"aria-hidden\", \"true\");\n const marker = new lib.Marker({ element: el, anchor: \"center\" }).setLngLat([...(path[reduce ? path.length - 1 : 0] ?? [0, 0])] as [number, number]).addTo(m);\n let frame = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (!reduce && visible) {\n const began = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - began) / Math.max(1, tripDuration));\n const eased = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n marker.setLngLat([...pointAlong(path, eased).position] as [number, number]);\n if (t < 1) frame = requestAnimationFrame(step);\n else if (repeatAfter !== undefined) timer = setTimeout(() => setTrip((n) => n + 1), repeatAfter);\n };\n frame = requestAnimationFrame(step);\n } else if (!reduce) marker.setLngLat([...(path[path.length - 1] ?? [0, 0])] as [number, number]);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(timer);\n marker.remove();\n };\n }, [courier, m, lib, ready, drawn, visible, reduce, path, tripDuration, repeatAfter, trip, token]);\n return null;\n}\n\n/** Adds a GeoJSON source and its layers while the map has a style (again after a change of scheme). */\nfunction useGeoLayer(id: string, data: GeoJSON.GeoJSON, layers: (palette: ReturnType<typeof mapPalette>) => object[], source: object = {}) {\n const { map: m, ready } = useMap();\n const dataRef = React.useRef(data);\n React.useEffect(() => {\n dataRef.current = data;\n const src = m?.getSource(id) as { setData?: (d: GeoJSON.GeoJSON) => void } | undefined;\n src?.setData?.(data);\n }, [m, id, data]);\n React.useEffect(() => {\n if (!m || !ready) return;\n const add = () => {\n if (m.getSource(id)) return;\n const scheme = document.documentElement.classList.contains(\"light\") ? \"light\" : \"dark\";\n m.addSource(id, { type: \"geojson\", data: dataRef.current, ...source } as never);\n for (const layer of layers(mapPalette(scheme))) m.addLayer(layer as never);\n };\n add();\n m.on(\"styledata\", add);\n return () => {\n m.off(\"styledata\", add);\n if (!m.getStyle()) return;\n for (const layer of m.getStyle().layers.filter((l) => \"source\" in l && l.source === id)) m.removeLayer(layer.id);\n if (m.getSource(id)) m.removeSource(id);\n };\n }, [m, ready, id]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nexport type RadiusCircleProps = {\n /** Its centre. */\n center: LngLat;\n /** Its radius in metres. */\n radius: number;\n /** `accent` (a zone that matters: a delivery area, a search radius) or `soft` (an accuracy). */\n tone?: \"accent\" | \"soft\";\n};\n\n/** A circle on the ground: a delivery zone, a search radius — filled with the accent, its edge drawn. */\nexport function RadiusCircle({ center, radius, tone = \"accent\" }: RadiusCircleProps) {\n const id = `kz-circle-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo(() => circlePolygon(center, radius), [center[0], center[1], radius]); // eslint-disable-line react-hooks/exhaustive-deps\n useGeoLayer(id, data, (c) => [\n { id: `${id}-fill`, type: \"fill\", source: id, paint: { \"fill-color\": c.accent, \"fill-opacity\": tone === \"soft\" ? 0.1 : 0.14 } },\n { id: `${id}-line`, type: \"line\", source: id, paint: { \"line-color\": c.accent, \"line-width\": tone === \"soft\" ? 1 : 2, \"line-opacity\": tone === \"soft\" ? 0.4 : 0.9, ...(tone === \"accent\" ? { \"line-dasharray\": [2, 1.5] } : {}) } },\n ]);\n return null;\n}\n\nexport type ClusterPoint = { id: string; position: LngLat; label: string };\n\nexport type ClusterProps = {\n /** The points: many of them (creators, pick-up points, events). */\n points: readonly ClusterPoint[];\n /** Pressing a single point. A group zooms in to open itself. */\n onPointPress?: (point: ClusterPoint) => void;\n /** The radius, in pixels, within which points group. Default 48. */\n radius?: number;\n};\n\n/** Many points grouped while they are close: a disc with the count, opened by zooming in; each point is listed for screen readers. */\nexport function Cluster({ points, onPointPress, radius = 48 }: ClusterProps) {\n const { map: m, ready, reduce, places } = useMap();\n const id = `kz-cluster-${React.useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n const data = React.useMemo<GeoJSON.FeatureCollection>(\n () => ({ type: \"FeatureCollection\", features: points.map((p) => ({ type: \"Feature\", properties: { id: p.id, label: p.label }, geometry: { type: \"Point\", coordinates: [...p.position] } })) }),\n [points],\n );\n React.useEffect(() => {\n for (const p of points) places.add(`${id}-${p.id}`, p.label);\n return () => points.forEach((p) => places.remove(`${id}-${p.id}`));\n }, [points, places, id]);\n useGeoLayer(\n id,\n data,\n (c) => [\n { id: `${id}-groups`, type: \"circle\", source: id, filter: [\"has\", \"point_count\"], paint: { \"circle-color\": c.accent, \"circle-opacity\": 0.92, \"circle-radius\": [\"step\", [\"get\", \"point_count\"], 16, 10, 20, 50, 26], \"circle-stroke-width\": 3, \"circle-stroke-color\": c.halo } },\n { id: `${id}-count`, type: \"symbol\", source: id, filter: [\"has\", \"point_count\"], layout: { \"text-field\": [\"get\", \"point_count_abbreviated\"], \"text-font\": [\"Noto Sans Medium\"], \"text-size\": 13 }, paint: { \"text-color\": c.onAccent } },\n { id: `${id}-points`, type: \"circle\", source: id, filter: [\"!\", [\"has\", \"point_count\"]], paint: { \"circle-color\": c.accent, \"circle-radius\": 7, \"circle-stroke-width\": 2.5, \"circle-stroke-color\": c.halo } },\n ],\n { cluster: true, clusterRadius: radius, clusterMaxZoom: 15 },\n );\n React.useEffect(() => {\n if (!m || !ready) return;\n const openGroup = async (e: { features?: { properties: Record<string, unknown>; geometry: GeoJSON.Geometry }[] }) => {\n const f = e.features?.[0];\n if (!f) return;\n const src = m.getSource(id) as unknown as { getClusterExpansionZoom: (id: number) => Promise<number> };\n const z = await src.getClusterExpansionZoom(f.properties.cluster_id as number);\n const center = (f.geometry as GeoJSON.Point).coordinates as [number, number];\n if (reduce) m.jumpTo({ center, zoom: z });\n else m.easeTo({ center, zoom: z });\n };\n const pressPoint = (e: { features?: { properties: Record<string, unknown> }[] }) => {\n const pid = e.features?.[0]?.properties.id;\n const point = points.find((p) => p.id === pid);\n if (point) onPointPress?.(point);\n };\n m.on(\"click\", `${id}-groups`, openGroup as never);\n m.on(\"click\", `${id}-points`, pressPoint as never);\n return () => {\n m.off(\"click\", `${id}-groups`, openGroup as never);\n m.off(\"click\", `${id}-points`, pressPoint as never);\n };\n }, [m, ready, id, points, onPointPress, reduce]);\n return null;\n}\n"
1036
1036
  },
1037
1037
  {
1038
1038
  "path": "map/meta.ts",
@@ -1060,7 +1060,7 @@
1060
1060
  }
1061
1061
  ],
1062
1062
  "dependencies": [
1063
- "@krizaka/tokens@^3.32.0",
1063
+ "@krizaka/tokens@^3.33.1",
1064
1064
  "@protomaps/basemaps@5.7.2",
1065
1065
  "tailwind-variants@^3.3.1"
1066
1066
  ],
@@ -618,7 +618,7 @@
618
618
  }
619
619
  ],
620
620
  "dependencies": [
621
- "@krizaka/tokens@^3.32.0",
621
+ "@krizaka/tokens@^3.33.1",
622
622
  "radix-ui@^1.7.0",
623
623
  "tailwind-variants@^3.3.1"
624
624
  ],
@@ -342,7 +342,7 @@
342
342
  }
343
343
  ],
344
344
  "dependencies": [
345
- "@krizaka/tokens@^3.32.0",
345
+ "@krizaka/tokens@^3.33.1",
346
346
  "radix-ui@^1.7.0",
347
347
  "tailwind-variants@^3.3.1"
348
348
  ],
@@ -329,7 +329,7 @@
329
329
  }
330
330
  ],
331
331
  "dependencies": [
332
- "@krizaka/tokens@^3.32.0",
332
+ "@krizaka/tokens@^3.33.1",
333
333
  "radix-ui@^1.7.0",
334
334
  "tailwind-variants@^3.3.1"
335
335
  ],