<!-- COMPASS · part 1 -->
===== FILE: public/compass/INSTALL.txt =====
HAWAIIAN COMPASS — static install
================================
Backup-first: tar your live compass dir before touching it.

1. Extract so that the `compass/` folder lands inside the site's
   static public dir (on the box: /opt/epistles/public/compass).
2. No server-code change needed — express.static serves it at /compass/.
3. No restart required for static files (a restart is harmless).
4. Verify live: https://epistles.xyz/compass/
   - dial pans with drag, nudge buttons step the bearing
   - red-on-black HUD dress matches the site
   - needle glows ice-blue, tail stays dim red


===== FILE: public/compass/assets/index-CtenZ_qU.js =====
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))s(n);new MutationObserver(n=>{for(const o of n)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&s(i)}).observe(document,{childList:!0,subtree:!0});function r(n){const o={};return n.integrity&&(o.integrity=n.integrity),n.referrerPolicy&&(o.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?o.credentials="include":n.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(n){if(n.ep)return;n.ep=!0;const o=r(n);fetch(n.href,o)}})();const L=[{short:"A",name:"Ākau",english:"north"},{short:"Hi",name:"Hikina",english:"east"},{short:"He",name:"Hema",english:"south"},{short:"K",name:"Komohana",english:"west"}],M=360/32;function F(e){const t=(e%360+360)%360;return t===0?0:t}function g(e,t){const r=10**t;return Math.round((e+Number.EPSILON)*r)/r}function k(e){const t=(e%32+32)%32,r=Math.floor(t/8),s=t%8,n=L[r],o=L[(r+1)%4],i=[[n.short,n.name],[`${n.short} ma ${o.short}`,`${n.name} ma ${o.name}`],[`${n.short} ${n.short} ${o.short}`,`${n.name} ${n.name} ${o.name}`],[`${n.short}${o.short} ma ${n.short}`,`${n.name}-${o.name} ma ${n.name}`],[`${n.short}${o.short}`,`${n.name}-${o.name}`],[`${n.short}${o.short} ma ${o.short}`,`${n.name}-${o.name} ma ${o.name}`],[`${o.short} me ${n.short}`,`${o.name} me ${n.name}`],[`${o.short} ma ${n.short}`,`${o.name} ma ${n.name}`]],[c,a]=i[s];return{index:t,bearing:g(t*M,4),mark:c,spoken:a,rank:s===0?"cardinal":s===4?"intercardinal":s%2===0?"half":"by"}}const O=Array.from({length:32},(e,t)=>k(t));function N(e){return g(F(e),4)}function q(e){const t=N(e),r=t*Math.PI/180,s=Math.round(t/M)%32,n=k(s);let o=t-n.bearing;o>180&&(o-=360),o<-180&&(o+=360);const i=N(t+180),c=k(Math.round(i/M)%32);let a;return t===0?a="north":t===90?a="east":t===180?a="south":t===270?a="west":t<90?a=`north ${f(t)} east`:t<180?a=`south ${f(180-t)} east`:t<270?a=`south ${f(t-180)} west`:a=`north ${f(360-t)} west`,{bearing:t,radians:g(r,6),northing:g(Math.cos(r),6),easting:g(Math.sin(r),6),point:n,offset:g(o,4),reciprocal:i,reciprocalPoint:c,quadrant:a,next:k((s+1)%32),previous:k((s+31)%32)}}function f(e,t=1){const r=g(F(e),t);return(r===360?0:r).toFixed(t)}function D(e,t=1){const r=g(e,t),s=Math.abs(r).toFixed(t);return r>0?`+${s}°`:r<0?`−${s}°`:`${s}°`}const b=500,E=500,d=458,p=document.getElementById("card"),x=document.getElementById("needle"),u={bearing:document.getElementById("bearing"),spoken:document.getElementById("spoken"),mark:document.getElementById("mark"),offset:document.getElementById("offset"),quadrant:document.getElementById("quadrant"),reciprocal:document.getElementById("reciprocal"),reciprocalName:document.getElementById("reciprocal-name"),northing:document.getElementById("northing"),easting:document.getElementById("easting"),radians:document.getElementById("radians"),pointBearing:document.getElementById("point-bearing")},I=document.getElementById("dial"),w=document.getElementById("bearing-field");function l(e,t){const r=e*Math.PI/180;return[b+t*Math.sin(r),E-t*Math.cos(r)]}function m(e,t={},r=p){const s=document.createElementNS("http://www.w3.org/2000/svg",e);for(const[n,o]of Object.entries(t))s.setAttribute(n,o);return r.appendChild(s),s}function P(e,t,r,s){const[n,o]=l(e,t),[i,c]=l(e,r);m("line",{x1:n,y1:o,x2:i,y2:c,...s})}function h(e,t){m("circle",{cx:b,cy:E,r:e,fill:"none",...t})}function K(e,t,r,s){const n=(t+r)/2,o=[l(e,r),l(e+s,n),l(e,t),l(e-s,n)];m("polygon",{points:o.map(([i,c])=>`${i.toFixed(3)},${c.toFixed(3)}`).join(" "),class:"diamond"})}function R(e,t,r){const[s,n]=l(e,t),o=[];for(let i=0;i<10;i+=1){const c=(-90+i*36)*(Math.PI/180),a=i%2===0?r:r*.4;o.push([s+a*Math.cos(c),n+a*Math.sin(c)])}m("polygon",{points:o.map(([i,c])=>`${i.toFixed(3)},${c.toFixed(3)}`).join(" "),class:"star"})}function H(){h(d,{class:"ring rim"}),h(d-28,{class:"ring"}),h(d-46,{class:"ring fine"}),h(318,{class:"ring"}),h(248,{class:"ring fine"}),h(168,{class:"ring"}),h(78,{class:"ring fine"});for(let e=0;e<360;e+=1){const t=e%10===0,r=e%5===0,s=t?d-28:r?d-18:d-12;if(P(e,s,d,{class:t?"tick ten":r?"tick five":"tick"}),t){const[n,o]=