// ------------------------------------------------------------------------
//  components/DayTabs.js - Day-tab strip, pro-prompt card, confidence
//  band bar, and filter-profile row for the UTCIForecast app.
//
//  Extracted from app.js to keep the main component under the AI edit
//  safe-zone. All state lives in useAppState - this component is purely
//  presentational and receives everything it needs as props.
//
//  Props:
//    days                   - array of day objects from buildHourlyRows
//    selectedDay            - index of the active day tab
//    setSelectedDay         - setter for selectedDay
//    isPro                  - boolean Pro status
//    openRestore            - opens the "Already subscribed?" restore modal
//    FREE_DAYS              - number of free days
//    proPromptDay           - index of locked day that was clicked
//    setProPromptDay        - setter for proPromptDay
//    proPromptSource        - string key for upsell copy
//    setProPromptSource     - setter for proPromptSource
//    activeProfile          - current filter profile key
//    activateProfile        - function to switch profile
//    activeCols             - effective column set for current profile
//    visibleCols            - object of col-key -> boolean visibility
//    activityOptions        - dropdown options for Activities selector
//    placeOptions           - dropdown options for Places selector
//    workOptions            - dropdown options for Work selector
//    activityValue          - current activity dropdown value
//    activityLabel          - current activity dropdown label
//    placeValue             - current place dropdown value
//    placeLabel             - current place dropdown label
//    workValue              - current work dropdown value
//    workLabel              - current work dropdown label
//    outdoorsVariant        - current outdoors sub-variant key
//    setOutdoorsVariantAndSave - setter that also persists to localStorage
//    setActiveProfile       - raw setter for activeProfile
//    setVisibleCols         - raw setter for visibleCols
//    setIndoorMode          - setter for indoorMode
//    setIndoorManaged       - setter for indoorManaged
//    dayTabsRef             - ref for the scrollable tab strip element
//    canScrollLeft          - boolean for left-fade chevron
//    canScrollRight         - boolean for right-fade chevron
//    scrollDayTabs          - function(dir) to scroll strip left/right
// ------------------------------------------------------------------------

import { h, Fragment } from '../../vendor/preact.js';
import { useRef, useEffect, useState } from '../../vendor/preact-hooks.js';
import htm from '../../vendor/htm.js';
import { confidenceBand, utciCategory } from '../utils.js';
import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, activityVariantKeys, placeVariantKeys, workVariantKeys } from '../config.js';
import { CloudIcon, PrecipIcon, CustomSelect } from '../components.js';
import { SubscribeModal } from './SubscribeModal.js';

const html = htm.bind(h);

// Active-tab variant: a more solid (less pastelised) version of the weather
// colour, drawn as a radial gradient whose strong colour sits at the outer
// edge and softens toward a lighter centre — so the hue reads as radiating
// inward from the outside of the tab.
// edgeBlend pastelises the outer edge (higher = lighter). centerBlend, when
// supplied, blends the centre straight from the raw colour for a lighter core
// (used by the Danger tier to keep a dark edge but a light centre); otherwise
// the centre is just a lightened version of the edge (original behaviour).
function weatherGradientNeutral(r, g, b, edgeBlend = 0.50, centerBlend = null) {
  const blend   = (c, amt) => Math.round(c + (255 - c) * amt);
  const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38));
  const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)];
  const [cr, cg, cb] = centerBlend != null
    ? [blend(r, centerBlend), blend(g, centerBlend), blend(b, centerBlend)]
    : [lighten(er), lighten(eg), lighten(eb)];
  const center = `rgb(${cr},${cg},${cb})`;
  const edge   = `rgb(${er},${eg},${eb})`;
  return `radial-gradient(circle at 50% 50%, ${center} 0%, ${center} 28%, ${edge} 100%)`;
}

function weatherGradientActive(r, g, b, edgeBlend = 0.42, centerBlend = null) {
  const blend   = (c, amt) => Math.round(c + (255 - c) * amt);
  const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.38));
  const [er, eg, eb] = [blend(r, edgeBlend), blend(g, edgeBlend), blend(b, edgeBlend)];
  const [cr, cg, cb] = centerBlend != null
    ? [blend(r, centerBlend), blend(g, centerBlend), blend(b, centerBlend)]
    : [lighten(er), lighten(eg), lighten(eb)];
  const center = `rgb(${cr},${cg},${cb})`;
  const edge   = `rgb(${er},${eg},${eb})`;
  return `radial-gradient(circle at 50% 50%, ${center} 0%, ${center} 28%, ${edge} 100%)`;
}

export function DayTabs({
  days,
  selectedDay, setSelectedDay,
  isPro,
  openRestore,
  proPromptDay, setProPromptDay,
  proPromptSource, setProPromptSource,
  activeProfile, activateProfile, activeCols,
  visibleCols,
  activityOptions, placeOptions, workOptions,
  activityValue, activityLabel,
  placeValue, placeLabel,
  workValue, workLabel,
  outdoorsVariant, setOutdoorsVariantAndSave,
  setActiveProfile, setVisibleCols,
  setIndoorMode, setIndoorManaged, setBuildingType,
  dayTabsRef, canScrollLeft, canScrollRight, scrollDayTabs,
}) {
  const profileScrollRef = useRef(null);
  const profileWrapRef   = useRef(null);

  // Active tab: common - places - activities - work.
  // Auto-derived from the current profile on mount and on change.
  const getTab = () => {
    if (activeProfile === 'outdoors' && placeVariantKeys.includes(outdoorsVariant)) return 'places';
    if (activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant)) return 'activities';
    if (activeProfile === 'farming' || (activeProfile === 'outdoors' && workVariantKeys.includes(outdoorsVariant))) return 'work';
    return 'common';
  };
  const [activeTab, setActiveTab] = useState(getTab);
  useEffect(() => { setActiveTab(getTab()); }, [activeProfile, outdoorsVariant]);

  useEffect(() => {
    const el   = profileScrollRef.current;
    const wrap = profileWrapRef.current;
    if (!el) return;
    let isDown = false, startX = 0, startScroll = 0, hasDragged = false;

    const updateFades = () => {
      if (!wrap) return;
      wrap.classList.toggle('fade-left',  el.scrollLeft > 1);
      wrap.classList.toggle('fade-right', el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
    };

    const onMouseDown = (e) => {
      if (!el.contains(e.target) || e.button !== 0) return;
      isDown = true; hasDragged = false;
      startX = e.clientX; startScroll = el.scrollLeft;
      document.body.style.userSelect = 'none';
      document.body.style.webkitUserSelect = 'none';
    };
    const onMouseMove = (e) => {
      if (!isDown) return;
      const dx = e.clientX - startX;
      if (Math.abs(dx) > 5) {
        hasDragged = true;
        el.style.cursor = 'grabbing';
        el.scrollLeft = startScroll - dx;
      }
    };
    const onMouseUp = () => {
      if (!isDown) return;
      isDown = false;
      el.style.cursor = '';
      document.body.style.userSelect = '';
      document.body.style.webkitUserSelect = '';
    };
    const onClickCapture = (e) => {
      if (hasDragged) { e.stopPropagation(); e.preventDefault(); hasDragged = false; }
    };

    el.addEventListener('scroll', updateFades);
    updateFades(); // set initial fade state

    document.addEventListener('mousedown', onMouseDown);
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup',   onMouseUp);
    el.addEventListener('click', onClickCapture, true);
    return () => {
      el.removeEventListener('scroll', updateFades);
      document.removeEventListener('mousedown', onMouseDown);
      document.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mouseup',   onMouseUp);
      el.removeEventListener('click', onClickCapture, true);
    };
  }, []);

  return html`
    <${Fragment}>

      <div class="filter-profiles">
        <div class="profile-tabs">
          ${[['common','Common'],['places','Places'],['activities','Activities'],['work','Work']].map(([key, label]) => html`
            <button
              key=${key}
              class=${`profile-tab${activeTab === key ? ' active' : ''}`}
              onClick=${() => setActiveTab(key)}
            >${label}</button>
          `)}
        </div>
        <div class="profile-scroll-wrap" ref=${profileWrapRef}>
          <span class="profile-scroll-chevron left" aria-hidden="true">‹</span>
          <span class="profile-scroll-chevron right" aria-hidden="true">›</span>
        <div class="profile-scroll" ref=${profileScrollRef}>

          ${activeTab === 'common' && profileButtonOrder.map((key) => {
            const profile = FILTER_PROFILES[key];
            const locked  = profile.proOnly && !isPro;
            return html`
              <button
                key=${key}
                class=${`profile-btn${activeProfile === key ? ' active' : ''}${locked ? ' locked' : ''}`}
                style=${{ cursor: 'pointer', position: 'relative' }}
                title=${locked ? `Profile ${profile.label} is part of SunScope Extra` : ''}
                onClick=${() => {
                  if (locked) { setProPromptSource(`profile:${key}`); setProPromptDay(0); return; }
                  activateProfile(key);
                }}
              >
                <span class="profile-btn-scene" style=${{ background: profile.scene, filter: locked ? 'grayscale(100%)' : 'none' }}>${profile.scene.includes('url(') ? null : profile.icon}</span>
                <span class="profile-btn-label">${locked ? '🔒 ' : ''}${profile.label}</span>
                ${locked && html`<span class="profile-lock-badge">🔒</span>`}
              </button>`;
          })}

          ${activeTab === 'places' && placeOptions.map((opt) => html`
            <button
              key=${opt.value}
              class=${`profile-btn${placeValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
              style=${{ cursor: 'pointer', position: 'relative' }}
              onClick=${() => {
                if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
                try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
                setActiveProfile('outdoors');
                setOutdoorsVariantAndSave(opt.value);
                setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
                setIndoorMode('off');
                setIndoorManaged(false);
              }}
            >
              <span class="profile-btn-scene" style=${{ background: opt.scene, filter: opt.disabled ? 'grayscale(100%)' : 'none' }}></span>
              <span class="profile-btn-label">${opt.disabled ? '🔒 ' : ''}${opt.name}</span>
              ${opt.disabled && html`<span class="profile-lock-badge">🔒</span>`}
            </button>
          `)}

          ${activeTab === 'activities' && activityOptions.map((opt) => html`
            <button
              key=${opt.value}
              class=${`profile-btn${activityValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
              style=${{ cursor: 'pointer', position: 'relative' }}
              onClick=${() => {
                if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
                try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
                setActiveProfile('outdoors');
                setOutdoorsVariantAndSave(opt.value);
                setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
                setIndoorMode('off');
                setIndoorManaged(false);
              }}
            >
              <span class="profile-btn-scene" style=${{ background: opt.scene, filter: opt.disabled ? 'grayscale(100%)' : 'none' }}></span>
              <span class="profile-btn-label">${opt.disabled ? '🔒 ' : ''}${opt.name}</span>
              ${opt.disabled && html`<span class="profile-lock-badge">🔒</span>`}
            </button>
          `)}

          ${activeTab === 'work' && workOptions.map((opt) => html`
            <button
              key=${opt.value}
              class=${`profile-btn${workValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
              style=${{ cursor: 'pointer', position: 'relative' }}
              onClick=${() => {
                if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
                if (opt.value === 'farming') { activateProfile('farming'); return; }
                try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
                setActiveProfile('outdoors');
                setOutdoorsVariantAndSave(opt.value);
                setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
                if (opt.value === 'office') { setBuildingType('office'); setIndoorMode('on'); setIndoorManaged(false); }
                else { setIndoorMode('off'); setIndoorManaged(false); }
              }}
            >
              <span class="profile-btn-scene" style=${{ background: opt.scene, filter: opt.disabled ? 'grayscale(100%)' : 'none' }}></span>
              <span class="profile-btn-label">${opt.disabled ? '🔒 ' : ''}${opt.name}</span>
              ${opt.disabled && html`<span class="profile-lock-badge">🔒</span>`}
            </button>
          `)}

        </div>
        </div>
      </div>

      <div class=${`utci-day-tabs-wrap${canScrollLeft ? ' fade-left' : ''}${canScrollRight ? ' fade-right' : ''}`}>
        <button
          class=${`utci-day-scroll left${canScrollLeft ? '' : ' hidden'}`}
          onClick=${() => scrollDayTabs(-1)}
          aria-label="Scroll days left"
          type="button">‹</button>
        <button
          class=${`utci-day-scroll right${canScrollRight ? '' : ' hidden'}`}
          onClick=${() => scrollDayTabs(1)}
          aria-label="Scroll days right"
          type="button">›</button>
      <div class="utci-day-tabs" ref=${dayTabsRef}>
        ${days.map((d, i) => {
          const band   = confidenceBand(i);
          const locked = !isPro && i >= FREE_DAYS;
          const isActive = i === selectedDay;
          const dDate  = new Date(d.key + 'T00:00Z');
          const dayName = i === 0 ? 'Today'
                        : i === 1 ? 'Tomorrow'
                        : dDate.toLocaleDateString('en-GB', { weekday: 'short', timeZone: 'UTC' });
          const utcipVals = d.rows.map(r => r.utciAdj).filter(v => isFinite(v));
          const dayHi = utcipVals.length ? Math.round(Math.max(...utcipVals)) : null;
          const dayLo = utcipVals.length ? Math.round(Math.min(...utcipVals)) : null;
          const shadeVals = d.rows.map(r => r.shadeT).filter(v => isFinite(v));
          const shadeHi  = shadeVals.length ? Math.round(Math.max(...shadeVals)) : null;
          const shadeLo  = shadeVals.length ? Math.round(Math.min(...shadeVals)) : null;

          // Day-tab weather icon - use core daylight rows (elev > 10°) where
          // available, falling back to any above-horizon rows, then all rows.
          const coreRows   = d.rows.filter(r => r.elev > 10);
          const aboveRows  = d.rows.filter(r => r.elev > 0);
          const repRows    = coreRows.length > 0 ? coreRows : aboveRows.length > 0 ? aboveRows : d.rows;
          const dayPrecip  = repRows.reduce((s, r) => s + (r.precip || 0), 0);
          const daySnow    = repRows.reduce((s, r) => s + (r.snow   || 0), 0);
          const catCounts  = {};
          repRows.forEach(r => { if (r.cloudCat) catCounts[r.cloudCat] = (catCounts[r.cloudCat] || 0) + 1; });
          const modalCloudCat = Object.keys(catCounts).sort((a, b) => catCounts[b] - catCounts[a])[0] || 'clear';
          // Use solar-noon row (highest elevation) as the representative row
          const noonRow    = repRows.reduce((best, r) => (r.elev > (best?.elev ?? -Infinity) ? r : best), null);
          const repElev    = noonRow ? noonRow.elev : 45;
          const repDt      = noonRow ? noonRow.dt   : dDate;
          const showPrecip = dayPrecip >= 0.3 || daySnow >= 0.1;

          // Day-tab colour — PRIORITY model, no hue blending.
          // Mixing a warm yellow with grey/blue passes through green, so rather
          // than blend temperature with sky we pick ONE dimension by priority and
          // only vary its shade: heat → snow → rain → cloud → sun (first wins).
          //   • Hot/extreme days always keep their heat colour (a safety signal).
          //   • Otherwise rain wins, then cloud, then a clear "sunny"/cold colour.
          const mix     = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * Math.max(0, Math.min(1, t))));
          const hex2rgb = (hx) => { const n = parseInt(hx.replace('#', ''), 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; };
          const tBand   = dayHi !== null ? utciCategory(dayHi) : { bg: '#f8e554' };

          const ccVals   = repRows.map(r => r.cc).filter(v => isFinite(v));
          const avgCloud = ccVals.length ? ccVals.reduce((s, v) => s + v, 0) / ccVals.length : 0;

          const isHot    = dayHi !== null && dayHi >= 29;   // Hot band and above
          const isDanger = tBand.solid === true;

          let rgb;
          if (isHot) {
            // Keep the heat hue; cloud only mutes it a touch (orange/red never
            // greens) and rain does not override heat.
            const heatFactor = dayHi >= 39 ? 0.15 : dayHi >= 34 ? 0.30 : 0.45;
            const t = Math.max(0, Math.min(1, (avgCloud - 30) / 70)) * heatFactor;
            rgb = mix(hex2rgb(tBand.bg), [170, 162, 152], t);
          } else if (daySnow >= 0.1) {
            // Snow: pale → icy blue with depth.
            rgb = mix([226, 234, 244], [176, 202, 232], Math.max(0, Math.min(1, daySnow / 1.5)));
          } else if (showPrecip) {
            // Rain: light → deep blue with amount (pure blue, no temperature hue).
            rgb = mix([150, 176, 212], [44, 84, 150], Math.max(0, Math.min(1, dayPrecip / 8)));
          } else if (avgCloud >= 30) {
            // Cloud: light → dark grey with cover (pure grey, no temperature hue).
            rgb = mix([205, 206, 208], [110, 115, 122], Math.max(0, Math.min(1, (avgCloud - 30) / 70)));
          } else if (dayHi !== null && dayHi < 10) {
            // Clear & cold: keep the cold temperature blue.
            rgb = hex2rgb(tBand.bg);
          } else {
            // Clear & mild/warm: a plain sunny yellow.
            rgb = hex2rgb('#f8e554');
          }
          const [wR, wG, wB] = rgb;

          const wBgNeutral = isDanger
            ? weatherGradientNeutral(wR, wG, wB, 0.15, 0.72)
            : weatherGradientNeutral(wR, wG, wB);
          const wBgActive = isDanger
            ? weatherGradientActive(wR, wG, wB, 0.12, 0.72)
            : weatherGradientActive(wR, wG, wB);
          const wText = '#2a1d10';
          // Active-tab outline: the day's weather colour, 20% darker.
          const wOutline = `rgb(${Math.round(wR * 0.8)},${Math.round(wG * 0.8)},${Math.round(wB * 0.8)})`;

          return html`
            <button
              key=${d.key}
              class=${`utci-day-tab ${isActive ? 'active' : ''}${locked ? ' locked' : ''}${isDanger ? ' danger' : ''}`}
              onClick=${() => {
                if (locked) {
                  setProPromptSource('day');
                  setProPromptDay(i);
                } else {
                  setSelectedDay(i);
                  setProPromptDay(null);
                }
              }}
              title=${locked
                ? `${band.label} · SunScope Extra unlocks day ${i + 1}`
                : `${band.label} · day ${i + 1} of 14`}
              style=${{
                background: isActive ? wBgActive : wBgNeutral,
                color: wText,
                borderTop: '1px solid #c9b08a',
                borderRight: 'none',
                borderBottom: '1px solid #c9b08a',
                borderLeft: i === 0 ? '1px solid #c9b08a' : 'none',
                opacity: locked ? 0.5 : 1,
                cursor: locked ? 'not-allowed' : 'pointer',
                position: 'relative',
                filter: isActive ? 'saturate(1.1) brightness(1.04)' : 'none',
                outline: isActive ? `2px solid ${wOutline}` : 'none',
                outlineOffset: isActive ? '-2px' : '0',
              }}
            >
              ${locked && html`
                <span style=${{ position: 'absolute', top: '3px', right: '5px', fontSize: '10px', opacity: 0.75 }}>🔒</span>`}
              ${dayName}
              <span class="utci-day-date" style=${{ color: '#5a3f24' }}>
                ${dDate.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', timeZone: 'UTC' })}
              </span>
                <span style=${{ display: 'block', margin: '6px auto 0px', lineHeight: 1 }}>
                ${showPrecip
                  ? html`<${PrecipIcon} precip=${dayPrecip} snow=${daySnow} size=${30} />`
                  : html`<${CloudIcon} category=${modalCloudCat} elev=${repElev} dt=${repDt} size=${30} />`}
              </span>
              ${dayHi !== null && html`
                <span style=${{
                  display: 'flex',
                  alignItems: 'stretch',
                  justifyContent: 'center',
                  gap: '6px',
                  fontFamily: 'Manrope, sans-serif',
                  fontStyle: 'normal',
                  letterSpacing: 0,
                  textTransform: 'none',
                  textShadow: '0 0 14px rgba(255,255,255,0.85), 0 0 11px rgba(255,255,255,0.7), 0 0 8px rgba(255,255,255,0.6), 0 0 5px rgba(255,255,255,0.5)',
                  marginTop: '2px',
                }}>
                  <span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
                    <span style=${{ color: '#c0622a', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayHi}°</span>
                    ${shadeHi !== null && html`<span style=${{ color: '#c0622a', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${shadeHi}°</span>`}
                  </span>
                  <span style=${{ width: '1px', background: 'rgba(0,0,0,0.15)', margin: '2px 0', alignSelf: 'stretch' }}></span>
                  <span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
                    <span style=${{ color: '#3a6080', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayLo}°</span>
                    ${shadeLo !== null && html`<span style=${{ color: '#3a6080', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${shadeLo}°</span>`}
                  </span>
                </span>`}
            </button>`;
        })}
      </div>
      </div>

      ${proPromptDay !== null && days[proPromptDay] && (() => {
        const promptDate = new Date(days[proPromptDay].key + 'T00:00Z');
        const dayName    = promptDate.toLocaleDateString('en-GB', { weekday: 'long', timeZone: 'UTC' });
        const extraPromptCopy = {
          // Profile buttons
          'profile:alltemps': {
            title: 'Temps is part of SunScope Extra',
            detail: 'Extra unlocks the temperature comparison view — air, soil, concrete, vehicle interior, and indoor estimates side by side in one place.',
          },
          'profile:showall': {
            title: 'Show All is part of SunScope Extra',
            detail: 'Extra unlocks the full column set — every data point SunScope tracks, visible at once for deep-dive analysis.',
          },
          'profile:custom': {
            title: 'Custom columns are part of SunScope Extra',
            detail: 'Extra lets you hand-pick exactly which columns appear. Mix and match air, dew, soil, UV, UTCI and more to build your perfect view.',
          },
          // Places
          'variant:construction': {
            title: 'Construction is part of SunScope Extra',
            detail: 'Extra adds the Construction profile with felt temperature, wind, rain probability, visibility, and air quality — the key factors for safe site working.',
          },
          // Activities
          'variant:hiking': {
            title: 'Hiking is part of SunScope Extra',
            detail: 'Extra adds the Hiking profile with felt temperature, UV index, burn time, wind direction, and visibility — essential for longer days on exposed terrain.',
          },
          'variant:photography': {
            title: 'Photography is part of SunScope Extra',
            detail: 'Extra adds the Photography profile with direct and diffuse radiation, sun elevation, cloud cover, and visibility — the conditions that make or break a shoot.',
          },
          'variant:sailing': {
            title: 'Sailing is part of SunScope Extra',
            detail: 'Extra adds the Sailing profile with wind speed and direction, dew point, UV, burn time, sun elevation, and visibility for on-water planning.',
          },
          'variant:wintersports': {
            title: 'Winter Sports is part of SunScope Extra',
            detail: 'Extra adds the Winter Sports profile with UV-A and UV-B, burn time, sun elevation, wind direction, and visibility for snow and slope conditions.',
          },
          'variant:naturist': {
            title: 'Naturist is part of SunScope Extra',
            detail: 'Extra adds the Naturist profile with full skin-exposure detail — UV, burn time, felt temperature, dew point, humidity, and air quality.',
          },
          // Work
          'variant:market': {
            title: 'Market Trading is part of SunScope Extra',
            detail: 'Extra adds the Market Trading profile with felt temperature, wind direction, rain probability, and visibility — the key factors for planning stall days.',
          },
          'variant:windowcleaning': {
            title: 'Window Cleaning is part of SunScope Extra',
            detail: 'Extra adds the Window Cleaning profile focused on wind speed and direction, rain, and felt temperature — the conditions that determine whether work is safe and worthwhile.',
          },
          'variant:office': {
            title: 'Office is part of SunScope Extra',
            detail: 'Extra adds the Office profile with solar gain, managed indoor temperature, humidity, air quality, and rain probability — useful for commute planning and building comfort.',
          },
          'variant:driver': {
            title: 'Driver / Trucker is part of SunScope Extra',
            detail: 'Extra adds the Driver / Trucker profile with vehicle interior temperature, UV, wind speed and direction, rain probability, and visibility — the conditions that matter for long hours on the road.',
          },
          'export': {
            title: 'Export day data is part of SunScope Extra',
            detail: 'Extra lets you download a full hourly spreadsheet for any day — all 41 columns including felt temperature, UV, soil, wind, and air quality, styled and ready to use in Excel or LibreOffice.',
          },
        };
        // Day-tab locks get the day-specific heading; any other locked feature
        // without its own entry above falls back to a feature-agnostic message.
        const fallbackCopy = proPromptSource === 'day'
          ? {
              title: `${dayName}'s forecast is part of SunScope Extra`,
              detail: 'Extra unlocks the full 14-day forecast, customisable columns, specialist profiles, and an ad-free view.',
            }
          : {
              title: 'This is part of SunScope Extra',
              detail: 'Extra unlocks the full 14-day forecast, customisable columns, specialist profiles, and an ad-free view.',
            };
        const promptCopy = extraPromptCopy[proPromptSource] || fallbackCopy;
        return html`
          <${SubscribeModal}
            title=${promptCopy.title}
            detail=${promptCopy.detail}
            onClose=${() => setProPromptDay(null)}
            openRestore=${openRestore}
          />`;
      })()}


    </${Fragment}>`;
}
