// DiveDex — OceanDex archetype + variant + registry system.
// Runs under Babel standalone. ES modules unavailable. Only public renderer + registries
// are exposed on window; archetype renderers stay module-local inside the IIFE.
(function () {
// ── helpers ─────────────────────────────────────────────────────────
const EYE = (cx, cy, r = 2.6) => (
);
// accent helpers — applied by archetypes when variant requests them
const Spots = ({ points, fill = '#EAF5FF', op = 0.55, r = 2 }) =>
{points.map(([x,y,rr],i) => )};
const Stripes = ({ lines, stroke = '#021321', op = 0.5, w = 1.2 }) =>
{lines.map((d,i)=>)};
// ── ARCHETYPES (each returns ; side-profile faces RIGHT) ─────────
// Each archetype accepts (props) — variant.style is destructured for accents.
const A = {}; // archetype registry
A.hammerhead = ({ color = '#D7B46A', style = {} }) => {
const headW = style.headW ?? 1.0; // 1.0 scalloped, 1.18 great, 0.95 smooth
const scallop = style.scallop !== false;
const offX = 222 + (headW-1)*22;
return (
{scallop && <>
>}
{EYE(offX-10, 62, 2.4)}
{style.school && [0,1,2,3].map(i => (
))}
);
};
A.reefShark = ({ color = '#82ECFF', style = {} }) => (
{style.finTip && }
{style.finTip && }
{style.belly && }
{style.stripes && `M${x} 60 Q ${x+2} 76 ${x-2} 90`)} w={1.2}/>}
{/* Saddle bands along the back — for banded reef sharks (e.g. houndshark) */}
{style.bands && (
{[
{ cx: 80, rx: 12, ry: 4.5 },
{ cx: 110, rx: 13, ry: 4.5 },
{ cx: 140, rx: 12, ry: 4.5 },
{ cx: 170, rx: 10, ry: 4 },
].map((b, i) => (
))}
)}
{EYE(200, 70, 2.4)}
);
A.whaleShark = ({ color = '#82ECFF', style = {} }) => (
{EYE(206, 66, 1.8)}
{style.spots !== false && }
);
A.zebraShark = ({ color = '#D7B46A', style = {} }) => (
{EYE(204, 74, 2.2)}
);
A.thresherShark = ({ color = '#82ECFF' }) => (
{/* huge upper tail lobe — defining feature */}
{EYE(170, 70, 2.2)}
);
A.bullheadShark = ({ color = '#D7B46A' }) => (
{EYE(196, 80, 2)}
);
A.nurseShark = ({ color = '#B0A07A' }) => (
{EYE(200, 74, 2.2)}
);
// ── rays ────────────────────────────────────────────────────────────
A.mantaRay = ({ color = '#8B7BFF', style = {} }) => (
{EYE(96, 50, 2.2)}{EYE(144, 50, 2.2)}
{style.underspots && }
);
A.eagleRay = ({ color = '#82ECFF' }) => (
{EYE(96, 56, 1.8)}{EYE(144, 56, 1.8)}
);
A.stingray = ({ color = '#A89070', style = {} }) => (
{style.bluespots && }
{EYE(102, 60, 1.8)}{EYE(138, 60, 1.8)}
);
A.devilRay = ({ color = '#5A7BBF' }) => (
{EYE(100, 50, 2)}{EYE(140, 50, 2)}
);
// ── turtles ─────────────────────────────────────────────────────────
A.turtle = ({ color = '#63FFD2', style = {} }) => (
{style.beak
?
: }
{EYE(28, 62, 2)}
{style.leatherback ? <>
> : <>
>}
{style.patternedShell && }
);
// ── eels & morays ───────────────────────────────────────────────────
A.moray = ({ color = '#FF7A6B', style = {} }) => (
{style.dragon && }
{EYE(20, 34, 1.8)}
{style.bands && [200,180,160,140,120,100,80,60,40].map((x,i)=>
120?(x-120)*0.2:0) + (i%2?2:-2)}
rx="3" ry="6" fill="#021321" opacity="0.35"
transform={`rotate(${(x-120)*0.4} ${x} 70)`}/>)}
{style.spots && }
);
A.ribbonEel = ({ color = '#31D7FF' }) => (
{EYE(34, 44, 1.6)}
);
A.gardenEel = ({ color = '#9BB6CC' }) => (
{EYE(40, 36, 1.4)}{EYE(90, 50, 1.4)}{EYE(136, 34, 1.4)}{EYE(182, 54, 1.4)}
);
// ── pipefish / seahorse ─────────────────────────────────────────────
A.pipefish = ({ color = '#D7B46A', style = {} }) => (
{style.ornate && <>
>}
{style.banded && `M${x} 76 L ${x} 90`)} w={2} op={0.55}/>}
{style.longsnout && }
{EYE(220, 80, 1.4)}
);
A.seahorse = ({ color = '#D7B46A', style = {} }) => (
{style.snoutLong && }
{!style.snoutLong && }
{style.thorny && [62,55,48,42,52,68,78,84,90].map((y,i) =>
)}
{style.pygmy && }
{EYE(160, 40, 1.6)}
);
// ── reef fish ───────────────────────────────────────────────────────
A.clownfish = ({ color = '#FFA86B', style = {} }) => (
{style.anemone &&
{[20,40,60,200,220].map((x,i) =>
)}
}
{EYE(158, 64, 2.6)}
);
A.angelfish = ({ color = '#31D7FF', style = {} }) => (
{style.bands && }
{style.queen && }
{EYE(170, 60, 2.4)}
);
A.butterflyfish = ({ color = '#FFD86B' }) => (
{EYE(202, 70, 1.8)}
);
A.parrotfish = ({ color = '#31D7FF', style = {} }) => (
{style.stoplight && <>
>}
{EYE(206, 70, 2.4)}
);
A.wrasse = ({ color = '#82ECFF', style = {} }) => (
{style.hump && }
{style.rainbow && }
{EYE(64, 70, 2.4)}
);
A.surgeonfish = ({ color = '#31D7FF', style = {} }) => (
{style.sohal && }
{EYE(196, 64, 2.2)}
);
A.tang = ({ color = '#31D7FF' }) => (
{EYE(196, 64, 2.2)}
);
A.damselfish = ({ color = '#31D7FF', style = {} }) => (
{style.spots && }
{EYE(184, 68, 2.2)}
);
A.grouper = ({ color = '#5A6E8C', style = {} }) => (
{style.dusky && }
{EYE(202, 68, 2.4)}
);
A.trevally = ({ color = '#9DB6CC' }) => (
{EYE(202, 64, 2.4)}
);
A.tarpon = ({ color = '#B4C8D8' }) => (
{[60,80,100,120,140,160,180].map((x,i)=>)}
{EYE(206, 68, 2.4)}
);
A.tuna = ({ color = '#5C7DB8' }) => (
{[150,170,190].map((x,i)=>)}
{[150,170,190].map((x,i)=>)}
{EYE(210, 70, 2.2)}
);
A.barracuda = ({ color = '#9DB8CC', style = {} }) => (
`M${x} 60 L ${x+3} 86`)} w={1} op={0.5}/>
{style.school && [-1,1,2].map(i => (
))}
{EYE(44, 68, 2.4)}
);
// ── pelagic predators / cetaceans ───────────────────────────────────
A.dolphin = ({ color = '#9DC8E0' }) => (
{EYE(212, 72, 1.8)}
);
A.whale = ({ color = '#8E9BB2' }) => (
{EYE(220, 70, 1.8)}
);
A.seal = ({ color = '#7A8AA0' }) => (
{EYE(54, 66, 2)}
);
A.dugong = ({ color = '#A89070' }) => (
{EYE(54, 60, 1.8)}
);
// ── cephalopods ─────────────────────────────────────────────────────
A.octopus = ({ color = '#FF7A6B', style = {} }) => (
{[
"M76 80 Q 50 96 36 124 Q 50 116 64 104 Q 70 96 78 90",
"M92 84 Q 76 110 70 132 Q 86 116 94 102 Q 96 96 98 90",
"M108 84 Q 102 112 102 134 Q 112 118 114 104 Q 114 96 114 90",
"M124 84 Q 130 112 128 134 Q 138 118 138 104 Q 138 96 134 90",
"M140 84 Q 154 110 158 132 Q 144 116 138 102 Q 134 96 132 90",
"M156 82 Q 178 106 192 132 Q 178 116 164 102 Q 156 94 152 88",
"M168 76 Q 192 86 210 100 Q 190 94 172 88",
"M70 70 Q 46 76 26 88 Q 50 80 70 80",
].map((d, i) => )}
);
A.cuttlefish = ({ color = '#A89070' }) => (
{[50,70,90,110,130,150].map((x,i)=>)}
);
A.squid = ({ color = '#FF7A6B' }) => (
{[0,1,2,3,4,5].map(i =>
)}
);
// ── macro / inverts ─────────────────────────────────────────────────
A.nudibranch = ({ color = '#FF7A6B', style = {} }) => (
{style.frilly && }
{style.gills && [60,80,100,120,140,160,180].map((x,i)=>)}
{style.gills && [55,75,95,115,135,155,175,195].map((x,i)=>)}
{style.cerata && [40,60,80,100,120,140,160,180,200].map((x,i)=>
)}
{style.mantleSpots && }
);
A.frogfish = ({ color = '#FF7A6B', style = {} }) => (
{style.hairy && [50,70,90,110,130,150,170,190].map((x,i)=>
)}
{style.sargassum &&
}
{EYE(168, 70, 2.4)}
);
A.lionfish = ({ color = '#FF7A6B' }) => (
`M${x} 56 Q ${x+2} 72 ${x-2} 88`)} w={4} op={0.65} stroke="#EAF5FF"/>
{[88,108,128,148].map((x,i)=>)}
{[78,98,118,138,158].map((x,i)=>)}
{EYE(184, 70, 2.2)}
);
A.crocodilefish = ({ color = '#A89070' }) => (
);
A.giantClam = ({ color = '#31D7FF' }) => (
);
A.shrimp = ({ color = '#FF7A6B' }) => (
);
A.crab = ({ color = '#FF7A6B', style = {} }) => (
{style.arrow && }
);
A.lobster = ({ color = '#A8462B', style = {} }) => (
`M${x} 70 L ${x} 96`)} w={1} op={0.55}/>
{style.spiny && [50,70,90,110,130,150,170,190].map((x,i)=>
)}
);
A.seaStar = ({ color = '#FF7A6B' }) => (
);
A.jellyfish = ({ color = '#82ECFF' }) => (
);
A.trunkfish = ({ color = '#FFD86B' }) => (
{EYE(192, 70, 2)}
);
A.tigerShark = ({ color = '#82ECFF' }) => (
`M${x} 60 Q ${x+2} 76 ${x-2} 90`)} w={1.4} op={0.55}/>
{EYE(200, 70, 2.4)}
);
// ── non-creature archetypes (wrecks, aircraft, coral, sponges) ──────
// These slot into the same sprite system so they pipe through the Dex,
// sightings picker, first-spotter attribution and Memories timeline.
A.coral = ({ color = '#FF9DB0', style = {} }) => {
// shape: 'soft' | 'brain' | 'fan' | 'staghorn' | 'black' | 'table'
const shape = style.shape || 'soft';
if (shape === 'brain') {
return (
{/* Brain coral — dome with grooved surface */}
);
}
if (shape === 'fan') {
return (
{/* Sea fan / gorgonian — vertical fan with ribbed branching */}
{/* Cross ribs */}
{/* Base */}
);
}
if (shape === 'staghorn') {
return (
{/* Staghorn coral — branched antler-like clusters */}
{/* Branch tips */}
{[[94,50],[78,60],[86,78],[168,50],[150,50],[142,64],[144,92],[160,84]].map(([x,y],i) => (
))}
);
}
if (shape === 'black') {
return (
{/* Black coral — dark feathery fern */}
);
}
if (shape === 'table') {
return (
{/* Table coral — flat plate on a stem */}
);
}
// 'soft' — Mikomoto-style pink soft coral cluster (default)
return (
{/* Bulbous tips */}
{[
{ cx: 122, cy: 60, r: 16 },
{ cx: 88, cy: 72, r: 14 },
{ cx: 156, cy: 70, r: 14 },
{ cx: 100, cy: 44, r: 11 },
{ cx: 144, cy: 42, r: 11 },
{ cx: 122, cy: 36, r: 9 },
].map((b, i) => (
))}
{/* Connecting stems */}
);
};
A.sponge = ({ color = '#FFB37A', style = {} }) => {
// shape: 'barrel' | 'tube' | 'rope' | 'encrusting'
const shape = style.shape || 'barrel';
if (shape === 'tube') {
// Cluster of vertical tubes with hollow openings
return (
{[
{ x: 78, w: 18, h: 70 },
{ x: 104, w: 22, h: 86 },
{ x: 134, w: 18, h: 64 },
{ x: 158, w: 16, h: 76 },
].map((t, i) => (
))}
);
}
if (shape === 'rope') {
// Looping rope sponge
return (
);
}
if (shape === 'encrusting') {
// Reef-coating colored patches
return (
);
}
// 'barrel' — single drum-shaped sponge (default)
return (
{/* Surface texture lines */}
);
};
A.wreck = ({ color = '#6B7A88', style = {} }) => {
// shape: 'small-hull' | 'large-hull' — different proportions
const isLarge = style.shape === 'large-hull';
return (
{/* Sloped hull, listing slightly */}
{/* Deck */}
{/* Cabin / superstructure */}
{isLarge && }
{/* Mast / funnel */}
{/* Portholes */}
{[[60,98],[80,96],[100,94],[140,90],[160,88],[180,86]].map(([x,y],i) => (
))}
{/* Coral growth speckle */}
);
};
A.aircraft = ({ color = '#8FA2B5', style = {} }) => {
// Top-down silhouette of a propeller fighter / small aircraft.
return (
{/* Fuselage */}
{/* Wings */}
{/* Tail wings */}
{/* Vertical stabilizer */}
{/* Cockpit canopy */}
{/* Propeller hub + blade hint */}
{/* Rust / coral specks for the sunken vibe */}
);
};
// ── extras / fallback ───────────────────────────────────────────────
A.mystery = ({ color = '#7A93AB' }) => (
?
);
// ── FAMILY TYPE STYLE DEFAULTS ──────────────────────────────────────
const FAMILY_TYPE_STYLE = {
shark: { base: '#9DB6CC' },
pelagic: { base: '#5C7DB8' },
ray: { base: '#8B7BFF' },
turtle: { base: '#63FFD2' },
reefFish: { base: '#31D7FF' },
macro: { base: '#FF7A6B' },
cephalopod: { base: '#C97BFF' },
crustacean: { base: '#D7B46A' },
mammal: { base: '#9DC8E0' },
invertebrate:{ base: '#FF7A6B' },
// Non-creature sightings — same data pipeline, different vibe.
coral: { base: '#FF9DB0' }, // soft pink default; variants override
sponge: { base: '#FFB37A' }, // warm orange default
wreck: { base: '#6B7A88' }, // weathered steel grey
aircraft: { base: '#8FA2B5' }, // aluminium grey
};
// ── VARIANT SKINS ───────────────────────────────────────────────────
// { archetype, color?, style? } — style is forwarded to the archetype
const CREATURE_VARIANTS = {
// hammerheads
'scalloped-hammerhead': { archetype: 'hammerhead', color: '#D7B46A', style: { scallop: true, headW: 1.0 }},
'great-hammerhead': { archetype: 'hammerhead', color: '#B7C8D8', style: { scallop: false, headW: 1.18 }},
'smooth-hammerhead': { archetype: 'hammerhead', color: '#9FB3C8', style: { scallop: false, headW: 0.95 }},
'hammerhead-school': { archetype: 'hammerhead', color: '#D7B46A', style: { scallop: true, school: true }},
// reef sharks
'banded-houndshark': { archetype: 'reefShark', color: '#7A8694', style: { bands: '#2C3946', tailTip: '#3A4754' }},
'blacktip-reef-shark': { archetype: 'reefShark', color: '#9DB6CC', style: { finTip: '#021321' }},
'whitetip-reef-shark': { archetype: 'reefShark', color: '#8AA0B8', style: { finTip: '#F6FCFF' }},
'grey-reef-shark': { archetype: 'reefShark', color: '#8A99AA', style: { tailTip: '#021321' }},
'caribbean-reef-shark': { archetype: 'reefShark', color: '#8DB0C8', style: {}},
'oceanic-whitetip': { archetype: 'reefShark', color: '#7A92AB', style: { finTip: '#F6FCFF', belly: '#F6FCFF' }},
'reef-shark': { archetype: 'reefShark', color: '#82ECFF' },
'tiger-shark': { archetype: 'tigerShark', color: '#A8C2D8' },
'zebra-shark': { archetype: 'zebraShark', color: '#D7B46A' },
'thresher-shark': { archetype: 'thresherShark', color: '#7A92AB' },
'japanese-bullhead-shark': { archetype: 'bullheadShark', color: '#B0A07A' },
'nurse-shark': { archetype: 'nurseShark', color: '#A8946C' },
'tawny-nurse-shark': { archetype: 'nurseShark', color: '#B7A074' },
// whale shark
'whale-shark': { archetype: 'whaleShark', color: '#5A7DB8' },
// rays
'reef-manta': { archetype: 'mantaRay', color: '#7B8BCC' },
'giant-manta': { archetype: 'mantaRay', color: '#5A6BBF', style: { giant: true, underspots: true }},
'spotted-eagle-ray': { archetype: 'eagleRay', color: '#5C7DB8' },
'eagle-ray': { archetype: 'eagleRay', color: '#5C7DB8' },
'blue-spotted-ray': { archetype: 'stingray', color: '#9F8B66', style: { bluespots: true }},
'southern-stingray': { archetype: 'stingray', color: '#B59E78', style: {}},
'mediterranean-devil-ray':{ archetype: 'devilRay', color: '#5A7DB8' },
// turtles
'green-turtle': { archetype: 'turtle', color: '#63FFD2', style: {}},
'hawksbill-turtle': { archetype: 'turtle', color: '#D7B46A', style: { beak: true, patternedShell: true }},
'loggerhead-turtle': { archetype: 'turtle', color: '#A88B55', style: { bigHead: true }},
'leatherback-turtle': { archetype: 'turtle', color: '#3E4A66', style: { leatherback: true }},
// eels
'giant-moray': { archetype: 'moray', color: '#B7A074', style: { spots: true }},
'dragon-moray': { archetype: 'moray', color: '#FF7A6B', style: { dragon: true, spots: true }},
'zebra-moray': { archetype: 'moray', color: '#1B3A56', style: { bands: true }},
'green-moray': { archetype: 'moray', color: '#63FFD2', style: {}},
'mediterranean-moray': { archetype: 'moray', color: '#5C7DB8', style: { spots: true }},
'ribbon-eel': { archetype: 'ribbonEel', color: '#31D7FF' },
'garden-eel': { archetype: 'gardenEel', color: '#9BB6CC' },
// pipefish & seahorse
'ornate-ghost-pipefish':{ archetype: 'pipefish', color: '#FFD86B', style: { ornate: true }},
'robust-ghost-pipefish':{ archetype: 'pipefish', color: '#A88B55', style: { ornate: true }},
'banded-pipefish': { archetype: 'pipefish', color: '#D7B46A', style: { banded: true }},
'mediterranean-pipefish':{ archetype: 'pipefish', color: '#7A9476', style: {}},
'longsnout-pipefish': { archetype: 'pipefish', color: '#9DB6CC', style: { longsnout: true }},
'pygmy-seahorse': { archetype: 'seahorse', color: '#FF7A6B', style: { pygmy: true }},
'thorny-seahorse': { archetype: 'seahorse', color: '#D7B46A', style: { thorny: true }},
'longsnout-seahorse': { archetype: 'seahorse', color: '#D7B46A', style: { snoutLong: true }},
'short-snouted-seahorse':{ archetype: 'seahorse', color: '#B59E78', style: {}},
// reef fish
'common-clownfish': { archetype: 'clownfish', color: '#FFA86B', style: { anemone: true }},
'maldives-anemonefish': { archetype: 'clownfish', color: '#FF8866', style: { anemone: true, anemoneColor: '#FFD86B' }},
'red-sea-anemonefish': { archetype: 'clownfish', color: '#E26B4B', style: { anemone: true, anemoneColor: '#A8462B' }},
'queen-angelfish': { archetype: 'angelfish', color: '#31D7FF', style: { queen: true, accent: '#FFD86B' }},
'french-angelfish': { archetype: 'angelfish', color: '#2A3A50', style: { accent: '#FFD86B' }},
'butterflyfish': { archetype: 'butterflyfish', color: '#FFD86B' },
'stoplight-parrotfish': { archetype: 'parrotfish', color: '#5BCC7A', style: { stoplight: true, stripe: '#FF7A6B', stripe2: '#FFD86B', beak: '#FFD86B' }},
'parrotfish': { archetype: 'parrotfish', color: '#31D7FF', style: { beak: '#FFD86B' }},
'napoleon-wrasse': { archetype: 'wrasse', color: '#5BCC7A', style: { hump: true }},
'rainbow-wrasse': { archetype: 'wrasse', color: '#FFD86B', style: { rainbow: true, stripe: '#31D7FF' }},
'ornate-wrasse': { archetype: 'wrasse', color: '#5BCC7A', style: { rainbow: true, stripe: '#FF7A6B' }},
'wrasse': { archetype: 'wrasse', color: '#82ECFF', style: { hump: true }},
'powder-blue-surgeonfish': { archetype: 'surgeonfish', color: '#5C9FE0', style: { tailColor: '#FFD86B' }},
'sohal-surgeonfish': { archetype: 'surgeonfish', color: '#7A92AB', style: { sohal: true, tailColor: '#31D7FF', scalpel: '#FF7A6B' }},
'caribbean-blue-tang': { archetype: 'tang', color: '#31D7FF' },
'mediterranean-damselfish':{ archetype: 'damselfish', color: '#3E5A8C' },
'dusky-grouper': { archetype: 'grouper', color: '#5A4A3A', style: { dusky: true }},
'giant-trevally': { archetype: 'trevally', color: '#9DB6CC' },
'tarpon': { archetype: 'tarpon', color: '#C0D2E0' },
'bluefin-tuna': { archetype: 'tuna', color: '#3E5A8C' },
'atlantic-bluefin-tuna':{ archetype: 'tuna', color: '#3E5A8C' },
'great-barracuda': { archetype: 'barracuda', color: '#9DB8CC' },
'yellowmouth-barracuda':{ archetype: 'barracuda', color: '#A8C2D8' },
'barracuda-school': { archetype: 'barracuda', color: '#9DB8CC', style: { school: true }},
'barracuda': { archetype: 'barracuda', color: '#9DB8CC' },
'lionfish': { archetype: 'lionfish', color: '#A8462B' },
'crocodilefish': { archetype: 'crocodilefish', color: '#A89070' },
// cephalopods
'common-octopus': { archetype: 'octopus', color: '#A8462B' },
'caribbean-reef-octopus':{ archetype: 'octopus', color: '#7BCFAA' },
'common-cuttlefish': { archetype: 'cuttlefish', color: '#A89070' },
'squid': { archetype: 'squid', color: '#FF7A6B' },
// macro
'spanish-dancer': { archetype: 'nudibranch', color: '#FF3A4B', style: { frilly: true, gills: true }},
'chromodoris': { archetype: 'nudibranch', color: '#C97BFF', style: { frilly: true, gills: true, mantleSpots: true }},
'hypselodoris': { archetype: 'nudibranch', color: '#5C9FE0', style: { gills: true, mantleSpots: true }},
'flabellina': { archetype: 'nudibranch', color: '#FF7A6B', style: { cerata: true, cerataColor: '#31D7FF' }},
'nudibranch': { archetype: 'nudibranch', color: '#FF7A6B', style: { frilly: true, gills: true }},
'painted-frogfish': { archetype: 'frogfish', color: '#FF7A6B', style: { lure: '#FFD86B' }},
'giant-frogfish': { archetype: 'frogfish', color: '#A89070', style: { lure: '#FFD86B' }},
'hairy-frogfish': { archetype: 'frogfish', color: '#D7B46A', style: { hairy: true, lure: '#FFD86B' }},
'sargassum-fish': { archetype: 'frogfish', color: '#5BCC7A', style: { sargassum: true }},
'frogfish': { archetype: 'frogfish', color: '#FF7A6B', style: { lure: '#FFD86B' }},
// inverts
'arrow-crab': { archetype: 'crab', color: '#FFD86B', style: { arrow: true }},
'spiny-lobster': { archetype: 'lobster', color: '#A8462B', style: { spiny: true }},
'shrimp': { archetype: 'shrimp', color: '#FF7A6B' },
'sea-star': { archetype: 'seaStar', color: '#FF7A6B' },
'jellyfish': { archetype: 'jellyfish', color: '#82ECFF' },
'giant-clam': { archetype: 'giantClam', color: '#31D7FF' },
'smooth-trunkfish': { archetype: 'trunkfish', color: '#FFD86B' },
// mammals
'dolphin': { archetype: 'dolphin', color: '#9DC8E0' },
'whale': { archetype: 'whale', color: '#8E9BB2' },
'monk-seal': { archetype: 'seal', color: '#7A8AA0' },
'dugong': { archetype: 'dugong', color: '#A89070' },
// corals
'pink-soft': { archetype: 'coral', color: '#FFA1B8', style: { shape: 'soft' }},
'brain': { archetype: 'coral', color: '#D4B98F', style: { shape: 'brain' }},
'fan': { archetype: 'coral', color: '#FF7A6B', style: { shape: 'fan' }},
'staghorn': { archetype: 'coral', color: '#F0CE9C', style: { shape: 'staghorn' }},
'black': { archetype: 'coral', color: '#2E3D4D', style: { shape: 'black' }},
'table': { archetype: 'coral', color: '#D2A37A', style: { shape: 'table' }},
// sponges
'barrel': { archetype: 'sponge', color: '#C77860', style: { shape: 'barrel' }},
'tube': { archetype: 'sponge', color: '#9D6BAF', style: { shape: 'tube' }},
'rope': { archetype: 'sponge', color: '#FFB37A', style: { shape: 'rope' }},
'encrusting': { archetype: 'sponge', color: '#FF6B8E', style: { shape: 'encrusting' }},
// wrecks
'small-hull': { archetype: 'wreck', color: '#6B7A88', style: { shape: 'small-hull' }},
'large-hull': { archetype: 'wreck', color: '#5C6B7A', style: { shape: 'large-hull' }},
// aircraft
'fighter': { archetype: 'aircraft', color: '#8FA2B5', style: { shape: 'fighter' }},
// fallback
'mystery': { archetype: 'mystery', color: '#7A93AB' },
};
// ── REGISTRY ────────────────────────────────────────────────────────
// creatureId → { name, sci, archetype-via-variant, family, rarity, regions, habitats, sizeClass, flavorText, achievementName? }
const R = (name, sci, family, rarity, regions, flavor, habitats=['reef'], sizeClass='M', ach=null) =>
({ commonName: name, scientificName: sci, familyType: family, rarity, regions, habitats, sizeClass, flavorText: flavor, achievementName: ach });
const CREATURE_REGISTRY = {
// ── Japan ─────────────────────────────────────────────────────────
'scalloped-hammerhead': R('Scalloped Hammerhead','Sphyrna lewini','shark','legendary',['japan','red-sea','indian-ocean'],'Schools by day, hunts at night. Mikomoto signature.',['pelagic','seamount'],'L','Mikomoto Mover'),
'hammerhead-school': R('Hammerhead School','Sphyrna lewini','shark','mythic',['japan','red-sea'],'Twenty pairs of eyes. One slow-motion ballet.',['pelagic'],'XL','Wall of Eyes'),
'great-hammerhead': R('Great Hammerhead','Sphyrna mokarran','shark','legendary',['red-sea','caribbean'],'Bigger, blockier, lonelier.',['pelagic'],'XL'),
'smooth-hammerhead': R('Smooth Hammerhead','Sphyrna zygaena','shark','epic',['japan'],'No notches. Just a curve and conviction.',['pelagic'],'L'),
'japanese-bullhead-shark': R('Japanese Bullhead Shark','Heterodontus japonicus','shark','rare',['japan'],'Reef bottoms. Looks like a beginner sketch of a shark.',['reef'],'S'),
'thresher-shark': R('Thresher Shark','Alopias pelagicus','shark','epic',['japan','indian-ocean'],'Slaps sardines stupid with its tail.',['pelagic'],'L'),
'whale-shark': R('Whale Shark','Rhincodon typus','shark','mythic',['japan','indian-ocean','red-sea','caribbean'],'Polka dots the size of dinner plates.',['pelagic'],'XL','Polka Dots'),
'reef-manta': R('Reef Manta','Mobula alfredi','ray','epic',['japan','indian-ocean','red-sea'],'Mantas don\'t flap. They glide.',['reef','pelagic'],'L'),
'green-turtle': R('Green Turtle','Chelonia mydas','turtle','uncommon',['japan','indian-ocean','red-sea','caribbean'],'Looks unbothered. Probably is.',['reef'],'L'),
'hawksbill-turtle': R('Hawksbill Turtle','Eretmochelys imbricata','turtle','rare',['japan','indian-ocean','red-sea','caribbean'],'Hooked beak. Mosaic shell.',['reef'],'M'),
'dragon-moray': R('Dragon Moray','Enchelycore pardalis','reefFish','legendary',['japan'],'Horns, fangs, and a permanent scowl.',['reef'],'M','Here Be Dragons'),
'zebra-moray': R('Zebra Moray','Gymnomuraena zebra','reefFish','rare',['japan','indian-ocean'],'Black-and-white pyjamas, cracks crabs for breakfast.',['reef'],'M'),
'giant-moray': R('Giant Moray','Gymnothorax javanicus','reefFish','rare',['japan','indian-ocean','red-sea'],'The classic moray. Mouth open, asking nothing.',['reef'],'L'),
'ribbon-eel': R('Ribbon Eel','Rhinomuraena quaesita','reefFish','epic',['japan','indian-ocean'],'Switches sex and colour through life. Cool flex.',['reef'],'S'),
'garden-eel': R('Garden Eel','Heteroconger hassi','reefFish','uncommon',['japan','indian-ocean','red-sea'],'Sand lawns of nervous noodles.',['sand'],'S'),
'ornate-ghost-pipefish':R('Ornate Ghost Pipefish','Solenostomus paradoxus','macro','epic',['japan','indian-ocean','red-sea'],'A leaf with a face. Macro royalty.',['reef'],'XS','Macro Eye'),
'robust-ghost-pipefish':R('Robust Ghost Pipefish','Solenostomus cyanopterus','macro','rare',['japan','indian-ocean'],'Same costume, sturdier build.',['reef'],'XS'),
'pygmy-seahorse': R('Pygmy Seahorse','Hippocampus bargibanti','macro','epic',['japan','indian-ocean'],'2 cm of pure smug.',['reef'],'XS'),
'painted-frogfish': R('Painted Frogfish','Antennarius pictus','macro','rare',['japan','indian-ocean'],'Sits. Wiggles lure. Eats things its own size.',['reef'],'S'),
'nudibranch': R('Nudibranch','Various','macro','uncommon',['japan','indian-ocean','red-sea','mediterranean'],'A sea slug that dresses better than you.',['reef'],'XS'),
'common-octopus': R('Common Octopus','Octopus vulgaris','cephalopod','rare',['japan','mediterranean','caribbean'],'Three hearts, nine brains, zero shame.',['reef'],'M'),
'giant-trevally': R('Giant Trevally','Caranx ignobilis','pelagic','rare',['japan','indian-ocean','red-sea'],'A torpedo with attitude.',['pelagic'],'L'),
'barracuda-school': R('Barracuda School','Sphyraena qenie','pelagic','epic',['japan','indian-ocean','red-sea'],'A wall of teeth doing a slow circle.',['pelagic'],'L','Tornado'),
'bluefin-tuna': R('Bluefin Tuna','Thunnus orientalis','pelagic','legendary',['japan','mediterranean'],'600 kg of muscle that never stops moving.',['pelagic'],'XL'),
// ── Indian Ocean ──────────────────────────────────────────────────
'grey-reef-shark': R('Grey Reef Shark','Carcharhinus amblyrhynchos','shark','rare',['indian-ocean','red-sea'],'The reef\'s neighbourhood patrol.',['reef'],'M'),
'blacktip-reef-shark': R('Blacktip Reef Shark','Carcharhinus melanopterus','shark','uncommon',['indian-ocean','red-sea','caribbean'],'Shy kid with painted fingertips.',['reef'],'M'),
'whitetip-reef-shark': R('Whitetip Reef Shark','Triaenodon obesus','shark','uncommon',['indian-ocean','red-sea','caribbean'],'Naps in caves all afternoon.',['reef'],'M'),
'zebra-shark': R('Zebra Shark','Stegostoma fasciatum','shark','rare',['indian-ocean'],'Stripy as a pup, spotted as an adult.',['reef'],'L'),
'tawny-nurse-shark': R('Tawny Nurse Shark','Nebrius ferrugineus','shark','uncommon',['indian-ocean','red-sea'],'Big, brown, mostly snoozing.',['reef'],'L'),
'napoleon-wrasse': R('Napoleon Wrasse','Cheilinus undulatus','reefFish','epic',['indian-ocean','red-sea'],'A reef sage with a giant forehead.',['reef'],'L','Forehead Friend'),
'maldives-anemonefish': R('Maldives Anemonefish','Amphiprion nigripes','reefFish','uncommon',['indian-ocean'],'Same vibes as Nemo. Different passport.',['reef'],'XS'),
'powder-blue-surgeonfish': R('Powder Blue Surgeonfish','Acanthurus leucosternon','reefFish','rare',['indian-ocean'],'A pop of impossible blue.',['reef'],'S'),
'spanish-dancer': R('Spanish Dancer','Hexabranchus sanguineus','macro','epic',['indian-ocean','red-sea'],'When it swims, the reef stops talking.',['reef'],'M','Encore'),
'dugong': R('Dugong','Dugong dugon','mammal','legendary',['indian-ocean','red-sea'],'A sea cow with a sad-looking face. Heart of gold.',['seagrass'],'XL','Sea Cow'),
'giant-manta': R('Giant Manta','Mobula birostris','ray','mythic',['indian-ocean','japan'],'Up to 7 metres wing-to-wing. A flying carpet.',['pelagic'],'XL'),
// ── Caribbean ─────────────────────────────────────────────────────
'caribbean-reef-shark': R('Caribbean Reef Shark','Carcharhinus perezi','shark','rare',['caribbean'],'The signature shark of the Bahamas wall.',['reef'],'M'),
'nurse-shark': R('Nurse Shark','Ginglymostoma cirratum','shark','uncommon',['caribbean'],'Sandy bottom, sandy nap.',['reef'],'M'),
'tiger-shark': R('Tiger Shark','Galeocerdo cuvier','shark','legendary',['caribbean','indian-ocean'],'Eats everything. Sometimes a licence plate.',['pelagic'],'XL','Apex'),
'spotted-eagle-ray': R('Spotted Eagle Ray','Aetobatus narinari','ray','rare',['caribbean','red-sea'],'White polka dots on midnight wings.',['pelagic'],'L'),
'southern-stingray': R('Southern Stingray','Hypanus americanus','ray','uncommon',['caribbean'],'Buries itself in sand and waits.',['sand'],'M'),
'loggerhead-turtle': R('Loggerhead Turtle','Caretta caretta','turtle','rare',['caribbean','mediterranean'],'A big head for cracking conch shells.',['reef'],'L'),
'queen-angelfish': R('Queen Angelfish','Holacanthus ciliaris','reefFish','rare',['caribbean'],'Wears a crown of yellow.',['reef'],'S'),
'french-angelfish': R('French Angelfish','Pomacanthus paru','reefFish','uncommon',['caribbean'],'Dark with gold-scale stippling.',['reef'],'S'),
'stoplight-parrotfish': R('Stoplight Parrotfish','Sparisoma viride','reefFish','uncommon',['caribbean'],'Crunches coral. Poops sand.',['reef'],'M'),
'caribbean-blue-tang': R('Caribbean Blue Tang','Acanthurus coeruleus','reefFish','uncommon',['caribbean'],'Forgetful but vibrant.',['reef'],'S'),
'tarpon': R('Tarpon','Megalops atlanticus','pelagic','rare',['caribbean'],'Silver king of the mangroves.',['pelagic'],'L'),
'great-barracuda': R('Great Barracuda','Sphyraena barracuda','pelagic','rare',['caribbean','red-sea'],'Watches everything. Forgives nothing.',['reef','pelagic'],'L'),
'green-moray': R('Green Moray','Gymnothorax funebris','reefFish','rare',['caribbean'],'A green ghost in coral.',['reef'],'L'),
'caribbean-reef-octopus': R('Caribbean Reef Octopus','Octopus briareus','cephalopod','rare',['caribbean'],'Changes colour mid-thought.',['reef'],'M'),
'longsnout-seahorse': R('Longsnout Seahorse','Hippocampus reidi','macro','rare',['caribbean'],'A long-nosed romantic.',['reef'],'XS'),
'banded-pipefish': R('Banded Pipefish','Doryrhamphus dactyliophorus','macro','uncommon',['caribbean','indian-ocean'],'A pencil sketch of a fish.',['reef'],'XS'),
'arrow-crab': R('Arrow Crab','Stenorhynchus seticornis','crustacean','uncommon',['caribbean'],'Pointy. Spidery. Slightly judgmental.',['reef'],'XS'),
'smooth-trunkfish': R('Smooth Trunkfish','Lactophrys triqueter','reefFish','uncommon',['caribbean'],'Boxy little tank of yellow dots.',['reef'],'S'),
// ── Red Sea ───────────────────────────────────────────────────────
'oceanic-whitetip': R('Oceanic Whitetip','Carcharhinus longimanus','shark','epic',['red-sea'],'The blue-water shark of legend.',['pelagic'],'L','Brothers'),
'red-sea-anemonefish': R('Red Sea Anemonefish','Amphiprion bicinctus','reefFish','uncommon',['red-sea'],'Endemic. Two stripes. One attitude.',['reef'],'XS'),
'sohal-surgeonfish': R('Sohal Surgeonfish','Acanthurus sohal','reefFish','rare',['red-sea'],'Owns the upper reef. Will tell you so.',['reef'],'S'),
'lionfish': R('Lionfish','Pterois volitans','reefFish','rare',['red-sea','caribbean','indian-ocean'],'Beautiful. Do not touch.',['reef'],'S'),
'crocodilefish': R('Crocodilefish','Papilloculiceps longiceps','reefFish','rare',['red-sea'],'Long flat face on the sand. Disappears.',['sand'],'M'),
'blue-spotted-ray': R('Blue-spotted Ribbontail Ray','Taeniura lymma','ray','rare',['red-sea','indian-ocean'],'Pocket-sized neon.',['reef'],'S'),
'giant-clam': R('Giant Clam','Tridacna gigas','invertebrate','uncommon',['red-sea','indian-ocean'],'Half a metre of psychedelic mantle.',['reef'],'M'),
// ── Mediterranean ─────────────────────────────────────────────────
'dusky-grouper': R('Dusky Grouper','Epinephelus marginatus','reefFish','epic',['mediterranean'],'The grandad of every Med wreck.',['wreck','reef'],'L'),
'mediterranean-moray': R('Mediterranean Moray','Muraena helena','reefFish','rare',['mediterranean'],'Black-and-white speckled, ageless.',['reef'],'M'),
'common-cuttlefish': R('Common Cuttlefish','Sepia officinalis','cephalopod','rare',['mediterranean'],'Disco hypnotist with W-shaped pupils.',['reef'],'S'),
'yellowmouth-barracuda':R('Yellowmouth Barracuda','Sphyraena viridensis','pelagic','rare',['mediterranean'],'The Med\'s school predator.',['pelagic'],'L'),
'rainbow-wrasse': R('Rainbow Wrasse','Coris julis','reefFish','common',['mediterranean'],'Tiny flicker of pink and blue.',['reef'],'XS'),
'ornate-wrasse': R('Ornate Wrasse','Thalassoma pavo','reefFish','uncommon',['mediterranean'],'Bright stripes on the rocky reef.',['reef'],'XS'),
'mediterranean-damselfish': R('Mediterranean Damselfish','Chromis chromis','reefFish','common',['mediterranean'],'Clouds of little blue-black sparks.',['reef'],'XS'),
'mediterranean-devil-ray':R('Mediterranean Devil Ray','Mobula mobular','ray','epic',['mediterranean'],'Manta\'s smaller, rarer cousin.',['pelagic'],'L'),
'short-snouted-seahorse':R('Short-snouted Seahorse','Hippocampus hippocampus','macro','rare',['mediterranean'],'Hides in seagrass. Hard to spot.',['seagrass'],'XS'),
'mediterranean-pipefish':R('Mediterranean Pipefish','Syngnathus typhle','macro','uncommon',['mediterranean'],'A blade of grass with a face.',['seagrass'],'XS'),
'atlantic-bluefin-tuna':R('Atlantic Bluefin Tuna','Thunnus thynnus','pelagic','epic',['mediterranean'],'Migrates through the Strait of Messina.',['pelagic'],'XL'),
'spiny-lobster': R('Spiny Lobster','Palinurus elephas','crustacean','rare',['mediterranean','caribbean'],'No claws. All antennae.',['reef'],'S'),
'monk-seal': R('Mediterranean Monk Seal','Monachus monachus','mammal','mythic',['mediterranean'],'Fewer than a thousand left. Sees you first.',['cave'],'L','Last of the Last'),
// ── extras / utility ──────────────────────────────────────────────
'mystery': R('Unidentified','—','reefFish','common',[],'Logged but not classified.',['reef'],'M'),
};
// ── RARITY / FRAME ──────────────────────────────────────────────────
const RARITY_COLOR = {
common: '#B7CCDE', uncommon: '#63FFD2', rare: '#31D7FF',
epic: '#8B7BFF', legendary: '#D7B46A', mythic: '#82ECFF',
};
// Back-compat: legacy spriteKey names → creatureId
const LEGACY_SPRITE_TO_CREATURE = {
'hammerhead': 'scalloped-hammerhead',
'whale-shark': 'whale-shark',
'reef-shark': 'caribbean-reef-shark',
'tiger-shark': 'tiger-shark',
'manta': 'reef-manta',
'eagle-ray': 'spotted-eagle-ray',
'turtle': 'green-turtle',
'moray': 'giant-moray',
'nudibranch': 'spanish-dancer',
'clownfish': 'common-clownfish',
'octopus': 'common-octopus',
'barracuda': 'great-barracuda',
'wrasse': 'napoleon-wrasse',
'mystery': 'mystery',
};
function resolveCreature(creatureId, spriteKey, id) {
const key = creatureId || LEGACY_SPRITE_TO_CREATURE[spriteKey || id] || (spriteKey || id) || 'mystery';
const variant = CREATURE_VARIANTS[key] || CREATURE_VARIANTS[LEGACY_SPRITE_TO_CREATURE[key]] || CREATURE_VARIANTS.mystery;
const meta = CREATURE_REGISTRY[key] || CREATURE_REGISTRY.mystery;
const Archetype = A[variant.archetype] || A.mystery;
return { key, variant, meta, Archetype };
}
// ── frame component ─────────────────────────────────────────────────
function CreatureSpriteFrame({
creatureId, spriteKey, id,
rarity, locked = false, size = 140,
color, className, glow = true, animate = true, bg = false, frame = false,
}) {
const { variant, meta, Archetype } = resolveCreature(creatureId, spriteKey, id);
const effRarity = rarity || meta.rarity || 'common';
const frameC = RARITY_COLOR[effRarity] || '#82ECFF';
const drawColor = locked ? '#1B3A56' : (color || variant.color || FAMILY_TYPE_STYLE[meta.familyType]?.base || frameC);
const filterCss = locked
? 'none'
: (glow ? `drop-shadow(0 0 8px ${frameC}66) drop-shadow(0 4px 14px ${frameC}44)` : 'none');
const h = size * 140 / 240;
// Only clip overflow when a visible frame/bg defines a rectangle. Without
// one, the drop-shadow glow should be free to extend past the SVG bounds
// — clipping it produces visible rectangular edges around the silhouette.
const needsClip = bg || frame;
return (
);
}
function CrSilhouette({ id, size = 120, color, glow = true }) {
return ;
}
// animation + locked silhouette CSS
if (!document.getElementById('dd-creature-anim')) {
const s = document.createElement('style');
s.id = 'dd-creature-anim';
s.textContent = `
@keyframes dd-float { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-3px) } }
.dd-locked-silhouette * { fill: #1B3A56 !important; stroke: #1B3A56 !important; }
.dd-locked-silhouette text { fill: #2A4C68 !important; }
`;
document.head.appendChild(s);
}
Object.assign(window, {
CreatureSpriteFrame, CrSilhouette,
CREATURE_REGISTRY, CREATURE_VARIANTS, FAMILY_TYPE_STYLE, RARITY_COLOR,
});
})();