Fishing Weather

This page works best on a mobile phone or a smaller screen. to see other updates please go to our..

Weather page for fishing.

For River levels in Northern Ohio

For Ohio Lake Levels

Please Contact us if you would like webpages like this made for your area of the country.

.

<!doctype html>
<html lang="en" class="h-full">
 <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Weather Search</title>
  <script src="https://cdn.tailwindcss.com/3.4.17"></script>
  <script src="/_sdk/element_sdk.js"></script>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&amp;display=swap" rel="stylesheet">
  <style>
    * { font-family: 'Outfit', sans-serif; }
    
    @keyframes float {
      0%, 100% { transform: translateY(0px) rotate(0deg); }
      50% { transform: translateY(-20px) rotate(5deg); }
    }
    
    @keyframes pulse-glow {
      0%, 100% { box-shadow: 0 0 20px rgba(99, 102, 241, 0.3); }
      50% { box-shadow: 0 0 40px rgba(99, 102, 241, 0.6); }
    }
    
    @keyframes gradient-shift {
      0% { background-position: 0% 50%; }
      50% { background-position: 100% 50%; }
      100% { background-position: 0% 50%; }
    }
    
    @keyframes spin-slow {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    
    .animate-float { animation: float 6s ease-in-out infinite; }
    .animate-float-delayed { animation: float 6s ease-in-out infinite 2s; }
    .animate-pulse-glow { animation: pulse-glow 3s ease-in-out infinite; }
    .animate-gradient { 
      background-size: 200% 200%;
      animation: gradient-shift 8s ease infinite; 
    }
    .animate-spin-slow { animation: spin-slow 20s linear infinite; }
    
    .glass-card {
      background: rgba(255, 255, 255, 0.08);
      backdrop-filter: blur(20px);
      border: 1px solid rgba(255, 255, 255, 0.15);
    }
    
    .weather-card {
      background: linear-gradient(135deg, rgba(99, 102, 241, 0.2) 0%, rgba(168, 85, 247, 0.2) 100%);
      backdrop-filter: blur(20px);
      border: 1px solid rgba(255, 255, 255, 0.2);
    }
    
    .forecast-card {
      background: rgba(255, 255, 255, 0.06);
      backdrop-filter: blur(15px);
      border: 1px solid rgba(255, 255, 255, 0.12);
    }
    
    .input-glow:focus {
      box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3), 0 0 30px rgba(99, 102, 241, 0.2);
    }
    
    .blob {
      border-radius: 40% 60% 70% 30% / 40% 50% 60% 50%;
    }

    .wind-indicator {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 60px;
      position: relative;
    }

    .wind-dial {
      position: relative;
      width: 60px;
      height: 60px;
      border-radius: 50%;
      border: 2px solid rgba(147, 197, 253, 0.3);
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(59, 130, 246, 0.1);
    }

    .wind-arrow-small {
      width: 24px;
      height: 24px;
      transition: transform 0.5s ease;
    }
  </style>
  <style>body { box-sizing: border-box; }</style>
  <script src="/_sdk/data_sdk.js" type="text/javascript"></script>
 </head>
 <body class="h-full overflow-auto">
  <div id="app-wrapper" class="min-h-full w-full bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 animate-gradient relative overflow-hidden"><!-- Animated Background Elements -->
   <div class="absolute inset-0 overflow-hidden pointer-events-none">
    <div class="absolute top-10 left-10 w-72 h-72 bg-indigo-500/20 blob animate-float blur-3xl"></div>
    <div class="absolute bottom-20 right-10 w-96 h-96 bg-purple-500/20 blob animate-float-delayed blur-3xl"></div>
    <div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-gradient-to-r from-indigo-500/10 to-purple-500/10 rounded-full animate-spin-slow blur-3xl"></div>
   </div>
   <div class="relative z-10 px-4 py-8 md:py-12 max-w-4xl mx-auto"><!-- Header -->
    <header class="text-center mb-10">
     <div class="inline-flex items-center gap-3 mb-4">
      <div class="w-12 h-12 rounded-2xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-lg shadow-indigo-500/30">
       <svg class="w-7 h-7 text-white" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
       </svg>
      </div>
     </div>
     <h1 id="app-title" class="text-4xl md:text-5xl font-bold bg-gradient-to-r from-white via-purple-200 to-indigo-200 bg-clip-text text-transparent mb-3">Weather Search</h1>
     <p class="text-purple-200/70 text-lg">Enter a US zip code to get current conditions</p>
    </header><!-- Search Form -->
    <form id="search-form" class="mb-8">
     <div class="flex flex-col gap-3">
      <div class="glass-card rounded-3xl p-2"><input type="text" id="zip-input" inputmode="numeric" pattern="[0-9]{5}" maxlength="5" placeholder="Enter zip code (e.g., 90210)" class="w-full bg-white/5 border-0 rounded-2xl px-6 py-4 text-white placeholder-purple-300/50 text-lg focus:outline-none input-glow transition-all duration-300" aria-label="Zip code">
      </div><button type="submit" id="search-btn" class="w-full bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 text-white px-8 py-4 rounded-2xl font-semibold text-lg transition-all duration-300 hover:scale-105 active:scale-95 shadow-lg shadow-indigo-500/30 flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">
       <svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
       </svg><span>Search</span> </button>
     </div>
    </form><!-- Error Message -->
    <div id="error-container" class="hidden mb-6">
     <div class="glass-card rounded-2xl p-4 border-red-500/30 bg-red-500/10">
      <div class="flex items-center gap-3 text-red-300">
       <svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
       </svg>
       <p id="error-message" class="text-sm"></p>
      </div>
     </div>
    </div><!-- Loading State -->
    <div id="loading-container" class="hidden">
     <div class="weather-card rounded-3xl p-8 text-center">
      <div class="w-16 h-16 mx-auto mb-4 rounded-full border-4 border-purple-300/30 border-t-purple-400 animate-spin"></div>
      <p class="text-purple-200">Fetching weather data...</p>
     </div>
    </div><!-- Weather Results -->
    <div id="weather-container" class="hidden">
     <div class="weather-card rounded-3xl p-6 md:p-8 animate-pulse-glow mb-8"><!-- Location Header -->
      <div class="text-center mb-8">
       <div class="inline-flex items-center gap-2 bg-white/10 rounded-full px-4 py-2 mb-4">
        <svg class="w-4 h-4 text-purple-300" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
        </svg><span id="location-name" class="text-purple-200 text-sm font-medium"></span>
       </div>
       <div id="temperature" class="text-7xl md:text-8xl font-bold bg-gradient-to-r from-white to-purple-200 bg-clip-text text-transparent"></div>
       <p class="text-purple-300/70 mt-2">Current Temperature</p>
      </div><!-- Weather Stats Grid -->
      <div class="grid grid-cols-1 sm:grid-cols-2 gap-4"><!-- Wind Direction Card -->
       <div class="glass-card rounded-2xl p-5 group hover:bg-white/10 transition-all duration-300">
        <div class="flex items-center gap-4">
         <div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-cyan-400/20 to-blue-500/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
          <svg id="wind-arrow" class="w-8 h-8 text-cyan-300 transition-transform duration-500" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
          </svg>
         </div>
         <div>
          <p class="text-purple-300/70 text-sm mb-1">Wind Direction</p>
          <p id="wind-direction" class="text-2xl font-bold text-white"></p>
          <p id="wind-speed" class="text-sm text-purple-300/50"></p>
         </div>
        </div>
       </div><!-- Barometric Pressure Card -->
       <div class="glass-card rounded-2xl p-5 group hover:bg-white/10 transition-all duration-300">
        <div class="flex items-center gap-4">
         <div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-amber-400/20 to-orange-500/20 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
          <svg class="w-8 h-8 text-amber-300" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
          </svg>
         </div>
         <div>
          <p class="text-purple-300/70 text-sm mb-1">Barometric Pressure</p>
          <p id="pressure" class="text-2xl font-bold text-white"></p>
          <p id="pressure-trend" class="text-sm text-purple-300/50"></p>
         </div>
        </div>
       </div>
      </div><!-- Last Updated -->
      <div class="mt-6 pt-4 border-t border-white/10 text-center">
       <p id="last-updated" class="text-purple-300/50 text-sm"></p>
      </div>
     </div><!-- 4-Day Forecast -->
     <div>
      <h2 class="text-2xl font-bold text-white mb-4">4-Day Forecast</h2>
      <div id="forecast-container" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
       <!-- Forecast cards will be populated here -->
      </div>
     </div>
    </div><!-- Empty State -->
    <div id="empty-state" class="text-center py-12">
     <div class="w-24 h-24 mx-auto mb-6 rounded-full bg-gradient-to-br from-indigo-500/20 to-purple-500/20 flex items-center justify-center">
      <svg class="w-12 h-12 text-purple-300/50" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
      </svg>
     </div>
     <h3 class="text-xl font-semibold text-purple-200 mb-2">Enter a Zip Code</h3>
     <p class="text-purple-300/50">Search for current weather conditions in any US location</p>
    </div>
   </div>
  </div>
  <script>
    // Default configuration
    const defaultConfig = {
      app_title: 'Weather Search',
      search_placeholder: 'Enter zip code (e.g., 90210)',
      background_color: '#0f172a',
      surface_color: 'rgba(255, 255, 255, 0.08)',
      text_color: '#e9d5ff',
      primary_action_color: '#6366f1',
      secondary_action_color: '#a855f7',
      font_family: 'Outfit',
      font_size: 16
    };

    // Initialize Element SDK
    if (window.elementSdk) {
      window.elementSdk.init({
        defaultConfig,
        onConfigChange: async (config) => {
          const title = config.app_title || defaultConfig.app_title;
          const placeholder = config.search_placeholder || defaultConfig.search_placeholder;
          const fontFamily = config.font_family || defaultConfig.font_family;
          const fontSize = config.font_size || defaultConfig.font_size;
          const primaryColor = config.primary_action_color || defaultConfig.primary_action_color;
          const secondaryColor = config.secondary_action_color || defaultConfig.secondary_action_color;
          
          document.getElementById('app-title').textContent = title;
          document.getElementById('zip-input').placeholder = placeholder;
          
          // Apply font
          document.body.style.fontFamily = `${fontFamily}, Outfit, sans-serif`;
          document.getElementById('app-title').style.fontSize = `${fontSize * 2.5}px`;
          
          // Apply colors to buttons
          const searchBtn = document.getElementById('search-btn');
          searchBtn.style.background = `linear-gradient(to right, ${primaryColor}, ${secondaryColor})`;
        },
        mapToCapabilities: (config) => ({
          recolorables: [
            {
              get: () => config.primary_action_color || defaultConfig.primary_action_color,
              set: (value) => { config.primary_action_color = value; window.elementSdk.setConfig({ primary_action_color: value }); }
            },
            {
              get: () => config.secondary_action_color || defaultConfig.secondary_action_color,
              set: (value) => { config.secondary_action_color = value; window.elementSdk.setConfig({ secondary_action_color: value }); }
            }
          ],
          borderables: [],
          fontEditable: {
            get: () => config.font_family || defaultConfig.font_family,
            set: (value) => { config.font_family = value; window.elementSdk.setConfig({ font_family: value }); }
          },
          fontSizeable: {
            get: () => config.font_size || defaultConfig.font_size,
            set: (value) => { config.font_size = value; window.elementSdk.setConfig({ font_size: value }); }
          }
        }),
        mapToEditPanelValues: (config) => new Map([
          ['app_title', config.app_title || defaultConfig.app_title],
          ['search_placeholder', config.search_placeholder || defaultConfig.search_placeholder]
        ])
      });
    }

    // DOM Elements
    const form = document.getElementById('search-form');
    const zipInput = document.getElementById('zip-input');
    const searchBtn = document.getElementById('search-btn');
    const loadingContainer = document.getElementById('loading-container');
    const weatherContainer = document.getElementById('weather-container');
    const errorContainer = document.getElementById('error-container');
    const errorMessage = document.getElementById('error-message');
    const emptyState = document.getElementById('empty-state');
    const forecastContainer = document.getElementById('forecast-container');

    // Convert degrees to compass direction
    function degreesToDirection(degrees) {
      const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
      const index = Math.round(degrees / 22.5) % 16;
      return directions[index];
    }

    // Get pressure trend description
    function getPressureTrend(pressure) {
      if (pressure > 1020) return 'High pressure - Fair weather';
      if (pressure < 1000) return 'Low pressure - Stormy';
      return 'Normal pressure - Stable';
    }

    // Format time
    function formatTime(isoString) {
      const date = new Date(isoString);
      return date.toLocaleString('en-US', {
        weekday: 'short',
        month: 'short',
        day: 'numeric',
        hour: 'numeric',
        minute: '2-digit',
        hour12: true
      });
    }

    // Format date for forecast
    function formatDate(dateString) {
      const date = new Date(dateString);
      return date.toLocaleString('en-US', {
        weekday: 'short',
        month: 'short',
        day: 'numeric'
      });
    }

    // Show error
    function showError(message) {
      errorMessage.textContent = message;
      errorContainer.classList.remove('hidden');
      loadingContainer.classList.add('hidden');
      weatherContainer.classList.add('hidden');
      emptyState.classList.add('hidden');
    }

    // Hide error
    function hideError() {
      errorContainer.classList.add('hidden');
    }

    // Set loading state
    function setLoading(isLoading) {
      if (isLoading) {
        loadingContainer.classList.remove('hidden');
        weatherContainer.classList.add('hidden');
        emptyState.classList.add('hidden');
        searchBtn.disabled = true;
      } else {
        loadingContainer.classList.add('hidden');
        searchBtn.disabled = false;
      }
    }

    // Create forecast card
    function createForecastCard(date, tempMax, tempMin, windDir, windSpeed) {
      const card = document.createElement('div');
      card.className = 'forecast-card rounded-2xl p-4 text-center hover:bg-white/10 transition-all duration-300';
      
      const directionLabel = degreesToDirection(windDir);
      
      card.innerHTML = `
        <p class="text-purple-300 text-sm font-medium mb-3">${date}</p>
        <div class="mb-4">
          <p class="text-2xl font-bold text-white mb-1">${Math.round(tempMax)}°</p>
          <p class="text-sm text-purple-300/60">${Math.round(tempMin)}°</p>
        </div>
        <div class="wind-indicator mb-3">
          <div class="wind-dial">
            <svg class="wind-arrow-small text-cyan-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="transform: rotate(${windDir + 180}deg);">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
            </svg>
          </div>
        </div>
        <p class="text-lg font-semibold text-cyan-300 mb-1">${directionLabel}</p>
        <p class="text-xs text-purple-300/50">${Math.round(windSpeed)} mph</p>
      `;
      
      return card;
    }

    // Fetch weather data
    async function fetchWeather(zipCode) {
      hideError();
      setLoading(true);

      try {
        // First, geocode the zip code using Open-Meteo's geocoding API
        const geoResponse = await fetch(
          `https://geocoding-api.open-meteo.com/v1/search?name=${zipCode}&count=1&language=en&format=json&country=US`
        );
        
        if (!geoResponse.ok) {
          throw new Error('Unable to connect to weather service');
        }

        const geoData = await geoResponse.json();
        
        if (!geoData.results || geoData.results.length === 0) {
          // Try with zip code as postal code search
          const postalResponse = await fetch(
            `https://geocoding-api.open-meteo.com/v1/search?name=${zipCode}&count=10&language=en&format=json`
          );
          const postalData = await postalResponse.json();
          
          // Filter for US results
          const usResult = postalData.results?.find(r => r.country_code === 'US');
          
          if (!usResult) {
            throw new Error('Could not find location for this zip code. Please try a city name or different zip code.');
          }
          
          geoData.results = [usResult];
        }

        const location = geoData.results[0];
        const { latitude, longitude, name, admin1 } = location;

        // Fetch weather data with 4-day forecast
        const weatherResponse = await fetch(
          `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,wind_speed_10m,wind_direction_10m,surface_pressure&daily=temperature_2m_max,temperature_2m_min,wind_direction_10m_dominant,wind_speed_10m_max&temperature_unit=fahrenheit&wind_speed_unit=mph&timezone=auto`
        );

        if (!weatherResponse.ok) {
          throw new Error('Unable to fetch weather data');
        }

        const weatherData = await weatherResponse.json();
        const current = weatherData.current;
        const daily = weatherData.daily;

        // Update current weather UI
        document.getElementById('location-name').textContent = `${name}${admin1 ? `, ${admin1}` : ''}`;
        document.getElementById('temperature').textContent = `${Math.round(current.temperature_2m)}°F`;
        
        const windDir = degreesToDirection(current.wind_direction_10m);
        document.getElementById('wind-direction').textContent = windDir;
        document.getElementById('wind-speed').textContent = `${Math.round(current.wind_speed_10m)} mph`;
        
        // Rotate wind arrow
        const windArrow = document.getElementById('wind-arrow');
        windArrow.style.transform = `rotate(${current.wind_direction_10m + 180}deg)`;
        
        const pressure = Math.round(current.surface_pressure);
        document.getElementById('pressure').textContent = `${pressure} hPa`;
        document.getElementById('pressure-trend').textContent = getPressureTrend(pressure);
        
        document.getElementById('last-updated').textContent = `Last updated: ${formatTime(current.time)}`;

        // Populate 4-day forecast
        forecastContainer.innerHTML = '';
        for (let i = 0; i < 4; i++) {
          const card = createForecastCard(
            formatDate(daily.time[i]),
            daily.temperature_2m_max[i],
            daily.temperature_2m_min[i],
            daily.wind_direction_10m_dominant[i],
            daily.wind_speed_10m_max[i]
          );
          forecastContainer.appendChild(card);
        }

        setLoading(false);
        weatherContainer.classList.remove('hidden');
        emptyState.classList.add('hidden');

      } catch (error) {
        setLoading(false);
        showError(error.message || 'An error occurred while fetching weather data');
      }
    }

    // Form submission
    form.addEventListener('submit', (e) => {
      e.preventDefault();
      const zipCode = zipInput.value.trim();
      
      if (!zipCode) {
        showError('Please enter a zip code');
        return;
      }
      
      if (!/^\d{5}$/.test(zipCode)) {
        showError('Please enter a valid 5-digit US zip code');
        return;
      }
      
      fetchWeather(zipCode);
    });

    // Allow only numeric input
    zipInput.addEventListener('input', (e) => {
      e.target.value = e.target.value.replace(/\D/g, '').slice(0, 5);
    });
  </script>
 <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9d1ec77151a41098',t:'MTc3MTc2NjQwOC4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
</html>