@alfadocs/ui-kit-debug 1.125.2 → 1.125.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"map-view-CO4F0hMV.js","sources":["../../src/components/map-view/map-view.agent.ts","../../src/components/map-view/map-view-pin-geometry.ts","../../src/components/map-view/map-view-pin.tsx","../../src/components/map-view/map-view-cluster.ts","../../src/components/_shared/focus-visible.ts","../../src/components/map-view/map-view.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — MapView. */\n/* */\n/* Read-safety actions on the kit's Google Maps wrapper. State is the */\n/* current centre / zoom / selected marker; actions pan, zoom, and */\n/* select markers programmatically. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { MapViewHandle, MapLatLng } from './map-view';\n\nexport const mapViewAgent: AgentAdapter<MapViewHandle> = {\n id: 'map-view',\n // pan → scroll (viewport move); set_zoom → set_value; select_marker → pick\n // (NON-persisting marker focus, no value state slot).\n capabilities: ['scroll', 'set_value', 'pick'],\n // Every action routes through the imperative bridge, which only exists\n // once the map has loaded: consent granted (the default is\n // `consentGranted={false}`) and Google Maps finished loading. Until then\n // all three actions are silent no-ops. `consentGranted` says whether the\n // gate is passed, and a null `center` after it means the map is still\n // loading. `center` / `zoom` are read off the live\n // map because Google clamps zoom and animates pans, so requested is not\n // applied; `selectedMarkerId` is the only confirmation the host accepted\n // a `select_marker` proposal.\n //\n // A9: marker ids only (`MapMarker.id` is a stable opaque id). Never\n // `MapMarker.label`, never the `address` prop (consumer-supplied free\n // text, PII in any patient-address deployment), never `apiKey`.\n state: {\n consentGranted: {\n type: 'boolean',\n descriptionKey: 'ui.agent.mapView.state.consentGranted',\n description:\n 'True when the GDPR consent gate has been passed. Read this FIRST: while it is false every action is a silent no-op against a placeholder, and nothing else in the surface says so. When it is true but `center` is still null, Google Maps is loading; wait for `center` before acting.',\n read: (handle) => handle.isConsentGranted(),\n },\n markerCount: {\n type: 'number',\n descriptionKey: 'ui.agent.mapView.state.markerCount',\n description:\n 'Number of markers the consumer passed. Zero means there is nothing for `select_marker` to select. A count only — no ids, labels or positions.',\n read: (handle) => handle.getMarkerCount(),\n },\n selectedMarkerId: {\n type: 'string | null',\n descriptionKey: 'ui.agent.mapView.state.selectedMarkerId',\n description:\n 'Id of the marker the host currently holds as selected, or null. Selection is fully controlled by the consumer, so `select_marker` only proposes — this slot is the only way to see whether the proposal was accepted.',\n read: (handle) => handle.getSelectedMarkerId(),\n },\n center: {\n type: '{ lat: number; lng: number } | null',\n descriptionKey: 'ui.agent.mapView.state.center',\n description:\n 'Live map centre in lat/lng, or null until the map has loaded (consent not granted, or Google Maps still loading). The read-back for `pan_to`, and the sign that the map is ready.',\n read: (handle) => handle.getCenter(),\n },\n zoom: {\n type: 'number | null',\n descriptionKey: 'ui.agent.mapView.state.zoom',\n description:\n \"Live zoom level, or null until the map has loaded. Google clamps zoom to the map type's range, so this is not necessarily the value last passed to `set_zoom`.\",\n read: (handle) => handle.getZoom(),\n },\n },\n actions: {\n pan_to: {\n // Ephemeral viewport move, no data loss → read.\n safety: 'read',\n argsType: '{ position: { lat: number; lng: number } }',\n argsSchema: {\n type: 'object',\n properties: {\n position: {\n type: 'object',\n properties: {\n lat: { type: 'number', description: 'Latitude in degrees.' },\n lng: { type: 'number', description: 'Longitude in degrees.' },\n },\n required: ['lat', 'lng'],\n },\n },\n required: ['position'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.panTo',\n description:\n 'Pan the map to a new centre without changing zoom. Read the `center` slot back to confirm — Google animates the pan, so the applied centre settles after the call.',\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n ],\n invoke: (handle, args: { position: MapLatLng }) => {\n handle.panTo(args.position);\n },\n },\n set_zoom: {\n // Ephemeral viewport zoom, no data loss → read.\n safety: 'read',\n argsType: '{ zoom: number }',\n argsSchema: {\n type: 'object',\n properties: {\n zoom: {\n type: 'number',\n description: 'Zoom level, typical range 1–20.',\n },\n },\n required: ['zoom'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.setZoom',\n description:\n 'Set the map zoom level (typical range 1–20). Read the `zoom` slot back — Google clamps the request to the map type’s supported range, so requested and applied can differ.',\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n \"Requested zoom is clamped to the map type's range; `zoom` reports what was actually applied.\",\n ],\n invoke: (handle, args: { zoom: number }) => {\n handle.setZoom(args.zoom);\n },\n },\n select_marker: {\n // Ephemeral marker focus, no data loss → read.\n safety: 'read',\n argsType: '{ id: string | null }',\n argsSchema: {\n type: 'object',\n properties: {\n id: {\n type: ['string', 'null'],\n description: 'Marker id to select, or null to clear the selection.',\n },\n },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.selectMarker',\n description:\n \"Propose a marker selection by id, or pass null to clear it. Fires the host's `onMarkerClick`; selection is controlled by the host, so read the `selectedMarkerId` slot back to see whether it accepted.\",\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n 'The host owns `selectedMarkerId` and may ignore the proposal; `selectedMarkerId` is unchanged when it does.',\n ],\n invoke: (handle, args: { id: string | null }) => {\n handle.selectMarker(args.id);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'map-view',\n description: 'Marks the element as a kit MapView.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","/**\n * The geometry of the MapView pin, shared by the live pin (map-view-pin.tsx)\n * and the static PNG for Google Static Maps (scripts/build-map-pin-png.mjs),\n * so the two cannot drift. Plain data and strings only, no imports: Node runs\n * it directly.\n */\n\n/** CSS pixel size of the live pin. The tip is at the bottom centre. */\nexport const MAP_PIN_WIDTH = 32;\nexport const MAP_PIN_HEIGHT = 40;\n\n/**\n * Teardrop: round head (centre 16,16, radius 14), point at 16,38. The white ring\n * is 2 wide on this line and the ink edge 1 more outside it, so the drawing\n * fills the 32 x 40 box exactly.\n */\nexport const MAP_PIN_BODY_PATH =\n 'M16 38C16 38 2 26 2 16A14 14 0 0 1 30 16C30 26 16 38 16 38Z';\n\n/** White ring width, in the same units as the viewBox. */\nexport const MAP_PIN_RING_WIDTH = 2;\n\n/**\n * The ink edge is drawn under the ring, centred on the same line: 4 wide, so\n * 1 shows outside the ring. It carries the 3:1 on light tiles; the ring\n * carries it on dark ones.\n */\nexport const MAP_PIN_EDGE_WIDTH = 4;\n\n/**\n * The four-pillar Alfadocs mark (reference/assets/logos/SVG/Alfadocs_Logo_Mark.svg,\n * viewBox 148) at 15.5 units: about 55% of the 28-wide head, centred on it.\n */\nexport const MAP_PIN_MARK_TRANSFORM = 'translate(8.25 8.25) scale(0.10473)';\n\nexport interface MapPinMarkShape {\n tag: 'rect' | 'path';\n attrs: Record<string, string>;\n}\n\nexport const MAP_PIN_MARK_SHAPES: MapPinMarkShape[] = [\n {\n tag: 'rect',\n attrs: {\n x: '53.04',\n y: '2.54',\n width: '33.99',\n height: '49.34',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-4.66 19.05) rotate(-15)',\n },\n },\n {\n tag: 'rect',\n attrs: {\n x: '96.12',\n y: '53.04',\n width: '49.34',\n height: '33.99',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-14.01 33.65) rotate(-15)',\n },\n },\n {\n tag: 'path',\n attrs: {\n d: 'M47.82,59.56c-.65-2.43-3.15-3.87-5.58-3.22L3.38,66.75c-2.43.65-3.87,3.15-3.22,5.58l6.44,24.03c.65,2.43,3.15,3.87,5.58,3.22l38.86-10.41c2.43-.65,3.87-3.15,3.22-5.58l-6.44-24.03Z',\n },\n },\n {\n tag: 'rect',\n attrs: {\n x: '60.97',\n y: '96.12',\n width: '33.99',\n height: '49.34',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-28.61 24.29) rotate(-15)',\n },\n },\n];\n\nconst attrString = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ');\n\n/** The pin as a standalone SVG string, in concrete colours (no CSS variables). */\nexport function mapPinSvg(\n fill: string,\n ring: string,\n ink: string,\n size: { width: number; height: number } = {\n width: MAP_PIN_WIDTH,\n height: MAP_PIN_HEIGHT,\n },\n): string {\n const mark = MAP_PIN_MARK_SHAPES.map(\n ({ tag, attrs }) => `<${tag} ${attrString(attrs)}/>`,\n ).join('');\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size.width}\" height=\"${size.height}\" ` +\n `viewBox=\"0 0 ${MAP_PIN_WIDTH} ${MAP_PIN_HEIGHT}\">` +\n `<path d=\"${MAP_PIN_BODY_PATH}\" fill=\"none\" stroke=\"${ink}\" ` +\n `stroke-width=\"${MAP_PIN_EDGE_WIDTH}\" stroke-linejoin=\"round\"/>` +\n `<path d=\"${MAP_PIN_BODY_PATH}\" fill=\"${fill}\" stroke=\"${ring}\" ` +\n `stroke-width=\"${MAP_PIN_RING_WIDTH}\" stroke-linejoin=\"round\"/>` +\n `<g fill=\"${ring}\" transform=\"${MAP_PIN_MARK_TRANSFORM}\">${mark}</g></svg>`\n );\n}\n","import type { ReactNode } from 'react';\nimport {\n MAP_PIN_BODY_PATH,\n MAP_PIN_EDGE_WIDTH,\n MAP_PIN_HEIGHT,\n MAP_PIN_MARK_SHAPES,\n MAP_PIN_MARK_TRANSFORM,\n MAP_PIN_RING_WIDTH,\n MAP_PIN_WIDTH,\n} from './map-view-pin-geometry';\n\n/** Hovered pin scale. The pin is resized, not transformed, so its box, hit area and focus ring grow with it. */\nexport const MAP_PIN_HOVER_SCALE = 1.4;\n\n/*\n * Google's marker element takes the focus; the ring is drawn on the pin and the\n * element's own outline is removed (KitAdvancedMarker). Deep ink, not `--ring`,\n * which is light in the dark themes: the tiles do not follow the kit theme.\n * `--focus-ring-width` is thicker in the accessible themes, `CanvasText` in\n * forced colours. Same ring as the cluster badge.\n */\nconst FOCUS_RING = [\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[length:var(--focus-ring-width)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-solid',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[color:var(--map-marker-hover-color)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:forced-colors:outline-[CanvasText]',\n].join(' ');\n\n/**\n * The MapView pin: a teardrop in the marker colour with a white ring, a thin\n * ink edge outside it and the four-pillar Alfadocs mark. Content of an\n * AdvancedMarker, which anchors it at the bottom centre, so the tip marks the\n * place. It sits at the bottom centre of a transparent box at least\n * `--min-target-size` square, the kit's target size. Decorative: the marker\n * carries the accessible name. The geometry is shared with the static PNG.\n */\nexport function MapViewPin({ hovered }: { hovered: boolean }): ReactNode {\n const scale = hovered ? MAP_PIN_HOVER_SCALE : 1;\n const body = hovered\n ? 'ds:fill-[var(--map-marker-selected-color)]'\n : 'ds:fill-[var(--map-marker-color)]';\n return (\n <span\n data-component=\"map-view-pin\"\n className=\"ds:flex ds:items-end ds:justify-center ds:[min-inline-size:var(--min-target-size)] ds:[min-block-size:var(--min-target-size)]\"\n >\n <svg\n aria-hidden=\"true\"\n focusable=\"false\"\n data-map-pin={hovered ? 'hovered' : 'normal'}\n width={MAP_PIN_WIDTH * scale}\n height={MAP_PIN_HEIGHT * scale}\n viewBox={`0 0 ${MAP_PIN_WIDTH} ${MAP_PIN_HEIGHT}`}\n className={`ds:block ds:fill-[var(--map-marker-hover-ring)] ds:forced-colors:fill-[Canvas] ${FOCUS_RING}`}\n >\n <path\n d={MAP_PIN_BODY_PATH}\n fill=\"none\"\n strokeWidth={MAP_PIN_EDGE_WIDTH}\n strokeLinejoin=\"round\"\n className=\"ds:stroke-[color:var(--map-marker-hover-color)] ds:forced-colors:stroke-[ButtonText]\"\n />\n <path\n d={MAP_PIN_BODY_PATH}\n strokeWidth={MAP_PIN_RING_WIDTH}\n strokeLinejoin=\"round\"\n className={`${body} ds:stroke-[color:var(--map-marker-hover-ring)] ds:forced-colors:fill-[ButtonText] ds:forced-colors:stroke-[Canvas]`}\n />\n <g transform={MAP_PIN_MARK_TRANSFORM}>\n {MAP_PIN_MARK_SHAPES.map(({ tag, attrs }, index) =>\n tag === 'path' ? (\n <path key={index} d={attrs.d} />\n ) : (\n <rect\n key={index}\n x={attrs.x}\n y={attrs.y}\n width={attrs.width}\n height={attrs.height}\n rx={attrs.rx}\n ry={attrs.ry}\n transform={attrs.transform}\n />\n ),\n )}\n </g>\n </svg>\n </span>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* MapView marker clustering: the pure part. */\n/* */\n/* A small greedy grid clusterer in Web Mercator pixels. Nothing here */\n/* touches Google or the DOM, so it is cheap to test and cannot cause */\n/* a request. Pixels are world pixels at the given zoom, not screen */\n/* pixels, so panning never changes a cluster: only zooming does. */\n/* ------------------------------------------------------------------ */\n\nexport interface ClusterInput {\n id: string;\n position: { lat: number; lng: number };\n}\n\nexport interface MarkerCluster<T extends ClusterInput> {\n /** Id of the first member: unique across clusters, so safe as a React key. */\n id: string;\n /** Average position of the members. */\n position: { lat: number; lng: number };\n members: T[];\n}\n\nexport interface ClusterResult<T extends ClusterInput> {\n /** Markers that stay on their own pin. */\n singles: T[];\n /** Groups of two or more. */\n clusters: MarkerCluster<T>[];\n}\n\n/** More markers than this and `clusterMarkers=\"auto\"` clusters. */\nexport const CLUSTER_AUTO_THRESHOLD = 30;\n/** Markers closer than this on screen, in px, share a cluster. */\nexport const CLUSTER_RADIUS_PX = 56;\n/**\n * From this zoom on nothing clusters: street level, where two pins that are\n * still together are in the same building and a cluster could never split.\n */\nexport const CLUSTER_OFF_ZOOM = 17;\n\n/** Google's world is 256px wide at zoom 0. */\nconst WORLD_PX = 256;\n\nconst toRadians = (degrees: number): number => (degrees * Math.PI) / 180;\n\nfunction project(\n lat: number,\n lng: number,\n zoom: number,\n): { x: number; y: number } {\n const world = WORLD_PX * 2 ** zoom;\n const sin = Math.min(Math.max(Math.sin(toRadians(lat)), -0.9999), 0.9999);\n const mercator = Math.log((1 + sin) / (1 - sin)) / 2;\n return {\n x: ((lng + 180) / 360) * world,\n y: (0.5 - mercator / (2 * Math.PI)) * world,\n };\n}\n\n/** Whether `clusterMarkers` asks for clustering of this many markers. */\nexport function shouldCluster(\n setting: boolean | 'auto',\n markerCount: number,\n): boolean {\n if (setting === 'auto') return markerCount > CLUSTER_AUTO_THRESHOLD;\n return setting && markerCount > 1;\n}\n\ninterface Bucket<T extends ClusterInput> {\n x: number;\n y: number;\n members: T[];\n lat: number;\n lng: number;\n}\n\n/**\n * Groups `markers` that fall within `radiusPx` of each other at `zoom`.\n * Greedy and order-independent (the points are sorted first, so re-sorting\n * the results never reshuffles the clusters). At or above\n * `CLUSTER_OFF_ZOOM` every marker is a single.\n */\nexport function clusterMarkerSet<T extends ClusterInput>(\n markers: readonly T[],\n zoom: number,\n radiusPx: number = CLUSTER_RADIUS_PX,\n): ClusterResult<T> {\n if (zoom >= CLUSTER_OFF_ZOOM || markers.length < 2) {\n return { singles: [...markers], clusters: [] };\n }\n const projected = markers\n .map((marker) => ({\n marker,\n ...project(marker.position.lat, marker.position.lng, zoom),\n }))\n .sort(\n (a, b) => a.x - b.x || a.y - b.y || (a.marker.id < b.marker.id ? -1 : 1),\n );\n\n const buckets: Bucket<T>[] = [];\n // Spatial hash of bucket centres, one cell per radius, so a marker only\n // compares with the nine cells around it.\n const grid = new Map<string, Bucket<T>[]>();\n const cellOf = (value: number): number => Math.floor(value / radiusPx);\n const radiusSquared = radiusPx * radiusPx;\n\n for (const point of projected) {\n const cx = cellOf(point.x);\n const cy = cellOf(point.y);\n let best: Bucket<T> | null = null;\n let bestDistance = radiusSquared;\n for (let dx = -1; dx <= 1; dx += 1) {\n for (let dy = -1; dy <= 1; dy += 1) {\n for (const bucket of grid.get(`${cx + dx}:${cy + dy}`) ?? []) {\n const distance =\n (bucket.x - point.x) ** 2 + (bucket.y - point.y) ** 2;\n if (distance <= bestDistance) {\n best = bucket;\n bestDistance = distance;\n }\n }\n }\n }\n if (best) {\n const n = best.members.length;\n // The centre moves with the mean, so its grid cell can change.\n const before = `${cellOf(best.x)}:${cellOf(best.y)}`;\n best.x = (best.x * n + point.x) / (n + 1);\n best.y = (best.y * n + point.y) / (n + 1);\n best.lat = (best.lat * n + point.marker.position.lat) / (n + 1);\n best.lng = (best.lng * n + point.marker.position.lng) / (n + 1);\n best.members.push(point.marker);\n const after = `${cellOf(best.x)}:${cellOf(best.y)}`;\n if (before !== after) {\n const old = grid.get(before);\n if (old) old.splice(old.indexOf(best), 1);\n const next = grid.get(after);\n if (next) next.push(best);\n else grid.set(after, [best]);\n }\n } else {\n const bucket: Bucket<T> = {\n x: point.x,\n y: point.y,\n members: [point.marker],\n lat: point.marker.position.lat,\n lng: point.marker.position.lng,\n };\n buckets.push(bucket);\n const key = `${cx}:${cy}`;\n const list = grid.get(key);\n if (list) list.push(bucket);\n else grid.set(key, [bucket]);\n }\n }\n\n const singles: T[] = [];\n const clusters: MarkerCluster<T>[] = [];\n for (const bucket of buckets) {\n if (bucket.members.length === 1) {\n singles.push(bucket.members[0]);\n } else {\n clusters.push({\n id: bucket.members[0].id,\n position: { lat: bucket.lat, lng: bucket.lng },\n members: bucket.members,\n });\n }\n }\n return { singles, clusters };\n}\n","/**\n * True when focus on `target` is keyboard-style (`:focus-visible`). A mouse\n * click also moves focus, and hover-like states (a map card, a hover pin)\n * must not stay up after the pointer has gone, so they follow this rather\n * than any focus.\n */\nexport function isFocusVisible(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false;\n try {\n return target.matches(':focus-visible');\n } catch {\n // An old engine without `:focus-visible`: treat focus as hover.\n return true;\n }\n}\n","/* ------------------------------------------------------------------ */\n/* MapView — kit wrapper over @vis.gl/react-google-maps. */\n/* */\n/* - Loads the Google Maps JS API on demand via the library's */\n/* `APIProvider`; consumer supplies the key (no kit-side cache). */\n/* - Defaults to a CONSENT-GATED placeholder (`consentGranted={false}`) */\n/* so the third-party load never happens before the consumer flips */\n/* the opt-in. GDPR-safe by default. */\n/* - The tiles are Google's own style and do not follow the kit theme; */\n/* consumers pass `mapId` for a cloud-styled map. Pins and clusters */\n/* use one theme-stable marker colour. */\n/* - `data-component=\"map-view\"` lives on the outer span; the inner */\n/* `<Map>` mounts inside it. */\n/* ------------------------------------------------------------------ */\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type MutableRefObject,\n type RefObject,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { MapPin, ExternalLink } from 'lucide-react';\nimport {\n APIProvider,\n Map as GoogleMap,\n AdvancedMarker,\n Marker,\n useApiIsLoaded,\n useMap,\n useMapsLibrary,\n} from '@vis.gl/react-google-maps';\nimport { Button } from '../button';\nimport { Skeleton } from '../skeleton';\nimport { VisuallyHidden } from '../visually-hidden';\nimport { ErrorBoundary } from '../error-boundary';\nimport { MapsCoreGate } from '../_shared/maps-core-gate';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { mapViewAgent } from './map-view.agent';\nimport { lengthTokenPx } from '../_shared/length-token';\nimport { MapViewPin, MAP_PIN_HOVER_SCALE } from './map-view-pin';\nimport {\n MAP_PIN_HEIGHT,\n MAP_PIN_WIDTH,\n mapPinSvg,\n} from './map-view-pin-geometry';\nimport {\n CLUSTER_OFF_ZOOM,\n clusterMarkerSet,\n shouldCluster,\n type MarkerCluster,\n} from './map-view-cluster';\nimport { isFocusVisible } from '../_shared/focus-visible';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface MapLatLng {\n lat: number;\n lng: number;\n}\n\nexport interface MapBounds {\n north: number;\n south: number;\n east: number;\n west: number;\n}\n\nexport interface MapBoundsChangeDetails {\n /** False for initial load, resizing, fitting and imperative camera moves. */\n userInteraction: boolean;\n}\n\n/**\n * A point to fit the camera to. `id` is its identity (a result id): it\n * decides whether a new set only added points to the old one. Without\n * it the coordinates stand in, so two results at one address count as\n * one.\n */\nexport interface MapFitPoint extends MapLatLng {\n id?: string;\n}\n\nexport interface MapMarker {\n /** Stable id — used for keyed re-rendering and `onMarkerClick`. */\n id: string;\n /** Position in lat/lng. */\n position: MapLatLng;\n /** Optional accessible label — falls back to the id. */\n label?: string;\n}\n\n/**\n * A map image shown in place of the interactive map until the host lets it\n * load (HTP-7348). Serve it from the host's own domain, so the browser makes\n * no request to Google before the visitor chooses.\n */\nexport interface MapViewStaticPreview {\n /** Image URL, typically on the host's own domain. */\n src: string;\n /**\n * Optional `srcset` with x descriptors only (`\"/map@2x.png 2x\"`): the image\n * has no `sizes`, so width descriptors would not work.\n */\n srcSet?: string;\n /** What the image shows, for example \"Map of Studio Rossi\". */\n alt: string;\n /** Intrinsic size in CSS pixels, so the image holds its box while loading. */\n width: number;\n height: number;\n /**\n * The same map in other shapes (tall, square, wide). When given, the kit\n * measures the preview area and loads only the source whose aspect ratio\n * (`width / height`) is closest to it; on a tie the earlier one wins. The\n * image is still fitted whole, never cropped. The top-level `src`, `srcSet`,\n * `width` and `height` stay as the default, used when the area cannot be\n * measured. `alt` is shared.\n */\n sources?: ReadonlyArray<MapViewStaticPreviewSource>;\n}\n\n/** One shape of a `MapViewStaticPreview`. */\nexport interface MapViewStaticPreviewSource {\n src: string;\n /** x descriptors only, as on the preview itself. */\n srcSet?: string;\n /** Intrinsic size in CSS pixels. */\n width: number;\n height: number;\n}\n\n/** A source must beat the current one by this much (log ratio) to replace it. */\nconst PREVIEW_SWITCH_MARGIN = 0.08;\n\nfunction closestSourceIndex(logRatios: ReadonlyArray<number>, area: number) {\n let best = 0;\n for (let i = 1; i < logRatios.length; i += 1) {\n if (Math.abs(logRatios[i]! - area) < Math.abs(logRatios[best]! - area)) {\n best = i;\n }\n }\n return best;\n}\n\n/**\n * Which `sources` entry fits the box that `ref` points at: measured before\n * first paint, so only one image is requested, and re-picked on resize with a\n * margin so it does not flip at a boundary. `null` means \"not measured\": use\n * the preview's own `src`.\n */\nfunction usePickedSourceIndex(\n sources: ReadonlyArray<MapViewStaticPreviewSource> | undefined,\n ref: RefObject<HTMLDivElement | null>,\n): number | null {\n const [index, setIndex] = useState<number | null>(null);\n const shapeKey = (sources ?? []).map((s) => `${s.width}x${s.height}`).join();\n useLayoutEffect(() => {\n const node = ref.current;\n const logRatios = shapeKey\n ? shapeKey.split(',').map((k) => {\n const [w, h] = k.split('x').map(Number);\n return Math.log(w! / h!);\n })\n : [];\n if (\n !node ||\n logRatios.length === 0 ||\n logRatios.some((r) => !isFinite(r))\n ) {\n setIndex(null);\n return undefined;\n }\n // New shapes start fresh: the margin only guards a resize.\n setIndex(null);\n const measure = (initial: boolean): void => {\n const { width, height } = node.getBoundingClientRect();\n if (!(width > 0 && height > 0)) {\n if (initial) setIndex(null);\n return;\n }\n const area = Math.log(width / height);\n const next = closestSourceIndex(logRatios, area);\n setIndex((current) => {\n if (current === null || current >= logRatios.length) return next;\n const gain =\n Math.abs(logRatios[current]! - area) -\n Math.abs(logRatios[next]! - area);\n return gain > PREVIEW_SWITCH_MARGIN ? next : current;\n });\n };\n measure(true);\n if (typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => measure(false));\n observer.observe(node);\n return () => observer.disconnect();\n }, [ref, shapeKey]);\n return index;\n}\n\n/** What moved onto or off a pin: the pointer, or keyboard-style focus. */\nexport type MapMarkerHoverSource = 'pointer' | 'focus';\n\nexport interface MapViewHandle {\n /** Pan the map to a new centre without changing zoom. */\n panTo: (position: MapLatLng) => void;\n /** Set zoom (typical range 1–20). */\n setZoom: (zoom: number) => void;\n /** Programmatically select a marker (fires `onMarkerClick`). */\n selectMarker: (id: string | null) => void;\n /* ---- read-back ------------------------------------------------- */\n /**\n * True once the consent gate has been passed. The map may still be\n * loading: the three actions above stay silent no-ops until\n * `getCenter` returns a value.\n */\n isConsentGranted: () => boolean;\n /** Live map centre, or `null` until the map has loaded. */\n getCenter: () => MapLatLng | null;\n /**\n * Live zoom level, or `null` until the map has loaded.\n * Google clamps to the map type's range, so this is not necessarily\n * the value last passed to `setZoom`.\n */\n getZoom: () => number | null;\n /**\n * The controlled `selectedMarkerId` prop as the host currently holds\n * it. `selectMarker` only proposes a selection — the host is free to\n * ignore it, and this is the only way to see whether it did.\n */\n getSelectedMarkerId: () => string | null;\n /** Number of markers passed to the component. Count only, no ids. */\n getMarkerCount: () => number;\n}\n\n/**\n * The slice of `MapViewHandle` that needs a live `google.maps.Map`.\n * Built inside the `APIProvider` subtree by `MapImperativeBridge`; the\n * outer component composes it with the props-derived getters, and holds\n * `null` whenever the subtree is unmounted.\n */\ntype MapBridgeHandle = Pick<\n MapViewHandle,\n 'panTo' | 'setZoom' | 'selectMarker' | 'getCenter' | 'getZoom'\n> & {\n /** Set centre and zoom in one step, never animated. Internal. */\n jumpTo: (center: MapLatLng, zoom: number) => void;\n};\n\nexport interface MapViewProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'onClick'>,\n VariantProps<typeof mapViewVariants> {\n /** Opaque instance id — emitted as `data-component-id`. */\n id?: string;\n /** Google Maps JS API key. Consumer-owned; never cached by the kit. */\n apiKey: string;\n /** Map centre (lat/lng). Required so the consent placeholder can also use it. */\n center: MapLatLng;\n /** Initial zoom. Default 15 (city-block detail, matches booking site). */\n zoom?: number;\n /**\n * Markers to render. The first marker auto-selects when no\n * `selectedMarkerId` is given.\n */\n markers?: MapMarker[];\n /** Controlled selection — fires `onMarkerClick` with the new id (or null on map click). */\n selectedMarkerId?: string | null;\n /** Fires when a marker is clicked, or `null` when the map background is clicked. */\n onMarkerClick?: (id: string | null) => void;\n /**\n * The marker the host is pointing at (for example the result card under\n * the pointer). Its pin grows and paints above the others, in a\n * contrasting ink so it clears WCAG 1.4.11 against the map. Separate from\n * `selectedMarkerId`, which is the committed choice. Where a classic\n * (no `mapId`) marker is used only the stacking order changes.\n */\n hoveredMarkerId?: string | null;\n /**\n * Content for a small card shown above the `hoveredMarkerId` pin. It is\n * hidden from assistive technology and kept out of the tab order, so it\n * must only repeat what the host already says elsewhere. The kit places\n * it above the pin, flips it below when there is no room, and nudges it\n * inside the map. Nothing renders when the pin is out of view.\n */\n renderMarkerCard?: (id: string) => ReactNode;\n /**\n * The pointer entered (`hovered` true) or left (false) a pin, or keyboard\n * focus (`:focus-visible`) moved onto one. `source` says which, so a host\n * can follow the pointer only where the device can hover. To show the card\n * for the pin, set `hoveredMarkerId` from it, and clear it only for the id\n * it holds. Focus that a mouse click gives a pin is not reported as \"on\";\n * a blur, and Escape on a focused pin, is reported as \"off\". A pointer\n * leave is not reported while the pin keeps keyboard focus, and a blur not\n * while the pointer is on the pin. Focus is\n * reported for `mapId` (advanced) markers only: Google's classic marker\n * sends no focus event. The kit bundles `@vis.gl/react-google-maps` 1.8.x,\n * which has the marker pointer events this needs.\n */\n onMarkerHover?: (\n id: string,\n hovered: boolean,\n source: MapMarkerHoverSource,\n ) => void;\n /**\n * The visible viewport once the map settles, including its first bounds.\n * Fires only when bounds change. `userInteraction` is true after patient\n * input that pans or zooms, never for a kit-initiated camera move.\n * Silent until the consented map has loaded.\n */\n onBoundsChange?: (bounds: MapBounds, details: MapBoundsChangeDetails) => void;\n /**\n * GDPR consent gate. When `false` (default) the component renders a\n * placeholder with a translated CTA — the Maps JS API is **not**\n * loaded. Flip to `true` once the consumer has captured opt-in.\n */\n consentGranted?: boolean;\n /** Fires when the consumer clicks the \"Load map\" CTA in the placeholder. */\n onConsentRequest?: () => void;\n /**\n * A map image to show instead of the text placeholder while\n * `consentGranted` is false (HTP-7348). A \"Show interactive map\" button on\n * the image calls `onConsentRequest`, as \"Load map\" does, under the same\n * disclosure (`consentDescription`) and with the \"Open in Google Maps\"\n * link. They sit on a plate at the top of the map. When consent flips,\n * focus moves from that button to the map region. The image is shown whole\n * (`object-fit: contain`, so Google's logo and copyright stay visible),\n * loads lazily, and stands in until the interactive map can draw. If it\n * fails to load, the text placeholder shows instead. `consentCta`\n * replaces the button.\n */\n staticPreview?: MapViewStaticPreview;\n /**\n * Optional address string shown in the consent placeholder so the\n * surface is useful before the map loads. Also used to build the\n * fallback \"Open in Google Maps\" link target.\n */\n address?: string;\n /**\n * Language of the practice's own text (the `address` shown in the\n * placeholder), as a BCP 47 tag such as `\"it\"`. Written as `lang` on that\n * line so a screen reader on a page in another language pronounces it\n * correctly. Leave it out and no `lang` is written. The kit's own labels\n * keep the page language.\n */\n contentLang?: string;\n /**\n * Cloud-styled-map id. When set, takes precedence over the JSON\n * `styles` array and disables the theme-bridge re-fire. Use when\n * you've provisioned a styled map in Google Cloud Console.\n */\n mapId?: string;\n /**\n * Override the consent-placeholder description text. The default\n * (`t('mapView.consentDescription')`) is a generic IP-disclosure\n * line. Consumers operating under specific GDPR / ePrivacy\n * disclosures (jurisdiction, named controller, policy link) should\n * supply their own text via this prop. Pass any `ReactNode` — a\n * paragraph, a localised `<Trans>` instance, etc.\n */\n consentDescription?: ReactNode;\n /**\n * Replace the kit-rendered \"Load map\" button with custom CTA chrome.\n * Use when the consent dialog needs a policy link, a specific\n * jurisdiction phrasing, or a different surface (Tooltip-wrapped\n * button, link to a settings page, etc.). When omitted the kit\n * renders its own `<Button>` — that button calls `onConsentRequest`\n * if provided, otherwise flips MapView's internal opt-in state so\n * the map loads inline.\n */\n consentCta?: ReactNode;\n /**\n * Visual surface. Default `bordered` (current behaviour: 1px\n * `--border` outline + `--radius-md`). `elevated` replaces the\n * border with `--shadow-card`, matching `<Card variant=\"elevated\">`.\n * `bare` drops both — map is flush against the page canvas.\n */\n surface?: 'bordered' | 'elevated' | 'bare';\n /** Accessible label for the map region. Falls back to `t('mapView.label')`. */\n ariaLabel?: string;\n /**\n * Points the camera fits into view, with a margin, once the map has\n * loaded, and again whenever `fitKey` changes. Use it to show every pin\n * of a result list without the patient moving the map.\n *\n * - One point (or several at the same place) centres on it at\n * `fitMaxZoom`.\n * - No points: no fit, the camera shows `center` / `zoom`. If the\n * points run out after a fit, or the prop is removed, the camera\n * goes back there in one step.\n * - Between key changes nothing refits, so a map the patient has\n * panned or zoomed stays where they left it.\n * - A key change that only adds points (a \"load more\": every fitted\n * point is still there, compared by `id`) refits only if the patient\n * has not moved the map. Any other change refits. Give each point the\n * `id` of its result: two results can share an address.\n * - The camera is set in one step, never animated.\n * - While there are points to fit, the fit owns the camera: later\n * changes to `center` or `zoom` do not move it.\n */\n fitToPoints?: MapFitPoint[];\n /**\n * Identity of the fitted set: the camera refits only when this\n * changes. Pass the ids of the listed results, so re-sorting the same\n * results does not refit. Defaults to the points themselves, in any\n * order.\n */\n fitKey?: string;\n /**\n * The closest a fit may zoom in, so one result does not open at street\n * level. Default 15.\n */\n fitMaxZoom?: number;\n /**\n * Group nearby pins into numbered clusters that split as the map zooms in.\n * `'auto'` (default) clusters only above 30 markers, so a map with a few\n * pins looks and behaves as before. `true` clusters any set of two or\n * more; `false` never does.\n *\n * - A cluster is a button named by its count (\"12 practices\"), at least\n * `--min-target-size` square. Click, Enter or Space zooms to its pins.\n * - The `selectedMarkerId` and `hoveredMarkerId` pins always stay out of\n * clusters, so a chosen or hovered practice is never hidden.\n * - From zoom 17 (street level) pins no longer cluster.\n * - It runs only inside the consented, loaded map: no Google request is\n * made for it.\n */\n clusterMarkers?: boolean | 'auto';\n /**\n * The name of a cluster, by its pin count: a string with `{count}` in it,\n * or a function. Defaults to the kit's translated \"12 practices\". Set it\n * when the pins are not practices (\"12 offices\").\n */\n clusterLabel?: string | ((count: number) => string);\n /**\n * A cluster was activated: the map zooms to its pins. The host can track\n * it. The zoom is not reported as a patient pan, so `onBoundsChange` says\n * `userInteraction: false` for it.\n */\n onClusterClick?: (details: { count: number; markerIds: string[] }) => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst mapViewVariants = cva(\n [\n 'ds:relative ds:overflow-hidden ds:block',\n 'ds:bg-[var(--muted)]',\n // Focus lands on the region when the interactive map replaces its button.\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:[--map-block-size:14rem]',\n md: 'ds:[--map-block-size:20rem]',\n lg: 'ds:[--map-block-size:28rem]',\n // `fill` defers sizing to the parent. The outer takes\n // `block-size: 100%` so the inner (also 100%) has something\n // to inherit from. Use inside a viewport-locked column —\n // `<PracticeResults variant=\"map-first\">` wires this through\n // automatically; standalone consumers must give the parent\n // a constrained block-size or the map collapses to 0.\n fill: 'ds:[block-size:100%]',\n },\n surface: {\n bordered:\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]',\n elevated:\n 'ds:rounded-[var(--radius-md)] ds:shadow-[var(--shadow-card)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--card-border)]',\n bare: 'ds:rounded-none ds:border-0',\n },\n },\n defaultVariants: { size: 'md', surface: 'bordered' },\n },\n);\n\n// Inner block-size: percentage when `size=\"fill\"`, rem-token otherwise.\n// Splitting this out (instead of compound-variants) keeps the size\n// switch readable and avoids enumerating every (size × surface) pair.\nconst innerSurfaceVariants = cva('ds:block ds:w-full', {\n variants: {\n size: {\n sm: 'ds:[block-size:var(--map-block-size)]',\n md: 'ds:[block-size:var(--map-block-size)]',\n lg: 'ds:[block-size:var(--map-block-size)]',\n fill: 'ds:[block-size:100%]',\n },\n },\n defaultVariants: { size: 'md' },\n});\n\nconst consentChromeVariants = cva(\n [\n 'ds:absolute ds:inset-0',\n 'ds:flex ds:flex-col ds:items-center ds:justify-center',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:p-[var(--spacing-md)]',\n 'ds:bg-[var(--muted)] ds:text-[var(--foreground)]',\n 'ds:text-center',\n ].join(' '),\n);\n\nconst consentTitleVariants = cva('type-heading-3 ds:m-0');\n\nconst consentDescriptionVariants = cva(\n 'type-body-sm ds:text-[var(--muted-foreground)] ds:[max-inline-size:28rem] ds:m-0',\n);\n\n/* ------------------------------------------------------------------ */\n/* Inner imperative bridge */\n/* */\n/* `useMap()` only resolves inside the APIProvider tree, so the */\n/* imperative actions live in a tiny inner component that the outer */\n/* `MapView` mounts conditionally. It exposes the `panTo` / `setZoom` */\n/* etc. to the forwardRef via a `useImperativeHandle` callback prop. */\n/* ------------------------------------------------------------------ */\n\ninterface MapImperativeBridgeProps {\n /** Called with the live handle on mount, and with `null` on unmount. */\n onHandleReady: (handle: MapBridgeHandle | null) => void;\n onMarkerSelect: (id: string | null) => void;\n onCameraMove: () => void;\n}\n\nfunction MapImperativeBridge({\n onHandleReady,\n onMarkerSelect,\n onCameraMove,\n}: MapImperativeBridgeProps): null {\n const map = useMap();\n useEffect(() => {\n if (!map) return;\n onHandleReady({\n panTo: (position) => {\n onCameraMove();\n map.panTo(position);\n },\n setZoom: (zoom) => {\n onCameraMove();\n map.setZoom(zoom);\n },\n jumpTo: (center, zoom) => {\n onCameraMove();\n map.moveCamera({ center, zoom });\n },\n selectMarker: (id) => onMarkerSelect(id),\n // Read straight off the live map: Google clamps `setZoom` to the\n // map type's range and animates `panTo`, so requested and applied\n // are not the same value.\n getCenter: () => map.getCenter()?.toJSON() ?? null,\n getZoom: () => map.getZoom() ?? null,\n });\n // Drop the handle when the subtree unmounts (consent revoked, or the\n // component tears down) so the outer getters answer `null` instead of\n // reporting a dead map's last known position.\n return () => onHandleReady(null);\n }, [map, onHandleReady, onMarkerSelect, onCameraMove]);\n return null;\n}\n\n/** Report once per settled viewport, with patient input kept separate from camera state. */\nfunction MapBoundsBridge({\n onBoundsChange,\n userInteraction,\n}: {\n onBoundsChange: NonNullable<MapViewProps['onBoundsChange']>;\n userInteraction: MutableRefObject<MapInteraction>;\n}): null {\n const map = useMap();\n const callback = useRef(onBoundsChange);\n callback.current = onBoundsChange;\n\n useEffect(() => {\n if (!map) return undefined;\n const div = map.getDiv();\n let previous: {\n bounds: MapBounds;\n center: MapLatLng;\n zoom: number;\n } | null = null;\n const resetInteraction = (): void => {\n userInteraction.current = {\n pendingInput: false,\n moved: false,\n programmatic: false,\n };\n };\n resetInteraction();\n let inputExpiry: ReturnType<typeof setTimeout> | undefined;\n const touchPointers = new Set<number>();\n const markInput = (event: Event): void => {\n if (event instanceof PointerEvent && event.pointerType === 'touch') {\n if (event.type === 'pointerdown') touchPointers.add(event.pointerId);\n if (event.type === 'pointerup' || event.type === 'pointercancel') {\n touchPointers.delete(event.pointerId);\n return;\n }\n }\n if (\n event instanceof PointerEvent &&\n event.type === 'pointermove' &&\n event.buttons === 0 &&\n event.pointerType !== 'touch'\n )\n return;\n if (\n event instanceof KeyboardEvent &&\n ![\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n '+',\n '=',\n '-',\n '_',\n 'Enter',\n ' ',\n 'PageUp',\n 'PageDown',\n ].includes(event.key)\n )\n return;\n // A short panTo can keep animating after the imperative call has\n // returned. Ordinary scrolling or clicking the map must not claim\n // that animation. A real camera gesture can interrupt it.\n const fullscreen = document.fullscreenElement;\n const fullscreenMap = Boolean(\n fullscreen && (fullscreen.contains(div) || div.contains(fullscreen)),\n );\n let cameraInput = event.type === 'dblclick';\n if (event instanceof PointerEvent && event.type === 'pointermove')\n cameraInput =\n event.pointerType !== 'touch' ||\n touchPointers.size >= 2 ||\n fullscreenMap;\n if (event.type === 'touchmove')\n cameraInput =\n (event as TouchEvent).touches.length >= 2 || fullscreenMap;\n if (event instanceof KeyboardEvent)\n cameraInput = event.key !== 'Enter' && event.key !== ' ';\n if (event instanceof WheelEvent)\n cameraInput = event.ctrlKey || event.metaKey || fullscreenMap;\n if (event.type === 'click')\n cameraInput =\n event.target instanceof Element &&\n event.target.closest('button, [role=\"button\"]') != null;\n if (cameraInput) userInteraction.current.programmatic = false;\n userInteraction.current.pendingInput = true;\n clearTimeout(inputExpiry);\n // Controls normally start moving the camera immediately. Expire an\n // input that did nothing; once movement starts it stays attributed\n // until idle, including a long drag or zoom animation.\n inputExpiry = setTimeout(() => {\n userInteraction.current.pendingInput = false;\n }, 250);\n };\n const markCameraChange = (): void => {\n if (\n userInteraction.current.pendingInput &&\n !userInteraction.current.programmatic\n )\n userInteraction.current.moved = true;\n };\n const report = (): void => {\n const bounds = map.getBounds()?.toJSON();\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const fromInput = userInteraction.current.moved;\n resetInteraction();\n clearTimeout(inputExpiry);\n if (!bounds || !center || zoom == null) return;\n const changed =\n !previous ||\n (Object.keys(bounds) as (keyof MapBounds)[]).some(\n (key) => bounds[key] !== previous!.bounds[key],\n );\n const moved =\n previous != null &&\n (zoom !== previous.zoom ||\n center.lat !== previous.center.lat ||\n center.lng !== previous.center.lng);\n previous = { bounds, center, zoom };\n if (changed)\n callback.current(bounds, { userInteraction: fromInput && moved });\n };\n // Camera events alone include initial load, refits and resizes. Input\n // also covers touch, keyboard and Google's zoom controls, without\n // preventing their defaults or depending on vendor DOM selectors.\n const inputEvents = [\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointercancel',\n 'touchstart',\n 'touchmove',\n 'click',\n 'dblclick',\n 'wheel',\n 'keydown',\n ] as const;\n inputEvents.forEach((event) =>\n div.addEventListener(event, markInput, { capture: true, passive: true }),\n );\n const listeners = [\n map.addListener('dragstart', () => {\n userInteraction.current.programmatic = false;\n userInteraction.current.moved = true;\n }),\n map.addListener('center_changed', markCameraChange),\n map.addListener('zoom_changed', markCameraChange),\n map.addListener('idle', report),\n ];\n report();\n return () => {\n listeners.forEach((listener) => listener.remove());\n inputEvents.forEach((event) =>\n div.removeEventListener(event, markInput, true),\n );\n clearTimeout(inputExpiry);\n resetInteraction();\n };\n }, [map, userInteraction]);\n return null;\n}\n\ninterface MapInteraction {\n pendingInput: boolean;\n moved: boolean;\n programmatic: boolean;\n}\n\n/* ------------------------------------------------------------------ */\n/* Fit to points */\n/* */\n/* Lives inside <GoogleMap>, so it only runs once the `maps` library */\n/* has arrived and `useMap()` holds a real map: the load-race rule */\n/* below applies to it too. The camera is computed here (Web Mercator) */\n/* and set in one step, rather than through fitBounds: fitBounds takes */\n/* no zoom limit, and capping it afterwards needs an `idle` event that */\n/* Google may never send when the view does not change, so the cap */\n/* could fire on the patient's next zoom instead. Setting the camera */\n/* directly is synchronous and never animates (reduced motion). */\n/* */\n/* A map in a hidden column or a sheet still opening has no size yet, */\n/* so the fit waits for the map to be laid out. */\n/* ------------------------------------------------------------------ */\n\n/* ------------------------------------------------------------------ */\n/* Hover card: pixel anchor + layer */\n/* */\n/* The card is plain DOM inside the map frame, not a Google overlay, so */\n/* it works with and without a `mapId`. The bridge turns the pin's */\n/* lat/lng into a pixel in the frame from the live centre and zoom. */\n/* ------------------------------------------------------------------ */\n\ninterface MarkerAnchor {\n id: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * The pixel under `position`, updated on every camera change. The state lives\n * here, in the small card wrapper, so camera frames re-render only the card\n * and never the map's markers.\n */\nfunction useMarkerAnchor(id: string, position: MapLatLng): MarkerAnchor | null {\n const map = useMap();\n const [anchor, setAnchor] = useState<MarkerAnchor | null>(null);\n const { lat, lng } = position;\n useEffect(() => {\n if (!map || typeof map.getCenter !== 'function') return undefined;\n const report = (): void => {\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const div = map.getDiv?.();\n if (!center || zoom == null || !div) {\n setAnchor(null);\n return;\n }\n const width = div.clientWidth;\n const height = div.clientHeight;\n const world = WORLD_PX * 2 ** zoom;\n setAnchor({\n id,\n width,\n height,\n x: width / 2 + ((lng - center.lng) / 360) * world,\n y:\n height / 2 -\n ((mercatorY(lat) - mercatorY(center.lat)) * world) / (2 * Math.PI),\n });\n };\n report();\n const listeners = (\n ['bounds_changed', 'zoom_changed', 'center_changed', 'idle'] as const\n ).flatMap((event) =>\n typeof map.addListener === 'function'\n ? [map.addListener(event, report)]\n : [],\n );\n return () => listeners.forEach((listener) => listener.remove());\n }, [map, id, lat, lng]);\n return anchor?.id === id ? anchor : null;\n}\n\n/**\n * Mounted inside the map (it needs the map instance), it draws the card into\n * the frame element through a portal.\n */\nfunction MapMarkerCard({\n marker,\n frame,\n direction,\n children,\n}: {\n marker: MapMarker;\n frame: HTMLElement | null;\n direction: 'ltr' | 'rtl';\n children: ReactNode;\n}): ReactNode {\n const anchor = useMarkerAnchor(marker.id, marker.position);\n if (!anchor || !frame) return null;\n return createPortal(\n <MapMarkerCardLayer anchor={anchor} direction={direction}>\n {children}\n </MapMarkerCardLayer>,\n frame,\n );\n}\n\n/** Room between the card and the pin tip, and between the card and the frame edge. */\nconst CARD_PIN_CLEARANCE_PX = 52;\nconst CARD_PIN_BELOW_PX = 12;\nconst CARD_EDGE_PX = 8;\n\nfunction MapMarkerCardLayer({\n anchor,\n children,\n direction,\n}: {\n anchor: MarkerAnchor;\n children: ReactNode;\n direction: 'ltr' | 'rtl';\n}): ReactNode {\n const cardRef = useRef<HTMLDivElement>(null);\n const [size, setSize] = useState<{ width: number; height: number } | null>(\n null,\n );\n useLayoutEffect(() => {\n const node = cardRef.current;\n if (!node) return;\n setSize((prev) =>\n prev?.width === node.offsetWidth && prev.height === node.offsetHeight\n ? prev\n : { width: node.offsetWidth, height: node.offsetHeight },\n );\n }, [children, anchor.id]);\n\n const inView =\n anchor.x >= 0 &&\n anchor.x <= anchor.width &&\n anchor.y >= 0 &&\n anchor.y <= anchor.height;\n if (!inView) return null;\n\n const width = size?.width ?? 0;\n const height = size?.height ?? 0;\n // Centred on the pin, nudged to stay inside the frame.\n const maxLeft = Math.max(CARD_EDGE_PX, anchor.width - width - CARD_EDGE_PX);\n const left = Math.min(Math.max(anchor.x - width / 2, CARD_EDGE_PX), maxLeft);\n // Above the pin; below it when there is no room above.\n const above = anchor.y - CARD_PIN_CLEARANCE_PX - height;\n const flipped = above < CARD_EDGE_PX;\n const top = flipped ? anchor.y + CARD_PIN_BELOW_PX : above;\n\n return (\n // The layer is `ltr` because the map is geographic: x is always measured\n // from the physical left. The card inside takes the page direction.\n <div\n dir=\"ltr\"\n aria-hidden=\"true\"\n data-component=\"map-view-marker-card-layer\"\n className=\"ds:pointer-events-none ds:absolute ds:inset-0 ds:overflow-hidden\"\n >\n <div\n ref={cardRef}\n data-component=\"map-view-marker-card\"\n data-marker-id={anchor.id}\n data-placement={flipped ? 'below' : 'above'}\n // eslint-disable-next-line react/forbid-dom-props -- pixel position computed from the live map camera, passed as CSS variables\n style={\n {\n '--marker-card-x': `${left}px`,\n '--marker-card-y': `${top}px`,\n } as CSSProperties\n }\n data-measured={size ? 'true' : 'false'}\n className=\"ds:absolute ds:[inset-inline-start:var(--marker-card-x)] ds:[inset-block-start:var(--marker-card-y)] ds:[inline-size:max-content] ds:[max-inline-size:calc(100%-var(--spacing-md))] ds:data-[measured=false]:invisible\"\n >\n {/* The positioned element stays `ltr`, so its insets read from the\n physical left like the pixel. Only the card's content follows\n the page direction. */}\n <div dir={direction}>{children}</div>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Advanced marker: the pin, its hover look, pointer and focus */\n/* */\n/* Google makes a clickable advanced marker a focusable button */\n/* (`gmp-advanced-marker`, role=\"button\", tabindex 0), so keyboard */\n/* focus is read from the element itself. */\n/* ------------------------------------------------------------------ */\n\ninterface KitAdvancedMarkerProps {\n marker: MapMarker;\n label: string;\n isSelected: boolean;\n isHovered: boolean;\n onSelect: (id: string) => void;\n onHover?: MapViewProps['onMarkerHover'];\n /** Where the pin's element is kept, so focus can be moved to it. */\n elements?: MarkerElements;\n}\n\n/** The element Google draws for an advanced marker. */\nfunction markerElement(\n marker: google.maps.marker.AdvancedMarkerElement | null,\n): HTMLElement | null {\n if (!marker) return null;\n if (marker instanceof HTMLElement) return marker;\n const element = (marker as { element?: unknown }).element;\n return element instanceof HTMLElement ? element : null;\n}\n\nfunction KitAdvancedMarker({\n marker,\n label,\n isSelected,\n isHovered,\n onSelect,\n onHover,\n elements,\n}: KitAdvancedMarkerProps): ReactNode {\n const [element, setElement] =\n useState<google.maps.marker.AdvancedMarkerElement | null>(null);\n const { id } = marker;\n // The pointer is on the pin. Read by the focus listeners, so a blur while\n // the pointer stays (Tab away, the window losing focus) keeps the card.\n const pointerInside = useRef(false);\n useEffect(() => {\n const node = markerElement(element);\n // The kit ring is drawn on the pin (MapViewPin), not Google's outline.\n if (node) node.style.outline = 'none';\n if (!node || !elements) return undefined;\n const registry = elements.current;\n registry.set(`pin:${marker.id}`, node);\n return () => {\n registry.delete(`pin:${marker.id}`);\n };\n }, [element, elements, marker.id]);\n useEffect(() => {\n const node = markerElement(element);\n if (!node || !onHover) return undefined;\n // Keyboard-style focus only: a mouse click focuses the pin too, and that\n // must not keep the card up once the pointer has gone.\n const focusIn = (event: FocusEvent): void => {\n if (isFocusVisible(event.target)) onHover(id, true, 'focus');\n };\n // A blur reports \"off\", mouse focus included, unless the pointer is still\n // on the pin. The host clears only the pin it holds.\n const focusOut = (event: FocusEvent): void => {\n if (node.contains(event.relatedTarget as Node | null)) return;\n if (pointerInside.current) return;\n onHover(id, false, 'focus');\n };\n // Escape hides the card without moving focus (WCAG 1.4.13).\n const keyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') onHover(id, false, 'focus');\n };\n node.addEventListener('focusin', focusIn);\n node.addEventListener('focusout', focusOut);\n node.addEventListener('keydown', keyDown);\n return () => {\n node.removeEventListener('focusin', focusIn);\n node.removeEventListener('focusout', focusOut);\n node.removeEventListener('keydown', keyDown);\n };\n }, [element, id, onHover]);\n\n return (\n <AdvancedMarker\n ref={setElement}\n position={marker.position}\n // Google names the pin from `title`: the marker element gets it as its\n // title and its accessible name (role button). vis.gl does not pass\n // `aria-label` or `data-*` on to the real marker, so `data-selected`\n // and `data-hovered` exist only on test fakes; QA cannot look for them\n // on a real pin.\n title={label}\n aria-label={label}\n data-selected={isSelected ? 'true' : 'false'}\n data-hovered={isHovered ? 'true' : 'false'}\n // Above every other pin while it is hovered.\n zIndex={isHovered ? 1000 : isSelected ? 500 : undefined}\n onClick={() => onSelect(id)}\n onMouseEnter={\n onHover\n ? () => {\n pointerInside.current = true;\n onHover(id, true, 'pointer');\n }\n : undefined\n }\n onMouseLeave={\n onHover\n ? () => {\n pointerInside.current = false;\n // Keyboard focus still on the pin keeps its card, as a\n // focused result card does when the pointer leaves it.\n const node = markerElement(element);\n const active = document.activeElement;\n if (\n node &&\n active &&\n node.contains(active) &&\n isFocusVisible(active)\n )\n return;\n onHover(id, false, 'pointer');\n }\n : undefined\n }\n >\n {/* The kit pin: the Alfadocs mark in the theme-stable marker violet with\n a white outline. Hovered: 1.4x from the tip, dark violet body. Both\n are theme-invariant, because the Google tiles do not follow the kit\n theme (3:1 against them, WCAG 1.4.11). */}\n <MapViewPin hovered={isHovered} />\n </AdvancedMarker>\n );\n}\n\n/** Google's world is 256px wide at zoom 0. */\nconst WORLD_PX = 256;\n\nconst toRadians = (degrees: number): number => (degrees * Math.PI) / 180;\n\n/** Web Mercator y of a latitude: the world spans -π..π. */\nfunction mercatorY(lat: number): number {\n const sin = Math.min(Math.max(Math.sin(toRadians(lat)), -0.9999), 0.9999);\n return Math.log((1 + sin) / (1 - sin)) / 2;\n}\n\nconst latitudeOf = (y: number): number =>\n (Math.atan(Math.sinh(y)) * 180) / Math.PI;\n\n/** The box round `points` (not antimeridian-aware: the kit maps Europe). */\nfunction boundsOfPoints(points: MapLatLng[]): google.maps.LatLngBoundsLiteral {\n let north = -90;\n let south = 90;\n let east = -180;\n let west = 180;\n for (const { lat, lng } of points) {\n north = Math.max(north, lat);\n south = Math.min(south, lat);\n east = Math.max(east, lng);\n west = Math.min(west, lng);\n }\n return { north, south, east, west };\n}\n\n/**\n * The centre and the closest whole zoom that shows every point inside a\n * `width` x `height` map, `padding` px in from each edge, no closer than\n * `maxZoom`. Exported for tests.\n */\nexport function cameraForPoints(\n points: MapLatLng[],\n width: number,\n height: number,\n padding: number,\n maxZoom: number,\n): { center: MapLatLng; zoom: number } {\n const { north, south, east, west } = boundsOfPoints(points);\n if (north === south && east === west) {\n return { center: { lat: north, lng: east }, zoom: maxZoom };\n }\n const top = mercatorY(north);\n const bottom = mercatorY(south);\n const center = {\n lat: latitudeOf((top + bottom) / 2),\n lng: (east + west) / 2,\n };\n const fit = (px: number, fraction: number): number =>\n fraction > 0 ? Math.log2(Math.max(px, 1) / WORLD_PX / fraction) : maxZoom;\n const zoom = Math.min(\n fit(width - 2 * padding, (east - west) / 360),\n fit(height - 2 * padding, (top - bottom) / (2 * Math.PI)),\n maxZoom,\n );\n return { center, zoom: Math.max(0, Math.floor(zoom)) };\n}\n\n/** A fit point's identity: its id, or else its coordinates. */\nconst pointKey = ({ id, lat, lng }: MapFitPoint): string =>\n id != null ? `id:${id}` : `at:${lat},${lng}`;\n\ninterface MapFitterProps {\n points: MapFitPoint[];\n fitKey: string;\n maxZoom: number;\n onCameraMove: () => void;\n}\n\nfunction MapFitter({\n points,\n fitKey,\n maxZoom,\n onCameraMove,\n}: MapFitterProps): null {\n const map = useMap();\n const fitted = useRef<{\n map: google.maps.Map;\n key: string;\n points: Set<string>;\n } | null>(null);\n // Whether the patient has panned or zoomed since the last fit: any\n // camera the kit did not set counts. Comparing with the camera the kit\n // last set (rather than flagging the kit's own call) holds whether\n // Google reports the change at once or later, and catches keyboard and\n // control-button pans, which fire no `dragstart`.\n const userMoved = useRef(false);\n const ownCamera = useRef<{ center: MapLatLng; zoom: number } | null>(null);\n\n useEffect(() => {\n if (!map) return undefined;\n const check = (): void => {\n const own = ownCamera.current;\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const same =\n own != null &&\n zoom === own.zoom &&\n center != null &&\n Math.abs(center.lat - own.center.lat) < 1e-6 &&\n Math.abs(center.lng - own.center.lng) < 1e-6;\n if (!same) userMoved.current = true;\n };\n const listeners = [\n map.addListener('dragstart', () => {\n userMoved.current = true;\n }),\n map.addListener('center_changed', check),\n map.addListener('zoom_changed', check),\n ];\n return () => listeners.forEach((listener) => listener.remove());\n }, [map]);\n\n useEffect(() => {\n if (!map) return undefined;\n const last = fitted.current?.map === map ? fitted.current : null;\n if (last?.key === fitKey) return undefined;\n const keys = new Set(points.map(pointKey));\n const moveCamera = (center: MapLatLng, zoom: number): void => {\n ownCamera.current = { center, zoom };\n onCameraMove();\n // One step, never animated.\n map.moveCamera({ center, zoom });\n userMoved.current = false;\n };\n\n if (points.length === 0) {\n // Nothing to fit. MapView returns the camera to center/zoom when\n // fitting ends. Recording the empty set means the same results\n // coming back later count as a change and fit again.\n fitted.current = { map, key: fitKey, points: keys };\n return undefined;\n }\n\n // More of the same results (\"load more\"): every point already fitted\n // is still listed, by identity. If the patient has moved the map,\n // their view wins. A result replaced by another at the same address\n // is a different id, so that set refits.\n const grewOnly =\n last != null &&\n last.points.size > 0 &&\n [...last.points].every((point) => keys.has(point));\n if (grewOnly && userMoved.current) {\n fitted.current = { map, key: fitKey, points: keys };\n return undefined;\n }\n\n const div = map.getDiv();\n // Only a fake map has no element; there is nothing to measure.\n if (!(div instanceof Element)) return undefined;\n const fit = (): void => {\n fitted.current = { map, key: fitKey, points: keys };\n // Never more than a quarter of either side, so a small map still\n // has room to fit the points rather than zooming out to the world.\n const padding = Math.min(\n lengthTokenPx('--spacing-2xl', div, 48),\n div.clientWidth / 4,\n div.clientHeight / 4,\n );\n const { center, zoom } = cameraForPoints(\n points,\n div.clientWidth,\n div.clientHeight,\n padding,\n maxZoom,\n );\n moveCamera(center, zoom);\n };\n const laidOut = (): boolean => div.clientWidth > 0 && div.clientHeight > 0;\n if (laidOut() || typeof ResizeObserver === 'undefined') {\n fit();\n return undefined;\n }\n const observer = new ResizeObserver(() => {\n if (!laidOut()) return;\n observer.disconnect();\n fit();\n });\n observer.observe(div);\n return () => observer.disconnect();\n }, [map, fitKey, points, maxZoom, onCameraMove]);\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Marker clustering */\n/* */\n/* Inside <GoogleMap>, so it only exists once the consented map has */\n/* loaded. Clusters are Google markers, like the pins: with a `mapId` */\n/* an AdvancedMarker with kit-styled content, without one a classic */\n/* Marker with a drawn icon. Google therefore keeps them under its own */\n/* controls and attribution, pans and zooms the map from a drag or */\n/* wheel on them, moves them with the tiles, and gives them the same */\n/* focus handling as a pin. They are re-grouped when the zoom changes. */\n/* ------------------------------------------------------------------ */\n\n/**\n * Below a selected (500) or hovered (1000) pin. Plain pins have no z-index,\n * so Google orders them by latitude; they rarely overlap a cluster.\n */\nconst CLUSTER_Z_INDEX = 10;\n\n/** The zoom, in quarter steps so a pinch does not regroup on every frame. */\nfunction useMapZoom(fallback: number): number {\n const map = useMap();\n const [zoom, setZoom] = useState(fallback);\n useEffect(() => {\n if (\n !map ||\n typeof map.addListener !== 'function' ||\n typeof map.getZoom !== 'function'\n )\n return undefined;\n const read = (): void => {\n const current = map.getZoom();\n if (current != null) setZoom(Math.round(current * 4) / 4);\n };\n read();\n const listeners = (['zoom_changed', 'idle'] as const).map((event) =>\n map.addListener(event, read),\n );\n return () => listeners.forEach((listener) => listener.remove());\n }, [map]);\n return zoom;\n}\n\nconst CLUSTER_BADGE_CLASS = [\n 'ds:inline-flex ds:items-center ds:justify-center',\n // 44 px, 48 px in the accessible themes, with room for a long count.\n 'ds:[min-inline-size:var(--min-target-size)] ds:[min-block-size:var(--min-target-size)]',\n 'ds:px-[var(--spacing-xs)] ds:rounded-full',\n 'ds:border-2 ds:border-solid ds:border-[color:var(--map-marker-hover-ring)]',\n // The theme-stable map marker colour, as the pins (see KitAdvancedMarker).\n 'ds:bg-[var(--map-marker-color)] ds:text-[var(--map-marker-hover-ring)]',\n // A 1 px ink edge outside the white ring: the ring separates the marker on\n // dark tiles, the edge carries 3:1 on light ones (WCAG 1.4.11).\n 'ds:shadow-[0_0_0_1px_var(--map-marker-hover-color),var(--shadow-md)] ds:tabular-nums ds:select-none type-label',\n 'ds:forced-colors:border-[ButtonText]',\n // Google's marker element takes the focus; the ring is drawn on the badge,\n // and the element's own outline is removed (see KitClusterMarker). The ring\n // is the theme-stable deep ink, not `--ring`, which is light in the dark\n // themes: the tiles do not follow the kit theme. `--focus-ring-width` is\n // thicker in the accessible themes.\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[length:var(--focus-ring-width)] ds:[gmp-advanced-marker:focus-visible_&]:outline-solid ds:[gmp-advanced-marker:focus-visible_&]:outline-[color:var(--map-marker-hover-color)] ds:[gmp-advanced-marker:focus-visible_&]:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:forced-colors:outline-[CanvasText]',\n].join(' ');\n\n/**\n * The look of a cluster: a round `--map-marker-color` marker with the count. It is the\n * content of a Google marker, which carries the name, the focus and the click,\n * so it is hidden from assistive technology itself. Exported for the story\n * specimens; MapView draws it by itself.\n */\nexport function MapClusterMarker({ count }: { count: number }): ReactNode {\n return (\n <span\n aria-hidden=\"true\"\n data-component=\"map-view-cluster\"\n data-count={count}\n className={CLUSTER_BADGE_CLASS}\n >\n {count}\n </span>\n );\n}\n\n/** The kit colours and target size a classic cluster icon is drawn with. */\ninterface ClusterIconTheme {\n fill: string;\n ink: string;\n ring: string;\n hover: string;\n /** Body of the hovered or selected pin. */\n selected: string;\n target: number;\n}\n\n/** A token's colour as the browser resolves it at `context`. */\nfunction resolveColorToken(token: string, context: Element): string {\n const probe = document.createElement('span');\n probe.style.color = `var(${token})`;\n probe.style.display = 'none';\n // A map element not in the page (a fake, a portal in flight) has no style.\n (context.isConnected ? context : document.documentElement).append(probe);\n const color = getComputedStyle(probe).color;\n probe.remove();\n return color || 'currentColor';\n}\n\n/**\n * The classic-marker icon's theme (no `mapId`): resolved once from the map's\n * own element, so a theme root or a host override applies, and again when the\n * theme changes, not once per cluster.\n */\nfunction useClusterIconTheme(enabled: boolean): ClusterIconTheme | null {\n const map = useMap();\n const [theme, setTheme] = useState<ClusterIconTheme | null>(null);\n useEffect(() => {\n if (!enabled || !map) return undefined;\n // The map's own element; the page root if the map cannot say.\n const own = map.getDiv?.();\n const div = own instanceof Element ? own : document.documentElement;\n const read = (): void => {\n const next = {\n fill: resolveColorToken('--map-marker-color', div),\n ink: resolveColorToken('--map-marker-hover-ring', div),\n ring: resolveColorToken('--map-marker-hover-ring', div),\n hover: resolveColorToken('--map-marker-hover-color', div),\n selected: resolveColorToken('--map-marker-selected-color', div),\n target: lengthTokenPx('--min-target-size', div, 44),\n };\n // The same four values keep the same state, so no icon is redrawn.\n setTheme((prev) =>\n prev &&\n prev.fill === next.fill &&\n prev.ink === next.ink &&\n prev.ring === next.ring &&\n prev.hover === next.hover &&\n prev.selected === next.selected &&\n prev.target === next.target\n ? prev\n : next,\n );\n };\n read();\n // The kit theme classes sit on <html>, or on the nearest ThemeRoot.\n const observer = new MutationObserver(read);\n const options = {\n attributes: true,\n attributeFilter: ['class', 'data-theme'],\n };\n observer.observe(document.documentElement, options);\n const themeRoot = div.closest('[data-component=\"theme-root\"]');\n if (themeRoot) observer.observe(themeRoot, options);\n return () => observer.disconnect();\n }, [map, enabled]);\n return theme;\n}\n\n/**\n * The classic-marker icon: the same round marker as an SVG in `theme`'s\n * colours. The count is the marker's label.\n */\nfunction clusterIcon(\n count: number,\n { fill, ink, ring, hover: edge, target }: ClusterIconTheme,\n): {\n icon: google.maps.Icon;\n label: google.maps.MarkerLabel;\n} {\n const width = Math.max(target, 20 + String(count).length * 12);\n // The ink edge (1 px outside the white ring) is drawn first, under the ring.\n const shape = `x=\"2\" y=\"2\" width=\"${width - 4}\" height=\"${target - 4}\" rx=\"${target / 2}\"`;\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${target}\">` +\n `<rect ${shape} fill=\"none\" stroke=\"${edge}\" stroke-width=\"4\"/>` +\n `<rect ${shape} fill=\"${fill}\" stroke=\"${ring}\" stroke-width=\"2\"/></svg>`;\n const maps = (globalThis as { google?: { maps?: typeof google.maps } }).google\n ?.maps;\n const icon = {\n url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,\n scaledSize: maps ? new maps.Size(width, target) : undefined,\n anchor: maps ? new maps.Point(width / 2, target / 2) : undefined,\n } as google.maps.Icon;\n return {\n icon,\n label: { text: String(count), color: ink, fontWeight: '600' },\n };\n}\n\nconst ClassicIconThemeContext = createContext<ClusterIconTheme | null>(null);\n\n/** Resolves the kit colours once for every classic marker below it. */\nfunction ClassicIconThemeProvider({\n enabled,\n children,\n}: {\n enabled: boolean;\n children: ReactNode;\n}): ReactNode {\n const theme = useClusterIconTheme(enabled);\n return (\n <ClassicIconThemeContext.Provider value={theme}>\n {children}\n </ClassicIconThemeContext.Provider>\n );\n}\n\n/**\n * The classic-marker icon (no `mapId`): the same pin as the live one, drawn as\n * a data-URL SVG in concrete colours, anchored at the tip. Hovered it is 1.4x.\n */\nfunction pinIcon(\n { fill, ring, hover, selected }: ClusterIconTheme,\n hovered: boolean,\n): google.maps.Icon {\n const scale = hovered ? MAP_PIN_HOVER_SCALE : 1;\n const width = MAP_PIN_WIDTH * scale;\n const height = MAP_PIN_HEIGHT * scale;\n const svg = mapPinSvg(hovered ? selected : fill, ring, hover, {\n width,\n height,\n });\n const maps = (globalThis as { google?: { maps?: typeof google.maps } }).google\n ?.maps;\n return {\n url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,\n scaledSize: maps ? new maps.Size(width, height) : undefined,\n anchor: maps ? new maps.Point(width / 2, height) : undefined,\n } as google.maps.Icon;\n}\n\nfunction KitClassicMarker({\n marker,\n label,\n isSelected,\n isHovered,\n onSelect,\n onHover,\n}: {\n marker: MapMarker;\n label: string;\n isSelected: boolean;\n isHovered: boolean;\n onSelect: (id: string) => void;\n onHover: MapViewProps['onMarkerHover'];\n}): ReactNode {\n const theme = useContext(ClassicIconThemeContext);\n const icon = useMemo(\n () => (theme ? pinIcon(theme, isHovered) : undefined),\n [theme, isHovered],\n );\n // Nothing until the colours are read: Google's red pin must not flash first.\n if (!icon) return null;\n return (\n <Marker\n position={marker.position}\n title={label}\n icon={icon}\n zIndex={isHovered ? 1000 : isSelected ? 500 : undefined}\n onClick={() => onSelect(marker.id)}\n onMouseOver={\n onHover ? () => onHover(marker.id, true, 'pointer') : undefined\n }\n onMouseOut={\n onHover ? () => onHover(marker.id, false, 'pointer') : undefined\n }\n />\n );\n}\n\ntype MarkerElements = MutableRefObject<Map<string, HTMLElement>>;\n\nfunction KitClusterMarker({\n cluster,\n label,\n mapId,\n iconTheme,\n elements,\n onActivate,\n}: {\n cluster: MarkerCluster<MapMarker>;\n label: string;\n mapId: boolean;\n iconTheme: ClusterIconTheme | null;\n elements: MarkerElements;\n onActivate: (cluster: MarkerCluster<MapMarker>) => void;\n}): ReactNode {\n const [element, setElement] =\n useState<google.maps.marker.AdvancedMarkerElement | null>(null);\n const key = `cluster:${cluster.id}`;\n useEffect(() => {\n const node = markerElement(element);\n if (!node) return undefined;\n const registry = elements.current;\n registry.set(key, node);\n // Only the kit ring on the badge shows (MapClusterMarker).\n node.style.outline = 'none';\n return () => {\n registry.delete(key);\n };\n }, [element, elements, key]);\n const count = cluster.members.length;\n const classic = useMemo(\n () => (mapId || !iconTheme ? null : clusterIcon(count, iconTheme)),\n [mapId, count, iconTheme],\n );\n if (mapId) {\n return (\n <AdvancedMarker\n ref={setElement}\n position={cluster.position}\n title={label}\n aria-label={label}\n zIndex={CLUSTER_Z_INDEX}\n anchorPoint={['50%', '50%']}\n onClick={() => onActivate(cluster)}\n >\n <MapClusterMarker count={count} />\n </AdvancedMarker>\n );\n }\n return (\n <Marker\n position={cluster.position}\n title={label}\n zIndex={CLUSTER_Z_INDEX}\n icon={classic?.icon}\n label={classic?.label}\n onClick={() => onActivate(cluster)}\n />\n );\n}\n\n/** The live region is emptied this long before it is filled again. */\nconst ANNOUNCE_DELAY_MS = 100;\n\n/** How long a focus move waits for Google to create the new markers. */\nconst FOCUS_FRAMES = 30;\n\ninterface PendingZoom {\n ids: Set<string>;\n centre: MapLatLng;\n}\n\n/**\n * Renders `markers` as single pins plus numbered clusters. The selected and\n * the hovered pin are taken out first, so neither can be hidden inside a\n * cluster.\n */\nfunction ClusteredMarkers({\n markers,\n fallbackZoom,\n selectedMarkerId,\n hoveredMarkerId,\n renderMarker,\n mapId,\n frame,\n elements,\n labelFor,\n onCameraMove,\n onClusterClick,\n}: {\n markers: MapMarker[];\n fallbackZoom: number;\n selectedMarkerId: string | null;\n hoveredMarkerId: string | null;\n renderMarker: (marker: MapMarker) => ReactNode;\n mapId: boolean;\n frame: HTMLElement | null;\n elements: MarkerElements;\n labelFor: (count: number) => string;\n onCameraMove: () => void;\n onClusterClick?: MapViewProps['onClusterClick'];\n}): ReactNode {\n const map = useMap();\n const { t } = useTranslation('ui');\n const zoom = useMapZoom(fallbackZoom);\n const iconTheme = useContext(ClassicIconThemeContext);\n // Grouped once per zoom, whatever is hovered or chosen, so a hover never\n // reshuffles the clusters around it.\n const grouped = useMemo(\n () => clusterMarkerSet(markers, zoom),\n [markers, zoom],\n );\n const { singles, clusters } = useMemo(() => {\n const out = (marker: MapMarker): boolean =>\n marker.id === selectedMarkerId || marker.id === hoveredMarkerId;\n const singles = grouped.singles.slice();\n const clusters: MarkerCluster<MapMarker>[] = [];\n for (const cluster of grouped.clusters) {\n const members = cluster.members.filter((marker) => !out(marker));\n // Pins taken out show on their own pin, where the cluster was.\n singles.push(...cluster.members.filter(out));\n if (members.length > 1) clusters.push({ ...cluster, members });\n else singles.push(...members);\n }\n return { singles, clusters };\n }, [grouped, selectedMarkerId, hoveredMarkerId]);\n\n const [announcement, setAnnouncement] = useState('');\n const pending = useRef<PendingZoom | null>(null);\n const announceTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n useEffect(() => () => clearTimeout(announceTimer.current), []);\n\n // Zoom to the cluster's pins in one step (never animated, so reduced\n // motion is honoured). It always zooms in at least one whole level, and\n // stops at the zoom where pins no longer cluster. The move is the kit's,\n // not a pan: the host hears about it from `onClusterClick`.\n const activate = (cluster: MarkerCluster<MapMarker>): void => {\n const div = map?.getDiv?.();\n if (!map || !(div instanceof Element)) return;\n const padding = Math.min(\n lengthTokenPx('--spacing-2xl', div, 48),\n div.clientWidth / 4,\n div.clientHeight / 4,\n );\n const fit = cameraForPoints(\n cluster.members.map((member) => member.position),\n div.clientWidth,\n div.clientHeight,\n padding,\n CLUSTER_OFF_ZOOM,\n );\n const zoomNow = map.getZoom() ?? zoom;\n const next = Math.min(\n CLUSTER_OFF_ZOOM,\n Math.max(fit.zoom, Math.ceil(zoomNow + 1)),\n );\n const count = cluster.members.length;\n pending.current = {\n ids: new Set(cluster.members.map((member) => member.id)),\n centre: cluster.position,\n };\n // Cleared first: a second identical message is read only if the region\n // changed in between.\n setAnnouncement('');\n clearTimeout(announceTimer.current);\n announceTimer.current = setTimeout(\n () =>\n setAnnouncement(t('mapView.clusterZoomed', { label: labelFor(count) })),\n ANNOUNCE_DELAY_MS,\n );\n onCameraMove();\n onClusterClick?.({\n count,\n markerIds: cluster.members.map((member) => member.id),\n });\n map.moveCamera({ center: fit.center, zoom: next });\n };\n\n // The activated cluster is gone after the zoom. Keyboard focus moves to the\n // new cluster nearest to it that holds some of its pins, else to one of its\n // pins, else (classic markers cannot take focus) to the map region.\n useEffect(() => {\n const zoomed = pending.current;\n if (!zoomed) return undefined;\n let frameId = 0;\n let tries = 0;\n const target = (): string | null => {\n let best: { key: string; distance: number } | null = null;\n for (const cluster of clusters) {\n if (!cluster.members.some((member) => zoomed.ids.has(member.id)))\n continue;\n const distance =\n (cluster.position.lat - zoomed.centre.lat) ** 2 +\n (cluster.position.lng - zoomed.centre.lng) ** 2;\n if (!best || distance < best.distance)\n best = { key: `cluster:${cluster.id}`, distance };\n }\n if (best) return best.key;\n const pin = singles.find((marker) => zoomed.ids.has(marker.id));\n return pin ? `pin:${pin.id}` : null;\n };\n const focus = (): void => {\n const key = target();\n const node = key ? elements.current.get(key) : null;\n if (node) {\n node.focus({ preventScroll: true });\n // A marker Google has not attached yet ignores focus: try again.\n if (node.contains(document.activeElement)) {\n pending.current = null;\n return;\n }\n }\n tries += 1;\n if (tries < FOCUS_FRAMES && mapId) {\n frameId = requestAnimationFrame(focus);\n return;\n }\n pending.current = null;\n const region = frame?.closest<HTMLElement>('[data-component=\"map-view\"]');\n if (region) {\n if (!region.hasAttribute('tabindex'))\n region.setAttribute('tabindex', '-1');\n region.focus({ preventScroll: true });\n }\n };\n frameId = requestAnimationFrame(focus);\n return () => cancelAnimationFrame(frameId);\n }, [clusters, singles, elements, frame, mapId]);\n\n return (\n <>\n {singles.map(renderMarker)}\n {clusters.map((cluster) => (\n <KitClusterMarker\n key={`${Math.floor(zoom)}:${cluster.id}`}\n cluster={cluster}\n label={labelFor(cluster.members.length)}\n mapId={mapId}\n iconTheme={iconTheme}\n elements={elements}\n onActivate={activate}\n />\n ))}\n {frame\n ? createPortal(\n <div role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {announcement}\n </div>,\n frame,\n )\n : null}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Library gates */\n/* */\n/* APIProvider reports LOADED as soon as `google.maps.importLibrary` */\n/* exists when another loader installed it (Google's bootstrap, a host */\n/* Places loader), before the `maps` library itself has arrived. <Map> */\n/* would then run `new google.maps.Map` and throw, taking the host's */\n/* React tree down. The map waits for `maps`, and the markers for */\n/* `marker`, whoever loaded the API. Before the API loads, <Map> only */\n/* renders its empty container, and once a constructor exists nothing */\n/* can throw, so both pass straight through as they always did. */\n/* ------------------------------------------------------------------ */\n\ntype GoogleMapsGlobal = {\n google?: { maps?: { Map?: unknown; Marker?: unknown } };\n};\nconst hasConstructor = (name: 'Map' | 'Marker'): boolean =>\n typeof (globalThis as GoogleMapsGlobal).google?.maps?.[name] === 'function';\n\n/** What the map area shows while Google's map library is on its way.\n * Exported for its story; not part of the public API. */\nexport function MapViewLoading(): ReactNode {\n const { t } = useTranslation('ui');\n return (\n <div\n role=\"status\"\n data-testid=\"map-view-loading\"\n className=\"ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <Skeleton variant=\"rectangular\" width=\"100%\" height=\"100%\" />\n <VisuallyHidden>{t('common.loading')}</VisuallyHidden>\n </div>\n );\n}\n\n/** The host's map image while Google's map is on its way (HTP-7348). */\nfunction MapPreviewLoading({\n preview,\n onError,\n}: {\n preview: MapViewStaticPreview;\n onError: () => void;\n}): ReactNode {\n const { t } = useTranslation('ui');\n return (\n <div\n data-testid=\"map-view-loading\"\n className=\"ds:relative ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <MapPreviewImage preview={preview} onError={onError} />\n {/* Only \"Loading\" is announced: the image stays out of the live region. */}\n <div role=\"status\">\n <VisuallyHidden>{t('common.loading')}</VisuallyHidden>\n </div>\n </div>\n );\n}\n\nfunction MapPreviewImage({\n preview,\n onError,\n}: {\n preview: MapViewStaticPreview;\n onError: () => void;\n}): ReactNode {\n const hasSources = (preview.sources?.length ?? 0) > 0;\n const areaRef = useRef<HTMLDivElement>(null);\n const picked = usePickedSourceIndex(preview.sources, areaRef);\n // The area box is measured first and the image only rendered after, so the\n // browser is never asked for a second picture.\n const [measured, setMeasured] = useState(!hasSources);\n useLayoutEffect(() => {\n setMeasured(true);\n }, []);\n const source =\n (picked !== null ? preview.sources?.[picked] : undefined) ?? preview;\n const image = (\n <img\n src={source.src}\n srcSet={source.srcSet}\n alt={preview.alt}\n width={source.width}\n height={source.height}\n loading=\"lazy\"\n decoding=\"async\"\n onError={onError}\n data-testid=\"map-view-preview-image\"\n // The whole image, never cropped: Google's logo and copyright line sit\n // at its edges and must stay visible. The muted frame shows round it.\n className=\"ds:block ds:[inline-size:100%] ds:[block-size:100%] ds:[object-fit:contain]\"\n />\n );\n if (!hasSources) return image;\n return (\n <div\n ref={areaRef}\n data-testid=\"map-view-preview-area\"\n className=\"ds:absolute ds:inset-0\"\n >\n {measured ? image : null}\n </div>\n );\n}\n\nfunction MapsLibraryGate({\n children,\n pending,\n holdUntilLoaded = false,\n}: {\n children: ReactNode;\n pending: ReactNode;\n /**\n * Keep `pending` up while the API itself loads too. With a host map image\n * (HTP-7348) the image stays until the map can draw, instead of an empty\n * frame.\n */\n holdUntilLoaded?: boolean;\n}): ReactNode {\n const apiIsLoaded = useApiIsLoaded();\n const maps = useMapsLibrary('maps');\n if (maps || hasConstructor('Map')) return children;\n if (!apiIsLoaded && !holdUntilLoaded) return children;\n return pending;\n}\n\nfunction MarkerLibraryGate({ children }: { children: ReactNode }): ReactNode {\n const marker = useMapsLibrary('marker');\n return marker || hasConstructor('Marker') ? children : null;\n}\n\n/**\n * On unmount, reports whether focus was inside `ref`. A layout effect's\n * cleanup runs before React removes the nodes, so the answer is exact.\n */\nfunction useReportFocusOnUnmount(\n ref: MutableRefObject<HTMLDivElement | null>,\n report: ((hadFocus: boolean) => void) | undefined,\n): void {\n useLayoutEffect(() => {\n const node = ref.current;\n return () => {\n report?.(Boolean(node && node.contains(document.activeElement)));\n };\n }, [ref, report]);\n}\n\n/* ------------------------------------------------------------------ */\n/* Static preview (HTP-7348) */\n/* */\n/* The host's own map image fills the frame, with \"Show interactive */\n/* map\" on it. Nothing here contacts Google. */\n/* ------------------------------------------------------------------ */\n\ninterface MapStaticPreviewProps {\n preview: MapViewStaticPreview;\n onLoadMap: () => void;\n consentCta: ReactNode | undefined;\n onImageError: () => void;\n /** The consent disclosure, as on the text placeholder. */\n description: ReactNode;\n /** \"Open in Google Maps\": a plain link, so it needs no consent. */\n openInGoogleMapsHref: string;\n onUnmount?: (hadFocus: boolean) => void;\n}\n\nfunction MapStaticPreview({\n preview,\n onLoadMap,\n consentCta,\n onImageError,\n description,\n openInGoogleMapsHref,\n onUnmount,\n}: MapStaticPreviewProps): ReactNode {\n const { t } = useTranslation('ui');\n const descriptionId = useId();\n const gateRef = useRef<HTMLDivElement>(null);\n useReportFocusOnUnmount(gateRef, onUnmount);\n return (\n <div\n ref={gateRef}\n className=\"ds:absolute ds:inset-0 ds:@container/mapbar ds:bg-[var(--muted)]\"\n data-testid=\"map-view-preview\"\n >\n <MapPreviewImage preview={preview} onError={onImageError} />\n {/* A slim frosted bar across the TOP: Google's logo and copyright line\n sit at the bottom of the image and must stay clear. A host can move\n it below its own floating controls with\n --map-preview-inset-block-start. */}\n <div className=\"ds:absolute ds:start-0 ds:end-0 ds:top-[var(--map-preview-inset-block-start,var(--spacing-sm))] ds:px-[var(--spacing-sm)]\">\n {/* The popover colour at about 96% over a blur (the kit's glass alpha, solid in the accessible themes), so the map shows round it and text\n keeps its contrast over a busy image. Solid under the accessible\n themes' token, more contrast, reduced transparency and forced\n colours. The disclosure says what the click does before the\n visitor makes it. Text and controls sit centred; in a wide map\n (640px and up) they share one centred line. */}\n <div\n data-testid=\"map-view-preview-plate\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:rounded-[var(--radius-md)] ds:bg-[color-mix(in_srgb,var(--popover)_calc(100%-(100%-var(--glass-surface-alpha,85%))*0.27),transparent)] ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-sm)] ds:backdrop-blur-md ds:contrast-more:bg-[var(--popover)] ds:[@media(prefers-reduced-transparency:reduce)]:bg-[var(--popover)] ds:forced-colors:bg-[Canvas] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--border)] ds:shadow-[var(--shadow-sm)] ds:items-center ds:text-center ds:@[640px]/mapbar:flex-row ds:@[640px]/mapbar:justify-center ds:@[640px]/mapbar:gap-[var(--spacing-md)]\"\n >\n <p\n id={descriptionId}\n className=\"type-meta ds:m-0 ds:min-w-0 ds:text-[var(--foreground)] ds:@[640px]/mapbar:text-start\"\n >\n {description}\n </p>\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:justify-center ds:gap-x-[var(--spacing-sm)] ds:gap-y-[var(--spacing-2xs)]\">\n {consentCta ?? (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"sm\"\n className=\"ds:min-h-[var(--min-target-size)] ds:sm:min-h-[var(--min-target-size)]\"\n onClick={onLoadMap}\n aria-describedby={descriptionId}\n data-testid=\"map-view-load-cta\"\n >\n {t('mapView.consentTitle')}\n </Button>\n )}\n <a\n href={openInGoogleMapsHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:inline-flex ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)] ds:items-center ds:justify-center ds:gap-[var(--spacing-2xs)] ds:rounded-[var(--radius-sm)] ds:text-[var(--primary-readable)] ds:underline ds:underline-offset-2 ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]\"\n >\n <span className=\"ds:sr-only ds:@[640px]/mapbar:not-sr-only\">\n {t('mapView.openInGoogleMaps')}\n </span>\n <ExternalLink aria-hidden=\"true\" className=\"ds:size-4\" />\n </a>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Consent placeholder */\n/* ------------------------------------------------------------------ */\n\ninterface ConsentPlaceholderProps {\n address: string | undefined;\n contentLang: string | undefined;\n description: ReactNode;\n /**\n * Click handler for the \"Load map\" button. Always rendered. When the\n * consumer provides `onConsentRequest` it owns the state transition\n * (typically: open a GDPR dialog, then flip `consentGranted` to true\n * after acceptance). When omitted, the kit's `MapView` falls back to\n * its own internal opt-in toggle — clicking grants consent for this\n * MapView instance only, no consumer plumbing required. This default\n * fixes a regression where the button was conditionally rendered and\n * any consumer omitting `onConsentRequest` left the patient with no\n * way to load the map (booking-website 0.31.0 hit this).\n */\n onLoadMap: () => void;\n openInGoogleMapsHref: string;\n /**\n * Custom CTA — replaces the kit-rendered `<Button>` when the consumer\n * needs a different surface (e.g. a Tooltip-wrapped button with a\n * policy link). The kit's button is still the recommended path; this\n * slot exists for consumers operating under specific compliance UX\n * requirements (jurisdiction, named controller, policy link). Pass\n * any `ReactNode`.\n */\n consentCta: ReactNode | undefined;\n onUnmount?: (hadFocus: boolean) => void;\n}\n\nfunction ConsentPlaceholder({\n address,\n contentLang,\n description,\n onLoadMap,\n openInGoogleMapsHref,\n consentCta,\n onUnmount,\n}: ConsentPlaceholderProps): ReactNode {\n const { t } = useTranslation('ui');\n const gateRef = useRef<HTMLDivElement>(null);\n useReportFocusOnUnmount(gateRef, onUnmount);\n return (\n <div\n ref={gateRef}\n className={consentChromeVariants()}\n data-testid=\"map-view-consent\"\n >\n <MapPin\n aria-hidden=\"true\"\n className=\"ds:size-6 ds:text-[var(--muted-foreground)]\"\n />\n <p className={consentTitleVariants()}>{t('mapView.consentTitle')}</p>\n {address ? (\n <p\n className={consentDescriptionVariants()}\n data-testid=\"map-view-address\"\n lang={contentLang}\n >\n {address}\n </p>\n ) : null}\n <p className={consentDescriptionVariants()}>{description}</p>\n <div\n className=\"ds:flex ds:gap-[var(--spacing-sm)] ds:flex-wrap ds:items-center ds:justify-center\"\n data-testid=\"map-view-consent-actions\"\n >\n {consentCta ?? (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"sm\"\n onClick={onLoadMap}\n data-testid=\"map-view-load-cta\"\n >\n {t('mapView.consentCta')}\n </Button>\n )}\n <a\n href={openInGoogleMapsHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] ds:text-[var(--primary-readable)] ds:underline ds:underline-offset-2\"\n >\n {t('mapView.openInGoogleMaps')}\n <ExternalLink aria-hidden=\"true\" className=\"ds:size-3\" />\n </a>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* MapView */\n/* ------------------------------------------------------------------ */\n\nexport const MapView = forwardRef<MapViewHandle, MapViewProps>(\n (\n {\n id,\n apiKey,\n center,\n zoom = 15,\n markers,\n selectedMarkerId,\n hoveredMarkerId,\n renderMarkerCard,\n onMarkerHover,\n onMarkerClick,\n onBoundsChange,\n consentGranted = false,\n onConsentRequest,\n staticPreview,\n address,\n contentLang,\n consentDescription,\n consentCta,\n mapId,\n size,\n surface,\n ariaLabel,\n fitToPoints,\n fitKey,\n fitMaxZoom = 15,\n clusterMarkers = 'auto',\n clusterLabel,\n onClusterClick,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n // Each pin's and cluster's element, so focus can be moved to one.\n const markerElements = useRef(new Map<string, HTMLElement>());\n const fitPoints = useMemo(() => fitToPoints ?? [], [fitToPoints]);\n // Identity and place: a result that keeps its id but moves (a\n // corrected address) still refits. \"Only grew\" uses identity alone.\n const resolvedFitKey = useMemo(\n () =>\n fitKey ??\n fitPoints\n .map((point) => `${pointKey(point)}@${point.lat},${point.lng}`)\n .sort()\n .join('|'),\n [fitKey, fitPoints],\n );\n // While there are points, the fit owns the camera (see `fitToPoints`).\n const fitting = fitPoints.length > 0;\n const wasFitting = useRef(fitting);\n const rawId = useId();\n const instanceId = useMemo(\n () => `map-${rawId.replace(/[^a-zA-Z0-9-_]/g, '')}`,\n [rawId],\n );\n\n // Reduced-motion gating. Google's own pan / zoom animations\n // happen inside the Maps JS API and the kit can't reach into\n // those — but the kit's own programmatic `panTo` calls (triggered\n // by prop changes via the controlled-sync effect below) can be\n // suppressed. When `prefers-reduced-motion: reduce` is set, we\n // skip the synthetic `panTo` so the consumer's prop change\n // results in a hard recenter rather than an animated glide.\n const [reducedMotion, setReducedMotion] = useState(false);\n useEffect(() => {\n if (typeof window === 'undefined') return undefined;\n const mql = window.matchMedia('(prefers-reduced-motion: reduce)');\n const sync = (): void => setReducedMotion(mql.matches);\n sync();\n mql.addEventListener('change', sync);\n return () => mql.removeEventListener('change', sync);\n }, []);\n\n // The \"Open in Google Maps\" fallback link — works without any\n // third-party script load. Uses the modern `?api=1&query=…` form\n // which the booking site already employs.\n const openInGoogleMapsHref = useMemo(() => {\n const query = address\n ? encodeURIComponent(address)\n : `${center.lat},${center.lng}`;\n return `https://www.google.com/maps/search/?api=1&query=${query}`;\n }, [address, center.lat, center.lng]);\n\n // 0.33.5: removed the kit's `getGoogleMapsTheme()` JSON-styles\n // override. Previously, when `mapId` was absent the kit painted\n // water / roads / landscape with kit tokens — a calm muted-grey\n // palette that diverged from the standard Google Maps appearance\n // users expect in every other maps surface they touch. Google's\n // default (blue water, beige land, named-road colours) is more\n // familiar and reads as a \"real map\" rather than a stylised one.\n //\n // Consumers wanting brand-themed colours opt in via `mapId` and\n // configure the palette in Google Cloud Console — that's the\n // Google-recommended path. The `getGoogleMapsTheme` export stays\n // in `src/tokens/google-maps-theme.ts` so consumers wanting the\n // old behaviour can apply the styles themselves, but the kit\n // does NOT install them by default any more.\n //\n // Side benefit: dropped the MutationObserver that previously\n // watched documentElement.class for theme switches — no more\n // observer churn on every theme toggle.\n\n // Imperative handle plumbing — populated by the inner bridge once\n // the APIProvider tree resolves a `Map` instance.\n const handleRef = useRef<MapBridgeHandle | null>(null);\n const userInteraction = useRef<MapInteraction>({\n pendingInput: false,\n moved: false,\n programmatic: false,\n });\n const onCameraMove = useCallback(() => {\n userInteraction.current = {\n pendingInput: false,\n moved: false,\n programmatic: true,\n };\n }, []);\n const handleMarkerClick = useCallback(\n (markerId: string | null) => {\n onCameraMove();\n onMarkerClick?.(markerId);\n },\n [onCameraMove, onMarkerClick],\n );\n // The map frame, so the hovered pin's card can be drawn into it.\n const [frame, setFrame] = useState<HTMLDivElement | null>(null);\n const hoveredMarker = useMemo(\n () =>\n hoveredMarkerId != null && renderMarkerCard\n ? ((markers ?? []).find((marker) => marker.id === hoveredMarkerId) ??\n null)\n : null,\n [hoveredMarkerId, renderMarkerCard, markers],\n );\n const onHandleReady = useCallback((handle: MapBridgeHandle | null) => {\n handleRef.current = handle;\n }, []);\n\n // Read in the OUTER component, not through `handleRef` — these three\n // must still answer when the map subtree is unmounted, which is\n // exactly the situation an agent needs explaining. Assigned during\n // render, below, once `effectiveConsent` exists.\n const consentRef = useRef(false);\n const selectedMarkerIdRef = useRef<string | null>(null);\n const markerCountRef = useRef(0);\n\n const buildHandle = useCallback(\n (): MapViewHandle => ({\n panTo: (position) => handleRef.current?.panTo(position),\n setZoom: (z) => handleRef.current?.setZoom(z),\n selectMarker: (markerId) => handleRef.current?.selectMarker(markerId),\n // Without `isConsentGranted` an agent has no way to learn that\n // its pan / zoom / select was swallowed by the GDPR gate rather\n // than applied: under the default (`consentGranted={false}`) the\n // whole Maps subtree is unmounted and every action optional-chains\n // into nothing.\n isConsentGranted: () => consentRef.current,\n getCenter: () => handleRef.current?.getCenter() ?? null,\n getZoom: () => handleRef.current?.getZoom() ?? null,\n getSelectedMarkerId: () => selectedMarkerIdRef.current,\n getMarkerCount: () => markerCountRef.current,\n }),\n [],\n );\n useImperativeHandle(ref, buildHandle, [buildHandle]);\n\n const agentHandle = useMemo<MapViewHandle>(\n () => buildHandle(),\n [buildHandle],\n );\n useAgentRegistration(mapViewAgent, agentHandle, id);\n\n // Controlled-feeling propagation: vis.gl's `<Map>` reads `center`\n // and `zoom` as initial values only (via `defaultCenter` /\n // `defaultZoom`). To honour the documented \"live prop\" contract\n // we shim post-mount sync through the imperative bridge — a\n // parent that swaps offices by changing `center` re-centres the\n // map. User drag does NOT trigger this because the prop doesn't\n // change on user gesture, so the effect doesn't fire and the\n // map stays where the user left it.\n //\n // While fitting, the fit owns the camera and both syncs stand down.\n // `fitting` is a dependency, so when fitting ends (the points run out\n // or `fitToPoints` is removed) the camera returns to `center` / `zoom`\n // even if neither changed. That return is one step, not animated, so\n // it applies under reduced motion too.\n useEffect(() => {\n const fitEnded = wasFitting.current && !fitting;\n wasFitting.current = fitting;\n if (fitting) return;\n if (fitEnded) {\n handleRef.current?.jumpTo(center, zoom);\n return;\n }\n // Skip the animated pan under reduced-motion preference; the\n // map will still recentre on the next render via the agent\n // handle's `panTo`, but Google's own glide animation runs\n // either way (kit can't reach inside it). At least the kit's\n // own initiated pan is gated.\n if (reducedMotion) return;\n handleRef.current?.panTo(center);\n // Including `center` (the object reference) instead of\n // `center.lat`/`center.lng` would fire on every parent render\n // even when the coordinates are identical, fighting user drag.\n // `zoom` is read only when fitting ends; its own sync is below.\n // The destructured deps are intentional.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [center.lat, center.lng, reducedMotion, fitting]);\n useEffect(() => {\n if (fitting) return;\n handleRef.current?.setZoom(zoom);\n }, [zoom, fitting]);\n\n // Internal opt-in fallback. The `consentGranted` prop is the\n // controlled path — consumer flips it after capturing GDPR\n // acceptance through their own dialog and re-renders. When the\n // consumer omits `onConsentRequest` they're opting out of that\n // workflow, so the kit needs a sensible default click handler on\n // the placeholder's button. `internalConsent` tracks that opt-in\n // for this instance only; the map renders when either signal is\n // truthy. The 0.31.0 regression was conditionally rendering the\n // button on `onConsentRequest` — patients of consumers without the\n // hook saw the placeholder with no way to proceed.\n const [internalConsent, setInternalConsent] = useState(false);\n const effectiveConsent = consentGranted || internalConsent;\n // Mirror the three props-derived agent state slots. Assigned during\n // render so a read immediately after a prop change sees the settled\n // value. Ids and a count only — never `MapMarker.label`, never the\n // `address` prop (consumer-supplied free text, PII in any\n // patient-address deployment), never `apiKey`.\n consentRef.current = effectiveConsent;\n selectedMarkerIdRef.current = selectedMarkerId ?? null;\n markerCountRef.current = (markers ?? []).length;\n // HTP-7348: the preview image falls back to the text placeholder when it\n // cannot load. The failure belongs to that address: a new image gets a\n // fresh try.\n const [failedSrc, setFailedSrc] = useState<string | null>(null);\n const preview =\n staticPreview && staticPreview.src !== failedSrc\n ? staticPreview\n : undefined;\n const previewSrc = staticPreview?.src;\n const onPreviewError = useCallback(() => {\n if (previewSrc) setFailedSrc(previewSrc);\n }, [previewSrc]);\n // When consent replaces the button the visitor used (the kit's, or a\n // host `consentCta`), focus would fall to the page. It moves to the map\n // region instead, as soon as consent flips.\n const rootRef = useRef<HTMLDivElement>(null);\n const focusOnLoad = useRef(false);\n // Set as the gate (preview or text placeholder) unmounts, before React\n // removes it: whether focus was inside it at that moment. A grant from\n // elsewhere (the cookie banner) leaves it false, so focus stays put.\n const focusWasInGate = useRef(false);\n const onGateUnmount = useCallback((hadFocus: boolean) => {\n focusWasInGate.current = hadFocus;\n }, []);\n useEffect(() => {\n if (!effectiveConsent) return;\n const active = document.activeElement;\n const focusLost = !active || active === document.body;\n const move = focusOnLoad.current || (focusWasInGate.current && focusLost);\n focusOnLoad.current = false;\n focusWasInGate.current = false;\n if (move) rootRef.current?.focus({ preventScroll: true });\n }, [effectiveConsent]);\n const handleLoadMap = useCallback(() => {\n focusOnLoad.current = true;\n if (onConsentRequest) {\n onConsentRequest();\n // The consumer typically flips `consentGranted` to true on the\n // next render. Flip the internal state too so the map still\n // loads if the consumer forgets to update its prop (the\n // common-case footgun this fallback exists to defuse).\n setInternalConsent(true);\n return;\n }\n setInternalConsent(true);\n }, [onConsentRequest]);\n\n const renderMarker = (marker: MapMarker): ReactNode => {\n // Selection state is exposed to the consumer via\n // `data-selected` on the marker root so consumers can\n // style their own InfoWindow / sidebar highlight from\n // it. The kit does NOT swap the Pin's fill colour on\n // selection — `--accent` (magenta-500 in light theme)\n // fails AA on white per 05-accessibility.mdx, and the\n // alternative tokens that pass (`--info`, `--success`)\n // carry semantic baggage. Visual selection state is\n // the consumer's call.\n const isSelected =\n selectedMarkerId != null && selectedMarkerId === marker.id;\n const isHovered =\n hoveredMarkerId != null && hoveredMarkerId === marker.id;\n const accessibleLabel = t('mapView.markerLabel', {\n label: marker.label ?? marker.id,\n });\n // Pin implementation switches on whether the\n // consumer supplied `mapId`. Google's\n // AdvancedMarkerElement requires the Map instance to\n // be initialised with a valid Map ID — without one\n // the API logs an error (\"La mappa è stata\n // inizializzata senza un ID mappa valido\") and any\n // AdvancedMarker rendered against that map fails to\n // appear. Cloud Map IDs are an opt-in feature; many\n // consumers (booking-website env without\n // GOOGLE_MAPS_MAP_ID, the OSS demo, etc.) legitimately\n // omit it.\n //\n // Fall back to the classic <Marker> (a thin wrapper\n // around google.maps.Marker) when mapId is absent.\n // Google has formally deprecated google.maps.Marker\n // but it still renders cleanly and produces no\n // console errors; the kit pin is drawn as the\n // marker's icon (a data-URL SVG, see pinIcon).\n if (mapId) {\n return (\n <KitAdvancedMarker\n key={marker.id}\n marker={marker}\n label={accessibleLabel}\n isSelected={isSelected}\n isHovered={isHovered}\n onSelect={handleMarkerClick}\n onHover={onMarkerHover}\n elements={markerElements}\n />\n );\n }\n return (\n <KitClassicMarker\n key={marker.id}\n marker={marker}\n label={accessibleLabel}\n isSelected={isSelected}\n isHovered={isHovered}\n onSelect={handleMarkerClick}\n onHover={onMarkerHover}\n />\n );\n };\n\n const clusterLabelFor = useCallback(\n (count: number): string =>\n typeof clusterLabel === 'function'\n ? clusterLabel(count)\n : typeof clusterLabel === 'string'\n ? clusterLabel.replaceAll('{count}', String(count))\n : t('mapView.clusterLabel', { count }),\n [clusterLabel, t],\n );\n const clustering = shouldCluster(clusterMarkers, (markers ?? []).length);\n\n return (\n <div\n ref={rootRef}\n className={mapViewVariants({ size, surface, className })}\n data-component=\"map-view\"\n data-component-id={id ?? instanceId}\n role=\"region\"\n aria-label={ariaLabel ?? t('mapView.label')}\n // Focus target once the interactive map replaces its button; not a\n // tab stop.\n tabIndex={-1}\n {...rest}\n >\n <div\n ref={setFrame}\n className={innerSurfaceVariants({ size, className: 'ds:relative' })}\n >\n {effectiveConsent ? (\n // A provider failure shows the consent placeholder (its \"Open in\n // Google Maps\" link needs no script) with \"Load map\" as the\n // retry, instead of unmounting the host's React tree. The gate\n // starts the provider only once Google's `core` and `maps`\n // libraries have arrived.\n <ErrorBoundary\n fallback={({ reset }) => (\n <ConsentPlaceholder\n address={address}\n contentLang={contentLang}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n onLoadMap={reset}\n // Always the kit button: a host's own CTA would not call\n // `reset`, so with consent already granted it would do\n // nothing and the patient could not retry.\n consentCta={undefined}\n openInGoogleMapsHref={openInGoogleMapsHref}\n />\n )}\n >\n <MapsCoreGate\n libraries={['maps']}\n pending={\n preview ? (\n <MapPreviewLoading\n preview={preview}\n onError={onPreviewError}\n />\n ) : (\n <MapViewLoading />\n )\n }\n >\n <APIProvider\n apiKey={apiKey}\n // Pull the active i18next locale through so map UI text\n // (zoom controls, attribution) localises to match the\n // surrounding kit chrome.\n language={i18n.language}\n >\n <MapsLibraryGate\n holdUntilLoaded={Boolean(preview)}\n pending={\n preview ? (\n <MapPreviewLoading\n preview={preview}\n onError={onPreviewError}\n />\n ) : (\n <MapViewLoading />\n )\n }\n >\n <GoogleMap\n defaultCenter={center}\n defaultZoom={zoom}\n mapId={mapId}\n gestureHandling=\"cooperative\"\n disableDefaultUI={false}\n onClick={() => handleMarkerClick(null)}\n // `<Map>` reads its own bounding rect for tile sizing.\n // Without explicit dimensions it collapses to 0×0, so\n // we pass kit-class sizing through `className` — vis.gl\n // forwards `className` straight to the rendered root.\n className=\"ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <MapImperativeBridge\n onHandleReady={onHandleReady}\n onMarkerSelect={handleMarkerClick}\n onCameraMove={onCameraMove}\n />\n {onBoundsChange ? (\n <MapBoundsBridge\n onBoundsChange={onBoundsChange}\n userInteraction={userInteraction}\n />\n ) : null}\n {fitToPoints ? (\n <MapFitter\n points={fitPoints}\n fitKey={resolvedFitKey}\n maxZoom={fitMaxZoom}\n onCameraMove={onCameraMove}\n />\n ) : null}\n {hoveredMarker && renderMarkerCard ? (\n <MapMarkerCard\n marker={hoveredMarker}\n frame={frame}\n direction={i18n.dir()}\n >\n {renderMarkerCard(hoveredMarker.id)}\n </MapMarkerCard>\n ) : null}\n <MarkerLibraryGate>\n <ClassicIconThemeProvider enabled={!mapId}>\n {clustering ? (\n <ClusteredMarkers\n markers={markers ?? []}\n fallbackZoom={zoom}\n selectedMarkerId={selectedMarkerId ?? null}\n hoveredMarkerId={hoveredMarkerId ?? null}\n renderMarker={renderMarker}\n mapId={Boolean(mapId)}\n frame={frame}\n elements={markerElements}\n labelFor={clusterLabelFor}\n onCameraMove={onCameraMove}\n onClusterClick={onClusterClick}\n />\n ) : (\n (markers ?? []).map(renderMarker)\n )}\n </ClassicIconThemeProvider>\n </MarkerLibraryGate>\n </GoogleMap>\n </MapsLibraryGate>\n </APIProvider>\n </MapsCoreGate>\n </ErrorBoundary>\n ) : preview ? (\n <MapStaticPreview\n preview={preview}\n onLoadMap={handleLoadMap}\n consentCta={consentCta}\n onImageError={onPreviewError}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n openInGoogleMapsHref={openInGoogleMapsHref}\n onUnmount={onGateUnmount}\n />\n ) : (\n <ConsentPlaceholder\n address={address}\n contentLang={contentLang}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n onLoadMap={handleLoadMap}\n consentCta={consentCta}\n openInGoogleMapsHref={openInGoogleMapsHref}\n onUnmount={onGateUnmount}\n />\n )}\n </div>\n </div>\n );\n },\n);\nMapView.displayName = 'MapView';\n"],"names":["mapViewAgent","handle","args","MAP_PIN_WIDTH","MAP_PIN_HEIGHT","MAP_PIN_BODY_PATH","MAP_PIN_RING_WIDTH","MAP_PIN_EDGE_WIDTH","MAP_PIN_MARK_TRANSFORM","MAP_PIN_MARK_SHAPES","attrString","attrs","key","value","mapPinSvg","fill","ring","ink","size","mark","tag","MAP_PIN_HOVER_SCALE","FOCUS_RING","MapViewPin","hovered","scale","body","jsx","jsxs","index","CLUSTER_AUTO_THRESHOLD","CLUSTER_RADIUS_PX","CLUSTER_OFF_ZOOM","WORLD_PX","toRadians","degrees","project","lat","lng","zoom","world","sin","mercator","shouldCluster","setting","markerCount","clusterMarkerSet","markers","radiusPx","projected","marker","b","buckets","grid","cellOf","radiusSquared","point","cx","cy","best","bestDistance","dx","dy","bucket","distance","n","before","after","old","next","list","singles","clusters","isFocusVisible","target","PREVIEW_SWITCH_MARGIN","closestSourceIndex","logRatios","area","i","usePickedSourceIndex","sources","ref","setIndex","useState","shapeKey","useLayoutEffect","node","k","w","h","r","measure","initial","width","height","current","observer","mapViewVariants","cva","innerSurfaceVariants","consentChromeVariants","consentTitleVariants","consentDescriptionVariants","MapImperativeBridge","onHandleReady","onMarkerSelect","onCameraMove","map","useMap","useEffect","position","center","id","_a","MapBoundsBridge","onBoundsChange","userInteraction","callback","useRef","div","previous","resetInteraction","inputExpiry","touchPointers","markInput","event","fullscreen","fullscreenMap","cameraInput","markCameraChange","report","bounds","_b","fromInput","changed","moved","inputEvents","listeners","listener","useMarkerAnchor","anchor","setAnchor","mercatorY","MapMarkerCard","frame","direction","children","createPortal","MapMarkerCardLayer","CARD_PIN_CLEARANCE_PX","CARD_PIN_BELOW_PX","CARD_EDGE_PX","cardRef","setSize","prev","maxLeft","left","above","flipped","top","markerElement","element","KitAdvancedMarker","label","isSelected","isHovered","onSelect","onHover","elements","setElement","pointerInside","registry","focusIn","focusOut","keyDown","AdvancedMarker","active","latitudeOf","y","boundsOfPoints","points","north","south","east","west","cameraForPoints","padding","maxZoom","bottom","fit","px","fraction","pointKey","MapFitter","fitKey","fitted","userMoved","ownCamera","check","own","last","keys","moveCamera","lengthTokenPx","laidOut","CLUSTER_Z_INDEX","useMapZoom","fallback","setZoom","read","CLUSTER_BADGE_CLASS","MapClusterMarker","count","resolveColorToken","token","context","probe","color","useClusterIconTheme","enabled","theme","setTheme","options","themeRoot","clusterIcon","edge","shape","svg","maps","ClassicIconThemeContext","createContext","ClassicIconThemeProvider","pinIcon","hover","selected","KitClassicMarker","useContext","icon","useMemo","Marker","KitClusterMarker","cluster","mapId","iconTheme","onActivate","classic","ANNOUNCE_DELAY_MS","FOCUS_FRAMES","ClusteredMarkers","fallbackZoom","selectedMarkerId","hoveredMarkerId","renderMarker","labelFor","onClusterClick","t","useTranslation","grouped","out","members","announcement","setAnnouncement","pending","announceTimer","activate","member","zoomNow","zoomed","frameId","tries","pin","focus","region","Fragment","hasConstructor","name","MapViewLoading","Skeleton","VisuallyHidden","MapPreviewLoading","preview","onError","MapPreviewImage","hasSources","areaRef","picked","measured","setMeasured","source","image","MapsLibraryGate","holdUntilLoaded","apiIsLoaded","useApiIsLoaded","useMapsLibrary","MarkerLibraryGate","useReportFocusOnUnmount","MapStaticPreview","onLoadMap","consentCta","onImageError","description","openInGoogleMapsHref","onUnmount","descriptionId","useId","gateRef","Button","ExternalLink","ConsentPlaceholder","address","contentLang","MapPin","MapView","forwardRef","apiKey","renderMarkerCard","onMarkerHover","onMarkerClick","consentGranted","onConsentRequest","staticPreview","consentDescription","surface","ariaLabel","fitToPoints","fitMaxZoom","clusterMarkers","clusterLabel","className","rest","i18n","markerElements","fitPoints","resolvedFitKey","fitting","wasFitting","rawId","instanceId","reducedMotion","setReducedMotion","mql","sync","handleRef","useCallback","handleMarkerClick","markerId","setFrame","hoveredMarker","consentRef","selectedMarkerIdRef","markerCountRef","buildHandle","z","useImperativeHandle","agentHandle","useAgentRegistration","fitEnded","internalConsent","setInternalConsent","effectiveConsent","failedSrc","setFailedSrc","previewSrc","onPreviewError","rootRef","focusOnLoad","focusWasInGate","onGateUnmount","hadFocus","focusLost","move","handleLoadMap","accessibleLabel","clusterLabelFor","clustering","ErrorBoundary","reset","MapsCoreGate","APIProvider","GoogleMap"],"mappings":"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"}
1
+ {"version":3,"file":"map-view-DkL58SRp.js","sources":["../../src/components/map-view/map-view.agent.ts","../../src/components/map-view/map-view-pin-geometry.ts","../../src/components/map-view/map-view-pin.tsx","../../src/components/map-view/map-view-cluster.ts","../../src/components/_shared/focus-visible.ts","../../src/components/map-view/map-view.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — MapView. */\n/* */\n/* Read-safety actions on the kit's Google Maps wrapper. State is the */\n/* current centre / zoom / selected marker; actions pan, zoom, and */\n/* select markers programmatically. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { MapViewHandle, MapLatLng } from './map-view';\n\nexport const mapViewAgent: AgentAdapter<MapViewHandle> = {\n id: 'map-view',\n // pan → scroll (viewport move); set_zoom → set_value; select_marker → pick\n // (NON-persisting marker focus, no value state slot).\n capabilities: ['scroll', 'set_value', 'pick'],\n // Every action routes through the imperative bridge, which only exists\n // once the map has loaded: consent granted (the default is\n // `consentGranted={false}`) and Google Maps finished loading. Until then\n // all three actions are silent no-ops. `consentGranted` says whether the\n // gate is passed, and a null `center` after it means the map is still\n // loading. `center` / `zoom` are read off the live\n // map because Google clamps zoom and animates pans, so requested is not\n // applied; `selectedMarkerId` is the only confirmation the host accepted\n // a `select_marker` proposal.\n //\n // A9: marker ids only (`MapMarker.id` is a stable opaque id). Never\n // `MapMarker.label`, never the `address` prop (consumer-supplied free\n // text, PII in any patient-address deployment), never `apiKey`.\n state: {\n consentGranted: {\n type: 'boolean',\n descriptionKey: 'ui.agent.mapView.state.consentGranted',\n description:\n 'True when the GDPR consent gate has been passed. Read this FIRST: while it is false every action is a silent no-op against a placeholder, and nothing else in the surface says so. When it is true but `center` is still null, Google Maps is loading; wait for `center` before acting.',\n read: (handle) => handle.isConsentGranted(),\n },\n markerCount: {\n type: 'number',\n descriptionKey: 'ui.agent.mapView.state.markerCount',\n description:\n 'Number of markers the consumer passed. Zero means there is nothing for `select_marker` to select. A count only — no ids, labels or positions.',\n read: (handle) => handle.getMarkerCount(),\n },\n selectedMarkerId: {\n type: 'string | null',\n descriptionKey: 'ui.agent.mapView.state.selectedMarkerId',\n description:\n 'Id of the marker the host currently holds as selected, or null. Selection is fully controlled by the consumer, so `select_marker` only proposes — this slot is the only way to see whether the proposal was accepted.',\n read: (handle) => handle.getSelectedMarkerId(),\n },\n center: {\n type: '{ lat: number; lng: number } | null',\n descriptionKey: 'ui.agent.mapView.state.center',\n description:\n 'Live map centre in lat/lng, or null until the map has loaded (consent not granted, or Google Maps still loading). The read-back for `pan_to`, and the sign that the map is ready.',\n read: (handle) => handle.getCenter(),\n },\n zoom: {\n type: 'number | null',\n descriptionKey: 'ui.agent.mapView.state.zoom',\n description:\n \"Live zoom level, or null until the map has loaded. Google clamps zoom to the map type's range, so this is not necessarily the value last passed to `set_zoom`.\",\n read: (handle) => handle.getZoom(),\n },\n },\n actions: {\n pan_to: {\n // Ephemeral viewport move, no data loss → read.\n safety: 'read',\n argsType: '{ position: { lat: number; lng: number } }',\n argsSchema: {\n type: 'object',\n properties: {\n position: {\n type: 'object',\n properties: {\n lat: { type: 'number', description: 'Latitude in degrees.' },\n lng: { type: 'number', description: 'Longitude in degrees.' },\n },\n required: ['lat', 'lng'],\n },\n },\n required: ['position'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.panTo',\n description:\n 'Pan the map to a new centre without changing zoom. Read the `center` slot back to confirm — Google animates the pan, so the applied centre settles after the call.',\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n ],\n invoke: (handle, args: { position: MapLatLng }) => {\n handle.panTo(args.position);\n },\n },\n set_zoom: {\n // Ephemeral viewport zoom, no data loss → read.\n safety: 'read',\n argsType: '{ zoom: number }',\n argsSchema: {\n type: 'object',\n properties: {\n zoom: {\n type: 'number',\n description: 'Zoom level, typical range 1–20.',\n },\n },\n required: ['zoom'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.setZoom',\n description:\n 'Set the map zoom level (typical range 1–20). Read the `zoom` slot back — Google clamps the request to the map type’s supported range, so requested and applied can differ.',\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n \"Requested zoom is clamped to the map type's range; `zoom` reports what was actually applied.\",\n ],\n invoke: (handle, args: { zoom: number }) => {\n handle.setZoom(args.zoom);\n },\n },\n select_marker: {\n // Ephemeral marker focus, no data loss → read.\n safety: 'read',\n argsType: '{ id: string | null }',\n argsSchema: {\n type: 'object',\n properties: {\n id: {\n type: ['string', 'null'],\n description: 'Marker id to select, or null to clear the selection.',\n },\n },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.mapView.actions.selectMarker',\n description:\n \"Propose a marker selection by id, or pass null to clear it. Fires the host's `onMarkerClick`; selection is controlled by the host, so read the `selectedMarkerId` slot back to see whether it accepted.\",\n errors: [\n 'Silent no-op until the map has loaded: while `consentGranted` is false (only the consent placeholder shows) or while `center` is still null (Google Maps is loading).',\n 'The host owns `selectedMarkerId` and may ignore the proposal; `selectedMarkerId` is unchanged when it does.',\n ],\n invoke: (handle, args: { id: string | null }) => {\n handle.selectMarker(args.id);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'map-view',\n description: 'Marks the element as a kit MapView.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","/**\n * The geometry of the MapView pin, shared by the live pin (map-view-pin.tsx)\n * and the static PNG for Google Static Maps (scripts/build-map-pin-png.mjs),\n * so the two cannot drift. Plain data and strings only, no imports: Node runs\n * it directly.\n */\n\n/** CSS pixel size of the live pin. The tip is at the bottom centre. */\nexport const MAP_PIN_WIDTH = 32;\nexport const MAP_PIN_HEIGHT = 40;\n\n/**\n * Teardrop: round head (centre 16,16, radius 14), point at 16,38. The white ring\n * is 2 wide on this line and the ink edge 1 more outside it, so the drawing\n * fills the 32 x 40 box exactly.\n */\nexport const MAP_PIN_BODY_PATH =\n 'M16 38C16 38 2 26 2 16A14 14 0 0 1 30 16C30 26 16 38 16 38Z';\n\n/** White ring width, in the same units as the viewBox. */\nexport const MAP_PIN_RING_WIDTH = 2;\n\n/**\n * The ink edge is drawn under the ring, centred on the same line: 4 wide, so\n * 1 shows outside the ring. It carries the 3:1 on light tiles; the ring\n * carries it on dark ones.\n */\nexport const MAP_PIN_EDGE_WIDTH = 4;\n\n/**\n * The four-pillar Alfadocs mark (reference/assets/logos/SVG/Alfadocs_Logo_Mark.svg,\n * viewBox 148) at 15.5 units: about 55% of the 28-wide head, centred on it.\n */\nexport const MAP_PIN_MARK_TRANSFORM = 'translate(8.25 8.25) scale(0.10473)';\n\nexport interface MapPinMarkShape {\n tag: 'rect' | 'path';\n attrs: Record<string, string>;\n}\n\nexport const MAP_PIN_MARK_SHAPES: MapPinMarkShape[] = [\n {\n tag: 'rect',\n attrs: {\n x: '53.04',\n y: '2.54',\n width: '33.99',\n height: '49.34',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-4.66 19.05) rotate(-15)',\n },\n },\n {\n tag: 'rect',\n attrs: {\n x: '96.12',\n y: '53.04',\n width: '49.34',\n height: '33.99',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-14.01 33.65) rotate(-15)',\n },\n },\n {\n tag: 'path',\n attrs: {\n d: 'M47.82,59.56c-.65-2.43-3.15-3.87-5.58-3.22L3.38,66.75c-2.43.65-3.87,3.15-3.22,5.58l6.44,24.03c.65,2.43,3.15,3.87,5.58,3.22l38.86-10.41c2.43-.65,3.87-3.15,3.22-5.58l-6.44-24.03Z',\n },\n },\n {\n tag: 'rect',\n attrs: {\n x: '60.97',\n y: '96.12',\n width: '33.99',\n height: '49.34',\n rx: '4.56',\n ry: '4.56',\n transform: 'translate(-28.61 24.29) rotate(-15)',\n },\n },\n];\n\nconst attrString = (attrs: Record<string, string>): string =>\n Object.entries(attrs)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ');\n\n/** The pin as a standalone SVG string, in concrete colours (no CSS variables). */\nexport function mapPinSvg(\n fill: string,\n ring: string,\n ink: string,\n size: { width: number; height: number } = {\n width: MAP_PIN_WIDTH,\n height: MAP_PIN_HEIGHT,\n },\n): string {\n const mark = MAP_PIN_MARK_SHAPES.map(\n ({ tag, attrs }) => `<${tag} ${attrString(attrs)}/>`,\n ).join('');\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size.width}\" height=\"${size.height}\" ` +\n `viewBox=\"0 0 ${MAP_PIN_WIDTH} ${MAP_PIN_HEIGHT}\">` +\n `<path d=\"${MAP_PIN_BODY_PATH}\" fill=\"none\" stroke=\"${ink}\" ` +\n `stroke-width=\"${MAP_PIN_EDGE_WIDTH}\" stroke-linejoin=\"round\"/>` +\n `<path d=\"${MAP_PIN_BODY_PATH}\" fill=\"${fill}\" stroke=\"${ring}\" ` +\n `stroke-width=\"${MAP_PIN_RING_WIDTH}\" stroke-linejoin=\"round\"/>` +\n `<g fill=\"${ring}\" transform=\"${MAP_PIN_MARK_TRANSFORM}\">${mark}</g></svg>`\n );\n}\n","import type { ReactNode } from 'react';\nimport {\n MAP_PIN_BODY_PATH,\n MAP_PIN_EDGE_WIDTH,\n MAP_PIN_HEIGHT,\n MAP_PIN_MARK_SHAPES,\n MAP_PIN_MARK_TRANSFORM,\n MAP_PIN_RING_WIDTH,\n MAP_PIN_WIDTH,\n} from './map-view-pin-geometry';\n\n/** Hovered pin scale. The pin is resized, not transformed, so its box, hit area and focus ring grow with it. */\nexport const MAP_PIN_HOVER_SCALE = 1.4;\n\n/*\n * Google's marker element takes the focus; the ring is drawn on the pin and the\n * element's own outline is removed (KitAdvancedMarker). Deep ink, not `--ring`,\n * which is light in the dark themes: the tiles do not follow the kit theme.\n * `--focus-ring-width` is thicker in the accessible themes, `CanvasText` in\n * forced colours. Same ring as the cluster badge.\n */\nconst FOCUS_RING = [\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[length:var(--focus-ring-width)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-solid',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[color:var(--map-marker-hover-color)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:forced-colors:outline-[CanvasText]',\n].join(' ');\n\n/**\n * The MapView pin: a teardrop in the marker colour with a white ring, a thin\n * ink edge outside it and the four-pillar Alfadocs mark. Content of an\n * AdvancedMarker, which anchors it at the bottom centre, so the tip marks the\n * place. It sits at the bottom centre of a transparent box at least\n * `--min-target-size` square, the kit's target size. Decorative: the marker\n * carries the accessible name. The geometry is shared with the static PNG.\n */\nexport function MapViewPin({ hovered }: { hovered: boolean }): ReactNode {\n const scale = hovered ? MAP_PIN_HOVER_SCALE : 1;\n const body = hovered\n ? 'ds:fill-[var(--map-marker-selected-color)]'\n : 'ds:fill-[var(--map-marker-color)]';\n return (\n <span\n data-component=\"map-view-pin\"\n className=\"ds:flex ds:items-end ds:justify-center ds:[min-inline-size:var(--min-target-size)] ds:[min-block-size:var(--min-target-size)]\"\n >\n <svg\n aria-hidden=\"true\"\n focusable=\"false\"\n data-map-pin={hovered ? 'hovered' : 'normal'}\n width={MAP_PIN_WIDTH * scale}\n height={MAP_PIN_HEIGHT * scale}\n viewBox={`0 0 ${MAP_PIN_WIDTH} ${MAP_PIN_HEIGHT}`}\n className={`ds:block ds:fill-[var(--map-marker-hover-ring)] ds:forced-colors:fill-[Canvas] ${FOCUS_RING}`}\n >\n <path\n d={MAP_PIN_BODY_PATH}\n fill=\"none\"\n strokeWidth={MAP_PIN_EDGE_WIDTH}\n strokeLinejoin=\"round\"\n className=\"ds:stroke-[color:var(--map-marker-hover-color)] ds:forced-colors:stroke-[ButtonText]\"\n />\n <path\n d={MAP_PIN_BODY_PATH}\n strokeWidth={MAP_PIN_RING_WIDTH}\n strokeLinejoin=\"round\"\n className={`${body} ds:stroke-[color:var(--map-marker-hover-ring)] ds:forced-colors:fill-[ButtonText] ds:forced-colors:stroke-[Canvas]`}\n />\n <g transform={MAP_PIN_MARK_TRANSFORM}>\n {MAP_PIN_MARK_SHAPES.map(({ tag, attrs }, index) =>\n tag === 'path' ? (\n <path key={index} d={attrs.d} />\n ) : (\n <rect\n key={index}\n x={attrs.x}\n y={attrs.y}\n width={attrs.width}\n height={attrs.height}\n rx={attrs.rx}\n ry={attrs.ry}\n transform={attrs.transform}\n />\n ),\n )}\n </g>\n </svg>\n </span>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* MapView marker clustering: the pure part. */\n/* */\n/* A small greedy grid clusterer in Web Mercator pixels. Nothing here */\n/* touches Google or the DOM, so it is cheap to test and cannot cause */\n/* a request. Pixels are world pixels at the given zoom, not screen */\n/* pixels, so panning never changes a cluster: only zooming does. */\n/* ------------------------------------------------------------------ */\n\nexport interface ClusterInput {\n id: string;\n position: { lat: number; lng: number };\n}\n\nexport interface MarkerCluster<T extends ClusterInput> {\n /** Id of the first member: unique across clusters, so safe as a React key. */\n id: string;\n /** Average position of the members. */\n position: { lat: number; lng: number };\n members: T[];\n}\n\nexport interface ClusterResult<T extends ClusterInput> {\n /** Markers that stay on their own pin. */\n singles: T[];\n /** Groups of two or more. */\n clusters: MarkerCluster<T>[];\n}\n\n/** More markers than this and `clusterMarkers=\"auto\"` clusters. */\nexport const CLUSTER_AUTO_THRESHOLD = 30;\n/** Markers closer than this on screen, in px, share a cluster. */\nexport const CLUSTER_RADIUS_PX = 56;\n/**\n * From this zoom on nothing clusters: street level, where two pins that are\n * still together are in the same building and a cluster could never split.\n */\nexport const CLUSTER_OFF_ZOOM = 17;\n\n/** Google's world is 256px wide at zoom 0. */\nconst WORLD_PX = 256;\n\nconst toRadians = (degrees: number): number => (degrees * Math.PI) / 180;\n\nfunction project(\n lat: number,\n lng: number,\n zoom: number,\n): { x: number; y: number } {\n const world = WORLD_PX * 2 ** zoom;\n const sin = Math.min(Math.max(Math.sin(toRadians(lat)), -0.9999), 0.9999);\n const mercator = Math.log((1 + sin) / (1 - sin)) / 2;\n return {\n x: ((lng + 180) / 360) * world,\n y: (0.5 - mercator / (2 * Math.PI)) * world,\n };\n}\n\n/** Whether `clusterMarkers` asks for clustering of this many markers. */\nexport function shouldCluster(\n setting: boolean | 'auto',\n markerCount: number,\n): boolean {\n if (setting === 'auto') return markerCount > CLUSTER_AUTO_THRESHOLD;\n return setting && markerCount > 1;\n}\n\ninterface Bucket<T extends ClusterInput> {\n x: number;\n y: number;\n members: T[];\n lat: number;\n lng: number;\n}\n\n/**\n * Groups `markers` that fall within `radiusPx` of each other at `zoom`.\n * Greedy and order-independent (the points are sorted first, so re-sorting\n * the results never reshuffles the clusters). At or above\n * `CLUSTER_OFF_ZOOM` every marker is a single.\n */\nexport function clusterMarkerSet<T extends ClusterInput>(\n markers: readonly T[],\n zoom: number,\n radiusPx: number = CLUSTER_RADIUS_PX,\n): ClusterResult<T> {\n if (zoom >= CLUSTER_OFF_ZOOM || markers.length < 2) {\n return { singles: [...markers], clusters: [] };\n }\n const projected = markers\n .map((marker) => ({\n marker,\n ...project(marker.position.lat, marker.position.lng, zoom),\n }))\n .sort(\n (a, b) => a.x - b.x || a.y - b.y || (a.marker.id < b.marker.id ? -1 : 1),\n );\n\n const buckets: Bucket<T>[] = [];\n // Spatial hash of bucket centres, one cell per radius, so a marker only\n // compares with the nine cells around it.\n const grid = new Map<string, Bucket<T>[]>();\n const cellOf = (value: number): number => Math.floor(value / radiusPx);\n const radiusSquared = radiusPx * radiusPx;\n\n for (const point of projected) {\n const cx = cellOf(point.x);\n const cy = cellOf(point.y);\n let best: Bucket<T> | null = null;\n let bestDistance = radiusSquared;\n for (let dx = -1; dx <= 1; dx += 1) {\n for (let dy = -1; dy <= 1; dy += 1) {\n for (const bucket of grid.get(`${cx + dx}:${cy + dy}`) ?? []) {\n const distance =\n (bucket.x - point.x) ** 2 + (bucket.y - point.y) ** 2;\n if (distance <= bestDistance) {\n best = bucket;\n bestDistance = distance;\n }\n }\n }\n }\n if (best) {\n const n = best.members.length;\n // The centre moves with the mean, so its grid cell can change.\n const before = `${cellOf(best.x)}:${cellOf(best.y)}`;\n best.x = (best.x * n + point.x) / (n + 1);\n best.y = (best.y * n + point.y) / (n + 1);\n best.lat = (best.lat * n + point.marker.position.lat) / (n + 1);\n best.lng = (best.lng * n + point.marker.position.lng) / (n + 1);\n best.members.push(point.marker);\n const after = `${cellOf(best.x)}:${cellOf(best.y)}`;\n if (before !== after) {\n const old = grid.get(before);\n if (old) old.splice(old.indexOf(best), 1);\n const next = grid.get(after);\n if (next) next.push(best);\n else grid.set(after, [best]);\n }\n } else {\n const bucket: Bucket<T> = {\n x: point.x,\n y: point.y,\n members: [point.marker],\n lat: point.marker.position.lat,\n lng: point.marker.position.lng,\n };\n buckets.push(bucket);\n const key = `${cx}:${cy}`;\n const list = grid.get(key);\n if (list) list.push(bucket);\n else grid.set(key, [bucket]);\n }\n }\n\n const singles: T[] = [];\n const clusters: MarkerCluster<T>[] = [];\n for (const bucket of buckets) {\n if (bucket.members.length === 1) {\n singles.push(bucket.members[0]);\n } else {\n clusters.push({\n id: bucket.members[0].id,\n position: { lat: bucket.lat, lng: bucket.lng },\n members: bucket.members,\n });\n }\n }\n return { singles, clusters };\n}\n","/**\n * True when focus on `target` is keyboard-style (`:focus-visible`). A mouse\n * click also moves focus, and hover-like states (a map card, a hover pin)\n * must not stay up after the pointer has gone, so they follow this rather\n * than any focus.\n */\nexport function isFocusVisible(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false;\n try {\n return target.matches(':focus-visible');\n } catch {\n // An old engine without `:focus-visible`: treat focus as hover.\n return true;\n }\n}\n","/* ------------------------------------------------------------------ */\n/* MapView — kit wrapper over @vis.gl/react-google-maps. */\n/* */\n/* - Loads the Google Maps JS API on demand via the library's */\n/* `APIProvider`; consumer supplies the key (no kit-side cache). */\n/* - Defaults to a CONSENT-GATED placeholder (`consentGranted={false}`) */\n/* so the third-party load never happens before the consumer flips */\n/* the opt-in. GDPR-safe by default. */\n/* - The tiles are Google's own style and do not follow the kit theme; */\n/* consumers pass `mapId` for a cloud-styled map. Pins and clusters */\n/* use one theme-stable marker colour. */\n/* - `data-component=\"map-view\"` lives on the outer span; the inner */\n/* `<Map>` mounts inside it. */\n/* ------------------------------------------------------------------ */\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type MutableRefObject,\n type RefObject,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { MapPin, ExternalLink } from 'lucide-react';\nimport {\n APIProvider,\n Map as GoogleMap,\n AdvancedMarker,\n Marker,\n useApiIsLoaded,\n useMap,\n useMapsLibrary,\n} from '@vis.gl/react-google-maps';\nimport { Button } from '../button';\nimport { Skeleton } from '../skeleton';\nimport { VisuallyHidden } from '../visually-hidden';\nimport { ErrorBoundary } from '../error-boundary';\nimport { MapsCoreGate } from '../_shared/maps-core-gate';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { mapViewAgent } from './map-view.agent';\nimport { lengthTokenPx } from '../_shared/length-token';\nimport { MapViewPin, MAP_PIN_HOVER_SCALE } from './map-view-pin';\nimport {\n MAP_PIN_HEIGHT,\n MAP_PIN_WIDTH,\n mapPinSvg,\n} from './map-view-pin-geometry';\nimport {\n CLUSTER_OFF_ZOOM,\n clusterMarkerSet,\n shouldCluster,\n type MarkerCluster,\n} from './map-view-cluster';\nimport { isFocusVisible } from '../_shared/focus-visible';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface MapLatLng {\n lat: number;\n lng: number;\n}\n\nexport interface MapBounds {\n north: number;\n south: number;\n east: number;\n west: number;\n}\n\nexport interface MapBoundsChangeDetails {\n /** False for initial load, resizing, fitting and imperative camera moves. */\n userInteraction: boolean;\n}\n\n/**\n * A point to fit the camera to. `id` is its identity (a result id): it\n * decides whether a new set only added points to the old one. Without\n * it the coordinates stand in, so two results at one address count as\n * one.\n */\nexport interface MapFitPoint extends MapLatLng {\n id?: string;\n}\n\nexport interface MapMarker {\n /** Stable id — used for keyed re-rendering and `onMarkerClick`. */\n id: string;\n /** Position in lat/lng. */\n position: MapLatLng;\n /** Optional accessible label — falls back to the id. */\n label?: string;\n}\n\n/**\n * A map image shown in place of the interactive map until the host lets it\n * load (HTP-7348). Serve it from the host's own domain, so the browser makes\n * no request to Google before the visitor chooses.\n */\nexport interface MapViewStaticPreview {\n /** Image URL, typically on the host's own domain. */\n src: string;\n /**\n * Optional `srcset` with x descriptors only (`\"/map@2x.png 2x\"`): the image\n * has no `sizes`, so width descriptors would not work.\n */\n srcSet?: string;\n /** What the image shows, for example \"Map of Studio Rossi\". */\n alt: string;\n /** Intrinsic size in CSS pixels, so the image holds its box while loading. */\n width: number;\n height: number;\n /**\n * The same map in other shapes (tall, square, wide). When given, the kit\n * measures the preview area and loads only the source whose aspect ratio\n * (`width / height`) is closest to it; on a tie the earlier one wins. The\n * image is still fitted whole, never cropped. The top-level `src`, `srcSet`,\n * `width` and `height` stay as the default, used when the area cannot be\n * measured. `alt` is shared.\n */\n sources?: ReadonlyArray<MapViewStaticPreviewSource>;\n}\n\n/** One shape of a `MapViewStaticPreview`. */\nexport interface MapViewStaticPreviewSource {\n src: string;\n /** x descriptors only, as on the preview itself. */\n srcSet?: string;\n /** Intrinsic size in CSS pixels. */\n width: number;\n height: number;\n}\n\n/** A source must beat the current one by this much (log ratio) to replace it. */\nconst PREVIEW_SWITCH_MARGIN = 0.08;\n\nfunction closestSourceIndex(logRatios: ReadonlyArray<number>, area: number) {\n let best = 0;\n for (let i = 1; i < logRatios.length; i += 1) {\n if (Math.abs(logRatios[i]! - area) < Math.abs(logRatios[best]! - area)) {\n best = i;\n }\n }\n return best;\n}\n\n/**\n * Which `sources` entry fits the box that `ref` points at: measured before\n * first paint, so only one image is requested, and re-picked on resize with a\n * margin so it does not flip at a boundary. `null` means \"not measured\": use\n * the preview's own `src`.\n */\nfunction usePickedSourceIndex(\n sources: ReadonlyArray<MapViewStaticPreviewSource> | undefined,\n ref: RefObject<HTMLDivElement | null>,\n): number | null {\n const [index, setIndex] = useState<number | null>(null);\n const shapeKey = (sources ?? []).map((s) => `${s.width}x${s.height}`).join();\n useLayoutEffect(() => {\n const node = ref.current;\n const logRatios = shapeKey\n ? shapeKey.split(',').map((k) => {\n const [w, h] = k.split('x').map(Number);\n return Math.log(w! / h!);\n })\n : [];\n if (\n !node ||\n logRatios.length === 0 ||\n logRatios.some((r) => !isFinite(r))\n ) {\n setIndex(null);\n return undefined;\n }\n // New shapes start fresh: the margin only guards a resize.\n setIndex(null);\n const measure = (initial: boolean): void => {\n const { width, height } = node.getBoundingClientRect();\n if (!(width > 0 && height > 0)) {\n if (initial) setIndex(null);\n return;\n }\n const area = Math.log(width / height);\n const next = closestSourceIndex(logRatios, area);\n setIndex((current) => {\n if (current === null || current >= logRatios.length) return next;\n const gain =\n Math.abs(logRatios[current]! - area) -\n Math.abs(logRatios[next]! - area);\n return gain > PREVIEW_SWITCH_MARGIN ? next : current;\n });\n };\n measure(true);\n if (typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => measure(false));\n observer.observe(node);\n return () => observer.disconnect();\n }, [ref, shapeKey]);\n return index;\n}\n\n/** What moved onto or off a pin: the pointer, or keyboard-style focus. */\nexport type MapMarkerHoverSource = 'pointer' | 'focus';\n\nexport interface MapViewHandle {\n /** Pan the map to a new centre without changing zoom. */\n panTo: (position: MapLatLng) => void;\n /** Set zoom (typical range 1–20). */\n setZoom: (zoom: number) => void;\n /** Programmatically select a marker (fires `onMarkerClick`). */\n selectMarker: (id: string | null) => void;\n /* ---- read-back ------------------------------------------------- */\n /**\n * True once the consent gate has been passed. The map may still be\n * loading: the three actions above stay silent no-ops until\n * `getCenter` returns a value.\n */\n isConsentGranted: () => boolean;\n /** Live map centre, or `null` until the map has loaded. */\n getCenter: () => MapLatLng | null;\n /**\n * Live zoom level, or `null` until the map has loaded.\n * Google clamps to the map type's range, so this is not necessarily\n * the value last passed to `setZoom`.\n */\n getZoom: () => number | null;\n /**\n * The controlled `selectedMarkerId` prop as the host currently holds\n * it. `selectMarker` only proposes a selection — the host is free to\n * ignore it, and this is the only way to see whether it did.\n */\n getSelectedMarkerId: () => string | null;\n /** Number of markers passed to the component. Count only, no ids. */\n getMarkerCount: () => number;\n}\n\n/**\n * The slice of `MapViewHandle` that needs a live `google.maps.Map`.\n * Built inside the `APIProvider` subtree by `MapImperativeBridge`; the\n * outer component composes it with the props-derived getters, and holds\n * `null` whenever the subtree is unmounted.\n */\ntype MapBridgeHandle = Pick<\n MapViewHandle,\n 'panTo' | 'setZoom' | 'selectMarker' | 'getCenter' | 'getZoom'\n> & {\n /** Set centre and zoom in one step, never animated. Internal. */\n jumpTo: (center: MapLatLng, zoom: number) => void;\n};\n\nexport interface MapViewProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'onClick'>,\n VariantProps<typeof mapViewVariants> {\n /** Opaque instance id — emitted as `data-component-id`. */\n id?: string;\n /** Google Maps JS API key. Consumer-owned; never cached by the kit. */\n apiKey: string;\n /** Map centre (lat/lng). Required so the consent placeholder can also use it. */\n center: MapLatLng;\n /** Initial zoom. Default 15 (city-block detail, matches booking site). */\n zoom?: number;\n /**\n * Markers to render. The first marker auto-selects when no\n * `selectedMarkerId` is given.\n */\n markers?: MapMarker[];\n /** Controlled selection — fires `onMarkerClick` with the new id (or null on map click). */\n selectedMarkerId?: string | null;\n /** Fires when a marker is clicked, or `null` when the map background is clicked. */\n onMarkerClick?: (id: string | null) => void;\n /**\n * The marker the host is pointing at (for example the result card under\n * the pointer). Its pin grows and paints above the others, in a\n * contrasting ink so it clears WCAG 1.4.11 against the map. Separate from\n * `selectedMarkerId`, which is the committed choice. Where a classic\n * (no `mapId`) marker is used only the stacking order changes.\n */\n hoveredMarkerId?: string | null;\n /**\n * Content for a small card shown above the `hoveredMarkerId` pin. It is\n * hidden from assistive technology and kept out of the tab order, so it\n * must only repeat what the host already says elsewhere. The kit places\n * it above the pin, flips it below when there is no room, and nudges it\n * inside the map. Nothing renders when the pin is out of view.\n */\n renderMarkerCard?: (id: string) => ReactNode;\n /**\n * The pointer entered (`hovered` true) or left (false) a pin, or keyboard\n * focus (`:focus-visible`) moved onto one. `source` says which, so a host\n * can follow the pointer only where the device can hover. To show the card\n * for the pin, set `hoveredMarkerId` from it, and clear it only for the id\n * it holds. Focus that a mouse click gives a pin is not reported as \"on\";\n * a blur, and Escape on a focused pin, is reported as \"off\". A pointer\n * leave is not reported while the pin keeps keyboard focus, and a blur not\n * while the pointer is on the pin. Focus is\n * reported for `mapId` (advanced) markers only: Google's classic marker\n * sends no focus event. The kit bundles `@vis.gl/react-google-maps` 1.8.x,\n * which has the marker pointer events this needs.\n */\n onMarkerHover?: (\n id: string,\n hovered: boolean,\n source: MapMarkerHoverSource,\n ) => void;\n /**\n * The visible viewport once the map settles, including its first bounds.\n * Fires only when bounds change. `userInteraction` is true after patient\n * input that pans or zooms, never for a kit-initiated camera move.\n * Silent until the consented map has loaded.\n */\n onBoundsChange?: (bounds: MapBounds, details: MapBoundsChangeDetails) => void;\n /**\n * GDPR consent gate. When `false` (default) the component renders a\n * placeholder with a translated CTA — the Maps JS API is **not**\n * loaded. Flip to `true` once the consumer has captured opt-in.\n */\n consentGranted?: boolean;\n /** Fires when the consumer clicks the \"Load map\" CTA in the placeholder. */\n onConsentRequest?: () => void;\n /**\n * A map image to show instead of the text placeholder while\n * `consentGranted` is false (HTP-7348). A \"Show interactive map\" button on\n * the image calls `onConsentRequest`, as \"Load map\" does, under the same\n * disclosure (`consentDescription`) and with the \"Open in Google Maps\"\n * link. They sit on a plate at the top of the map. When consent flips,\n * focus moves from that button to the map region. The image is shown whole\n * (`object-fit: contain`, so Google's logo and copyright stay visible),\n * loads lazily, and stands in until the interactive map can draw. If it\n * fails to load, the text placeholder shows instead. `consentCta`\n * replaces the button.\n */\n staticPreview?: MapViewStaticPreview;\n /**\n * Optional address string shown in the consent placeholder so the\n * surface is useful before the map loads. Also used to build the\n * fallback \"Open in Google Maps\" link target.\n */\n address?: string;\n /**\n * Language of the practice's own text (the `address` shown in the\n * placeholder), as a BCP 47 tag such as `\"it\"`. Written as `lang` on that\n * line so a screen reader on a page in another language pronounces it\n * correctly. Leave it out and no `lang` is written. The kit's own labels\n * keep the page language.\n */\n contentLang?: string;\n /**\n * Cloud-styled-map id. When set, takes precedence over the JSON\n * `styles` array and disables the theme-bridge re-fire. Use when\n * you've provisioned a styled map in Google Cloud Console.\n */\n mapId?: string;\n /**\n * Override the consent-placeholder description text. The default\n * (`t('mapView.consentDescription')`) is a generic IP-disclosure\n * line. Consumers operating under specific GDPR / ePrivacy\n * disclosures (jurisdiction, named controller, policy link) should\n * supply their own text via this prop. Pass any `ReactNode` — a\n * paragraph, a localised `<Trans>` instance, etc.\n */\n consentDescription?: ReactNode;\n /**\n * Replace the kit-rendered \"Load map\" button with custom CTA chrome.\n * Use when the consent dialog needs a policy link, a specific\n * jurisdiction phrasing, or a different surface (Tooltip-wrapped\n * button, link to a settings page, etc.). When omitted the kit\n * renders its own `<Button>` — that button calls `onConsentRequest`\n * if provided, otherwise flips MapView's internal opt-in state so\n * the map loads inline.\n */\n consentCta?: ReactNode;\n /**\n * Visual surface. Default `bordered` (current behaviour: 1px\n * `--border` outline + `--radius-md`). `elevated` replaces the\n * border with `--shadow-card`, matching `<Card variant=\"elevated\">`.\n * `bare` drops both — map is flush against the page canvas.\n */\n surface?: 'bordered' | 'elevated' | 'bare';\n /** Accessible label for the map region. Falls back to `t('mapView.label')`. */\n ariaLabel?: string;\n /**\n * Points the camera fits into view, with a margin, once the map has\n * loaded, and again whenever `fitKey` changes. Use it to show every pin\n * of a result list without the patient moving the map.\n *\n * - One point (or several at the same place) centres on it at\n * `fitMaxZoom`.\n * - No points: no fit, the camera shows `center` / `zoom`. If the\n * points run out after a fit, or the prop is removed, the camera\n * goes back there in one step.\n * - Between key changes nothing refits, so a map the patient has\n * panned or zoomed stays where they left it.\n * - A key change that only adds points (a \"load more\": every fitted\n * point is still there, compared by `id`) refits only if the patient\n * has not moved the map. Any other change refits. Give each point the\n * `id` of its result: two results can share an address.\n * - The camera is set in one step, never animated.\n * - While there are points to fit, the fit owns the camera: later\n * changes to `center` or `zoom` do not move it.\n */\n fitToPoints?: MapFitPoint[];\n /**\n * Identity of the fitted set: the camera refits only when this\n * changes. Pass the ids of the listed results, so re-sorting the same\n * results does not refit. Defaults to the points themselves, in any\n * order.\n */\n fitKey?: string;\n /**\n * The closest a fit may zoom in, so one result does not open at street\n * level. Default 15.\n */\n fitMaxZoom?: number;\n /**\n * Group nearby pins into numbered clusters that split as the map zooms in.\n * `'auto'` (default) clusters only above 30 markers, so a map with a few\n * pins looks and behaves as before. `true` clusters any set of two or\n * more; `false` never does.\n *\n * - A cluster is a button named by its count (\"12 practices\"), at least\n * `--min-target-size` square. Click, Enter or Space zooms to its pins.\n * - The `selectedMarkerId` and `hoveredMarkerId` pins always stay out of\n * clusters, so a chosen or hovered practice is never hidden.\n * - From zoom 17 (street level) pins no longer cluster.\n * - It runs only inside the consented, loaded map: no Google request is\n * made for it.\n */\n clusterMarkers?: boolean | 'auto';\n /**\n * The name of a cluster, by its pin count: a string with `{count}` in it,\n * or a function. Defaults to the kit's translated \"12 practices\". Set it\n * when the pins are not practices (\"12 offices\").\n */\n clusterLabel?: string | ((count: number) => string);\n /**\n * A cluster was activated: the map zooms to its pins. The host can track\n * it. The zoom is not reported as a patient pan, so `onBoundsChange` says\n * `userInteraction: false` for it.\n */\n onClusterClick?: (details: { count: number; markerIds: string[] }) => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst mapViewVariants = cva(\n [\n 'ds:relative ds:overflow-hidden ds:block',\n 'ds:bg-[var(--muted)]',\n // Focus lands on the region when the interactive map replaces its button.\n 'ds:focus-visible:outline-solid ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:[--map-block-size:14rem]',\n md: 'ds:[--map-block-size:20rem]',\n lg: 'ds:[--map-block-size:28rem]',\n // `fill` defers sizing to the parent. The outer takes\n // `block-size: 100%` so the inner (also 100%) has something\n // to inherit from. Use inside a viewport-locked column —\n // `<PracticeResults variant=\"map-first\">` wires this through\n // automatically; standalone consumers must give the parent\n // a constrained block-size or the map collapses to 0.\n fill: 'ds:[block-size:100%]',\n },\n surface: {\n bordered:\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]',\n elevated:\n 'ds:rounded-[var(--radius-md)] ds:shadow-[var(--shadow-card)] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--card-border)]',\n bare: 'ds:rounded-none ds:border-0',\n },\n },\n defaultVariants: { size: 'md', surface: 'bordered' },\n },\n);\n\n// Inner block-size: percentage when `size=\"fill\"`, rem-token otherwise.\n// Splitting this out (instead of compound-variants) keeps the size\n// switch readable and avoids enumerating every (size × surface) pair.\nconst innerSurfaceVariants = cva('ds:block ds:w-full', {\n variants: {\n size: {\n sm: 'ds:[block-size:var(--map-block-size)]',\n md: 'ds:[block-size:var(--map-block-size)]',\n lg: 'ds:[block-size:var(--map-block-size)]',\n fill: 'ds:[block-size:100%]',\n },\n },\n defaultVariants: { size: 'md' },\n});\n\nconst consentChromeVariants = cva(\n [\n 'ds:absolute ds:inset-0',\n 'ds:flex ds:flex-col ds:items-center ds:justify-center',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:p-[var(--spacing-md)]',\n 'ds:bg-[var(--muted)] ds:text-[var(--foreground)]',\n 'ds:text-center',\n ].join(' '),\n);\n\nconst consentTitleVariants = cva('type-heading-3 ds:m-0');\n\nconst consentDescriptionVariants = cva(\n 'type-body-sm ds:text-[var(--muted-foreground)] ds:[max-inline-size:28rem] ds:m-0',\n);\n\n/* ------------------------------------------------------------------ */\n/* Inner imperative bridge */\n/* */\n/* `useMap()` only resolves inside the APIProvider tree, so the */\n/* imperative actions live in a tiny inner component that the outer */\n/* `MapView` mounts conditionally. It exposes the `panTo` / `setZoom` */\n/* etc. to the forwardRef via a `useImperativeHandle` callback prop. */\n/* ------------------------------------------------------------------ */\n\ninterface MapImperativeBridgeProps {\n /** Called with the live handle on mount, and with `null` on unmount. */\n onHandleReady: (handle: MapBridgeHandle | null) => void;\n onMarkerSelect: (id: string | null) => void;\n onCameraMove: () => void;\n}\n\nfunction MapImperativeBridge({\n onHandleReady,\n onMarkerSelect,\n onCameraMove,\n}: MapImperativeBridgeProps): null {\n const map = useMap();\n useEffect(() => {\n if (!map) return;\n onHandleReady({\n panTo: (position) => {\n onCameraMove();\n map.panTo(position);\n },\n setZoom: (zoom) => {\n onCameraMove();\n map.setZoom(zoom);\n },\n jumpTo: (center, zoom) => {\n onCameraMove();\n map.moveCamera({ center, zoom });\n },\n selectMarker: (id) => onMarkerSelect(id),\n // Read straight off the live map: Google clamps `setZoom` to the\n // map type's range and animates `panTo`, so requested and applied\n // are not the same value.\n getCenter: () => map.getCenter()?.toJSON() ?? null,\n getZoom: () => map.getZoom() ?? null,\n });\n // Drop the handle when the subtree unmounts (consent revoked, or the\n // component tears down) so the outer getters answer `null` instead of\n // reporting a dead map's last known position.\n return () => onHandleReady(null);\n }, [map, onHandleReady, onMarkerSelect, onCameraMove]);\n return null;\n}\n\n/** Report once per settled viewport, with patient input kept separate from camera state. */\nfunction MapBoundsBridge({\n onBoundsChange,\n userInteraction,\n}: {\n onBoundsChange: NonNullable<MapViewProps['onBoundsChange']>;\n userInteraction: MutableRefObject<MapInteraction>;\n}): null {\n const map = useMap();\n const callback = useRef(onBoundsChange);\n callback.current = onBoundsChange;\n\n useEffect(() => {\n if (!map) return undefined;\n const div = map.getDiv();\n let previous: {\n bounds: MapBounds;\n center: MapLatLng;\n zoom: number;\n } | null = null;\n const resetInteraction = (): void => {\n userInteraction.current = {\n pendingInput: false,\n moved: false,\n programmatic: false,\n };\n };\n resetInteraction();\n let inputExpiry: ReturnType<typeof setTimeout> | undefined;\n const touchPointers = new Set<number>();\n const markInput = (event: Event): void => {\n if (event instanceof PointerEvent && event.pointerType === 'touch') {\n if (event.type === 'pointerdown') touchPointers.add(event.pointerId);\n if (event.type === 'pointerup' || event.type === 'pointercancel') {\n touchPointers.delete(event.pointerId);\n return;\n }\n }\n if (\n event instanceof PointerEvent &&\n event.type === 'pointermove' &&\n event.buttons === 0 &&\n event.pointerType !== 'touch'\n )\n return;\n if (\n event instanceof KeyboardEvent &&\n ![\n 'ArrowUp',\n 'ArrowDown',\n 'ArrowLeft',\n 'ArrowRight',\n '+',\n '=',\n '-',\n '_',\n 'Enter',\n ' ',\n 'PageUp',\n 'PageDown',\n ].includes(event.key)\n )\n return;\n // A short panTo can keep animating after the imperative call has\n // returned. Ordinary scrolling or clicking the map must not claim\n // that animation. A real camera gesture can interrupt it.\n const fullscreen = document.fullscreenElement;\n const fullscreenMap = Boolean(\n fullscreen && (fullscreen.contains(div) || div.contains(fullscreen)),\n );\n let cameraInput = event.type === 'dblclick';\n if (event instanceof PointerEvent && event.type === 'pointermove')\n cameraInput =\n event.pointerType !== 'touch' ||\n touchPointers.size >= 2 ||\n fullscreenMap;\n if (event.type === 'touchmove')\n cameraInput =\n (event as TouchEvent).touches.length >= 2 || fullscreenMap;\n if (event instanceof KeyboardEvent)\n cameraInput = event.key !== 'Enter' && event.key !== ' ';\n if (event instanceof WheelEvent)\n cameraInput = event.ctrlKey || event.metaKey || fullscreenMap;\n if (event.type === 'click')\n cameraInput =\n event.target instanceof Element &&\n event.target.closest('button, [role=\"button\"]') != null;\n if (cameraInput) userInteraction.current.programmatic = false;\n userInteraction.current.pendingInput = true;\n clearTimeout(inputExpiry);\n // Controls normally start moving the camera immediately. Expire an\n // input that did nothing; once movement starts it stays attributed\n // until idle, including a long drag or zoom animation.\n inputExpiry = setTimeout(() => {\n userInteraction.current.pendingInput = false;\n }, 250);\n };\n const markCameraChange = (): void => {\n if (\n userInteraction.current.pendingInput &&\n !userInteraction.current.programmatic\n )\n userInteraction.current.moved = true;\n };\n const report = (): void => {\n const bounds = map.getBounds()?.toJSON();\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const fromInput = userInteraction.current.moved;\n resetInteraction();\n clearTimeout(inputExpiry);\n if (!bounds || !center || zoom == null) return;\n const changed =\n !previous ||\n (Object.keys(bounds) as (keyof MapBounds)[]).some(\n (key) => bounds[key] !== previous!.bounds[key],\n );\n const moved =\n previous != null &&\n (zoom !== previous.zoom ||\n center.lat !== previous.center.lat ||\n center.lng !== previous.center.lng);\n previous = { bounds, center, zoom };\n if (changed)\n callback.current(bounds, { userInteraction: fromInput && moved });\n };\n // Camera events alone include initial load, refits and resizes. Input\n // also covers touch, keyboard and Google's zoom controls, without\n // preventing their defaults or depending on vendor DOM selectors.\n const inputEvents = [\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointercancel',\n 'touchstart',\n 'touchmove',\n 'click',\n 'dblclick',\n 'wheel',\n 'keydown',\n ] as const;\n inputEvents.forEach((event) =>\n div.addEventListener(event, markInput, { capture: true, passive: true }),\n );\n const listeners = [\n map.addListener('dragstart', () => {\n userInteraction.current.programmatic = false;\n userInteraction.current.moved = true;\n }),\n map.addListener('center_changed', markCameraChange),\n map.addListener('zoom_changed', markCameraChange),\n map.addListener('idle', report),\n ];\n report();\n return () => {\n listeners.forEach((listener) => listener.remove());\n inputEvents.forEach((event) =>\n div.removeEventListener(event, markInput, true),\n );\n clearTimeout(inputExpiry);\n resetInteraction();\n };\n }, [map, userInteraction]);\n return null;\n}\n\ninterface MapInteraction {\n pendingInput: boolean;\n moved: boolean;\n programmatic: boolean;\n}\n\n/* ------------------------------------------------------------------ */\n/* Fit to points */\n/* */\n/* Lives inside <GoogleMap>, so it only runs once the `maps` library */\n/* has arrived and `useMap()` holds a real map: the load-race rule */\n/* below applies to it too. The camera is computed here (Web Mercator) */\n/* and set in one step, rather than through fitBounds: fitBounds takes */\n/* no zoom limit, and capping it afterwards needs an `idle` event that */\n/* Google may never send when the view does not change, so the cap */\n/* could fire on the patient's next zoom instead. Setting the camera */\n/* directly is synchronous and never animates (reduced motion). */\n/* */\n/* A map in a hidden column or a sheet still opening has no size yet, */\n/* so the fit waits for the map to be laid out. */\n/* ------------------------------------------------------------------ */\n\n/* ------------------------------------------------------------------ */\n/* Hover card: pixel anchor + layer */\n/* */\n/* The card is plain DOM inside the map frame, not a Google overlay, so */\n/* it works with and without a `mapId`. The bridge turns the pin's */\n/* lat/lng into a pixel in the frame from the live centre and zoom. */\n/* ------------------------------------------------------------------ */\n\ninterface MarkerAnchor {\n id: string;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * The pixel under `position`, updated on every camera change. The state lives\n * here, in the small card wrapper, so camera frames re-render only the card\n * and never the map's markers.\n */\nfunction useMarkerAnchor(id: string, position: MapLatLng): MarkerAnchor | null {\n const map = useMap();\n const [anchor, setAnchor] = useState<MarkerAnchor | null>(null);\n const { lat, lng } = position;\n useEffect(() => {\n if (!map || typeof map.getCenter !== 'function') return undefined;\n const report = (): void => {\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const div = map.getDiv?.();\n if (!center || zoom == null || !div) {\n setAnchor(null);\n return;\n }\n const width = div.clientWidth;\n const height = div.clientHeight;\n const world = WORLD_PX * 2 ** zoom;\n setAnchor({\n id,\n width,\n height,\n x: width / 2 + ((lng - center.lng) / 360) * world,\n y:\n height / 2 -\n ((mercatorY(lat) - mercatorY(center.lat)) * world) / (2 * Math.PI),\n });\n };\n report();\n const listeners = (\n ['bounds_changed', 'zoom_changed', 'center_changed', 'idle'] as const\n ).flatMap((event) =>\n typeof map.addListener === 'function'\n ? [map.addListener(event, report)]\n : [],\n );\n return () => listeners.forEach((listener) => listener.remove());\n }, [map, id, lat, lng]);\n return anchor?.id === id ? anchor : null;\n}\n\n/**\n * Mounted inside the map (it needs the map instance), it draws the card into\n * the frame element through a portal.\n */\nfunction MapMarkerCard({\n marker,\n frame,\n direction,\n children,\n}: {\n marker: MapMarker;\n frame: HTMLElement | null;\n direction: 'ltr' | 'rtl';\n children: ReactNode;\n}): ReactNode {\n const anchor = useMarkerAnchor(marker.id, marker.position);\n if (!anchor || !frame) return null;\n return createPortal(\n <MapMarkerCardLayer anchor={anchor} direction={direction}>\n {children}\n </MapMarkerCardLayer>,\n frame,\n );\n}\n\n/** Room between the card and the pin tip, and between the card and the frame edge. */\nconst CARD_PIN_CLEARANCE_PX = 52;\nconst CARD_PIN_BELOW_PX = 12;\nconst CARD_EDGE_PX = 8;\n\nfunction MapMarkerCardLayer({\n anchor,\n children,\n direction,\n}: {\n anchor: MarkerAnchor;\n children: ReactNode;\n direction: 'ltr' | 'rtl';\n}): ReactNode {\n const cardRef = useRef<HTMLDivElement>(null);\n const [size, setSize] = useState<{ width: number; height: number } | null>(\n null,\n );\n useLayoutEffect(() => {\n const node = cardRef.current;\n if (!node) return;\n setSize((prev) =>\n prev?.width === node.offsetWidth && prev.height === node.offsetHeight\n ? prev\n : { width: node.offsetWidth, height: node.offsetHeight },\n );\n }, [children, anchor.id]);\n\n const inView =\n anchor.x >= 0 &&\n anchor.x <= anchor.width &&\n anchor.y >= 0 &&\n anchor.y <= anchor.height;\n if (!inView) return null;\n\n const width = size?.width ?? 0;\n const height = size?.height ?? 0;\n // Centred on the pin, nudged to stay inside the frame.\n const maxLeft = Math.max(CARD_EDGE_PX, anchor.width - width - CARD_EDGE_PX);\n const left = Math.min(Math.max(anchor.x - width / 2, CARD_EDGE_PX), maxLeft);\n // Above the pin; below it when there is no room above.\n const above = anchor.y - CARD_PIN_CLEARANCE_PX - height;\n const flipped = above < CARD_EDGE_PX;\n const top = flipped ? anchor.y + CARD_PIN_BELOW_PX : above;\n\n return (\n // The layer is `ltr` because the map is geographic: x is always measured\n // from the physical left. The card inside takes the page direction.\n <div\n dir=\"ltr\"\n aria-hidden=\"true\"\n data-component=\"map-view-marker-card-layer\"\n className=\"ds:pointer-events-none ds:absolute ds:inset-0 ds:overflow-hidden\"\n >\n <div\n ref={cardRef}\n data-component=\"map-view-marker-card\"\n data-marker-id={anchor.id}\n data-placement={flipped ? 'below' : 'above'}\n // eslint-disable-next-line react/forbid-dom-props -- pixel position computed from the live map camera, passed as CSS variables\n style={\n {\n '--marker-card-x': `${left}px`,\n '--marker-card-y': `${top}px`,\n } as CSSProperties\n }\n data-measured={size ? 'true' : 'false'}\n className=\"ds:absolute ds:[inset-inline-start:var(--marker-card-x)] ds:[inset-block-start:var(--marker-card-y)] ds:[inline-size:max-content] ds:[max-inline-size:calc(100%-var(--spacing-md))] ds:data-[measured=false]:invisible\"\n >\n {/* The positioned element stays `ltr`, so its insets read from the\n physical left like the pixel. Only the card's content follows\n the page direction. */}\n <div dir={direction}>{children}</div>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Advanced marker: the pin, its hover look, pointer and focus */\n/* */\n/* Google makes a clickable advanced marker a focusable button */\n/* (`gmp-advanced-marker`, role=\"button\", tabindex 0), so keyboard */\n/* focus is read from the element itself. */\n/* ------------------------------------------------------------------ */\n\ninterface KitAdvancedMarkerProps {\n marker: MapMarker;\n label: string;\n isSelected: boolean;\n isHovered: boolean;\n onSelect: (id: string) => void;\n onHover?: MapViewProps['onMarkerHover'];\n /** Where the pin's element is kept, so focus can be moved to it. */\n elements?: MarkerElements;\n}\n\n/** The element Google draws for an advanced marker. */\nfunction markerElement(\n marker: google.maps.marker.AdvancedMarkerElement | null,\n): HTMLElement | null {\n if (!marker) return null;\n if (marker instanceof HTMLElement) return marker;\n const element = (marker as { element?: unknown }).element;\n return element instanceof HTMLElement ? element : null;\n}\n\nfunction KitAdvancedMarker({\n marker,\n label,\n isSelected,\n isHovered,\n onSelect,\n onHover,\n elements,\n}: KitAdvancedMarkerProps): ReactNode {\n const [element, setElement] =\n useState<google.maps.marker.AdvancedMarkerElement | null>(null);\n const { id } = marker;\n // The pointer is on the pin. Read by the focus listeners, so a blur while\n // the pointer stays (Tab away, the window losing focus) keeps the card.\n const pointerInside = useRef(false);\n useEffect(() => {\n const node = markerElement(element);\n // The kit ring is drawn on the pin (MapViewPin), not Google's outline.\n if (node) node.style.outline = 'none';\n if (!node || !elements) return undefined;\n const registry = elements.current;\n registry.set(`pin:${marker.id}`, node);\n return () => {\n registry.delete(`pin:${marker.id}`);\n };\n }, [element, elements, marker.id]);\n useEffect(() => {\n const node = markerElement(element);\n if (!node || !onHover) return undefined;\n // Keyboard-style focus only: a mouse click focuses the pin too, and that\n // must not keep the card up once the pointer has gone.\n const focusIn = (event: FocusEvent): void => {\n if (isFocusVisible(event.target)) onHover(id, true, 'focus');\n };\n // A blur reports \"off\", mouse focus included, unless the pointer is still\n // on the pin. The host clears only the pin it holds.\n const focusOut = (event: FocusEvent): void => {\n if (node.contains(event.relatedTarget as Node | null)) return;\n if (pointerInside.current) return;\n onHover(id, false, 'focus');\n };\n // Escape hides the card without moving focus (WCAG 1.4.13).\n const keyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') onHover(id, false, 'focus');\n };\n node.addEventListener('focusin', focusIn);\n node.addEventListener('focusout', focusOut);\n node.addEventListener('keydown', keyDown);\n return () => {\n node.removeEventListener('focusin', focusIn);\n node.removeEventListener('focusout', focusOut);\n node.removeEventListener('keydown', keyDown);\n };\n }, [element, id, onHover]);\n\n return (\n <AdvancedMarker\n ref={setElement}\n position={marker.position}\n // Google names the pin from `title`: the marker element gets it as its\n // title and its accessible name (role button). vis.gl does not pass\n // `aria-label` or `data-*` on to the real marker, so `data-selected`\n // and `data-hovered` exist only on test fakes; QA cannot look for them\n // on a real pin.\n title={label}\n aria-label={label}\n data-selected={isSelected ? 'true' : 'false'}\n data-hovered={isHovered ? 'true' : 'false'}\n // Above every other pin while it is hovered.\n zIndex={isHovered ? 1000 : isSelected ? 500 : undefined}\n onClick={() => onSelect(id)}\n onMouseEnter={\n onHover\n ? () => {\n pointerInside.current = true;\n onHover(id, true, 'pointer');\n }\n : undefined\n }\n onMouseLeave={\n onHover\n ? () => {\n pointerInside.current = false;\n // Keyboard focus still on the pin keeps its card, as a\n // focused result card does when the pointer leaves it.\n const node = markerElement(element);\n const active = document.activeElement;\n if (\n node &&\n active &&\n node.contains(active) &&\n isFocusVisible(active)\n )\n return;\n onHover(id, false, 'pointer');\n }\n : undefined\n }\n >\n {/* The kit pin: the Alfadocs mark in the theme-stable marker violet with\n a white outline. Hovered: 1.4x from the tip, dark violet body. Both\n are theme-invariant, because the Google tiles do not follow the kit\n theme (3:1 against them, WCAG 1.4.11). */}\n <MapViewPin hovered={isHovered} />\n </AdvancedMarker>\n );\n}\n\n/** Google's world is 256px wide at zoom 0. */\nconst WORLD_PX = 256;\n\nconst toRadians = (degrees: number): number => (degrees * Math.PI) / 180;\n\n/** Web Mercator y of a latitude: the world spans -π..π. */\nfunction mercatorY(lat: number): number {\n const sin = Math.min(Math.max(Math.sin(toRadians(lat)), -0.9999), 0.9999);\n return Math.log((1 + sin) / (1 - sin)) / 2;\n}\n\nconst latitudeOf = (y: number): number =>\n (Math.atan(Math.sinh(y)) * 180) / Math.PI;\n\n/** The box round `points` (not antimeridian-aware: the kit maps Europe). */\nfunction boundsOfPoints(points: MapLatLng[]): google.maps.LatLngBoundsLiteral {\n let north = -90;\n let south = 90;\n let east = -180;\n let west = 180;\n for (const { lat, lng } of points) {\n north = Math.max(north, lat);\n south = Math.min(south, lat);\n east = Math.max(east, lng);\n west = Math.min(west, lng);\n }\n return { north, south, east, west };\n}\n\n/**\n * The centre and the closest whole zoom that shows every point inside a\n * `width` x `height` map, `padding` px in from each edge, no closer than\n * `maxZoom`. Exported for tests.\n */\nexport function cameraForPoints(\n points: MapLatLng[],\n width: number,\n height: number,\n padding: number,\n maxZoom: number,\n): { center: MapLatLng; zoom: number } {\n const { north, south, east, west } = boundsOfPoints(points);\n if (north === south && east === west) {\n return { center: { lat: north, lng: east }, zoom: maxZoom };\n }\n const top = mercatorY(north);\n const bottom = mercatorY(south);\n const center = {\n lat: latitudeOf((top + bottom) / 2),\n lng: (east + west) / 2,\n };\n const fit = (px: number, fraction: number): number =>\n fraction > 0 ? Math.log2(Math.max(px, 1) / WORLD_PX / fraction) : maxZoom;\n const zoom = Math.min(\n fit(width - 2 * padding, (east - west) / 360),\n fit(height - 2 * padding, (top - bottom) / (2 * Math.PI)),\n maxZoom,\n );\n return { center, zoom: Math.max(0, Math.floor(zoom)) };\n}\n\n/** A fit point's identity: its id, or else its coordinates. */\nconst pointKey = ({ id, lat, lng }: MapFitPoint): string =>\n id != null ? `id:${id}` : `at:${lat},${lng}`;\n\ninterface MapFitterProps {\n points: MapFitPoint[];\n fitKey: string;\n maxZoom: number;\n onCameraMove: () => void;\n}\n\nfunction MapFitter({\n points,\n fitKey,\n maxZoom,\n onCameraMove,\n}: MapFitterProps): null {\n const map = useMap();\n const fitted = useRef<{\n map: google.maps.Map;\n key: string;\n points: Set<string>;\n } | null>(null);\n // Whether the patient has panned or zoomed since the last fit: any\n // camera the kit did not set counts. Comparing with the camera the kit\n // last set (rather than flagging the kit's own call) holds whether\n // Google reports the change at once or later, and catches keyboard and\n // control-button pans, which fire no `dragstart`.\n const userMoved = useRef(false);\n const ownCamera = useRef<{ center: MapLatLng; zoom: number } | null>(null);\n\n useEffect(() => {\n if (!map) return undefined;\n const check = (): void => {\n const own = ownCamera.current;\n const center = map.getCenter()?.toJSON();\n const zoom = map.getZoom();\n const same =\n own != null &&\n zoom === own.zoom &&\n center != null &&\n Math.abs(center.lat - own.center.lat) < 1e-6 &&\n Math.abs(center.lng - own.center.lng) < 1e-6;\n if (!same) userMoved.current = true;\n };\n const listeners = [\n map.addListener('dragstart', () => {\n userMoved.current = true;\n }),\n map.addListener('center_changed', check),\n map.addListener('zoom_changed', check),\n ];\n return () => listeners.forEach((listener) => listener.remove());\n }, [map]);\n\n useEffect(() => {\n if (!map) return undefined;\n const last = fitted.current?.map === map ? fitted.current : null;\n if (last?.key === fitKey) return undefined;\n const keys = new Set(points.map(pointKey));\n const moveCamera = (center: MapLatLng, zoom: number): void => {\n ownCamera.current = { center, zoom };\n onCameraMove();\n // One step, never animated.\n map.moveCamera({ center, zoom });\n userMoved.current = false;\n };\n\n if (points.length === 0) {\n // Nothing to fit. MapView returns the camera to center/zoom when\n // fitting ends. Recording the empty set means the same results\n // coming back later count as a change and fit again.\n fitted.current = { map, key: fitKey, points: keys };\n return undefined;\n }\n\n // More of the same results (\"load more\"): every point already fitted\n // is still listed, by identity. If the patient has moved the map,\n // their view wins. A result replaced by another at the same address\n // is a different id, so that set refits.\n const grewOnly =\n last != null &&\n last.points.size > 0 &&\n [...last.points].every((point) => keys.has(point));\n if (grewOnly && userMoved.current) {\n fitted.current = { map, key: fitKey, points: keys };\n return undefined;\n }\n\n const div = map.getDiv();\n // Only a fake map has no element; there is nothing to measure.\n if (!(div instanceof Element)) return undefined;\n const fit = (): void => {\n fitted.current = { map, key: fitKey, points: keys };\n // Never more than a quarter of either side, so a small map still\n // has room to fit the points rather than zooming out to the world.\n const padding = Math.min(\n lengthTokenPx('--spacing-2xl', div, 48),\n div.clientWidth / 4,\n div.clientHeight / 4,\n );\n const { center, zoom } = cameraForPoints(\n points,\n div.clientWidth,\n div.clientHeight,\n padding,\n maxZoom,\n );\n moveCamera(center, zoom);\n };\n const laidOut = (): boolean => div.clientWidth > 0 && div.clientHeight > 0;\n if (laidOut() || typeof ResizeObserver === 'undefined') {\n fit();\n return undefined;\n }\n const observer = new ResizeObserver(() => {\n if (!laidOut()) return;\n observer.disconnect();\n fit();\n });\n observer.observe(div);\n return () => observer.disconnect();\n }, [map, fitKey, points, maxZoom, onCameraMove]);\n return null;\n}\n\n/* ------------------------------------------------------------------ */\n/* Marker clustering */\n/* */\n/* Inside <GoogleMap>, so it only exists once the consented map has */\n/* loaded. Clusters are Google markers, like the pins: with a `mapId` */\n/* an AdvancedMarker with kit-styled content, without one a classic */\n/* Marker with a drawn icon. Google therefore keeps them under its own */\n/* controls and attribution, pans and zooms the map from a drag or */\n/* wheel on them, moves them with the tiles, and gives them the same */\n/* focus handling as a pin. They are re-grouped when the zoom changes. */\n/* ------------------------------------------------------------------ */\n\n/**\n * Below a selected (500) or hovered (1000) pin. Plain pins have no z-index,\n * so Google orders them by latitude; they rarely overlap a cluster.\n */\nconst CLUSTER_Z_INDEX = 10;\n\n/** The zoom, in quarter steps so a pinch does not regroup on every frame. */\nfunction useMapZoom(fallback: number): number {\n const map = useMap();\n const [zoom, setZoom] = useState(fallback);\n useEffect(() => {\n if (\n !map ||\n typeof map.addListener !== 'function' ||\n typeof map.getZoom !== 'function'\n )\n return undefined;\n const read = (): void => {\n const current = map.getZoom();\n if (current != null) setZoom(Math.round(current * 4) / 4);\n };\n read();\n const listeners = (['zoom_changed', 'idle'] as const).map((event) =>\n map.addListener(event, read),\n );\n return () => listeners.forEach((listener) => listener.remove());\n }, [map]);\n return zoom;\n}\n\nconst CLUSTER_BADGE_CLASS = [\n 'ds:inline-flex ds:items-center ds:justify-center',\n // 44 px, 48 px in the accessible themes, with room for a long count.\n 'ds:[min-inline-size:var(--min-target-size)] ds:[min-block-size:var(--min-target-size)]',\n 'ds:px-[var(--spacing-xs)] ds:rounded-full',\n 'ds:border-2 ds:border-solid ds:border-[color:var(--map-marker-hover-ring)]',\n // The theme-stable map marker colour, as the pins (see KitAdvancedMarker).\n 'ds:bg-[var(--map-marker-color)] ds:text-[var(--map-marker-hover-ring)]',\n // A 1 px ink edge outside the white ring: the ring separates the marker on\n // dark tiles, the edge carries 3:1 on light ones (WCAG 1.4.11).\n 'ds:shadow-[0_0_0_1px_var(--map-marker-hover-color),var(--shadow-md)] ds:tabular-nums ds:select-none type-label',\n 'ds:forced-colors:border-[ButtonText]',\n // Google's marker element takes the focus; the ring is drawn on the badge,\n // and the element's own outline is removed (see KitClusterMarker). The ring\n // is the theme-stable deep ink, not `--ring`, which is light in the dark\n // themes: the tiles do not follow the kit theme. `--focus-ring-width` is\n // thicker in the accessible themes.\n 'ds:[gmp-advanced-marker:focus-visible_&]:outline-[length:var(--focus-ring-width)] ds:[gmp-advanced-marker:focus-visible_&]:outline-solid ds:[gmp-advanced-marker:focus-visible_&]:outline-[color:var(--map-marker-hover-color)] ds:[gmp-advanced-marker:focus-visible_&]:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:[gmp-advanced-marker:focus-visible_&]:forced-colors:outline-[CanvasText]',\n].join(' ');\n\n/**\n * The look of a cluster: a round `--map-marker-color` marker with the count. It is the\n * content of a Google marker, which carries the name, the focus and the click,\n * so it is hidden from assistive technology itself. Exported for the story\n * specimens; MapView draws it by itself.\n */\nexport function MapClusterMarker({ count }: { count: number }): ReactNode {\n return (\n <span\n aria-hidden=\"true\"\n data-component=\"map-view-cluster\"\n data-count={count}\n className={CLUSTER_BADGE_CLASS}\n >\n {count}\n </span>\n );\n}\n\n/** The kit colours and target size a classic cluster icon is drawn with. */\ninterface ClusterIconTheme {\n fill: string;\n ink: string;\n ring: string;\n hover: string;\n /** Body of the hovered or selected pin. */\n selected: string;\n target: number;\n}\n\n/** A token's colour as the browser resolves it at `context`. */\nfunction resolveColorToken(token: string, context: Element): string {\n const probe = document.createElement('span');\n probe.style.color = `var(${token})`;\n probe.style.display = 'none';\n // A map element not in the page (a fake, a portal in flight) has no style.\n (context.isConnected ? context : document.documentElement).append(probe);\n const color = getComputedStyle(probe).color;\n probe.remove();\n return color || 'currentColor';\n}\n\n/**\n * The classic-marker icon's theme (no `mapId`): resolved once from the map's\n * own element, so a theme root or a host override applies, and again when the\n * theme changes, not once per cluster.\n */\nfunction useClusterIconTheme(enabled: boolean): ClusterIconTheme | null {\n const map = useMap();\n const [theme, setTheme] = useState<ClusterIconTheme | null>(null);\n useEffect(() => {\n if (!enabled || !map) return undefined;\n // The map's own element; the page root if the map cannot say.\n const own = map.getDiv?.();\n const div = own instanceof Element ? own : document.documentElement;\n const read = (): void => {\n const next = {\n fill: resolveColorToken('--map-marker-color', div),\n ink: resolveColorToken('--map-marker-hover-ring', div),\n ring: resolveColorToken('--map-marker-hover-ring', div),\n hover: resolveColorToken('--map-marker-hover-color', div),\n selected: resolveColorToken('--map-marker-selected-color', div),\n target: lengthTokenPx('--min-target-size', div, 44),\n };\n // The same four values keep the same state, so no icon is redrawn.\n setTheme((prev) =>\n prev &&\n prev.fill === next.fill &&\n prev.ink === next.ink &&\n prev.ring === next.ring &&\n prev.hover === next.hover &&\n prev.selected === next.selected &&\n prev.target === next.target\n ? prev\n : next,\n );\n };\n read();\n // The kit theme classes sit on <html>, or on the nearest ThemeRoot.\n const observer = new MutationObserver(read);\n const options = {\n attributes: true,\n attributeFilter: ['class', 'data-theme'],\n };\n observer.observe(document.documentElement, options);\n const themeRoot = div.closest('[data-component=\"theme-root\"]');\n if (themeRoot) observer.observe(themeRoot, options);\n return () => observer.disconnect();\n }, [map, enabled]);\n return theme;\n}\n\n/**\n * The classic-marker icon: the same round marker as an SVG in `theme`'s\n * colours. The count is the marker's label.\n */\nfunction clusterIcon(\n count: number,\n { fill, ink, ring, hover: edge, target }: ClusterIconTheme,\n): {\n icon: google.maps.Icon;\n label: google.maps.MarkerLabel;\n} {\n const width = Math.max(target, 20 + String(count).length * 12);\n // The ink edge (1 px outside the white ring) is drawn first, under the ring.\n const shape = `x=\"2\" y=\"2\" width=\"${width - 4}\" height=\"${target - 4}\" rx=\"${target / 2}\"`;\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${target}\">` +\n `<rect ${shape} fill=\"none\" stroke=\"${edge}\" stroke-width=\"4\"/>` +\n `<rect ${shape} fill=\"${fill}\" stroke=\"${ring}\" stroke-width=\"2\"/></svg>`;\n const maps = (globalThis as { google?: { maps?: typeof google.maps } }).google\n ?.maps;\n const icon = {\n url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,\n scaledSize: maps ? new maps.Size(width, target) : undefined,\n anchor: maps ? new maps.Point(width / 2, target / 2) : undefined,\n } as google.maps.Icon;\n return {\n icon,\n label: { text: String(count), color: ink, fontWeight: '600' },\n };\n}\n\nconst ClassicIconThemeContext = createContext<ClusterIconTheme | null>(null);\n\n/** Resolves the kit colours once for every classic marker below it. */\nfunction ClassicIconThemeProvider({\n enabled,\n children,\n}: {\n enabled: boolean;\n children: ReactNode;\n}): ReactNode {\n const theme = useClusterIconTheme(enabled);\n return (\n <ClassicIconThemeContext.Provider value={theme}>\n {children}\n </ClassicIconThemeContext.Provider>\n );\n}\n\n/**\n * The classic-marker icon (no `mapId`): the same pin as the live one, drawn as\n * a data-URL SVG in concrete colours, anchored at the tip. Hovered it is 1.4x.\n */\nfunction pinIcon(\n { fill, ring, hover, selected }: ClusterIconTheme,\n hovered: boolean,\n): google.maps.Icon {\n const scale = hovered ? MAP_PIN_HOVER_SCALE : 1;\n const width = MAP_PIN_WIDTH * scale;\n const height = MAP_PIN_HEIGHT * scale;\n const svg = mapPinSvg(hovered ? selected : fill, ring, hover, {\n width,\n height,\n });\n const maps = (globalThis as { google?: { maps?: typeof google.maps } }).google\n ?.maps;\n return {\n url: `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`,\n scaledSize: maps ? new maps.Size(width, height) : undefined,\n anchor: maps ? new maps.Point(width / 2, height) : undefined,\n } as google.maps.Icon;\n}\n\nfunction KitClassicMarker({\n marker,\n label,\n isSelected,\n isHovered,\n onSelect,\n onHover,\n}: {\n marker: MapMarker;\n label: string;\n isSelected: boolean;\n isHovered: boolean;\n onSelect: (id: string) => void;\n onHover: MapViewProps['onMarkerHover'];\n}): ReactNode {\n const theme = useContext(ClassicIconThemeContext);\n const icon = useMemo(\n () => (theme ? pinIcon(theme, isHovered) : undefined),\n [theme, isHovered],\n );\n // Nothing until the colours are read: Google's red pin must not flash first.\n if (!icon) return null;\n return (\n <Marker\n position={marker.position}\n title={label}\n icon={icon}\n zIndex={isHovered ? 1000 : isSelected ? 500 : undefined}\n onClick={() => onSelect(marker.id)}\n onMouseOver={\n onHover ? () => onHover(marker.id, true, 'pointer') : undefined\n }\n onMouseOut={\n onHover ? () => onHover(marker.id, false, 'pointer') : undefined\n }\n />\n );\n}\n\ntype MarkerElements = MutableRefObject<Map<string, HTMLElement>>;\n\nfunction KitClusterMarker({\n cluster,\n label,\n mapId,\n iconTheme,\n elements,\n onActivate,\n}: {\n cluster: MarkerCluster<MapMarker>;\n label: string;\n mapId: boolean;\n iconTheme: ClusterIconTheme | null;\n elements: MarkerElements;\n onActivate: (cluster: MarkerCluster<MapMarker>) => void;\n}): ReactNode {\n const [element, setElement] =\n useState<google.maps.marker.AdvancedMarkerElement | null>(null);\n const key = `cluster:${cluster.id}`;\n useEffect(() => {\n const node = markerElement(element);\n if (!node) return undefined;\n const registry = elements.current;\n registry.set(key, node);\n // Only the kit ring on the badge shows (MapClusterMarker).\n node.style.outline = 'none';\n return () => {\n registry.delete(key);\n };\n }, [element, elements, key]);\n const count = cluster.members.length;\n const classic = useMemo(\n () => (mapId || !iconTheme ? null : clusterIcon(count, iconTheme)),\n [mapId, count, iconTheme],\n );\n if (mapId) {\n return (\n <AdvancedMarker\n ref={setElement}\n position={cluster.position}\n title={label}\n aria-label={label}\n zIndex={CLUSTER_Z_INDEX}\n anchorPoint={['50%', '50%']}\n onClick={() => onActivate(cluster)}\n >\n <MapClusterMarker count={count} />\n </AdvancedMarker>\n );\n }\n return (\n <Marker\n position={cluster.position}\n title={label}\n zIndex={CLUSTER_Z_INDEX}\n icon={classic?.icon}\n label={classic?.label}\n onClick={() => onActivate(cluster)}\n />\n );\n}\n\n/** The live region is emptied this long before it is filled again. */\nconst ANNOUNCE_DELAY_MS = 100;\n\n/** How long a focus move waits for Google to create the new markers. */\nconst FOCUS_FRAMES = 30;\n\ninterface PendingZoom {\n ids: Set<string>;\n centre: MapLatLng;\n}\n\n/**\n * Renders `markers` as single pins plus numbered clusters. The selected and\n * the hovered pin are taken out first, so neither can be hidden inside a\n * cluster.\n */\nfunction ClusteredMarkers({\n markers,\n fallbackZoom,\n selectedMarkerId,\n hoveredMarkerId,\n renderMarker,\n mapId,\n frame,\n elements,\n labelFor,\n onCameraMove,\n onClusterClick,\n}: {\n markers: MapMarker[];\n fallbackZoom: number;\n selectedMarkerId: string | null;\n hoveredMarkerId: string | null;\n renderMarker: (marker: MapMarker) => ReactNode;\n mapId: boolean;\n frame: HTMLElement | null;\n elements: MarkerElements;\n labelFor: (count: number) => string;\n onCameraMove: () => void;\n onClusterClick?: MapViewProps['onClusterClick'];\n}): ReactNode {\n const map = useMap();\n const { t } = useTranslation('ui');\n const zoom = useMapZoom(fallbackZoom);\n const iconTheme = useContext(ClassicIconThemeContext);\n // Grouped once per zoom, whatever is hovered or chosen, so a hover never\n // reshuffles the clusters around it.\n const grouped = useMemo(\n () => clusterMarkerSet(markers, zoom),\n [markers, zoom],\n );\n const { singles, clusters } = useMemo(() => {\n const out = (marker: MapMarker): boolean =>\n marker.id === selectedMarkerId || marker.id === hoveredMarkerId;\n const singles = grouped.singles.slice();\n const clusters: MarkerCluster<MapMarker>[] = [];\n for (const cluster of grouped.clusters) {\n const members = cluster.members.filter((marker) => !out(marker));\n // Pins taken out show on their own pin, where the cluster was.\n singles.push(...cluster.members.filter(out));\n if (members.length > 1) clusters.push({ ...cluster, members });\n else singles.push(...members);\n }\n return { singles, clusters };\n }, [grouped, selectedMarkerId, hoveredMarkerId]);\n\n const [announcement, setAnnouncement] = useState('');\n const pending = useRef<PendingZoom | null>(null);\n const announceTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n useEffect(() => () => clearTimeout(announceTimer.current), []);\n\n // Zoom to the cluster's pins in one step (never animated, so reduced\n // motion is honoured). It always zooms in at least one whole level, and\n // stops at the zoom where pins no longer cluster. The move is the kit's,\n // not a pan: the host hears about it from `onClusterClick`.\n const activate = (cluster: MarkerCluster<MapMarker>): void => {\n const div = map?.getDiv?.();\n if (!map || !(div instanceof Element)) return;\n const padding = Math.min(\n lengthTokenPx('--spacing-2xl', div, 48),\n div.clientWidth / 4,\n div.clientHeight / 4,\n );\n const fit = cameraForPoints(\n cluster.members.map((member) => member.position),\n div.clientWidth,\n div.clientHeight,\n padding,\n CLUSTER_OFF_ZOOM,\n );\n const zoomNow = map.getZoom() ?? zoom;\n const next = Math.min(\n CLUSTER_OFF_ZOOM,\n Math.max(fit.zoom, Math.ceil(zoomNow + 1)),\n );\n const count = cluster.members.length;\n pending.current = {\n ids: new Set(cluster.members.map((member) => member.id)),\n centre: cluster.position,\n };\n // Cleared first: a second identical message is read only if the region\n // changed in between.\n setAnnouncement('');\n clearTimeout(announceTimer.current);\n announceTimer.current = setTimeout(\n () =>\n setAnnouncement(t('mapView.clusterZoomed', { label: labelFor(count) })),\n ANNOUNCE_DELAY_MS,\n );\n onCameraMove();\n onClusterClick?.({\n count,\n markerIds: cluster.members.map((member) => member.id),\n });\n map.moveCamera({ center: fit.center, zoom: next });\n };\n\n // The activated cluster is gone after the zoom. Keyboard focus moves to the\n // new cluster nearest to it that holds some of its pins, else to one of its\n // pins, else (classic markers cannot take focus) to the map region.\n useEffect(() => {\n const zoomed = pending.current;\n if (!zoomed) return undefined;\n let frameId = 0;\n let tries = 0;\n const target = (): string | null => {\n let best: { key: string; distance: number } | null = null;\n for (const cluster of clusters) {\n if (!cluster.members.some((member) => zoomed.ids.has(member.id)))\n continue;\n const distance =\n (cluster.position.lat - zoomed.centre.lat) ** 2 +\n (cluster.position.lng - zoomed.centre.lng) ** 2;\n if (!best || distance < best.distance)\n best = { key: `cluster:${cluster.id}`, distance };\n }\n if (best) return best.key;\n const pin = singles.find((marker) => zoomed.ids.has(marker.id));\n return pin ? `pin:${pin.id}` : null;\n };\n const focus = (): void => {\n const key = target();\n const node = key ? elements.current.get(key) : null;\n if (node) {\n node.focus({ preventScroll: true });\n // A marker Google has not attached yet ignores focus: try again.\n if (node.contains(document.activeElement)) {\n pending.current = null;\n return;\n }\n }\n tries += 1;\n if (tries < FOCUS_FRAMES && mapId) {\n frameId = requestAnimationFrame(focus);\n return;\n }\n pending.current = null;\n const region = frame?.closest<HTMLElement>('[data-component=\"map-view\"]');\n if (region) {\n if (!region.hasAttribute('tabindex'))\n region.setAttribute('tabindex', '-1');\n region.focus({ preventScroll: true });\n }\n };\n frameId = requestAnimationFrame(focus);\n return () => cancelAnimationFrame(frameId);\n }, [clusters, singles, elements, frame, mapId]);\n\n return (\n <>\n {singles.map(renderMarker)}\n {clusters.map((cluster) => (\n <KitClusterMarker\n key={`${Math.floor(zoom)}:${cluster.id}`}\n cluster={cluster}\n label={labelFor(cluster.members.length)}\n mapId={mapId}\n iconTheme={iconTheme}\n elements={elements}\n onActivate={activate}\n />\n ))}\n {frame\n ? createPortal(\n <div role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {announcement}\n </div>,\n frame,\n )\n : null}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Library gates */\n/* */\n/* APIProvider reports LOADED as soon as `google.maps.importLibrary` */\n/* exists when another loader installed it (Google's bootstrap, a host */\n/* Places loader), before the `maps` library itself has arrived. <Map> */\n/* would then run `new google.maps.Map` and throw, taking the host's */\n/* React tree down. The map waits for `maps`, and the markers for */\n/* `marker`, whoever loaded the API. Before the API loads, <Map> only */\n/* renders its empty container, and once a constructor exists nothing */\n/* can throw, so both pass straight through as they always did. */\n/* ------------------------------------------------------------------ */\n\ntype GoogleMapsGlobal = {\n google?: { maps?: { Map?: unknown; Marker?: unknown } };\n};\nconst hasConstructor = (name: 'Map' | 'Marker'): boolean =>\n typeof (globalThis as GoogleMapsGlobal).google?.maps?.[name] === 'function';\n\n/** What the map area shows while Google's map library is on its way.\n * Exported for its story; not part of the public API. */\nexport function MapViewLoading(): ReactNode {\n const { t } = useTranslation('ui');\n return (\n <div\n role=\"status\"\n data-testid=\"map-view-loading\"\n className=\"ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <Skeleton variant=\"rectangular\" width=\"100%\" height=\"100%\" />\n <VisuallyHidden>{t('common.loading')}</VisuallyHidden>\n </div>\n );\n}\n\n/** The host's map image while Google's map is on its way (HTP-7348). */\nfunction MapPreviewLoading({\n preview,\n onError,\n}: {\n preview: MapViewStaticPreview;\n onError: () => void;\n}): ReactNode {\n const { t } = useTranslation('ui');\n return (\n <div\n data-testid=\"map-view-loading\"\n className=\"ds:relative ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <MapPreviewImage preview={preview} onError={onError} />\n {/* Only \"Loading\" is announced: the image stays out of the live region. */}\n <div role=\"status\">\n <VisuallyHidden>{t('common.loading')}</VisuallyHidden>\n </div>\n </div>\n );\n}\n\nfunction MapPreviewImage({\n preview,\n onError,\n}: {\n preview: MapViewStaticPreview;\n onError: () => void;\n}): ReactNode {\n const hasSources = (preview.sources?.length ?? 0) > 0;\n const areaRef = useRef<HTMLDivElement>(null);\n const picked = usePickedSourceIndex(preview.sources, areaRef);\n // The area box is measured first and the image only rendered after, so the\n // browser is never asked for a second picture.\n const [measured, setMeasured] = useState(!hasSources);\n useLayoutEffect(() => {\n setMeasured(true);\n }, []);\n const source =\n (picked !== null ? preview.sources?.[picked] : undefined) ?? preview;\n const image = (\n <img\n src={source.src}\n srcSet={source.srcSet}\n alt={preview.alt}\n width={source.width}\n height={source.height}\n loading=\"lazy\"\n decoding=\"async\"\n onError={onError}\n data-testid=\"map-view-preview-image\"\n // The whole image, never cropped: Google's logo and copyright line sit\n // at its edges and must stay visible. The muted frame shows round it.\n className=\"ds:block ds:[inline-size:100%] ds:[block-size:100%] ds:[object-fit:contain]\"\n />\n );\n if (!hasSources) return image;\n return (\n <div\n ref={areaRef}\n data-testid=\"map-view-preview-area\"\n className=\"ds:absolute ds:inset-0\"\n >\n {measured ? image : null}\n </div>\n );\n}\n\nfunction MapsLibraryGate({\n children,\n pending,\n holdUntilLoaded = false,\n}: {\n children: ReactNode;\n pending: ReactNode;\n /**\n * Keep `pending` up while the API itself loads too. With a host map image\n * (HTP-7348) the image stays until the map can draw, instead of an empty\n * frame.\n */\n holdUntilLoaded?: boolean;\n}): ReactNode {\n const apiIsLoaded = useApiIsLoaded();\n const maps = useMapsLibrary('maps');\n if (maps || hasConstructor('Map')) return children;\n if (!apiIsLoaded && !holdUntilLoaded) return children;\n return pending;\n}\n\nfunction MarkerLibraryGate({ children }: { children: ReactNode }): ReactNode {\n const marker = useMapsLibrary('marker');\n return marker || hasConstructor('Marker') ? children : null;\n}\n\n/**\n * On unmount, reports whether focus was inside `ref`. A layout effect's\n * cleanup runs before React removes the nodes, so the answer is exact.\n */\nfunction useReportFocusOnUnmount(\n ref: MutableRefObject<HTMLDivElement | null>,\n report: ((hadFocus: boolean) => void) | undefined,\n): void {\n useLayoutEffect(() => {\n const node = ref.current;\n return () => {\n report?.(Boolean(node && node.contains(document.activeElement)));\n };\n }, [ref, report]);\n}\n\n/* ------------------------------------------------------------------ */\n/* Static preview (HTP-7348) */\n/* */\n/* The host's own map image fills the frame, with \"Show interactive */\n/* map\" on it. Nothing here contacts Google. */\n/* ------------------------------------------------------------------ */\n\ninterface MapStaticPreviewProps {\n preview: MapViewStaticPreview;\n onLoadMap: () => void;\n consentCta: ReactNode | undefined;\n onImageError: () => void;\n /** The consent disclosure, as on the text placeholder. */\n description: ReactNode;\n /** \"Open in Google Maps\": a plain link, so it needs no consent. */\n onUnmount?: (hadFocus: boolean) => void;\n}\n\nfunction MapStaticPreview({\n preview,\n onLoadMap,\n consentCta,\n onImageError,\n description,\n onUnmount,\n}: MapStaticPreviewProps): ReactNode {\n const { t } = useTranslation('ui');\n const descriptionId = useId();\n const gateRef = useRef<HTMLDivElement>(null);\n useReportFocusOnUnmount(gateRef, onUnmount);\n return (\n <div\n ref={gateRef}\n className=\"ds:absolute ds:inset-0 ds:@container/mapbar ds:bg-[var(--muted)]\"\n data-testid=\"map-view-preview\"\n >\n <MapPreviewImage preview={preview} onError={onImageError} />\n {/* A slim frosted bar across the TOP: Google's logo and copyright line\n sit at the bottom of the image and must stay clear. A host can move\n it below its own floating controls with\n --map-preview-inset-block-start. */}\n <div className=\"ds:absolute ds:start-0 ds:end-0 ds:top-[var(--map-preview-inset-block-start,var(--spacing-sm))] ds:px-[var(--spacing-sm)]\">\n {/* The popover colour at about 96% over a blur (the kit's glass alpha, solid in the accessible themes), so the map shows round it and text\n keeps its contrast over a busy image. Solid under the accessible\n themes' token, more contrast, reduced transparency and forced\n colours. The disclosure says what the click does before the\n visitor makes it. Text and button sit centred; in a wide map\n (640px and up) they share one centred line. No \"Open in Google\n Maps\" link here: the page shows it under the address. */}\n <div\n data-testid=\"map-view-preview-plate\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:rounded-[var(--radius-md)] ds:bg-[color-mix(in_srgb,var(--popover)_calc(100%-(100%-var(--glass-surface-alpha,85%))*0.27),transparent)] ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-sm)] ds:backdrop-blur-md ds:contrast-more:bg-[var(--popover)] ds:[@media(prefers-reduced-transparency:reduce)]:bg-[var(--popover)] ds:forced-colors:bg-[Canvas] ds:[:where(&)]:border-[length:var(--surface-border-width,var(--border-width-sm))] ds:border-[color:var(--border)] ds:shadow-[var(--shadow-sm)] ds:items-center ds:text-center ds:@[640px]/mapbar:flex-row ds:@[640px]/mapbar:justify-center ds:@[640px]/mapbar:gap-[var(--spacing-md)]\"\n >\n <p\n id={descriptionId}\n className=\"type-meta ds:m-0 ds:min-w-0 ds:text-[var(--foreground)] ds:@[640px]/mapbar:text-start\"\n >\n {description}\n </p>\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:justify-center ds:gap-x-[var(--spacing-sm)] ds:gap-y-[var(--spacing-2xs)]\">\n {consentCta ?? (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"sm\"\n className=\"ds:min-h-[var(--min-target-size)] ds:sm:min-h-[var(--min-target-size)]\"\n onClick={onLoadMap}\n aria-describedby={descriptionId}\n data-testid=\"map-view-load-cta\"\n >\n {t('mapView.consentTitle')}\n </Button>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Consent placeholder */\n/* ------------------------------------------------------------------ */\n\ninterface ConsentPlaceholderProps {\n address: string | undefined;\n contentLang: string | undefined;\n description: ReactNode;\n /**\n * Click handler for the \"Load map\" button. Always rendered. When the\n * consumer provides `onConsentRequest` it owns the state transition\n * (typically: open a GDPR dialog, then flip `consentGranted` to true\n * after acceptance). When omitted, the kit's `MapView` falls back to\n * its own internal opt-in toggle — clicking grants consent for this\n * MapView instance only, no consumer plumbing required. This default\n * fixes a regression where the button was conditionally rendered and\n * any consumer omitting `onConsentRequest` left the patient with no\n * way to load the map (booking-website 0.31.0 hit this).\n */\n onLoadMap: () => void;\n openInGoogleMapsHref: string;\n /**\n * Custom CTA — replaces the kit-rendered `<Button>` when the consumer\n * needs a different surface (e.g. a Tooltip-wrapped button with a\n * policy link). The kit's button is still the recommended path; this\n * slot exists for consumers operating under specific compliance UX\n * requirements (jurisdiction, named controller, policy link). Pass\n * any `ReactNode`.\n */\n consentCta: ReactNode | undefined;\n onUnmount?: (hadFocus: boolean) => void;\n}\n\nfunction ConsentPlaceholder({\n address,\n contentLang,\n description,\n onLoadMap,\n openInGoogleMapsHref,\n consentCta,\n onUnmount,\n}: ConsentPlaceholderProps): ReactNode {\n const { t } = useTranslation('ui');\n const gateRef = useRef<HTMLDivElement>(null);\n useReportFocusOnUnmount(gateRef, onUnmount);\n return (\n <div\n ref={gateRef}\n className={consentChromeVariants()}\n data-testid=\"map-view-consent\"\n >\n <MapPin\n aria-hidden=\"true\"\n className=\"ds:size-6 ds:text-[var(--muted-foreground)]\"\n />\n <p className={consentTitleVariants()}>{t('mapView.consentTitle')}</p>\n {address ? (\n <p\n className={consentDescriptionVariants()}\n data-testid=\"map-view-address\"\n lang={contentLang}\n >\n {address}\n </p>\n ) : null}\n <p className={consentDescriptionVariants()}>{description}</p>\n <div\n className=\"ds:flex ds:gap-[var(--spacing-sm)] ds:flex-wrap ds:items-center ds:justify-center\"\n data-testid=\"map-view-consent-actions\"\n >\n {consentCta ?? (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"sm\"\n onClick={onLoadMap}\n data-testid=\"map-view-load-cta\"\n >\n {t('mapView.consentCta')}\n </Button>\n )}\n <a\n href={openInGoogleMapsHref}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] ds:text-[var(--primary-readable)] ds:underline ds:underline-offset-2\"\n >\n {t('mapView.openInGoogleMaps')}\n <ExternalLink aria-hidden=\"true\" className=\"ds:size-3\" />\n </a>\n </div>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* MapView */\n/* ------------------------------------------------------------------ */\n\nexport const MapView = forwardRef<MapViewHandle, MapViewProps>(\n (\n {\n id,\n apiKey,\n center,\n zoom = 15,\n markers,\n selectedMarkerId,\n hoveredMarkerId,\n renderMarkerCard,\n onMarkerHover,\n onMarkerClick,\n onBoundsChange,\n consentGranted = false,\n onConsentRequest,\n staticPreview,\n address,\n contentLang,\n consentDescription,\n consentCta,\n mapId,\n size,\n surface,\n ariaLabel,\n fitToPoints,\n fitKey,\n fitMaxZoom = 15,\n clusterMarkers = 'auto',\n clusterLabel,\n onClusterClick,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n // Each pin's and cluster's element, so focus can be moved to one.\n const markerElements = useRef(new Map<string, HTMLElement>());\n const fitPoints = useMemo(() => fitToPoints ?? [], [fitToPoints]);\n // Identity and place: a result that keeps its id but moves (a\n // corrected address) still refits. \"Only grew\" uses identity alone.\n const resolvedFitKey = useMemo(\n () =>\n fitKey ??\n fitPoints\n .map((point) => `${pointKey(point)}@${point.lat},${point.lng}`)\n .sort()\n .join('|'),\n [fitKey, fitPoints],\n );\n // While there are points, the fit owns the camera (see `fitToPoints`).\n const fitting = fitPoints.length > 0;\n const wasFitting = useRef(fitting);\n const rawId = useId();\n const instanceId = useMemo(\n () => `map-${rawId.replace(/[^a-zA-Z0-9-_]/g, '')}`,\n [rawId],\n );\n\n // Reduced-motion gating. Google's own pan / zoom animations\n // happen inside the Maps JS API and the kit can't reach into\n // those — but the kit's own programmatic `panTo` calls (triggered\n // by prop changes via the controlled-sync effect below) can be\n // suppressed. When `prefers-reduced-motion: reduce` is set, we\n // skip the synthetic `panTo` so the consumer's prop change\n // results in a hard recenter rather than an animated glide.\n const [reducedMotion, setReducedMotion] = useState(false);\n useEffect(() => {\n if (typeof window === 'undefined') return undefined;\n const mql = window.matchMedia('(prefers-reduced-motion: reduce)');\n const sync = (): void => setReducedMotion(mql.matches);\n sync();\n mql.addEventListener('change', sync);\n return () => mql.removeEventListener('change', sync);\n }, []);\n\n // The \"Open in Google Maps\" fallback link — works without any\n // third-party script load. Uses the modern `?api=1&query=…` form\n // which the booking site already employs.\n const openInGoogleMapsHref = useMemo(() => {\n const query = address\n ? encodeURIComponent(address)\n : `${center.lat},${center.lng}`;\n return `https://www.google.com/maps/search/?api=1&query=${query}`;\n }, [address, center.lat, center.lng]);\n\n // 0.33.5: removed the kit's `getGoogleMapsTheme()` JSON-styles\n // override. Previously, when `mapId` was absent the kit painted\n // water / roads / landscape with kit tokens — a calm muted-grey\n // palette that diverged from the standard Google Maps appearance\n // users expect in every other maps surface they touch. Google's\n // default (blue water, beige land, named-road colours) is more\n // familiar and reads as a \"real map\" rather than a stylised one.\n //\n // Consumers wanting brand-themed colours opt in via `mapId` and\n // configure the palette in Google Cloud Console — that's the\n // Google-recommended path. The `getGoogleMapsTheme` export stays\n // in `src/tokens/google-maps-theme.ts` so consumers wanting the\n // old behaviour can apply the styles themselves, but the kit\n // does NOT install them by default any more.\n //\n // Side benefit: dropped the MutationObserver that previously\n // watched documentElement.class for theme switches — no more\n // observer churn on every theme toggle.\n\n // Imperative handle plumbing — populated by the inner bridge once\n // the APIProvider tree resolves a `Map` instance.\n const handleRef = useRef<MapBridgeHandle | null>(null);\n const userInteraction = useRef<MapInteraction>({\n pendingInput: false,\n moved: false,\n programmatic: false,\n });\n const onCameraMove = useCallback(() => {\n userInteraction.current = {\n pendingInput: false,\n moved: false,\n programmatic: true,\n };\n }, []);\n const handleMarkerClick = useCallback(\n (markerId: string | null) => {\n onCameraMove();\n onMarkerClick?.(markerId);\n },\n [onCameraMove, onMarkerClick],\n );\n // The map frame, so the hovered pin's card can be drawn into it.\n const [frame, setFrame] = useState<HTMLDivElement | null>(null);\n const hoveredMarker = useMemo(\n () =>\n hoveredMarkerId != null && renderMarkerCard\n ? ((markers ?? []).find((marker) => marker.id === hoveredMarkerId) ??\n null)\n : null,\n [hoveredMarkerId, renderMarkerCard, markers],\n );\n const onHandleReady = useCallback((handle: MapBridgeHandle | null) => {\n handleRef.current = handle;\n }, []);\n\n // Read in the OUTER component, not through `handleRef` — these three\n // must still answer when the map subtree is unmounted, which is\n // exactly the situation an agent needs explaining. Assigned during\n // render, below, once `effectiveConsent` exists.\n const consentRef = useRef(false);\n const selectedMarkerIdRef = useRef<string | null>(null);\n const markerCountRef = useRef(0);\n\n const buildHandle = useCallback(\n (): MapViewHandle => ({\n panTo: (position) => handleRef.current?.panTo(position),\n setZoom: (z) => handleRef.current?.setZoom(z),\n selectMarker: (markerId) => handleRef.current?.selectMarker(markerId),\n // Without `isConsentGranted` an agent has no way to learn that\n // its pan / zoom / select was swallowed by the GDPR gate rather\n // than applied: under the default (`consentGranted={false}`) the\n // whole Maps subtree is unmounted and every action optional-chains\n // into nothing.\n isConsentGranted: () => consentRef.current,\n getCenter: () => handleRef.current?.getCenter() ?? null,\n getZoom: () => handleRef.current?.getZoom() ?? null,\n getSelectedMarkerId: () => selectedMarkerIdRef.current,\n getMarkerCount: () => markerCountRef.current,\n }),\n [],\n );\n useImperativeHandle(ref, buildHandle, [buildHandle]);\n\n const agentHandle = useMemo<MapViewHandle>(\n () => buildHandle(),\n [buildHandle],\n );\n useAgentRegistration(mapViewAgent, agentHandle, id);\n\n // Controlled-feeling propagation: vis.gl's `<Map>` reads `center`\n // and `zoom` as initial values only (via `defaultCenter` /\n // `defaultZoom`). To honour the documented \"live prop\" contract\n // we shim post-mount sync through the imperative bridge — a\n // parent that swaps offices by changing `center` re-centres the\n // map. User drag does NOT trigger this because the prop doesn't\n // change on user gesture, so the effect doesn't fire and the\n // map stays where the user left it.\n //\n // While fitting, the fit owns the camera and both syncs stand down.\n // `fitting` is a dependency, so when fitting ends (the points run out\n // or `fitToPoints` is removed) the camera returns to `center` / `zoom`\n // even if neither changed. That return is one step, not animated, so\n // it applies under reduced motion too.\n useEffect(() => {\n const fitEnded = wasFitting.current && !fitting;\n wasFitting.current = fitting;\n if (fitting) return;\n if (fitEnded) {\n handleRef.current?.jumpTo(center, zoom);\n return;\n }\n // Skip the animated pan under reduced-motion preference; the\n // map will still recentre on the next render via the agent\n // handle's `panTo`, but Google's own glide animation runs\n // either way (kit can't reach inside it). At least the kit's\n // own initiated pan is gated.\n if (reducedMotion) return;\n handleRef.current?.panTo(center);\n // Including `center` (the object reference) instead of\n // `center.lat`/`center.lng` would fire on every parent render\n // even when the coordinates are identical, fighting user drag.\n // `zoom` is read only when fitting ends; its own sync is below.\n // The destructured deps are intentional.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [center.lat, center.lng, reducedMotion, fitting]);\n useEffect(() => {\n if (fitting) return;\n handleRef.current?.setZoom(zoom);\n }, [zoom, fitting]);\n\n // Internal opt-in fallback. The `consentGranted` prop is the\n // controlled path — consumer flips it after capturing GDPR\n // acceptance through their own dialog and re-renders. When the\n // consumer omits `onConsentRequest` they're opting out of that\n // workflow, so the kit needs a sensible default click handler on\n // the placeholder's button. `internalConsent` tracks that opt-in\n // for this instance only; the map renders when either signal is\n // truthy. The 0.31.0 regression was conditionally rendering the\n // button on `onConsentRequest` — patients of consumers without the\n // hook saw the placeholder with no way to proceed.\n const [internalConsent, setInternalConsent] = useState(false);\n const effectiveConsent = consentGranted || internalConsent;\n // Mirror the three props-derived agent state slots. Assigned during\n // render so a read immediately after a prop change sees the settled\n // value. Ids and a count only — never `MapMarker.label`, never the\n // `address` prop (consumer-supplied free text, PII in any\n // patient-address deployment), never `apiKey`.\n consentRef.current = effectiveConsent;\n selectedMarkerIdRef.current = selectedMarkerId ?? null;\n markerCountRef.current = (markers ?? []).length;\n // HTP-7348: the preview image falls back to the text placeholder when it\n // cannot load. The failure belongs to that address: a new image gets a\n // fresh try.\n const [failedSrc, setFailedSrc] = useState<string | null>(null);\n const preview =\n staticPreview && staticPreview.src !== failedSrc\n ? staticPreview\n : undefined;\n const previewSrc = staticPreview?.src;\n const onPreviewError = useCallback(() => {\n if (previewSrc) setFailedSrc(previewSrc);\n }, [previewSrc]);\n // When consent replaces the button the visitor used (the kit's, or a\n // host `consentCta`), focus would fall to the page. It moves to the map\n // region instead, as soon as consent flips.\n const rootRef = useRef<HTMLDivElement>(null);\n const focusOnLoad = useRef(false);\n // Set as the gate (preview or text placeholder) unmounts, before React\n // removes it: whether focus was inside it at that moment. A grant from\n // elsewhere (the cookie banner) leaves it false, so focus stays put.\n const focusWasInGate = useRef(false);\n const onGateUnmount = useCallback((hadFocus: boolean) => {\n focusWasInGate.current = hadFocus;\n }, []);\n useEffect(() => {\n if (!effectiveConsent) return;\n const active = document.activeElement;\n const focusLost = !active || active === document.body;\n const move = focusOnLoad.current || (focusWasInGate.current && focusLost);\n focusOnLoad.current = false;\n focusWasInGate.current = false;\n if (move) rootRef.current?.focus({ preventScroll: true });\n }, [effectiveConsent]);\n const handleLoadMap = useCallback(() => {\n focusOnLoad.current = true;\n if (onConsentRequest) {\n onConsentRequest();\n // The consumer typically flips `consentGranted` to true on the\n // next render. Flip the internal state too so the map still\n // loads if the consumer forgets to update its prop (the\n // common-case footgun this fallback exists to defuse).\n setInternalConsent(true);\n return;\n }\n setInternalConsent(true);\n }, [onConsentRequest]);\n\n const renderMarker = (marker: MapMarker): ReactNode => {\n // Selection state is exposed to the consumer via\n // `data-selected` on the marker root so consumers can\n // style their own InfoWindow / sidebar highlight from\n // it. The kit does NOT swap the Pin's fill colour on\n // selection — `--accent` (magenta-500 in light theme)\n // fails AA on white per 05-accessibility.mdx, and the\n // alternative tokens that pass (`--info`, `--success`)\n // carry semantic baggage. Visual selection state is\n // the consumer's call.\n const isSelected =\n selectedMarkerId != null && selectedMarkerId === marker.id;\n const isHovered =\n hoveredMarkerId != null && hoveredMarkerId === marker.id;\n const accessibleLabel = t('mapView.markerLabel', {\n label: marker.label ?? marker.id,\n });\n // Pin implementation switches on whether the\n // consumer supplied `mapId`. Google's\n // AdvancedMarkerElement requires the Map instance to\n // be initialised with a valid Map ID — without one\n // the API logs an error (\"La mappa è stata\n // inizializzata senza un ID mappa valido\") and any\n // AdvancedMarker rendered against that map fails to\n // appear. Cloud Map IDs are an opt-in feature; many\n // consumers (booking-website env without\n // GOOGLE_MAPS_MAP_ID, the OSS demo, etc.) legitimately\n // omit it.\n //\n // Fall back to the classic <Marker> (a thin wrapper\n // around google.maps.Marker) when mapId is absent.\n // Google has formally deprecated google.maps.Marker\n // but it still renders cleanly and produces no\n // console errors; the kit pin is drawn as the\n // marker's icon (a data-URL SVG, see pinIcon).\n if (mapId) {\n return (\n <KitAdvancedMarker\n key={marker.id}\n marker={marker}\n label={accessibleLabel}\n isSelected={isSelected}\n isHovered={isHovered}\n onSelect={handleMarkerClick}\n onHover={onMarkerHover}\n elements={markerElements}\n />\n );\n }\n return (\n <KitClassicMarker\n key={marker.id}\n marker={marker}\n label={accessibleLabel}\n isSelected={isSelected}\n isHovered={isHovered}\n onSelect={handleMarkerClick}\n onHover={onMarkerHover}\n />\n );\n };\n\n const clusterLabelFor = useCallback(\n (count: number): string =>\n typeof clusterLabel === 'function'\n ? clusterLabel(count)\n : typeof clusterLabel === 'string'\n ? clusterLabel.replaceAll('{count}', String(count))\n : t('mapView.clusterLabel', { count }),\n [clusterLabel, t],\n );\n const clustering = shouldCluster(clusterMarkers, (markers ?? []).length);\n\n return (\n <div\n ref={rootRef}\n className={mapViewVariants({ size, surface, className })}\n data-component=\"map-view\"\n data-component-id={id ?? instanceId}\n role=\"region\"\n aria-label={ariaLabel ?? t('mapView.label')}\n // Focus target once the interactive map replaces its button; not a\n // tab stop.\n tabIndex={-1}\n {...rest}\n >\n <div\n ref={setFrame}\n className={innerSurfaceVariants({ size, className: 'ds:relative' })}\n >\n {effectiveConsent ? (\n // A provider failure shows the consent placeholder (its \"Open in\n // Google Maps\" link needs no script) with \"Load map\" as the\n // retry, instead of unmounting the host's React tree. The gate\n // starts the provider only once Google's `core` and `maps`\n // libraries have arrived.\n <ErrorBoundary\n fallback={({ reset }) => (\n <ConsentPlaceholder\n address={address}\n contentLang={contentLang}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n onLoadMap={reset}\n // Always the kit button: a host's own CTA would not call\n // `reset`, so with consent already granted it would do\n // nothing and the patient could not retry.\n consentCta={undefined}\n openInGoogleMapsHref={openInGoogleMapsHref}\n />\n )}\n >\n <MapsCoreGate\n libraries={['maps']}\n pending={\n preview ? (\n <MapPreviewLoading\n preview={preview}\n onError={onPreviewError}\n />\n ) : (\n <MapViewLoading />\n )\n }\n >\n <APIProvider\n apiKey={apiKey}\n // Pull the active i18next locale through so map UI text\n // (zoom controls, attribution) localises to match the\n // surrounding kit chrome.\n language={i18n.language}\n >\n <MapsLibraryGate\n holdUntilLoaded={Boolean(preview)}\n pending={\n preview ? (\n <MapPreviewLoading\n preview={preview}\n onError={onPreviewError}\n />\n ) : (\n <MapViewLoading />\n )\n }\n >\n <GoogleMap\n defaultCenter={center}\n defaultZoom={zoom}\n mapId={mapId}\n gestureHandling=\"cooperative\"\n disableDefaultUI={false}\n onClick={() => handleMarkerClick(null)}\n // `<Map>` reads its own bounding rect for tile sizing.\n // Without explicit dimensions it collapses to 0×0, so\n // we pass kit-class sizing through `className` — vis.gl\n // forwards `className` straight to the rendered root.\n className=\"ds:[inline-size:100%] ds:[block-size:100%]\"\n >\n <MapImperativeBridge\n onHandleReady={onHandleReady}\n onMarkerSelect={handleMarkerClick}\n onCameraMove={onCameraMove}\n />\n {onBoundsChange ? (\n <MapBoundsBridge\n onBoundsChange={onBoundsChange}\n userInteraction={userInteraction}\n />\n ) : null}\n {fitToPoints ? (\n <MapFitter\n points={fitPoints}\n fitKey={resolvedFitKey}\n maxZoom={fitMaxZoom}\n onCameraMove={onCameraMove}\n />\n ) : null}\n {hoveredMarker && renderMarkerCard ? (\n <MapMarkerCard\n marker={hoveredMarker}\n frame={frame}\n direction={i18n.dir()}\n >\n {renderMarkerCard(hoveredMarker.id)}\n </MapMarkerCard>\n ) : null}\n <MarkerLibraryGate>\n <ClassicIconThemeProvider enabled={!mapId}>\n {clustering ? (\n <ClusteredMarkers\n markers={markers ?? []}\n fallbackZoom={zoom}\n selectedMarkerId={selectedMarkerId ?? null}\n hoveredMarkerId={hoveredMarkerId ?? null}\n renderMarker={renderMarker}\n mapId={Boolean(mapId)}\n frame={frame}\n elements={markerElements}\n labelFor={clusterLabelFor}\n onCameraMove={onCameraMove}\n onClusterClick={onClusterClick}\n />\n ) : (\n (markers ?? []).map(renderMarker)\n )}\n </ClassicIconThemeProvider>\n </MarkerLibraryGate>\n </GoogleMap>\n </MapsLibraryGate>\n </APIProvider>\n </MapsCoreGate>\n </ErrorBoundary>\n ) : preview ? (\n <MapStaticPreview\n preview={preview}\n onLoadMap={handleLoadMap}\n consentCta={consentCta}\n onImageError={onPreviewError}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n onUnmount={onGateUnmount}\n />\n ) : (\n <ConsentPlaceholder\n address={address}\n contentLang={contentLang}\n description={\n consentDescription ?? t('mapView.consentDescription')\n }\n onLoadMap={handleLoadMap}\n consentCta={consentCta}\n openInGoogleMapsHref={openInGoogleMapsHref}\n onUnmount={onGateUnmount}\n />\n )}\n </div>\n </div>\n );\n },\n);\nMapView.displayName = 'MapView';\n"],"names":["mapViewAgent","handle","args","MAP_PIN_WIDTH","MAP_PIN_HEIGHT","MAP_PIN_BODY_PATH","MAP_PIN_RING_WIDTH","MAP_PIN_EDGE_WIDTH","MAP_PIN_MARK_TRANSFORM","MAP_PIN_MARK_SHAPES","attrString","attrs","key","value","mapPinSvg","fill","ring","ink","size","mark","tag","MAP_PIN_HOVER_SCALE","FOCUS_RING","MapViewPin","hovered","scale","body","jsx","jsxs","index","CLUSTER_AUTO_THRESHOLD","CLUSTER_RADIUS_PX","CLUSTER_OFF_ZOOM","WORLD_PX","toRadians","degrees","project","lat","lng","zoom","world","sin","mercator","shouldCluster","setting","markerCount","clusterMarkerSet","markers","radiusPx","projected","marker","b","buckets","grid","cellOf","radiusSquared","point","cx","cy","best","bestDistance","dx","dy","bucket","distance","n","before","after","old","next","list","singles","clusters","isFocusVisible","target","PREVIEW_SWITCH_MARGIN","closestSourceIndex","logRatios","area","i","usePickedSourceIndex","sources","ref","setIndex","useState","shapeKey","useLayoutEffect","node","k","w","h","r","measure","initial","width","height","current","observer","mapViewVariants","cva","innerSurfaceVariants","consentChromeVariants","consentTitleVariants","consentDescriptionVariants","MapImperativeBridge","onHandleReady","onMarkerSelect","onCameraMove","map","useMap","useEffect","position","center","id","_a","MapBoundsBridge","onBoundsChange","userInteraction","callback","useRef","div","previous","resetInteraction","inputExpiry","touchPointers","markInput","event","fullscreen","fullscreenMap","cameraInput","markCameraChange","report","bounds","_b","fromInput","changed","moved","inputEvents","listeners","listener","useMarkerAnchor","anchor","setAnchor","mercatorY","MapMarkerCard","frame","direction","children","createPortal","MapMarkerCardLayer","CARD_PIN_CLEARANCE_PX","CARD_PIN_BELOW_PX","CARD_EDGE_PX","cardRef","setSize","prev","maxLeft","left","above","flipped","top","markerElement","element","KitAdvancedMarker","label","isSelected","isHovered","onSelect","onHover","elements","setElement","pointerInside","registry","focusIn","focusOut","keyDown","AdvancedMarker","active","latitudeOf","y","boundsOfPoints","points","north","south","east","west","cameraForPoints","padding","maxZoom","bottom","fit","px","fraction","pointKey","MapFitter","fitKey","fitted","userMoved","ownCamera","check","own","last","keys","moveCamera","lengthTokenPx","laidOut","CLUSTER_Z_INDEX","useMapZoom","fallback","setZoom","read","CLUSTER_BADGE_CLASS","MapClusterMarker","count","resolveColorToken","token","context","probe","color","useClusterIconTheme","enabled","theme","setTheme","options","themeRoot","clusterIcon","edge","shape","svg","maps","ClassicIconThemeContext","createContext","ClassicIconThemeProvider","pinIcon","hover","selected","KitClassicMarker","useContext","icon","useMemo","Marker","KitClusterMarker","cluster","mapId","iconTheme","onActivate","classic","ANNOUNCE_DELAY_MS","FOCUS_FRAMES","ClusteredMarkers","fallbackZoom","selectedMarkerId","hoveredMarkerId","renderMarker","labelFor","onClusterClick","t","useTranslation","grouped","out","members","announcement","setAnnouncement","pending","announceTimer","activate","member","zoomNow","zoomed","frameId","tries","pin","focus","region","Fragment","hasConstructor","name","MapViewLoading","Skeleton","VisuallyHidden","MapPreviewLoading","preview","onError","MapPreviewImage","hasSources","areaRef","picked","measured","setMeasured","source","image","MapsLibraryGate","holdUntilLoaded","apiIsLoaded","useApiIsLoaded","useMapsLibrary","MarkerLibraryGate","useReportFocusOnUnmount","MapStaticPreview","onLoadMap","consentCta","onImageError","description","onUnmount","descriptionId","useId","gateRef","Button","ConsentPlaceholder","address","contentLang","openInGoogleMapsHref","MapPin","ExternalLink","MapView","forwardRef","apiKey","renderMarkerCard","onMarkerHover","onMarkerClick","consentGranted","onConsentRequest","staticPreview","consentDescription","surface","ariaLabel","fitToPoints","fitMaxZoom","clusterMarkers","clusterLabel","className","rest","i18n","markerElements","fitPoints","resolvedFitKey","fitting","wasFitting","rawId","instanceId","reducedMotion","setReducedMotion","mql","sync","handleRef","useCallback","handleMarkerClick","markerId","setFrame","hoveredMarker","consentRef","selectedMarkerIdRef","markerCountRef","buildHandle","z","useImperativeHandle","agentHandle","useAgentRegistration","fitEnded","internalConsent","setInternalConsent","effectiveConsent","failedSrc","setFailedSrc","previewSrc","onPreviewError","rootRef","focusOnLoad","focusWasInGate","onGateUnmount","hadFocus","focusLost","move","handleLoadMap","accessibleLabel","clusterLabelFor","clustering","ErrorBoundary","reset","MapsCoreGate","APIProvider","GoogleMap"],"mappings":"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"}