// ------------------------------------------------------------------------
//  components/DayTabs.js - Day-tab strip, pro-prompt card and confidence
//  band bar 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.
//
//  The profile picker and config strip that used to live here now sit in the
//  ConfigPanel nav dropdown; DayTabs keeps the derived "main" metric needed
//  to compute the day-tab hi/lo numbers, and renders a read-only row above
//  the day tabs stating the active profile + thermal basis so it's always
//  clear what temperatures are being shown (click/Enter opens the dropdown).
//
//  Props:
//    days                   - array of day objects from buildHourlyRows
//    selectedDay/setSelectedDay - active day tab index + setter
//    isPro                  - boolean Pro status
//    openRestore            - opens the "Already subscribed?" restore modal
//    proPromptDay/setProPromptDay     - locked-day upsell index + setter
//    proPromptSource/setProPromptSource - upsell copy key + setter
//    activeProfile          - current filter profile key
//    outdoorsVariant        - current outdoors sub-variant key
//    openPanel              - opens the profile/config nav dropdown
//    vehicleType/vehicleSpeed - current vehicle config (vehicle-cabin day-tab
//                             reference calc + row label)
//    buildingType/indoorManaged/utciEnv/furColor - current config values,
//                             for the "Viewing" row label
//    dayTabsRef             - ref for the scrollable tab strip element
//    canScrollLeft/canScrollRight - booleans for the fade chevrons
//    scrollDayTabs          - function(dir) to scroll strip left/right
// ------------------------------------------------------------------------

import { h, Fragment } from '../../vendor/preact.js';
import htm from '../../vendor/htm.js';
import { confidenceBand, utciCategory, petCategory, VEHICLE_SPEEDS, VEHICLE_TYPES, BUILDING_TYPES, FUR_COLORS } from '../utils.js';
import { FREE_DAYS, UTCI_ENVIRONMENTS, deriveProfileMain, FILTER_PROFILES, variantIcons } from '../config.js';
import { calcVehicleInteriorTempPass } from '../physics.js';
import { CloudIcon, PrecipIcon, HouseIcon, CarIcon, FogIcon, IceIcon } from '../components.js';
import { SubscribeModal } from './SubscribeModal.js';

const html = htm.bind(h);

// Per-selection gradients for the active-profile-row background — one entry
// per possible value of each thermal-model category, so the row's shade
// reflects not just "which model" (SunSoak/Vehicle/Indoor/Pets) but the
// specific option currently picked within it (e.g. Beach vs Forest), the
// same way the profile cards use a distinct "scene" per preset.
const ROW_GRADIENTS = {
  solar: {
    open:      'linear-gradient(120deg, rgba(150,200,120,0.55), rgba(222,236,196,0.30))',
    urban:     'linear-gradient(120deg, rgba(160,168,178,0.55), rgba(218,222,228,0.30))',
    beach:     'linear-gradient(120deg, rgba(240,208,140,0.55), rgba(182,220,236,0.30))',
    river:     'linear-gradient(120deg, rgba(120,196,182,0.55), rgba(202,236,228,0.30))',
    forest:    'linear-gradient(120deg, rgba(66,124,70,0.55), rgba(170,202,156,0.30))',
    openwater: 'linear-gradient(120deg, rgba(96,150,204,0.55), rgba(180,214,238,0.30))',
    alpine:    'linear-gradient(120deg, rgba(180,210,230,0.55), rgba(238,246,250,0.30))',
    desert:    'linear-gradient(120deg, rgba(220,164,96,0.55), rgba(246,220,172,0.30))',
  },
  vehicle: {
    car:       'linear-gradient(120deg, rgba(180,192,202,0.55), rgba(222,228,234,0.30))',
    mpv:       'linear-gradient(120deg, rgba(172,186,200,0.55), rgba(216,224,232,0.30))',
    suv:       'linear-gradient(120deg, rgba(154,172,190,0.55), rgba(206,216,228,0.30))',
    truck:     'linear-gradient(120deg, rgba(136,158,180,0.55), rgba(196,208,222,0.30))',
    motorhome: 'linear-gradient(120deg, rgba(200,180,148,0.55), rgba(232,220,196,0.30))',
    caravan:   'linear-gradient(120deg, rgba(208,188,158,0.55), rgba(236,224,204,0.30))',
  },
  indoor: {
    brick:        'linear-gradient(120deg, rgba(190,120,92,0.50),  rgba(226,178,158,0.28))',
    modern:       'linear-gradient(120deg, rgba(162,172,180,0.50), rgba(212,218,224,0.28))',
    victorian:    'linear-gradient(120deg, rgba(160,76,54,0.50),   rgba(210,142,122,0.28))',
    stone:        'linear-gradient(120deg, rgba(146,138,124,0.50), rgba(202,196,184,0.28))',
    timber:       'linear-gradient(120deg, rgba(182,142,92,0.50),  rgba(222,192,150,0.28))',
    flat:         'linear-gradient(120deg, rgba(162,162,168,0.50), rgba(212,212,218,0.28))',
    conservatory: 'linear-gradient(120deg, rgba(140,198,220,0.50), rgba(202,232,242,0.28))',
    office:       'linear-gradient(120deg, rgba(122,148,184,0.50), rgba(188,204,226,0.28))',
  },
  fur: {
    black:  'linear-gradient(120deg, rgba(52,48,44,0.55),  rgba(112,106,100,0.30))',
    brown:  'linear-gradient(120deg, rgba(126,84,48,0.55), rgba(178,140,98,0.30))',
    golden: 'linear-gradient(120deg, rgba(208,160,80,0.55),rgba(236,204,144,0.30))',
    white:  'linear-gradient(120deg, rgba(222,214,198,0.55),rgba(248,246,238,0.30))',
  },
};

// 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, outdoorsVariant,
  openPanel,
  vehicleType, vehicleSpeed, buildingType, indoorManaged, utciEnv, furColor,
  dayTabsRef, canScrollLeft, canScrollRight, scrollDayTabs,
}) {
  // Which metric drives the day-tab's hi/lo readout depends on the active
  // profile — default is SunSoak (felt) + Shade, but profiles with their own
  // dedicated calc show their own numbers instead. The profile picker and the
  // full config strip now live in the nav dropdown (ConfigPanel); here we
  // only need the derived main metric to compute the day-tab numbers and to
  // label the "what temps am I looking at" row below.
  const {
    isHomeOrOffice, isVehicleProfile, isPetsProfile, mainConfigKey, mainField, secondaryField, mainLabel,
    mainMetricLabel, mainMetricDesc, secondaryMetricLabel, secondaryMetricDesc,
  } = deriveProfileMain(activeProfile, outdoorsVariant);

  // Read-only label of the current main-config value, for the row above the
  // day tabs — makes it obvious at a glance which temperature basis (SunSoak
  // environment, vehicle type, building type, fur colour) the numbers below
  // are computed from.
  const mainConfigValue = mainConfigKey === 'vehicle'
      ? (VEHICLE_TYPES[vehicleType]?.name || '')
    : mainConfigKey === 'indoor'
      ? ((BUILDING_TYPES[buildingType]?.name || '') + (indoorManaged ? ' · Managed' : ''))
    : mainConfigKey === 'fur'
      ? (FUR_COLORS[furColor]?.name || '')
      : (UTCI_ENVIRONMENTS[utciEnv]?.label || '');

  // Tints the row background to match the thermal model driving it, reusing
  // the same colour groups the CustomSelect pickers use (e.g. SunSoak's
  // "Solar model" dropdown is grp-felt) so the row reads as an extension of
  // those controls rather than a separate, unrelated style.
  const rowGrpClass = mainConfigKey === 'vehicle' ? 'grp-wind'
    : mainConfigKey === 'indoor' ? 'grp-ambient'
    : mainConfigKey === 'fur' ? 'grp-surface'
    : 'grp-felt';

  // Within that group, shade further by the exact option chosen — same idea
  // as the "Solar model" pulldown, but keyed to whichever value is actually
  // driving the numbers right now (environment / vehicle / building / fur).
  const rowOptionKey = mainConfigKey === 'vehicle' ? vehicleType
    : mainConfigKey === 'indoor' ? buildingType
    : mainConfigKey === 'fur' ? furColor
    : utciEnv;
  const rowGradient = ROW_GRADIENTS[mainConfigKey]?.[rowOptionKey];

  return html`
    <${Fragment}>

      <div class=${`active-profile-row ${rowGrpClass}`} onClick=${openPanel} role="button" tabIndex="0"
        onKeyDown=${(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openPanel(); } }}
        style=${rowGradient ? { '--row-bg': rowGradient } : undefined}
        title="Change profile & settings">
        <span class="active-profile-row-item">
          <span class="active-profile-row-label">Profile</span>
          <span class="active-profile-row-value">
            ${activeProfile === 'outdoors' 
              ? (variantIcons[outdoorsVariant] || '🌤️') 
              : (FILTER_PROFILES[activeProfile]?.icon || '🎯')}
            ${mainLabel}
          </span>
        </span>
        ${mainConfigValue && html`
          <span class="active-profile-row-sep" aria-hidden="true">·</span>
          <span class="active-profile-row-item">
            <span class="active-profile-row-label">Viewing</span>
            <span class="active-profile-row-value">${mainConfigValue}</span>
          </span>`}
        <span class="active-profile-row-edit" aria-hidden="true">Change ✎</span>
      </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>
      ${/* Fixed label column outside the scroller — names every stacked row
            in each tab (day name, date, icon, then the two numbers, since
            which metrics those are changes with the active profile). Mirrors
            the tabs' own top-down layout row for row (see .utci-day-legend-col
            in table.css). */''}
      <div class="utci-day-legend-col">
        <span class="utci-day-legend-day">Day</span>
        <span class="utci-day-legend-date">Date</span>
        <span class="utci-day-legend-icon">${isHomeOrOffice ? 'Home' : isVehicleProfile ? 'Alert' : 'Sky'}</span>
        <div class="utci-day-legend-values">
          <span class="utci-day-legend-main" title=${mainMetricDesc}>${mainMetricLabel}</span>
          ${(isVehicleProfile || secondaryField) && html`
            <span class="utci-day-legend-sep" aria-hidden="true"></span>
            <span class="utci-day-legend-sec" title=${secondaryMetricDesc}>${secondaryMetricLabel}</span>`}
        </div>
      </div>
      <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 ? 'Tom'
                        : dDate.toLocaleDateString('en-GB', { weekday: 'short', timeZone: 'UTC' });
          const mainVals = d.rows.map(r => r[mainField]).filter(v => isFinite(v));
          const dayHi = mainVals.length ? Math.round(Math.max(...mainVals)) : null;
          const dayLo = mainVals.length ? Math.round(Math.min(...mainVals)) : null;

          // Vehicle/Driver: no table column exists for the ventilated cabin
          // temp (ventilation is just an input toggle on the main vehicleT
          // calc), so run the physics model again with vent forced on, to
          // show "if you opened the windows" as a reference hi/lo.
          let shadeHi = null, shadeLo = null;
          if (isVehicleProfile) {
            const speedMph = (VEHICLE_SPEEDS[vehicleSpeed] ?? VEHICLE_SPEEDS.static).mph;
            // Run the lagged pass over the whole day, not per-hour equilibrium,
            // so this hi/lo is directly comparable with the vehicleT column.
            const ventVals = calcVehicleInteriorTempPass(
              d.rows.map(r => r.Ta), d.rows.map(r => r.glob), d.rows.map(r => r.elev),
              d.rows.map(r => r.va), vehicleType, true, speedMph
            ).filter(v => v != null && isFinite(v));
            shadeHi = ventVals.length ? Math.round(Math.max(...ventVals)) : null;
            shadeLo = ventVals.length ? Math.round(Math.min(...ventVals)) : null;
          } else if (secondaryField) {
            const secondaryVals = d.rows.map(r => r[secondaryField]).filter(v => isFinite(v));
            shadeHi = secondaryVals.length ? Math.round(Math.max(...secondaryVals)) : null;
            shadeLo = secondaryVals.length ? Math.round(Math.min(...secondaryVals)) : 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;

          // Vehicle tab: swap the default car glyph for a hazard icon when
          // conditions could affect driving. Uses the full day (not just
          // daylight repRows) since fog and frost often hit overnight or on
          // an early commute, then picks the worst hazard by priority.
          let vehicleHazard = null;
          let hazPrecip = 0, hazSnow = 0;
          if (isVehicleProfile) {
            const allSnow   = d.rows.reduce((s, r) => s + (r.snow   || 0), 0);
            const allPrecip = d.rows.reduce((s, r) => s + (r.precip || 0), 0);
            const visVals   = d.rows.map(r => r.visKm).filter(v => isFinite(v));
            const minVis    = visVals.length ? Math.min(...visVals) : null;
            const taVals    = d.rows.map(r => r.Ta).filter(v => isFinite(v));
            const minTa     = taVals.length ? Math.min(...taVals) : null;
            if (allSnow >= 0.1) {
              vehicleHazard = 'snow'; hazSnow = allSnow;
            } else if (minTa !== null && minTa <= 0 && allPrecip < 0.3) {
              vehicleHazard = 'ice';
            } else if (minVis !== null && minVis < 2) {
              vehicleHazard = 'fog';
            } else if (allPrecip >= 0.3) {
              vehicleHazard = 'rain'; hazPrecip = allPrecip;
            }
          }

          // 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 catFn   = isPetsProfile ? petCategory : utciCategory;
          const tBand   = dayHi !== null ? catFn(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;

          // Hot/cold cutoffs below are calibrated against UTCI_BANDS; for the
          // Pets profile (furSurfaceT, judged against the shifted PET_BANDS)
          // the same cutoffs are re-projected onto the pet scale so the
          // "hot hue" / "plain cold" branches kick in at the same relative
          // point in each band, not the same absolute degree.
          const isHot    = dayHi !== null && dayHi >= (isPetsProfile ? 35 : 27);   // Hot band and above
          const isDanger = tBand.solid === true;

          // Home/indoor/vehicle tabs show a modelled temperature, not the
          // outdoor sky, so the tab shade should track that temperature only
          // — no rain/snow/cloud tinting, and no weather icon.
          const tempOnly = isHomeOrOffice || isVehicleProfile;

          let rgb;
          if (tempOnly) {
            rgb = hex2rgb(tBand.bg);
          } else if (isHot) {
            // Keep the heat hue; cloud only mutes it a touch (orange/red never
            // greens) and rain does not override heat.
            const heatFactor = isPetsProfile
              ? (dayHi >= 49 ? 0.15 : dayHi >= 43 ? 0.30 : 0.45)
              : (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 < (isPetsProfile ? 2 : 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>`}
              <span class="utci-day-name">${dayName}</span>
              <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 }}>
                ${tempOnly
                  ? (isVehicleProfile
                      ? (vehicleHazard === 'snow' ? html`<${PrecipIcon} precip=${0} snow=${hazSnow} size=${30} />`
                        : vehicleHazard === 'rain' ? html`<${PrecipIcon} precip=${hazPrecip} snow=${0} size=${30} />`
                        : vehicleHazard === 'fog'  ? html`<${FogIcon} size=${30} />`
                        : vehicleHazard === 'ice'  ? html`<${IceIcon} size=${30} />`
                        : html`<${CarIcon} size=${30} />`)
                      : html`<${HouseIcon} size=${30} />`)
                  : 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=${{ width: '10px', height: '1px', background: 'rgba(0,0,0,0.15)', margin: '1px 0' }}></span>
                      <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=${{ width: '10px', height: '1px', background: 'rgba(0,0,0,0.15)', margin: '1px 0' }}></span>
                      <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.',
          },
          // Edit columns button on the forecast toolbar
          'columns': {
            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}>`;
}
