<!doctype html>

<html lang="en">

<head>

<meta charset="utf-8" />

<title>Atlantic Hurricane Dashboard — Mobile (FL-first)</title>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />

<meta name="color-scheme" content="dark light" />

<style>

  :root{

    --bg:#0b1220;--card:#121b2d;--fg:#eaf2ff;--muted:#9fb4d7;--accent:#5fd1ff;

    --ok:#ffd166;--bad:#ff6b6b;--gap:14px;--r:14px

  }

  @media (prefers-color-scheme: light){

    :root{--bg:#f7fbff;--card:#ffffff;--fg:#0a2540;--muted:#456;--accent:#0077ff}

  }

  *{box-sizing:border-box}

  html,body{margin:0;background:var(--bg);color:var(--fg);font-family:system-ui,-apple-system,Roboto,Inter,Segoe UI,Arial,sans-serif}

  header{position:sticky;top:0;z-index:10;background:linear-gradient(180deg,rgba(0,0,0,.35),transparent);backdrop-filter:blur(6px)}

  .wrap{max-width:860px;margin:0 auto;padding:16px}

  h1{margin:0;font-size:1.25rem}

  p.small{color:var(--muted);margin:.25rem 0 0}

  .grid{display:grid;grid-template-columns:1fr;gap:var(--gap)}

  .card{background:var(--card);border-radius:var(--r);box-shadow:0 8px 24px rgba(0,0,0,.18);overflow:hidden}

  .card h3{font-size:1rem;margin:0;padding:12px 14px;border-bottom:1px solid rgba(255,255,255,.06)}

  .body{padding:12px 14px}

  .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}

  .btn{appearance:none;border:1px solid #2b4266;background:#15223a;color:#eaf2ff;

       padding:12px 14px;border-radius:12px;font-size:.95rem;line-height:1;cursor:pointer;flex:1;min-width:120px;text-align:center}

  .btn.accent{background:var(--accent);color:#012038;border-color:transparent;font-weight:600}

  .btn.ghost{background:transparent}

  .btn.warn{background:var(--bad);border-color:transparent;color:#2b0000}

  .pill{padding:10px 12px;border-radius:999px;background:#162641;border:1px solid #2b4266;color:#eaf2ff;min-width:120px}

  .hint{color:var(--muted);font-size:.9rem}

  .imgbox{display:grid;place-items:center;background:#0f1728}

  .imgbox img{display:block;max-width:100%;height:auto}

  .frame{aspect-ratio:16/9;background:#0f1728;border:0;width:100%}

  .toggles{display:flex;gap:8px;flex-wrap:wrap}

  .toggle{padding:10px 12px;border-radius:999px;border:1px solid #2b4266;background:#0f1728;color:#eaf2ff;font-size:.9rem}

  .toggle.active{background:#1f3358;border-color:#4a6aa0}

  .switch{display:flex;align-items:center;gap:8px}

  .switch input{appearance:none;width:48px;height:28px;border-radius:999px;background:#1a2a45;position:relative;border:1px solid #2b4266}

  .switch input:checked{background:#0c663f;border-color:#0c663f}

  .switch input::after{content:"";position:absolute;width:22px;height:22px;border-radius:50%;background:#cfe5ff;top:2px;left:2px;transition:all .2s}

  .switch input:checked::after{left:24px;background:#eafff7}

  footer{color:var(--muted);font-size:.9rem;margin:10px 0 24px}

  .toolbar{display:grid;grid-template-columns:1fr 1fr;gap:10px}

  @media (min-width:620px){ .toolbar{grid-template-columns:repeat(4,1fr)} }

  /* preset chips */

  .chip{padding:10px 12px;border:1px solid #2b4266;border-radius:999px;background:#0f1728;color:#eaf2ff;font-size:.9rem;cursor:pointer}

  .chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}

  /* touch-friendly */

  .btn, .toggle, .chip {touch-action:manipulation}

</style>

</head>

<body>

<header>

  <div class="wrap">

    <h1>Atlantic Hurricane Dashboard</h1>

    <p class="small">Florida-first view • NHC outlooks • Live map • Silent satellite • Quick resources</p>

    <div class="row" style="margin-top:10px">

      <input id="lat" class="pill" inputmode="decimal" placeholder="Lat e.g. 27.95" />

      <input id="lon" class="pill" inputmode="decimal" placeholder="Lon e.g. -82.46" />

      <button class="btn accent" id="center">Center Map</button>

      <button class="btn" id="geo">My Location</button>

    </div>

    <div class="row" style="margin-top:8px">

      <label class="switch">

        🔇 Silent Mode

        <input type="checkbox" id="silent" />

      </label>

      <button class="btn" id="zoomEarth">🛰️ Zoom Earth</button>

      <button class="btn ghost" id="saveHome" title="Save current center as Home">⭐ Set Home</button>

      <button class="btn" id="goHome" title="Go to saved Home">🏠 Home</button>

    </div>

    <!-- Florida & nearby presets -->

    <div class="chips" id="presets">

      <button class="chip" data-lat="27.95" data-lon="-82.46" data-zoom="7">Tampa Bay</button>

      <button class="chip" data-lat="24" data-lon="-90" data-zoom="5">Gulf of Mexico</button>

      <button class="chip" data-lat="27.0" data-lon="-81.5" data-zoom="6">Florida Peninsula</button>

      <button class="chip" data-lat="25.6" data-lon="-80.2" data-zoom="7">SE Florida</button>

      <button class="chip" data-lat="30.3" data-lon="-86.6" data-zoom="7">FL Panhandle</button>

      <button class="chip" data-lat="16" data-lon="-72" data-zoom="5">Caribbean</button>

    </div>

  </div>

</header>

<main class="wrap">

  <!-- NHC Outlooks -->

  <section class="grid">

    <article class="card">

      <h3>NHC Graphical Tropical Weather Outlook — 2-Day (Atlantic)</h3>

      <div class="imgbox">

        <img id="twoDay" alt="NHC 2-day outlook (Atlantic)" loading="lazy"

             src="https://www.nhc.noaa.gov/xgtwo/two_atl_2d0.png" />

      </div>

      <div class="body toolbar">

        <button class="btn" data-open="https://www.nhc.noaa.gov/gtwo.php?basin=atlc">Open Outlook</button>

        <button class="btn ghost" id="r2">↻ Refresh</button>

        <button class="btn" data-open="https://www.nhc.noaa.gov/">NHC Active</button>

        <button class="btn" data-open="https://www.nhc.noaa.gov/text/refresh/MIATWOAT+shtml/">Text Outlook</button>

      </div>

    </article>

    <article class="card">

      <h3>NHC Graphical Tropical Weather Outlook — 7-Day (Atlantic)</h3>

      <div class="imgbox">

        <img id="sevenDay" alt="NHC 7-day outlook (Atlantic)" loading="lazy"

             src="https://www.nhc.noaa.gov/xgtwo/two_atl_7d0.png" />

      </div>

      <div class="body toolbar">

        <button class="btn" data-open="https://www.nhc.noaa.gov/gtwo.php?basin=atlc&fdays=7">Open 7-Day</button>

        <button class="btn ghost" id="r7">↻ Refresh</button>

        <button class="btn" data-open="https://www.nhc.noaa.gov/graphics_at1.shtml">AL Basin Graphics</button>

        <button class="btn" data-open="https://www.nhc.noaa.gov/archive/">Advisory Archive</button>

      </div>

    </article>

  </section>

  <!-- Live Map + Silent Satellite -->

  <section class="grid">

    <article class="card">

      <h3>Live Map — Windy (interactive) / NOAA (silent)</h3>

      <div class="body">

        <div class="toggles" id="layers">

          <button class="toggle active" data-layer="satellite">🛰️ Satellite</button>

          <button class="toggle" data-layer="radar">🌧️ Radar</button>

          <button class="toggle" data-layer="wind">💨 Wind</button>

          <button class="toggle" data-layer="pressure">🌀 Pressure</button>

        </div>

        <p class="hint" style="margin-top:6px">Tip: Turn on 🔇 Silent Mode to switch to a noise-free GOES image.</p>

      </div>

      <!-- Windy iframe (default) -->

      <iframe id="windy" class="frame" title="Windy Map" loading="lazy" referrerpolicy="no-referrer"></iframe>

      <!-- NOAA silent image (shown when Silent Mode is on) -->

      <div class="imgbox" id="noaaBox" style="display:none">

        <img id="goes" alt="GOES-East GeoColor (latest)" loading="lazy"

             src="https://cdn.star.nesdis.noaa.gov/GOES16/ABI/FD/GEOCOLOR/latest.jpg"

             style="width:100%;height:100%;object-fit:contain" />

      </div>

      <div class="body row">

        <button class="btn" id="reset">↺ Center Florida</button>

        <button class="btn" data-open="https://www.noaa.gov/hurricane-season">NOAA Season Hub</button>

        <button class="btn" data-open="https://tropical.colostate.edu/forecasting.html">CSU Forecasts</button>

        <button class="btn" data-open="https://alerts.weather.gov/">NWS Alerts Map</button>

      </div>

    </article>

  </section>

  <!-- Tips -->

  <section class="grid">

    <article class="card">

      <h3>Quick Tips</h3>

      <div class="body">

        <ul style="margin:8px 0 0 18px; line-height:1.35">

          <li>Use 7-day outlook to spot early “areas to watch.”</li>

          <li>Tap Zoom Earth for tracks/winds; NHC pages for cones & hazards.</li>

          <li><b>Safety first:</b> Follow local NWS guidance over any third-party map.</li>

        </ul>

      </div>

    </article>

  </section>

  <footer class="wrap">

    Sources: NOAA/NHC, Windy, NOAA STAR (GOES-East), CSU. Built for phones—fast taps, low bandwidth.

  </footer>

</main>

<script>

  // ---------- helpers ----------

  function openExt(url){ window.open(url,'_blank','noopener'); }

  document.querySelectorAll('[data-open]').forEach(b=>b.onclick=()=>openExt(b.dataset.open));

  function getParam(name){ return new URLSearchParams(location.search).get(name); }

  // refresh images with cache-buster

  function refresh(id){

    const el = document.getElementById(id);

    const u = new URL(el.src); u.searchParams.set('t', Date.now()); el.src = u.toString();

  }

  document.getElementById('r2').onclick = ()=>refresh('twoDay');

  document.getElementById('r7').onclick = ()=>refresh('sevenDay');

  // ---------- Map state ----------

  const windy = document.getElementById('windy');

  const layers = document.getElementById('layers');

  const silent = document.getElementById('silent');

  const noaaBox = document.getElementById('noaaBox');

  // Florida-centric default (good Tampa Bay vicinity)

  const flDefault = { lat:27.6, lon:-82.6, zoom:6 }; // west/central FL view

  const basinDefault = { lat:24.0, lon:-60.0, zoom:4 };

  let state = { ...flDefault, layer:'satellite' };

  // URL params override defaults

  const pLat = parseFloat(getParam('lat'));

  const pLon = parseFloat(getParam('lon'));

  const pZoom = parseInt(getParam('zoom')||'',10);

  if(Number.isFinite(pLat) && Number.isFinite(pLon)){ state.lat = pLat; state.lon = pLon; }

  if(Number.isFinite(pZoom)) state.zoom = pZoom;

  // Restore Home if present (and no URL center)

  const homeJSON = localStorage.getItem('wt_home_center');

  if(!(Number.isFinite(pLat) && Number.isFinite(pLon)) && homeJSON){

    try{

      const h = JSON.parse(homeJSON);

      if(Number.isFinite(h.lat) && Number.isFinite(h.lon)){

        state.lat = h.lat; state.lon = h.lon; state.zoom = h.zoom ?? state.zoom;

      }

    }catch(e){}

  }

  // Build Windy URL

  function windySrc(){

    const overlay = ({satellite:'satellite',radar:'radar',wind:'wind',pressure:'pressure'})[state.layer] || 'satellite';

    return `https://embed.windy.com/embed2.html?lat=${state.lat.toFixed(3)}&lon=${state.lon.toFixed(3)}&zoom=${state.zoom}`+

           `&level=surface&overlay=${overlay}&product=ecmwf&menu=&message=true&marker=&calendar=now&pressure=true`+

           `&type=map&location=coordinates&detail=&detailLat=${state.lat.toFixed(3)}&detailLon=${state.lon.toFixed(3)}`+

           `&metricWind=kt&metricTemp=%C2%B0F&sound=false`;

  }

  function loadWindy(){ windy.src = windySrc(); }

  // Try to auto-center if geolocation already granted (no surprise prompts)

  async function tryAutoGeo(){

    try{

      if(!navigator.permissions || !navigator.geolocation) { loadWindy(); return; }

      const perm = await navigator.permissions.query({ name:'geolocation' });

      if(perm.state === 'granted'){

        navigator.geolocation.getCurrentPosition(pos=>{

          state.lat = pos.coords.latitude; state.lon = pos.coords.longitude; state.zoom = 7;

          loadWindy();

          // also prefill inputs

          document.getElementById('lat').value = state.lat.toFixed(4);

          document.getElementById('lon').value = state.lon.toFixed(4);

        }, ()=>loadWindy());

      } else {

        // keep Florida default without prompting

        loadWindy();

      }

    } catch(e){ loadWindy(); }

  }

  tryAutoGeo();

  // ---------- layer toggles ----------

  layers.addEventListener('click', e=>{

    const btn = e.target.closest('.toggle'); if(!btn) return;

    layers.querySelectorAll('.toggle').forEach(b=>b.classList.remove('active'));

    btn.classList.add('active');

    state.layer = btn.dataset.layer;

    if(!silent.checked) loadWindy();

  });

  // ---------- controls ----------

  document.getElementById('reset').onclick = ()=>{

    state = { ...state, ...flDefault };

    if(!silent.checked) loadWindy();

  };

  document.getElementById('center').onclick = ()=>{

    const lat = parseFloat(document.getElementById('lat').value);

    const lon = parseFloat(document.getElementById('lon').value);

    if(Number.isFinite(lat) && Number.isFinite(lon)){

      state.lat = lat; state.lon = lon; state.zoom = 7;

      if(!silent.checked) loadWindy();

    }else alert('Enter valid numbers for lat/lon (e.g. 27.95 and -82.46).');

  };

  document.getElementById('geo').onclick = ()=>{

    if(!navigator.geolocation) return alert('Geolocation not supported.');

    navigator.geolocation.getCurrentPosition(pos=>{

      state.lat = pos.coords.latitude; state.lon = pos.coords.longitude; state.zoom = 7;

      document.getElementById('lat').value = state.lat.toFixed(4);

      document.getElementById('lon').value = state.lon.toFixed(4);

      if(!silent.checked) loadWindy();

    }, ()=>alert('Could not get your location. You can type lat/lon manually.'));

  };

  document.getElementById('zoomEarth').onclick = ()=>{

    const url = Number.isFinite(state.lat) && Number.isFinite(state.lon)

      ? `https://zoom.earth/#view=${state.lat},${state.lon},7z,now,labels:off`

      : 'https://zoom.earth/hurricanes/';

    openExt(url);

  };

  // ---------- Silent Mode (image only, auto-refresh) ----------

  let goesTimer=null;

  function startGOES(){ stopGOES(); refresh('goes'); goesTimer=setInterval(()=>refresh('goes'), 5*60*1000); }

  function stopGOES(){ if(goesTimer){ clearInterval(goesTimer); goesTimer=null; } }

  silent.addEventListener('change', ()=>{

    if(silent.checked){

      windy.style.display='none';

      noaaBox.style.display='grid';

      startGOES();

    }else{

      stopGOES();

      noaaBox.style.display='none';

      windy.style.display='block';

      loadWindy();

    }

  });

  // ---------- Presets + Home ----------

  document.getElementById('presets').addEventListener('click', (e)=>{

    const chip = e.target.closest('.chip'); if(!chip) return;

    state.lat = parseFloat(chip.dataset.lat);

    state.lon = parseFloat(chip.dataset.lon);

    state.zoom = parseInt(chip.dataset.zoom,10) || 6;

    if(!silent.checked) loadWindy();

    document.getElementById('lat').value = state.lat.toFixed(4);

    document.getElementById('lon').value = state.lon.toFixed(4);

  });

  document.getElementById('saveHome').onclick = ()=>{

    const home = { lat: state.lat, lon: state.lon, zoom: state.zoom };

    localStorage.setItem('wt_home_center', JSON.stringify(home));

    alert('Saved current center as Home.');

  };

  document.getElementById('goHome').onclick = ()=>{

    const j = localStorage.getItem('wt_home_center');

    if(!j) return alert('No Home saved yet. Tap ⭐ Set Home first.');

    try{

      const h = JSON.parse(j);

      if(Number.isFinite(h.lat) && Number.isFinite(h.lon)){

        state.lat = h.lat; state.lon = h.lon; state.zoom = h.zoom ?? 7;

        if(!silent.checked) loadWindy();

        document.getElementById('lat').value = state.lat.toFixed(4);

        document.getElementById('lon').value = state.lon.toFixed(4);

      }

    }catch(e){ alert('Home data corrupted; please set it again.'); }

  };

  // initial NHC refresh (deferred for snappier first paint)

  window.addEventListener('load', ()=> setTimeout(()=>{ refresh('twoDay'); refresh('sevenDay'); }, 700));

</script>

</body>

</html>