@rr0/ufoathome 0.51.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ const e={type:"number"},t={type:"object",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},o={type:"object",doc:"See SightingEvent.endTime.",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},n={type:"number",doc:"See SightingEvent.durationSeconds."},i={type:"number",doc:"See SightingEvent.utcOffsetHours — the legal time zone `time`/`endTime` are expressed in."},s={type:"string",doc:"See SightingEvent.timeZone — the IANA rule `utcOffsetHours` was derived from, when it was."},r={type:"array",fields:{lat:{type:"number"},lng:{type:"number"},name:{type:"string",doc:'The place as it is named, fully qualified — "Valensole, Alpes-de-Haute-Provence, …, France".'}}},a={type:"object",doc:"See Sighting.witness.",fields:{id:{type:"string"},dirName:{type:"string"},title:{type:"string"},lastName:{type:"string"},firstNames:{type:"array"}}},d={type:"object",doc:"Who saw it and how their account travelled — see Testimony, and Sighting.testimony.",fields:{witnessAgeYears:{type:"number",doc:"The main witness's age in years AT THE TIME, not today — a 1974 account is read by whoever finds it decades later, and the number that means anything is the one from then."},witnessOccupation:{type:"string",doc:'What the main witness did, as they are described: "boulanger", "pilote de ligne", "gendarme".'},source:{type:"enum",doc:"How the account reached the person who wrote this recording.",values:["on-site","interview","telephone","questionnaire","letter","press"]},followedUp:{type:"boolean",doc:"Whether the witness was gone back to after the first account."}}},c={type:"string",doc:"See Sighting.caseId — shared by every witness's own sighting.json for the same case, so a page (e.g."},h={type:"string",doc:"See SightingEvent.description — a plain string, or one per language."},l={type:"array",doc:"See SightingEvent.tags."},p={type:"object",fields:{keyframes:{type:"array",fields:{t:{type:"number"},shapes:{type:"array",fields:{sourceId:{type:"string"},shape:{type:"object",fields:{kind:{type:"enum",values:["oval","polygon"]},bounds:{type:"object",fields:{x:{type:"number"},y:{type:"number"},width:{type:"number"},height:{type:"number"}}},color:{type:"string",doc:"CSS color"},angle:{type:"number",doc:"Rotation in radians; 0 = no rotation"},transparency:{type:"number",doc:"0 = opaque, 1 = fully transparent"},haloScale:{type:"number",doc:"0 = no halo/glow, >1 = larger glow radius"},blur:{type:"number",doc:"How indistinct the witness said its edges were — 0 for a hard outline, 1 for a shape with no edge at all."},brightness:{type:"number",doc:"How dazzling the witness said it was — 0 for a light you can look at, 1 for one you cannot."},selected:{type:"boolean"},title:{type:"string",doc:"What the witness calls it, if anything."},behindCloud:{type:"boolean",doc:'Whether the witness reported this shape as being behind cloud at this instant — "it disappeared into a cloud", which is a thing they SAW, not something to be deduced.'},angular:{type:"object",doc:"How big this looked to the witness, in degrees of arc (see AngularExtent) — the recording's OWN statement of size, and the one it is allowed to make.",fields:{widthDeg:{type:"number",doc:"How wide it looked, in degrees of arc."},heightDeg:{type:"number",doc:"How tall it looked, in degrees of arc."}}},aim:{type:"object",doc:"Which way the witness was actually looking when the thing was there — degrees of azimuth from true north and of altitude above the horizon, the same conventions as ObserverPose.",fields:{azimuthDeg:{type:"number",doc:"Degrees clockwise from true north, the same convention as ObserverPose.headingDeg."},altitudeDeg:{type:"number",doc:"Degrees above (positive) or below the horizon."}}},points:{type:"array",doc:"Points relative to bounds origin (0,0 = bounds.x, bounds.y)",fields:{x:{type:"number"},y:{type:"number"}}}}}}}}},order:{type:"array",doc:"Back-to-front paint/hit-test order — see Timeline's own `order` field."},groups:{type:"array",doc:"Each inner array is one group's member sourceIds (always >= 2 members) — see Timeline's own `groups` field."}}},y={type:"object",doc:"The witness's position/elevation/orientation over time — absent for older recordings, which fall back to the legacy static place[0] (see Sighting.ts's resolveObserverPoseAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},pose:{type:"object",fields:{lat:{type:"number",doc:"Decimal degrees."},lng:{type:"number"},elevationM:{type:"number"},headingDeg:{type:"number",doc:"Degrees clockwise from true north."},pitchDeg:{type:"number",doc:"Degrees above (positive) or below (negative) the local horizontal — how far up/down the witness was looking, e.g."},rollDeg:{type:"number",doc:"How far the instrument itself was tilted about its own line of sight, degrees, positive clockwise as the witness saw it — a camera held askew, a head leaned over."},fovDeg:{type:"number",doc:"Vertical field of view in degrees, as a Three.js PerspectiveCamera would take it."},fNumber:{type:"number",doc:"How far the lens was stopped down — undefined for anything the recording did not state, which then falls back to the instrument's own setting (see Instrument.fNumber)."},focusDistanceM:{type:"number",doc:"How far away the lens was focused, metres — undefined meaning at infinity, which is where a camera pointed at the sky sits and the only setting under which everything celestial comes out sharp."}}}}}}},u={type:"object",doc:"Weather over time — absent for older recordings, which fall back to the legacy static `weather` field below (see Sighting.ts's resolveWeatherAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},weather:{type:"object",fields:{cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a witness and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both visual precipitation drift and wind audio volume directly, see SceneRenderer/WeatherAudio."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}}}}}},m={type:"object",doc:`Legacy static weather condition, kept only as weatherTrack's own fallback for recordings made before it existed — absent means "unknown/not recorded", not "clear skies" (renderers default to DEFAULT_WEATHER, see Weather.ts).`,fields:{cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a witness and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both visual precipitation drift and wind audio volume directly, see SceneRenderer/WeatherAudio."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}},g={type:"object",doc:"What the sighting sounded like over time — see SoundTrack.",fields:{keyframes:{type:"array",fields:{t:{type:"number"},sound:{type:"object",fields:{kind:{type:"enum",values:["none","hum","whistle","rumble","crackle"]},volume:{type:"number",doc:"How loud it was, 0 (inaudible) to 1 (as loud as the witness could describe) — deliberately relative rather than a dB SPL figure: nobody reports a sound level in decibels, and inventing one would claim a measurement where there is only an impression."},pitchHz:{type:"number",doc:`The sound's own frequency in Hz — the oscillator's pitch for the pitched kinds ("hum", "whistle"), and the noise filter's centre for the unpitched ones ("rumble", "crackle"), where it reads as how deep or how sharp the noise is.`},src:{type:"string",doc:"An actual audio recording of the sound, when one exists (a witness who filmed the event, a later recording of the same phenomenon) — replaces synthesis entirely while it is set, with `volume` still scaling it."}}}}}}},b={type:"array",doc:"See Sighting.decor.",fields:{id:{type:"string"},kind:{type:"enum",values:["building","tree","crop","mound","streetlight","vehicle","witness","aircraft","entity"]},title:{type:"string",doc:'Optional display name, same role as Shape.title — falls back to a generic "{kind} {n}" label (see SightingEditorElement.decorLabel) when absent.'},eastM:{type:"number",doc:"Meters from the observer's own position — eastM positive = east, northM positive = north."},northM:{type:"number"},headingDeg:{type:"number",doc:"Degrees clockwise from true north, same convention as ObserverPose.headingDeg — which way a vehicle is facing, or a witness looking."},lit:{type:"boolean",doc:"Streetlight lamp / vehicle headlights switched on."},litKeyframes:{type:"array",doc:"Sorted by t.",fields:{t:{type:"number"},lit:{type:"boolean"}}},lights:{type:"array",doc:"The object's individual lamps, each with its own place on the body and its own rhythm — see DecorLight, and LIGHT_RIGS for the ready-made sets.",fields:{id:{type:"string"},offsetM:{type:"object",doc:"Where it sits on the object, in meters relative to the object's own centre and heading: +x is its right, +y up, +z its front.",fields:{x:{type:"number"},y:{type:"number"},z:{type:"number"}}},color:{type:"string",doc:"CSS hex colour, as emitted — the regulated red/green/white of navigation lights, the amber of a hazard flasher."},intensity:{type:"number",doc:"Relative brightness, 1 being an ordinary navigation light."},pattern:{type:"object",fields:{kind:{type:"enum",values:["steady","flash"]},perMinute:{type:"number"},dutyCycle:{type:"number"},phase:{type:"number"}}}}},track:{type:"array",doc:"Where the object is over time, when it moves — an aircraft crossing the sky, a car driving past.",fields:{t:{type:"number"},eastM:{type:"number"},northM:{type:"number"},altitudeM:{type:"number",doc:"Meters above the observer's own elevation."},headingDeg:{type:"number"}}},sightingUrl:{type:"string",doc:`For kind "witness" only: the URL of that witness's own sighting.json recording, if known — lets the 3D scene's own context menu offer "view this witness's testimony" (loading it the same way SightingEditorElement.importFromUrl already does).`},windows:{type:"string",doc:"Per-side window opacity, 0 (fully open — the pane is invisible, and DecorSystem.addRoom's own interior wall panel is skipped there too, same as no window at all) to 100 (fully closed — a solid-looking pane)."},color:{type:"string",doc:'What colour this object actually is, as a CSS colour — the same way a shape states its own (see Shape.color), because a recording is written and read by people and "#8f86a8" is a colour a person can type.'},witnessSide:{type:"enum",values:["front","behind","left","right","front-left","front-right","behind-left","behind-right"]},floors:{type:"number",doc:'Kind "building" only: number of upper stories above the ground floor, set when the building is created (default 2, see SightingEditorElement.addDecor) and editable afterward.'},occupiedFloor:{type:"number",doc:'Kind "building" only, meaningful once witnessSide is set: which floor the witness is on, 0 (ground floor) up to floors.'},occludesSourceIds:{type:"array",doc:"Shape sourceIds (see Timeline.sourceIds/Shape) this decor object sits in front of, from the witness's own reported viewpoint — occluding that shape wherever their screen positions line up (see SceneRenderer.isScreenPointOccluded)."},sizeM:{type:"object",doc:"How big it really is, in meters, along its own axes — see DecorSize.",fields:{widthM:{type:"number"},lengthM:{type:"number"},heightM:{type:"number"}}},model:{type:"object",doc:"The real 3D model that stands in for the primitive, if one does — see DecorModelRef.",fields:{id:{type:"string",doc:"Catalogue entry, resolved by the live DecorModelProvider."},url:{type:"string",doc:"Direct glTF/GLB address, taking precedence over `id`."},headingOffsetDeg:{type:"number",doc:"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},credit:{type:"object",doc:"Who made it and under what licence.",fields:{title:{type:"string"},author:{type:"string"},license:{type:"string",doc:"Licence name as its author states it, e.g."},sourceUrl:{type:"string",doc:"Where it came from, so the claim can be checked."}}}}}}},w={type:"object",doc:"Which meteorological record `weatherTrack` was looked up from, when it wasn't the witness who stated the conditions — see Sighting.weatherSource.",fields:{id:{type:"string",doc:"Stable dataset id, e.g."},name:{type:"string",doc:"Human-readable dataset name, e.g."},url:{type:"string",doc:"The exact request that produced these values, so the claim stays checkable years later."}}},f={type:"string",doc:"Which INSTRUMENTS entry this was observed through — see Sighting.instrumentId."},v={type:"number",doc:"How long the shutter stayed open, seconds — one value for the whole observation, see Sighting.exposureSeconds."},k={type:"array",doc:"The named moments of the account — see Milestone.",fields:{t:{type:"number",doc:"When, in milliseconds from the start of the recording — the same scale every track uses."},label:{type:"string",doc:'Short label, shown on the seek bar: the letter or number the account itself uses ("A", "B"), or a couple of words where it uses none.'},note:{type:"string",doc:"What happened at that moment, in the account's own words where possible."}}},S={version:e,time:t,endTime:o,durationSeconds:n,utcOffsetHours:i,timeZone:s,place:r,witness:a,testimony:d,caseId:c,description:h,tags:l,timeline:p,witnessTrack:y,weatherTrack:u,weather:m,soundTrack:g,decor:b,weatherSource:w,instrument:f,exposureSeconds:v,milestones:k};export{c as caseId,b as decor,S as default,h as description,n as durationSeconds,o as endTime,v as exposureSeconds,f as instrument,k as milestones,r as place,g as soundTrack,l as tags,d as testimony,t as time,s as timeZone,p as timeline,i as utcOffsetHours,e as version,m as weather,w as weatherSource,u as weatherTrack,a as witness,y as witnessTrack};
@@ -401,4 +401,4 @@ input[type=range] {
401
401
  pointer-events: none;
402
402
  white-space: nowrap;
403
403
  }
404
- `;class _ extends Error{constructor(t,e,s){super(`${t}: ${e}${s===void 0?"":` (HTTP ${s})`}`),this.kind=t,this.url=e,this.status=s,this.name="SightingFetchError"}}class _t{static async json(t){const e=new URL(t,location.href);if(location.protocol==="https:"&&e.protocol==="http:")throw new _("mixed-content",t);let s;try{s=await fetch(t)}catch{throw await this.diagnose(t,e)}if(!s.ok)throw new _("status",t,s.status);try{return await s.json()}catch{throw new _("malformed",t)}}static async diagnose(t,e){if(e.origin===location.origin)return new _("unreachable",t);try{return await fetch(t,{mode:"no-cors"}),new _("cors",t)}catch{return new _("unreachable",t)}}}const H=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant",stabilization:.97},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5,detailUm:20},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6,detailUm:20},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975},detailUm:20},{id:"phone-landscape",name:{en:"Phone, held sideways",fr:"Téléphone, tenu couché"},projection:"rectilinear",frame:{widthMm:7.6,heightMm:5.7,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4},{id:"phone-portrait",name:{en:"Phone, held upright",fr:"Téléphone, tenu debout"},projection:"rectilinear",frame:{widthMm:5.7,heightMm:7.6,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4}];class b{static get default(){return H[0]}static byId(t){return H.find(e=>e.id===t)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(t){const e=t.frame;return e?2*Math.atan(e.heightMm/(2*e.focalLengthMm))*180/Math.PI:b.UNAIDED_FIELD_DEG}static aspectOf(t){const e=t.frame;return e?e.widthMm/e.heightMm:b.UNAIDED_ASPECT}static frameWidthPx(t,e){return Math.round(e*b.aspectOf(t))}static availableAt(t){return t===void 0?H:H.filter(e=>!e.years||t>=e.years.from&&(e.years.to===void 0||t<=e.years.to))}static fieldOfViewDegAt(t,e){const s=t.frame;if(!(!s||e<=0))return 2*Math.atan(s.heightMm/(2*e))*180/Math.PI}static focalLengthMmFor(t,e){const s=t.frame;if(!(!s||e<=0||e>=180))return s.heightMm/(2*Math.tan(e*Math.PI/360))}static bladesShowing(t,e){const s=e??t.fNumber;if(s===void 0||t.apertureBlades===void 0)return 0;const i=t.fNumberRange;return!i||i.max<=i.min?1:Math.max(0,Math.min(1,(s-i.min)/(i.max-i.min)))}static LENS_FLARE_ARTIFACTS=1;static flareArtifactsOf(t){return t.flare??b.LENS_FLARE_ARTIFACTS}static starPointsOf(t){const e=t.apertureBlades;return e===void 0||e<3?0:e%2===0?e:e*2}}const Et=Math.PI/180,q=111320;function D(n,t,e,s){const i=q*Math.cos(e*Et),r=(n-e)*q;return{x:(t-s)*i,z:-r}}function L(n,t,e,s){const i=q*Math.cos(e*Et),r=-t,a=n;return{lat:e+r/q,lng:s+a/i}}class F{constructor(t){this.points=t}static STATIONARY_M=5;static of(t){const e=[];for(const s of t.witnessTrack.allKeyframes){const{lat:i,lng:r,headingDeg:a}=s.pose;i===void 0||r===void 0||e.push({t:s.t,lat:i,lng:r,headingDeg:a})}if(e.length===0){const s=t.event.place?.[0];if(!s)return;e.push({t:0,lat:s.lat,lng:s.lng})}return new F(e)}get moved(){return this.spanM>F.STATIONARY_M}get spanM(){const t=this.box,e=(t.south+t.north)/2,s=(t.west+t.east)/2;return Math.max(Math.abs(D(e,t.east,e,t.west).x),Math.abs(D(t.north,s,t.south,s).z))}get center(){const t=this.box;return{lat:(t.south+t.north)/2,lng:(t.west+t.east)/2}}boundsAround(t,e){const{lat:s,lng:i}=this.center,r=Math.max(this.spanM*(1+e),t)/2;return{north:L(0,-r,s,i).lat,south:L(0,r,s,i).lat,east:L(r,0,s,i).lng,west:L(-r,0,s,i).lng}}get box(){const t=this.points.map(s=>s.lat),e=this.points.map(s=>s.lng);return{south:Math.min(...t),north:Math.max(...t),west:Math.min(...e),east:Math.max(...e)}}}class g{constructor(t,e){this.stretches=t,this.rotationPassed=e}static REFERENCE_SPEED_M_PER_S=1.4;static REFERENCE_STEP_HZ=1.9;static CADENCE_EXPONENT=.43;static RISE_M_PER_M_PER_S=.0175;static SWAY_M=.0175;static SLOWEST_WALK_M_PER_S=.2;static FASTEST_WALK_M_PER_S=2.2;static HEAD_ROLL_DEG=1.5;static HEAD_PITCH_DEG=1.25;static HEAD_YAW_DEG=1;static STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(t){const e=F.of(t);if(!e||e.points.length<2)return;const s=[];let i=0;for(let r=1;r<e.points.length;r++){const a=e.points[r-1],o=e.points[r],h=(o.t-a.t)/1e3;if(h<=0)continue;const l=D(o.lat,o.lng,a.lat,a.lng),d=l.x,c=-l.z,u=Math.hypot(d,c),f=u/h,p=g.stepHzFor(f);s.push({startMs:a.t,endMs:o.t,speedMPerS:f,eastUnit:u>0?d/u:0,northUnit:u>0?c/u:0,stepHz:p,stepsBefore:i,rampsIn:!0,rampsOut:!0}),i+=p*h}for(let r=0;r<s.length;r++){const a=s[r-1],o=s[r+1];s[r].rampsIn=a===void 0||a.stepHz===0,s[r].rampsOut=o===void 0||o.stepHz===0}if(s.some(r=>r.stepHz>0))return new g(s,1-(t.instrument.stabilization??0))}static stepHzFor(t){return t<g.SLOWEST_WALK_M_PER_S||t>g.FASTEST_WALK_M_PER_S?0:g.REFERENCE_STEP_HZ*Math.pow(t/g.REFERENCE_SPEED_M_PER_S,g.CADENCE_EXPONENT)}offsetAt(t){const e=this.stretches.find(h=>t>=h.startMs&&t<=h.endMs);if(!e||e.stepHz===0)return g.STILL;const s=e.stepsBefore+e.stepHz*(t-e.startMs)/1e3,i=2*Math.PI*s,r=this.amplitudeAt(e,t),a=g.SWAY_M*r*Math.cos(i/2),o=r*this.rotationPassed*e.speedMPerS/g.REFERENCE_SPEED_M_PER_S;return{eastM:a*e.northUnit,northM:-a*e.eastUnit,upM:g.RISE_M_PER_M_PER_S*e.speedMPerS*r*Math.cos(i),rollDeg:g.HEAD_ROLL_DEG*o*Math.cos(i/2),pitchDeg:-1.25*o*Math.cos(i),yawDeg:g.HEAD_YAW_DEG*o*Math.cos(i/2)}}amplitudeAt(t,e){const s=1e3/t.stepHz,i=Math.min(s,(t.endMs-t.startMs)/2);if(i<=0)return 1;const r=e-t.startMs,a=t.endMs-e,o=Math.min(t.rampsIn?r/i:1,t.rampsOut?a/i:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class M{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(t){return 2*Math.atan(t.sizeM/(2*t.distanceM))*180/Math.PI}static inMoons(t){return t/M.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(t,e,s){if(!(!t||!e))return{widthDeg:t.widthDeg+(e.widthDeg-t.widthDeg)*s,heightDeg:t.heightDeg+(e.heightDeg-t.heightDeg)*s}}static sizeMAt(t,e){return 2*t*Math.tan(e*Math.PI/360)}static distanceMAt(t,e){return t/(2*Math.tan(e*Math.PI/360))}}function Rt(n,t,e){if(!n||!t)return e<1?n:t;const s=((t.azimuthDeg-n.azimuthDeg)%360+540)%360-180;return{azimuthDeg:((n.azimuthDeg+s*e)%360+360)%360,altitudeDeg:n.altitudeDeg+(t.altitudeDeg-n.altitudeDeg)*e}}function Bt(n,t,e){const{bounds:s}=n;return t>=s.x&&t<=s.x+s.width&&e>=s.y&&e<=s.y+s.height}function w(n,t,e){return n+(t-n)*e}function rt(n){const t=/^#([0-9a-f]{6})$/i.exec(n);if(!t)return;const e=parseInt(t[1],16);return[e>>16&255,e>>8&255,e&255]}function Lt(n){return`#${n.map(t=>Math.round(t).toString(16).padStart(2,"0")).join("")}`}function Ft(n,t,e){const s=rt(n),i=rt(t);return!s||!i?e<1?n:t:Lt([w(s[0],i[0],e),w(s[1],i[1],e),w(s[2],i[2],e)])}function Ot(n,t,e){const s={x:w(n.bounds.x,t.bounds.x,e),y:w(n.bounds.y,t.bounds.y,e),width:w(n.bounds.width,t.bounds.width,e),height:w(n.bounds.height,t.bounds.height,e)},i=w(n.angle,t.angle,e),r=w(n.transparency,t.transparency,e),a=w(n.haloScale,t.haloScale,e),o=w(n.blur??0,t.blur??0,e),h=w(n.brightness??0,t.brightness??0,e),l=Ft(n.color,t.color,e),d=M.lerpAngular(n.angular,t.angular,e),c=Rt(n.aim,t.aim,e);return n.kind==="polygon"&&t.kind==="polygon"&&n.points.length===t.points.length?{...n,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:l,angular:d,aim:c,behindCloud:e<1?n.behindCloud:t.behindCloud,points:n.points.map((u,f)=>({x:w(u.x,t.points[f].x,e),y:w(u.y,t.points[f].y,e)}))}:{...e<1?n:t,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:l,angular:d,aim:c}}class Z{keyframes=[];order=[];groups=[];addKeyframe(t,e){const s=this.findInsertIndex(t);if(this.keyframes[s]?.t===t){const i=new Set(e.map(r=>r.sourceId));this.keyframes[s]={t,shapes:[...this.keyframes[s].shapes.filter(r=>!i.has(r.sourceId)),...e]}}else this.keyframes.splice(s,0,{t,shapes:[...e]});for(const i of e)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getKeyframeAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e];return s?.t===t?s:void 0}getShapeAt(t,e){return this.getKeyframeAt(t)?.shapes.find(s=>s.sourceId===e)?.shape}getLatestShapeAt(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return i.shape}}getInterpolatedShapeAt(t,e){const s=this.findShapeAtOrBefore(t,e);if(s?.t===t)return s.shape;const i=this.findShapeAtOrAfter(t,e);return s?i?Ot(s.shape,i.shape,(t-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(t,e){for(let s=this.findInsertIndex(t);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(t,e,s,i){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(i?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(t,r[a]);if(o&&Bt(o,e,s))return{sourceId:r[a],shape:o}}}removeSource(t){for(let e=this.keyframes.length-1;e>=0;e--){const s=this.keyframes[e].shapes.filter(i=>i.sourceId!==t);s.length===0?this.keyframes.splice(e,1):s.length!==this.keyframes[e].shapes.length&&(this.keyframes[e]={t:this.keyframes[e].t,shapes:s})}this.order=this.order.filter(e=>e!==t),this.removeFromGroup(t)}group(t){if(!(t.length<2)){for(const e of t)this.removeFromGroup(e);this.groups.push([...t])}}ungroup(t){const e=this.groups.findIndex(s=>s.includes(t));e!==-1&&this.groups.splice(e,1)}groupMembers(t){return this.groups.find(e=>e.includes(t))}removeFromGroup(t){const e=this.groups.findIndex(i=>i.includes(t));if(e===-1)return;const s=this.groups[e].filter(i=>i!==t);s.length<2?this.groups.splice(e,1):this.groups[e]=s}bringToFront(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.push(t))}sendToBack(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.unshift(t))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(t){const e=new Z;for(const s of t.keyframes)e.addKeyframe(s.t,s.shapes);if(t.order){const s=e.order.filter(i=>!t.order.includes(i));e.order=[...t.order,...s]}return t.groups&&(e.groups=t.groups.map(s=>s.filter(i=>e.order.includes(i))).filter(s=>s.length>=2)),e}}function Nt(n,t,e){return Math.max(t,Math.min(e,n))}function Wt(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function R(n,t,e){return n+(t-n)*e}function zt(n,t,e){return{lat:n.lat===void 0||t.lat===void 0?void 0:R(n.lat,t.lat,e),lng:n.lng===void 0||t.lng===void 0?void 0:R(n.lng,t.lng,e),elevationM:R(n.elevationM,t.elevationM,e),headingDeg:n.headingDeg===void 0||t.headingDeg===void 0?void 0:Wt(n.headingDeg,t.headingDeg,e),pitchDeg:R(n.pitchDeg,t.pitchDeg,e),rollDeg:R(n.rollDeg??0,t.rollDeg??0,e),fovDeg:R(n.fovDeg,t.fovDeg,e),fNumber:n.fNumber,focusDistanceM:n.focusDistanceM}}class O{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,pose:e}:this.keyframes.splice(s,0,{t,pose:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestPoseAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].pose:void 0}getInterpolatedPoseAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.pose;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?zt(s.pose,i.pose,Nt((t-s.t)/(i.t-s.t),0,1)):s.pose:i?.pose}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new O;for(const s of t.keyframes)e.addKeyframe(s.t,s.pose);return e}}function E(n,t,e){return n+(t-n)*e}function Ht(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function Ut(n,t,e){return Math.max(t,Math.min(e,n))}function $t(n,t,e){return{cloudCover:E(n.cloudCover,t.cloudCover,e),cloudDarkness:E(n.cloudDarkness,t.cloudDarkness,e),highCloudCover:n.highCloudCover===void 0||t.highCloudCover===void 0?void 0:E(n.highCloudCover,t.highCloudCover,e),iceCrystalAlignment:n.iceCrystalAlignment===void 0||t.iceCrystalAlignment===void 0?void 0:E(n.iceCrystalAlignment,t.iceCrystalAlignment,e),lowerCloudCover:n.lowerCloudCover===void 0||t.lowerCloudCover===void 0?void 0:E(n.lowerCloudCover,t.lowerCloudCover,e),cloudBaseM:n.cloudBaseM===void 0||t.cloudBaseM===void 0?void 0:E(n.cloudBaseM,t.cloudBaseM,e),precipitationType:e<1?n.precipitationType:t.precipitationType,precipitationIntensity:E(n.precipitationIntensity,t.precipitationIntensity,e),windDirectionDeg:Ht(n.windDirectionDeg,t.windDirectionDeg,e),windSpeed:E(n.windSpeed,t.windSpeed,e),storm:e<1?n.storm:t.storm}}class N{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,weather:e}:this.keyframes.splice(s,0,{t,weather:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestWeatherAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].weather:void 0}getInterpolatedWeatherAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.weather;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?$t(s.weather,i.weather,Ut((t-s.t)/(i.t-s.t),0,1)):s.weather:i?.weather}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new N;for(const s of t.keyframes)e.addKeyframe(s.t,s.weather);return e}}class W{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,sound:e}:this.keyframes.splice(s,0,{t,sound:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}getLatestSoundAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].sound:void 0}getInterpolatedSoundAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.sound;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];if(!s)return i?.sound;if(!i)return s.sound;const r=(t-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,r)))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new W;for(const s of t.keyframes)e.addKeyframe(s.t,s.sound);return e}lerp(t,e,s){return{kind:s<1?t.kind:e.kind,volume:t.volume+(e.volume-t.volume)*s,pitchHz:t.pitchHz+(e.pitchHz-t.pitchHz)*s,src:s<1?t.src:e.src}}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}}const Xt=100,Gt={kind:"none",volume:0,pitchHz:Xt};class S{constructor(t){this.entries=t}static empty(){return new S(new Map)}get size(){return this.entries.size}at(t){return this.entries.get(t)}paths(){return[...this.entries.keys()]}set(t,e){e.basis==="stated"&&e.rationale===void 0?this.entries.delete(t):this.entries.set(t,e)}clear(t){for(const e of this.entries.keys())(e===t||e.startsWith(`${t}.`))&&this.entries.delete(e)}static strip(t){const e=new S(new Map);return{recording:S.stripValue(t,[],e),provenance:e}}static restore(t,e){if(e.size===0)return t;const s=structuredClone(t);for(const i of e.paths()){const r=e.at(i),a=i.split("."),o=S.holderOf(s,a);if(!o)continue;const h=a[a.length-1],l=o[h];l!==void 0&&(r.of!==void 0&&JSON.stringify(r.of)!==JSON.stringify(l)||(o[h]={value:l,...r.basis==="stated"?{}:{basis:r.basis},...r.rationale===void 0?{}:{rationale:r.rationale}}))}return s}static isWrapper(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)&&"value"in t}static stripValue(t,e,s){return S.isWrapper(t)?(s.set(e.join("."),{basis:t.basis??"stated",...t.rationale===void 0?{}:{rationale:t.rationale},of:t.value}),S.stripValue(t.value,e,s)):Array.isArray(t)?t.map((i,r)=>S.stripValue(i,[...e,String(r)],s)):typeof t=="object"&&t!==null?Object.fromEntries(Object.entries(t).map(([i,r])=>[i,S.stripValue(r,[...e,i],s)])):t}static holderOf(t,e){let s=t;for(const i of e.slice(0,-1)){if(typeof s!="object"||s===null)return;s=s[i]}return typeof s=="object"&&s!==null?s:void 0}}function it(n){if(n.year!==void 0)return Date.UTC(n.year,(n.month??1)-1,n.day??1,n.hour??0,n.minute??0,n.second??0)}function at(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const Vt=["year","month","day","hour","minute"],Kt=["day","hour","minute"];function ot(n,t){return t.filter(e=>n[e]!==void 0).join(",")}function qt(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const t=n.time.year!==void 0&&n.endTime.year!==void 0,e=t?Vt:Kt,s=ot(n.time,e);if(!(s===""||s!==ot(n.endTime,e)))return t?it(n.endTime)-it(n.time):at(n.endTime)-at(n.time)}class J{constructor(t,e,s,i,r,a,o,h,l=[],d,c,u,f=[]){this.event=t,this.timeline=e,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=a,this.caseId=o,this.weather=h,this.decor=l,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}provenance=S.empty();get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return b.byId(this.instrumentId)}static create(t,e,s){return new J({eventType:"sighting",time:t,place:e},new Z,new O,new N,new W,s)}}const Yt=0,Zt=0;function T(n,t){const e=n.witnessTrack.getInterpolatedPoseAt(t);if(e)return e;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Yt,headingDeg:void 0,pitchDeg:Zt,fovDeg:b.fieldOfViewDeg(n.instrument)}}function Jt(n,t){return n.soundTrack.getInterpolatedSoundAt(t)??Gt}class jt{constructor(t,e){this.timeline=t,this.onFrame=e}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const t=()=>{if(this.state!=="playing")return;const e=performance.now();if(this.currentT+=(e-this.lastWallTime)*this.playbackRate,this.lastWallTime=e,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(t){this.currentT=Math.max(0,Math.min(t,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(t){const e=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}this.onFrame(t,e)}}const Tt=["nw","n","ne","e","se","s","sw","w"],Qt=24,U=8,te=3,ee=8;function $(n,t,e){const s=n.x-t.x,i=n.y-t.y,r=Math.cos(e),a=Math.sin(e);return{x:t.x+s*r-i*a,y:t.y+s*a+i*r}}function B(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const se={nw:{left:!0,top:!0},n:{top:!0},ne:{right:!0,top:!0},e:{right:!0},se:{right:!0,bottom:!0},s:{bottom:!0},sw:{left:!0,bottom:!0},w:{left:!0}};class v{static handlePointsFor(t){const{x:e,y:s,width:i,height:r}=t.bounds,a={nw:{x:e,y:s},n:{x:e+i/2,y:s},ne:{x:e+i,y:s},e:{x:e+i,y:s+r/2},se:{x:e+i,y:s+r},s:{x:e+i/2,y:s+r},sw:{x:e,y:s+r},w:{x:e,y:s+r/2},rotate:{x:e+i/2,y:s-Qt}},o=B(t.bounds),h={};for(const l of Object.keys(a))h[l]=$(a[l],o,t.angle);return h}static hitTestHandle(t,e,s=8,i=[...Tt,"rotate"]){const r=v.handlePointsFor(t);for(const a of i)if(Math.hypot(e.x-r[a].x,e.y-r[a].y)<=s)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(t,e){const i=((v.HANDLE_DIRECTION_DEG[t]+e*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(i)%r.length]}static resizeBounds(t,e,s,i){const r=B(t),a=$(i,r,-e),{x:o,y:h,width:l,height:d}=t,c=se[s];let u=o,f=h,p=o+l,m=h+d;return c.left&&(u=Math.min(a.x,p-U)),c.right&&(p=Math.max(a.x,u+U)),c.top&&(f=Math.min(a.y,m-U)),c.bottom&&(m=Math.max(a.y,f+U)),{x:u,y:f,width:p-u,height:m-f}}static resizeShape(t,e,s){if(e==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:r}=t.bounds,a=v.resizeBounds(t.bounds,t.angle,e,s);if(t.kind==="oval")return{...t,bounds:a};const o=i===0?1:a.width/i,h=r===0?1:a.height/r;return{...t,bounds:a,points:t.points.map(l=>({x:l.x*o,y:l.y*h}))}}static rotateShape(t,e){const s=B(t.bounds),i=Math.atan2(e.y-s.y,e.x-s.x)+Math.PI/2;return{...t,angle:i}}static groupBoundsFor(t){const e=Math.min(...t.map(a=>a.x)),s=Math.min(...t.map(a=>a.y)),i=Math.max(...t.map(a=>a.x+a.width)),r=Math.max(...t.map(a=>a.y+a.height));return{x:e,y:s,width:i-e,height:r-s}}static vertexPointsFor(t){const e=B(t.bounds);return t.points.map(s=>$({x:t.bounds.x+s.x,y:t.bounds.y+s.y},e,t.angle))}static hitTestVertex(t,e,s=ee){const i=v.vertexPointsFor(t);let r,a=s;return i.forEach((o,h)=>{const l=Math.hypot(e.x-o.x,e.y-o.y);l<=a&&(a=l,r=h)}),r}static toLocalPoint(t,e){const s=B(t.bounds),i=$(e,s,-t.angle);return{x:i.x-t.bounds.x,y:i.y-t.bounds.y}}static moveVertex(t,e,s){const i=t.points.map((c,u)=>u===e?v.toLocalPoint(t,s):c),r=Math.min(...i.map(c=>c.x)),a=Math.min(...i.map(c=>c.y)),o=Math.max(...i.map(c=>c.x)),h=Math.max(...i.map(c=>c.y)),l={x:t.bounds.x+r,y:t.bounds.y+a,width:o-r,height:h-a},d=i.map(c=>({x:c.x-r,y:c.y-a}));return{...t,bounds:l,points:d}}static insertVertexNear(t,e){const s=v.toLocalPoint(t,e),{points:i}=t;let r=i.length-1,a=1/0;for(let h=0;h<i.length;h++){const l=i[h],d=i[(h+1)%i.length],c=v.distanceToSegment(s,l,d);c<a&&(a=c,r=h)}const o=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...t,points:o}}static distanceToSegment(t,e,s){const i=s.x-e.x,r=s.y-e.y,a=i*i+r*r,o=a===0?0:Math.max(0,Math.min(1,((t.x-e.x)*i+(t.y-e.y)*r)/a)),h=e.x+o*i,l=e.y+o*r;return Math.hypot(t.x-h,t.y-l)}static deleteVertex(t,e){return t.points.length<=te?t:{...t,points:t.points.filter((s,i)=>i!==e)}}}const nt=6,X=nt/2,ie=20,ne=24,re=9,ht=6,ae=7,oe=6,lt=4;class he{constructor(t){this.ctx=t}starPoints=0;roll=0;clear(t,e){this.ctx.clearRect(0,0,t,e)}paintShape(t){this.ctx.save(),this.ctx.globalAlpha=1-t.transparency;const e=t.blur??0;e>0&&(this.ctx.filter=`blur(${(ne*e).toFixed(2)}px)`);const s=t.brightness??0;s>0&&this.paintDazzle(t,s),t.haloScale>0&&this.paintHalo(t),this.paintBase(t),this.ctx.restore(),t.selected&&this.paintSelectionHandles(t)}setStarPoints(t){this.starPoints=Math.max(0,Math.floor(t))}setRoll(t){this.roll=t}paintDazzle(t,e){const{x:s,y:i,width:r,height:a}=t.bounds,o=s+r/2,h=i+a/2,l=Math.max(r,a)/2,d=Math.max(l*(1+re*e),oe*e);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(o,h,0,o,h,d);for(let u=0;u<=ht;u++){const f=u/ht,p=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(t.color,p*e*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(o,h,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=l*(1+ae*e);this.ctx.lineWidth=Math.max(1,l*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const p=f/this.starPoints*Math.PI*2+this.roll,m=o+Math.cos(p)*u,y=h+Math.sin(p)*u,k=this.ctx.createLinearGradient(o,h,m,y);k.addColorStop(0,this.withAlpha(t.color,e*.55)),k.addColorStop(.35,this.withAlpha(t.color,e*.22)),k.addColorStop(1,this.withAlpha(t.color,0)),this.ctx.strokeStyle=k,this.ctx.beginPath(),this.ctx.moveTo(o,h),this.ctx.lineTo(m,y),this.ctx.stroke()}}this.ctx.restore()}dazzledFill(t,e){if(e<=0)return t.color;const s=/^#([0-9a-f]{6})$/i.exec(t.color.trim());if(!s)return t.color;const i=Number.parseInt(s[1],16),r=a=>Math.round(a+(255-a)*e);return`rgb(${r(i>>16&255)}, ${r(i>>8&255)}, ${r(i&255)})`}withAlpha(t,e){const s=Math.max(0,Math.min(1,e)),i=/^#([0-9a-f]{6})$/i.exec(t.trim());if(!i)return t;const r=Number.parseInt(i[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${s.toFixed(3)})`}paintBase(t){if(this.ctx.fillStyle=this.dazzledFill(t,t.brightness??0),this.ctx.beginPath(),t.kind==="oval"){const{x:e,y:s,width:i,height:r}=t.bounds,a=i/2,o=r/2;this.ctx.ellipse(e+a,s+o,a,o,t.angle,0,2*Math.PI)}else{const{x:e,y:s,width:i,height:r}=t.bounds;this.ctx.save(),this.ctx.translate(e+i/2,s+r/2),this.ctx.rotate(t.angle),this.ctx.translate(-i/2,-r/2),t.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(t){this.ctx.save(),this.ctx.shadowColor=t.color,this.ctx.shadowBlur=ie*t.haloScale,this.paintBase(t),this.ctx.restore()}paintSelectionHandles(t){this.paintHandleFrame(v.handlePointsFor(t),{includeRotate:!0}),t.kind==="polygon"&&this.paintVertexHandles(t)}paintVertexHandles(t){this.ctx.fillStyle="#39f";for(const e of v.vertexPointsFor(t))this.ctx.beginPath(),this.ctx.ellipse(e.x,e.y,lt,lt,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(t){this.paintOutline(v.handlePointsFor(t))}paintGroupHandles(t){this.paintHandleFrame(v.handlePointsFor({bounds:t,angle:0}),{includeRotate:!0})}paintOutline(t){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(t,{includeRotate:e}){this.paintOutline(t),this.ctx.fillStyle="lightgray";for(const s of Tt){const i=t[s];this.ctx.fillRect(i.x-X,i.y-X,nt,nt)}e&&(this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,X+1,X+1,0,0,2*Math.PI),this.ctx.fill())}}const ct=.05,dt=.08,ut=2,ft=3,le=14,ce=.02;class de{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(t){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",t);return}}this.context.state==="suspended"&&this.context.resume()}setSound(t){if(this.requested=t,!this.context)return;const e=this.keyFor(t);if(!e){this.silence();return}if(this.voice?.key!==e){this.stopVoice();const s=++this.voiceToken;e.startsWith("src:")?this.buildRecordedVoice(t.src,e,s):this.voice=this.buildSynthesizedVoice(t,e)}this.applyTo(this.voice,t)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(t){if(!(t.volume<=0))return t.src?`src:${t.src}`:t.kind==="none"?void 0:t.kind}applyTo(t,e){const s=this.context;if(!t||!s)return;const i=s.currentTime;t.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,e.volume))*t.gainScale,i,ct);for(const{param:r,ratio:a}of t.pitch)r.setTargetAtTime(Math.min(e.pitchHz*a,s.sampleRate/2-1),i,ct)}buildSynthesizedVoice(t,e){const s=this.context;if(s)switch(t.kind){case"hum":return this.buildHum(s,e);case"whistle":return this.buildWhistle(s,e);case"rumble":return this.buildRumble(s,e);case"crackle":return this.buildCrackle(s,e);default:return}}buildHum(t,e){const s=t.createOscillator();s.type="sawtooth";const i=t.createOscillator();i.type="sawtooth";const r=t.createBiquadFilter();r.type="lowpass",r.Q.value=1;const a=t.createGain();return a.gain.value=0,s.connect(r),i.connect(r),r.connect(a).connect(t.destination),s.start(),i.start(),{key:e,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:r.frequency,ratio:6}]}}buildWhistle(t,e){const s=t.createOscillator();s.type="sine";const i=t.createOscillator();i.type="sine",i.frequency.value=5;const r=t.createGain(),a=t.createGain();return a.gain.value=0,i.connect(r).connect(s.frequency),s.connect(a).connect(t.destination),s.start(),i.start(),{key:e,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:r.gain,ratio:.012}]}}buildRumble(t,e){const s=t.createBufferSource();s.buffer=this.brownNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const r=t.createGain();return r.gain.value=0,s.connect(i).connect(r).connect(t.destination),s.start(),{key:e,nodes:[i,r],sources:[s],volumeParam:r.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(t,e){const s=t.createBufferSource();s.buffer=this.whiteNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const r=t.createGain();r.gain.value=0;const a=t.createBufferSource();a.buffer=this.crackleEnvelope(t),a.loop=!0;const o=t.createGain();return o.gain.value=0,a.connect(o).connect(r.gain),s.connect(i).connect(r).connect(t.destination),s.start(),a.start(),{key:e,nodes:[i,r,o],sources:[s,a],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(t,e,s){const i=this.context;if(!i)return;let r;try{r=await this.getBuffer(t)}catch(h){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",h);return}if(s!==this.voiceToken||!this.context)return;const a=i.createBufferSource();a.buffer=r,a.loop=!0;const o=i.createGain();o.gain.value=0,a.connect(o).connect(i.destination),a.start(),this.voice={key:e,nodes:[o],sources:[a],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(t){const e=this.context;if(!e)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(t);return s||(s=fetch(t).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${t}`);return i.arrayBuffer()}).then(i=>e.decodeAudioData(i)),this.buffers.set(t,s)),s}whiteNoise(t){if(!this.noiseBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ut),t.sampleRate),s=e.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=e}return this.noiseBuffer}brownNoise(t){const e=t.createBuffer(1,Math.floor(t.sampleRate*ut),t.sampleRate),s=e.getChannelData(0);let i=0,r=0;for(let a=0;a<s.length;a++)i=(i+.02*(Math.random()*2-1))/1.02,s[a]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let a=0;a<s.length;a++)s[a]/=r;return e}crackleEnvelope(t){if(!this.crackleBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ft),t.sampleRate),s=e.getChannelData(0),i=Math.max(1,Math.floor(ce*t.sampleRate)),r=Math.round(ft*le);for(let a=0;a<r;a++){const o=Math.floor(Math.random()*s.length),h=.4+Math.random()*.6;for(let l=0;l<i&&o+l<s.length;l++)s[o+l]=Math.max(s[o+l],h*(1-l/i))}this.crackleBuffer=e}return this.crackleBuffer}stopVoice(){const t=this.voice,e=this.context;if(this.voice=void 0,!t||!e)return;const s=e.currentTime;t.volumeParam.cancelScheduledValues(s),t.volumeParam.setTargetAtTime(0,s,dt/3);const i=s+dt;for(const r of t.sources){r.onended=()=>{r.disconnect();for(const a of t.nodes)a.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const a of t.nodes)a.disconnect()}}}}function Y(n){return[...n].sort((t,e)=>t.t-e.t)}function pt(n,t){let e;for(const s of Y(n)){if(s.t>t)break;e=s}return e}const Q=Math.PI/180,G=180/Math.PI;class I{constructor(t,e,s){this.kind=t,this.canvasHeightPx=e,this.fovDeg=s;const i=s*Q/2;this.focalPx=t==="equidistant"?e/2/i:e/2/Math.tan(i)}focalPx;static of(t,e,s){return new I(t.projection,e,s)}degToPx(t){const e=t*Q;return this.kind==="equidistant"?this.focalPx*e:2*this.focalPx*Math.tan(e/2)}pxToDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:2*Math.atan(t/(2*this.focalPx))*G}angleDegToRadiusPx(t){const e=t*Q;return this.kind==="equidistant"?this.focalPx*e:this.focalPx*Math.tan(e)}halfWidthAngleDeg(t){return this.radiusPxToAngleDeg(this.canvasHeightPx*t/2)}radiusPxToAngleDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:Math.atan(t/this.focalPx)*G}ofBounds(t){return{widthDeg:this.pxToDeg(t.width),heightDeg:this.pxToDeg(t.height)}}toBoundsSize(t){return{width:this.degToPx(t.widthDeg),height:this.degToPx(t.heightDeg)}}widthPx(t){return this.degToPx(M.angularWidthDeg(t))}}class C{static fovOf(t,e){return T(t,e)?.fovDeg??b.fieldOfViewDeg(t.instrument)}static toAngular(t){this.eachKeyframe(t,(e,s)=>({...e,angular:s.ofBounds(e.bounds)}))}static toBounds(t){this.eachKeyframe(t,(e,s)=>{if(!e.angular)return e;const{width:i,height:r}=s.toBoundsSize(e.angular),a={x:e.bounds.x+(e.bounds.width-i)/2,y:e.bounds.y+(e.bounds.height-r)/2,width:i,height:r};if(e.kind!=="polygon")return{...e,bounds:a};const o=e.bounds.width===0?1:i/e.bounds.width,h=e.bounds.height===0?1:r/e.bounds.height;return{...e,bounds:a,points:e.points.map(l=>({x:l.x*o,y:l.y*h}))}})}static toAim(t){this.eachKeyframe(t,(e,s,i)=>{if(i?.headingDeg===void 0)return e;const r=this.frameCentre(t),a=s.radiusPxToAngleDeg(e.bounds.x+e.bounds.width/2-r.x),o=s.radiusPxToAngleDeg(r.y-(e.bounds.y+e.bounds.height/2));return{...e,aim:{azimuthDeg:((i.headingDeg+a)%360+360)%360,altitudeDeg:i.pitchDeg+o}}})}static toPosition(t){this.eachKeyframe(t,(e,s,i)=>{if(!e.aim||i?.headingDeg===void 0)return e;const r=this.frameCentre(t),a=this.shortestArc(e.aim.azimuthDeg-i.headingDeg),o=e.aim.altitudeDeg-i.pitchDeg,h=r.x+this.offAxisPx(s,a)-e.bounds.width/2,l=r.y-this.offAxisPx(s,o)-e.bounds.height/2;return{...e,bounds:{...e.bounds,x:h,y:l}}})}static frameCentre(t){return{x:b.frameWidthPx(t.instrument,M.CANVAS_HEIGHT_PX)/2,y:M.CANVAS_HEIGHT_PX/2}}static shortestArc(t){return(t%360+540)%360-180}static offAxisPx(t,e){return Math.abs(e)>=C.OFF_CANVAS_DEG?Math.sign(e)*C.OFF_CANVAS_PX:t.angleDegToRadiusPx(e)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(t,e,s){const i=b.frameWidthPx(e,M.CANVAS_HEIGHT_PX)/2,r=b.frameWidthPx(t.instrument,M.CANVAS_HEIGHT_PX)/2,a=M.CANVAS_HEIGHT_PX/2;for(const o of[...t.timeline.allKeyframes]){const h=this.fovOf(t,o.t),l=s?.get(o.t)??h,d=I.of(e,M.CANVAS_HEIGHT_PX,l),c=I.of(t.instrument,M.CANVAS_HEIGHT_PX,h);t.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,p=f.x+f.width/2-i,m=f.y+f.height/2-a,y=Math.hypot(p,m),k=y===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(y))/y,z={...f,x:r+p*k-f.width/2,y:a+m*k-f.height/2};return{...u,shape:{...u.shape,bounds:z}}}))}this.toBounds(t)}static eachKeyframe(t,e){const{timeline:s}=t;for(const i of[...s.allKeyframes]){const r=T(t,i.t),a=this.fovOf(t,i.t),o=I.of(t.instrument,M.CANVAS_HEIGHT_PX,a);s.addKeyframe(i.t,i.shapes.map(h=>({...h,shape:e(h.shape,o,r)})))}}}function ue(n){return S.restore(fe(n),n.provenance)}function fe(n){return C.toAngular(n),C.toAim(n),{version:1,time:n.event.time,endTime:n.event.endTime,durationSeconds:n.event.durationSeconds,utcOffsetHours:n.event.utcOffsetHours,timeZone:n.event.timeZone,place:n.event.place,witness:n.witness,caseId:n.caseId,description:n.event.description,tags:n.event.tags,timeline:n.timeline.toJSON(),witnessTrack:n.witnessTrack.toJSON(),weatherTrack:n.weatherTrack.toJSON(),soundTrack:n.soundTrack.toJSON(),weather:n.weather,decor:n.decor,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function pe(n){const{recording:t,provenance:e}=S.strip(n),s=me(t);return s.provenance=e,s}function me(n){const t=new J({eventType:"sighting",time:n.time,endTime:n.endTime,durationSeconds:n.durationSeconds,utcOffsetHours:n.utcOffsetHours,timeZone:n.timeZone,place:n.place,description:n.description,tags:n.tags},Z.fromJSON(n.timeline),n.witnessTrack?O.fromJSON(n.witnessTrack):new O,n.weatherTrack?N.fromJSON(n.weatherTrack):new N,n.soundTrack?W.fromJSON(n.soundTrack):new W,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument,n.exposureSeconds??n.witnessTrack?.keyframes.map(e=>e.pose.exposureSeconds).find(e=>e!==void 0),Y(n.milestones??[]));return C.toBounds(t),C.toPosition(t),t}function ge(n,t){for(const e of n){const s=e.toLowerCase().split("-")[0];if(t.includes(s))return s}return"en"}class mt{static preferencesFor(t){const e=navigator.languages??[],s=this.declaredFor(t);return s?[s,...e]:e}static declaredFor(t){for(let e=t;e;){const s=e.closest("[lang]")?.getAttribute("lang")?.trim();if(s)return s;const i=e.getRootNode();e=i instanceof ShadowRoot?i.host:void 0}}}class P{constructor(t){this.preferences=t}read(t){if(t===void 0||typeof t=="string")return t===""?void 0:t;for(const s of this.preferences){const i=t[s]??t[P.base(s)];if(i)return i}const e=new Set(this.preferences.map(s=>P.base(s)));for(const[s,i]of Object.entries(t))if(i&&e.has(P.base(s)))return i;return Object.values(t).find(s=>s)??void 0}write(t,e,s){const i=e?.trim()?e.trim():void 0;if(t===void 0||typeof t=="string")return i;const r={...t},a=Object.keys(r).find(o=>o===s)??Object.keys(r).find(o=>P.base(o)===P.base(s))??s;return i===void 0?delete r[a]:r[a]=i,Object.keys(r).length===0?void 0:r}static base(t){return t.toLowerCase().split("-")[0]}}const ye="modulepreload",we=function(n,t){return new URL(n,t).href},gt={},yt=function(t,e,s){let i=Promise.resolve();if(e&&e.length>0){let a=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const o=document.getElementsByTagName("link"),h=document.querySelector("meta[property=csp-nonce]"),l=h?.nonce||h?.getAttribute("nonce");i=a(e.map(d=>{if(d=we(d,s),d in gt)return;gt[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let m=o.length-1;m>=0;m--){const y=o[m];if(y.href===d&&(!c||y.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const p=document.createElement("link");if(p.rel=c?"stylesheet":ye,c||(p.as="script"),p.crossOrigin="",p.href=d,l&&p.setAttribute("nonce",l),document.head.appendChild(p),c)return new Promise((m,y)=>{p.addEventListener("load",m),p.addEventListener("error",()=>y(new Error(`Unable to preload CSS for ${d}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return i.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return t().catch(r)})},be=["en","fr"],ve={en:()=>yt(()=>Promise.resolve().then(()=>Me),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>yt(()=>import("./UfoMessages_fr-BJPYHN17.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function xe(n){return ve[n]()}const Pt={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",witnessHere:"The witness, here",decorHere:"Scenery"},Me=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Pt},Symbol.toStringTag,{value:"Module"}));function Se(n,t){const e=n.track,s={eastM:n.eastM,northM:n.northM,altitudeM:0,headingDeg:n.headingDeg};if(!e||e.length===0)return s;if(e.length===1||t<=e[0].t)return wt(e[0],n);const i=e[e.length-1];if(t>=i.t)return wt(i,n);for(let r=0;r<e.length-1;r++){const a=e[r],o=e[r+1];if(t<a.t||t>o.t)continue;const h=o.t-a.t,l=h===0?0:(t-a.t)/h;return{eastM:a.eastM+(o.eastM-a.eastM)*l,northM:a.northM+(o.northM-a.northM)*l,altitudeM:(a.altitudeM??0)+((o.altitudeM??0)-(a.altitudeM??0))*l,headingDeg:a.headingDeg??n.headingDeg}}return s}function wt(n,t){return{eastM:n.eastM,northM:n.northM,altitudeM:n.altitudeM??0,headingDeg:n.headingDeg??t.headingDeg}}const At=Math.PI/180,ke=180/Math.PI,Ee=256,Te=19;function Pe(n,t){return 156543.03392*Math.cos(n*At)/2**t}function Ae(n,t){return Pe(n,t)*Ee}function Ie(n,t){let e=0;for(;e<Te&&Ae(n,e+1)>=t;)e++;return e}function K(n){const t=n*At;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}function De(n,t,e){const s=2**e;return{x:(n+180)/360*s,y:K(t)*s}}function Ce(n,t,e){const s=K(n.north),i=K(n.south);return{x:(t-n.west)/(n.east-n.west),y:(K(e)-s)/(i-s)}}function _e(n,t,e){const{x:s,y:i}=De(n,t,e);return{x:Math.floor(s),y:Math.floor(i),z:e}}function Re(n){const t=2**n.z,e=n.x/t*360-180,s=(n.x+1)/t*360-180,i=bt(n.y,t);return{south:bt(n.y+1,t),north:i,west:e,east:s}}function bt(n,t){const e=n/t;return Math.atan(Math.sinh(Math.PI*(1-2*e)))*ke}function Be(n,t,e,s){const i=_e(n,t,e),r=Math.floor(s/2),a=[];for(let l=-r;l<=r;l++)for(let d=-r;d<=r;d++)a.push({x:i.x+d,y:i.y+l,z:e});const o=a.map(Re),h={south:Math.min(...o.map(l=>l.south)),north:Math.max(...o.map(l=>l.north)),west:Math.min(...o.map(l=>l.west)),east:Math.max(...o.map(l=>l.east))};return{tiles:a,bounds:h}}class A{constructor(t){this.ctx=t}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(t,e){let s,i=1/0;for(const r of this.targets){const a=Math.hypot(r.x-t,r.y-e);a>r.radius||a>i||(s=r,i=a)}return s}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(t){const{ctx:e}=this;e.save(),this.targets=[],e.clearRect(0,0,this.width,this.height),this.paintGround(t),this.paintNight(t.nightFraction),this.paintPath(t),t.position?(this.paintCone(t,t.position),this.paintDecor(t),this.paintMarkers(t),this.paintWitness(t,t.position)):(this.paintDecor(t),this.paintMarkers(t)),this.paintNorth(),this.paintFooter(t.bounds,t.attribution),e.restore()}toCanvas(t,e,s){const i=Ce(t,s,e);return{x:i.x*this.width,y:i.y*this.height}}paintGround(t){const{ctx:e}=this;e.fillStyle="#1d2321",e.fillRect(0,0,this.width,this.height);const s=t.imagery;if(!s)return;const i=this.toCanvas(t.bounds,s.bounds.north,s.bounds.west),r=this.toCanvas(t.bounds,s.bounds.south,s.bounds.east);e.drawImage(s.source,i.x,i.y,r.x-i.x,r.y-i.y)}paintNight(t=0){if(t<=0)return;const{ctx:e}=this;e.save(),e.fillStyle=`rgba(6, 12, 30, ${Math.min(t,1)*.78})`,e.fillRect(0,0,this.width,this.height),e.restore()}paintDecor(t){const{ctx:e}=this;for(const s of t.decor){const i=this.toCanvas(t.bounds,s.lat,s.lng);if(s.label&&this.targets.push({kind:"decor",label:s.label,lat:s.lat,lng:s.lng,x:i.x,y:i.y,radius:7}),e.beginPath(),e.rect(i.x-3.5,i.y-3.5,7,7),e.fillStyle="rgba(120, 220, 255, 0.85)",e.fill(),e.lineWidth=1.5,e.strokeStyle="rgba(0, 0, 0, 0.7)",e.stroke(),s.headingDeg===void 0)continue;const r=(s.headingDeg-90)*Math.PI/180;e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+Math.cos(r)*10,i.y+Math.sin(r)*10),e.lineWidth=3,e.strokeStyle="rgba(0, 0, 0, 0.6)",e.stroke(),e.lineWidth=1.5,e.strokeStyle="rgba(120, 220, 255, 0.9)",e.stroke()}}paintPath(t){if(!t.path.moved)return;const{ctx:e}=this,s=t.path.points.map(i=>this.toCanvas(t.bounds,i.lat,i.lng));e.beginPath(),e.moveTo(s[0].x,s[0].y);for(const i of s.slice(1))e.lineTo(i.x,i.y);e.lineJoin="round",e.lineCap="round",e.strokeStyle="rgba(0, 0, 0, 0.65)",e.lineWidth=5,e.stroke(),e.strokeStyle="rgba(255, 255, 255, 0.9)",e.lineWidth=2,e.stroke()}paintCone(t,e){const{headingDeg:s,coneHalfAngleDeg:i}=t;if(s===void 0||i===void 0)return;const{ctx:r}=this,a=this.toCanvas(t.bounds,e.lat,e.lng),o=Math.hypot(this.width,this.height),h=(s-90)*Math.PI/180,l=i*Math.PI/180;r.save(),r.beginPath(),r.moveTo(a.x,a.y),r.arc(a.x,a.y,o,h-l,h+l),r.closePath();const d=r.createRadialGradient(a.x,a.y,0,a.x,a.y,o);d.addColorStop(0,"rgba(255, 224, 130, 0.55)"),d.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),d.addColorStop(1,"rgba(255, 224, 130, 0)"),r.fillStyle=d,r.fill(),r.restore()}paintMarkers(t){const{ctx:e}=this;e.textAlign="center",e.textBaseline="middle",e.font="bold 10px sans-serif";for(const s of[...t.markers].sort((i,r)=>Number(i.current)-Number(r.current))){const i=this.toCanvas(t.bounds,s.lat,s.lng);this.targets.push({kind:"milestone",label:s.note?`${s.label} — ${s.note}`:s.label,lat:s.lat,lng:s.lng,t:s.t,x:i.x,y:i.y,radius:A.MARKER_RADIUS_PX}),e.beginPath(),e.arc(i.x,i.y,A.MARKER_RADIUS_PX,0,Math.PI*2),e.fillStyle=s.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",e.fill(),e.lineWidth=2,e.strokeStyle="rgba(255, 255, 255, 0.9)",e.stroke(),e.fillStyle=s.current?"#1d2321":"#fff",e.fillText(s.label,i.x,i.y)}}paintWitness(t,e){const{ctx:s}=this,i=this.toCanvas(t.bounds,e.lat,e.lng);t.witnessLabel&&this.targets.push({kind:"witness",label:t.witnessLabel,lat:e.lat,lng:e.lng,x:i.x,y:i.y,radius:8});const r=t.markers.some(a=>{if(!a.current)return!1;const o=D(a.lat,a.lng,e.lat,e.lng);return Math.hypot(o.x,o.z)<=A.ON_MARKER_M});s.beginPath(),s.arc(i.x,i.y,r?A.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),r||(s.fillStyle="#ff5a3c",s.fill()),s.lineWidth=4,s.strokeStyle="rgba(0, 0, 0, 0.5)",s.stroke(),s.lineWidth=r?3:2.5,s.strokeStyle=r?"#ff5a3c":"#fff",s.stroke()}paintNorth(){const{ctx:t}=this,e=this.width-14,s=16;t.beginPath(),t.moveTo(e,s-9),t.lineTo(e-4.5,s+5),t.lineTo(e,s+2),t.lineTo(e+4.5,s+5),t.closePath(),t.fillStyle="rgba(255, 255, 255, 0.92)",t.strokeStyle="rgba(0, 0, 0, 0.6)",t.lineWidth=1,t.fill(),t.stroke(),t.font="bold 9px sans-serif",t.textAlign="center",t.textBaseline="top",t.strokeText("N",e,s+6),t.fillText("N",e,s+6)}paintFooter(t,e){const{ctx:s}=this;s.font="9px sans-serif",s.textBaseline="bottom";const i=e?this.wrap(e,this.width-20):[];let r=this.height-4;s.textAlign="right";for(const f of i.slice().reverse())this.paintOutlinedText(f,this.width-6,r),r-=11;const a=(t.south+t.north)/2,h=Math.abs(D(a,t.east,a,t.west).x)/this.width,l=A.SCALE_STEPS_M.find(f=>f/h<=this.width/3);if(l===void 0)return;const d=l/h,c=8,u=r-3;s.lineWidth=3,s.strokeStyle="rgba(0, 0, 0, 0.6)",s.beginPath(),s.moveTo(c,u),s.lineTo(c+d,u),s.stroke(),s.lineWidth=1.5,s.strokeStyle="rgba(255, 255, 255, 0.95)",s.stroke(),s.textAlign="left",this.paintOutlinedText(l>=1e3?`${l/1e3} km`:`${l} m`,c,u-3)}paintOutlinedText(t,e,s){const{ctx:i}=this;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.75)",i.strokeText(t,e,s),i.fillStyle="#fff",i.fillText(t,e,s)}wrap(t,e){const s=[];let i="";for(const r of t.split(" ")){const a=i?`${i} ${r}`:r;i&&this.ctx.measureText(a).width>e?(s.push(i),i=r):i=a}return i&&s.push(i),s}}const tt=256,vt=3;async function Le(n,t,e,s){const i=(n.south+n.north)/2,r=(n.west+n.east)/2,a=Math.abs(D(i,n.east,i,n.west).x),o=Math.abs(D(n.north,r,n.south,r).z),h=Ie(i,Math.max(a,o)/2),{tiles:l,bounds:d}=Be(r,i,h,vt),c=vt*tt,u=document.createElement("canvas");u.width=c,u.height=c;const f=u.getContext("2d");if(!f)throw new Error("2D canvas context unavailable");const p=l[0];await Promise.all(l.map(async k=>{const z=e(k),j=await s(z);if(!j.ok)throw new Error(`Imagery tile fetch failed (${j.status}): ${z}`);const It=await createImageBitmap(await j.blob());f.drawImage(It,(k.x-p.x)*tt,(k.y-p.y)*tt)}));const m=document.createElement("canvas");m.width=t.width,m.height=t.height;const y=m.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(u,0,0,c,c,0,0,t.width,t.height),{source:m,width:t.width,height:t.height,bounds:d}}class Fe{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(t={}){this.fetchImpl=t.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=t.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(t,e){return Le(t,e,s=>this.tileUrlTemplate.replace("{z}",String(s.z)).replace("{y}",String(s.y)).replace("{x}",String(s.x)),this.fetchImpl)}}function Oe(){return new Fe}const xt=new Set,et="show-witness-map",st="hide-milestones";class x extends HTMLElement{static get observedAttributes(){return["src",et,st]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;witnessMapButton;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=J.create();sightingAudio=new de;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=xt;enableClickToPlay=!0;fullscreenTarget;messages=Pt;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=t=>{t.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=t=>{const e=this.canvasPointFromEvent(t),s=e&&this.shapeAt(e.x,e.y),i=this.said.read(s?.shape.title);if(!i){this.tooltip.hidden=!0;return}this.tooltip.textContent=i,this.tooltip.hidden=!1;const r=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-r.left+12}px`,this.tooltip.style.top=`${t.clientY-r.top+12}px`};handleWitnessMapPointerMove=t=>{const e=this.witnessMapTargetFrom(t);if(!e){this.tooltip.hidden=!0,this.witnessMapCanvas.style.cursor="default";return}this.witnessMapCanvas.style.cursor="pointer",this.tooltip.textContent=e.label,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-s.left+12}px`,this.tooltip.style.top=`${t.clientY-s.top+12}px`};handleWitnessMapClick=t=>{const e=this.witnessMapTargetFrom(t);if(e){if(e.kind==="milestone"&&e.t!==void 0){this.player.seek(e.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:e.lat,lng:e.lng,kind:e.kind},bubbles:!0,composed:!0}))}};witnessMapTargetFrom(t){const e=this.witnessMapCanvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return this.witnessMapRenderer.hitTest((t.clientX-e.left)/e.width*this.witnessMapCanvas.width,(t.clientY-e.top)/e.height*this.witnessMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const t=document.createElement("template");t.innerHTML=`<style>${Ct}</style>${Dt}`,this.shadow.appendChild(t.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new he(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new A(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const e of[this.timeStartLabel,this.timeEndLabel])e.addEventListener("click",()=>this.toggleTimeDisplay()),e.addEventListener("keydown",s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapCanvas.addEventListener("pointermove",this.handleWitnessMapPointerMove),this.witnessMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.witnessMapCanvas.addEventListener("click",this.handleWitnessMapClick),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",e=>{this.enableClickToPlay&&(e.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",e=>{this.enableClickToPlay&&(e.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const t=this.getAttribute("src");t&&this.loadFromSrc(t)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(t,e,s){t==="src"&&s&&s!==e&&this.isConnected&&this.loadFromSrc(s),t===et&&this.applyWitnessMapDefault(),t===st&&this.setMilestonesShown(!this.hasAttribute(st))}async loadFromSrc(t){this.sightingData=await _t.json(t)}get sightingData(){return ue(this.currentSighting)}set sightingData(t){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=pe(t),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}get sighting(){return this.currentSighting}previewSound(t){this.soundPreview=t,this.sightingAudio.resume(),this.sightingAudio.setSound(t)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(t){this.player.seek(t)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(t){t!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(t){this.toolbar.classList.toggle("hidden",!t)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(t){this.currentSighting.event.durationSeconds=t,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(t){const e=new Set(t);e.size===this.highlightedSourceIds.size&&[...e].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=e,this.refresh())}setOccludedSourceIds(t){t.size===this.occludedSourceIds.size&&[...t].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=t,this.refresh())}hasVisibleShapeAt(t,e){return this.shapeAt(t,e)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateWitnessMap(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const t=this.player.seekableDuration,e=t>0?Y(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...e.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`;const r=this.said.read(s.label)??"",a=this.said.read(s.note),o=a?`${r} — ${a}`:r;return i.title=o,i.setAttribute("aria-label",o),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(t){const e=this.milestonesShown&&this.sighting.milestones.length>0?pt(this.sighting.milestones,t):void 0;if(this.milestoneCaption.hidden=e===void 0,!e)return;const s=document.createElement("b");s.textContent=this.said.read(e.label)??"";const i=this.said.read(e.note),r=i?` — ${i}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(r))}exposureInstants(t){const e=this.exposureTimes(t),s=1/e.length;return e.map(i=>({shapes:this.shapesAt(i),share:s}))}exposureTimes(t=this.currentTime){const e=(this.currentSighting.exposure??0)*1e3;if(e<x.SHORTEST_VISIBLE_EXPOSURE_MS)return[t];const s=Math.min(x.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(e/x.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(t,e)/x.EXPOSURE_STEP_PX),r=Math.min(x.MAX_TRAVEL_STEPS,Math.max(s,i)),a=[];for(let o=0;o<r;o++)a.push(t+e*o/r);return a}travelPxOver(t,e){const s=this.shapesAt(t),i=this.shapesAt(t+e);let r=0;for(const[a,o]of s){const h=i.get(a);if(!h)continue;const l=h.bounds.x+h.bounds.width/2-(o.bounds.x+o.bounds.width/2),d=h.bounds.y+h.bounds.height/2-(o.bounds.y+o.bounds.height/2);r=Math.max(r,Math.hypot(l,d))}return r}shapeAt(t,e,s=this.occludedSourceIds){for(const i of this.exposureTimes()){const r=this.currentSighting.timeline.hitTest(i,t,e,s);if(r)return r}}shapesAt(t){const e=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}return e}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const t=b.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===t)return;this.canvas.width=t,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${t} / ${this.canvas.height}`)}canvasPointFromEvent(t){const e=this.canvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return{x:(t.clientX-e.left)/e.width*this.canvas.width,y:(t.clientY-e.top)/e.height*this.canvas.height}}onFrame(t,e){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:xt;this.canvasRenderer.setStarPoints(b.starPointsOf(this.sighting.instrument));const i=(T(this.sighting,t)?.rollDeg??0)+this.gaitAt(t).rollDeg;this.canvasRenderer.setRoll(i*Math.PI/180);const r=this.exposureInstants(t),a=this.frameShift;for(const o of r)for(const[h,l]of o.shapes){if(this.occludedSourceIds.has(h))continue;const d=o.share,c=d===1?l:{...l,transparency:1-(1-l.transparency)*d};this.canvasRenderer.paintShape(this.shifted(c,a))}for(const[o,h]of r[0].shapes)this.occludedSourceIds.has(o)||!s.has(o)||(s.size===1?this.canvasRenderer.paintShape({...h,selected:!0}):this.canvasRenderer.paintMemberOutline(h));if(s.size>1){const o=v.groupBoundsFor([...e].filter(([h])=>s.has(h)).map(([,h])=>h.bounds));this.canvasRenderer.paintGroupHandles(o)}this.keepWitnessMapClear(r[0].shapes),this.seekInput.value=String(t),this.timeStartLabel.textContent=this.formatPosition(t),this.showMilestoneAt(t),this.paintWitnessMap(t),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Jt(this.currentSighting,t))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:t}}))}shifted(t,e){return e.x===0&&e.y===0?t:{...t,bounds:{...t.bounds,x:t.bounds.x+e.x,y:t.bounds.y+e.y}}}createPlayer(){const t=new jt(this.currentSighting.timeline,(e,s)=>this.onFrame(e,s));return t.loop=this.loopEnabled,t.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},t}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton())}updatePlayPauseButton(){const t=this.player.playbackState==="playing";this.playPauseButton.textContent=t?"⏸":"▶";const e=this.player.seekableDuration>0;this.playPauseButton.disabled=!e;const s=e?t?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",t),this.cornerButtons.classList.toggle("auto-hide",t)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}restorePlayback(){const t=this.playbackBeforeClick;t&&(this.currentTime=t.time,t.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(t=>{console.error("<rr0-ufo>: requestFullscreen() failed —",t)})}enterSimulatedFullscreen(){const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=t.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const e=t.style;e.setProperty("position","fixed"),e.setProperty("inset","0"),e.setProperty("margin","0"),e.setProperty("max-width","none"),e.setProperty("max-height","none"),e.setProperty("aspect-ratio","auto"),e.setProperty("z-index","2147483647"),e.setProperty("background","#000"),e.setProperty("width","100vw"),e.setProperty("width","100dvw"),e.setProperty("height","100vh"),e.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?t.setAttribute("style",this.styleBeforeSimulatedFullscreen):t.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(et)&&this.witnessPath!==void 0)}setWitnessMapOpen(t){this.witnessMapPanel.hidden=!t,this.witnessMapPanel.removeAttribute("title"),this.witnessMapButton.setAttribute("aria-pressed",String(t)),this.updateWitnessMapButton(),t&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const e=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=e,this.witnessMapButton.setAttribute("aria-label",e)}updateWitnessMap(){const t=this.witnessPath!==void 0;if(this.witnessPath=F.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!t&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const e=this.witnessPath.boundsAround(x.WITNESS_MAP_MIN_SPAN_M,x.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,e))&&(this.witnessMapBounds=e,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}sameGround(t,e){const s=Math.abs(t.east-t.west)/20;return Math.abs(t.north-e.north)<s&&Math.abs(t.south-e.south)<s&&Math.abs(t.west-e.west)<s&&Math.abs(t.east-e.east)<s}async loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const t=Oe(),e=this.witnessMapBounds;try{const s=await t.getImageryTexture(e,{width:x.WITNESS_MAP_IMAGERY_PX,height:x.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==e)return;this.witnessMapImagery=s,this.witnessMapImageryCredit=t.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}keepWitnessMapClear(t){if(this.witnessMapPanel.hidden)return;const e=this.witnessMapBoxPx;if(!e)return;const s=(o,h,l)=>{for(const[d,c]of t)if(!(this.occludedSourceIds.has(d)||c.transparency>=1)&&!(c.bounds.x+c.bounds.width<o-l||c.bounds.x>h+l)&&!(c.bounds.y>e.bottom+l||c.bounds.y+c.bounds.height<e.top-l))return!0;return!1},i=[this.canvas.width-e.width,this.canvas.width],r=[0,e.width],a=x.WITNESS_MAP_CLEARANCE_PX;if(this.witnessMapPanel.classList.contains("on-the-left")){s(i[0],i[1],a)||this.witnessMapPanel.classList.remove("on-the-left");return}s(i[0],i[1],0)&&!s(r[0],r[1],0)&&this.witnessMapPanel.classList.add("on-the-left")}witnessMapBoxPx;static WITNESS_MAP_CLEARANCE_PX=24;sizeWitnessMapCanvas(){const t=this.witnessMapPanel.clientWidth;if(t===0)return;const e=Math.round(t*(globalThis.devicePixelRatio??1));this.measureWitnessMapBox(),this.witnessMapCanvas.width!==e&&(this.witnessMapCanvas.width=e,this.witnessMapCanvas.height=e)}measureWitnessMapBox(){const t=this.witnessMapPanel.getBoundingClientRect(),e=this.canvas.getBoundingClientRect();if(e.width===0||e.height===0)return;const s=this.canvas.width/e.width,i=this.canvas.height/e.height;this.witnessMapBoxPx={width:t.width*s,top:(t.top-e.top)*i,bottom:(t.bottom-e.top)*i}}paintWitnessMap(t){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const e=T(this.currentSighting,t),s=this.currentSighting.instrument,i=[],r=this.currentSighting.milestones.length>0?pt(this.currentSighting.milestones,t):void 0;for(const a of this.milestonesShown?Y(this.currentSighting.milestones):[]){const o=T(this.currentSighting,a.t);o?.lat===void 0||o.lng===void 0||i.push({label:this.said.read(a.label)??"",note:this.said.read(a.note),t:a.t,lat:o.lat,lng:o.lng,current:a===r})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,position:e?.lat!==void 0&&e.lng!==void 0?{lat:e.lat,lng:e.lng}:void 0,headingDeg:e?.headingDeg===void 0?void 0:e.headingDeg+this.lookYawDeg,coneHalfAngleDeg:e?I.of(s,this.canvas.height,e.fovDeg).halfWidthAngleDeg(b.aspectOf(s)):void 0,markers:i,decor:this.witnessMapDecorAt(t),witnessLabel:this.said.read(this.currentSighting.witness?.title)??this.messages.witnessHere,nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(t){this.milestonesShown=t,this.milestoneMarks.hidden=!t,this.milestonesButton.setAttribute("aria-pressed",String(t)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const t=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=t,this.milestonesButton.setAttribute("aria-label",t)}witnessMapDecorAt(t){const e=T(this.currentSighting,0);return e?.lat===void 0||e.lng===void 0?[]:this.currentSighting.decor.filter(s=>s.kind!=="crop").map(s=>{const i=Se(s,t),{lat:r,lng:a}=L(i.eastM,-i.northM,e.lat,e.lng);return{lat:r,lng:a,headingDeg:i.headingDeg,label:this.said.read(s.title)??this.messages.decorHere}})}witnessMapNightFraction;setSunAltitude(t){const e=t===void 0?void 0:Math.max(0,Math.min(1,-t/12));e!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=e,this.paintWitnessMap(this.currentTime))}setLookOffset(t,e){t===this.lookYawDeg&&e===this.lookPitchDeg||(this.lookYawDeg=t,this.lookPitchDeg=e,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const t=this.gaitAt(this.currentTime),e=this.lookYawDeg+t.yawDeg,s=this.lookPitchDeg+t.pitchDeg;if(e===0&&s===0)return{x:0,y:0};const i=T(this.currentSighting,this.currentTime),r=I.of(this.currentSighting.instrument,this.canvas.height,i?.fovDeg??60);return{x:-r.angleDegToRadiusPx(e),y:r.angleDegToRadiusPx(s)}}gaitAt(t){return g.of(this.currentSighting)?.offsetAt(t)??g.STILL}updateFullscreenButton(){const t=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=t?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new P(mt.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const t=ge(mt.preferencesFor(this),be);t!=="en"&&this.applyMessages(await xe(t))}updateTimeLabelTitles(){const t=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,e=this.canSwitchTimeDisplay?` — ${t}`:"";this.timeStartLabel.title=this.messages.currentPosition+e,this.timeEndLabel.title=this.messages.duration+e;for(const s of[this.timeStartLabel,this.timeEndLabel])s.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(s.setAttribute("role","button"),s.setAttribute("tabindex","0")):(s.removeAttribute("role"),s.removeAttribute("tabindex"))}applyMessages(t){this.messages=t,this.updateTimeLabelTitles(),this.loopButton.title=t.autoReplay,this.loopButton.setAttribute("aria-label",t.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton()}updateTimeLabels(){const t=this.currentSighting.event,e=qt(t);this.realDurationMs=e!==void 0&&e>0?e:void 0,this.realStartMs=t.time?it(t.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=s>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(t){if(this.realDurationMs===void 0)return V(t);const e=this.currentSighting.timeline.duration,s=e>0&&t<=e?t/e*this.realDurationMs:t;return this.canShowClockTime?St(Mt(this.realStartMs+s)):V(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?V(this.currentSighting.timeline.duration):this.canShowClockTime?St(Mt(this.realStartMs+this.realDurationMs)):V(this.realDurationMs)}}function Mt(n){const t=new Date(n);return{hour:t.getUTCHours(),minute:t.getUTCMinutes(),second:t.getUTCSeconds()}}function St(n){if(n.hour===void 0)return"0:00";const t=e=>String(e).padStart(2,"0");return n.second?`${t(n.hour)}:${t(n.minute??0)}:${t(n.second)}`:`${t(n.hour)}:${t(n.minute??0)}`}function V(n){const t=Math.round(n/1e3),e=Math.floor(t/60),s=t%60;return`${e}:${String(s).padStart(2,"0")}`}const kt="rr0-ufo";function Ne(){customElements.get(kt)||customElements.define(kt,x)}Ne();
404
+ `;class _ extends Error{constructor(t,e,s){super(`${t}: ${e}${s===void 0?"":` (HTTP ${s})`}`),this.kind=t,this.url=e,this.status=s,this.name="SightingFetchError"}}class _t{static async json(t){const e=new URL(t,location.href);if(location.protocol==="https:"&&e.protocol==="http:")throw new _("mixed-content",t);let s;try{s=await fetch(t)}catch{throw await this.diagnose(t,e)}if(!s.ok)throw new _("status",t,s.status);try{return await s.json()}catch{throw new _("malformed",t)}}static async diagnose(t,e){if(e.origin===location.origin)return new _("unreachable",t);try{return await fetch(t,{mode:"no-cors"}),new _("cors",t)}catch{return new _("unreachable",t)}}}const H=[{id:"eye",name:{en:"Naked eye",fr:"Œil nu"},flare:0,projection:"equidistant",stabilization:.97},{id:"rectilinear-lens",name:{en:"Camera, unknown device",fr:"Appareil, modèle inconnu"},projection:"rectilinear",fNumber:8,fNumberRange:{min:2,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:8},apertureBlades:6},{id:"instamatic-126",name:{en:"Instamatic, 126 film",fr:"Instamatic, film 126"},projection:"rectilinear",frame:{widthMm:28,heightMm:28,focalLengthMm:43},fNumber:11,exposureSeconds:1/90,years:{from:1963,to:1988},apertureBlades:5,detailUm:20},{id:"slr-35mm-50",name:{en:"35 mm SLR, 50 mm lens",fr:"Reflex 35 mm, objectif 50 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:50},fNumber:8,fNumberRange:{min:2,max:16},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},years:{from:1959},apertureBlades:6,detailUm:20},{id:"slr-35mm-zoom",name:{en:"35 mm SLR, 70-210 mm zoom",fr:"Reflex 35 mm, zoom 70-210 mm"},projection:"rectilinear",frame:{widthMm:36,heightMm:24,focalLengthMm:135,focalRangeMm:{minMm:70,maxMm:210}},fNumber:8,fNumberRange:{min:4,max:22},exposureSeconds:1/250,exposureRangeSeconds:{min:1/1e3,max:3600},apertureBlades:8,years:{from:1975},detailUm:20},{id:"phone-landscape",name:{en:"Phone, held sideways",fr:"Téléphone, tenu couché"},projection:"rectilinear",frame:{widthMm:7.6,heightMm:5.7,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4},{id:"phone-portrait",name:{en:"Phone, held upright",fr:"Téléphone, tenu debout"},projection:"rectilinear",frame:{widthMm:5.7,heightMm:7.6,focalLengthMm:5.7},fNumber:1.8,exposureSeconds:1/120,exposureRangeSeconds:{min:1/8e3,max:10},years:{from:2007},apertureBlades:void 0,detailUm:1.4}];class b{static get default(){return H[0]}static byId(t){return H.find(e=>e.id===t)??this.default}static UNAIDED_FIELD_DEG=60;static UNAIDED_ASPECT=16/9;static fieldOfViewDeg(t){const e=t.frame;return e?2*Math.atan(e.heightMm/(2*e.focalLengthMm))*180/Math.PI:b.UNAIDED_FIELD_DEG}static aspectOf(t){const e=t.frame;return e?e.widthMm/e.heightMm:b.UNAIDED_ASPECT}static frameWidthPx(t,e){return Math.round(e*b.aspectOf(t))}static availableAt(t){return t===void 0?H:H.filter(e=>!e.years||t>=e.years.from&&(e.years.to===void 0||t<=e.years.to))}static fieldOfViewDegAt(t,e){const s=t.frame;if(!(!s||e<=0))return 2*Math.atan(s.heightMm/(2*e))*180/Math.PI}static focalLengthMmFor(t,e){const s=t.frame;if(!(!s||e<=0||e>=180))return s.heightMm/(2*Math.tan(e*Math.PI/360))}static bladesShowing(t,e){const s=e??t.fNumber;if(s===void 0||t.apertureBlades===void 0)return 0;const i=t.fNumberRange;return!i||i.max<=i.min?1:Math.max(0,Math.min(1,(s-i.min)/(i.max-i.min)))}static LENS_FLARE_ARTIFACTS=1;static flareArtifactsOf(t){return t.flare??b.LENS_FLARE_ARTIFACTS}static starPointsOf(t){const e=t.apertureBlades;return e===void 0||e<3?0:e%2===0?e:e*2}}const Et=Math.PI/180,q=111320;function D(n,t,e,s){const i=q*Math.cos(e*Et),r=(n-e)*q;return{x:(t-s)*i,z:-r}}function L(n,t,e,s){const i=q*Math.cos(e*Et),r=-t,a=n;return{lat:e+r/q,lng:s+a/i}}class F{constructor(t){this.points=t}static STATIONARY_M=5;static of(t){const e=[];for(const s of t.witnessTrack.allKeyframes){const{lat:i,lng:r,headingDeg:a}=s.pose;i===void 0||r===void 0||e.push({t:s.t,lat:i,lng:r,headingDeg:a})}if(e.length===0){const s=t.event.place?.[0];if(!s)return;e.push({t:0,lat:s.lat,lng:s.lng})}return new F(e)}get moved(){return this.spanM>F.STATIONARY_M}get spanM(){const t=this.box,e=(t.south+t.north)/2,s=(t.west+t.east)/2;return Math.max(Math.abs(D(e,t.east,e,t.west).x),Math.abs(D(t.north,s,t.south,s).z))}get center(){const t=this.box;return{lat:(t.south+t.north)/2,lng:(t.west+t.east)/2}}boundsAround(t,e){const{lat:s,lng:i}=this.center,r=Math.max(this.spanM*(1+e),t)/2;return{north:L(0,-r,s,i).lat,south:L(0,r,s,i).lat,east:L(r,0,s,i).lng,west:L(-r,0,s,i).lng}}get box(){const t=this.points.map(s=>s.lat),e=this.points.map(s=>s.lng);return{south:Math.min(...t),north:Math.max(...t),west:Math.min(...e),east:Math.max(...e)}}}class g{constructor(t,e){this.stretches=t,this.rotationPassed=e}static REFERENCE_SPEED_M_PER_S=1.4;static REFERENCE_STEP_HZ=1.9;static CADENCE_EXPONENT=.43;static RISE_M_PER_M_PER_S=.0175;static SWAY_M=.0175;static SLOWEST_WALK_M_PER_S=.2;static FASTEST_WALK_M_PER_S=2.2;static HEAD_ROLL_DEG=1.5;static HEAD_PITCH_DEG=1.25;static HEAD_YAW_DEG=1;static STILL=Object.freeze({eastM:0,northM:0,upM:0,rollDeg:0,pitchDeg:0,yawDeg:0});static of(t){const e=F.of(t);if(!e||e.points.length<2)return;const s=[];let i=0;for(let r=1;r<e.points.length;r++){const a=e.points[r-1],o=e.points[r],h=(o.t-a.t)/1e3;if(h<=0)continue;const l=D(o.lat,o.lng,a.lat,a.lng),d=l.x,c=-l.z,u=Math.hypot(d,c),f=u/h,p=g.stepHzFor(f);s.push({startMs:a.t,endMs:o.t,speedMPerS:f,eastUnit:u>0?d/u:0,northUnit:u>0?c/u:0,stepHz:p,stepsBefore:i,rampsIn:!0,rampsOut:!0}),i+=p*h}for(let r=0;r<s.length;r++){const a=s[r-1],o=s[r+1];s[r].rampsIn=a===void 0||a.stepHz===0,s[r].rampsOut=o===void 0||o.stepHz===0}if(s.some(r=>r.stepHz>0))return new g(s,1-(t.instrument.stabilization??0))}static stepHzFor(t){return t<g.SLOWEST_WALK_M_PER_S||t>g.FASTEST_WALK_M_PER_S?0:g.REFERENCE_STEP_HZ*Math.pow(t/g.REFERENCE_SPEED_M_PER_S,g.CADENCE_EXPONENT)}offsetAt(t){const e=this.stretches.find(h=>t>=h.startMs&&t<=h.endMs);if(!e||e.stepHz===0)return g.STILL;const s=e.stepsBefore+e.stepHz*(t-e.startMs)/1e3,i=2*Math.PI*s,r=this.amplitudeAt(e,t),a=g.SWAY_M*r*Math.cos(i/2),o=r*this.rotationPassed*e.speedMPerS/g.REFERENCE_SPEED_M_PER_S;return{eastM:a*e.northUnit,northM:-a*e.eastUnit,upM:g.RISE_M_PER_M_PER_S*e.speedMPerS*r*Math.cos(i),rollDeg:g.HEAD_ROLL_DEG*o*Math.cos(i/2),pitchDeg:-1.25*o*Math.cos(i),yawDeg:g.HEAD_YAW_DEG*o*Math.cos(i/2)}}amplitudeAt(t,e){const s=1e3/t.stepHz,i=Math.min(s,(t.endMs-t.startMs)/2);if(i<=0)return 1;const r=e-t.startMs,a=t.endMs-e,o=Math.min(t.rampsIn?r/i:1,t.rampsOut?a/i:1,1);return(1-Math.cos(Math.PI*Math.max(o,0)))/2}}class M{static MOON_ANGULAR_WIDTH_DEG=.5237;static CANVAS_WIDTH_PX=640;static CANVAS_HEIGHT_PX=360;static angularWidthDeg(t){return 2*Math.atan(t.sizeM/(2*t.distanceM))*180/Math.PI}static inMoons(t){return t/M.MOON_ANGULAR_WIDTH_DEG}static lerpAngular(t,e,s){if(!(!t||!e))return{widthDeg:t.widthDeg+(e.widthDeg-t.widthDeg)*s,heightDeg:t.heightDeg+(e.heightDeg-t.heightDeg)*s}}static sizeMAt(t,e){return 2*t*Math.tan(e*Math.PI/360)}static distanceMAt(t,e){return t/(2*Math.tan(e*Math.PI/360))}}function Rt(n,t,e){if(!n||!t)return e<1?n:t;const s=((t.azimuthDeg-n.azimuthDeg)%360+540)%360-180;return{azimuthDeg:((n.azimuthDeg+s*e)%360+360)%360,altitudeDeg:n.altitudeDeg+(t.altitudeDeg-n.altitudeDeg)*e}}function Bt(n,t,e){const{bounds:s}=n;return t>=s.x&&t<=s.x+s.width&&e>=s.y&&e<=s.y+s.height}function w(n,t,e){return n+(t-n)*e}function rt(n){const t=/^#([0-9a-f]{6})$/i.exec(n);if(!t)return;const e=parseInt(t[1],16);return[e>>16&255,e>>8&255,e&255]}function Lt(n){return`#${n.map(t=>Math.round(t).toString(16).padStart(2,"0")).join("")}`}function Ft(n,t,e){const s=rt(n),i=rt(t);return!s||!i?e<1?n:t:Lt([w(s[0],i[0],e),w(s[1],i[1],e),w(s[2],i[2],e)])}function Ot(n,t,e){const s={x:w(n.bounds.x,t.bounds.x,e),y:w(n.bounds.y,t.bounds.y,e),width:w(n.bounds.width,t.bounds.width,e),height:w(n.bounds.height,t.bounds.height,e)},i=w(n.angle,t.angle,e),r=w(n.transparency,t.transparency,e),a=w(n.haloScale,t.haloScale,e),o=w(n.blur??0,t.blur??0,e),h=w(n.brightness??0,t.brightness??0,e),l=Ft(n.color,t.color,e),d=M.lerpAngular(n.angular,t.angular,e),c=Rt(n.aim,t.aim,e);return n.kind==="polygon"&&t.kind==="polygon"&&n.points.length===t.points.length?{...n,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:l,angular:d,aim:c,behindCloud:e<1?n.behindCloud:t.behindCloud,points:n.points.map((u,f)=>({x:w(u.x,t.points[f].x,e),y:w(u.y,t.points[f].y,e)}))}:{...e<1?n:t,bounds:s,angle:i,transparency:r,haloScale:a,blur:o,brightness:h,color:l,angular:d,aim:c}}class Z{keyframes=[];order=[];groups=[];addKeyframe(t,e){const s=this.findInsertIndex(t);if(this.keyframes[s]?.t===t){const i=new Set(e.map(r=>r.sourceId));this.keyframes[s]={t,shapes:[...this.keyframes[s].shapes.filter(r=>!i.has(r.sourceId)),...e]}}else this.keyframes.splice(s,0,{t,shapes:[...e]});for(const i of e)this.order.includes(i.sourceId)||this.order.push(i.sourceId)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getKeyframeAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e];return s?.t===t?s:void 0}getShapeAt(t,e){return this.getKeyframeAt(t)?.shapes.find(s=>s.sourceId===e)?.shape}getLatestShapeAt(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return i.shape}}getInterpolatedShapeAt(t,e){const s=this.findShapeAtOrBefore(t,e);if(s?.t===t)return s.shape;const i=this.findShapeAtOrAfter(t,e);return s?i?Ot(s.shape,i.shape,(t-s.t)/(i.t-s.t)):s.shape:i?.shape}findShapeAtOrBefore(t,e){let s=this.findInsertIndex(t);for(this.keyframes[s]?.t!==t&&(s-=1);s>=0;s--){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}findShapeAtOrAfter(t,e){for(let s=this.findInsertIndex(t);s<this.keyframes.length;s++){const i=this.keyframes[s].shapes.find(r=>r.sourceId===e);if(i)return{t:this.keyframes[s].t,shape:i.shape}}}hitTest(t,e,s,i){const r=this.sourceIds;for(let a=r.length-1;a>=0;a--){if(i?.has(r[a]))continue;const o=this.getInterpolatedShapeAt(t,r[a]);if(o&&Bt(o,e,s))return{sourceId:r[a],shape:o}}}removeSource(t){for(let e=this.keyframes.length-1;e>=0;e--){const s=this.keyframes[e].shapes.filter(i=>i.sourceId!==t);s.length===0?this.keyframes.splice(e,1):s.length!==this.keyframes[e].shapes.length&&(this.keyframes[e]={t:this.keyframes[e].t,shapes:s})}this.order=this.order.filter(e=>e!==t),this.removeFromGroup(t)}group(t){if(!(t.length<2)){for(const e of t)this.removeFromGroup(e);this.groups.push([...t])}}ungroup(t){const e=this.groups.findIndex(s=>s.includes(t));e!==-1&&this.groups.splice(e,1)}groupMembers(t){return this.groups.find(e=>e.includes(t))}removeFromGroup(t){const e=this.groups.findIndex(i=>i.includes(t));if(e===-1)return;const s=this.groups[e].filter(i=>i!==t);s.length<2?this.groups.splice(e,1):this.groups[e]=s}bringToFront(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.push(t))}sendToBack(t){const e=this.order.indexOf(t);e!==-1&&(this.order.splice(e,1),this.order.unshift(t))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get sourceIds(){return[...this.order]}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes,order:this.order,groups:this.groups}}static fromJSON(t){const e=new Z;for(const s of t.keyframes)e.addKeyframe(s.t,s.shapes);if(t.order){const s=e.order.filter(i=>!t.order.includes(i));e.order=[...t.order,...s]}return t.groups&&(e.groups=t.groups.map(s=>s.filter(i=>e.order.includes(i))).filter(s=>s.length>=2)),e}}function Nt(n,t,e){return Math.max(t,Math.min(e,n))}function Wt(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function R(n,t,e){return n+(t-n)*e}function zt(n,t,e){return{lat:n.lat===void 0||t.lat===void 0?void 0:R(n.lat,t.lat,e),lng:n.lng===void 0||t.lng===void 0?void 0:R(n.lng,t.lng,e),elevationM:R(n.elevationM,t.elevationM,e),headingDeg:n.headingDeg===void 0||t.headingDeg===void 0?void 0:Wt(n.headingDeg,t.headingDeg,e),pitchDeg:R(n.pitchDeg,t.pitchDeg,e),rollDeg:R(n.rollDeg??0,t.rollDeg??0,e),fovDeg:R(n.fovDeg,t.fovDeg,e),fNumber:n.fNumber,focusDistanceM:n.focusDistanceM}}class O{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,pose:e}:this.keyframes.splice(s,0,{t,pose:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestPoseAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].pose:void 0}getInterpolatedPoseAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.pose;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?zt(s.pose,i.pose,Nt((t-s.t)/(i.t-s.t),0,1)):s.pose:i?.pose}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new O;for(const s of t.keyframes)e.addKeyframe(s.t,s.pose);return e}}function E(n,t,e){return n+(t-n)*e}function Ht(n,t,e){const s=((t-n)%360+540)%360-180;return((n+s*e)%360+360)%360}function Ut(n,t,e){return Math.max(t,Math.min(e,n))}function $t(n,t,e){return{cloudCover:E(n.cloudCover,t.cloudCover,e),cloudDarkness:E(n.cloudDarkness,t.cloudDarkness,e),highCloudCover:n.highCloudCover===void 0||t.highCloudCover===void 0?void 0:E(n.highCloudCover,t.highCloudCover,e),iceCrystalAlignment:n.iceCrystalAlignment===void 0||t.iceCrystalAlignment===void 0?void 0:E(n.iceCrystalAlignment,t.iceCrystalAlignment,e),lowerCloudCover:n.lowerCloudCover===void 0||t.lowerCloudCover===void 0?void 0:E(n.lowerCloudCover,t.lowerCloudCover,e),cloudBaseM:n.cloudBaseM===void 0||t.cloudBaseM===void 0?void 0:E(n.cloudBaseM,t.cloudBaseM,e),precipitationType:e<1?n.precipitationType:t.precipitationType,precipitationIntensity:E(n.precipitationIntensity,t.precipitationIntensity,e),windDirectionDeg:Ht(n.windDirectionDeg,t.windDirectionDeg,e),windSpeed:E(n.windSpeed,t.windSpeed,e),storm:e<1?n.storm:t.storm}}class N{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,weather:e}:this.keyframes.splice(s,0,{t,weather:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}getLatestWeatherAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].weather:void 0}getInterpolatedWeatherAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.weather;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];return s?i?$t(s.weather,i.weather,Ut((t-s.t)/(i.t-s.t),0,1)):s.weather:i?.weather}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new N;for(const s of t.keyframes)e.addKeyframe(s.t,s.weather);return e}}class W{keyframes=[];addKeyframe(t,e){const s=this.findInsertIndex(t);this.keyframes[s]?.t===t?this.keyframes[s]={t,sound:e}:this.keyframes.splice(s,0,{t,sound:e})}clear(){this.keyframes.length=0}removeKeyframeAt(t){const e=this.findInsertIndex(t);this.keyframes[e]?.t===t&&this.keyframes.splice(e,1)}getLatestSoundAt(t){let e=this.findInsertIndex(t);return this.keyframes[e]?.t!==t&&(e-=1),e>=0?this.keyframes[e].sound:void 0}getInterpolatedSoundAt(t){const e=this.findInsertIndex(t),s=this.keyframes[e]?.t===t?this.keyframes[e]:this.keyframes[e-1];if(s?.t===t)return s.sound;const i=this.keyframes[e]?.t===t?void 0:this.keyframes[e];if(!s)return i?.sound;if(!i)return s.sound;const r=(t-s.t)/(i.t-s.t);return this.lerp(s.sound,i.sound,Math.max(0,Math.min(1,r)))}get duration(){return this.keyframes.length===0?0:this.keyframes[this.keyframes.length-1].t}get allKeyframes(){return this.keyframes}toJSON(){return{keyframes:this.keyframes}}static fromJSON(t){const e=new W;for(const s of t.keyframes)e.addKeyframe(s.t,s.sound);return e}lerp(t,e,s){return{kind:s<1?t.kind:e.kind,volume:t.volume+(e.volume-t.volume)*s,pitchHz:t.pitchHz+(e.pitchHz-t.pitchHz)*s,src:s<1?t.src:e.src}}findInsertIndex(t){let e=0,s=this.keyframes.length;for(;e<s;){const i=e+s>>>1;this.keyframes[i].t<t?e=i+1:s=i}return e}}const Xt=100,Gt={kind:"none",volume:0,pitchHz:Xt};class S{constructor(t){this.entries=t}static empty(){return new S(new Map)}get size(){return this.entries.size}at(t){return this.entries.get(t)}paths(){return[...this.entries.keys()]}set(t,e){e.basis==="stated"&&e.rationale===void 0?this.entries.delete(t):this.entries.set(t,e)}clear(t){for(const e of this.entries.keys())(e===t||e.startsWith(`${t}.`))&&this.entries.delete(e)}static strip(t){const e=new S(new Map);return{recording:S.stripValue(t,[],e),provenance:e}}static restore(t,e){if(e.size===0)return t;const s=structuredClone(t);for(const i of e.paths()){const r=e.at(i),a=i.split("."),o=S.holderOf(s,a);if(!o)continue;const h=a[a.length-1],l=o[h];l!==void 0&&(r.of!==void 0&&JSON.stringify(r.of)!==JSON.stringify(l)||(o[h]={value:l,...r.basis==="stated"?{}:{basis:r.basis},...r.rationale===void 0?{}:{rationale:r.rationale}}))}return s}static isWrapper(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)&&"value"in t}static stripValue(t,e,s){return S.isWrapper(t)?(s.set(e.join("."),{basis:t.basis??"stated",...t.rationale===void 0?{}:{rationale:t.rationale},of:t.value}),S.stripValue(t.value,e,s)):Array.isArray(t)?t.map((i,r)=>S.stripValue(i,[...e,String(r)],s)):typeof t=="object"&&t!==null?Object.fromEntries(Object.entries(t).map(([i,r])=>[i,S.stripValue(r,[...e,i],s)])):t}static holderOf(t,e){let s=t;for(const i of e.slice(0,-1)){if(typeof s!="object"||s===null)return;s=s[i]}return typeof s=="object"&&s!==null?s:void 0}}function it(n){if(n.year!==void 0)return Date.UTC(n.year,(n.month??1)-1,n.day??1,n.hour??0,n.minute??0,n.second??0)}function at(n){return(((n.day??0)*24+(n.hour??0))*60+(n.minute??0))*6e4+(n.second??0)*1e3}const Vt=["year","month","day","hour","minute"],Kt=["day","hour","minute"];function ot(n,t){return t.filter(e=>n[e]!==void 0).join(",")}function qt(n){if(n.durationSeconds!==void 0)return n.durationSeconds*1e3;if(!n.time||!n.endTime)return;const t=n.time.year!==void 0&&n.endTime.year!==void 0,e=t?Vt:Kt,s=ot(n.time,e);if(!(s===""||s!==ot(n.endTime,e)))return t?it(n.endTime)-it(n.time):at(n.endTime)-at(n.time)}class J{constructor(t,e,s,i,r,a,o,h,l=[],d,c,u,f=[]){this.event=t,this.timeline=e,this.witnessTrack=s,this.weatherTrack=i,this.soundTrack=r,this.witness=a,this.caseId=o,this.weather=h,this.decor=l,this.weatherSource=d,this.instrumentId=c,this.exposureSeconds=u,this.milestones=f}provenance=S.empty();testimony;get witnessCount(){return 1+this.decor.filter(t=>t.kind==="witness").length}get exposure(){return this.exposureSeconds??this.instrument.exposureSeconds}get instrument(){return b.byId(this.instrumentId)}static create(t,e,s){return new J({eventType:"sighting",time:t,place:e},new Z,new O,new N,new W,s)}}const Yt=0,Zt=0;function T(n,t){const e=n.witnessTrack.getInterpolatedPoseAt(t);if(e)return e;const s=n.event.place?.[0];if(s)return{lat:s.lat,lng:s.lng,elevationM:Yt,headingDeg:void 0,pitchDeg:Zt,fovDeg:b.fieldOfViewDeg(n.instrument)}}function Jt(n,t){return n.soundTrack.getInterpolatedSoundAt(t)??Gt}class jt{constructor(t,e){this.timeline=t,this.onFrame=e}rafId=null;state="stopped";currentT=0;lastWallTime=0;playbackRate=1;loop=!1;onEnded;durationOverrideMs=0;get seekableDuration(){return Math.max(this.timeline.duration,this.durationOverrideMs)}play(){if(this.state==="playing")return;this.currentT>=this.seekableDuration&&(this.currentT=0,this.resolveFrame(0)),this.state="playing",this.lastWallTime=performance.now();const t=()=>{if(this.state!=="playing")return;const e=performance.now();if(this.currentT+=(e-this.lastWallTime)*this.playbackRate,this.lastWallTime=e,this.currentT>=this.seekableDuration){if(this.loop&&this.seekableDuration>0){this.currentT%=this.seekableDuration,this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t);return}this.currentT=this.seekableDuration,this.stop(),this.resolveFrame(this.currentT),this.onEnded?.();return}this.resolveFrame(this.currentT),this.rafId=requestAnimationFrame(t)};this.rafId=requestAnimationFrame(t)}pause(){this.state==="playing"&&(this.state="paused",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null))}stop(){this.state="stopped",this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}seek(t){this.currentT=Math.max(0,Math.min(t,this.seekableDuration)),this.resolveFrame(this.currentT)}get playbackState(){return this.state}get time(){return this.currentT}resolveFrame(t){const e=new Map;for(const s of this.timeline.sourceIds){const i=this.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}this.onFrame(t,e)}}const Tt=["nw","n","ne","e","se","s","sw","w"],Qt=24,U=8,te=3,ee=8;function $(n,t,e){const s=n.x-t.x,i=n.y-t.y,r=Math.cos(e),a=Math.sin(e);return{x:t.x+s*r-i*a,y:t.y+s*a+i*r}}function B(n){return{x:n.x+n.width/2,y:n.y+n.height/2}}const se={nw:{left:!0,top:!0},n:{top:!0},ne:{right:!0,top:!0},e:{right:!0},se:{right:!0,bottom:!0},s:{bottom:!0},sw:{left:!0,bottom:!0},w:{left:!0}};class v{static handlePointsFor(t){const{x:e,y:s,width:i,height:r}=t.bounds,a={nw:{x:e,y:s},n:{x:e+i/2,y:s},ne:{x:e+i,y:s},e:{x:e+i,y:s+r/2},se:{x:e+i,y:s+r},s:{x:e+i/2,y:s+r},sw:{x:e,y:s+r},w:{x:e,y:s+r/2},rotate:{x:e+i/2,y:s-Qt}},o=B(t.bounds),h={};for(const l of Object.keys(a))h[l]=$(a[l],o,t.angle);return h}static hitTestHandle(t,e,s=8,i=[...Tt,"rotate"]){const r=v.handlePointsFor(t);for(const a of i)if(Math.hypot(e.x-r[a].x,e.y-r[a].y)<=s)return a}static HANDLE_DIRECTION_DEG={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315};static resizeAxisFor(t,e){const i=((v.HANDLE_DIRECTION_DEG[t]+e*180/Math.PI)%180+180)%180/45,r=["ew","nwse","ns","nesw"];return r[Math.round(i)%r.length]}static resizeBounds(t,e,s,i){const r=B(t),a=$(i,r,-e),{x:o,y:h,width:l,height:d}=t,c=se[s];let u=o,f=h,p=o+l,m=h+d;return c.left&&(u=Math.min(a.x,p-U)),c.right&&(p=Math.max(a.x,u+U)),c.top&&(f=Math.min(a.y,m-U)),c.bottom&&(m=Math.max(a.y,f+U)),{x:u,y:f,width:p-u,height:m-f}}static resizeShape(t,e,s){if(e==="rotate")throw new Error("resizeShape does not accept the rotate handle");const{width:i,height:r}=t.bounds,a=v.resizeBounds(t.bounds,t.angle,e,s);if(t.kind==="oval")return{...t,bounds:a};const o=i===0?1:a.width/i,h=r===0?1:a.height/r;return{...t,bounds:a,points:t.points.map(l=>({x:l.x*o,y:l.y*h}))}}static rotateShape(t,e){const s=B(t.bounds),i=Math.atan2(e.y-s.y,e.x-s.x)+Math.PI/2;return{...t,angle:i}}static groupBoundsFor(t){const e=Math.min(...t.map(a=>a.x)),s=Math.min(...t.map(a=>a.y)),i=Math.max(...t.map(a=>a.x+a.width)),r=Math.max(...t.map(a=>a.y+a.height));return{x:e,y:s,width:i-e,height:r-s}}static vertexPointsFor(t){const e=B(t.bounds);return t.points.map(s=>$({x:t.bounds.x+s.x,y:t.bounds.y+s.y},e,t.angle))}static hitTestVertex(t,e,s=ee){const i=v.vertexPointsFor(t);let r,a=s;return i.forEach((o,h)=>{const l=Math.hypot(e.x-o.x,e.y-o.y);l<=a&&(a=l,r=h)}),r}static toLocalPoint(t,e){const s=B(t.bounds),i=$(e,s,-t.angle);return{x:i.x-t.bounds.x,y:i.y-t.bounds.y}}static moveVertex(t,e,s){const i=t.points.map((c,u)=>u===e?v.toLocalPoint(t,s):c),r=Math.min(...i.map(c=>c.x)),a=Math.min(...i.map(c=>c.y)),o=Math.max(...i.map(c=>c.x)),h=Math.max(...i.map(c=>c.y)),l={x:t.bounds.x+r,y:t.bounds.y+a,width:o-r,height:h-a},d=i.map(c=>({x:c.x-r,y:c.y-a}));return{...t,bounds:l,points:d}}static insertVertexNear(t,e){const s=v.toLocalPoint(t,e),{points:i}=t;let r=i.length-1,a=1/0;for(let h=0;h<i.length;h++){const l=i[h],d=i[(h+1)%i.length],c=v.distanceToSegment(s,l,d);c<a&&(a=c,r=h)}const o=[...i.slice(0,r+1),s,...i.slice(r+1)];return{...t,points:o}}static distanceToSegment(t,e,s){const i=s.x-e.x,r=s.y-e.y,a=i*i+r*r,o=a===0?0:Math.max(0,Math.min(1,((t.x-e.x)*i+(t.y-e.y)*r)/a)),h=e.x+o*i,l=e.y+o*r;return Math.hypot(t.x-h,t.y-l)}static deleteVertex(t,e){return t.points.length<=te?t:{...t,points:t.points.filter((s,i)=>i!==e)}}}const nt=6,X=nt/2,ie=20,ne=24,re=9,ht=6,ae=7,oe=6,lt=4;class he{constructor(t){this.ctx=t}starPoints=0;roll=0;clear(t,e){this.ctx.clearRect(0,0,t,e)}paintShape(t){this.ctx.save(),this.ctx.globalAlpha=1-t.transparency;const e=t.blur??0;e>0&&(this.ctx.filter=`blur(${(ne*e).toFixed(2)}px)`);const s=t.brightness??0;s>0&&this.paintDazzle(t,s),t.haloScale>0&&this.paintHalo(t),this.paintBase(t),this.ctx.restore(),t.selected&&this.paintSelectionHandles(t)}setStarPoints(t){this.starPoints=Math.max(0,Math.floor(t))}setRoll(t){this.roll=t}paintDazzle(t,e){const{x:s,y:i,width:r,height:a}=t.bounds,o=s+r/2,h=i+a/2,l=Math.max(r,a)/2,d=Math.max(l*(1+re*e),oe*e);if(d<=0)return;this.ctx.save(),this.ctx.globalCompositeOperation="lighter";const c=this.ctx.createRadialGradient(o,h,0,o,h,d);for(let u=0;u<=ht;u++){const f=u/ht,p=1/(1+24*f*f);this.ctx.globalAlpha=1,c.addColorStop(f,this.withAlpha(t.color,p*e*.55))}if(this.ctx.fillStyle=c,this.ctx.beginPath(),this.ctx.arc(o,h,d,0,2*Math.PI),this.ctx.fill(),this.starPoints>0){const u=l*(1+ae*e);this.ctx.lineWidth=Math.max(1,l*.12),this.ctx.lineCap="round";for(let f=0;f<this.starPoints;f++){const p=f/this.starPoints*Math.PI*2+this.roll,m=o+Math.cos(p)*u,y=h+Math.sin(p)*u,k=this.ctx.createLinearGradient(o,h,m,y);k.addColorStop(0,this.withAlpha(t.color,e*.55)),k.addColorStop(.35,this.withAlpha(t.color,e*.22)),k.addColorStop(1,this.withAlpha(t.color,0)),this.ctx.strokeStyle=k,this.ctx.beginPath(),this.ctx.moveTo(o,h),this.ctx.lineTo(m,y),this.ctx.stroke()}}this.ctx.restore()}dazzledFill(t,e){if(e<=0)return t.color;const s=/^#([0-9a-f]{6})$/i.exec(t.color.trim());if(!s)return t.color;const i=Number.parseInt(s[1],16),r=a=>Math.round(a+(255-a)*e);return`rgb(${r(i>>16&255)}, ${r(i>>8&255)}, ${r(i&255)})`}withAlpha(t,e){const s=Math.max(0,Math.min(1,e)),i=/^#([0-9a-f]{6})$/i.exec(t.trim());if(!i)return t;const r=Number.parseInt(i[1],16);return`rgba(${r>>16&255}, ${r>>8&255}, ${r&255}, ${s.toFixed(3)})`}paintBase(t){if(this.ctx.fillStyle=this.dazzledFill(t,t.brightness??0),this.ctx.beginPath(),t.kind==="oval"){const{x:e,y:s,width:i,height:r}=t.bounds,a=i/2,o=r/2;this.ctx.ellipse(e+a,s+o,a,o,t.angle,0,2*Math.PI)}else{const{x:e,y:s,width:i,height:r}=t.bounds;this.ctx.save(),this.ctx.translate(e+i/2,s+r/2),this.ctx.rotate(t.angle),this.ctx.translate(-i/2,-r/2),t.points.forEach((a,o)=>{o===0?this.ctx.moveTo(a.x,a.y):this.ctx.lineTo(a.x,a.y)}),this.ctx.closePath(),this.ctx.restore()}this.ctx.fill()}paintHalo(t){this.ctx.save(),this.ctx.shadowColor=t.color,this.ctx.shadowBlur=ie*t.haloScale,this.paintBase(t),this.ctx.restore()}paintSelectionHandles(t){this.paintHandleFrame(v.handlePointsFor(t),{includeRotate:!0}),t.kind==="polygon"&&this.paintVertexHandles(t)}paintVertexHandles(t){this.ctx.fillStyle="#39f";for(const e of v.vertexPointsFor(t))this.ctx.beginPath(),this.ctx.ellipse(e.x,e.y,lt,lt,0,0,2*Math.PI),this.ctx.fill()}paintMemberOutline(t){this.paintOutline(v.handlePointsFor(t))}paintGroupHandles(t){this.paintHandleFrame(v.handlePointsFor({bounds:t,angle:0}),{includeRotate:!0})}paintOutline(t){this.ctx.strokeStyle="lightgray",this.ctx.beginPath(),this.ctx.moveTo(t.nw.x,t.nw.y),this.ctx.lineTo(t.ne.x,t.ne.y),this.ctx.lineTo(t.se.x,t.se.y),this.ctx.lineTo(t.sw.x,t.sw.y),this.ctx.closePath(),this.ctx.stroke()}paintHandleFrame(t,{includeRotate:e}){this.paintOutline(t),this.ctx.fillStyle="lightgray";for(const s of Tt){const i=t[s];this.ctx.fillRect(i.x-X,i.y-X,nt,nt)}e&&(this.ctx.beginPath(),this.ctx.moveTo(t.n.x,t.n.y),this.ctx.lineTo(t.rotate.x,t.rotate.y),this.ctx.stroke(),this.ctx.beginPath(),this.ctx.ellipse(t.rotate.x,t.rotate.y,X+1,X+1,0,0,2*Math.PI),this.ctx.fill())}}const ct=.05,dt=.08,ut=2,ft=3,le=14,ce=.02;class de{context;voice;voiceToken=0;buffers=new Map;noiseBuffer;crackleBuffer;requested;resume(){if(!this.context){if(typeof AudioContext>"u")return;try{this.context=new AudioContext}catch(t){console.warn("SightingAudio: Web Audio unavailable, the sighting stays silent:",t);return}}this.context.state==="suspended"&&this.context.resume()}setSound(t){if(this.requested=t,!this.context)return;const e=this.keyFor(t);if(!e){this.silence();return}if(this.voice?.key!==e){this.stopVoice();const s=++this.voiceToken;e.startsWith("src:")?this.buildRecordedVoice(t.src,e,s):this.voice=this.buildSynthesizedVoice(t,e)}this.applyTo(this.voice,t)}silence(){this.voiceToken++,this.stopVoice()}dispose(){this.silence(),this.context?.close(),this.context=void 0,this.buffers.clear(),this.noiseBuffer=void 0,this.crackleBuffer=void 0}keyFor(t){if(!(t.volume<=0))return t.src?`src:${t.src}`:t.kind==="none"?void 0:t.kind}applyTo(t,e){const s=this.context;if(!t||!s)return;const i=s.currentTime;t.volumeParam.setTargetAtTime(Math.max(0,Math.min(1,e.volume))*t.gainScale,i,ct);for(const{param:r,ratio:a}of t.pitch)r.setTargetAtTime(Math.min(e.pitchHz*a,s.sampleRate/2-1),i,ct)}buildSynthesizedVoice(t,e){const s=this.context;if(s)switch(t.kind){case"hum":return this.buildHum(s,e);case"whistle":return this.buildWhistle(s,e);case"rumble":return this.buildRumble(s,e);case"crackle":return this.buildCrackle(s,e);default:return}}buildHum(t,e){const s=t.createOscillator();s.type="sawtooth";const i=t.createOscillator();i.type="sawtooth";const r=t.createBiquadFilter();r.type="lowpass",r.Q.value=1;const a=t.createGain();return a.gain.value=0,s.connect(r),i.connect(r),r.connect(a).connect(t.destination),s.start(),i.start(),{key:e,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.16,pitch:[{param:s.frequency,ratio:1},{param:i.frequency,ratio:1.006},{param:r.frequency,ratio:6}]}}buildWhistle(t,e){const s=t.createOscillator();s.type="sine";const i=t.createOscillator();i.type="sine",i.frequency.value=5;const r=t.createGain(),a=t.createGain();return a.gain.value=0,i.connect(r).connect(s.frequency),s.connect(a).connect(t.destination),s.start(),i.start(),{key:e,nodes:[r,a],sources:[s,i],volumeParam:a.gain,gainScale:.22,pitch:[{param:s.frequency,ratio:1},{param:r.gain,ratio:.012}]}}buildRumble(t,e){const s=t.createBufferSource();s.buffer=this.brownNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="lowpass",i.Q.value=.8;const r=t.createGain();return r.gain.value=0,s.connect(i).connect(r).connect(t.destination),s.start(),{key:e,nodes:[i,r],sources:[s],volumeParam:r.gain,gainScale:1.4,pitch:[{param:i.frequency,ratio:1}]}}buildCrackle(t,e){const s=t.createBufferSource();s.buffer=this.whiteNoise(t),s.loop=!0;const i=t.createBiquadFilter();i.type="bandpass",i.Q.value=1.2;const r=t.createGain();r.gain.value=0;const a=t.createBufferSource();a.buffer=this.crackleEnvelope(t),a.loop=!0;const o=t.createGain();return o.gain.value=0,a.connect(o).connect(r.gain),s.connect(i).connect(r).connect(t.destination),s.start(),a.start(),{key:e,nodes:[i,r,o],sources:[s,a],volumeParam:o.gain,gainScale:.9,pitch:[{param:i.frequency,ratio:4}]}}async buildRecordedVoice(t,e,s){const i=this.context;if(!i)return;let r;try{r=await this.getBuffer(t)}catch(h){console.warn("SightingAudio: the recorded sound failed to load, staying silent:",h);return}if(s!==this.voiceToken||!this.context)return;const a=i.createBufferSource();a.buffer=r,a.loop=!0;const o=i.createGain();o.gain.value=0,a.connect(o).connect(i.destination),a.start(),this.voice={key:e,nodes:[o],sources:[a],volumeParam:o.gain,gainScale:1,pitch:[]},this.requested&&this.applyTo(this.voice,this.requested)}async getBuffer(t){const e=this.context;if(!e)throw new Error("SightingAudio: AudioContext not ready — resume() must be called first");let s=this.buffers.get(t);return s||(s=fetch(t).then(i=>{if(!i.ok)throw new Error(`Audio fetch failed (${i.status}): ${t}`);return i.arrayBuffer()}).then(i=>e.decodeAudioData(i)),this.buffers.set(t,s)),s}whiteNoise(t){if(!this.noiseBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ut),t.sampleRate),s=e.getChannelData(0);for(let i=0;i<s.length;i++)s[i]=Math.random()*2-1;this.noiseBuffer=e}return this.noiseBuffer}brownNoise(t){const e=t.createBuffer(1,Math.floor(t.sampleRate*ut),t.sampleRate),s=e.getChannelData(0);let i=0,r=0;for(let a=0;a<s.length;a++)i=(i+.02*(Math.random()*2-1))/1.02,s[a]=i,r=Math.max(r,Math.abs(i));if(r>0)for(let a=0;a<s.length;a++)s[a]/=r;return e}crackleEnvelope(t){if(!this.crackleBuffer){const e=t.createBuffer(1,Math.floor(t.sampleRate*ft),t.sampleRate),s=e.getChannelData(0),i=Math.max(1,Math.floor(ce*t.sampleRate)),r=Math.round(ft*le);for(let a=0;a<r;a++){const o=Math.floor(Math.random()*s.length),h=.4+Math.random()*.6;for(let l=0;l<i&&o+l<s.length;l++)s[o+l]=Math.max(s[o+l],h*(1-l/i))}this.crackleBuffer=e}return this.crackleBuffer}stopVoice(){const t=this.voice,e=this.context;if(this.voice=void 0,!t||!e)return;const s=e.currentTime;t.volumeParam.cancelScheduledValues(s),t.volumeParam.setTargetAtTime(0,s,dt/3);const i=s+dt;for(const r of t.sources){r.onended=()=>{r.disconnect();for(const a of t.nodes)a.disconnect()};try{r.stop(i)}catch{r.disconnect();for(const a of t.nodes)a.disconnect()}}}}function Y(n){return[...n].sort((t,e)=>t.t-e.t)}function pt(n,t){let e;for(const s of Y(n)){if(s.t>t)break;e=s}return e}const Q=Math.PI/180,G=180/Math.PI;class I{constructor(t,e,s){this.kind=t,this.canvasHeightPx=e,this.fovDeg=s;const i=s*Q/2;this.focalPx=t==="equidistant"?e/2/i:e/2/Math.tan(i)}focalPx;static of(t,e,s){return new I(t.projection,e,s)}degToPx(t){const e=t*Q;return this.kind==="equidistant"?this.focalPx*e:2*this.focalPx*Math.tan(e/2)}pxToDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:2*Math.atan(t/(2*this.focalPx))*G}angleDegToRadiusPx(t){const e=t*Q;return this.kind==="equidistant"?this.focalPx*e:this.focalPx*Math.tan(e)}halfWidthAngleDeg(t){return this.radiusPxToAngleDeg(this.canvasHeightPx*t/2)}radiusPxToAngleDeg(t){return this.kind==="equidistant"?t/this.focalPx*G:Math.atan(t/this.focalPx)*G}ofBounds(t){return{widthDeg:this.pxToDeg(t.width),heightDeg:this.pxToDeg(t.height)}}toBoundsSize(t){return{width:this.degToPx(t.widthDeg),height:this.degToPx(t.heightDeg)}}widthPx(t){return this.degToPx(M.angularWidthDeg(t))}}class C{static fovOf(t,e){return T(t,e)?.fovDeg??b.fieldOfViewDeg(t.instrument)}static toAngular(t){this.eachKeyframe(t,(e,s)=>({...e,angular:s.ofBounds(e.bounds)}))}static toBounds(t){this.eachKeyframe(t,(e,s)=>{if(!e.angular)return e;const{width:i,height:r}=s.toBoundsSize(e.angular),a={x:e.bounds.x+(e.bounds.width-i)/2,y:e.bounds.y+(e.bounds.height-r)/2,width:i,height:r};if(e.kind!=="polygon")return{...e,bounds:a};const o=e.bounds.width===0?1:i/e.bounds.width,h=e.bounds.height===0?1:r/e.bounds.height;return{...e,bounds:a,points:e.points.map(l=>({x:l.x*o,y:l.y*h}))}})}static toAim(t){this.eachKeyframe(t,(e,s,i)=>{if(i?.headingDeg===void 0)return e;const r=this.frameCentre(t),a=s.radiusPxToAngleDeg(e.bounds.x+e.bounds.width/2-r.x),o=s.radiusPxToAngleDeg(r.y-(e.bounds.y+e.bounds.height/2));return{...e,aim:{azimuthDeg:((i.headingDeg+a)%360+360)%360,altitudeDeg:i.pitchDeg+o}}})}static toPosition(t){this.eachKeyframe(t,(e,s,i)=>{if(!e.aim||i?.headingDeg===void 0)return e;const r=this.frameCentre(t),a=this.shortestArc(e.aim.azimuthDeg-i.headingDeg),o=e.aim.altitudeDeg-i.pitchDeg,h=r.x+this.offAxisPx(s,a)-e.bounds.width/2,l=r.y-this.offAxisPx(s,o)-e.bounds.height/2;return{...e,bounds:{...e.bounds,x:h,y:l}}})}static frameCentre(t){return{x:b.frameWidthPx(t.instrument,M.CANVAS_HEIGHT_PX)/2,y:M.CANVAS_HEIGHT_PX/2}}static shortestArc(t){return(t%360+540)%360-180}static offAxisPx(t,e){return Math.abs(e)>=C.OFF_CANVAS_DEG?Math.sign(e)*C.OFF_CANVAS_PX:t.angleDegToRadiusPx(e)}static OFF_CANVAS_DEG=89;static OFF_CANVAS_PX=1e5;static reproject(t,e,s){const i=b.frameWidthPx(e,M.CANVAS_HEIGHT_PX)/2,r=b.frameWidthPx(t.instrument,M.CANVAS_HEIGHT_PX)/2,a=M.CANVAS_HEIGHT_PX/2;for(const o of[...t.timeline.allKeyframes]){const h=this.fovOf(t,o.t),l=s?.get(o.t)??h,d=I.of(e,M.CANVAS_HEIGHT_PX,l),c=I.of(t.instrument,M.CANVAS_HEIGHT_PX,h);t.timeline.addKeyframe(o.t,o.shapes.map(u=>{const{bounds:f}=u.shape,p=f.x+f.width/2-i,m=f.y+f.height/2-a,y=Math.hypot(p,m),k=y===0?1:c.angleDegToRadiusPx(d.radiusPxToAngleDeg(y))/y,z={...f,x:r+p*k-f.width/2,y:a+m*k-f.height/2};return{...u,shape:{...u.shape,bounds:z}}}))}this.toBounds(t)}static eachKeyframe(t,e){const{timeline:s}=t;for(const i of[...s.allKeyframes]){const r=T(t,i.t),a=this.fovOf(t,i.t),o=I.of(t.instrument,M.CANVAS_HEIGHT_PX,a);s.addKeyframe(i.t,i.shapes.map(h=>({...h,shape:e(h.shape,o,r)})))}}}function ue(n){return S.restore(fe(n),n.provenance)}function fe(n){return C.toAngular(n),C.toAim(n),{version:1,time:n.event.time,endTime:n.event.endTime,durationSeconds:n.event.durationSeconds,utcOffsetHours:n.event.utcOffsetHours,timeZone:n.event.timeZone,place:n.event.place,witness:n.witness,testimony:n.testimony,caseId:n.caseId,description:n.event.description,tags:n.event.tags,timeline:n.timeline.toJSON(),witnessTrack:n.witnessTrack.toJSON(),weatherTrack:n.weatherTrack.toJSON(),soundTrack:n.soundTrack.toJSON(),weather:n.weather,decor:n.decor,milestones:n.milestones.length>0?n.milestones:void 0,weatherSource:n.weatherSource,instrument:n.instrumentId,exposureSeconds:n.exposureSeconds}}function pe(n){const{recording:t,provenance:e}=S.strip(n),s=me(t);return s.provenance=e,s}function me(n){const t=new J({eventType:"sighting",time:n.time,endTime:n.endTime,durationSeconds:n.durationSeconds,utcOffsetHours:n.utcOffsetHours,timeZone:n.timeZone,place:n.place,description:n.description,tags:n.tags},Z.fromJSON(n.timeline),n.witnessTrack?O.fromJSON(n.witnessTrack):new O,n.weatherTrack?N.fromJSON(n.weatherTrack):new N,n.soundTrack?W.fromJSON(n.soundTrack):new W,n.witness,n.caseId,n.weather,n.decor??[],n.weatherSource,n.instrument,n.exposureSeconds??n.witnessTrack?.keyframes.map(e=>e.pose.exposureSeconds).find(e=>e!==void 0),Y(n.milestones??[]));return t.testimony=n.testimony,C.toBounds(t),C.toPosition(t),t}function ge(n,t){for(const e of n){const s=e.toLowerCase().split("-")[0];if(t.includes(s))return s}return"en"}class mt{static preferencesFor(t){const e=navigator.languages??[],s=this.declaredFor(t);return s?[s,...e]:e}static declaredFor(t){for(let e=t;e;){const s=e.closest("[lang]")?.getAttribute("lang")?.trim();if(s)return s;const i=e.getRootNode();e=i instanceof ShadowRoot?i.host:void 0}}}class P{constructor(t){this.preferences=t}read(t){if(t===void 0||typeof t=="string")return t===""?void 0:t;for(const s of this.preferences){const i=t[s]??t[P.base(s)];if(i)return i}const e=new Set(this.preferences.map(s=>P.base(s)));for(const[s,i]of Object.entries(t))if(i&&e.has(P.base(s)))return i;return Object.values(t).find(s=>s)??void 0}write(t,e,s){const i=e?.trim()?e.trim():void 0;if(t===void 0||typeof t=="string")return i;const r={...t},a=Object.keys(r).find(o=>o===s)??Object.keys(r).find(o=>P.base(o)===P.base(s))??s;return i===void 0?delete r[a]:r[a]=i,Object.keys(r).length===0?void 0:r}static base(t){return t.toLowerCase().split("-")[0]}}const ye="modulepreload",we=function(n,t){return new URL(n,t).href},gt={},yt=function(t,e,s){let i=Promise.resolve();if(e&&e.length>0){let a=function(d){return Promise.all(d.map(c=>Promise.resolve(c).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};const o=document.getElementsByTagName("link"),h=document.querySelector("meta[property=csp-nonce]"),l=h?.nonce||h?.getAttribute("nonce");i=a(e.map(d=>{if(d=we(d,s),d in gt)return;gt[d]=!0;const c=d.endsWith(".css"),u=c?'[rel="stylesheet"]':"";if(!!s)for(let m=o.length-1;m>=0;m--){const y=o[m];if(y.href===d&&(!c||y.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${d}"]${u}`))return;const p=document.createElement("link");if(p.rel=c?"stylesheet":ye,c||(p.as="script"),p.crossOrigin="",p.href=d,l&&p.setAttribute("nonce",l),document.head.appendChild(p),c)return new Promise((m,y)=>{p.addEventListener("load",m),p.addEventListener("error",()=>y(new Error(`Unable to preload CSS for ${d}`)))})}))}function r(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return i.then(a=>{for(const o of a||[])o.status==="rejected"&&r(o.reason);return t().catch(r)})},be=["en","fr"],ve={en:()=>yt(()=>Promise.resolve().then(()=>Me),void 0,import.meta.url).then(n=>n.ufoMessages_en),fr:()=>yt(()=>import("./UfoMessages_fr-BJPYHN17.js"),[],import.meta.url).then(n=>n.ufoMessages_fr)};function xe(n){return ve[n]()}const Pt={play:"Play",pause:"Pause",noDuration:"No observation duration",autoReplay:"Auto-replay",currentPosition:"Current position",duration:"Duration",switchToElapsed:"click to show elapsed time",switchToClockTime:"click to show the time of day",fullscreen:"Fullscreen",exitFullscreen:"Exit fullscreen",showWitnessMap:"Show where the witness was",hideWitnessMap:"Hide where the witness was",mapImageryUnavailable:"Aerial imagery unavailable",showMilestones:"Show the account's moments",hideMilestones:"Hide the account's moments",witnessHere:"The witness, here",decorHere:"Scenery"},Me=Object.freeze(Object.defineProperty({__proto__:null,ufoMessages_en:Pt},Symbol.toStringTag,{value:"Module"}));function Se(n,t){const e=n.track,s={eastM:n.eastM,northM:n.northM,altitudeM:0,headingDeg:n.headingDeg};if(!e||e.length===0)return s;if(e.length===1||t<=e[0].t)return wt(e[0],n);const i=e[e.length-1];if(t>=i.t)return wt(i,n);for(let r=0;r<e.length-1;r++){const a=e[r],o=e[r+1];if(t<a.t||t>o.t)continue;const h=o.t-a.t,l=h===0?0:(t-a.t)/h;return{eastM:a.eastM+(o.eastM-a.eastM)*l,northM:a.northM+(o.northM-a.northM)*l,altitudeM:(a.altitudeM??0)+((o.altitudeM??0)-(a.altitudeM??0))*l,headingDeg:a.headingDeg??n.headingDeg}}return s}function wt(n,t){return{eastM:n.eastM,northM:n.northM,altitudeM:n.altitudeM??0,headingDeg:n.headingDeg??t.headingDeg}}const At=Math.PI/180,ke=180/Math.PI,Ee=256,Te=19;function Pe(n,t){return 156543.03392*Math.cos(n*At)/2**t}function Ae(n,t){return Pe(n,t)*Ee}function Ie(n,t){let e=0;for(;e<Te&&Ae(n,e+1)>=t;)e++;return e}function K(n){const t=n*At;return(1-Math.log(Math.tan(t)+1/Math.cos(t))/Math.PI)/2}function De(n,t,e){const s=2**e;return{x:(n+180)/360*s,y:K(t)*s}}function Ce(n,t,e){const s=K(n.north),i=K(n.south);return{x:(t-n.west)/(n.east-n.west),y:(K(e)-s)/(i-s)}}function _e(n,t,e){const{x:s,y:i}=De(n,t,e);return{x:Math.floor(s),y:Math.floor(i),z:e}}function Re(n){const t=2**n.z,e=n.x/t*360-180,s=(n.x+1)/t*360-180,i=bt(n.y,t);return{south:bt(n.y+1,t),north:i,west:e,east:s}}function bt(n,t){const e=n/t;return Math.atan(Math.sinh(Math.PI*(1-2*e)))*ke}function Be(n,t,e,s){const i=_e(n,t,e),r=Math.floor(s/2),a=[];for(let l=-r;l<=r;l++)for(let d=-r;d<=r;d++)a.push({x:i.x+d,y:i.y+l,z:e});const o=a.map(Re),h={south:Math.min(...o.map(l=>l.south)),north:Math.max(...o.map(l=>l.north)),west:Math.min(...o.map(l=>l.west)),east:Math.max(...o.map(l=>l.east))};return{tiles:a,bounds:h}}class A{constructor(t){this.ctx=t}static SCALE_STEPS_M=[5e3,2e3,1e3,500,200,100,50,20,10,5];static MARKER_RADIUS_PX=8.5;static ON_MARKER_M=3;targets=[];hitTest(t,e){let s,i=1/0;for(const r of this.targets){const a=Math.hypot(r.x-t,r.y-e);a>r.radius||a>i||(s=r,i=a)}return s}get width(){return this.ctx.canvas.width}get height(){return this.ctx.canvas.height}paint(t){const{ctx:e}=this;e.save(),this.targets=[],e.clearRect(0,0,this.width,this.height),this.paintGround(t),this.paintNight(t.nightFraction),this.paintPath(t),t.position?(this.paintCone(t,t.position),this.paintDecor(t),this.paintMarkers(t),this.paintWitness(t,t.position)):(this.paintDecor(t),this.paintMarkers(t)),this.paintNorth(),this.paintFooter(t.bounds,t.attribution),e.restore()}toCanvas(t,e,s){const i=Ce(t,s,e);return{x:i.x*this.width,y:i.y*this.height}}paintGround(t){const{ctx:e}=this;e.fillStyle="#1d2321",e.fillRect(0,0,this.width,this.height);const s=t.imagery;if(!s)return;const i=this.toCanvas(t.bounds,s.bounds.north,s.bounds.west),r=this.toCanvas(t.bounds,s.bounds.south,s.bounds.east);e.drawImage(s.source,i.x,i.y,r.x-i.x,r.y-i.y)}paintNight(t=0){if(t<=0)return;const{ctx:e}=this;e.save(),e.fillStyle=`rgba(6, 12, 30, ${Math.min(t,1)*.78})`,e.fillRect(0,0,this.width,this.height),e.restore()}paintDecor(t){const{ctx:e}=this;for(const s of t.decor){const i=this.toCanvas(t.bounds,s.lat,s.lng);if(s.label&&this.targets.push({kind:"decor",label:s.label,lat:s.lat,lng:s.lng,x:i.x,y:i.y,radius:7}),e.beginPath(),e.rect(i.x-3.5,i.y-3.5,7,7),e.fillStyle="rgba(120, 220, 255, 0.85)",e.fill(),e.lineWidth=1.5,e.strokeStyle="rgba(0, 0, 0, 0.7)",e.stroke(),s.headingDeg===void 0)continue;const r=(s.headingDeg-90)*Math.PI/180;e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+Math.cos(r)*10,i.y+Math.sin(r)*10),e.lineWidth=3,e.strokeStyle="rgba(0, 0, 0, 0.6)",e.stroke(),e.lineWidth=1.5,e.strokeStyle="rgba(120, 220, 255, 0.9)",e.stroke()}}paintPath(t){if(!t.path.moved)return;const{ctx:e}=this,s=t.path.points.map(i=>this.toCanvas(t.bounds,i.lat,i.lng));e.beginPath(),e.moveTo(s[0].x,s[0].y);for(const i of s.slice(1))e.lineTo(i.x,i.y);e.lineJoin="round",e.lineCap="round",e.strokeStyle="rgba(0, 0, 0, 0.65)",e.lineWidth=5,e.stroke(),e.strokeStyle="rgba(255, 255, 255, 0.9)",e.lineWidth=2,e.stroke()}paintCone(t,e){const{headingDeg:s,coneHalfAngleDeg:i}=t;if(s===void 0||i===void 0)return;const{ctx:r}=this,a=this.toCanvas(t.bounds,e.lat,e.lng),o=Math.hypot(this.width,this.height),h=(s-90)*Math.PI/180,l=i*Math.PI/180;r.save(),r.beginPath(),r.moveTo(a.x,a.y),r.arc(a.x,a.y,o,h-l,h+l),r.closePath();const d=r.createRadialGradient(a.x,a.y,0,a.x,a.y,o);d.addColorStop(0,"rgba(255, 224, 130, 0.55)"),d.addColorStop(.35,"rgba(255, 224, 130, 0.22)"),d.addColorStop(1,"rgba(255, 224, 130, 0)"),r.fillStyle=d,r.fill(),r.restore()}paintMarkers(t){const{ctx:e}=this;e.textAlign="center",e.textBaseline="middle",e.font="bold 10px sans-serif";for(const s of[...t.markers].sort((i,r)=>Number(i.current)-Number(r.current))){const i=this.toCanvas(t.bounds,s.lat,s.lng);this.targets.push({kind:"milestone",label:s.note?`${s.label} — ${s.note}`:s.label,lat:s.lat,lng:s.lng,t:s.t,x:i.x,y:i.y,radius:A.MARKER_RADIUS_PX}),e.beginPath(),e.arc(i.x,i.y,A.MARKER_RADIUS_PX,0,Math.PI*2),e.fillStyle=s.current?"rgba(255, 224, 130, 0.95)":"rgba(0, 0, 0, 0.55)",e.fill(),e.lineWidth=2,e.strokeStyle="rgba(255, 255, 255, 0.9)",e.stroke(),e.fillStyle=s.current?"#1d2321":"#fff",e.fillText(s.label,i.x,i.y)}}paintWitness(t,e){const{ctx:s}=this,i=this.toCanvas(t.bounds,e.lat,e.lng);t.witnessLabel&&this.targets.push({kind:"witness",label:t.witnessLabel,lat:e.lat,lng:e.lng,x:i.x,y:i.y,radius:8});const r=t.markers.some(a=>{if(!a.current)return!1;const o=D(a.lat,a.lng,e.lat,e.lng);return Math.hypot(o.x,o.z)<=A.ON_MARKER_M});s.beginPath(),s.arc(i.x,i.y,r?A.MARKER_RADIUS_PX+2.5:5.5,0,Math.PI*2),r||(s.fillStyle="#ff5a3c",s.fill()),s.lineWidth=4,s.strokeStyle="rgba(0, 0, 0, 0.5)",s.stroke(),s.lineWidth=r?3:2.5,s.strokeStyle=r?"#ff5a3c":"#fff",s.stroke()}paintNorth(){const{ctx:t}=this,e=this.width-14,s=16;t.beginPath(),t.moveTo(e,s-9),t.lineTo(e-4.5,s+5),t.lineTo(e,s+2),t.lineTo(e+4.5,s+5),t.closePath(),t.fillStyle="rgba(255, 255, 255, 0.92)",t.strokeStyle="rgba(0, 0, 0, 0.6)",t.lineWidth=1,t.fill(),t.stroke(),t.font="bold 9px sans-serif",t.textAlign="center",t.textBaseline="top",t.strokeText("N",e,s+6),t.fillText("N",e,s+6)}paintFooter(t,e){const{ctx:s}=this;s.font="9px sans-serif",s.textBaseline="bottom";const i=e?this.wrap(e,this.width-20):[];let r=this.height-4;s.textAlign="right";for(const f of i.slice().reverse())this.paintOutlinedText(f,this.width-6,r),r-=11;const a=(t.south+t.north)/2,h=Math.abs(D(a,t.east,a,t.west).x)/this.width,l=A.SCALE_STEPS_M.find(f=>f/h<=this.width/3);if(l===void 0)return;const d=l/h,c=8,u=r-3;s.lineWidth=3,s.strokeStyle="rgba(0, 0, 0, 0.6)",s.beginPath(),s.moveTo(c,u),s.lineTo(c+d,u),s.stroke(),s.lineWidth=1.5,s.strokeStyle="rgba(255, 255, 255, 0.95)",s.stroke(),s.textAlign="left",this.paintOutlinedText(l>=1e3?`${l/1e3} km`:`${l} m`,c,u-3)}paintOutlinedText(t,e,s){const{ctx:i}=this;i.lineWidth=3,i.strokeStyle="rgba(0, 0, 0, 0.75)",i.strokeText(t,e,s),i.fillStyle="#fff",i.fillText(t,e,s)}wrap(t,e){const s=[];let i="";for(const r of t.split(" ")){const a=i?`${i} ${r}`:r;i&&this.ctx.measureText(a).width>e?(s.push(i),i=r):i=a}return i&&s.push(i),s}}const tt=256,vt=3;async function Le(n,t,e,s){const i=(n.south+n.north)/2,r=(n.west+n.east)/2,a=Math.abs(D(i,n.east,i,n.west).x),o=Math.abs(D(n.north,r,n.south,r).z),h=Ie(i,Math.max(a,o)/2),{tiles:l,bounds:d}=Be(r,i,h,vt),c=vt*tt,u=document.createElement("canvas");u.width=c,u.height=c;const f=u.getContext("2d");if(!f)throw new Error("2D canvas context unavailable");const p=l[0];await Promise.all(l.map(async k=>{const z=e(k),j=await s(z);if(!j.ok)throw new Error(`Imagery tile fetch failed (${j.status}): ${z}`);const It=await createImageBitmap(await j.blob());f.drawImage(It,(k.x-p.x)*tt,(k.y-p.y)*tt)}));const m=document.createElement("canvas");m.width=t.width,m.height=t.height;const y=m.getContext("2d");if(!y)throw new Error("2D canvas context unavailable");return y.drawImage(u,0,0,c,c,0,0,t.width,t.height),{source:m,width:t.width,height:t.height,bounds:d}}class Fe{attribution="Imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community";fetchImpl;tileUrlTemplate;constructor(t={}){this.fetchImpl=t.fetchImpl??fetch.bind(globalThis),this.tileUrlTemplate=t.tileUrlTemplate??"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}async getImageryTexture(t,e){return Le(t,e,s=>this.tileUrlTemplate.replace("{z}",String(s.z)).replace("{y}",String(s.y)).replace("{x}",String(s.x)),this.fetchImpl)}}function Oe(){return new Fe}const xt=new Set,et="show-witness-map",st="hide-milestones";class x extends HTMLElement{static get observedAttributes(){return["src",et,st]}shadow;stageElement;canvas;canvasRenderer;tooltip;toolbar;playPauseButton;loopButton;static WITNESS_MAP_MIN_SPAN_M=400;static WITNESS_MAP_MARGIN=.6;static WITNESS_MAP_IMAGERY_PX=768;fullscreenButton;cornerButtons;witnessMapButton;milestonesButton;witnessMapPanel;witnessMapCanvas;witnessMapRenderer;seekInput;milestoneMarks;milestoneCaption;timeStartLabel;timeEndLabel;witnessPath;witnessMapBounds;witnessMapImagery;witnessMapImageryRequested=!1;witnessMapImageryCredit;witnessMapImageryFailed=!1;creditShownExternally=!1;lookYawDeg=0;lookPitchDeg=0;milestonesShown=!0;currentSighting=J.create();sightingAudio=new de;soundPreview;player;loopEnabled=!0;playbackBeforeClick;highlightedSourceIds=new Set;occludedSourceIds=xt;enableClickToPlay=!0;fullscreenTarget;messages=Pt;realDurationMs;realStartMs;showClockTime=!0;handleFullscreenChange=()=>{this.updateFullscreenButton(),this.resizeWitnessMap()};witnessMapResizeObserver=typeof ResizeObserver>"u"?void 0:new ResizeObserver(()=>this.resizeWitnessMap());resizeWitnessMap(){this.witnessMapPanel.hidden||(this.sizeWitnessMapCanvas(),this.paintWitnessMap(this.currentTime))}simulatedFullscreen=!1;styleBeforeSimulatedFullscreen;bodyOverflowBeforeSimulatedFullscreen;handleSimulatedFullscreenKey=t=>{t.key==="Escape"&&this.exitSimulatedFullscreen()};handlePointerMove=t=>{const e=this.canvasPointFromEvent(t),s=e&&this.shapeAt(e.x,e.y),i=this.said.read(s?.shape.title);if(!i){this.tooltip.hidden=!0;return}this.tooltip.textContent=i,this.tooltip.hidden=!1;const r=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-r.left+12}px`,this.tooltip.style.top=`${t.clientY-r.top+12}px`};handleWitnessMapPointerMove=t=>{const e=this.witnessMapTargetFrom(t);if(!e){this.tooltip.hidden=!0,this.witnessMapCanvas.style.cursor="default";return}this.witnessMapCanvas.style.cursor="pointer",this.tooltip.textContent=e.label,this.tooltip.hidden=!1;const s=this.stageElement.getBoundingClientRect();this.tooltip.style.left=`${t.clientX-s.left+12}px`,this.tooltip.style.top=`${t.clientY-s.top+12}px`};handleWitnessMapClick=t=>{const e=this.witnessMapTargetFrom(t);if(e){if(e.kind==="milestone"&&e.t!==void 0){this.player.seek(e.t);return}this.dispatchEvent(new CustomEvent("lookat",{detail:{lat:e.lat,lng:e.lng,kind:e.kind},bubbles:!0,composed:!0}))}};witnessMapTargetFrom(t){const e=this.witnessMapCanvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return this.witnessMapRenderer.hitTest((t.clientX-e.left)/e.width*this.witnessMapCanvas.width,(t.clientY-e.top)/e.height*this.witnessMapCanvas.height)}handlePointerLeave=()=>{this.tooltip.hidden=!0};constructor(){super(),this.shadow=this.attachShadow({mode:"open"});const t=document.createElement("template");t.innerHTML=`<style>${Ct}</style>${Dt}`,this.shadow.appendChild(t.content.cloneNode(!0)),this.stageElement=this.shadow.getElementById("stage"),this.canvas=this.shadow.getElementById("canvas"),this.canvasRenderer=new he(this.canvas.getContext("2d")),this.tooltip=this.shadow.getElementById("tooltip"),this.toolbar=this.shadow.getElementById("toolbar"),this.playPauseButton=this.shadow.getElementById("play-pause"),this.loopButton=this.shadow.getElementById("loop"),this.fullscreenButton=this.shadow.getElementById("fullscreen"),this.cornerButtons=this.shadow.getElementById("corner-buttons"),this.witnessMapButton=this.shadow.getElementById("witness-map"),this.milestonesButton=this.shadow.getElementById("milestones"),this.witnessMapPanel=this.shadow.getElementById("witness-map-panel"),this.witnessMapCanvas=this.shadow.getElementById("witness-map-canvas"),this.witnessMapRenderer=new A(this.witnessMapCanvas.getContext("2d")),this.seekInput=this.shadow.getElementById("seek"),this.milestoneMarks=this.shadow.getElementById("milestone-marks"),this.milestoneCaption=this.shadow.getElementById("milestone-caption"),this.timeStartLabel=this.shadow.getElementById("time-start"),this.timeEndLabel=this.shadow.getElementById("time-end");for(const e of[this.timeStartLabel,this.timeEndLabel])e.addEventListener("click",()=>this.toggleTimeDisplay()),e.addEventListener("keydown",s=>{s.key!=="Enter"&&s.key!==" "||(s.preventDefault(),this.toggleTimeDisplay())});this.fullscreenTarget=this.stageElement,this.playPauseButton.addEventListener("click",()=>this.togglePlayPause()),this.loopButton.addEventListener("click",()=>this.toggleLoop()),this.fullscreenButton.addEventListener("click",()=>this.toggleFullscreen()),this.witnessMapButton.addEventListener("click",()=>this.toggleWitnessMap()),this.milestonesButton.addEventListener("click",()=>this.toggleMilestones()),this.witnessMapCanvas.addEventListener("pointermove",this.handleWitnessMapPointerMove),this.witnessMapCanvas.addEventListener("pointerleave",this.handlePointerLeave),this.witnessMapCanvas.addEventListener("click",this.handleWitnessMapClick),this.seekInput.addEventListener("input",()=>this.player.seek(Number(this.seekInput.value))),this.canvas.addEventListener("click",e=>{this.enableClickToPlay&&(e.detail<=1&&(this.playbackBeforeClick={state:this.playbackState,time:this.currentTime}),this.togglePlayPause())}),this.canvas.addEventListener("dblclick",e=>{this.enableClickToPlay&&(e.preventDefault(),this.restorePlayback(),this.toggleFullscreen())}),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerleave",this.handlePointerLeave),document.addEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.observe(this.witnessMapPanel),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton(),this.refresh(),this.loadLocaleMessages()}connectedCallback(){this.saidTexts=void 0;const t=this.getAttribute("src");t&&this.loadFromSrc(t)}disconnectedCallback(){document.removeEventListener("fullscreenchange",this.handleFullscreenChange),this.witnessMapResizeObserver?.disconnect(),this.exitSimulatedFullscreen(),this.sightingAudio.dispose()}attributeChangedCallback(t,e,s){t==="src"&&s&&s!==e&&this.isConnected&&this.loadFromSrc(s),t===et&&this.applyWitnessMapDefault(),t===st&&this.setMilestonesShown(!this.hasAttribute(st))}async loadFromSrc(t){this.sightingData=await _t.json(t)}get sightingData(){return ue(this.currentSighting)}set sightingData(t){this.player.stop(),this.soundPreview=void 0,this.sightingAudio.silence(),this.currentSighting=pe(t),this.player=this.createPlayer(),this.updateTimeLabels(),this.updatePlayPauseButton(),this.refresh(),this.applyWitnessMapDefault()}get sighting(){return this.currentSighting}previewSound(t){this.soundPreview=t,this.sightingAudio.resume(),this.sightingAudio.setSound(t)}stopSoundPreview(){this.soundPreview=void 0,this.sightingAudio.silence()}get canvasElement(){return this.canvas}get renderer(){return this.canvasRenderer}get currentTime(){return this.player.time}set currentTime(t){this.player.seek(t)}get seekableDuration(){return this.player.seekableDuration}get autoReplayEnabled(){return this.loopEnabled}set autoReplayEnabled(t){t!==this.loopEnabled&&this.toggleLoop()}play(){this.player.seekableDuration<=0||this.playbackState==="playing"||this.togglePlayPause()}pause(){this.playbackState==="playing"&&this.togglePlayPause()}get positionLabel(){return this.timeStartLabel.textContent??""}get durationLabel(){return this.timeEndLabel.textContent??""}get showingClockTime(){return this.canShowClockTime}get canSwitchTimeDisplay(){return this.realStartMs!==void 0}toggleTimeDisplay(){this.canSwitchTimeDisplay&&(this.showClockTime=!this.showClockTime,this.updateTimeLabels(),this.updateTimeLabelTitles(),this.dispatchEvent(new CustomEvent("timedisplaychange",{bubbles:!0,composed:!0})))}get canShowClockTime(){return this.realStartMs!==void 0&&this.showClockTime}set showToolbar(t){this.toolbar.classList.toggle("hidden",!t)}get playbackState(){return this.player.playbackState}get selectedSourceIds(){return this.highlightedSourceIds}get durationSeconds(){return this.currentSighting.event.durationSeconds}set durationSeconds(t){this.currentSighting.event.durationSeconds=t,this.updateTimeLabels(),this.refresh(),this.updatePlayPauseButton()}set selectedSourceIds(t){const e=new Set(t);e.size===this.highlightedSourceIds.size&&[...e].every(i=>this.highlightedSourceIds.has(i))||(this.highlightedSourceIds=e,this.refresh())}setOccludedSourceIds(t){t.size===this.occludedSourceIds.size&&[...t].every(s=>this.occludedSourceIds.has(s))||(this.occludedSourceIds=t,this.refresh())}hasVisibleShapeAt(t,e){return this.shapeAt(t,e)!==void 0}refresh(){this.applyFrameFormat(),this.updateTimeLabels(),this.seekInput.max=String(this.player.seekableDuration),this.refreshMilestoneMarks(),this.updateMilestonesButton(),this.updateWitnessMap(),this.player.seek(this.player.time)}refreshMilestoneMarks(){const t=this.player.seekableDuration,e=t>0?Y(this.sighting.milestones):[];this.milestoneMarks.replaceChildren(...e.map(s=>{const i=document.createElement("button");i.type="button",i.className="milestone-mark",i.style.left=`${Math.min(Math.max(s.t/t,0),1)*100}%`;const r=this.said.read(s.label)??"",a=this.said.read(s.note),o=a?`${r} — ${a}`:r;return i.title=o,i.setAttribute("aria-label",o),i.addEventListener("click",()=>this.player.seek(s.t)),i}))}showMilestoneAt(t){const e=this.milestonesShown&&this.sighting.milestones.length>0?pt(this.sighting.milestones,t):void 0;if(this.milestoneCaption.hidden=e===void 0,!e)return;const s=document.createElement("b");s.textContent=this.said.read(e.label)??"";const i=this.said.read(e.note),r=i?` — ${i}`:"";this.milestoneCaption.replaceChildren(s,document.createTextNode(r))}exposureInstants(t){const e=this.exposureTimes(t),s=1/e.length;return e.map(i=>({shapes:this.shapesAt(i),share:s}))}exposureTimes(t=this.currentTime){const e=(this.currentSighting.exposure??0)*1e3;if(e<x.SHORTEST_VISIBLE_EXPOSURE_MS)return[t];const s=Math.min(x.MAX_EXPOSURE_STEPS,Math.max(2,Math.round(e/x.SHORTEST_VISIBLE_EXPOSURE_MS))),i=Math.ceil(this.travelPxOver(t,e)/x.EXPOSURE_STEP_PX),r=Math.min(x.MAX_TRAVEL_STEPS,Math.max(s,i)),a=[];for(let o=0;o<r;o++)a.push(t+e*o/r);return a}travelPxOver(t,e){const s=this.shapesAt(t),i=this.shapesAt(t+e);let r=0;for(const[a,o]of s){const h=i.get(a);if(!h)continue;const l=h.bounds.x+h.bounds.width/2-(o.bounds.x+o.bounds.width/2),d=h.bounds.y+h.bounds.height/2-(o.bounds.y+o.bounds.height/2);r=Math.max(r,Math.hypot(l,d))}return r}shapeAt(t,e,s=this.occludedSourceIds){for(const i of this.exposureTimes()){const r=this.currentSighting.timeline.hitTest(i,t,e,s);if(r)return r}}shapesAt(t){const e=new Map;for(const s of this.currentSighting.timeline.sourceIds){const i=this.currentSighting.timeline.getInterpolatedShapeAt(t,s);i&&e.set(s,i)}return e}static SHORTEST_VISIBLE_EXPOSURE_MS=20;static MAX_EXPOSURE_STEPS=48;static EXPOSURE_STEP_PX=2;static MAX_TRAVEL_STEPS=320;applyFrameFormat(){const t=b.frameWidthPx(this.currentSighting.instrument,this.canvas.height);if(this.canvas.width===t)return;this.canvas.width=t,this.canvas.parentElement?.style.setProperty("--frame-aspect",`${t} / ${this.canvas.height}`)}canvasPointFromEvent(t){const e=this.canvas.getBoundingClientRect();if(!(e.width===0||e.height===0))return{x:(t.clientX-e.left)/e.width*this.canvas.width,y:(t.clientY-e.top)/e.height*this.canvas.height}}onFrame(t,e){this.canvasRenderer.clear(this.canvas.width,this.canvas.height);const s=this.playbackState!=="playing"?this.highlightedSourceIds:xt;this.canvasRenderer.setStarPoints(b.starPointsOf(this.sighting.instrument));const i=(T(this.sighting,t)?.rollDeg??0)+this.gaitAt(t).rollDeg;this.canvasRenderer.setRoll(i*Math.PI/180);const r=this.exposureInstants(t),a=this.frameShift;for(const o of r)for(const[h,l]of o.shapes){if(this.occludedSourceIds.has(h))continue;const d=o.share,c=d===1?l:{...l,transparency:1-(1-l.transparency)*d};this.canvasRenderer.paintShape(this.shifted(c,a))}for(const[o,h]of r[0].shapes)this.occludedSourceIds.has(o)||!s.has(o)||(s.size===1?this.canvasRenderer.paintShape({...h,selected:!0}):this.canvasRenderer.paintMemberOutline(h));if(s.size>1){const o=v.groupBoundsFor([...e].filter(([h])=>s.has(h)).map(([,h])=>h.bounds));this.canvasRenderer.paintGroupHandles(o)}this.keepWitnessMapClear(r[0].shapes),this.seekInput.value=String(t),this.timeStartLabel.textContent=this.formatPosition(t),this.showMilestoneAt(t),this.paintWitnessMap(t),this.playbackState==="playing"?(this.soundPreview=void 0,this.sightingAudio.setSound(Jt(this.currentSighting,t))):this.soundPreview?this.sightingAudio.setSound(this.soundPreview):this.sightingAudio.silence(),this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("timeupdate",{detail:{time:t}}))}shifted(t,e){return e.x===0&&e.y===0?t:{...t,bounds:{...t.bounds,x:t.bounds.x+e.x,y:t.bounds.y+e.y}}}createPlayer(){const t=new jt(this.currentSighting.timeline,(e,s)=>this.onFrame(e,s));return t.loop=this.loopEnabled,t.onEnded=()=>{this.updatePlayPauseButton(),this.dispatchEvent(new CustomEvent("ended",{bubbles:!0,composed:!0}))},t}togglePlayPause(){this.player.seekableDuration<=0||(this.player.playbackState==="playing"?(this.player.pause(),this.soundPreview=void 0,this.sightingAudio.silence(),this.refresh()):(this.sightingAudio.resume(),this.player.play()),this.updatePlayPauseButton())}updatePlayPauseButton(){const t=this.player.playbackState==="playing";this.playPauseButton.textContent=t?"⏸":"▶";const e=this.player.seekableDuration>0;this.playPauseButton.disabled=!e;const s=e?t?this.messages.pause:this.messages.play:this.messages.noDuration;this.playPauseButton.title=s,this.playPauseButton.setAttribute("aria-label",s),this.toolbar.classList.toggle("auto-hide",t),this.cornerButtons.classList.toggle("auto-hide",t)}toggleLoop(){this.loopEnabled=!this.loopEnabled,this.loopButton.setAttribute("aria-pressed",String(this.loopEnabled)),this.player.loop=this.loopEnabled}restorePlayback(){const t=this.playbackBeforeClick;t&&(this.currentTime=t.time,t.state==="playing"?this.play():this.pause())}get canUseNativeFullscreen(){return typeof this.fullscreenTarget.requestFullscreen=="function"&&document.fullscreenEnabled}toggleFullscreen(){if(!this.canUseNativeFullscreen){this.simulatedFullscreen?this.exitSimulatedFullscreen():this.enterSimulatedFullscreen();return}document.fullscreenElement?document.exitFullscreen():this.fullscreenTarget.requestFullscreen().catch(t=>{console.error("<rr0-ufo>: requestFullscreen() failed —",t)})}enterSimulatedFullscreen(){const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen=t.getAttribute("style")??"",this.bodyOverflowBeforeSimulatedFullscreen=document.body.style.overflow;const e=t.style;e.setProperty("position","fixed"),e.setProperty("inset","0"),e.setProperty("margin","0"),e.setProperty("max-width","none"),e.setProperty("max-height","none"),e.setProperty("aspect-ratio","auto"),e.setProperty("z-index","2147483647"),e.setProperty("background","#000"),e.setProperty("width","100vw"),e.setProperty("width","100dvw"),e.setProperty("height","100vh"),e.setProperty("height","100dvh"),document.body.style.overflow="hidden",this.simulatedFullscreen=!0,document.addEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}exitSimulatedFullscreen(){if(!this.simulatedFullscreen)return;const t=this.fullscreenTarget;this.styleBeforeSimulatedFullscreen?t.setAttribute("style",this.styleBeforeSimulatedFullscreen):t.removeAttribute("style"),document.body.style.overflow=this.bodyOverflowBeforeSimulatedFullscreen??"",this.simulatedFullscreen=!1,document.removeEventListener("keydown",this.handleSimulatedFullscreenKey),this.updateFullscreenButton()}toggleWitnessMap(){this.setWitnessMapOpen(this.witnessMapPanel.hidden)}applyWitnessMapDefault(){this.setWitnessMapOpen(this.hasAttribute(et)&&this.witnessPath!==void 0)}setWitnessMapOpen(t){this.witnessMapPanel.hidden=!t,this.witnessMapPanel.removeAttribute("title"),this.witnessMapButton.setAttribute("aria-pressed",String(t)),this.updateWitnessMapButton(),t&&(this.sizeWitnessMapCanvas(),this.loadWitnessMapImagery(),this.paintWitnessMap(this.currentTime))}updateWitnessMapButton(){const e=!this.witnessMapPanel.hidden?this.messages.hideWitnessMap:this.messages.showWitnessMap;this.witnessMapButton.title=e,this.witnessMapButton.setAttribute("aria-label",e)}updateWitnessMap(){const t=this.witnessPath!==void 0;if(this.witnessPath=F.of(this.currentSighting),this.witnessMapButton.hidden=this.witnessPath===void 0,!t&&this.witnessPath&&this.applyWitnessMapDefault(),!this.witnessPath){this.setWitnessMapOpen(!1),this.witnessMapBounds=void 0;return}const e=this.witnessPath.boundsAround(x.WITNESS_MAP_MIN_SPAN_M,x.WITNESS_MAP_MARGIN);(!this.witnessMapBounds||!this.sameGround(this.witnessMapBounds,e))&&(this.witnessMapBounds=e,this.witnessMapImagery=void 0,this.witnessMapImageryCredit=void 0,this.witnessMapImageryFailed=!1,this.witnessMapImageryRequested=!1,this.witnessMapPanel.hidden||this.loadWitnessMapImagery())}sameGround(t,e){const s=Math.abs(t.east-t.west)/20;return Math.abs(t.north-e.north)<s&&Math.abs(t.south-e.south)<s&&Math.abs(t.west-e.west)<s&&Math.abs(t.east-e.east)<s}async loadWitnessMapImagery(){if(this.witnessMapImageryRequested||!this.witnessMapBounds)return;this.witnessMapImageryRequested=!0;const t=Oe(),e=this.witnessMapBounds;try{const s=await t.getImageryTexture(e,{width:x.WITNESS_MAP_IMAGERY_PX,height:x.WITNESS_MAP_IMAGERY_PX});if(this.witnessMapBounds!==e)return;this.witnessMapImagery=s,this.witnessMapImageryCredit=t.attribution}catch{this.witnessMapImageryFailed=!0}this.paintWitnessMap(this.currentTime)}get witnessMapCredit(){return this.witnessMapImageryCredit}get witnessMapFooterLine(){return this.witnessMapImageryFailed?this.messages.mapImageryUnavailable:this.creditShownExternally?void 0:this.witnessMapImageryCredit}keepWitnessMapClear(t){if(this.witnessMapPanel.hidden)return;const e=this.witnessMapBoxPx;if(!e)return;const s=(o,h,l)=>{for(const[d,c]of t)if(!(this.occludedSourceIds.has(d)||c.transparency>=1)&&!(c.bounds.x+c.bounds.width<o-l||c.bounds.x>h+l)&&!(c.bounds.y>e.bottom+l||c.bounds.y+c.bounds.height<e.top-l))return!0;return!1},i=[this.canvas.width-e.width,this.canvas.width],r=[0,e.width],a=x.WITNESS_MAP_CLEARANCE_PX;if(this.witnessMapPanel.classList.contains("on-the-left")){s(i[0],i[1],a)||this.witnessMapPanel.classList.remove("on-the-left");return}s(i[0],i[1],0)&&!s(r[0],r[1],0)&&this.witnessMapPanel.classList.add("on-the-left")}witnessMapBoxPx;static WITNESS_MAP_CLEARANCE_PX=24;sizeWitnessMapCanvas(){const t=this.witnessMapPanel.clientWidth;if(t===0)return;const e=Math.round(t*(globalThis.devicePixelRatio??1));this.measureWitnessMapBox(),this.witnessMapCanvas.width!==e&&(this.witnessMapCanvas.width=e,this.witnessMapCanvas.height=e)}measureWitnessMapBox(){const t=this.witnessMapPanel.getBoundingClientRect(),e=this.canvas.getBoundingClientRect();if(e.width===0||e.height===0)return;const s=this.canvas.width/e.width,i=this.canvas.height/e.height;this.witnessMapBoxPx={width:t.width*s,top:(t.top-e.top)*i,bottom:(t.bottom-e.top)*i}}paintWitnessMap(t){if(this.witnessMapPanel.hidden||!this.witnessPath||!this.witnessMapBounds)return;const e=T(this.currentSighting,t),s=this.currentSighting.instrument,i=[],r=this.currentSighting.milestones.length>0?pt(this.currentSighting.milestones,t):void 0;for(const a of this.milestonesShown?Y(this.currentSighting.milestones):[]){const o=T(this.currentSighting,a.t);o?.lat===void 0||o.lng===void 0||i.push({label:this.said.read(a.label)??"",note:this.said.read(a.note),t:a.t,lat:o.lat,lng:o.lng,current:a===r})}this.witnessMapRenderer.paint({bounds:this.witnessMapBounds,imagery:this.witnessMapImagery,path:this.witnessPath,position:e?.lat!==void 0&&e.lng!==void 0?{lat:e.lat,lng:e.lng}:void 0,headingDeg:e?.headingDeg===void 0?void 0:e.headingDeg+this.lookYawDeg,coneHalfAngleDeg:e?I.of(s,this.canvas.height,e.fovDeg).halfWidthAngleDeg(b.aspectOf(s)):void 0,markers:i,decor:this.witnessMapDecorAt(t),witnessLabel:this.said.read(this.currentSighting.witness?.title)??this.messages.witnessHere,nightFraction:this.witnessMapNightFraction,attribution:this.witnessMapFooterLine})}toggleMilestones(){this.setMilestonesShown(!this.milestonesShown)}setMilestonesShown(t){this.milestonesShown=t,this.milestoneMarks.hidden=!t,this.milestonesButton.setAttribute("aria-pressed",String(t)),this.updateMilestonesButton(),this.showMilestoneAt(this.currentTime),this.paintWitnessMap(this.currentTime)}updateMilestonesButton(){this.milestonesButton.hidden=this.currentSighting.milestones.length===0;const t=this.milestonesShown?this.messages.hideMilestones:this.messages.showMilestones;this.milestonesButton.title=t,this.milestonesButton.setAttribute("aria-label",t)}witnessMapDecorAt(t){const e=T(this.currentSighting,0);return e?.lat===void 0||e.lng===void 0?[]:this.currentSighting.decor.filter(s=>s.kind!=="crop").map(s=>{const i=Se(s,t),{lat:r,lng:a}=L(i.eastM,-i.northM,e.lat,e.lng);return{lat:r,lng:a,headingDeg:i.headingDeg,label:this.said.read(s.title)??this.messages.decorHere}})}witnessMapNightFraction;setSunAltitude(t){const e=t===void 0?void 0:Math.max(0,Math.min(1,-t/12));e!==this.witnessMapNightFraction&&(this.witnessMapNightFraction=e,this.paintWitnessMap(this.currentTime))}setLookOffset(t,e){t===this.lookYawDeg&&e===this.lookPitchDeg||(this.lookYawDeg=t,this.lookPitchDeg=e,this.refresh())}get frameShiftPx(){return this.frameShift}get frameShift(){const t=this.gaitAt(this.currentTime),e=this.lookYawDeg+t.yawDeg,s=this.lookPitchDeg+t.pitchDeg;if(e===0&&s===0)return{x:0,y:0};const i=T(this.currentSighting,this.currentTime),r=I.of(this.currentSighting.instrument,this.canvas.height,i?.fovDeg??60);return{x:-r.angleDegToRadiusPx(e),y:r.angleDegToRadiusPx(s)}}gaitAt(t){return g.of(this.currentSighting)?.offsetAt(t)??g.STILL}updateFullscreenButton(){const t=this.simulatedFullscreen||document.fullscreenElement===this.fullscreenTarget;this.fullscreenButton.title=t?this.messages.exitFullscreen:this.messages.fullscreen,this.fullscreenButton.setAttribute("aria-label",this.fullscreenButton.title)}get said(){return this.saidTexts??=new P(mt.preferencesFor(this))}saidTexts;async loadLocaleMessages(){const t=ge(mt.preferencesFor(this),be);t!=="en"&&this.applyMessages(await xe(t))}updateTimeLabelTitles(){const t=this.showClockTime?this.messages.switchToElapsed:this.messages.switchToClockTime,e=this.canSwitchTimeDisplay?` — ${t}`:"";this.timeStartLabel.title=this.messages.currentPosition+e,this.timeEndLabel.title=this.messages.duration+e;for(const s of[this.timeStartLabel,this.timeEndLabel])s.classList.toggle("switchable",this.canSwitchTimeDisplay),this.canSwitchTimeDisplay?(s.setAttribute("role","button"),s.setAttribute("tabindex","0")):(s.removeAttribute("role"),s.removeAttribute("tabindex"))}applyMessages(t){this.messages=t,this.updateTimeLabelTitles(),this.loopButton.title=t.autoReplay,this.loopButton.setAttribute("aria-label",t.autoReplay),this.updatePlayPauseButton(),this.updateFullscreenButton(),this.updateWitnessMapButton(),this.updateMilestonesButton()}updateTimeLabels(){const t=this.currentSighting.event,e=qt(t);this.realDurationMs=e!==void 0&&e>0?e:void 0,this.realStartMs=t.time?it(t.time):void 0;const s=this.currentSighting.timeline.duration;this.player.playbackRate=this.realDurationMs!==void 0&&s>0?s/this.realDurationMs:1,this.player.durationOverrideMs=s>0?0:this.realDurationMs??0,this.timeEndLabel.textContent=this.formatEndOfTimeline(),this.timeStartLabel.textContent=this.formatPosition(this.player.time),this.updateTimeLabelTitles()}formatPosition(t){if(this.realDurationMs===void 0)return V(t);const e=this.currentSighting.timeline.duration,s=e>0&&t<=e?t/e*this.realDurationMs:t;return this.canShowClockTime?St(Mt(this.realStartMs+s)):V(s)}formatEndOfTimeline(){return this.realDurationMs===void 0?V(this.currentSighting.timeline.duration):this.canShowClockTime?St(Mt(this.realStartMs+this.realDurationMs)):V(this.realDurationMs)}}function Mt(n){const t=new Date(n);return{hour:t.getUTCHours(),minute:t.getUTCMinutes(),second:t.getUTCSeconds()}}function St(n){if(n.hour===void 0)return"0:00";const t=e=>String(e).padStart(2,"0");return n.second?`${t(n.hour)}:${t(n.minute??0)}:${t(n.second)}`:`${t(n.hour)}:${t(n.minute??0)}`}function V(n){const t=Math.round(n/1e3),e=Math.floor(t/60),s=t%60;return`${e}:${String(s).padStart(2,"0")}`}const kt="rr0-ufo";function Ne(){customElements.get(kt)||customElements.define(kt,x)}Ne();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rr0/ufoathome",
3
3
  "type": "module",
4
- "version": "0.51.0",
4
+ "version": "0.53.0",
5
5
  "description": "UFO@home — record and replay a UFO sighting's shape, movement and appearance",
6
6
  "author": "Jérôme Beau <rr0@rr0.org> (https://rr0.org)",
7
7
  "license": "MIT",
@@ -1 +0,0 @@
1
- const e={color:"Couleur",transparency:"Transparence",halo:"Halo",blur:"Flou",brightness:"Éclat",shapeTitle:"Nom",utcOffset:"Fuseau horaire",cloudBase:"Base des nuages",elevation:"Altitude",heightAboveGround:"Hauteur au-dessus du sol",duration:"Durée",placeName:"Lieu",latitude:"Latitude",longitude:"Longitude",heading:"Orientation",pitch:"Inclinaison",roll:"Roulis",observationTime:"Début de l'observation",observationEndTime:"Fin de l'observation",witnessGroup:"Témoin",witnessId:"ID témoin",witnessTitle:"Titre du témoin",witnessLastName:"Nom de famille du témoin",witnessFirstNames:"Prénoms du témoin",caseId:"ID de l'affaire",tags:"Mots-clés",cloudCover:"Couverture nuageuse",highCloudCover:"Nuages de glace (cirrus)",focalLength:"Focale",fieldOfView:"Champ de vision",aperture:"Diaphragme",exposure:"Temps de pose",focusDistance:"Mise au point à",iceCrystalAlignment:"Alignement des cristaux",cloudDarkness:"Obscurité des nuages",precipitationType:"Précipitations",precipitationNone:"Aucune",precipitationRain:"Pluie",precipitationSnow:"Neige",precipitationHail:"Grêle",precipitationIntensity:"Intensité",windDirection:"Direction du vent",windSpeed:"Force du vent",storm:"Orage",soundKind:"Son",soundNone:"Aucun (silencieux)",soundHum:"Bourdonnement",soundWhistle:"Sifflement",soundRumble:"Grondement",soundCrackle:"Crépitement",soundVolume:"Intensité sonore",soundPitch:"Hauteur",soundSrc:"Enregistrement",instrument:"Observé avec",decorAircraft:"Aéronef",decorAltitude:"Altitude",decorLights:"Feux",decorLightsNone:"aucun",decor:"Environnement",decorBuilding:"Bâtiment",decorTree:"Arbre",decorCrop:"Rang de culture",decorMound:"Tas de pierres",decorStreetlight:"Lampadaire",decorVehicle:"Véhicule",decorWitness:"Autre témoin",decorEast:"Distance à l'est",decorNorth:"Distance au nord",decorHeading:"Orientation",decorColor:"Couleur",decorLit:"Allumé",decorTitle:"Nom",milestones:"Moments",milestoneLabel:"Repère",milestoneNote:"Ce qui se passe",addMilestone:"Nommer cet instant",deleteMilestone:"Supprimer le moment",confirmAccept:"Supprimer",confirmDecline:"Annuler",decorWidth:"Largeur",decorLength:"Longueur",decorHeight:"Hauteur",decorModel:"Modèle 3D",decorModelNone:"Forme intégrée",decorModelAdvanced:"Modèle depuis une adresse",decorModelUrl:"Adresse glTF/GLB",decorModelTitle:"Nom du modèle",decorModelAuthor:"Auteur",decorModelLicense:"Licence",decorModelSource:"Provenance",decorFloors:"Étages",decorOccupiedFloor:"Étage occupé",decorWitnessSide:"Emplacement du témoin",decorWitnessSideNone:"Non présent",decorWindows:"Fenêtres",decorSideFront:"Avant",decorSideBehind:"Arrière",decorSideLeft:"Gauche",decorSideRight:"Droite",decorSideFrontLeft:"Avant-gauche",decorSideFrontRight:"Avant-droit",decorSideBehindLeft:"Arrière-gauche",decorSideBehindRight:"Arrière-droit"},o={...e,testimonyBy:"Témoignage de",unnamedWitness:"Témoin {n}",about:"À propos",close:"Fermer",observation:"Observation",date:"Date",location:"Lieu",case:"Dossier",description:"Description",credits:"Crédits",editThisObservation:"Éditer cette observation",embed:"Intégrer",embedReplay:"Relecture",embedEdit:"Édition",embedCopy:"Copier",embedCopied:"Copié",showLabels:"Afficher ce qu'elle indique",hideLabels:"Masquer ce qu'elle indique"};export{o as sightingMessages_fr};
@@ -1 +0,0 @@
1
- const e={color:"Couleur",transparency:"Transparence",halo:"Halo",blur:"Flou",brightness:"Éclat",shapeTitle:"Nom",utcOffset:"Fuseau horaire",cloudBase:"Base des nuages",elevation:"Altitude",heightAboveGround:"Hauteur au-dessus du sol",duration:"Durée",placeName:"Lieu",latitude:"Latitude",longitude:"Longitude",heading:"Orientation",pitch:"Inclinaison",roll:"Roulis",observationTime:"Début de l'observation",observationEndTime:"Fin de l'observation",witnessGroup:"Témoin",witnessId:"ID témoin",witnessTitle:"Titre du témoin",witnessLastName:"Nom de famille du témoin",witnessFirstNames:"Prénoms du témoin",caseId:"ID de l'affaire",tags:"Mots-clés",cloudCover:"Couverture nuageuse",highCloudCover:"Nuages de glace (cirrus)",focalLength:"Focale",fieldOfView:"Champ de vision",aperture:"Diaphragme",exposure:"Temps de pose",focusDistance:"Mise au point à",iceCrystalAlignment:"Alignement des cristaux",cloudDarkness:"Obscurité des nuages",precipitationType:"Précipitations",precipitationNone:"Aucune",precipitationRain:"Pluie",precipitationSnow:"Neige",precipitationHail:"Grêle",precipitationIntensity:"Intensité",windDirection:"Direction du vent",windSpeed:"Force du vent",storm:"Orage",soundKind:"Son",soundNone:"Aucun (silencieux)",soundHum:"Bourdonnement",soundWhistle:"Sifflement",soundRumble:"Grondement",soundCrackle:"Crépitement",soundVolume:"Intensité sonore",soundPitch:"Hauteur",soundSrc:"Enregistrement",instrument:"Observé avec",decorAircraft:"Aéronef",decorAltitude:"Altitude",decorLights:"Feux",decorLightsNone:"aucun",decor:"Environnement",decorBuilding:"Bâtiment",decorTree:"Arbre",decorCrop:"Rang de culture",decorMound:"Tas de pierres",decorStreetlight:"Lampadaire",decorVehicle:"Véhicule",decorWitness:"Autre témoin",decorEast:"Distance à l'est",decorNorth:"Distance au nord",decorHeading:"Orientation",decorColor:"Couleur",decorLit:"Allumé",decorTitle:"Nom",milestones:"Moments",milestoneLabel:"Repère",milestoneNote:"Ce qui se passe",addMilestone:"Nommer cet instant",deleteMilestone:"Supprimer le moment",confirmAccept:"Supprimer",confirmDecline:"Annuler",decorWidth:"Largeur",decorLength:"Longueur",decorHeight:"Hauteur",decorModel:"Modèle 3D",decorModelNone:"Forme intégrée",decorModelAdvanced:"Modèle depuis une adresse",decorModelUrl:"Adresse glTF/GLB",decorModelTitle:"Nom du modèle",decorModelAuthor:"Auteur",decorModelLicense:"Licence",decorModelSource:"Provenance",decorFloors:"Étages",decorOccupiedFloor:"Étage occupé",decorWitnessSide:"Emplacement du témoin",decorWitnessSideNone:"Non présent",decorWindows:"Fenêtres",decorSideFront:"Avant",decorSideBehind:"Arrière",decorSideLeft:"Gauche",decorSideRight:"Droite",decorSideFrontLeft:"Avant-gauche",decorSideFrontRight:"Avant-droit",decorSideBehindLeft:"Arrière-gauche",decorSideBehindRight:"Arrière-droit"},r={...e,oval:"Ovale",polygon:"Polygone",addVertex:"Ajouter un sommet",deleteVertex:"Supprimer le sommet",notAPolygon:"Sélectionnez une seule forme polygonale",tooFewVertices:"Une forme a besoin d'au moins 3 points",shape:"Forme",objectSize:"Essayer une taille",objectDistance:"à une distance de",apparentSize:"soit {deg}° — {moons}× la Lune",utcOffsetPlaceholder:"d'après la longitude",objectSizePlaceholder:"largeur supposée",objectDistancePlaceholder:"distance supposée",realSizeBetween:"Largeur réelle : entre {min} et {max} m, d'après ce qu'il croise",realSizeAtLeast:"Largeur réelle : au moins {min} m, d'après ce qu'il passe derrière",realSizeAtMost:"Largeur réelle : au plus {max} m, d'après ce qu'il passe devant",realSizeUnknown:"Largeur réelle indéterminée : rien dans la scène ne croise sa ligne de visée",blurBound:"Flou à travers cet objectif : plus près que {max} m",blurBoundNoInstrument:"Le flou à l'œil nu ne borne aucune distance — un œil n'a pas de diaphragme dont sortir",blurBoundNotAtInfinity:"Le flou ne borne une distance que pour une mise au point à l'infini : celle-ci est à {focus} m, qui floute des deux côtés",realSizeContradiction:"Contradiction : les passages déclarés ne peuvent pas être vrais ensemble",realDistanceHere:" — soit ici entre {min} et {max} m",addShape:"Ajouter une forme",deleteShape:"Supprimer la forme",play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",autoReplay:"Lecture automatique",group:"Grouper",ungroup:"Dégrouper",bringToFront:"Placer devant",sendToBack:"Placer derrière",contextMenuDelete:"Supprimer",confirmDeleteShape:"Supprimer {name} ? Cette action est irréversible.",confirmDeleteShapes:"Supprimer {count} formes ? Cette action est irréversible.",confirmDeleteMilestone:"Ne plus nommer le moment {name} ? L'enregistrement lui-même est inchangé.",onlyOneShape:"Il n'y a qu'une seule forme",alreadyAtFront:"Cette forme est déjà la plus en avant",alreadyAtBack:"Cette forme est déjà la plus en arrière",needTwoShapesToGroup:"Sélectionnez au moins deux formes pour les grouper",notGrouped:"Cette forme ne fait pas partie d'un groupe",multipleShapesSelected:"Plusieurs formes sélectionnées — sélectionnez-en une seule pour modifier ceci",samplingRate:"Échantillonnage",currentPosition:"Position actuelle",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",durationPlaceholder:"durée de l'observation",durationImprecise:"Ces dates ne permettent pas de déterminer précisément une durée — saisissez-la manuellement.",export:"Exporter",importFile:"Charger un fichier JSON",importUrl:"Ou charger depuis une URL",importUrlPlaceholder:"https://…/sighting.json",importButton:"Charger",importError:"Impossible de charger cet enregistrement — vérifiez le fichier ou l'URL et réessayez.",importErrorCors:"Cette adresse répond, mais le navigateur n'a pas le droit de la lire depuis cette page. Le fichier lui-même est bon : c'est son serveur qui doit envoyer l'en-tête « Access-Control-Allow-Origin: * » avec. Si ce serveur n'est pas le vôtre, c'est ce qu'il faut demander à son administrateur.",importErrorMixedContent:"Une page sécurisée ne peut pas charger une adresse commençant par http://. Utilisez https:// si ce serveur le propose.",importErrorUnreachable:"Rien n'a répondu à cette adresse. Vérifiez l'orthographe, et que vous êtes connecté.",importErrorStatus:"Ce serveur a répondu par une erreur ({status}). L'adresse est probablement fausse, ou le fichier a été déplacé.",importErrorMalformed:"Cette adresse a répondu, mais ce qui revient n'est pas un enregistrement.",record:"Enregistrer",stop:"Arrêter",according:"selon",sourceElevation:"Relief",sourceImagery:"Imagerie",placeNamePlaceholder:"Valensole, France",searchPlace:"Localiser",placeMatch:"Correspondances",placeSearching:"Recherche du lieu…",placeMatchFound:"lieu trouvé",placeMatchesFound:"lieux trouvés",placeNotFound:"Aucun lieu de ce nom",placeSearchFailed:"Recherche de lieu momentanément indisponible",utcOffsetImplausible:"Ce fuseau ne correspond pas à la longitude saisie, dont l'heure solaire est UTC{solar} — la scène et le relevé météo obéissent pourtant à ce qui est déclaré ici. Videz le champ pour le déduire de la longitude.",timeZoneManual:"décalage saisi",altitudeAboveSeaLevel:"Altitude du témoin au-dessus du niveau de la mer — le sol du lieu saisi en fixe le minimum : un témoin dans les Alpes n'est pas à 0 m.",groundAt:"sol à {m} m",headingPlaceholder:"inconnu",edtfInvalid:"Date/heure EDTF invalide (ex. 1965-07-01T05:00, 2025-06?, 2025~).",edtfPlaceholder:"AAAA-MM-JJThh:mm[?~%] ou hh:mm",observationTimeHint:"EDTF — ex. 1965-07-01T05:00, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:00 si la date est inconnue",observationEndTimeHint:"EDTF — ex. 1965-07-01T05:10, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:10 si la date est inconnue",presetsGroupLabel:"Forme de l'ovni",witnessDirName:"Répertoire du témoin",description:"Description",tagsPlaceholder:"séparés par des virgules",weather:"Météo",shapeGroup:"Phénomène",soundGroup:"Son",temporalGroup:"Moment",timeQualifierExact:"Exacte",timeQualifierApproximate:"Approximative",timeQualifierUncertain:"Incertaine",timeQualifierBoth:"Incertaine et approximative",edtfModeTitle:"Saisir plutôt une date imprécise : une année seule, un mois, une heure sans date",locationGroup:"Lieu",observationGroup:"Observation",weatherWhilePlaying:"Mettez la lecture en pause pour décrire le temps — pendant qu'elle tourne, l'instant où ceci s'écrirait se déplace",weatherInferred:"D'après les relevés",weatherInferredTitle:"La météo n'est pas un témoignage : c'est un fait mesurable, relevé pour la date, l'heure et le lieu de l'observation. Décochez pour saisir les conditions rapportées par le témoin, qui priment alors sur le relevé.",weatherLookingUp:"Recherche du relevé…",weatherNeedsDateAndPlace:"Date complète et lieu requis pour retrouver le relevé",weatherNoRecord:"Aucun relevé pour cette date et ce lieu",weatherLookupFailed:"Relevés météo momentanément inaccessibles",instrumentOutOfPeriod:"{name} — hors de son époque",unitMillimetres:"mm",unitDegrees:"°",soundSrcPlaceholder:"URL d'un enregistrement réel",skyLine:"Ciel : {parts}",skyStarsEye:"étoiles jusqu'à la magnitude {limit}, ce que l'œil atteignait sous ce ciel",skyStarsDeeper:"étoiles jusqu'à la magnitude {limit} à travers un {device} — {gain} magnitudes au-delà des {eye} qu'atteignaient les yeux du témoin",skyStarsShallower:"étoiles jusqu'à la magnitude {limit} à travers un {device} — {gain} magnitudes en deçà des {eye} qu'atteignaient les yeux du témoin",skyStarsCatalogue:", dessinées jusqu'à {catalogue}, où s'arrête le catalogue de ce projet",skyStarsNone:"aucune étoile ne se détachait d'un ciel aussi clair",skyDetails:"Voir le détail des relevés",skyDetailsHide:"Masquer le détail des relevés",skyShowerActive:"{name} — radiant à {altitude}° de hauteur, {bearing} — environ {rate}/h sous ce ciel, sur un fond sporadique de {sporadic}/h",skyShowerBelowHorizon:"{name} est actif, mais son radiant est sous l'horizon — il n'a rien pu produire",skyNothingActive:"aucune pluie de météores active à cette date — un fond sporadique d'environ {sporadic}/h",showMeteor:"Montrer un météore",skyComet:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing}, queue longue de {tail}°",skyCometNoTail:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing}",skyCometBelowHorizon:"La {name} était là, magnitude {magnitude}, mais sous l'horizon — personne ici ne l'a vue",skyCometInDaylight:"La {name}, magnitude {magnitude}, à {altitude}° de hauteur {bearing} — mais à {elongation}° du Soleil seulement",showComet:"Montrer la comète",skyOpticsPossible:"nuages de glace suffisants, autour {source}, pour {forms}",skyOpticsAlignment:" — tout sauf les halos exige des cristaux tombant à plat, ici supposés alignés à {alignment} %, qu'aucun relevé ne donne",skyOpticsHalo22:"un halo à {angle}°",skyOpticsHalo46:"un halo à {angle}°",skyOpticsParhelia:"des parhélies à {angle}° de part et d'autre",skyOpticsTangentArc:"un arc tangent au sommet",skyOpticsParhelicCircle:"un cercle parhélique blanc faisant le tour du ciel à sa hauteur",skyOpticsCircumzenithal:"un arc circumzénithal à {angle}° de hauteur",skyOpticsCircumhorizontal:"un arc circumhorizontal très au-dessous",skyOpticsPillar:"un pilier dressé au-dessus",skyOpticsNoIce:"aucun nuage de glace : ni halo ni parhélie",skyOpticsHidden:"des nuages de glace en altitude, mais une couche plus basse les cache",skyOpticsSun:"du Soleil",skyOpticsMoon:"de la Lune",skyBowPossible:"pluie et soleil suffisants pour {forms}",skyBowMoon:"pluie sous une Lune éclairée à {lit} %, de quoi former {forms} — un arc-en-ciel lunaire, que l'œil voit blanc",skyGlowBand:"la Voie lactée à {contrast}× le fond du ciel, à {altitude}° de hauteur {bearing}",skyGlowCone:"la lumière zodiacale à {contrast}× le fond du ciel, à {altitude}° de hauteur {bearing}, à {elongation}° du Soleil — un cône penché, sans bord, disparu dans l'heure",skyGlowZodiacalBand:"la bande zodiacale à {contrast}× le fond du ciel, à {altitude}° de hauteur {bearing} — toute l'écliptique faiblement allumée, ce qui n'est pas ce que les gens rapportent",skyGlowMoon:"ni Voie lactée ni lumière zodiacale : une Lune éclairée à {lit} % tenait le ciel à {sky} magnitudes par seconde d'arc, là où il leur en faut 22",skyGlowTwilight:"ni Voie lactée ni lumière zodiacale : le crépuscule tenait encore le ciel à {sky} magnitudes par seconde d'arc, là où il leur en faut 22",skyGlowNothingUp:"ciel assez noir ({sky} magnitudes par seconde d'arc), mais ni le plan de la Galaxie ni l'écliptique ne s'y tenaient assez haut",skyBowPrimary:"un arc de {radius}° de rayon, dont le sommet montait à {top}°",skyBowSecondary:"un second, plus pâle, à {radius}°, aux couleurs inversées",skyBowSourceTooHigh:"pluie, mais l'astre à {altitude}° de hauteur : aucun arc ne pouvait dépasser l'horizon, ils se tiennent à {radius}° du point opposé",skyBowHidden:"pluie, mais une couche continue entre elle et la lumière : aucun arc",skyBowNoSource:"pluie, et rien d'allumé pour en tirer un arc",skySatellitesLit:"l'ombre de la Terre montait à {height} km au-dessus du témoin, l'orbite basse était donc encore éclairée — {count} objets suivis étaient en orbite ce mois-là",skySatellitesLitWith:"l'ombre de la Terre montait à {height} km au-dessus du témoin, l'orbite basse était donc encore éclairée — {count} objets suivis étaient en orbite ce mois-là, dont {eras}",skySatellitesShadowed:"l'ombre de la Terre montait à {height} km au-dessus du témoin : sur les {count} objets suivis en orbite ce mois-là, rien en orbite basse n'était éclairé",skySatellitesNotYet:"rien n'était encore en orbite",skySatellitesDaylight:"tout ce qui est en orbite était éclairé, comme toujours de jour, et {eras} pouvaient l'emporter même sur ce ciel",skySatellitesDaylightOne:"tout ce qui est en orbite était éclairé, comme toujours de jour, et {eras} pouvait l'emporter même sur ce ciel",skyUnknown:"indéterminable sans date ni lieu",lookAtDecor:"Regarder cet élément",addDecor:"Ajouter",deleteDecor:"Supprimer l'élément de décor",decorSightingUrl:"URL de l'enregistrement du témoin",viewTestimony:"Voir le témoignage",noWitnessRecording:"Aucune URL d'enregistrement définie pour ce témoin",masks:"Masque",addWitness:"Ajouter un témoin",narrativePlaceholder:"Ce que le témoin a rapporté, dans ses propres mots",narrativeKey:"Clé d'API {source}",narrativeNeedsKey:"Saisissez votre propre clé d'API {source} pour générer la reconstitution d'après le récit",narrativeKeyUnlikely:"Cela ne ressemble pas à une clé d'API Claude : elles commencent par sk-ant-",narrativeWorkspace:"Espace de travail",narrativeWorkspacePlaceholder:"seulement si votre clé en couvre plusieurs",narrativeWorkspaceHint:"Seulement pour une clé dont la portée est l'organisation entière. Plus simple : laissez vide et créez une clé de portée « espace de travail ».",narrativeNeedsAsk:"Rien à envoyer pour l'instant",narrativeRemember:"Retenir sur cet appareil",narrativeDraft:"Générer la reconstitution",narrativeStop:"Arrêter",narrativeWorking:"Lecture du récit…",narrativeApplied:"{count} champs écrits dans l'éditeur",narrativeUnchanged:"Le récit n'énonce rien que l'enregistrement sache encore porter",narrativeStated:"Ce que le témoin a dit",narrativeDerived:"Déduit du récit",narrativeAssumed:"Supposé, pour que la reconstitution tourne : à vérifier",narrativeGaps:"Ce que rien n'a pu trancher",narrativeErrorCredential:"Pas de clé, ou une clé refusée : vérifiez-la sur console.anthropic.com",narrativeErrorRateLimited:"Trop de requêtes sur votre compte : patientez un instant et redemandez",narrativeErrorRefused:"Le récit n'a pas reçu de réponse. Le reformuler peut aider, le redemander non",narrativeErrorMalformed:"La réponse n'était pas un brouillon exploitable : cela vaut la peine de redemander",narrativeErrorUnreachable:"Rien n'a répondu : vérifiez la connexion",narrativeErrorRejected:"Refusé : {detail}"};export{r as sightingEditorMessages_fr};
@@ -1 +0,0 @@
1
- const e={type:"number"},t={type:"object",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},o={type:"object",doc:"See SightingEvent.endTime.",fields:{year:{type:"number"},month:{type:"number"},day:{type:"number"},hour:{type:"number"},minute:{type:"number"},second:{type:"number"},raw:{type:"string",doc:"The exact EDTF-ish text as typed in the editor UI (e.g."}}},n={type:"number",doc:"See SightingEvent.durationSeconds."},i={type:"number",doc:"See SightingEvent.utcOffsetHours — the legal time zone `time`/`endTime` are expressed in."},s={type:"string",doc:"See SightingEvent.timeZone — the IANA rule `utcOffsetHours` was derived from, when it was."},r={type:"array",fields:{lat:{type:"number"},lng:{type:"number"},name:{type:"string",doc:'The place as it is named, fully qualified — "Valensole, Alpes-de-Haute-Provence, …, France".'}}},a={type:"object",doc:"See Sighting.witness.",fields:{id:{type:"string"},dirName:{type:"string"},title:{type:"string"},lastName:{type:"string"},firstNames:{type:"array"}}},d={type:"string",doc:"See Sighting.caseId — shared by every witness's own sighting.json for the same case, so a page (e.g."},c={type:"string",doc:"See SightingEvent.description — a plain string, or one per language."},l={type:"array",doc:"See SightingEvent.tags."},h={type:"object",fields:{keyframes:{type:"array",fields:{t:{type:"number"},shapes:{type:"array",fields:{sourceId:{type:"string"},shape:{type:"object",fields:{kind:{type:"enum",values:["oval","polygon"]},bounds:{type:"object",fields:{x:{type:"number"},y:{type:"number"},width:{type:"number"},height:{type:"number"}}},color:{type:"string",doc:"CSS color"},angle:{type:"number",doc:"Rotation in radians; 0 = no rotation"},transparency:{type:"number",doc:"0 = opaque, 1 = fully transparent"},haloScale:{type:"number",doc:"0 = no halo/glow, >1 = larger glow radius"},blur:{type:"number",doc:"How indistinct the witness said its edges were — 0 for a hard outline, 1 for a shape with no edge at all."},brightness:{type:"number",doc:"How dazzling the witness said it was — 0 for a light you can look at, 1 for one you cannot."},selected:{type:"boolean"},title:{type:"string",doc:"What the witness calls it, if anything."},behindCloud:{type:"boolean",doc:'Whether the witness reported this shape as being behind cloud at this instant — "it disappeared into a cloud", which is a thing they SAW, not something to be deduced.'},angular:{type:"object",doc:"How big this looked to the witness, in degrees of arc (see AngularExtent) — the recording's OWN statement of size, and the one it is allowed to make.",fields:{widthDeg:{type:"number",doc:"How wide it looked, in degrees of arc."},heightDeg:{type:"number",doc:"How tall it looked, in degrees of arc."}}},aim:{type:"object",doc:"Which way the witness was actually looking when the thing was there — degrees of azimuth from true north and of altitude above the horizon, the same conventions as ObserverPose.",fields:{azimuthDeg:{type:"number",doc:"Degrees clockwise from true north, the same convention as ObserverPose.headingDeg."},altitudeDeg:{type:"number",doc:"Degrees above (positive) or below the horizon."}}},points:{type:"array",doc:"Points relative to bounds origin (0,0 = bounds.x, bounds.y)",fields:{x:{type:"number"},y:{type:"number"}}}}}}}}},order:{type:"array",doc:"Back-to-front paint/hit-test order — see Timeline's own `order` field."},groups:{type:"array",doc:"Each inner array is one group's member sourceIds (always >= 2 members) — see Timeline's own `groups` field."}}},p={type:"object",doc:"The witness's position/elevation/orientation over time — absent for older recordings, which fall back to the legacy static place[0] (see Sighting.ts's resolveObserverPoseAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},pose:{type:"object",fields:{lat:{type:"number",doc:"Decimal degrees."},lng:{type:"number"},elevationM:{type:"number"},headingDeg:{type:"number",doc:"Degrees clockwise from true north."},pitchDeg:{type:"number",doc:"Degrees above (positive) or below (negative) the local horizontal — how far up/down the witness was looking, e.g."},rollDeg:{type:"number",doc:"How far the instrument itself was tilted about its own line of sight, degrees, positive clockwise as the witness saw it — a camera held askew, a head leaned over."},fovDeg:{type:"number",doc:"Vertical field of view in degrees, as a Three.js PerspectiveCamera would take it."},fNumber:{type:"number",doc:"How far the lens was stopped down — undefined for anything the recording did not state, which then falls back to the instrument's own setting (see Instrument.fNumber)."},focusDistanceM:{type:"number",doc:"How far away the lens was focused, metres — undefined meaning at infinity, which is where a camera pointed at the sky sits and the only setting under which everything celestial comes out sharp."}}}}}}},u={type:"object",doc:"Weather over time — absent for older recordings, which fall back to the legacy static `weather` field below (see Sighting.ts's resolveWeatherAt).",fields:{keyframes:{type:"array",fields:{t:{type:"number"},weather:{type:"object",fields:{cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a witness and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both visual precipitation drift and wind audio volume directly, see SceneRenderer/WeatherAudio."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}}}}}},y={type:"object",doc:`Legacy static weather condition, kept only as weatherTrack's own fallback for recordings made before it existed — absent means "unknown/not recorded", not "clear skies" (renderers default to DEFAULT_WEATHER, see Weather.ts).`,fields:{cloudCover:{type:"number",doc:"0 = clear sky, 1 = fully overcast."},cloudDarkness:{type:"number",doc:"0 = light/clear clouds, 1 = dark/stormy character."},cloudBaseM:{type:"number",doc:"Height of the cloud layer's BASE above the ground, in meters — 600 to 2000 m covers most real low cloud."},highCloudCover:{type:"number",doc:"How much of the sky the HIGH deck covered, 0 to 1 — cirrus and cirrostratus, above about six kilometres, where anything present is ice rather than water."},lowerCloudCover:{type:"number",doc:"How much of the sky the LOW and MIDDLE decks covered, 0 to 1 — the cloud that stands between a witness and anything higher up."},iceCrystalAlignment:{type:"number",doc:"How steadily the ice crystals were falling, 0 to 1 — the one thing about the sky that decides which halo forms stood, and the one thing no record of any sighting holds."},precipitationType:{type:"enum",values:["none","rain","snow","hail"]},precipitationIntensity:{type:"number",doc:'0-1; meaningless while precipitationType is "none".'},windDirectionDeg:{type:"number",doc:"Degrees clockwise from true north — same convention as ObserverPose.headingDeg, and the direction the wind blows *toward*, not the one it comes from: SceneRenderer drifts every precipitation particle along exactly this bearing (`sin/-cos`, north = -Z), so 0 means snow drifting northward."},windSpeed:{type:"number",doc:"Real-world wind speed in m/s (0 = calm, ~30 = violent storm/hurricane-force) — drives both visual precipitation drift and wind audio volume directly, see SceneRenderer/WeatherAudio."},storm:{type:"boolean",doc:"Whether a thunderstorm was reported — drives both a scene-fog lightning flash and a delayed thunderclap (see SceneRenderer's lightning-flash scheduling, named for the visual mechanism it implements internally, not for this field)."}}},m={type:"object",doc:"What the sighting sounded like over time — see SoundTrack.",fields:{keyframes:{type:"array",fields:{t:{type:"number"},sound:{type:"object",fields:{kind:{type:"enum",values:["none","hum","whistle","rumble","crackle"]},volume:{type:"number",doc:"How loud it was, 0 (inaudible) to 1 (as loud as the witness could describe) — deliberately relative rather than a dB SPL figure: nobody reports a sound level in decibels, and inventing one would claim a measurement where there is only an impression."},pitchHz:{type:"number",doc:`The sound's own frequency in Hz — the oscillator's pitch for the pitched kinds ("hum", "whistle"), and the noise filter's centre for the unpitched ones ("rumble", "crackle"), where it reads as how deep or how sharp the noise is.`},src:{type:"string",doc:"An actual audio recording of the sound, when one exists (a witness who filmed the event, a later recording of the same phenomenon) — replaces synthesis entirely while it is set, with `volume` still scaling it."}}}}}}},g={type:"array",doc:"See Sighting.decor.",fields:{id:{type:"string"},kind:{type:"enum",values:["building","tree","crop","mound","streetlight","vehicle","witness","aircraft"]},title:{type:"string",doc:'Optional display name, same role as Shape.title — falls back to a generic "{kind} {n}" label (see SightingEditorElement.decorLabel) when absent.'},eastM:{type:"number",doc:"Meters from the observer's own position — eastM positive = east, northM positive = north."},northM:{type:"number"},headingDeg:{type:"number",doc:"Degrees clockwise from true north, same convention as ObserverPose.headingDeg — which way a vehicle is facing, or a witness looking."},lit:{type:"boolean",doc:"Streetlight lamp / vehicle headlights switched on."},litKeyframes:{type:"array",doc:"Sorted by t.",fields:{t:{type:"number"},lit:{type:"boolean"}}},lights:{type:"array",doc:"The object's individual lamps, each with its own place on the body and its own rhythm — see DecorLight, and LIGHT_RIGS for the ready-made sets.",fields:{id:{type:"string"},offsetM:{type:"object",doc:"Where it sits on the object, in meters relative to the object's own centre and heading: +x is its right, +y up, +z its front.",fields:{x:{type:"number"},y:{type:"number"},z:{type:"number"}}},color:{type:"string",doc:"CSS hex colour, as emitted — the regulated red/green/white of navigation lights, the amber of a hazard flasher."},intensity:{type:"number",doc:"Relative brightness, 1 being an ordinary navigation light."},pattern:{type:"object",fields:{kind:{type:"enum",values:["steady","flash"]},perMinute:{type:"number"},dutyCycle:{type:"number"},phase:{type:"number"}}}}},track:{type:"array",doc:"Where the object is over time, when it moves — an aircraft crossing the sky, a car driving past.",fields:{t:{type:"number"},eastM:{type:"number"},northM:{type:"number"},altitudeM:{type:"number",doc:"Meters above the observer's own elevation."},headingDeg:{type:"number"}}},sightingUrl:{type:"string",doc:`For kind "witness" only: the URL of that witness's own sighting.json recording, if known — lets the 3D scene's own context menu offer "view this witness's testimony" (loading it the same way SightingEditorElement.importFromUrl already does).`},windows:{type:"string",doc:"Per-side window opacity, 0 (fully open — the pane is invisible, and DecorSystem.addRoom's own interior wall panel is skipped there too, same as no window at all) to 100 (fully closed — a solid-looking pane)."},color:{type:"string",doc:'What colour this object actually is, as a CSS colour — the same way a shape states its own (see Shape.color), because a recording is written and read by people and "#8f86a8" is a colour a person can type.'},witnessSide:{type:"enum",values:["front","behind","left","right","front-left","front-right","behind-left","behind-right"]},floors:{type:"number",doc:'Kind "building" only: number of upper stories above the ground floor, set when the building is created (default 2, see SightingEditorElement.addDecor) and editable afterward.'},occupiedFloor:{type:"number",doc:'Kind "building" only, meaningful once witnessSide is set: which floor the witness is on, 0 (ground floor) up to floors.'},occludesSourceIds:{type:"array",doc:"Shape sourceIds (see Timeline.sourceIds/Shape) this decor object sits in front of, from the witness's own reported viewpoint — occluding that shape wherever their screen positions line up (see SceneRenderer.isScreenPointOccluded)."},sizeM:{type:"object",doc:"How big it really is, in meters, along its own axes — see DecorSize.",fields:{widthM:{type:"number"},lengthM:{type:"number"},heightM:{type:"number"}}},model:{type:"object",doc:"The real 3D model that stands in for the primitive, if one does — see DecorModelRef.",fields:{id:{type:"string",doc:"Catalogue entry, resolved by the live DecorModelProvider."},url:{type:"string",doc:"Direct glTF/GLB address, taking precedence over `id`."},headingOffsetDeg:{type:"number",doc:"Degrees to turn the loaded model about its own vertical axis so its nose points the way this scene's heading convention expects (0 faces -Z — see DecorSystem's build)."},credit:{type:"object",doc:"Who made it and under what licence.",fields:{title:{type:"string"},author:{type:"string"},license:{type:"string",doc:"Licence name as its author states it, e.g."},sourceUrl:{type:"string",doc:"Where it came from, so the claim can be checked."}}}}}}},b={type:"object",doc:"Which meteorological record `weatherTrack` was looked up from, when it wasn't the witness who stated the conditions — see Sighting.weatherSource.",fields:{id:{type:"string",doc:"Stable dataset id, e.g."},name:{type:"string",doc:"Human-readable dataset name, e.g."},url:{type:"string",doc:"The exact request that produced these values, so the claim stays checkable years later."}}},w={type:"string",doc:"Which INSTRUMENTS entry this was observed through — see Sighting.instrumentId."},f={type:"number",doc:"How long the shutter stayed open, seconds — one value for the whole observation, see Sighting.exposureSeconds."},v={type:"array",doc:"The named moments of the account — see Milestone.",fields:{t:{type:"number",doc:"When, in milliseconds from the start of the recording — the same scale every track uses."},label:{type:"string",doc:'Short label, shown on the seek bar: the letter or number the account itself uses ("A", "B"), or a couple of words where it uses none.'},note:{type:"string",doc:"What happened at that moment, in the account's own words where possible."}}},k={version:e,time:t,endTime:o,durationSeconds:n,utcOffsetHours:i,timeZone:s,place:r,witness:a,caseId:d,description:c,tags:l,timeline:h,witnessTrack:p,weatherTrack:u,weather:y,soundTrack:m,decor:g,weatherSource:b,instrument:w,exposureSeconds:f,milestones:v};export{d as caseId,g as decor,k as default,c as description,n as durationSeconds,o as endTime,f as exposureSeconds,w as instrument,v as milestones,r as place,m as soundTrack,l as tags,t as time,s as timeZone,h as timeline,i as utcOffsetHours,e as version,y as weather,b as weatherSource,u as weatherTrack,a as witness,p as witnessTrack};