(function () { 'use strict'; const { useState, useRef, useEffect, useCallback, useMemo } = React; // ReactDOM is available as a global (CDN-loaded alongside React) /* * ═══════════════════════════════════════════════════════════════════════════════ * PZO INTERACTIVE WORLD MAP * ═══════════════════════════════════════════════════════════════════════════════ * * PURPOSE: * Interactive regional map component for the Phantom Zenith Origin website. * Displays the area surrounding Haven Core with clickable landmarks, cursor- * driven parallax panning, and a parabolic perspective warp to simulate a * holographic terminal display. * * VISUAL DESIGN: * - Dark blue-black base (--map-land landmass, --map-sea at 40% opacity) * - Muted mint-teal grid (--map-grid at 40% opacity, 0.75px stroke) * - Solid coastline stroke matching grid color at 37.5% opacity * - Scanline overlay for CRT/holographic texture * - 10° CSS perspective warp (rotateX) for curved-surface illusion * - Corner clipping from perspective warp is intentional — reads as * holographic display edge falloff * - Dashed outlines for landmarks, color-coded by type * - Share Tech Mono for all text (PZO's terminal/data font) * * INTERACTION MODEL: * Desktop: Cursor position drives a soft parallax pan of the map. The * viewport is slightly smaller than the full map extent (~15% overflow), * so moving the cursor toward edges reveals more of the map. Cursor * tracking uses cubic easing (pow 1.5) so movement is gentle near center * and accelerating toward edges. Hovering a landmark highlights its label. * Clicking opens a popover card with description + future CTA slot. * Mobile: Tap to select a landmark (pans to center it). Tap elsewhere to * deselect. No hover-to-pan since there's no persistent cursor. * Both: When a landmark is selected, the map pans to center on it. * Dismissing the popover returns to default behavior (cursor-following * on desktop, centered on mobile). * * POPOVER ARCHITECTURE: * The popover card is rendered via React portal (ReactDOM.createPortal) to * document.body. This is necessary because the map container uses * overflow:hidden for the parallax panning, which would clip popovers near * edges. The portal keeps the popover in the top-level DOM stacking context * while React still fully owns its lifecycle within this component. * Positioning uses position:fixed with viewport-relative coordinates from * SVG's getScreenCTM(), updated every animation frame while selected. * A document-level click listener handles click-outside dismissal since * the popover isn't a DOM child of the map container. * * DATA EXTERNALIZATION NOTES: * All map data (landmarks, terrain paths, config) is defined in the * LANDMARK_DATA, TERRAIN_DATA, and MAP_CONFIG objects at the top of this * file. These are intentionally structured for easy extraction into separate * JSON or JS data files when the website's data pipeline * (assets/data/*.js with IIFE patterns writing to window.PZO.data) is * ready to absorb them. * * When externalizing: * 1. Move LANDMARK_DATA → assets/data/worldmap-landmarks.js * 2. Move TERRAIN_DATA → assets/data/worldmap-terrain.js * 3. Move MAP_CONFIG → assets/data/worldmap-config.js (or merge into one) * 4. Have each write to window.PZO.data.worldmap.landmarks, .terrain, .config * 5. This component then reads from props or a context provider instead of * importing the constants directly. * * The terrain SVG paths (coastlines, sea shapes) are defined as path data * strings so they remain resolution-independent and can be authored in any * vector tool then pasted in as `d` attribute values. * * FUTURE FEATURES (not yet implemented): * - "View in Field Record" CTA button inside popover cards * (wire via onNavigateToRecord(landmarkId) prop callback) * - Stage-specific color overlays or pulsing indicators for active missions * - Fog-of-war / progressive reveal based on player progression * - Connection lines between related landmarks */ /* ───────────────────────────────────────────────────────────────────────────── * MAP CONFIGURATION * ───────────────────────────────────────────────────────────────────────────── * Controls the viewport, parallax, and perspective behavior. * When externalized, this becomes its own config file or merges into a * unified worldmap data file. */ const MAP_CONFIG = { // The "virtual canvas" size in arbitrary units. All landmark positions and // terrain paths are authored relative to this coordinate space. // 1600×1200 gives a 4:3 working area with plenty of room. canvasWidth: 1600, canvasHeight: 1200, // How much larger the map canvas is than the visible viewport, as a ratio. // 1.15 means the canvas is 15% larger than what's shown — the extra 15% // is the parallax travel range. Kept small so all landmarks remain visible // from the default centered position. parallaxOverflow: 1.15, // How quickly the viewport tracks the cursor. Lower = smoother/slower. // This is the lerp factor applied per animation frame. panEasing: 0.04, // CSS perspective degree for the parabolic warp effect. // 10° gives a pronounced holographic curve. The resulting corner clipping // from overflow:hidden is intentional and reads as display edge falloff. // Tested range: 4° (subtle) → 16° (extreme). 10° is the sweet spot. perspectiveDeg: 10, // Grid line spacing in canvas units gridSpacing: 80, // How long the pan-to-landmark animation takes (ms) before easing settles panToLandmarkDuration: 600, }; /* ───────────────────────────────────────────────────────────────────────────── * LANDMARK DATA * ───────────────────────────────────────────────────────────────────────────── * Each landmark has: * id – unique key (will map to Field Record entry IDs) * name – display label (rendered uppercase via CSS) * type – "city" | "region" | "location" | "hotzone" * Determines icon shape, color, and border style * x, y – center position in canvas units (0,0 = top-left) * description – short blurb for the popover card * size – relative size multiplier for the icon (default 1) * fieldRecordSlug – (future) URL slug for Field Record navigation, or null * * PLACEHOLDER NOTE: All names, positions, descriptions, and sizes below are * placeholders. Replace with authored PZO world data when ready. The visual * representation (icon shape, outline style) is driven by `type`, so adding * new landmarks is purely a data authoring task — no code changes needed. * * POSITIONING GUIDE: Keep landmarks within the inner ~90% of the canvas * (roughly x: 80-1520, y: 60-1140) to avoid labels being clipped by the * perspective warp's corner falloff effect. */ const LANDMARK_DATA = [ { id: "haven-core", name: "Haven Core", type: "city", x: 564, y: 235, size: 2, description: "The central city-state and last bastion of organized civilization on Orryx. All Runner operations originate from and return to Haven Core.", fieldRecordSlug: "haven-core", }, { id: "veilwood-fractals", name: "Veilwood Fractals", type: "region", x: 200, y: 310, size: 1.5, description: "Tessellated forest region where trees reproduce via geometric splitting triggered by Refractant saturation. Navigational disorientation, audio distortion, and reactive flora make sustained operations difficult.", fieldRecordSlug: null, }, { id: "glass-reaches", name: "Glass Reaches", type: "region", x: 1030, y: 670, size: 1.4, description: "Vast fused silica plains produced by a historical Refractant detonation. Mirror-smooth for kilometers with extreme environmental exposure. Ancient salvage lies sealed under the glass surface.", fieldRecordSlug: null, }, { id: "lumen-marshes", name: "Lumen Marshes", type: "region", x: 310, y: 720, size: 1.4, description: "Bioluminescent wetlands fed by underground Refractant seepage. Native fauna exhibit psionic mimicry — the capacity to project false sensory information as a biological defense adaptation.", fieldRecordSlug: null, }, { id: "hollow-trenches", name: "Hollow Trenches", type: "region", x: 729, y: 180, size: 1.3, description: "Deep ravines carved by collapsed orbital elevators from the colonial era. Structurally complex metallic terrain. Heritage site for early Karn autonomous communities.", fieldRecordSlug: null, }, { id: "thales-refuge", name: "Thale's Refuge", type: "location", x: 1350, y: 850, size: 1.0, description: "Fortified mining town exporting metals to Haven Core. One to four days' travel depending on route and conditions. The closest thing to a second city the region has.", fieldRecordSlug: null, }, { id: "ovir-station", name: "Ovir Station", type: "location", x: 732, y: 900, size: 0.9, description: "Black-market biotech hub built on a derelict monorail junction. The Governmental Consensus knows about it. The relationship is complicated.", fieldRecordSlug: null, }, { id: "bralin", name: "Bralin", type: "location", x: 160, y: 580, size: 1.0, description: "Sire-majority enclave specializing in psionic horticulture. Predates Haven Core. Living continuity with pre-contact Sire tradition and knowledge the city has never thought to ask about.", fieldRecordSlug: null, }, { id: "refractant-breach", name: "The Saturation", type: "hotzone", x: 390, y: 1050, size: 0.8, description: "A volatile region where subsurface Refractant deposits have breached the surface in sustained, unpredictable eruptions. Extremely hazardous. High-value mineral salvage reported by surviving survey teams.", fieldRecordSlug: null, }, { id: "dust-flats", name: "Periarch Flats", type: "location", x: 748, y: 385, size: 0.9, description: "Arid dust plains and canyon approaches marking the transition from the Threshold Expanse into true wilderness. Derelict extraction infrastructure and abandoned processing facilities line the route outward.", fieldRecordSlug: null, }, ]; /* ───────────────────────────────────────────────────────────────────────────── * TERRAIN DATA * ───────────────────────────────────────────────────────────────────────────── * SVG path data for terrain features rendered behind landmarks. * Each entry has: * id – unique key * type – "sea" | "coastline" | (future: "river", "road", etc.) * d – SVG path `d` attribute string, authored in canvas coordinates * opacity – fill/stroke opacity (used as fallback; render logic may override) * * RENDERING NOTES: * - Sea: Filled with --map-sea at 40% opacity. The low opacity lets the * grid lines bleed through, creating a unified scanner aesthetic across * land and sea while still reading as distinct zones. * - Coastline: Solid stroke in --map-grid color at 37.5% opacity. * Originally dashed, changed to solid during visual iteration for a * cleaner land/sea boundary that matches the grid's visual weight. * * AUTHOR GUIDE: Paths must extend well beyond the canvas bounds to avoid * showing edges during parallax panning. Current sea path extends from * y:-400 to y:1600 and x out to 2200 (canvas is 1600×1200 with ~120px * parallax overflow on each side). When authoring new terrain, pad * generously — at least 400 units beyond canvas edges in any direction * the terrain touches. * * PLACEHOLDER NOTE: The paths below are rough placeholders. Author final * coastline/terrain shapes in a vector editor, export path data, and paste * here (or into the externalized data file). */ const TERRAIN_DATA = [ { id: "sea-main", type: "sea", // Bounding rectangle with authored landmass cut out via evenodd fill-rule. // The rectangle fills everything with sea color; evenodd subtracts the // landmass interior (including island cutouts from the original SVG). // Coordinates are pre-scaled from the 1896×1897 source SVG to the // 1600×1200 canvas space (scale 0.8439, Y offset -200.4 to center). // Bounding box extends 500 units beyond canvas edges for parallax safety. fillRule: "evenodd", d: `M -500 -500 L 2100 -500 L 2100 1700 L -500 1700 Z M 1007.6 -199.6 L 992.0 -159.5 C 989.2 -157.2 982.6 -151.6 978.9 -146.8 C 975.2 -142.1 969.5 -123.8 967.1 -115.2 L 957.4 -98.7 L 949.4 -90.7 V -67.5 L 943.0 -51.5 L 929.9 -41.4 H 908.4 L 894.1 -56.1 L 888.2 -73.8 L 883.5 -77.6 L 867.1 -56.1 L 843.4 -51.5 L 838.0 -56.1 L 830.8 -47.7 L 809.7 7.2 V 39.2 L 804.2 65.4 V 74.7 L 822.8 89.0 L 834.6 74.7 L 830.8 59.1 L 825.3 55.3 L 834.6 43.0 H 858.2 L 863.7 39.2 H 870.0 L 872.6 46.8 H 888.2 L 892.8 43.0 L 899.6 46.8 L 909.7 43.0 L 923.2 46.8 L 929.9 43.0 L 935.8 44.3 L 939.2 51.5 L 948.1 48.5 L 949.4 51.5 L 938.8 60.8 L 933.7 63.3 L 932.5 67.1 L 947.2 65.8 L 961.6 54.0 L 969.6 54.9 V 59.5 H 965.8 V 63.3 L 971.3 62.4 L 974.7 65.8 L 968.3 71.7 L 959.9 73.4 L 950.6 72.6 L 946.4 73.8 L 952.3 78.9 L 974.2 80.6 L 978.0 73.8 L 1036.3 85.7 L 1038.4 92.8 L 1038.0 97.9 H 1053.6 L 1059.1 92.8 L 1074.2 97.9 L 1081.0 102.5 L 1082.7 105.1 L 1087.7 97.5 H 1093.7 L 1099.6 92.8 L 1112.6 89.0 L 1121.1 101.7 L 1119.0 107.6 H 1105.5 L 1096.6 111.0 L 1093.7 114.8 L 1104.2 122.8 L 1111.0 124.1 L 1123.6 128.7 V 132.5 L 1119.0 134.2 L 1122.8 138.8 L 1119.4 141.8 L 1087.7 147.7 L 1084.4 160.3 L 1061.2 162.0 L 1057.4 169.6 L 1051.9 168.8 L 1035.0 152.3 H 1023.6 L 1012.2 156.5 L 988.6 151.5 L 946.4 178.9 L 956.1 230.4 L 949.8 234.6 L 891.5 219.0 L 874.7 230.4 L 870.4 238.4 L 859.5 239.7 L 849.8 231.6 L 848.5 237.6 L 834.6 227.0 L 828.7 235.9 V 241.4 L 813.1 240.5 L 802.9 246.4 L 792.8 248.5 L 784.8 255.7 V 273.0 L 800.4 293.2 H 813.1 L 835.8 305.9 L 853.6 300.8 L 872.6 308.4 L 891.5 303.8 L 897.9 304.6 L 923.6 301.7 L 935.4 297.9 V 292.8 L 948.1 290.7 L 949.8 294.5 L 960.3 289.9 L 966.6 289.5 L 971.3 281.0 L 973.8 288.2 L 991.1 285.2 L 1021.1 289.5 L 1032.1 296.6 L 1026.1 299.2 L 1027.8 305.9 V 310.5 H 1033.3 L 1038.4 317.3 L 1046.0 318.6 L 1056.9 330.8 L 1060.7 328.7 L 1064.1 336.3 L 1071.3 338.0 L 1108.4 347.7 L 1132.9 345.1 L 1144.3 353.6 H 1149.8 L 1159.9 358.6 L 1180.2 351.9 L 1192.4 355.3 L 1197.9 359.9 L 1193.7 366.7 L 1194.5 375.5 L 1198.7 383.1 L 1199.6 386.9 L 1204.6 389.9 H 1210.5 L 1216.4 395.4 L 1220.2 394.5 L 1224.5 403.0 L 1218.1 400.8 L 1207.6 401.3 L 1204.6 393.2 L 1185.2 388.6 L 1180.2 386.9 L 1183.5 392.4 L 1184.4 397.9 L 1189.9 404.6 L 1193.7 410.1 H 1190.7 L 1185.2 406.8 V 408.9 L 1192.4 418.1 L 1195.3 425.3 L 1190.7 430.4 L 1183.5 435.4 L 1183.1 438.0 L 1175.9 439.2 L 1167.1 430.4 L 1151.5 425.3 L 1140.9 423.6 L 1133.7 421.1 L 1125.3 419.8 L 1132.1 427.0 L 1130.4 428.7 L 1118.1 432.1 L 1111.8 433.3 L 1105.5 428.7 L 1104.2 433.3 L 1106.7 441.8 V 451.5 L 1102.1 460.8 L 1094.1 460.3 L 1089.9 457.4 L 1091.1 452.7 L 1076.8 443.9 V 447.7 L 1067.9 440.5 L 1065.8 441.8 V 445.6 L 1051.5 438.0 L 1045.6 435.4 L 1033.7 425.7 L 1030.4 415.6 L 1023.2 422.8 L 1016.4 417.7 L 995.3 412.2 L 983.1 405.9 L 977.6 405.1 V 414.8 L 974.2 419.4 L 965.0 408.9 L 956.1 405.1 L 954.4 414.3 H 947.2 L 940.1 409.3 H 937.1 L 934.2 425.7 H 923.6 L 918.5 439.2 L 919.4 451.1 L 912.2 457.4 L 917.7 465.8 L 921.5 466.7 L 923.6 473.8 V 478.1 L 934.2 486.1 L 943.9 488.2 L 960.3 496.6 L 968.8 509.7 L 1002.9 524.9 L 1019.4 525.3 L 1027.8 529.5 H 1035.4 L 1031.6 533.8 L 1040.5 541.8 L 1043.4 546.0 H 1059.9 L 1067.1 544.7 L 1070.9 549.4 L 1078.0 552.3 L 1081.8 555.3 L 1094.5 557.8 L 1105.9 551.1 L 1121.1 554.9 L 1129.1 562.4 L 1132.1 569.6 L 1141.8 573.4 L 1165.0 577.2 H 1171.3 L 1173.4 568.8 L 1165.0 563.3 L 1159.9 557.0 V 551.9 L 1146.8 548.1 L 1151.5 546.0 L 1163.7 548.1 L 1170.0 549.4 L 1174.7 552.3 L 1185.6 553.6 L 1192.8 559.1 L 1201.7 559.9 L 1209.7 569.6 L 1219.0 573.4 L 1225.7 578.5 L 1221.9 579.3 L 1212.6 577.2 L 1216.4 583.1 L 1229.1 592.4 L 1227.4 594.5 L 1221.9 597.9 H 1216.4 V 603.0 L 1203.8 606.3 L 1201.7 600.4 L 1195.3 599.6 V 594.5 L 1189.4 591.6 L 1187.3 593.2 L 1176.4 591.6 L 1168.3 584.8 L 1161.2 582.7 L 1159.9 587.3 L 1154.8 595.8 L 1154.0 599.6 L 1155.7 604.6 L 1152.7 605.5 L 1147.7 604.6 L 1145.6 597.9 L 1138.8 595.8 L 1134.6 589.0 H 1128.3 L 1122.3 593.2 L 1124.9 601.7 L 1123.2 606.3 L 1121.1 610.1 V 619.8 L 1118.5 624.5 L 1116.0 632.5 L 1128.3 639.2 L 1134.6 644.3 L 1143.4 646.4 L 1144.3 651.1 L 1140.5 654.4 L 1143.4 661.2 L 1140.5 663.7 L 1138.0 666.2 L 1147.7 673.8 L 1154.0 674.7 L 1173.4 684.4 H 1182.7 L 1192.8 685.2 L 1204.2 692.4 L 1210.5 702.1 L 1204.2 703.4 L 1202.9 711.0 L 1201.2 714.3 L 1211.4 718.1 L 1213.9 722.4 L 1212.6 725.7 L 1210.5 727.4 L 1212.6 730.0 L 1229.9 732.1 L 1236.7 732.9 L 1242.2 736.7 L 1243.4 732.9 L 1256.5 733.8 L 1257.8 737.6 L 1267.9 738.8 V 742.2 L 1274.7 741.4 L 1279.7 742.2 L 1283.1 746.8 L 1285.6 740.5 L 1290.3 739.2 L 1309.7 748.1 L 1309.3 752.3 L 1315.6 757.8 L 1316.9 760.3 L 1323.2 757.8 L 1328.7 759.9 L 1335.0 765.8 L 1345.1 778.5 L 1346.4 785.2 L 1351.0 782.7 L 1355.7 794.9 L 1361.6 792.8 L 1368.8 799.2 V 801.7 L 1373.0 805.9 V 811.0 H 1386.5 L 1394.5 813.5 L 1410.1 827.4 L 1418.1 838.8 L 1422.8 840.9 L 1429.1 843.0 L 1441.8 854.9 L 1445.6 860.8 L 1451.5 862.0 L 1454.8 873.4 H 1449.4 L 1451.5 879.7 L 1448.9 881.0 L 1450.2 884.4 L 1442.6 882.3 L 1448.1 890.7 V 892.4 L 1441.8 888.2 L 1440.1 892.0 L 1444.3 901.3 L 1437.1 899.2 L 1429.1 894.1 L 1424.9 895.8 L 1428.7 900.4 L 1435.0 903.8 L 1439.2 910.1 L 1442.6 912.7 L 1446.8 916.0 L 1445.6 917.7 L 1446.8 921.5 L 1452.7 928.7 L 1453.6 933.3 L 1462.9 939.7 L 1473.0 943.0 L 1478.5 948.5 L 1486.1 962.0 L 1487.3 965.8 L 1475.5 966.2 L 1473.0 970.0 L 1464.1 971.7 L 1459.1 972.2 L 1443.0 963.7 H 1433.3 L 1422.3 956.5 L 1415.6 951.9 L 1411.8 948.5 L 1408.8 942.2 L 1411.0 938.4 V 935.4 L 1402.9 928.7 L 1395.8 927.8 V 932.5 L 1392.8 933.3 L 1395.3 937.1 L 1394.9 940.9 L 1394.1 943.5 L 1399.1 947.3 L 1400.0 951.9 L 1399.1 954.4 L 1400.4 959.5 L 1398.7 961.6 L 1387.7 952.7 L 1384.4 951.1 L 1382.7 948.5 L 1380.6 952.7 H 1373.4 L 1364.1 947.3 L 1359.9 943.0 L 1354.0 935.9 L 1349.4 931.2 L 1345.1 927.4 L 1336.7 925.7 H 1334.2 L 1342.2 935.9 L 1343.4 942.2 L 1348.5 948.1 V 951.9 L 1344.7 954.9 L 1340.1 955.7 L 1334.2 951.5 L 1329.5 953.6 L 1322.3 944.3 L 1318.1 944.7 L 1313.1 948.5 L 1305.0 948.9 L 1297.0 949.8 L 1294.9 952.3 L 1278.5 949.8 L 1276.8 944.7 L 1278.5 940.1 L 1279.7 937.6 L 1284.8 933.8 V 930.4 L 1270.9 912.7 L 1254.4 911.0 L 1248.9 910.1 L 1243.9 901.3 L 1243.0 887.8 L 1239.2 869.2 L 1241.3 861.6 L 1241.8 852.3 L 1246.0 849.4 L 1247.2 844.3 L 1252.3 838.8 L 1250.2 828.7 L 1248.1 823.2 L 1243.0 818.1 V 812.2 L 1246.0 808.4 L 1243.4 802.5 V 789.0 V 785.2 L 1247.2 779.7 L 1243.0 775.5 L 1242.2 771.3 V 766.7 L 1244.3 763.3 L 1240.5 760.3 L 1237.5 757.0 H 1232.1 V 761.6 L 1235.0 765.0 L 1234.2 769.6 L 1237.5 773.8 V 776.4 L 1231.6 778.5 L 1232.5 786.1 L 1229.9 790.7 L 1224.9 789.0 L 1220.2 786.1 L 1222.3 792.0 L 1227.0 799.2 L 1228.7 802.5 L 1227.8 804.6 L 1221.5 806.8 L 1216.0 803.8 L 1213.9 808.4 L 1209.7 808.9 V 813.1 H 1205.0 L 1194.9 804.6 L 1189.0 797.0 L 1181.4 792.4 L 1172.6 794.1 H 1169.2 L 1167.5 792.0 V 795.8 V 800.4 L 1165.4 802.5 L 1164.5 805.9 L 1159.1 813.9 H 1155.7 L 1154.0 816.0 H 1152.3 L 1150.2 808.0 L 1143.9 801.7 L 1135.4 798.7 L 1141.3 809.7 V 819.4 L 1137.5 827.0 L 1140.5 830.8 L 1145.1 831.6 L 1146.8 838.4 L 1149.4 843.0 L 1148.9 846.8 L 1143.9 852.3 L 1141.3 861.2 L 1138.8 862.0 L 1132.1 857.8 L 1130.4 865.8 L 1120.7 867.1 L 1116.4 881.0 L 1117.7 890.3 L 1119.0 900.4 L 1114.3 905.9 L 1117.7 909.7 L 1129.5 912.2 L 1134.6 916.5 L 1132.1 920.3 L 1121.9 919.0 L 1127.0 927.8 L 1132.9 932.5 V 941.8 L 1123.2 937.1 L 1123.6 944.7 L 1116.4 946.4 L 1108.4 940.9 L 1088.6 941.8 L 1092.0 948.1 L 1090.3 950.6 L 1097.9 960.3 V 964.1 L 1101.7 967.5 V 970.9 H 1098.7 L 1100.8 973.8 L 1100.4 978.1 H 1098.7 V 981.9 L 1097.0 987.3 L 1093.2 991.6 L 1086.1 985.7 L 1081.4 997.5 H 1076.4 L 1072.6 1008.9 H 1068.3 L 1066.2 1016.0 L 1071.3 1021.5 L 1077.6 1023.2 L 1078.5 1028.7 L 1077.6 1032.1 L 1072.6 1030.0 L 1071.7 1034.2 L 1072.6 1038.8 L 1066.2 1043.0 L 1063.3 1037.1 H 1060.3 L 1053.1 1030.8 L 1051.9 1024.1 L 1048.9 1018.6 L 1038.8 1011.4 L 1035.4 1002.5 L 1027.4 996.2 L 1021.5 981.0 L 1020.7 971.3 L 1019.4 964.1 L 1011.4 958.6 L 1001.2 959.1 V 962.9 L 993.7 966.2 L 992.4 949.8 L 1000.4 940.1 L 997.9 932.1 L 1003.4 923.6 L 1001.2 918.1 L 996.2 914.8 L 1009.3 908.9 V 894.1 L 1006.7 893.2 L 1003.4 898.7 L 1002.5 904.2 L 998.3 900.0 L 997.0 881.9 L 994.5 873.0 L 992.4 863.7 L 991.1 868.8 L 988.6 871.7 V 882.7 L 986.5 886.1 L 979.7 901.7 L 975.1 900.4 V 887.8 L 973.4 876.8 L 971.3 871.7 L 972.6 868.8 L 960.7 866.7 L 953.1 855.7 L 955.3 844.3 L 953.1 841.8 V 832.1 L 965.0 818.6 L 966.2 805.9 L 969.6 800.8 L 968.8 792.8 L 973.0 770.9 V 758.6 L 968.8 746.4 V 742.2 L 965.0 735.0 L 959.5 717.7 L 945.6 709.7 L 943.9 693.2 L 944.7 681.4 L 942.6 678.9 L 940.9 675.1 L 931.2 670.9 L 912.6 659.5 L 911.8 655.3 L 893.7 653.6 L 884.4 658.6 L 868.3 657.4 L 865.4 648.9 L 864.1 640.9 L 856.9 627.4 L 855.7 618.1 L 849.4 615.6 L 848.9 611.4 L 844.7 608.9 L 843.4 603.4 L 828.3 593.7 L 817.3 588.2 L 812.2 580.6 L 813.1 571.3 L 808.0 572.2 L 805.9 569.2 V 565.0 L 795.8 563.3 L 794.1 559.9 L 791.1 561.2 L 793.2 567.5 L 791.1 570.0 L 793.2 573.4 L 792.0 586.1 H 780.6 L 779.7 594.1 L 783.5 599.6 L 786.5 601.3 L 788.6 609.7 L 790.3 613.1 L 798.7 614.8 L 802.5 619.8 L 808.0 622.4 L 808.8 627.4 L 799.6 626.2 L 797.5 624.9 H 794.1 L 789.4 619.8 L 786.5 621.5 H 783.5 L 779.7 612.2 L 774.2 613.1 L 772.6 608.0 L 774.2 604.6 L 770.0 603.4 L 765.4 606.8 L 759.5 599.6 L 760.7 589.9 L 754.8 584.4 L 756.1 578.5 L 744.7 565.0 L 744.3 558.6 L 740.1 556.1 L 738.4 549.8 L 741.3 546.4 L 738.4 540.9 L 732.5 542.2 L 731.2 538.8 L 735.8 535.0 L 734.6 533.3 L 727.4 530.0 L 723.6 524.5 L 713.9 522.8 L 708.8 519.4 L 705.5 521.9 H 695.3 L 694.1 525.7 H 691.1 L 689.0 522.8 L 675.5 520.3 L 674.7 524.5 L 669.6 529.1 L 671.7 530.8 V 534.2 L 668.3 535.9 L 664.1 540.9 L 654.0 542.2 L 646.4 537.6 L 645.6 529.1 L 647.2 525.7 L 642.6 515.6 L 635.4 508.0 L 630.8 496.6 L 623.2 489.5 L 608.8 483.1 L 602.5 478.9 L 598.7 468.4 L 600.8 464.1 L 596.2 454.0 L 591.5 455.3 L 583.1 441.8 L 584.0 436.7 L 581.8 435.0 L 582.3 431.2 L 577.2 428.3 L 578.5 422.8 L 577.2 419.8 L 584.0 410.1 L 585.2 403.4 L 587.3 401.7 L 588.6 392.0 L 596.2 384.4 L 599.6 383.5 L 605.9 376.8 L 626.1 363.3 L 630.8 362.4 L 632.5 358.6 L 635.4 356.5 L 639.6 351.1 L 647.7 346.8 L 654.4 341.8 L 659.9 339.7 L 658.2 329.1 L 660.7 321.5 L 665.4 318.1 L 666.2 309.7 L 659.1 303.4 L 652.7 299.6 L 641.3 286.5 L 638.4 277.6 L 636.7 271.7 L 632.5 265.8 L 627.4 264.6 L 616.0 265.4 L 608.0 268.4 L 604.2 271.7 L 602.1 278.5 L 594.5 277.6 L 588.6 274.7 L 587.3 276.8 L 580.6 274.7 L 577.2 275.5 L 572.1 273.4 L 571.3 269.6 L 576.4 268.4 L 572.1 263.7 L 564.5 260.3 L 559.5 251.9 L 558.2 244.7 L 553.2 250.2 L 548.3 255.7 L 547.8 267.7 L 548.2 273.7 L 548.6 279.7 L 545.2 290.8 L 537.2 301.4 L 534.3 306.6 L 543.0 309.4 L 550.4 309.1 L 560.8 319.3 L 564.5 326.1 L 574.4 328.5 L 576.2 335.0 L 581.4 342.3 L 587.8 348.0 L 583.3 353.6 L 578.7 355.8 L 576.5 360.4 L 572.6 360.6 L 570.1 368.8 L 565.5 372.8 L 561.0 372.3 L 555.5 376.1 L 553.1 384.7 L 547.5 386.3 L 543.2 385.3 L 539.7 397.7 L 533.6 397.7 L 532.3 400.7 L 525.7 400.8 L 524.9 405.5 L 520.7 408.0 L 523.2 416.5 L 518.8 424.3 L 520.7 433.9 L 516.5 432.9 L 516.2 439.6 L 514.7 442.2 L 514.1 446.3 L 510.3 446.5 L 510.3 455.7 L 507.8 457.8 L 509.7 466.0 L 508.0 469.1 L 512.2 474.9 L 515.8 475.2 L 518.4 479.7 L 520.2 481.4 L 520.9 488.2 L 519.2 490.0 L 515.6 486.7 L 516.5 497.4 L 511.6 497.0 L 512.8 504.6 L 510.9 511.7 L 502.7 504.4 L 497.4 498.0 L 491.7 486.0 L 491.3 495.4 L 491.3 504.6 L 501.0 519.8 L 498.3 522.8 L 502.5 531.2 L 506.0 537.3 L 508.0 535.4 L 515.1 541.7 L 516.5 546.5 L 516.8 555.2 L 507.4 560.6 L 504.3 553.1 L 503.5 545.4 L 501.1 544.8 L 496.2 543.1 L 498.0 549.2 L 501.8 552.1 L 502.0 557.3 L 495.1 565.4 L 497.9 569.0 L 497.2 572.7 L 501.0 584.3 L 498.4 586.0 L 493.1 581.3 L 481.0 577.3 L 475.4 564.0 L 460.9 565.5 L 451.2 557.8 L 445.5 557.6 L 443.0 553.5 L 428.5 548.9 L 424.5 553.1 L 429.6 557.4 L 439.1 561.2 L 443.7 566.5 L 446.7 575.7 L 456.9 582.0 L 466.9 584.9 L 469.2 591.2 L 475.4 594.7 L 479.8 594.8 L 487.7 600.9 L 494.6 600.2 L 500.6 607.7 L 507.6 607.5 L 512.6 612.6 L 520.4 610.8 L 530.4 619.8 L 530.2 626.5 L 535.1 633.0 L 532.9 636.2 L 533.7 640.4 L 532.6 644.2 L 529.8 648.1 L 538.4 649.1 L 541.8 653.4 L 543.8 655.9 L 549.6 656.0 L 551.0 657.8 L 551.8 669.9 L 545.5 675.6 L 541.5 673.7 C 541.0 674.7 540.1 676.7 539.7 676.8 C 539.4 676.9 537.2 675.8 536.2 675.2 L 532.1 668.5 L 531.0 677.1 L 526.5 681.1 L 526.3 683.7 L 532.7 685.9 L 535.6 688.5 L 535.0 694.0 L 543.3 696.9 L 550.9 702.1 L 548.7 705.0 L 542.9 703.1 L 535.7 705.6 L 523.9 699.8 L 517.8 692.3 L 514.9 692.7 L 510.9 683.3 L 513.0 678.3 L 507.9 678.9 L 497.9 668.6 L 494.9 662.9 L 481.5 658.8 L 470.8 659.7 L 460.6 663.9 L 457.7 667.8 L 454.4 673.1 L 459.9 676.9 L 467.9 674.1 L 479.5 674.8 L 485.0 672.7 L 487.3 674.7 L 488.7 680.9 L 492.9 681.9 L 496.0 689.8 L 500.9 691.4 L 503.0 698.7 L 511.1 707.0 L 514.0 706.6 L 527.3 717.7 L 530.6 718.5 L 538.4 718.0 L 542.9 720.2 L 545.8 724.2 L 555.4 728.5 L 561.3 733.8 L 566.1 741.3 L 569.7 744.6 L 567.2 746.6 L 570.1 750.6 L 577.5 753.8 L 584.5 753.5 L 587.3 751.0 L 591.0 751.7 L 597.4 761.7 L 599.5 769.0 L 597.2 771.9 L 598.4 774.6 L 605.7 780.0 L 609.3 778.9 L 616.4 782.2 L 622.7 794.9 L 622.2 799.4 L 619.4 803.3 L 620.3 806.5 L 624.9 808.7 L 632.2 808.4 L 630.3 813.7 L 634.4 817.4 L 636.1 824.8 L 639.3 823.9 L 642.1 826.1 L 644.2 822.5 L 654.7 831.8 L 655.4 835.5 L 659.3 843.2 L 666.7 845.9 L 675.1 846.1 L 679.0 844.6 L 687.1 848.4 L 692.5 844.2 L 694.1 845.1 L 693.9 849.1 L 699.8 849.6 L 705.8 854.0 L 714.9 856.7 L 717.3 858.6 L 714.5 861.2 L 719.4 862.9 L 722.4 869.0 L 728.6 870.7 L 732.8 870.0 L 740.8 873.4 L 748.7 873.3 L 752.4 877.1 L 751.9 879.9 L 754.6 884.8 L 758.3 885.5 L 763.0 885.0 L 767.6 882.8 L 771.8 885.2 L 775.4 885.4 L 774.7 889.2 L 770.1 890.0 L 766.6 888.4 C 766.0 889.3 764.7 891.2 764.3 891.2 L 760.3 892.4 L 759.8 896.9 L 767.8 895.5 L 777.6 896.2 L 783.2 902.1 L 789.8 902.0 L 794.7 902.3 L 800.2 909.1 L 799.3 910.6 L 792.9 906.8 L 788.2 907.2 L 786.6 910.8 L 782.9 911.8 L 784.1 914.1 L 789.4 916.1 L 794.9 916.7 L 798.1 915.8 L 801.0 919.8 L 802.9 915.7 L 806.5 916.0 L 808.7 920.7 L 816.3 921.1 L 823.3 927.0 L 828.1 933.1 L 834.5 933.9 L 834.6 937.4 L 822.4 942.2 L 817.2 947.2 L 811.1 947.2 L 809.4 949.0 L 800.1 945.5 L 794.7 939.2 L 789.7 943.2 L 786.7 945.0 L 784.5 955.7 L 776.0 956.4 L 770.0 952.4 L 766.7 953.3 L 762.7 956.2 L 763.6 963.9 L 761.5 965.8 L 757.4 974.4 L 751.9 982.6 L 747.1 984.4 L 745.3 988.9 L 736.6 987.4 L 731.6 987.1 L 728.3 990.7 L 721.7 990.8 L 717.3 992.1 L 711.3 997.3 L 702.0 1001.7 L 700.0 1005.3 L 700.2 1009.2 L 700.5 1014.8 L 695.7 1018.0 L 692.1 1028.2 L 687.2 1028.3 L 680.9 1029.2 L 676.8 1031.7 L 668.1 1030.3 L 658.3 1026.9 L 654.1 1029.0 L 655.5 1033.9 L 654.0 1040.9 L 651.5 1042.9 L 652.6 1047.0 L 650.1 1047.2 L 647.8 1048.4 L 645.6 1049.9 L 654.2 1054.0 L 658.9 1057.9 L 659.7 1065.6 L 655.7 1068.5 L 655.3 1074.7 L 656.4 1081.4 L 654.5 1085.5 L 648.3 1083.8 L 644.1 1086.3 L 641.0 1081.5 L 636.3 1080.2 L 630.3 1081.0 L 630.2 1075.8 L 627.5 1081.8 L 619.5 1088.9 L 601.6 1097.1 L 594.9 1096.8 L 587.5 1096.8 L 578.4 1095.8 L 563.4 1089.2 L 563.7 1093.5 L 562.1 1101.8 L 558.8 1107.2 L 555.6 1108.1 L 552.8 1105.8 L 551.9 1109.1 L 549.2 1110.3 L 544.3 1106.9 L 541.4 1112.1 L 537.5 1118.5 L 530.0 1119.8 L 524.1 1117.5 L 523.7 1123.8 L 515.6 1124.8 L 516.4 1133.8 L 513.0 1135.6 L 505.4 1132.1 L 503.8 1147.9 L 499.5 1151.3 L 495.1 1148.2 L 494.5 1165.9 L 486.1 1179.7 L 485.3 1184.4 L 482.4 1185.2 L 483.2 1188.0 L 478.4 1195.6 L 473.1 1197.1 L 468.6 1207.6 L 464.9 1213.0 L 463.8 1227.4 L 467.2 1244.0 L 469.2 1250.9 L 483.1 1258.3 L 490.4 1257.5 L 496.5 1255.8 L 500.9 1258.9 L 499.0 1261.6 L 495.2 1260.5 L 493.1 1262.4 L 482.2 1264.2 L 472.4 1268.4 L 470.9 1279.8 L 466.2 1282.0 L 465.9 1290.0 L 459.7 1296.1 L 456.1 1311.2 L 441.2 1332.6 L 441.3 1340.5 L 440.5 1346.9 L 445.6 1363.7 L 450.6 1369.2 L 448.9 1374.3 L 449.8 1384.8 L 445.6 1399.6 H 2400 V -199.6 H 1007.6 Z M 981.3 996.4 L 989.6 980.6 L 1003.1 978.1 L 1018.1 998.9 L 1029.0 1019.8 L 1036.5 1052.3 L 1058.2 1071.5 L 1061.6 1094.9 L 1053.2 1098.2 V 1112.4 L 1045.7 1108.3 L 1042.4 1130.0 L 1032.3 1125.8 L 1016.4 1112.4 L 994.7 1081.6 L 990.5 1087.4 L 978.8 1073.2 V 1056.5 L 969.6 1047.3 L 971.2 1030.6 L 969.6 1010.6 L 981.3 996.4 Z M 959.2 904.2 L 978.0 942.8 L 981.3 965.9 L 964.7 994.5 L 949.2 990.1 L 931.5 972.5 L 934.9 958.2 L 931.5 940.6 L 934.9 921.8 L 949.2 916.3 L 954.8 904.2 H 959.2 Z M 1267.8 965.9 L 1250.2 975.3 L 1256.1 991.8 L 1250.2 1015.3 L 1273.7 1038.8 L 1264.3 1067.0 L 1273.7 1095.3 L 1305.3 1116.5 L 1314.7 1105.9 L 1355.7 1121.2 L 1359.2 1111.7 L 1381.5 1121.2 L 1388.5 1111.7 L 1381.5 1092.9 L 1375.6 1044.7 L 1355.7 995.3 L 1326.4 969.4 L 1314.7 988.2 L 1293.6 982.4 L 1267.8 965.9 Z M 1083.2 233.6 L 1101.0 223.9 L 1110.9 202.4 L 1169.9 196.9 L 1209.0 214.9 L 1230.6 207.2 L 1316.9 215.7 L 1340.6 198.2 L 1369.9 198.4 L 1405.1 226.1 L 1426.6 236.1 L 1422.5 249.8 L 1404.9 259.5 L 1396.9 277.1 L 1363.5 284.7 L 1340.0 280.6 L 1306.5 290.2 L 1275.1 299.8 L 1218.1 305.3 L 1169.2 279.4 L 1134.0 275.2 L 1089.0 251.3 L 1083.2 233.6 Z M 1405.0 1085.9 L 1392.1 1088.2 L 1401.4 1111.7 H 1409.6 L 1424.9 1114.1 L 1422.5 1092.9 L 1409.6 1076.5 L 1405.0 1085.9 Z`, opacity: 1, // Overridden in render to 0.4 for grid bleed-through }, { id: "coastline-main", type: "coastline", // Authored coastline, rivers, and island outlines from the source SVG. // Contains multiple subpaths (M commands) for disconnected features: // the main continental coastline, interior rivers/waterways, offshore // islands, and smaller coastal features. d: `M 454.4 673.1 L 457.7 667.8 L 460.6 663.9 L 470.8 659.7 L 481.5 658.8 L 494.9 662.9 L 497.9 668.6 L 507.9 678.9 L 513.0 678.3 L 510.9 683.3 L 514.9 692.7 L 517.8 692.3 L 523.9 699.8 L 535.7 705.6 L 542.9 703.1 L 548.7 705.0 L 550.9 702.1 L 543.3 696.9 L 535.0 694.0 L 535.6 688.5 L 532.7 685.9 L 526.3 683.7 L 526.5 681.1 L 531.0 677.1 L 532.1 668.5 L 536.2 675.2 C 537.2 675.8 539.4 676.9 539.7 676.8 C 540.1 676.7 541.0 674.7 541.5 673.7 L 545.5 675.6 L 551.8 669.9 L 551.0 657.8 L 549.6 656.0 L 543.8 655.9 L 541.8 653.4 L 538.4 649.1 L 529.8 648.1 L 532.6 644.2 L 533.7 640.4 L 532.9 636.2 L 535.1 633.0 L 530.2 626.5 L 530.4 619.8 L 520.4 610.8 L 512.6 612.6 L 507.6 607.5 L 500.6 607.7 L 494.6 600.2 L 487.7 600.9 L 479.8 594.8 L 475.4 594.7 L 469.2 591.2 L 466.9 584.9 L 456.9 582.0 L 446.7 575.7 L 443.7 566.5 L 439.1 561.2 L 429.6 557.4 L 424.5 553.1 L 428.5 548.9 L 443.0 553.5 L 445.5 557.6 L 451.2 557.8 L 460.9 565.5 L 475.4 564.0 L 481.0 577.3 L 493.1 581.3 L 498.4 586.0 L 501.0 584.3 L 497.2 572.7 L 497.9 569.0 L 495.1 565.4 L 502.0 557.3 L 501.8 552.1 L 498.0 549.2 L 496.2 543.1 L 501.1 544.8 L 503.5 545.4 L 504.3 553.1 L 507.4 560.6 L 516.8 555.2 L 516.5 546.5 L 515.1 541.7 L 508.0 535.4 L 506.0 537.3 L 502.5 531.2 L 498.3 522.8 L 501.0 519.8 L 491.3 504.6 L 491.3 495.4 L 491.7 486.0 L 497.4 498.0 L 502.7 504.4 L 510.9 511.7 L 512.8 504.6 L 511.6 497.0 L 516.5 497.4 L 515.6 486.7 L 519.2 490.0 L 520.9 488.2 L 520.2 481.4 L 518.4 479.7 L 515.8 475.2 L 512.2 474.9 L 508.0 469.1 L 509.7 466.0 L 507.8 457.8 L 510.3 455.7 L 510.3 446.5 L 514.1 446.3 L 514.7 442.2 L 516.2 439.6 L 516.5 432.9 L 520.7 433.9 L 518.8 424.3 L 523.2 416.5 L 520.7 408.0 L 524.9 405.5 L 525.7 400.8 L 532.3 400.7 L 533.6 397.7 L 539.7 397.7 L 543.2 385.3 L 547.5 386.3 L 553.1 384.7 L 555.5 376.1 L 561.0 372.3 L 565.5 372.8 L 570.1 368.8 L 572.6 360.6 L 576.5 360.4 L 578.7 355.8 L 583.3 353.6 L 587.8 348.0 L 581.4 342.3 L 576.2 335.0 L 574.4 328.5 L 564.5 326.1 L 560.8 319.3 L 550.4 309.1 L 543.0 309.4 L 534.3 306.6 L 537.2 301.4 L 545.2 290.8 L 548.6 279.7 L 548.2 273.7 M 454.4 673.1 L 459.9 676.9 L 467.9 674.1 L 479.5 674.8 L 485.0 672.7 L 487.3 674.7 L 488.7 680.9 L 492.9 681.9 L 496.0 689.8 L 500.9 691.4 L 503.0 698.7 L 511.1 707.0 L 514.0 706.6 L 527.3 717.7 L 530.6 718.5 L 538.4 718.0 L 542.9 720.2 L 545.8 724.2 L 555.4 728.5 L 561.3 733.8 L 566.1 741.3 L 569.7 744.6 L 567.2 746.6 L 570.1 750.6 L 577.5 753.8 L 584.5 753.5 L 587.3 751.0 L 591.0 751.7 L 597.4 761.7 L 599.5 769.0 L 597.2 771.9 L 598.4 774.6 L 605.7 780.0 L 609.3 778.9 L 616.4 782.2 L 622.7 794.9 L 622.2 799.4 L 619.4 803.3 L 620.3 806.5 L 624.9 808.7 L 632.2 808.4 L 630.3 813.7 L 634.4 817.4 L 636.1 824.8 L 639.3 823.9 L 642.1 826.1 L 644.2 822.5 L 654.7 831.8 L 655.4 835.5 L 659.3 843.2 L 666.7 845.9 L 675.1 846.1 L 679.0 844.6 L 687.1 848.4 L 692.5 844.2 L 694.1 845.1 L 693.9 849.1 L 699.8 849.6 L 705.8 854.0 L 714.9 856.7 L 717.3 858.6 L 714.5 861.2 L 719.4 862.9 L 722.4 869.0 L 728.6 870.7 L 732.8 870.0 L 740.8 873.4 L 748.7 873.3 L 752.4 877.1 L 751.9 879.9 L 754.6 884.8 L 758.3 885.5 L 763.0 885.0 L 767.6 882.8 L 771.8 885.2 L 775.4 885.4 L 774.7 889.2 L 770.1 890.0 L 766.6 888.4 C 766.0 889.3 764.7 891.2 764.3 891.2 M 454.4 673.1 L 446.7 675.2 L 436.7 678.3 L 426.6 678.9 L 416.9 676.9 L 401.3 666.2 L 394.1 658.8 L 390.3 645.6 L 384.4 632.9 L 381.4 624.5 L 370.0 616.5 L 363.7 614.8 L 352.7 616.5 H 343.5 L 335.0 611.4 L 333.3 604.2 L 334.2 599.2 L 338.0 585.7 L 336.7 573.0 L 333.3 564.1 L 325.7 559.1 L 319.8 561.2 L 308.0 557.8 L 300.4 553.5 L 297.9 546.8 L 297.0 540.5 L 292.0 536.7 L 285.2 535.9 L 277.2 536.7 L 268.4 534.2 L 263.3 529.1 V 522.4 L 265.4 514.3 L 262.0 507.6 L 255.7 502.1 L 248.9 504.6 L 236.7 507.6 L 227.4 504.4 L 221.1 497.0 L 217.7 486.0 L 210.1 475.2 L 199.6 469.1 L 187.3 467.5 M 764.3 891.2 C 764.0 891.3 761.5 892.1 760.3 892.4 M 764.3 891.2 L 760.3 892.4 M 760.3 892.4 L 759.8 896.9 L 767.8 895.5 L 777.6 896.2 L 783.2 902.1 L 789.8 902.0 L 794.7 902.3 L 800.2 909.1 L 799.3 910.6 L 792.9 906.8 L 788.2 907.2 L 786.6 910.8 L 782.9 911.8 L 784.1 914.1 L 789.4 916.1 L 794.9 916.7 L 798.1 915.8 L 801.0 919.8 L 802.9 915.7 L 806.5 916.0 L 808.7 920.7 L 816.3 921.1 L 823.3 927.0 L 828.1 933.1 L 834.5 933.9 L 834.6 937.4 L 822.4 942.2 L 817.2 947.2 L 811.1 947.2 L 809.4 949.0 L 800.1 945.5 L 794.7 939.2 L 789.7 943.2 L 786.7 945.0 L 784.5 955.7 L 776.0 956.4 L 770.0 952.4 L 766.7 953.3 L 762.7 956.2 L 763.6 963.9 L 761.5 965.8 L 757.4 974.4 L 751.9 982.6 L 747.1 984.4 L 745.3 988.9 L 736.6 987.4 L 731.6 987.1 L 728.3 990.7 L 721.7 990.8 L 717.3 992.1 L 711.3 997.3 L 702.0 1001.7 L 700.0 1005.3 L 700.2 1009.2 L 700.5 1014.8 L 695.7 1018.0 L 692.1 1028.2 L 687.2 1028.3 L 680.9 1029.2 L 676.8 1031.7 L 668.1 1030.3 L 658.3 1026.9 L 654.1 1029.0 L 655.5 1033.9 L 654.0 1040.9 L 651.5 1042.9 L 652.6 1047.0 L 650.1 1047.2 L 647.8 1048.4 L 645.6 1049.9 L 654.2 1054.0 L 658.9 1057.9 L 659.7 1065.6 L 655.7 1068.5 L 655.3 1074.7 L 656.4 1081.4 L 654.5 1085.5 L 648.3 1083.8 L 644.1 1086.3 L 641.0 1081.5 L 636.3 1080.2 L 630.3 1081.0 L 630.2 1075.8 L 627.5 1081.8 L 619.5 1088.9 L 601.6 1097.1 L 594.9 1096.8 L 587.5 1096.8 L 578.4 1095.8 L 563.4 1089.2 L 563.7 1093.5 L 562.1 1101.8 L 558.8 1107.2 L 555.6 1108.1 L 552.8 1105.8 L 551.9 1109.1 L 549.2 1110.3 L 544.3 1106.9 L 541.4 1112.1 L 537.5 1118.5 L 530.0 1119.8 L 524.1 1117.5 L 523.7 1123.8 L 515.6 1124.8 L 516.4 1133.8 L 513.0 1135.6 L 505.4 1132.1 L 503.8 1147.9 L 499.5 1151.3 L 495.1 1148.2 L 494.5 1165.9 L 486.1 1179.7 L 485.3 1184.4 L 482.4 1185.2 L 483.2 1188.0 L 478.4 1195.6 L 473.1 1197.1 L 468.6 1207.6 L 464.9 1213.0 L 463.8 1227.4 L 467.2 1244.0 L 469.2 1250.9 L 483.1 1258.3 L 490.4 1257.5 L 496.5 1255.8 L 500.9 1258.9 L 499.0 1261.6 L 495.2 1260.5 L 493.1 1262.4 L 482.2 1264.2 L 472.4 1268.4 L 470.9 1279.8 L 466.2 1282.0 L 465.9 1290.0 L 459.7 1296.1 L 456.1 1311.2 L 441.2 1332.6 L 441.3 1340.5 L 440.5 1346.9 L 445.6 1363.7 L 450.6 1369.2 L 448.9 1374.3 L 449.8 1384.8 L 445.6 1399.6 H 2400 V -199.6 H 1007.6 L 992.0 -159.5 C 989.2 -157.2 982.6 -151.6 978.9 -146.8 C 975.2 -142.1 969.5 -123.8 967.1 -115.2 L 957.4 -98.7 L 949.4 -90.7 V -67.5 L 943.0 -51.5 L 929.9 -41.4 H 908.4 L 894.1 -56.1 L 888.2 -73.8 L 883.5 -77.6 L 867.1 -56.1 L 843.4 -51.5 L 838.0 -56.1 L 830.8 -47.7 L 809.7 7.2 V 39.2 L 804.2 65.4 V 74.7 L 822.8 89.0 L 834.6 74.7 L 830.8 59.1 L 825.3 55.3 L 834.6 43.0 H 858.2 L 863.7 39.2 H 870.0 L 872.6 46.8 H 888.2 L 892.8 43.0 L 899.6 46.8 L 909.7 43.0 L 923.2 46.8 L 929.9 43.0 L 935.8 44.3 L 939.2 51.5 L 948.1 48.5 L 949.4 51.5 L 938.8 60.8 L 933.7 63.3 L 932.5 67.1 L 947.2 65.8 L 961.6 54.0 L 969.6 54.9 V 59.5 H 965.8 V 63.3 L 971.3 62.4 L 974.7 65.8 L 968.3 71.7 L 959.9 73.4 L 950.6 72.6 L 946.4 73.8 L 952.3 78.9 L 974.2 80.6 L 978.0 73.8 L 1036.3 85.7 L 1038.4 92.8 L 1038.0 97.9 H 1053.6 L 1059.1 92.8 L 1074.2 97.9 L 1081.0 102.5 L 1082.7 105.1 L 1087.7 97.5 H 1093.7 L 1099.6 92.8 L 1112.6 89.0 L 1121.1 101.7 L 1119.0 107.6 H 1105.5 L 1096.6 111.0 L 1093.7 114.8 L 1104.2 122.8 L 1111.0 124.1 L 1123.6 128.7 V 132.5 L 1119.0 134.2 L 1122.8 138.8 L 1119.4 141.8 L 1087.7 147.7 L 1084.4 160.3 L 1061.2 162.0 L 1057.4 169.6 L 1051.9 168.8 L 1035.0 152.3 H 1023.6 L 1012.2 156.5 L 988.6 151.5 L 946.4 178.9 L 956.1 230.4 L 949.8 234.6 L 891.5 219.0 L 874.7 230.4 L 870.4 238.4 L 859.5 239.7 L 849.8 231.6 L 848.5 237.6 L 834.6 227.0 L 828.7 235.9 V 241.4 L 813.1 240.5 L 802.9 246.4 L 792.8 248.5 L 784.8 255.7 V 273.0 L 800.4 293.2 H 813.1 L 835.8 305.9 L 853.6 300.8 L 872.6 308.4 L 891.5 303.8 L 897.9 304.6 L 923.6 301.7 L 935.4 297.9 V 292.8 L 948.1 290.7 L 949.8 294.5 L 960.3 289.9 L 966.6 289.5 L 971.3 281.0 L 973.8 288.2 L 991.1 285.2 L 1021.1 289.5 L 1032.1 296.6 L 1026.1 299.2 L 1027.8 305.9 V 310.5 H 1033.3 L 1038.4 317.3 L 1046.0 318.6 L 1056.9 330.8 L 1060.7 328.7 L 1064.1 336.3 L 1071.3 338.0 L 1108.4 347.7 L 1132.9 345.1 L 1144.3 353.6 H 1149.8 L 1159.9 358.6 L 1180.2 351.9 L 1192.4 355.3 L 1197.9 359.9 L 1193.7 366.7 L 1194.5 375.5 L 1198.7 383.1 L 1199.6 386.9 L 1204.6 389.9 H 1210.5 L 1216.4 395.4 L 1220.2 394.5 L 1224.5 403.0 L 1218.1 400.8 L 1207.6 401.3 L 1204.6 393.2 L 1185.2 388.6 L 1180.2 386.9 L 1183.5 392.4 L 1184.4 397.9 L 1189.9 404.6 L 1193.7 410.1 H 1190.7 L 1185.2 406.8 V 408.9 L 1192.4 418.1 L 1195.3 425.3 L 1190.7 430.4 L 1183.5 435.4 L 1183.1 438.0 L 1175.9 439.2 L 1167.1 430.4 L 1151.5 425.3 L 1140.9 423.6 L 1133.7 421.1 L 1125.3 419.8 L 1132.1 427.0 L 1130.4 428.7 L 1118.1 432.1 L 1111.8 433.3 L 1105.5 428.7 L 1104.2 433.3 L 1106.7 441.8 V 451.5 L 1102.1 460.8 L 1094.1 460.3 L 1089.9 457.4 L 1091.1 452.7 L 1076.8 443.9 V 447.7 L 1067.9 440.5 L 1065.8 441.8 V 445.6 L 1051.5 438.0 L 1045.6 435.4 L 1033.7 425.7 L 1030.4 415.6 L 1023.2 422.8 L 1016.4 417.7 L 995.3 412.2 L 983.1 405.9 L 977.6 405.1 V 414.8 L 974.2 419.4 L 965.0 408.9 L 956.1 405.1 L 954.4 414.3 H 947.2 L 940.1 409.3 H 937.1 L 934.2 425.7 H 923.6 L 918.5 439.2 L 919.4 451.1 L 912.2 457.4 L 917.7 465.8 L 921.5 466.7 L 923.6 473.8 V 478.1 L 934.2 486.1 L 943.9 488.2 L 960.3 496.6 L 968.8 509.7 L 1002.9 524.9 L 1019.4 525.3 L 1027.8 529.5 H 1035.4 L 1031.6 533.8 L 1040.5 541.8 L 1043.4 546.0 H 1059.9 L 1067.1 544.7 L 1070.9 549.4 L 1078.0 552.3 L 1081.8 555.3 L 1094.5 557.8 L 1105.9 551.1 L 1121.1 554.9 L 1129.1 562.4 L 1132.1 569.6 L 1141.8 573.4 L 1165.0 577.2 H 1171.3 L 1173.4 568.8 L 1165.0 563.3 L 1159.9 557.0 V 551.9 L 1146.8 548.1 L 1151.5 546.0 L 1163.7 548.1 L 1170.0 549.4 L 1174.7 552.3 L 1185.6 553.6 L 1192.8 559.1 L 1201.7 559.9 L 1209.7 569.6 L 1219.0 573.4 L 1225.7 578.5 L 1221.9 579.3 L 1212.6 577.2 L 1216.4 583.1 L 1229.1 592.4 L 1227.4 594.5 L 1221.9 597.9 H 1216.4 V 603.0 L 1203.8 606.3 L 1201.7 600.4 L 1195.3 599.6 V 594.5 L 1189.4 591.6 L 1187.3 593.2 L 1176.4 591.6 L 1168.3 584.8 L 1161.2 582.7 L 1159.9 587.3 L 1154.8 595.8 L 1154.0 599.6 L 1155.7 604.6 L 1152.7 605.5 L 1147.7 604.6 L 1145.6 597.9 L 1138.8 595.8 L 1134.6 589.0 H 1128.3 L 1122.3 593.2 L 1124.9 601.7 L 1123.2 606.3 L 1121.1 610.1 V 619.8 L 1118.5 624.5 L 1116.0 632.5 L 1128.3 639.2 L 1134.6 644.3 L 1143.4 646.4 L 1144.3 651.1 L 1140.5 654.4 L 1143.4 661.2 L 1140.5 663.7 L 1138.0 666.2 L 1147.7 673.8 L 1154.0 674.7 L 1173.4 684.4 H 1182.7 L 1192.8 685.2 L 1204.2 692.4 L 1210.5 702.1 L 1204.2 703.4 L 1202.9 711.0 L 1201.2 714.3 L 1211.4 718.1 L 1213.9 722.4 L 1212.6 725.7 L 1210.5 727.4 L 1212.6 730.0 L 1229.9 732.1 L 1236.7 732.9 L 1242.2 736.7 L 1243.4 732.9 L 1256.5 733.8 L 1257.8 737.6 L 1267.9 738.8 V 742.2 L 1274.7 741.4 L 1279.7 742.2 L 1283.1 746.8 L 1285.6 740.5 L 1290.3 739.2 L 1309.7 748.1 L 1309.3 752.3 L 1315.6 757.8 L 1316.9 760.3 L 1323.2 757.8 L 1328.7 759.9 L 1335.0 765.8 L 1345.1 778.5 L 1346.4 785.2 L 1351.0 782.7 L 1355.7 794.9 L 1361.6 792.8 L 1368.8 799.2 V 801.7 L 1373.0 805.9 V 811.0 H 1386.5 L 1394.5 813.5 L 1410.1 827.4 L 1418.1 838.8 L 1422.8 840.9 L 1429.1 843.0 L 1441.8 854.9 L 1445.6 860.8 L 1451.5 862.0 L 1454.8 873.4 H 1449.4 L 1451.5 879.7 L 1448.9 881.0 L 1450.2 884.4 L 1442.6 882.3 L 1448.1 890.7 V 892.4 L 1441.8 888.2 L 1440.1 892.0 L 1444.3 901.3 L 1437.1 899.2 L 1429.1 894.1 L 1424.9 895.8 L 1428.7 900.4 L 1435.0 903.8 L 1439.2 910.1 L 1442.6 912.7 L 1446.8 916.0 L 1445.6 917.7 L 1446.8 921.5 L 1452.7 928.7 L 1453.6 933.3 L 1462.9 939.7 L 1473.0 943.0 L 1478.5 948.5 L 1486.1 962.0 L 1487.3 965.8 L 1475.5 966.2 L 1473.0 970.0 L 1464.1 971.7 L 1459.1 972.2 L 1443.0 963.7 H 1433.3 L 1422.3 956.5 L 1415.6 951.9 L 1411.8 948.5 L 1408.8 942.2 L 1411.0 938.4 V 935.4 L 1402.9 928.7 L 1395.8 927.8 V 932.5 L 1392.8 933.3 L 1395.3 937.1 L 1394.9 940.9 L 1394.1 943.5 L 1399.1 947.3 L 1400.0 951.9 L 1399.1 954.4 L 1400.4 959.5 L 1398.7 961.6 L 1387.7 952.7 L 1384.4 951.1 L 1382.7 948.5 L 1380.6 952.7 H 1373.4 L 1364.1 947.3 L 1359.9 943.0 L 1354.0 935.9 L 1349.4 931.2 L 1345.1 927.4 L 1336.7 925.7 H 1334.2 L 1342.2 935.9 L 1343.4 942.2 L 1348.5 948.1 V 951.9 L 1344.7 954.9 L 1340.1 955.7 L 1334.2 951.5 L 1329.5 953.6 L 1322.3 944.3 L 1318.1 944.7 L 1313.1 948.5 L 1305.0 948.9 L 1297.0 949.8 L 1294.9 952.3 L 1278.5 949.8 L 1276.8 944.7 L 1278.5 940.1 L 1279.7 937.6 L 1284.8 933.8 V 930.4 L 1270.9 912.7 L 1254.4 911.0 L 1248.9 910.1 L 1243.9 901.3 L 1243.0 887.8 L 1239.2 869.2 L 1241.3 861.6 L 1241.8 852.3 L 1246.0 849.4 L 1247.2 844.3 L 1252.3 838.8 L 1250.2 828.7 L 1248.1 823.2 L 1243.0 818.1 V 812.2 L 1246.0 808.4 L 1243.4 802.5 V 789.0 V 785.2 L 1247.2 779.7 L 1243.0 775.5 L 1242.2 771.3 V 766.7 L 1244.3 763.3 L 1240.5 760.3 L 1237.5 757.0 H 1232.1 V 761.6 L 1235.0 765.0 L 1234.2 769.6 L 1237.5 773.8 V 776.4 L 1231.6 778.5 L 1232.5 786.1 L 1229.9 790.7 L 1224.9 789.0 L 1220.2 786.1 L 1222.3 792.0 L 1227.0 799.2 L 1228.7 802.5 L 1227.8 804.6 L 1221.5 806.8 L 1216.0 803.8 L 1213.9 808.4 L 1209.7 808.9 V 813.1 H 1205.0 L 1194.9 804.6 L 1189.0 797.0 L 1181.4 792.4 L 1172.6 794.1 H 1169.2 L 1167.5 792.0 V 795.8 V 800.4 L 1165.4 802.5 L 1164.5 805.9 L 1159.1 813.9 H 1155.7 L 1154.0 816.0 H 1152.3 L 1150.2 808.0 L 1143.9 801.7 L 1135.4 798.7 L 1141.3 809.7 V 819.4 L 1137.5 827.0 L 1140.5 830.8 L 1145.1 831.6 L 1146.8 838.4 L 1149.4 843.0 L 1148.9 846.8 L 1143.9 852.3 L 1141.3 861.2 L 1138.8 862.0 L 1132.1 857.8 L 1130.4 865.8 L 1120.7 867.1 L 1116.4 881.0 L 1117.7 890.3 L 1119.0 900.4 L 1114.3 905.9 L 1117.7 909.7 L 1129.5 912.2 L 1134.6 916.5 L 1132.1 920.3 L 1121.9 919.0 L 1127.0 927.8 L 1132.9 932.5 V 941.8 L 1123.2 937.1 L 1123.6 944.7 L 1116.4 946.4 L 1108.4 940.9 L 1088.6 941.8 L 1092.0 948.1 L 1090.3 950.6 L 1097.9 960.3 V 964.1 L 1101.7 967.5 V 970.9 H 1098.7 L 1100.8 973.8 L 1100.4 978.1 H 1098.7 V 981.9 L 1097.0 987.3 L 1093.2 991.6 L 1086.1 985.7 L 1081.4 997.5 H 1076.4 L 1072.6 1008.9 H 1068.3 L 1066.2 1016.0 L 1071.3 1021.5 L 1077.6 1023.2 L 1078.5 1028.7 L 1077.6 1032.1 L 1072.6 1030.0 L 1071.7 1034.2 L 1072.6 1038.8 L 1066.2 1043.0 L 1063.3 1037.1 H 1060.3 L 1053.1 1030.8 L 1051.9 1024.1 L 1048.9 1018.6 L 1038.8 1011.4 L 1035.4 1002.5 L 1027.4 996.2 L 1021.5 981.0 L 1020.7 971.3 L 1019.4 964.1 L 1011.4 958.6 L 1001.2 959.1 V 962.9 L 993.7 966.2 L 992.4 949.8 L 1000.4 940.1 L 997.9 932.1 L 1003.4 923.6 L 1001.2 918.1 L 996.2 914.8 L 1009.3 908.9 V 894.1 L 1006.7 893.2 L 1003.4 898.7 L 1002.5 904.2 L 998.3 900.0 L 997.0 881.9 L 994.5 873.0 L 992.4 863.7 L 991.1 868.8 L 988.6 871.7 V 882.7 L 986.5 886.1 L 979.7 901.7 L 975.1 900.4 V 887.8 L 973.4 876.8 L 971.3 871.7 L 972.6 868.8 L 960.7 866.7 L 953.1 855.7 L 955.3 844.3 L 953.1 841.8 V 832.1 L 965.0 818.6 L 966.2 805.9 L 969.6 800.8 L 968.8 792.8 L 973.0 770.9 V 758.6 L 968.8 746.4 V 742.2 L 965.0 735.0 L 959.5 717.7 L 945.6 709.7 L 943.9 693.2 L 944.7 681.4 L 942.6 678.9 L 940.9 675.1 L 931.2 670.9 L 912.6 659.5 L 911.8 655.3 L 893.7 653.6 L 884.4 658.6 L 868.3 657.4 L 865.4 648.9 L 864.1 640.9 L 856.9 627.4 L 855.7 618.1 L 849.4 615.6 L 848.9 611.4 L 844.7 608.9 L 843.4 603.4 L 828.3 593.7 L 817.3 588.2 L 812.2 580.6 L 813.1 571.3 L 808.0 572.2 L 805.9 569.2 V 565.0 L 795.8 563.3 L 794.1 559.9 L 791.1 561.2 L 793.2 567.5 L 791.1 570.0 L 793.2 573.4 L 792.0 586.1 H 780.6 L 779.7 594.1 L 783.5 599.6 L 786.5 601.3 L 788.6 609.7 L 790.3 613.1 L 798.7 614.8 L 802.5 619.8 L 808.0 622.4 L 808.8 627.4 L 799.6 626.2 L 797.5 624.9 H 794.1 L 789.4 619.8 L 786.5 621.5 H 783.5 L 779.7 612.2 L 774.2 613.1 L 772.6 608.0 L 774.2 604.6 L 770.0 603.4 L 765.4 606.8 L 759.5 599.6 L 760.7 589.9 L 754.8 584.4 L 756.1 578.5 L 744.7 565.0 L 744.3 558.6 L 740.1 556.1 L 738.4 549.8 L 741.3 546.4 L 738.4 540.9 L 732.5 542.2 L 731.2 538.8 L 735.8 535.0 L 734.6 533.3 L 727.4 530.0 L 723.6 524.5 L 713.9 522.8 L 708.8 519.4 L 705.5 521.9 H 695.3 L 694.1 525.7 H 691.1 L 689.0 522.8 L 675.5 520.3 L 674.7 524.5 L 669.6 529.1 L 671.7 530.8 V 534.2 L 668.3 535.9 L 664.1 540.9 L 654.0 542.2 L 646.4 537.6 L 645.6 529.1 L 647.2 525.7 L 642.6 515.6 L 635.4 508.0 L 630.8 496.6 L 623.2 489.5 L 608.8 483.1 L 602.5 478.9 L 598.7 468.4 L 600.8 464.1 L 596.2 454.0 L 591.5 455.3 L 583.1 441.8 L 584.0 436.7 L 581.8 435.0 L 582.3 431.2 L 577.2 428.3 L 578.5 422.8 L 577.2 419.8 L 584.0 410.1 L 585.2 403.4 L 587.3 401.7 L 588.6 392.0 L 596.2 384.4 L 599.6 383.5 L 605.9 376.8 L 626.1 363.3 L 630.8 362.4 L 632.5 358.6 L 635.4 356.5 L 639.6 351.1 L 647.7 346.8 L 654.4 341.8 L 659.9 339.7 L 658.2 329.1 L 660.7 321.5 L 665.4 318.1 L 666.2 309.7 L 659.1 303.4 L 652.7 299.6 L 641.3 286.5 L 638.4 277.6 L 636.7 271.7 L 632.5 265.8 L 627.4 264.6 L 616.0 265.4 L 608.0 268.4 L 604.2 271.7 L 602.1 278.5 L 594.5 277.6 L 588.6 274.7 L 587.3 276.8 L 580.6 274.7 L 577.2 275.5 L 572.1 273.4 L 571.3 269.6 L 576.4 268.4 L 572.1 263.7 L 564.5 260.3 L 559.5 251.9 L 558.2 244.7 L 553.2 250.2 M 274.3 -199.6 L 280.2 -178.9 L 274.3 -167.1 V -155.7 L 284.4 -140.9 L 289.5 -115.2 L 296.6 -104.6 L 309.7 -93.7 L 317.7 -84.0 L 327.0 -78.5 L 335.9 -71.3 L 341.8 -67.1 L 350.6 -71.3 L 362.9 -63.7 L 372.2 -59.1 L 376.8 -52.7 L 373.8 -40.5 L 376.8 -36.3 L 383.1 -34.2 L 388.2 -27.0 L 397.5 -21.1 L 399.2 -15.6 V -7.6 L 397.5 0.4 L 403.8 8.4 L 415.2 13.1 L 425.7 14.8 L 435.9 21.9 L 449.8 30.0 L 456.1 37.6 L 465.0 57.0 V 75.1 L 467.1 91.1 L 463.3 104.2 L 457.4 116.9 L 447.3 128.7 L 444.3 139.2 L 447.3 154.9 L 454.0 166.7 L 471.3 179.3 L 483.1 186.1 L 493.7 190.3 L 507.2 192.0 L 519.4 196.6 L 533.6 199.6 L 545.2 213.1 L 550.4 228.3 L 553.1 244.7 L 553.2 250.2 M 553.2 250.2 L 548.3 255.7 L 547.8 267.7 L 548.2 273.7 M 343.0 152.3 L 348.9 163.3 L 357.8 173.0 L 361.6 181.0 L 366.2 188.6 H 379.7 L 389.9 186.1 L 401.7 190.3 L 407.6 194.1 L 412.7 202.1 V 210.1 L 417.7 217.7 L 424.1 223.6 H 434.6 L 444.3 219.8 L 454.9 221.1 L 465.0 226.6 L 473.4 236.3 L 475.5 244.7 V 251.9 L 483.1 260.3 L 491.1 269.6 L 500.0 277.6 L 503.6 280.0 M 548.2 273.7 L 539.7 276.8 L 523.2 283.5 L 507.2 282.3 L 503.6 280.0 M 0.0 60.3 H 7.6 L 13.5 65.8 L 17.7 70.9 H 30.4 L 45.1 79.3 L 53.6 84.0 L 65.0 78.1 L 73.8 76.8 L 79.3 80.6 L 81.0 87.3 L 84.4 92.0 L 94.5 94.1 L 102.5 97.9 V 105.1 L 105.5 111.4 H 115.6 L 124.5 107.6 L 128.7 111.4 V 120.3 L 131.2 125.7 L 136.7 127.4 L 146.4 123.2 H 151.9 L 154.4 129.1 L 165.8 132.9 L 169.2 138.0 L 167.1 145.6 V 152.3 L 170.9 161.6 L 171.7 166.7 L 167.1 171.3 L 165.8 178.9 L 171.7 186.9 H 181.4 L 192.4 183.5 L 203.8 183.1 L 213.1 188.2 L 217.3 195.4 L 228.3 200.4 H 238.8 L 249.4 197.5 L 256.5 199.6 L 263.3 205.9 L 265.8 216.5 L 264.1 228.3 L 265.8 234.2 C 269.2 235.3 276.1 237.6 276.8 237.6 C 277.5 237.6 283.3 237.0 286.1 236.7 L 293.2 239.7 L 296.6 248.9 L 307.6 252.7 H 317.7 L 324.1 249.8 L 329.5 244.7 L 335.9 238.8 L 343.5 240.9 C 345.6 242.1 349.8 244.4 349.8 244.7 C 349.8 245.1 348.7 249.9 348.1 252.3 L 341.8 257.0 L 341.4 261.6 L 345.1 266.7 L 361.6 270.5 L 382.7 277.6 L 389.9 286.5 L 400.0 292.0 L 418.6 296.6 H 429.5 L 446.4 305.1 L 459.9 305.9 L 472.2 300.0 L 478.5 290.8 L 483.1 283.5 L 492.0 280.2 L 503.6 280.0 M 1229.1 605.5 L 1236.3 608.4 H 1243.0 L 1246.4 612.2 L 1253.6 615.6 L 1266.7 610.1 L 1262.0 604.6 L 1253.6 600.4 H 1243.9 L 1239.2 603.0 H 1232.5 L 1229.1 605.5 Z M 1256.5 587.3 L 1262.0 595.8 V 601.7 L 1267.9 604.6 L 1275.9 600.4 L 1277.2 594.5 L 1268.8 589.0 L 1259.9 586.5 L 1256.5 587.3 Z M 1141.8 445.6 L 1146.0 439.7 L 1151.5 435.4 L 1156.1 439.2 L 1158.2 443.9 L 1152.3 448.9 L 1141.8 445.6 Z M 1083.2 233.6 L 1101.0 223.9 L 1110.9 202.4 L 1169.9 196.9 L 1209.0 214.9 L 1230.6 207.2 L 1316.9 215.7 L 1340.6 198.2 L 1369.9 198.4 L 1405.1 226.1 L 1426.6 236.1 L 1422.5 249.8 L 1404.9 259.5 L 1396.9 277.1 L 1363.5 284.7 L 1340.0 280.6 L 1306.5 290.2 L 1275.1 299.8 L 1218.1 305.3 L 1169.2 279.4 L 1134.0 275.2 L 1089.0 251.3 L 1083.2 233.6 Z M 959.2 904.2 L 978.0 942.8 L 981.3 965.9 L 964.7 994.5 L 949.2 990.1 L 931.5 972.5 L 934.9 958.2 L 931.5 940.6 L 934.9 921.8 L 949.2 916.3 L 954.8 904.2 H 959.2 Z M 989.6 980.6 L 981.3 996.4 L 969.6 1010.6 L 971.2 1030.6 L 969.6 1047.3 L 978.8 1056.5 V 1073.2 L 990.5 1087.4 L 994.7 1081.6 L 1016.4 1112.4 L 1032.3 1125.8 L 1042.4 1130.0 L 1045.7 1108.3 L 1053.2 1112.4 V 1098.2 L 1061.6 1094.9 L 1058.2 1071.5 L 1036.5 1052.3 L 1029.0 1019.8 L 1018.1 998.9 L 1003.1 978.1 L 989.6 980.6 Z M 1250.2 975.3 L 1267.8 965.9 L 1293.6 982.4 L 1314.7 988.2 L 1326.4 969.4 L 1355.7 995.3 L 1375.6 1044.7 L 1381.5 1092.9 L 1388.5 1111.7 L 1381.5 1121.2 L 1359.2 1111.7 L 1355.7 1121.2 L 1314.7 1105.9 L 1305.3 1116.5 L 1273.7 1095.3 L 1264.3 1067.0 L 1273.7 1038.8 L 1250.2 1015.3 L 1256.1 991.8 L 1250.2 975.3 Z M 1392.1 1088.2 L 1405.0 1085.9 L 1409.6 1076.5 L 1422.5 1092.9 L 1424.9 1114.1 L 1409.6 1111.7 H 1401.4 L 1392.1 1088.2 Z`, opacity: 0.5, // Overridden in render to 0.375 }, ]; /* ───────────────────────────────────────────────────────────────────────────── * TYPE STYLE DEFINITIONS * ───────────────────────────────────────────────────────────────────────────── * Visual properties for each landmark type. This drives icon rendering * without hardcoding colors/shapes into the render logic. Adding a new * landmark type means adding one entry here and one `case` in LandmarkIcon. * * Color values are kept as hex literals (not CSS var() strings) because many * usages concatenate an alpha suffix: e.g. style.color + "66". These values * match the CSS tokens defined in tokens.css: * city / location → --landmark-city (#c8b832) * city label → --landmark-city-label (#e8d84a) * region → --landmark-region (#00d4ff) * location label → --landmark-location-label (#c8b862) * hotzone → --landmark-hotzone (#ff3344) * hotzone label → --landmark-hotzone-label (#ff5566) */ const TYPE_STYLES = { city: { color: "#c8b832", glowColor: "#c8b83266", dashArray: "6 4", labelColor: "#e8d84a", }, region: { color: "#00d4ff", glowColor: "#00d4ff44", dashArray: "8 5", labelColor: "#00d4ff", }, location: { color: "#c8b832", glowColor: "#c8b83244", dashArray: "5 3", labelColor: "#c8b862", }, hotzone: { color: "#ff3344", glowColor: "#ff334444", dashArray: "4 4", labelColor: "#ff5566", }, }; /* ───────────────────────────────────────────────────────────────────────────── * COMPONENT: LandmarkIcon * ───────────────────────────────────────────────────────────────────────────── * Renders the appropriate SVG icon for a landmark based on its type. * Each type has a distinct geometric shape to maintain visual taxonomy: * city → irregular polygon (organic settlement footprint) * region → large dashed circle (geographic area) * location → rotated square / diamond (structural point) * hotzone → concentric circles with warning triangle * * FUTURE: When landmark graphics are authored as custom SVG, this component * can be extended to accept a `customPath` field from the landmark data, * falling back to these procedural shapes for any landmark without one. */ const LandmarkIcon = ({ landmark, isHovered, isSelected, onClick, onHover }) => { const style = TYPE_STYLES[landmark.type]; const s = 24 * (landmark.size || 1); // Base icon size scaled by landmark's size multiplier const { x, y } = landmark; // Glow filter and stroke width respond to hover/select state const glowIntensity = isSelected ? 12 : isHovered ? 8 : 0; const strokeWidth = isSelected ? 2.5 : isHovered ? 2 : 1.5; // Wrap onClick to stop event propagation — without this, clicks on // landmarks bubble up to the container's handleBackgroundClick which // deselects the current landmark, causing a hitch when trying to // navigate directly between landmarks. const handleClick = (e) => { e.stopPropagation(); onClick(landmark.id); }; let shapeElement = null; switch (landmark.type) { case "city": // Irregular polygon — approximates an organic settlement footprint. // Vertices are authored as multipliers of `s` offset from center. shapeElement = ( 0 ? `url(#glow-${landmark.type})` : undefined} style={{ cursor: "pointer", transition: "all 0.3s ease" }} onClick={handleClick} onMouseEnter={() => onHover(landmark.id)} onMouseLeave={() => onHover(null)} /> ); break; case "region": // Large dashed circle — represents a broad geographic area shapeElement = ( 0 ? `url(#glow-${landmark.type})` : undefined} style={{ cursor: "pointer", transition: "all 0.3s ease" }} onClick={handleClick} onMouseEnter={() => onHover(landmark.id)} onMouseLeave={() => onHover(null)} /> ); break; case "location": // Rotated square (diamond) — structural point of interest shapeElement = ( 0 ? `url(#glow-${landmark.type})` : undefined} style={{ cursor: "pointer", transition: "all 0.3s ease" }} onClick={handleClick} onMouseEnter={() => onHover(landmark.id)} onMouseLeave={() => onHover(null)} /> ); break; case "hotzone": // Concentric dashed circles with warning triangle and exclamation dot shapeElement = ( onHover(landmark.id)} onMouseLeave={() => onHover(null)} > {/* Outer ring — wider, lower opacity */} {/* Inner ring — primary interactive area */} 0 ? `url(#glow-${landmark.type})` : undefined} style={{ transition: "all 0.3s ease" }} /> {/* Warning triangle */} {/* Exclamation dot */} ); break; default: break; } return shapeElement; }; /* ───────────────────────────────────────────────────────────────────────────── * COMPONENT: LandmarkLabel * ───────────────────────────────────────────────────────────────────────────── * Renders the text label above a landmark. Positioned relative to the * landmark's center, offset upward by its size so labels don't overlap icons. * Region labels get extra offset since their circle radius is larger. * Labels are non-interactive (pointerEvents: none) so they don't interfere * with icon click/hover targets. */ const LandmarkLabel = ({ landmark, isHovered, isSelected }) => { const style = TYPE_STYLES[landmark.type]; const s = 24 * (landmark.size || 1); const labelY = landmark.type === "region" ? landmark.y - s * 1.8 - 12 // Above the circle radius : landmark.y - s - 14; // Above the icon bounds return ( {landmark.name} ); }; /* ───────────────────────────────────────────────────────────────────────────── * COMPONENT: PopoverCard * ───────────────────────────────────────────────────────────────────────────── * The expanded information panel that appears when a landmark is selected. * Rendered via React portal to document.body (see POPOVER ARCHITECTURE note * at the top of this file) so it is free of the map container's * overflow:hidden clipping. * * Uses position:fixed with viewport-relative coordinates from getScreenCTM(). * The transform translate(-50%, -110%) centers the card horizontally above * the landmark with a small gap, and the gradient connector line bridges * the visual gap down to the landmark position. * * Contains: * - Type badge (e.g. "◆ CITY", "◆ HOT ZONE") * - Landmark name heading * - Gradient divider * - Description text * - [FUTURE] "View in Field Record" CTA button * * FUTURE: The CTA button calls onNavigateToRecord(landmark.fieldRecordSlug) * which the parent page uses to route to the Field Record entry. The button * markup is included below as a comment block — uncomment and wire when the * Field Record navigation system is ready to accept external entry points. * * CSS CLASSES: * Static structural styles live in operations.css (.worldmap-popover-*). * Dynamic values driven by landmark type color remain as inline styles. */ const PopoverCard = ({ landmark, position, visible, onClose, onNavigateToRecord }) => { const style = TYPE_STYLES[landmark.type]; const cardRef = useRef(null); if (!landmark || !position) return null; const typeLabels = { city: "CITY", region: "REGION", location: "LOCATION", hotzone: "HOT ZONE", }; return (
e.stopPropagation()} > {/* Card body — dynamic border and boxShadow keyed to landmark type color */}
{/* Type badge */}
◆ {typeLabels[landmark.type]}
{/* Name */}
{landmark.name}
{/* Divider */}
{/* Description */}
{landmark.description}
{/* * FUTURE: "View in Field Record" CTA button * Uncomment and wire when Field Record navigation is ready. * The onNavigateToRecord prop should be passed down from the * parent page component that controls routing. * * {landmark.fieldRecordSlug && ( * * )} */}
{/* Connector line pointing down to the landmark */}
); }; /* ───────────────────────────────────────────────────────────────────────────── * COMPONENT: PZOWorldMap * ───────────────────────────────────────────────────────────────────────────── * The main map component. Orchestrates all subsystems: * * RENDERING: * SVG scene with layered content (back to front): * 1. Background rect (landmass fill — var(--map-land)) * 2. Grid lines (var(--map-grid), 0.75px, 40% opacity) * 3. Terrain (sea fill var(--map-sea) at 40%, coastline var(--map-grid) at 37.5%) * 4. Landmark icons (type-driven shapes) * 5. Landmark labels (Share Tech Mono, uppercase) * 6. Vignette overlay (radial gradient edge darkening) * Plus DOM overlays (not in SVG, not affected by perspective): * - Scanline texture (.worldmap-scanline) * - Title badge (.worldmap-title-badge) * - Compass rose (.worldmap-compass) * - Coordinate readout (.worldmap-coord-readout) * * PARALLAX: * Animation loop runs via requestAnimationFrame, writing directly to * the SVG viewBox attribute (no React re-renders). panCurrent lerps * toward panTarget at the configured easing rate. Mouse position maps * to pan offset via cubic easing (pow 1.5). * * SELECTION: * Clicking a landmark locks parallax, pans toward the landmark, and * opens a portaled popover. Clicking again or clicking background * dismisses and unlocks parallax. */ function PZOWorldMap() { // ── State ────────────────────────────────────────────────────────────── const [hoveredId, setHoveredId] = useState(null); const [selectedId, setSelectedId] = useState(null); const [popoverPos, setPopoverPos] = useState(null); // Controls the popover's visible fade-in. The popover always renders // immediately when selected (so position tracking works from frame one), // but starts at opacity 0. After a 500ms delay, popoverVisible flips // to true, triggering a 500ms CSS opacity transition to full visibility. // This hides the ghost-position flash during the pan animation without // blocking the position tracker from running. const [popoverVisible, setPopoverVisible] = useState(false); // ── Refs ─────────────────────────────────────────────────────────────── const containerRef = useRef(null); const svgRef = useRef(null); const animFrameRef = useRef(null); // Parallax state stored in refs to avoid re-renders on every mouse move. // panTarget is where we WANT to be; panCurrent is where we ARE (lerped). const panTarget = useRef({ x: 0, y: 0 }); const panCurrent = useRef({ x: 0, y: 0 }); const isLockedToLandmark = useRef(false); const mouseInside = useRef(false); const { canvasWidth: CW, canvasHeight: CH, parallaxOverflow, panEasing, gridSpacing } = MAP_CONFIG; // ── Derived values ───────────────────────────────────────────────────── // Maximum pan offset in each direction (canvas units). // This is half the overflow, since pan is centered. const maxPanX = (CW * (parallaxOverflow - 1)) / 2; const maxPanY = (CH * (parallaxOverflow - 1)) / 2; // ── Memoized landmark lookup ────────────────────────────────────────── const landmarkMap = useMemo(() => { const map = {}; LANDMARK_DATA.forEach((lm) => (map[lm.id] = lm)); return map; }, []); // ── Parallax animation loop ─────────────────────────────────────────── // Runs continuously via requestAnimationFrame. Smoothly interpolates // panCurrent toward panTarget using the configured easing factor. // Updates the SVG viewBox directly (no React state = no re-renders). const animatePan = useCallback(() => { const cur = panCurrent.current; const tgt = panTarget.current; // Lerp toward target cur.x += (tgt.x - cur.x) * panEasing; cur.y += (tgt.y - cur.y) * panEasing; // Snap if close enough to avoid infinite micro-adjustments if (Math.abs(tgt.x - cur.x) < 0.01) cur.x = tgt.x; if (Math.abs(tgt.y - cur.y) < 0.01) cur.y = tgt.y; // Apply to SVG viewBox if (svgRef.current) { const vbX = -maxPanX - cur.x; const vbY = -maxPanY - cur.y; const vbW = CW + maxPanX * 2; const vbH = CH + maxPanY * 2; svgRef.current.setAttribute("viewBox", `${vbX} ${vbY} ${vbW} ${vbH}`); } animFrameRef.current = requestAnimationFrame(animatePan); }, [CW, CH, maxPanX, maxPanY, panEasing]); // Start/stop the animation loop useEffect(() => { animFrameRef.current = requestAnimationFrame(animatePan); return () => { if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current); }; }, [animatePan]); // ── Mouse move handler (parallax tracking) ──────────────────────────── // Converts cursor position within the container to a pan offset. // When cursor is at center: pan = 0. At edges: pan = ±maxPan. // Cubic easing (pow 1.5) makes movement gentle near center and // accelerating toward edges. const handleMouseMove = useCallback( (e) => { if (isLockedToLandmark.current) return; const rect = containerRef.current?.getBoundingClientRect(); if (!rect) return; // Normalize cursor position to -1..+1 range const nx = ((e.clientX - rect.left) / rect.width) * 2 - 1; const ny = ((e.clientY - rect.top) / rect.height) * 2 - 1; // Cubic easing for natural feel const easedX = Math.sign(nx) * Math.pow(Math.abs(nx), 1.5); const easedY = Math.sign(ny) * Math.pow(Math.abs(ny), 1.5); panTarget.current = { x: -easedX * maxPanX, y: -easedY * maxPanY, }; }, [maxPanX, maxPanY] ); const handleMouseEnter = useCallback(() => { mouseInside.current = true; }, []); const handleMouseLeave = useCallback(() => { mouseInside.current = false; if (!isLockedToLandmark.current) { // Ease back to center when cursor leaves panTarget.current = { x: 0, y: 0 }; } }, []); // ── Popover position tracking ───────────────────────────────────────── // Projects a landmark's SVG-space position into viewport-relative screen // coordinates using getScreenCTM(). Since the popover uses position:fixed, // viewport coords are exactly what we need. const updatePopoverPosition = useCallback((lm) => { if (!svgRef.current) return; const svg = svgRef.current; const pt = svg.createSVGPoint(); pt.x = lm.x; pt.y = lm.y; const ctm = svg.getScreenCTM(); if (!ctm) return; const screenPt = pt.matrixTransform(ctm); setPopoverPos({ x: screenPt.x, y: screenPt.y }); }, []); // ── Landmark selection ──────────────────────────────────────────────── // When a landmark is clicked: // 1. Lock parallax panning (stop following cursor) // 2. Set pan target to shift toward the landmark (damped by 0.5) // 3. Begin updating popover position every frame const handleDeselect = useCallback(() => { if (window.PZO.sound) window.PZO.sound.collapse(); setSelectedId(null); setPopoverPos(null); setPopoverVisible(false); isLockedToLandmark.current = false; if (!mouseInside.current) { panTarget.current = { x: 0, y: 0 }; } }, []); // Ref to track the fade-in delay timer so we can cancel it on // rapid re-selection or deselection const popoverTimerRef = useRef(null); // Clear popover timer on unmount to prevent state updates after teardown useEffect(() => { return () => { if (popoverTimerRef.current) clearTimeout(popoverTimerRef.current); }; }, []); const handleLandmarkClick = useCallback( (id) => { if (selectedId === id) { // Clicking same landmark again deselects handleDeselect(); return; } const lm = landmarkMap[id]; if (!lm) return; // Clear any pending timers from a previous selection if (popoverTimerRef.current) clearTimeout(popoverTimerRef.current); // Clamp pan so the viewBox never overshoots the parallax travel range. // Without this, landmarks near top/bottom edges pull the viewport past // the terrain boundaries, exposing raw background. const clampedPan = { x: Math.max(-maxPanX, Math.min(maxPanX, -(lm.x - CW / 2) * 0.5)), y: Math.max(-maxPanY, Math.min(maxPanY, -(lm.y - CH / 2) * 0.5)), }; const isSwap = selectedId !== null; if (!isSwap) { // FRESH OPEN: No popover showing. Select immediately, start // invisible, then fade in after 500ms delay. setPopoverVisible(false); setSelectedId(id); isLockedToLandmark.current = true; panTarget.current = clampedPan; requestAnimationFrame(() => updatePopoverPosition(lm)); popoverTimerRef.current = setTimeout(() => { setPopoverVisible(true); if (window.PZO.sound) window.PZO.sound.expand(); }, 500); } else { // SWAP: Popover is currently visible. Fade it out first (500ms // CSS transition), then after the fade-out completes, switch // to the new landmark and fade the new popover in. setPopoverVisible(false); isLockedToLandmark.current = true; panTarget.current = clampedPan; popoverTimerRef.current = setTimeout(() => { // Fade-out complete — switch to new landmark setSelectedId(id); requestAnimationFrame(() => updatePopoverPosition(lm)); // Schedule fade-in after another 500ms popoverTimerRef.current = setTimeout(() => { setPopoverVisible(true); if (window.PZO.sound) window.PZO.sound.expand(); }, 500); }, 500); } }, [selectedId, landmarkMap, CW, CH, handleDeselect, updatePopoverPosition] ); // Continuously update popover position as the map pans to keep it anchored useEffect(() => { if (!selectedId) return; const lm = landmarkMap[selectedId]; if (!lm) return; let running = true; const update = () => { if (!running) return; updatePopoverPosition(lm); requestAnimationFrame(update); }; update(); return () => { running = false; }; }, [selectedId, landmarkMap, updatePopoverPosition]); const handleBackgroundClick = useCallback((e) => { if (selectedId) handleDeselect(); }, [selectedId, handleDeselect]); /* * PORTAL CLICK-OUTSIDE HANDLING * Since the popover is portaled to document.body, we need a document-level * click listener to dismiss it when clicking outside. The popover itself * calls stopPropagation on clicks so they don't reach this handler. * The map container's own onClick also calls handleDeselect for clicks * on the map background. This listener catches clicks on surrounding page * content that would otherwise be ignored. * * Uses setTimeout(0) to avoid the same click that selected the landmark * from immediately triggering dismissal via event bubbling. */ useEffect(() => { if (!selectedId) return; const handleGlobalClick = (e) => { if (containerRef.current && !containerRef.current.contains(e.target)) { handleDeselect(); } }; const timer = setTimeout(() => { document.addEventListener("click", handleGlobalClick); }, 0); return () => { clearTimeout(timer); document.removeEventListener("click", handleGlobalClick); }; }, [selectedId, handleDeselect]); // ── Grid lines (rendered as SVG) ────────────────────────────────────── // Grid covers the full canvas plus overflow so it's always visible during // panning. Color: --map-grid at 40% opacity, 0.75px stroke. // Grid is rendered behind terrain so sea fill can partially occlude it // (sea at 40% opacity lets grid bleed through for unified scanner feel). const gridLines = useMemo(() => { const lines = []; const pad = maxPanX + gridSpacing; for (let x = -pad; x <= CW + pad; x += gridSpacing) { lines.push( ); } for (let y = -pad; y <= CH + pad; y += gridSpacing) { lines.push( ); } return lines; }, [CW, CH, maxPanX, maxPanY, gridSpacing]); // ── Render ───────────────────────────────────────────────────────────── const selectedLandmark = selectedId ? landmarkMap[selectedId] : null; return (
{/* ── Topbar (matches Stage Map chrome) ──────────────────────────── */}
World Map Orryx // Regional Survey
{/* ── Map viewport ──────────────────────────────────────────────── */}
{/* Perspective-warped map layer */}
{/* ── SVG Definitions (filters, gradients) ────────────────── */} {/* Glow filters for each landmark type */} {Object.entries(TYPE_STYLES).map(([type, s]) => ( ))} {/* Vignette gradient for the map edges */} {/* ── Background fill (landmass) ──────────────────────────── */} {/* ── Grid ────────────────────────────────────────────────── */} {gridLines} {/* ── Terrain layers ──────────────────────────────────────── */} {/* * Terrain is rendered on top of the grid. The sea fill at 40% * opacity lets grid lines show through for a unified scanner * aesthetic. The coastline is a solid stroke in grid color. */} {TERRAIN_DATA.map((terrain) => { switch (terrain.type) { case "sea": return ( ); case "coastline": return ( ); default: return null; } })} {/* ── Landmark icons ──────────────────────────────────────── */} {LANDMARK_DATA.map((lm) => ( ))} {/* ── Landmark labels ─────────────────────────────────────── */} {LANDMARK_DATA.map((lm) => ( ))} {/* ── Vignette overlay ────────────────────────────────────── */}
{/* ── Popover Card (portaled to document.body) ────────────────── */} {/* * Rendered via React portal so it is free of the map container's * overflow:hidden clipping. Position tracking uses fixed positioning * with viewport-relative coordinates from getScreenCTM(), updated * every animation frame while a landmark is selected. */} {selectedLandmark && popoverPos && ReactDOM.createPortal( { // TODO: wire to SPA router — e.g. PZO.navigate(`/field-reference#${slug}`); }} />, document.body )} {/* ── Scanline overlay (cosmetic) ──────────────────────────────── */} {/* * Faint horizontal scanlines to reinforce the terminal/holographic * display aesthetic. Pure CSS class, no performance cost. */}
{/* ── Compass Rose (fixed overlay, does not pan with map) ───────── */} {/* * Rendered as a DOM overlay rather than inside the SVG so it stays * fixed while the map pans beneath it. Reinforces the HUD/instrument * overlay aesthetic. */}
N
{/* end .worldmap-container */} {/* ── Bottom bar (coordinate readout, matches Stage Map chrome) ──── */}
SECTOR 7G • ORRYX SURFACE • GRID REF ACTIVE
/* end .worldmap-frame */ ); } window.OpsTool_WorldMap = PZOWorldMap; }());