function createWeekCalendarImport() {
  const maxBytes=1024*1024;
  const zonedInstantFormatters=new Map(),zonePartFormatters=new Map(),supportedTimeZones=new Map();
  function clock(value) {
    const match=/^(\d{2}):(\d{2})$/.exec(String(value||''));
    if(!match)return null;
    const minutes=Number(match[1])*60+Number(match[2]);
    return Number(match[1])<24&&Number(match[2])<60?minutes:null;
  }
  function instant(value) {
    const match=/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z?)$/.exec(value||'');
    if(!match)return null;
    const parts=match.slice(1,7).map(Number);
    const milliseconds=match[7]?Date.UTC(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]):
      new Date(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]).getTime();
    return Number.isFinite(milliseconds)?milliseconds:null;
  }
  function zonedInstant(value,timeZone) {
    if(!timeZone)return instant(value);
    const match=/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})$/.exec(value||'');
    if(!match)return null;
    try {
      const wanted=match.slice(1).map(Number);
      if(!zonedInstantFormatters.has(timeZone))zonedInstantFormatters.set(timeZone,new Intl.DateTimeFormat('en-CA',{
        timeZone,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hourCycle:'h23',
      }));
      const formatter=zonedInstantFormatters.get(timeZone);
      let result=Date.UTC(wanted[0],wanted[1]-1,wanted[2],wanted[3],wanted[4],wanted[5]);
      for(let attempt=0;attempt<2;attempt+=1) {
        const shown=Object.fromEntries(formatter.formatToParts(new Date(result)).map(part=>[part.type,part.value]));
        const represented=Date.UTC(Number(shown.year),Number(shown.month)-1,Number(shown.day),Number(shown.hour),Number(shown.minute),Number(shown.second));
        result+=Date.UTC(wanted[0],wanted[1]-1,wanted[2],wanted[3],wanted[4],wanted[5])-represented;
      }
      return result;
    } catch(error) { return null; }
  }
  function supportedTimeZone(timeZone) {
    if(!timeZone)return true;
    if(supportedTimeZones.has(timeZone))return supportedTimeZones.get(timeZone);
    try {new Intl.DateTimeFormat('en-US',{timeZone}).format(0);supportedTimeZones.set(timeZone,true);return true;}
    catch(error) {supportedTimeZones.set(timeZone,false);return false;}
  }
  function zoneParts(value,timeZone) {
    const date=new Date(value);
    if(!timeZone)return {year:date.getFullYear(),month:date.getMonth()+1,day:date.getDate(),weekday:date.getDay(),
      hour:date.getHours(),minute:date.getMinutes(),second:date.getSeconds()};
    if(!zonePartFormatters.has(timeZone))zonePartFormatters.set(timeZone,new Intl.DateTimeFormat('en-US',{
      timeZone,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',weekday:'short',hourCycle:'h23',
    }));
    const formatter=zonePartFormatters.get(timeZone);
    const parts=Object.fromEntries(formatter.formatToParts(date).map(part=>[part.type,part.value]));
    return {year:Number(parts.year),month:Number(parts.month),day:Number(parts.day),
      weekday:['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].indexOf(parts.weekday),hour:Number(parts.hour),
      minute:Number(parts.minute),second:Number(parts.second)};
  }
  function addCalendarDays(value,days,timeZone) {
    if(!timeZone) {const date=new Date(value);date.setDate(date.getDate()+days);return date.getTime();}
    const parts=zoneParts(value,timeZone),day=new Date(Date.UTC(parts.year,parts.month-1,parts.day+days));
    const wall=[day.getUTCFullYear(),String(day.getUTCMonth()+1).padStart(2,'0'),String(day.getUTCDate()).padStart(2,'0')].join('')+
      'T'+[parts.hour,parts.minute,parts.second].map(part=>String(part).padStart(2,'0')).join('');
    return zonedInstant(wall,timeZone);
  }
  function dateInstant(value) {
    const match=/^(\d{4})(\d{2})(\d{2})$/.exec(value||'');
    return match?new Date(Number(match[1]),Number(match[2])-1,Number(match[3])).getTime():null;
  }
  function events(source) {
    if(typeof source!=='string'||!source.includes('BEGIN:VCALENDAR'))throw new Error('Choose a valid .ics calendar file.');
    if(new TextEncoder().encode(source).length>maxBytes)throw new Error('Calendar files must be 1 MB or smaller.');
    const lines=source.replace(/\r\n[ \t]/g,'').split(/\r?\n/);
    const result=[];let current=null;
    lines.forEach(line=>{
      if(line==='BEGIN:VEVENT'){current={};return;}
      if(line==='END:VEVENT'){
        if((current?.unsupportedTimezone||(current?.start!=null&&current?.end>current.start))&&
          current.status!=='CANCELLED'&&current.transparency!=='TRANSPARENT')result.push(current);
        current=null;return;
      }
      if(!current)return;
      const separator=line.indexOf(':');if(separator<0)return;
      const property=line.slice(0,separator),parts=property.split(';'),name=parts[0],value=line.slice(separator+1);
      const parameters=Object.fromEntries(parts.slice(1).map(part=>part.split('=')));
      if(parameters.TZID&&!supportedTimeZone(parameters.TZID))current.unsupportedTimezone=true;
      const allDay=parameters.VALUE==='DATE',parsed=allDay?dateInstant(value):zonedInstant(value,parameters.TZID);
      if(name==='DTSTART'){current.start=parsed;current.timeZone=parameters.TZID||null;}
      if(name==='DTEND')current.end=parsed;
      if(name==='RRULE')current.rule=value;
      if(name==='RDATE')current.rdates=(current.rdates||[]).concat(value.split(',').map(item=>allDay?dateInstant(item):zonedInstant(item,parameters.TZID)).filter(value=>value!=null));
      if(name==='EXDATE')current.exdates=(current.exdates||[]).concat(value.split(',').map(item=>allDay?dateInstant(item):zonedInstant(item,parameters.TZID)).filter(value=>value!=null));
      if(name==='STATUS')current.status=value.toUpperCase();
      if(name==='TRANSP')current.transparency=value.toUpperCase();
    });
    return result;
  }
  function recurrenceRule(value) {
    return Object.fromEntries(String(value||'').split(';').filter(Boolean).map(part=>part.split('=')));
  }
  function supportedRecurrence(value) {
    const rule=recurrenceRule(value),keys=Object.keys(rule);
    if(!['DAILY','WEEKLY'].includes(rule.FREQ))return false;
    const allowed=new Set(['FREQ','COUNT','INTERVAL','UNTIL',...(rule.FREQ==='WEEKLY'?['BYDAY']:[])]);
    if(keys.some(key=>!allowed.has(key)))return false;
    if(rule.COUNT&&(!/^\d+$/.test(rule.COUNT)||Number(rule.COUNT)<1))return false;
    if(rule.INTERVAL&&(!/^\d+$/.test(rule.INTERVAL)||Number(rule.INTERVAL)<1))return false;
    if(rule.COUNT&&rule.UNTIL)return false;
    if(rule.UNTIL&&instant(rule.UNTIL)==null&&dateInstant(rule.UNTIL)==null)return false;
    return !rule.BYDAY||rule.BYDAY.split(',').every(day=>/^(MO|TU|WE|TH|FR|SA|SU)$/.test(day));
  }
  function occurrences(event,rangeStart,rangeEnd) {
    const duration=event.end-event.start,excluded=new Set(event.exdates||[]);
    if(!event.rule) {
      return [event.start,...(event.rdates||[])].filter((start,index,all)=>start<rangeEnd&&!excluded.has(start)&&all.indexOf(start)===index)
        .map(start=>({start,end:start+duration}));
    }
    const rule=recurrenceRule(event.rule);
    if(!['DAILY','WEEKLY'].includes(rule.FREQ))return [event];
    const count=Math.max(1,Math.min(Number(rule.COUNT)||10000,10000));
    const result=[];
    const weekdays={SU:0,MO:1,TU:2,WE:3,TH:4,FR:5,SA:6};
    const selected=new Set((rule.BYDAY||'').split(',').map(day=>weekdays[day]).filter(day=>day!=null));
    if(rule.FREQ==='WEEKLY'&&!selected.size)selected.add(zoneParts(event.start,event.timeZone).weekday);
    const interval=Math.max(1,Number(rule.INTERVAL)||1),origin=zoneParts(event.start,event.timeZone);
    const until=rule.UNTIL?(rule.UNTIL.endsWith('Z')?instant(rule.UNTIL):
      (zonedInstant(rule.UNTIL,event.timeZone)??dateInstant(rule.UNTIL))):null;
    const originDay=Date.UTC(origin.year,origin.month-1,origin.day);
    let start=event.start,matched=0;
    if(rule.FREQ==='DAILY'&&start+duration<=rangeStart) {
      const target=zoneParts(rangeStart-duration,event.timeZone);
      const targetDay=Date.UTC(target.year,target.month-1,target.day);
      const elapsed=Math.max(0,Math.floor((targetDay-originDay)/(24*60*60*1000)));
      const jumps=Math.floor(elapsed/interval);
      if(jumps) {start=addCalendarDays(start,jumps*interval,event.timeZone);matched=jumps;}
      while(start+duration<=rangeStart&&matched<count) {start=addCalendarDays(start,interval,event.timeZone);matched+=1;}
    }
    if(rule.FREQ==='WEEKLY'&&start+duration<=rangeStart) {
      const target=zoneParts(rangeStart-duration,event.timeZone);
      const targetDay=Date.UTC(target.year,target.month-1,target.day);
      const elapsed=Math.max(0,Math.floor((targetDay-originDay)/(24*60*60*1000)));
      const cycleDays=7*interval,cycles=Math.floor(elapsed/cycleDays),offset=elapsed-cycles*cycleDays;
      matched=cycles*selected.size;
      start=addCalendarDays(event.start,cycles*cycleDays+(offset>=7?offset:0),event.timeZone);
      while(start+duration<=rangeStart&&matched<count) {
        const cursor=zoneParts(start,event.timeZone),cursorDay=Date.UTC(cursor.year,cursor.month-1,cursor.day);
        const weeks=Math.floor((cursorDay-originDay)/(7*24*60*60*1000));
        if(weeks%interval===0&&selected.has(cursor.weekday))matched+=1;
        start=addCalendarDays(start,1,event.timeZone);
      }
    }
    while(start<rangeEnd&&matched<count&&(until==null||start<=until)) {
      const cursor=zoneParts(start,event.timeZone),cursorDay=Date.UTC(cursor.year,cursor.month-1,cursor.day);
      const weeks=Math.floor((cursorDay-originDay)/(7*24*60*60*1000));
      const matches=rule.FREQ==='DAILY'||(weeks%interval===0&&selected.has(cursor.weekday));
      if(matches) {
        matched+=1;
        if(!excluded.has(start))result.push({start,end:start+duration});
      }
      start=addCalendarDays(start,rule.FREQ==='DAILY'?interval:1,event.timeZone);
    }
    (event.rdates||[]).forEach(extra=>{
      if(extra<rangeEnd&&!excluded.has(extra)&&!result.some(item=>item.start===extra))result.push({start:extra,end:extra+duration});
    });
    return result;
  }
  function dayBoundary(date,minutes) {
    const [year,month,day]=date.split('-').map(Number);
    return new Date(year,month-1,day,Math.floor(minutes/60),minutes%60).getTime();
  }
  function localClock(value) {
    const date=new Date(value);
    return String(date.getHours()).padStart(2,'0')+':'+String(date.getMinutes()).padStart(2,'0');
  }
  function review(source,{dates,workdayStart='09:00',workdayEnd='17:00'}={}) {
    if(!Array.isArray(dates)||dates.length!==7)throw new Error('Week Ahead must contain seven dates.');
    const startMinute=clock(workdayStart),endMinute=clock(workdayEnd);
    if(startMinute==null||endMinute==null||endMinute<=startMinute)throw new Error('Working hours must end after they start.');
    const parsedEvents=events(source),rangeStart=dayBoundary(dates[0],0),rangeEnd=dayBoundary(dates[6],24*60);
    const unsupportedRecurrence=parsedEvents.filter(event=>event.rule&&!supportedRecurrence(event.rule)&&
      event.start<rangeEnd&&(event.end>rangeStart||event.rule)).length;
    const unsupportedTimezone=parsedEvents.filter(event=>event.unsupportedTimezone).length;
    const unsupported_count=unsupportedRecurrence+unsupportedTimezone;
    const calendarEvents=parsedEvents.filter(event=>!event.unsupportedTimezone).flatMap(event=>occurrences(event,rangeStart,rangeEnd));
    const days=dates.map(plan_date=>{
      const start=dayBoundary(plan_date,startMinute),end=dayBoundary(plan_date,endMinute);
      const ranges=calendarEvents.map(event=>[Math.max(start,event.start),Math.min(end,event.end)])
        .filter(range=>range[1]>range[0]).sort((left,right)=>left[0]-right[0]);
      const merged=[];
      ranges.forEach(range=>{
        const previous=merged[merged.length-1];
        if(previous&&range[0]<=previous[1])previous[1]=Math.max(previous[1],range[1]);
        else merged.push([...range]);
      });
      const busy_minutes=Math.round(merged.reduce((total,range)=>total+range[1]-range[0],0)/60000);
      const free_windows=[];let cursor=start;
      merged.forEach(range=>{
        if(range[0]>cursor)free_windows.push({start_time:localClock(cursor),end_time:localClock(range[0])});
        cursor=Math.max(cursor,range[1]);
      });
      if(cursor<end)free_windows.push({start_time:localClock(cursor),end_time:localClock(end)});
      return {plan_date,busy_minutes,capacity_minutes:endMinute-startMinute-busy_minutes,free_windows};
    });
    days.unsupported_count=unsupported_count;
    days.unsupported_timezone_count=unsupportedTimezone;
    return days;
  }
  function createWorkflow({controller,qs,onApplied=()=>{}}={}) {
    let reviewed=null,activeAvailability=null,needsReflow=false,reflowPreview=null;
    const root=()=>qs('#week-capacity-import');
    function clear() {
      reviewed=null;needsReflow=false;reflowPreview=null;
      qs('#week-capacity-file').value='';
      qs('#week-capacity-review').hidden=true;
      qs('#week-capacity-days').innerHTML='';
      qs('#apply-week-capacities').disabled=false;
      qs('#apply-week-capacities').textContent='Apply seven capacities';
    }
    function open() {
      clear();root().hidden=false;qs('#week-capacity-status').textContent='';
      qs('#week-capacity-file').focus?.();return true;
    }
    function cancel() {clear();root().hidden=true;return true;}
    function reviewSource(source) {
      const display=controller.dates();
      reviewed=review(source,{dates:display.map(item=>item.date),workdayStart:qs('#week-capacity-start').value,
        workdayEnd:qs('#week-capacity-end').value});
      const current=controller.review?.().days||[];
      needsReflow=reviewed.some((day,index)=>Number(current[index]?.planned_minutes)>day.capacity_minutes);
      const previewValues=reviewed.map(day=>({plan_date:day.plan_date,capacity_minutes:day.capacity_minutes,
        free_windows:day.free_windows.map(window=>({...window}))}));
      reflowPreview=needsReflow?controller.previewCapacityReflow?.(previewValues):null;
      const destinations=new Map();
      (reflowPreview?.days||[]).forEach(day=>(day.ids||[]).forEach(id=>destinations.set(id,day.plan_date)));
      qs('#week-capacity-days').innerHTML=reviewed.map((day,index)=>{
        const previous=current[index]||{},planned=Number(previous.planned_minutes)||0,over=Math.max(0,planned-day.capacity_minutes);
        const moved=[...new Set((previous.ids||[]).map(id=>destinations.get(id)).filter(date=>date&&date!==day.plan_date))]
          .map(date=>display.find(item=>item.date===date)?.label||date);
        return '<article class="week-capacity-day'+(over?' is-overloaded':'')+'"><strong>'+display[index].label+'</strong><span>'+
          (Number.isInteger(previous.capacity_minutes)?previous.capacity_minutes+' → ':'')+day.capacity_minutes+' min available</span><small>'+
          planned+' min planned · '+day.busy_minutes+' min busy</small>'+(over?'<small class="week-capacity-over">'+over+
          ' min over refreshed capacity</small>':'')+(moved.length?'<small>Moves to '+moved.join(', ')+'</small>':'')+'</article>';
      }).join('');
      qs('#week-capacity-review').hidden=false;
      const unsupported=reviewed.unsupported_count||0;
      const reflowBlocked=Boolean(reflowPreview?.blockers?.length);
      qs('#apply-week-capacities').disabled=unsupported>0||reflowBlocked;
      qs('#apply-week-capacities').textContent=needsReflow?'Apply capacities & reflow':'Apply seven capacities';
      const kind=reviewed.unsupported_timezone_count?'calendar event':'recurring event';
      qs('#week-capacity-status').textContent=unsupported?
        unsupported+' '+kind+(unsupported===1?'':'s')+' could not be counted. Apply is unavailable; export a simpler seven-day calendar and try again.':
        (reflowBlocked?'Reflow needs an estimate for every planned item before refreshed capacity can be applied.':
          (reflowPreview?.unscheduled?.length?reflowPreview.unscheduled.length+' planned item'+
            (reflowPreview.unscheduled.length===1?'':'s')+' will return to My Work. Review before applying.':
            'Review seven capacity totals. Calendar details stay on this device.'));
      return reviewed.map(day=>({...day}));
    }
    async function apply() {
      if(!reviewed||reviewed.unsupported_count)return false;
      const keep=Boolean(qs('#keep-week-free-times')?.checked);
      const values=reviewed.map(day=>({
        plan_date:day.plan_date,capacity_minutes:day.capacity_minutes,
        ...(keep?{free_windows:day.free_windows.map(window=>({...window}))}:{}),
      }));
      const staged=needsReflow?controller.applyCapacityReflow?.(values):controller.stageCapacities(values);
      if(!staged)return false;
      qs('#apply-week-capacities').disabled=true;
      try {await controller.flush();activeAvailability=reviewed.map(day=>({plan_date:day.plan_date,
        free_windows:day.free_windows.map(window=>({...window}))}));cancel();onApplied();return true;}
      finally {qs('#apply-week-capacities').disabled=false;}
    }
    qs('#week-capacity-file')?.addEventListener('change',async event=>{
      const file=event.currentTarget.files?.[0];if(!file)return;
      try {
        if(file.size>maxBytes)throw new Error('Calendar files must be 1 MB or smaller.');
        reviewSource(await file.text());
      } catch(error) {clear();qs('#week-capacity-status').textContent=error.message||'Calendar could not be read.';}
    });
    qs('#cancel-week-capacity-import')?.addEventListener('click',cancel);
    qs('#apply-week-capacities')?.addEventListener('click',()=>apply().catch(error=>{
      qs('#week-capacity-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Capacities remain saved on this phone.';
    }));
    function availability() {
      const saved=(controller.state?.().days||[]).filter(day=>Array.isArray(day.free_windows));
      const source=activeAvailability||saved;
      return source.length?source.map(day=>({plan_date:day.plan_date,
        free_windows:day.free_windows.map(window=>({...window}))})):null;
    }
    return {open,cancel,review:reviewSource,apply,state:()=>reviewed?reviewed.map(day=>({...day})):null,
      availability};
  }
  function mount(controller,weekWorkflow,qs) {
    const workflow=createWorkflow({controller,qs,onApplied:weekWorkflow.renderReview});
    StackchainWeekCalendar.setAvailabilityProvider(workflow.availability);
    qs('#open-week-capacity-import').addEventListener('click',workflow.open);
    return workflow;
  }
  return {review,createWorkflow,mount};
}
const weekCalendarImport=createWeekCalendarImport();
if(typeof module!=='undefined'&&module.exports)module.exports=weekCalendarImport;
