function createWeekPlan({fetchJson,localDate,timeZone,storage,getLogin,coordinator,now=()=>Date.now()}={}) {
  let week={revision:0,timezone:null,days:[]};
  let flushing=null;
  let lastConflict=null;
  let offlineSnapshot=false;
  let refreshedAt=null;
  let confirmedItems={};
  let pendingItems={};
  let pulling=null;
  const storagePrefix='stackchain.week-sync.v1.';
  const confirmedPrefix='stackchain.week-confirmed.v1.';
  const pullPrefix='stackchain.week-today-pull.v1.';
  const cloneDefaults=value=>Array.isArray(value)&&value.length===7?value.slice():null;
  const cloneWindows=value=>Array.isArray(value)?value.map(window=>({
    start_time:window.start_time,end_time:window.end_time,
  })):null;
  const cloneStartTimes=day=>Object.fromEntries(Object.entries(day.start_times||{})
    .filter(([id,value])=>(day.ids||[]).includes(id)&&/^\d{2}:\d{2}$/.test(value)));
  const cloneDay=day=>({
    plan_date:day.plan_date,ids:[...(day.ids||[])],capacity_minutes:day.capacity_minutes??null,
    estimates:{...(day.estimates||{})},
    ...(cloneWindows(day.free_windows)?{free_windows:cloneWindows(day.free_windows)}:{}),
    ...(Object.keys(cloneStartTimes(day)).length?{start_times:cloneStartTimes(day)}:{}),
  });
  const sanitizeItem=value=>value?{kind:value.kind==='pull'?'pull':'issue',
    title:String(value.title||'').slice(0,500),repository:String(value.repository||'').slice(0,255),
    number:Number.isInteger(value.number)?value.number:null}:null;
  function sanitizeItems(days,sources={}) {
    const sanitized={};
    new Set((days||[]).flatMap(day=>day.ids||[])).forEach(id=>{
      const value=sanitizeItem(sources[id]);if(value)sanitized[id]=value;
    });
    return sanitized;
  }
  const state=()=>({revision:week.revision,timezone:week.timezone,days:week.days.map(cloneDay),
    ...(cloneDefaults(week.availability_defaults)?{availability_defaults:cloneDefaults(week.availability_defaults)}:{}),
    offline_snapshot:offlineSnapshot,...(refreshedAt?{refreshed_at:refreshedAt}:{}),
    ...(week.sync_pending?{base_revision:week.base_revision,sync_pending:true}:{})});
  function storageKey() {
    const login=String(getLogin?.()||'').trim().toLowerCase();
    return login?storagePrefix+encodeURIComponent(login):'';
  }
  function confirmedKey() {
    const login=String(getLogin?.()||'').trim().toLowerCase();
    return login?confirmedPrefix+encodeURIComponent(login):'';
  }
  function pullKey() {
    const login=String(getLogin?.()||'').trim().toLowerCase();
    return login?pullPrefix+encodeURIComponent(login):'';
  }
  function pullQueue() {
    const key=pullKey();
    if(!key||!storage)return [];
    try {
      const value=JSON.parse(storage.getItem(key)||'null');
      const entries=Array.isArray(value?.entries)?value.entries:(value?[value]:[]);
      return entries.filter(entry=>typeof entry?.body?.operation_id==='string'&&Array.isArray(entry?.today?.ids)&&
        Number.isInteger(entry?.week?.revision)&&Array.isArray(entry?.week?.days));
    } catch(_error){return [];}
  }
  function writePullQueue(entries) {
    const key=pullKey();if(!key||!storage)return false;
    try {
      if(entries.length)storage.setItem(key,JSON.stringify({version:2,entries}));
      else storage.removeItem(key);
      return true;
    } catch(_error){return false;}
  }
  function pendingPull() {
    return pullQueue()[0]||null;
  }
  function resumePull() {
    const queue=pullQueue(),conflicted=queue.find(entry=>entry.conflict&&entry.current?.week);
    const value=conflicted||queue[queue.length-1];
    if(!value)return null;
    adopt(conflicted?conflicted.current.week:value.week);
    return {...value,...(conflicted?.current||{}),sync_pending:true};
  }
  function readConfirmed() {
    const key=confirmedKey();
    if(!key||!storage)return null;
    try {
      const value=JSON.parse(storage.getItem(key)||'null');
      if(!Number.isInteger(value?.week?.revision)||!Array.isArray(value?.week?.days)||
        typeof value?.refreshed_at!=='string')return null;
      return value;
    } catch(_error){return null;}
  }
  function persistConfirmed() {
    const key=confirmedKey();
    if(!key||!storage||week.sync_pending)return false;
    const record={refreshed_at:new Date(now()).toISOString(),week:{revision:week.revision,
      timezone:week.timezone,days:week.days.map(cloneDay),
      ...(cloneDefaults(week.availability_defaults)?{availability_defaults:cloneDefaults(week.availability_defaults)}:{})},items:{...confirmedItems}};
    try {storage.setItem(key,JSON.stringify(record));refreshedAt=record.refreshed_at;return true;}
    catch(_error){return false;}
  }
  function rememberItems(items={}) {
    confirmedItems=sanitizeItems(week.days,{...confirmedItems,...items});
    return persistConfirmed();
  }
  function pending() {
    const key=storageKey();
    if(!key||!storage) return false;
    try {
      const value=JSON.parse(storage.getItem(key)||'null');
      return Number.isInteger(value?.base_revision)&&Array.isArray(value?.days)?{
        revision:value.base_revision,base_revision:value.base_revision,timezone:value.timezone||null,
        ...(cloneDefaults(value.availability_defaults)?{availability_defaults:cloneDefaults(value.availability_defaults)}:{}),
        days:value.days.map(cloneDay).sort((left,right)=>left.plan_date.localeCompare(right.plan_date)),sync_pending:true,
        items:sanitizeItems(value.days,value.items||{}),
        ...(Array.isArray(value.base_days)?{base_days:value.base_days.map(cloneDay)
          .sort((left,right)=>left.plan_date.localeCompare(right.plan_date))}:{}),
        ...(value.resolutions&&typeof value.resolutions==='object'?{resolutions:{...value.resolutions}}:{}),
      }:false;
    } catch(_error) { return false; }
  }
  function adopt(value) {
    if(!Number.isInteger(value?.revision)||!Array.isArray(value?.days)) return false;
    week={revision:value.revision,timezone:value.timezone||null,days:value.days.map(cloneDay)
      .sort((left,right)=>left.plan_date.localeCompare(right.plan_date))};
    if(cloneDefaults(value.availability_defaults))week.availability_defaults=cloneDefaults(value.availability_defaults);
    offlineSnapshot=false;
    pendingItems={};
    lastConflict=null;
    return state();
  }
  function adoptConfirmed(value,items=confirmedItems) {
    const adopted=adopt(value);
    if(adopted){confirmedItems=sanitizeItems(week.days,items);persistConfirmed();}
    return adopted;
  }
  function addDays(value, amount) {
    const [year,month,day]=value.split('-').map(Number);
    return new Date(Date.UTC(year,month-1,day+amount)).toISOString().slice(0,10);
  }
  function dates() {
    return Array.from({length:7},(_,index)=>{
      const date=addDays(localDate(),index+1);
      const parsed=new Date(date+'T12:00:00Z');
      return {date,label:new Intl.DateTimeFormat('en',{weekday:'short',month:'short',day:'numeric',timeZone:'UTC'}).format(parsed)};
    });
  }
  function day(planDate) {
    const found=week.days.find(item=>item.plan_date===planDate);
    if(found&&found.capacity_minutes!==null&&found.capacity_minutes!==undefined)
      return {...cloneDay(found),capacity_source:'override'};
    const parsed=new Date(planDate+'T12:00:00Z'),weekday=(parsed.getUTCDay()+6)%7;
    const inherited=cloneDefaults(week.availability_defaults)?.[weekday];
    return {...(found?cloneDay(found):{plan_date:planDate,ids:[],capacity_minutes:null,estimates:{}}),
      ...(Number.isInteger(inherited)?{capacity_minutes:inherited,capacity_source:'default'}:{capacity_source:'unset'})};
  }
  function pass(planDate) {
    const available=dates(),index=available.findIndex(item=>item.date===planDate);
    if(index<0) return null;
    return {position:index+1,total:available.length,planned:week.days.filter(item=>item.ids.length).length,
      next_date:available[index+1]?.date||null,last:index===available.length-1};
  }
  async function load() {
    const queued=pending();
    if(queued){
      week=queued;pendingItems=queued.items||{};
      confirmedItems=sanitizeItems(week.days,readConfirmed()?.items||{});
      offlineSnapshot=false;return state();
    }
    try {
      adopt(await fetchJson('api/v1/week'));
      const cached=readConfirmed();confirmedItems=sanitizeItems(week.days,cached?.items||{});persistConfirmed();
      return state();
    } catch(error) {
      if(error?.status===401||error?.status===403)throw error;
      const cached=readConfirmed();
      if(!cached)throw error;
      adopt(cached.week);offlineSnapshot=true;refreshedAt=cached.refreshed_at;
      confirmedItems=sanitizeItems(week.days,cached.items||{});
      return state();
    }
  }
  function stageDays(days) {
    if(offlineSnapshot)return false;
    const key=storageKey();
    if(!key||!storage) return false;
    const queued={base_revision:Number.isInteger(week.base_revision)?week.base_revision:week.revision,
      timezone:timeZone(),days:days.map(cloneDay).sort((left,right)=>left.plan_date.localeCompare(right.plan_date)),
      base_days:(week.base_days||week.days).map(cloneDay)};
    if(cloneDefaults(week.availability_defaults))queued.availability_defaults=cloneDefaults(week.availability_defaults);
    queued.items=sanitizeItems(queued.days,{...confirmedItems,...pendingItems,...(pending()?.items||{})});
    try { storage.setItem(key,JSON.stringify(queued)); }
    catch(_error) { return false; }
    lastConflict=null;
    week={revision:queued.base_revision,...queued,sync_pending:true};
    pendingItems=queued.items;
    return state();
  }
  function rememberPendingItem(id,value) {
    const key=storageKey(),item=sanitizeItem(value);
    if(!key||!storage||!item)return false;
    try {
      const raw=JSON.parse(storage.getItem(key)||'null');
      if(!Number.isInteger(raw?.base_revision)||!Array.isArray(raw?.days)||
        !raw.days.some(day=>(day.ids||[]).includes(id)))return false;
      raw.items=sanitizeItems(raw.days,{...(raw.items||{}),[id]:item});
      storage.setItem(key,JSON.stringify(raw));pendingItems=raw.items;
      if(week.sync_pending)week={...week,items:raw.items};
      return true;
    } catch(_error){return false;}
  }
  function stageDay(planDate,value) {
    return stageDays(week.days.filter(item=>item.plan_date!==planDate).concat([{
      plan_date:planDate,ids:[...(value.ids||[])],capacity_minutes:value.capacity_minutes??null,
      estimates:{...(value.estimates||{})},
      ...(cloneWindows(value.free_windows)?{free_windows:cloneWindows(value.free_windows)}:{}),
      ...(Object.keys(cloneStartTimes(value)).length?{start_times:cloneStartTimes(value)}:{}),
    }]));
  }
  function stageStartTimes(planDate,startTimes) {
    const value=day(planDate);
    if(!value||!startTimes||typeof startTimes!=='object'||Array.isArray(startTimes))return false;
    value.start_times=Object.fromEntries(value.ids.filter(id=>typeof startTimes[id]==='string').map(id=>[id,startTimes[id]]));
    return stageDay(planDate,value);
  }
  function stageCapacities(values) {
    const changed=capacityDays(values);
    return changed&&stageDays(changed);
  }
  function capacityDays(values) {
    const available=dates().map(item=>item.date);
    if(!Array.isArray(values)||values.length!==available.length)return false;
    const capacities=new Map(values.map(value=>[value?.plan_date,value]));
    if(capacities.size!==available.length||available.some(date=>{
      const value=capacities.get(date)?.capacity_minutes;
      return !Number.isInteger(value)||value<0||value>1440;
    }))return false;
    return available.map(planDate=>{
      const value=day(planDate),capacity=capacities.get(planDate);
      value.capacity_minutes=capacity.capacity_minutes;
      if(Array.isArray(capacity.free_windows))value.free_windows=cloneWindows(capacity.free_windows);
      else delete value.free_windows;
      return value;
    });
  }
  function stageAvailabilityDefaults(values) {
    if(!cloneDefaults(values)||values.some(value=>!Number.isInteger(value)||value<0||value>1440)||offlineSnapshot)return false;
    const previous=week.availability_defaults;
    week.availability_defaults=values.slice();
    const staged=stageDays(week.days);
    if(!staged)week.availability_defaults=previous;
    return staged;
  }
  function disableAvailabilityDefaults() {
    if(offlineSnapshot)return false;
    const previous=week.availability_defaults;
    delete week.availability_defaults;
    const staged=stageDays(week.days);
    if(!staged&&previous)week.availability_defaults=previous;
    return staged;
  }
  function review() {
    const assigned=new Map();
    const days=dates().map(item=>{
      const value=day(item.date),estimates=value.estimates||{};
      value.ids.forEach(id=>assigned.set(id,[...(assigned.get(id)||[]),item.date]));
      const planned_minutes=value.ids.reduce((total,id)=>total+(Number(estimates[id])||0),0);
      const capacity_minutes=Number(value.capacity_minutes)||0;
      return {...value,label:item.label,planned_minutes,
        overloaded:Boolean(capacity_minutes&&planned_minutes>capacity_minutes)};
    });
    const duplicates=[...assigned.entries()].filter(([_id,assignedDates])=>assignedDates.length>1)
      .map(([id,assignedDates])=>({id,dates:assignedDates}));
    const blockers=[];
    days.forEach(value=>{
      if(value.ids.length&&!Number(value.capacity_minutes))blockers.push({type:'missing-capacity',plan_date:value.plan_date});
      value.ids.forEach(id=>{if(!(Number(value.estimates?.[id])>0))blockers.push({type:'missing-estimate',plan_date:value.plan_date,id});});
      if(value.overloaded)blockers.push({type:'over-capacity',plan_date:value.plan_date,
        minutes:value.planned_minutes-Number(value.capacity_minutes)});
    });
    blockers.sort((left,right)=>left.plan_date.localeCompare(right.plan_date)||
      ['over-capacity','missing-estimate','missing-capacity'].indexOf(left.type)-['over-capacity','missing-estimate','missing-capacity'].indexOf(right.type));
    return {days,duplicates,blockers,can_confirm:duplicates.length===0&&blockers.length===0};
  }
  function previewReflow(capacities=null) {
    const available=capacities?capacityDays(capacities):dates().map(item=>day(item.date));
    if(!available)return {days:[],blockers:[{type:'invalid-capacities'}],unscheduled:[]};
    const seen=new Set(),work=[];
    available.forEach(source=>(source.ids||[]).forEach(id=>{
      if(seen.has(id))return;
      seen.add(id);
      const estimate=Number(source.estimates?.[id]);
      work.push({id,estimate:Number.isFinite(estimate)?estimate:null,from_date:source.plan_date});
    }));
    const blockers=work.filter(item=>!(item.estimate>0)).map(item=>({
      type:'missing-estimate',id:item.id,plan_date:item.from_date,
    }));
    if(blockers.length)return {days:[],blockers,unscheduled:[]};
    const days=available.map(source=>({plan_date:source.plan_date,ids:[],
      capacity_minutes:source.capacity_minutes??null,estimates:{},
      ...(cloneWindows(source.free_windows)?{free_windows:cloneWindows(source.free_windows)}:{})}));
    const unscheduled=[];let cursor=0;
    if(!blockers.length)work.forEach(item=>{
      const destination=days.slice(cursor).find(value=>value.ids.length<5&&Number(value.capacity_minutes)>0&&
        value.ids.reduce((total,id)=>total+Number(value.estimates[id]),0)+item.estimate<=Number(value.capacity_minutes));
      if(!destination){unscheduled.push({...item});return;}
      cursor=days.indexOf(destination);
      destination.ids.push(item.id);destination.estimates[item.id]=item.estimate;
    });
    return {days,blockers,unscheduled};
  }
  function applyReflow() {
    const preview=previewReflow();
    if(preview.blockers.length||offlineSnapshot)return false;
    return stageDays(preview.days);
  }
  function previewCapacityReflow(values) { return previewReflow(values); }
  function applyCapacityReflow(values) {
    const preview=previewCapacityReflow(values);
    if(preview.blockers.length||offlineSnapshot)return false;
    return stageDays(preview.days);
  }
  function move(id,toDate) {
    if(!dates().some(item=>item.date===toDate)) return false;
    const sources=week.days.filter(item=>(item.ids||[]).includes(id));
    if(!sources.length) return false;
    const estimate=sources.map(item=>Number(item.estimates?.[id])).find(Number.isFinite);
    const moved=week.days.map(item=>{
      const estimates={...(item.estimates||{})};delete estimates[id];
      return {...cloneDay(item),ids:(item.ids||[]).filter(value=>value!==id),estimates};
    });
    let destination=moved.find(item=>item.plan_date===toDate);
    if(!destination){destination={plan_date:toDate,ids:[],capacity_minutes:null,estimates:{}};moved.push(destination);}
    destination.ids.push(id);
    if(Number.isFinite(estimate)) destination.estimates[id]=estimate;
    return Boolean(stageDays(moved));
  }
  function retire(id) {
    const removed=week.days.reduce((total,item)=>total+(item.ids||[]).filter(value=>value===id).length,0);
    if(!id||!removed||offlineSnapshot)return false;
    const days=week.days.map(item=>{
      const estimates={...(item.estimates||{})};delete estimates[id];
      return {...cloneDay(item),ids:(item.ids||[]).filter(value=>value!==id),estimates};
    });
    const staged=stageDays(days);
    return staged?{...staged,removed}:false;
  }
  function unplan(id) {
    if(!id||offlineSnapshot)return false;
    const source=week.days.find(item=>(item.ids||[]).includes(id));
    if(!source)return false;
    const index=source.ids.indexOf(id),estimate=Number(source.estimates?.[id]);
    const receipt={id,plan_date:source.plan_date,index,estimate:Number.isFinite(estimate)?estimate:null};
    const staged=retire(id);
    return staged?receipt:false;
  }
  function restore(receipt) {
    if(!receipt?.id||!receipt.plan_date||!Number.isInteger(receipt.index)||offlineSnapshot||placement(receipt.id))return false;
    const days=week.days.map(cloneDay);
    let destination=days.find(item=>item.plan_date===receipt.plan_date);
    if(!destination){destination={plan_date:receipt.plan_date,ids:[],capacity_minutes:null,estimates:{}};days.push(destination);}
    destination.ids.splice(Math.min(receipt.index,destination.ids.length),0,receipt.id);
    if(Number.isFinite(Number(receipt.estimate)))destination.estimates[receipt.id]=Number(receipt.estimate);
    return stageDays(days);
  }
  function placement(id) {
    const found=week.days.find(item=>(item.ids||[]).includes(id));
    if(!found)return null;
    const estimate=Number(found.estimates?.[id]);
    return {date:found.plan_date,estimate:Number.isFinite(estimate)?estimate:null};
  }
  function place(id,toDate,estimate,{move:allowMove=false}={}) {
    if(!id||!dates().some(item=>item.date===toDate)||!Number.isFinite(Number(estimate))||Number(estimate)<=0)return false;
    const existing=placement(id);
    if(existing&&existing.date!==toDate&&!allowMove)return false;
    const changed=week.days.map(item=>{
      const estimates={...(item.estimates||{})};
      if(item.plan_date!==toDate){delete estimates[id];return {...cloneDay(item),ids:(item.ids||[]).filter(value=>value!==id),estimates};}
      return cloneDay(item);
    });
    let destination=changed.find(item=>item.plan_date===toDate);
    if(!destination){destination={plan_date:toDate,ids:[],capacity_minutes:null,estimates:{}};changed.push(destination);}
    if(!destination.ids.includes(id))destination.ids.push(id);
    destination.estimates[id]=Number(estimate);
    return Boolean(stageDays(changed));
  }
  function deliveryBody(value) {
    return {base_revision:value.base_revision,timezone:value.timezone,days:value.days.map(cloneDay),
      ...(cloneDefaults(value.availability_defaults)?{availability_defaults:cloneDefaults(value.availability_defaults)}:{})};
  }
  function reconcile(local,remote) {
    if(!Array.isArray(local.base_days)) return null;
    const byDate=(days,date)=>days.find(day=>day.plan_date===date)||
      {plan_date:date,ids:[],capacity_minutes:null,estimates:{}};
    const same=(left,right)=>JSON.stringify(cloneDay(left))===JSON.stringify(cloneDay(right));
    const dates=[...new Set([...local.base_days,...local.days,...remote.days].map(day=>day.plan_date))].sort();
    const conflicts=[],days=dates.map(date=>{
      const base=byDate(local.base_days,date),phone=byDate(local.days,date),account=byDate(remote.days,date);
      const phoneChanged=!same(phone,base),accountChanged=!same(account,base);
      if(phoneChanged&&accountChanged&&!same(phone,account)){
        conflicts.push({plan_date:date,local:cloneDay(phone),remote:cloneDay(account)});return cloneDay(phone);
      }
      return cloneDay(phoneChanged?phone:account);
    });
    return {days,conflicts};
  }
  function flush() {
    if(flushing) return flushing;
    const key=storageKey();
    if(!pending()||!key) return Promise.resolve(false);
    const sameBody=(value,body)=>JSON.stringify(deliveryBody(value))===JSON.stringify(body);
    const deliver=async()=>{
      const queued=pending();
      if(!queued)return false;
      const body=deliveryBody(queued);
      let saved;
      try {
        saved=await fetchJson('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
      } catch(error) {
        if(error?.status===409){
          const remote=await fetchJson('api/v1/week');
          const local=pending();
          const merged=local&&reconcile(local,remote);
          if(merged&&!merged.conflicts.length){
            saved=await fetchJson('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'},
              body:JSON.stringify({base_revision:remote.revision,timezone:local.timezone,days:merged.days,
                ...(cloneDefaults(local.availability_defaults)?{availability_defaults:cloneDefaults(local.availability_defaults)}:{})})});
            const current=pending();
            if(current&&sameBody(current,deliveryBody(local)))storage.removeItem(key);
            if(!pending())adoptConfirmed(saved,{...confirmedItems,...(local.items||{})});
            return saved;
          }
          lastConflict={key,local,remote:{revision:remote.revision,timezone:remote.timezone||null,
            days:remote.days.map(cloneDay),...(cloneDefaults(remote.availability_defaults)?
              {availability_defaults:cloneDefaults(remote.availability_defaults)}:{})},merged,choices:{...(local?.resolutions||{})}};
        } else {
          const current=pending();
          if(current&&!sameBody(current,body))return deliver();
        }
        throw error;
      }
      const current=pending();
      if(current&&sameBody(current,body)){
        storage.removeItem(key);adoptConfirmed(saved,{...confirmedItems,...(current.items||{})});return saved;
      }
      if(current){
        try {
          const raw=JSON.parse(storage.getItem(key));
          raw.base_revision=saved.revision;raw.base_days=saved.days.map(cloneDay);
          storage.setItem(key,JSON.stringify(raw));
          week={revision:raw.base_revision,...raw,sync_pending:true};
          pendingItems=raw.items||{};
        } catch(_error){return saved;}
        return deliver();
      }
      adoptConfirmed(saved,{...confirmedItems,...(queued.items||{})});return saved;
    };
    flushing=deliver().finally(()=>{flushing=null;});
    return flushing;
  }
  async function saveDay(planDate, value) {
    if(offlineSnapshot)return false;
    const local={revision:week.revision,timezone:timeZone(),days:week.days
      .filter(item=>item.plan_date!==planDate).concat([{
        plan_date:planDate,ids:[...(value.ids||[])],capacity_minutes:value.capacity_minutes??null,
        estimates:{...(value.estimates||{})},
      }]).sort((left,right)=>left.plan_date.localeCompare(right.plan_date))};
    const body={base_revision:local.revision,timezone:local.timezone,days:local.days,
      ...(cloneDefaults(week.availability_defaults)?{availability_defaults:cloneDefaults(week.availability_defaults)}:{})};
    try {
      return adoptConfirmed(await fetchJson('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));
    } catch(error) {
      if(error?.status===409) {
        const remote=await fetchJson('api/v1/week');
        lastConflict={local:{...local,...(cloneDefaults(week.availability_defaults)?{availability_defaults:cloneDefaults(week.availability_defaults)}:{})},
          remote:{revision:remote.revision,timezone:remote.timezone,days:remote.days.map(cloneDay),
            ...(cloneDefaults(remote.availability_defaults)?{availability_defaults:cloneDefaults(remote.availability_defaults)}:{})}};
      }
      throw error;
    }
  }
  function conflict() {
    const value=lastConflict&&(!lastConflict.key||lastConflict.key===storageKey())?{
      local:{...lastConflict.local,days:lastConflict.local.days.map(cloneDay)},
      remote:{...lastConflict.remote,days:lastConflict.remote.days.map(cloneDay)},
    }:null;
    if(value&&lastConflict.merged) value.conflicts=lastConflict.merged.conflicts.map(item=>({
      plan_date:item.plan_date,local:cloneDay(item.local),remote:cloneDay(item.remote),
      choice:lastConflict.choices[item.plan_date]||null,
    }));
    return value;
  }
  function chooseDay(planDate,source) {
    if(!lastConflict?.merged||!['phone','account'].includes(source)||
      !lastConflict.merged.conflicts.some(item=>item.plan_date===planDate)) return false;
    const key=storageKey(),queued=pending();
    if(key&&queued){
      try {
        const raw=JSON.parse(storage.getItem(key));
        raw.resolutions={...(raw.resolutions||{}),[planDate]:source};
        storage.setItem(key,JSON.stringify(raw));
      } catch(_error) { return false; }
    }
    lastConflict.choices[planDate]=source;
    return conflict();
  }
  async function saveMerged() {
    if(!lastConflict?.merged||lastConflict.key!==storageKey()) return false;
    if(lastConflict.merged.conflicts.some(item=>!lastConflict.choices[item.plan_date])) return false;
    const choices=lastConflict.choices;
    const days=lastConflict.merged.days.map(day=>{
      const item=lastConflict.merged.conflicts.find(value=>value.plan_date===day.plan_date);
      return item?cloneDay(choices[day.plan_date]==='account'?item.remote:item.local):cloneDay(day);
    });
    const local=lastConflict.local,body={base_revision:lastConflict.remote.revision,timezone:local.timezone,days,
      ...(cloneDefaults(local.availability_defaults)?{availability_defaults:cloneDefaults(local.availability_defaults)}:{})};
    try {
      const saved=await fetchJson('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
      const current=pending();
      if(current&&JSON.stringify(deliveryBody(current))===JSON.stringify(deliveryBody(local))) storage.removeItem(storageKey());
      if(!pending()) adoptConfirmed(saved,{...confirmedItems,...(local.items||{})});
      lastConflict=null;
      return saved;
    } catch(error) {
      if(error?.status===409){
        const remote=await fetchJson('api/v1/week'),merged=reconcile(local,remote);
        lastConflict={key:storageKey(),local,remote:{revision:remote.revision,timezone:remote.timezone||null,
          days:remote.days.map(cloneDay),...(cloneDefaults(remote.availability_defaults)?
            {availability_defaults:cloneDefaults(remote.availability_defaults)}:{})},merged,choices:{...(local.resolutions||{})}};
      }
      throw error;
    }
  }
  async function keepLocal() {
    if(!lastConflict||lastConflict.key!==storageKey()) return false;
    const conflictKey=lastConflict.key;
    const local={...lastConflict.local,days:lastConflict.local.days.map(cloneDay)};
    const body=deliveryBody({...local,base_revision:lastConflict.remote.revision});
    try {
      const saved=await fetchJson('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
      const current=pending();
      if(current&&JSON.stringify(deliveryBody(current))===JSON.stringify(deliveryBody(local))) storage.removeItem(storageKey());
      if(!pending()) adoptConfirmed(saved,{...confirmedItems,...(local.items||{})});
      lastConflict=null;
      return saved;
    } catch(error) {
      if(error?.status===409){
        const remote=await fetchJson('api/v1/week');
        lastConflict={key:conflictKey,local,remote:{revision:remote.revision,timezone:remote.timezone||null,
          days:remote.days.map(cloneDay),...(cloneDefaults(remote.availability_defaults)?
            {availability_defaults:cloneDefaults(remote.availability_defaults)}:{})}};
      }
      throw error;
    }
  }
  function useRemote() {
    if(!lastConflict||lastConflict.key!==storageKey()) return false;
    const remote=lastConflict.remote;
    storage.removeItem(storageKey());
    const adopted=adoptConfirmed(remote);
    lastConflict=null;
    return adopted;
  }
  async function promote(todayRevision) {
    if(offlineSnapshot||pending()||conflict()) return false;
    const due=week.days.find(item=>item.plan_date<=localDate()&&item.ids.length);
    if(!due) return false;
    return fetchJson('api/v1/week/promote',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
      promotion_id:`week-${due.plan_date}-r${week.revision}`,week_revision:week.revision,
      plan_date:due.plan_date,today_revision:todayRevision,
    })});
  }
  async function pullItem(identity,today,operationId,allowOverCapacity=false) {
    if(offlineSnapshot||pending()||conflict()||!identity||!operationId||
      !Number.isInteger(today?.revision)||!(today.ids||[]).length)return false;
    const body={operation_id:operationId,identity,today_revision:today.revision,week_revision:week.revision,
      allow_over_capacity:Boolean(allowOverCapacity)};
    const key=pullKey();
    if(key&&storage){
      const source=week.days.find(day=>(day.ids||[]).includes(identity));
      if(!source)return false;
      const estimate=Number(source.estimates?.[identity]);
      const optimisticToday={...today,ids:[...(today.ids||[]),identity],estimates:{...(today.estimates||{}),
        ...(Number.isFinite(estimate)?{[identity]:estimate}:{})}};
      const optimisticWeek={revision:week.revision,timezone:week.timezone,days:week.days.map(day=>{
        const estimates={...(day.estimates||{})};delete estimates[identity];
        return {...cloneDay(day),ids:(day.ids||[]).filter(id=>id!==identity),estimates};
      })};
      const record={body,today:optimisticToday,week:optimisticWeek,queued_at:now()};
      const queue=pullQueue();
      if(!writePullQueue([...queue,record]))throw new Error('Could not save this pull on this device. Nothing changed.');
      adopt(optimisticWeek);
      try{return await flushPull();}
      catch(error){
        if(error?.status===401||error?.status===403)throw error;
        return {...record,sync_pending:true};
      }
    }
    const result=await fetchJson('api/v1/week/pull-item',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
    adoptConfirmed(result.week,{...confirmedItems,...pendingItems});return result;
  }
  function pullConflict() {
    const queue=pullQueue(),entry=queue[0];
    if(!entry?.conflict||!entry.current)return null;
    const identity=entry.body.identity,today=entry.current.today;
    const source=(entry.current.week.days||[]).find(day=>(day.ids||[]).includes(identity));
    return {identity,title:(pendingItems[identity]||confirmedItems[identity])?.title||identity,
      source_date:source?.plan_date||null,today_items:(today.ids||[]).length,
      today_minutes:(today.ids||[]).reduce((total,id)=>total+(Number(today.estimates?.[id])||0),0),
      today_capacity:Number(today.capacity_minutes)||0,operation_id:entry.body.operation_id,queue_length:queue.length};
  }
  async function resolvePullConflict(action) {
    const queue=pullQueue(),current=queue[0];
    if(!current?.conflict||!current.current||!['add','keep'].includes(action))return false;
    const latest=current.current,identity=current.body.identity;
    const alreadyToday=(latest.today.ids||[]).includes(identity);
    const stillInWeek=(latest.week.days||[]).some(day=>(day.ids||[]).includes(identity));
    let remaining=queue.slice(1);
    if(action==='add'&&!alreadyToday&&stillInWeek){
      if((latest.today.ids||[]).length>=5){const error=new Error('Today is full.');error.code='today_full';throw error;}
      const source=latest.week.days.find(day=>(day.ids||[]).includes(identity));
      const estimate=Number(source?.estimates?.[identity])||0;
      const minutes=(latest.today.ids||[]).reduce((total,id)=>total+(Number(latest.today.estimates?.[id])||0),0);
      const capacity=Number(latest.today.capacity_minutes)||0;
      if(capacity&&minutes+estimate>capacity&&!current.body.allow_over_capacity){
        const error=new Error('Today would exceed its current capacity.');error.code='today_over_capacity';throw error;
      }
      const rebased={...current,conflict:false,current:undefined,body:{...current.body,
        today_revision:latest.today.revision,week_revision:latest.week.revision}};
      remaining=[rebased,...remaining];
    }
    if(remaining.length){
      const head=remaining[0];
      remaining[0]={...head,conflict:false,current:undefined,body:{...head.body,
        today_revision:latest.today.revision,week_revision:latest.week.revision}};
    }
    if(!writePullQueue(remaining))throw new Error('Could not update queued Week Ahead work on this device.');
    adopt(latest.week);
    if(!remaining.length)return {...latest,sync_pending:false,resolved:alreadyToday?'already-moved':(!stillInWeek?'no-longer-planned':'kept')};
    return flushPull();
  }
  function flushPull() {
    if(pulling)return pulling;
    if(!pendingPull()||!pullKey())return Promise.resolve(false);
    const run=async()=>{
      let result,lastResult=false;
      while(pendingPull()){
        const current=pendingPull();
        try {
          result=await fetchJson('api/v1/week/pull-item',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(current.body)});
        } catch(error) {
          if(error?.status!==409)throw error;
          const [today,currentWeek]=await Promise.all([fetchJson('api/v1/today'),fetchJson('api/v1/week')]);
          const queue=pullQueue(),conflicted={...current,conflict:true,current:{today,week:currentWeek}};
          if(!writePullQueue([conflicted,...queue.slice(1)]))throw new Error('Could not preserve queued Week Ahead work on this device.');
          adopt(currentWeek);
          return {today,week:currentWeek,conflict:true,sync_pending:true};
        }
        const queue=pullQueue();
        if(queue[0]?.body?.operation_id!==current.body.operation_id)continue;
        const remaining=queue.slice(1).map(entry=>{
          const identity=entry.body.identity;
          const source=entry.week.days.find(day=>(day.ids||[]).includes(identity));
          const estimate=Number(entry.today?.estimates?.[identity]??source?.estimates?.[identity]);
          const today={...result.today,ids:(result.today.ids||[]).includes(identity)?[...result.today.ids]:[...result.today.ids,identity],
            estimates:{...(result.today.estimates||{}),...(Number.isFinite(estimate)?{[identity]:estimate}:{})}};
          const nextWeek={revision:result.week.revision,timezone:result.week.timezone||null,days:result.week.days.map(day=>{
            const estimates={...(day.estimates||{})};delete estimates[identity];
            return {...cloneDay(day),ids:(day.ids||[]).filter(id=>id!==identity),estimates};
          })};
          result={today,week:nextWeek};
          return {...entry,body:{...entry.body,today_revision:today.revision,week_revision:nextWeek.revision},today,week:nextWeek};
        });
        if(!writePullQueue(remaining))throw new Error('Could not update queued Week Ahead work on this device.');
        lastResult=result;
      }
      adoptConfirmed(lastResult.week,{...confirmedItems,...pendingItems});
      return {...lastResult,sync_pending:false};
    };
    const delivery=coordinator?coordinator.runExclusive('week',run):run();
    pulling=Promise.resolve(delivery).finally(()=>{pulling=null;});
    return pulling;
  }
  async function startEarly(planDate,todayRevision) {
    if(offlineSnapshot||pending()||conflict()||!Number.isInteger(todayRevision))return false;
    const day=week.days.find(item=>item.plan_date===planDate&&item.ids.length);
    if(!day)return false;
    const promoted=await fetchJson('api/v1/week/start-early',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
      promotion_id:`early-${day.plan_date}-r${week.revision}`,week_revision:week.revision,
      plan_date:day.plan_date,today_revision:todayRevision,
    })});
    await load();
    return promoted;
  }
  async function reconcilePromotion(preserved,selection) {
    if(offlineSnapshot||pending()||conflict()) return false;
    const due=(preserved?.week?.days||week.days).find(item=>item.plan_date<=localDate()&&item.ids.length);
    if(!due||!Number.isInteger(preserved?.today?.revision)) return false;
    const result=await fetchJson('api/v1/week/reconcile',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
      promotion_id:`reconcile-${due.plan_date}-r${preserved.week.revision}`,
      week_revision:preserved.week.revision,plan_date:due.plan_date,today_revision:preserved.today.revision,
      ids:[...(selection.ids||[])],capacity_minutes:selection.capacity_minutes??null,estimates:{...(selection.estimates||{})},
    })});
    adoptConfirmed(result.week);
    return result;
  }
  function summary() {
    const planned=week.days.filter(item=>item.ids.length);
    const items=planned.reduce((total,item)=>total+item.ids.length,0);
    const label=planned.length?`${items} item${items===1?'':'s'} across ${planned.length} day${planned.length===1?'':'s'}`:'Nothing planned';
    return label+(pending()?' · sync pending':'');
  }
  return {adopt,state,dates,day,pass,load,saveDay,stageDay,stageStartTimes,stageCapacities,stageAvailabilityDefaults,disableAvailabilityDefaults,review,previewReflow,applyReflow,previewCapacityReflow,applyCapacityReflow,move,retire,unplan,restore,placement,place,pending,flush,conflict,chooseDay,saveMerged,
    keepLocal,useRemote,promote,pullItem,pendingPull,resumePull,pullConflict,resolvePullConflict,flushPull,startEarly,reconcile:reconcilePromotion,summary,rememberItems,rememberPendingItem,
    item:id=>pendingItems[id]||confirmedItems[id]||null,
    offline:()=>offlineSnapshot,request:fetchJson,reschedule:()=>({storage,getLogin})};
}
function createWeekPlanWorkflow({controller,qs,getItem=()=>null,openItem,openPlanner,setReviewMode=()=>{},escapeHtml,escapeAttribute,
  todayWork,refresh:r=()=>{},warm:w=()=>{},today:t=()=>null,r:refresh=r,w:warm=w,t:getTodayPlan=t,
  confirmEarly=message=>globalThis.confirm?.(message)??false,setTimer=globalThis.setTimeout,clearTimer=globalThis.clearTimeout,
  operationId=()=>globalThis.crypto?.randomUUID?.()||('pull-'+Date.now()+'-'+Math.random().toString(16).slice(2)),x=null}={}) {
  let selectedDate=null;
  let reviewing=false;
  let overviewing=false;
  let editingFromReview=false;
  let blockedReviewOpen=false;
  let reconciliation=null;
  let loadState=null;
  let unplanReceipt=null;
  let unplanTimer=null;
  function renderOfflineState(value=loadState) {
    const notice=qs('#week-offline-snapshot'),retry=qs('#retry-week-live'),capacity=qs('#open-week-capacity-import');
    const offline=Boolean(value?.offline_snapshot);
    if(notice){
      notice.hidden=!offline;
      if(offline){
        const refreshed=new Date(value.refreshed_at);
        const label=Number.isNaN(refreshed.getTime())?'an earlier sync':refreshed.toLocaleString('en',{month:'short',day:'numeric',hour:'numeric',minute:'2-digit'});
        notice.textContent='Offline snapshot · refreshed '+label+'. Viewing only until live data returns.';
      }
    }
    if(retry)retry.hidden=!offline;
    if(capacity)capacity.hidden=offline;
    const availability=qs('#open-week-availability');if(availability)availability.hidden=offline;
  }
  function closeAvailability({focus=false}={}) {
    const panel=qs('#week-availability-editor');if(panel)panel.hidden=true;
    if(focus)qs('#open-week-availability')?.focus?.();
  }
  function openAvailability() {
    const panel=qs('#week-availability-editor'),defaults=controller.state().availability_defaults||[0,0,0,0,0,0,0];
    panel?.querySelectorAll?.('[data-weekday-capacity]').forEach(input=>{input.value=String(defaults[Number(input.dataset.weekdayCapacity)]||0);});
    if(panel){panel.hidden=false;panel.querySelector?.('[data-weekday-capacity]')?.focus?.();}
  }
  async function saveAvailability() {
    const panel=qs('#week-availability-editor'),inputs=[...(panel?.querySelectorAll?.('[data-weekday-capacity]')||[])];
    const values=inputs.map(input=>Number(input.value));
    const status=qs('#week-availability-status');
    if(values.length!==7||values.some(value=>!Number.isInteger(value)||value<0||value>1440)){
      if(status)status.textContent='Enter 0 to 1440 minutes for every weekday.';return false;
    }
    if(!controller.stageAvailabilityDefaults(values))return false;
    closeAvailability();renderReview();
    try{await controller.flush();renderReview();if(status)status.textContent='Weekly availability saved.';
      qs('#week-review-status').textContent='Weekly availability saved.';return true;}
    catch(error){if(status)status.textContent=(error.message||'Sync is unavailable.')+' Weekly availability remains saved on this phone.';return false;}
  }
  async function disableAvailability() {
    if(!controller.disableAvailabilityDefaults?.())return false;
    closeAvailability();renderReview();
    try{await controller.flush();renderReview();qs('#week-review-status').textContent='Weekly availability disabled.';return true;}
    catch(error){qs('#week-review-status').textContent=(error.message||'Sync is unavailable.')+' Change remains saved on this phone.';return false;}
  }
  qs('#open-week-availability')?.addEventListener('click',openAvailability);
  qs('#cancel-week-availability')?.addEventListener('click',()=>closeAvailability({focus:true}));
  qs('#save-week-availability')?.addEventListener('click',saveAvailability);
  qs('#disable-week-availability')?.addEventListener('click',disableAvailability);
  function reconciliationDay(){
    if(!reconciliation)return null;
    const due=(reconciliation.week?.days||[]).find(day=>day.ids?.length);
    if(!due)return null;
    const ids=[...new Set([...(reconciliation.today?.ids||[]),...(due.ids||[])])];
    return {plan_date:due.plan_date,ids,capacity_minutes:due.capacity_minutes??reconciliation.today?.capacity_minutes??null,
      estimates:{...(due.estimates||{}),...(reconciliation.today?.estimates||{})}};
  }
  function renderPass() {
    const progress=qs('#week-plan-progress'),save=qs('#save-today-plan');
    if(reviewing){
      const value=controller.review();
      progress.hidden=false;
      progress.textContent=`Review week · ${value.days.filter(day=>day.ids.length).length} planned`;
      return;
    }
    if(!selectedDate){progress.hidden=true;return;}
    const current=controller.pass(selectedDate);
    progress.hidden=false;
    progress.textContent=`Day ${current.position} of ${current.total} · ${current.planned} planned`;
    save.textContent=editingFromReview?'Save & review':(current.last?'Save week':'Save & next');
  }
  function conflictDetail(day) {
    const estimates=day.estimates||{},ids=day.ids||[];
    const minutes=ids.reduce((total,id)=>total+(Number(estimates[id])||0),0),capacity=Number(day.capacity_minutes)||0;
    return ids.length+' item'+(ids.length===1?'':'s')+(minutes&&capacity?' · '+minutes+' of '+capacity+' min':'');
  }
  function conflictMarkup(value) {
    const days=value?.days||[];
    return days.length?days.map(day=>'<p class="week-conflict-day"><strong>'+escapeHtml(day.plan_date)+'</strong>'+escapeHtml(conflictDetail(day))+'</p>').join(''):
      '<p class="muted">Nothing planned.</p>';
  }
  function renderConflict(conflict) {
    qs('#week-conflict-phone-plan').innerHTML=conflictMarkup(conflict.local);
    qs('#week-conflict-server-plan').innerHTML=conflictMarkup(conflict.remote);
    const perDay=Array.isArray(conflict.conflicts),days=qs('#week-conflict-days'),save=qs('#save-merged-week');
    qs('#week-conflict-legacy-plans').hidden=perDay;qs('#week-conflict-legacy-actions').hidden=perDay;
    days.hidden=!perDay;save.hidden=!perDay;
    if(perDay){
      days.innerHTML=conflict.conflicts.map(item=>{
        const date=escapeAttribute(item.plan_date),name='week-conflict-'+date;
        const choice=source=>'<label class="week-conflict-choice"><input type="radio" name="'+name+
          '" data-week-conflict-choice="'+source+'" data-week-conflict-date="'+date+'"'+(item.choice===source?' checked':'')+
          '><span><strong>'+(source==='phone'?'This phone':'Saved account')+'</strong><small>'+escapeHtml(conflictDetail(source==='phone'?item.local:item.remote))+'</small></span></label>';
        return '<section class="week-conflict-date"><h4>'+escapeHtml(item.plan_date)+'</h4>'+choice('phone')+choice('account')+'</section>';
      }).join('');
      save.disabled=conflict.conflicts.some(item=>!item.choice);
      days.querySelectorAll('[data-week-conflict-choice]').forEach(input=>input.addEventListener('change',event=>{
        controller.chooseDay(event.currentTarget.dataset.weekConflictDate,event.currentTarget.dataset.weekConflictChoice);
        save.disabled=controller.conflict().conflicts.some(item=>!item.choice);
      }));
    }
    return perDay?days.querySelector('[data-week-conflict-choice]'):qs('#keep-phone-week');
  }
  function renderDates() {
    const root=qs('#week-plan-dates');
    root.hidden=!selectedDate;
    renderPass();
    if(!selectedDate)return;
    root.innerHTML=controller.dates().map(item=>'<button type="button" data-week-plan-date="'+
      escapeAttribute(item.date)+'"'+(item.date===selectedDate?' aria-current="date"':'')+'><strong>'+escapeHtml(item.label)+
      '</strong><small>'+escapeHtml(controller.day(item.date).ids.length+' planned')+'</small></button>').join('');
    root.querySelectorAll('[data-week-plan-date]').forEach(button=>button.addEventListener('click',()=>{
      selectedDate=button.dataset.weekPlanDate;renderDates();openPlanner(null,false);
    }));
  }
  function closeReflow() {
    const panel=qs('#week-reflow-review');if(panel)panel.hidden=true;
    const apply=qs('#apply-week-reflow');if(apply)apply.disabled=false;
    return true;
  }
  function openReflow() {
    const preview=controller.previewReflow?.();
    if(!preview)return false;
    const labels=new Map(controller.dates().map(item=>[item.date,item.label]));
    const panel=qs('#week-reflow-review'),days=qs('#week-reflow-days'),unscheduled=qs('#week-reflow-unscheduled');
    panel.hidden=false;
    if(preview.blockers.length){
      days.innerHTML='';
      const blocker=preview.blockers[0],item=getItem(blocker.id)||controller.item?.(blocker.id);
      unscheduled.textContent='Add an estimate for '+(item?.title||blocker.id)+' before reflowing.';
      qs('#apply-week-reflow').disabled=true;
      panel.focus?.();return preview;
    }
    days.innerHTML=preview.days.filter(day=>day.ids.length).map(day=>{
      const minutes=day.ids.reduce((total,id)=>total+Number(day.estimates?.[id]||0),0);
      return '<div class="week-reflow-day"><strong>'+escapeHtml(labels.get(day.plan_date)||day.plan_date)+
        '</strong><span>'+escapeHtml(day.ids.length+' item'+(day.ids.length===1?'':'s')+' · '+minutes+' of '+Number(day.capacity_minutes||0)+' min')+'</span></div>';
    }).join('')||'<p class="small muted">No work can be scheduled with the current capacities.</p>';
    if(preview.unscheduled.length){
      const names=preview.unscheduled.map(value=>(getItem(value.id)||controller.item?.(value.id))?.title||value.id);
      unscheduled.textContent=names.length+' item'+(names.length===1?'':'s')+' cannot fit: '+names.join(', ')+'. '+
        (names.length===1?'It':'They')+' will remain in My Work.';
    } else unscheduled.textContent='All planned work fits within capacity.';
    panel.focus?.();return preview;
  }
  async function applyReflow() {
    const button=qs('#apply-week-reflow');button.disabled=true;
    const staged=controller.applyReflow?.();
    if(!staged){button.disabled=false;return false;}
    closeReflow();renderReview();
    try {
      await controller.flush();renderReview();
      qs('#mobile-week-summary').textContent=controller.summary();
      qs('#week-review-status').textContent='Week Ahead reflowed and saved.';
      return true;
    } catch(error) {
      renderReview();
      qs('#week-review-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Reflow remains saved on this phone.';
      return false;
    }
  }
  function dismissUnplanReceipt({focus=false}={}) {
    if(unplanTimer){clearTimer?.(unplanTimer);unplanTimer=null;}
    unplanReceipt=null;
    const receipt=qs('#week-unplan-receipt'),undo=qs('#undo-week-unplan');
    if(receipt)receipt.hidden=true;
    if(undo)undo.hidden=true;
    if(focus)qs('#edit-week-plan')?.focus?.();
  }
  function showUnplanReceipt(title) {
    const receipt=qs('#week-unplan-receipt'),undo=qs('#undo-week-unplan');
    if(receipt){receipt.textContent=title+' removed from Week Ahead.';receipt.hidden=false;}
    if(undo){undo.hidden=false;undo.focus?.();}
    if(unplanTimer)clearTimer?.(unplanTimer);
    unplanTimer=setTimer?.(()=>dismissUnplanReceipt({focus:true}),10000)||null;
  }
  async function undoUnplan() {
    const receipt=unplanReceipt;
    if(!receipt||!controller.restore?.(receipt))return false;
    dismissUnplanReceipt();renderReview();renderPass();
    try{
      await controller.flush();
      renderReview();
      qs('#week-review-status').textContent='Restored to Week Ahead.';
    }catch(error){
      qs('#week-review-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Restore remains saved on this phone.';
    }
    qs('#week-review-days').querySelector?.(`[data-week-unplan="${receipt.id}"]`)?.focus?.();
    return true;
  }
  qs('#undo-week-unplan')?.addEventListener('click',undoUnplan);
  function renderReview() {
    const value=controller.review(),root=qs('#week-review-days'),duplicates=qs('#week-review-duplicates');
    const readOnly=Boolean(controller.offline?.());
    const pending=Boolean(controller.pending?.());
    const nextUp=overviewing?value.days.find(day=>day.ids.length):null;
    const today=getTodayPlan?.();
    const canStartEarly=Boolean(nextUp&&!readOnly&&!pending&&Number.isInteger(today?.revision)&&!today.ids?.length);
    const destinations=value.days.map(day=>'<option value="'+escapeAttribute(day.plan_date)+'">'+escapeHtml(day.label)+'</option>').join('');
    const itemMarkup=(id,day)=>{
      const item=getItem(id)||controller.item?.(id),estimate=Number(day.estimates?.[id])||0;
      if(!item)return '<div class="week-review-item-copy"><strong>Work details unavailable</strong><small>'+escapeHtml(String(id).slice(0,96))+'</small></div>';
      const kind=item.kind==='pull'?'Pull request':'Issue';
      const reference=item.repository&&item.number!=null?item.repository+' #'+item.number:kind;
      const copy='<span class="week-review-item-copy"><strong>'+escapeHtml(item.title||'Untitled work')+'</strong><small>'+escapeHtml(reference+' · '+kind+(estimate?' · '+estimate+' min':''))+'</small></span>';
      const actionable=overviewing&&['issue','pull'].includes(item.kind)&&item.repository&&item.number>0;
      return actionable?'<button type="button" class="week-review-item-open" data-week-open-item="'+escapeAttribute(id)+'">'+copy+'</button>':copy;
    };
    root.innerHTML=value.days.map(day=>{
      const capacity=Number(day.capacity_minutes)||0;
      const source=day.capacity_source==='default'?' · Default':(day.capacity_source==='override'?' · Override':'');
      const load=day.planned_minutes+(capacity?' of '+capacity:'')+' min'+source+(day.overloaded?' · over capacity':'');
      const move=id=>overviewing?'':'<div class="week-review-move"><label>Move to <select data-week-move-destination="'+escapeAttribute(id)+'">'+destinations+
        '</select></label><button type="button" data-week-move="'+escapeAttribute(id)+'">Move</button></div>';
      const unplan=id=>overviewing&&!readOnly?'<button type="button" class="week-review-unplan" data-week-unplan="'+escapeAttribute(id)+'">Remove from week</button>':'';
      const pull=id=>{
        const inToday=(today?.ids||[]).includes(id),full=(today?.ids||[]).length>=5;
        if(!overviewing||readOnly||pending||!Number.isInteger(today?.revision)||!(today?.ids||[]).length||inToday)return '';
        const todayMinutes=(today.ids||[]).reduce((total,itemId)=>total+(Number(today.estimates?.[itemId])||0),0);
        const projected=todayMinutes+(Number(day.estimates?.[id])||0),capacity=Number(today.capacity_minutes)||0;
        const overload=capacity&&projected>capacity;
        return '<button type="button" class="week-review-pull" data-week-pull-item="'+escapeAttribute(id)+'"'+(full?' disabled':'')+'>'+
          (full?'Today is full':(overload?'Review Today · '+projected+' of '+capacity+' min':'Add to Today'))+'</button>';
      };
      const items=day.ids.length?'<ul>'+day.ids.map(id=>'<li>'+itemMarkup(id,day)+move(id)+pull(id)+unplan(id)+'</li>').join('')+'</ul>':
        '<p class="small muted">Nothing planned.</p>';
      const edit=readOnly?'':'<button type="button" data-week-edit-day="'+escapeAttribute(day.plan_date)+'" aria-label="Edit '+escapeAttribute(day.label)+'">Edit day</button>';
      const start=day===nextUp&&canStartEarly?'<button type="button" class="week-start-early" data-week-start-early="'+escapeAttribute(day.plan_date)+'">Start this day early</button>':'';
      return '<article class="week-review-day'+(day.overloaded?' is-overloaded':'')+(day===nextUp?' is-next-up':'')+'"><header><h3>'+escapeHtml(day.label)+(day===nextUp?' <span class="week-next-up">Next up</span>':'')+'</h3>'+edit+'</header><p class="week-review-load">'+
        escapeHtml(load)+'</p>'+items+start+'</article>';
    }).join('');
    duplicates.hidden=!value.duplicates.length;
    duplicates.innerHTML=value.duplicates.length?'<strong>Choose one date for duplicated work before confirming.</strong><ul>'+value.duplicates.map(item=>
      '<li><code>'+escapeHtml(item.id)+'</code> · '+escapeHtml(item.dates.join(', '))+'</li>').join('')+'</ul>':'';
    const confirm=qs('#confirm-week-plan');
    confirm.disabled=!value.can_confirm||pending;
    const blocker=value.blockers?.[0];
    const blockerMessage=blocker?(
      blocker.type==='over-capacity'?blocker.plan_date+' is '+blocker.minutes+' min over capacity. Edit that day before confirming.':
        blocker.type==='missing-estimate'?'Add an estimate for work on '+blocker.plan_date+' before confirming.':
          'Add capacity for '+blocker.plan_date+' before confirming.'
    ):'';
    qs('#week-review-status').textContent=readOnly?'Offline snapshot · viewing only. Retry for live editing.':(overviewing?(pending?'Week Ahead overview · sync pending.':
      (blockerMessage||'Week Ahead overview · no changes made.')):(value.duplicates.length?'Duplicate work must be moved to one date.':
      (pending?'Saving the latest week to your account…':blocker?(
        blockerMessage
      ):'Week Ahead is balanced and ready.')));
    confirm.hidden=overviewing||readOnly;
    const exportSaved=qs('#export-saved-week-calendar');
    if(exportSaved){
      exportSaved.hidden=!overviewing||!value.days.some(day=>day.ids.length);
      exportSaved.disabled=pending||!value.can_confirm;
    }
    const reflow=qs('#open-week-reflow');if(reflow)reflow.hidden=!overviewing||readOnly||pending||!value.days.some(day=>day.overloaded);
    const editWeek=qs('#edit-week-plan');if(editWeek){editWeek.hidden=!overviewing||readOnly;editWeek.textContent=nextUp?'Edit week':'Plan Week Ahead';}
    root.querySelectorAll('[data-week-move]').forEach(button=>button.addEventListener('click',()=>{
      const selector=root.querySelector(`[data-week-move-destination="${button.dataset.weekMove}"]`);
      if(!controller.move(button.dataset.weekMove,selector?.value))return;
      renderReview();renderPass();
      controller.flush().then(()=>{renderReview();qs('#mobile-week-summary').textContent=controller.summary();})
        .catch(error=>{qs('#week-review-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Changes remain on this phone.';});
    }));
    root.querySelectorAll('[data-week-unplan]').forEach(button=>button.addEventListener('click',async event=>{
      const id=event.currentTarget.dataset.weekUnplan,item=getItem(id)||controller.item?.(id);
      const receipt=controller.unplan?.(id);
      if(!receipt)return;
      unplanReceipt=receipt;
      renderReview();renderPass();showUnplanReceipt(String(item?.title||'Work'));
      try{
        await controller.flush();
        renderReview();
        qs('#mobile-week-summary').textContent=controller.summary();
      }catch(error){
        qs('#week-review-status').textContent=(error.message||'Week Ahead sync is unavailable.')+' Removal remains saved on this phone.';
      }
    }));
    root.querySelectorAll('[data-week-pull-item]').forEach(button=>button.addEventListener('click',async event=>{
      const id=event.currentTarget.dataset.weekPullItem,current=getTodayPlan?.();
      if(!id||!Number.isInteger(current?.revision)||!(current.ids||[]).length||current.ids.length>=5)return;
      const source=value.days.find(day=>day.ids.includes(id)),estimate=Number(source?.estimates?.[id])||0;
      const todayMinutes=current.ids.reduce((total,itemId)=>total+(Number(current.estimates?.[itemId])||0),0);
      const projected=todayMinutes+estimate,capacity=Number(current.capacity_minutes)||0;
      const overCapacity=Boolean(capacity&&projected>capacity),item=getItem(id)||controller.item?.(id);
      if(overCapacity&&!confirmEarly('Add '+String(item?.title||'work')+' to Today?\n\nToday will be '+projected+' of '+capacity+' min · '+(projected-capacity)+' min over capacity.'))return;
      event.currentTarget.disabled=true;
      try{
        const result=await controller.pullItem?.(id,current,operationId(),overCapacity);
        if(!result)return;
        getTodayPlan?.(result.today);todayWork.replace(result.today.ids);
        todayWork.replacePlanning({capacity_minutes:result.today.capacity_minutes??null,estimates:result.today.estimates||{}});
        refresh();warm();renderReview();
        qs('#mobile-week-summary').textContent=controller.summary();
        qs('#week-review-status').textContent=result.sync_pending?String(item?.title||'Work')+' added to Today · sync pending.':
          String(item?.title||'Work')+' added to Today.';
      }catch(error){
        renderReview();
        qs('#week-review-status').textContent=error?.code==='today_full'?'Today is full. Finish or move work before adding more.':
          ((error.message||'Today or Week Ahead changed.')+' Reopen Week Ahead and retry.');
      }
    }));
    root.querySelectorAll('[data-week-open-item]').forEach(button=>button.addEventListener('click',event=>{
      const id=button.dataset.weekOpenItem,item=getItem(id)||controller.item?.(id);
      if(item)openItem?.(item,event.currentTarget);
    }));
    root.querySelectorAll('[data-week-start-early]').forEach(button=>button.addEventListener('click',async event=>{
      const day=value.days.find(item=>item.plan_date===event.currentTarget.dataset.weekStartEarly);
      const current=getTodayPlan?.();
      if(!day||!Number.isInteger(current?.revision)||current.ids?.length)return;
      const capacity=Number(day.capacity_minutes)||0;
      const detail=day.ids.length+' item'+(day.ids.length===1?'':'s')+' · '+day.planned_minutes+(capacity?' of '+capacity:'')+' min';
      if(!confirmEarly('Start '+day.label+' early?\n\n'+detail+' will move into Today.'))return;
      event.currentTarget.disabled=true;
      try{
        const promoted=await controller.startEarly(day.plan_date,current.revision);
        if(!promoted)return;
        todayWork.replace(promoted.ids);todayWork.replacePlanning(promoted);refresh();warm();
        qs('#mobile-week-summary').textContent=controller.summary();
        qs('#my-work-action-status').textContent=day.label+' is now Today.';
        finish();
      }catch(error){
        qs('#my-work-action-status').textContent=(error.message||'Week Ahead changed before it could start.')+' Reopen Week Ahead and review.';
        event.currentTarget.disabled=false;
      }
    }));
    root.querySelectorAll('[data-week-edit-day]').forEach(button=>button.addEventListener('click',()=>editDay(button.dataset.weekEditDay)));
    renderPass();
  }
  function editDay(planDate) {
    if(!controller.dates().some(item=>item.date===planDate))return false;
    reviewing=false;editingFromReview=true;selectedDate=planDate;setReviewMode(false);
    qs('#week-review').hidden=true;
    const back=qs('#back-to-week-review');if(back)back.hidden=false;
    renderDates();openPlanner(null,false);
    qs('#week-plan-progress')?.focus?.();
    return true;
  }
  function editWeek() {
    overviewing=false;reviewing=false;selectedDate=controller.dates()[0].date;setReviewMode(false);
    qs('#week-review').hidden=true;qs('#confirm-week-plan').hidden=false;
    const edit=qs('#edit-week-plan');if(edit)edit.hidden=true;
    renderDates();openPlanner(null,false);qs('#week-plan-progress')?.focus?.();return true;
  }
  function returnToReview() {
    if(!editingFromReview)return false;
    const editedDate=selectedDate;
    editingFromReview=false;reviewing=true;selectedDate=null;setReviewMode(true);
    qs('#week-review').hidden=false;
    const back=qs('#back-to-week-review');if(back)back.hidden=true;
    renderDates();renderReview();openPlanner(null,false);
    qs('#week-review-days').querySelector(`[data-week-edit-day="${editedDate}"]`)?.focus?.();
    return true;
  }
  async function open(trigger) {
    if(trigger)trigger.disabled=true;reviewing=false;overviewing=false;editingFromReview=false;setReviewMode(false);const reviewRoot=qs('#week-review');if(reviewRoot)reviewRoot.hidden=true;
    const back=qs('#back-to-week-review');if(back)back.hidden=true;
    selectedDate=null;renderDates();openPlanner(trigger);
    qs('#mobile-week-summary').textContent='Loading Week Ahead…';
    try{
      loadState=await controller.load();
      if(!loadState.offline_snapshot&&controller.rememberItems){
        const items={};
        (loadState.days||controller.state?.().days||[]).flatMap(day=>day.ids||[]).forEach(id=>{const value=getItem(id);if(value)items[id]=value;});
        controller.rememberItems(items);
      }
      renderOfflineState();qs('#mobile-week-summary').textContent=controller.summary();overviewing=true;reviewing=true;
      setReviewMode(true);reviewRoot.hidden=false;renderDates();renderReview();openPlanner(null,false);return true;}
    catch(error){renderOfflineState(null);qs('#mobile-week-summary').textContent='Unavailable · tap to retry';qs('#my-work-action-status').textContent=(error.message||'Week Ahead is unavailable.')+' Retry when connected.';return false;}
    finally{if(trigger)trigger.disabled=false;}
  }
  function save(plan){
    const date=selectedDate,normalized=Array.isArray(plan)?{ids:plan,capacity_minutes:null,estimates:{}}:plan;
    if(reconciliation){
      const preserved=reconciliation;
      controller.reconcile(preserved,normalized).then(result=>{
        reconciliation=null;blockedReviewOpen=false;
        todayWork.replace(result.today.ids);todayWork.replacePlanning(result.today);
        refresh();warm();qs('#mobile-week-summary').textContent=controller.summary();
        qs('#my-work-action-status').textContent='Today started from unfinished and Week Ahead work.';
      }).catch(error=>{
        qs('#my-work-action-status').textContent=(error.message||'Plans changed during review.')+' Reopen Start today’s plan.';
      });
      return true;
    }
    const staged=controller.stageDay(date,normalized);
    if(!staged){qs('#my-work-action-status').textContent='Week Ahead could not be saved on this phone. Free browser storage and retry.';return false;}
    qs('#mobile-week-summary').textContent=controller.summary();
    qs('#my-work-action-status').textContent='Week Ahead saved on this phone · sync pending.';
    controller.flush().then(()=>{qs('#mobile-week-summary').textContent=controller.summary();qs('#my-work-action-status').textContent=(normalized.ids.length?'Week Ahead saved for ':'Week Ahead cleared for ')+date+'.';if(reviewing)renderReview();})
      .catch(error=>{
        const conflict=controller.conflict();
        qs('#mobile-week-summary').textContent=conflict?'Conflict · review required':controller.summary();
        qs('#my-work-action-status').textContent=conflict?'Another device changed Week Ahead. Both versions are preserved; choose each changed day.':
          (error.message||'Week Ahead sync is unavailable.')+' Saved on this phone · sync pending.';
        if(conflict) openPlanner(null,false);
      });
    return true;
  }
  function advance() {
    if(editingFromReview)return returnToReview();
    const current=selectedDate&&controller.pass(selectedDate);
    if(current?.last){
      reviewing=true;selectedDate=null;setReviewMode(true);qs('#week-review').hidden=false;
      renderDates();renderReview();openPlanner(null,false);qs('#confirm-week-plan').focus?.();return true;
    }
    if(!current?.next_date) return false;
    selectedDate=current.next_date;renderDates();openPlanner(null,false);
    const selected=qs('#week-plan-dates').querySelector(`[data-week-plan-date="${selectedDate}"]`);
    selected?.scrollIntoView({block:'nearest',inline:'center'});selected?.focus?.();
    return true;
  }
  function confirm() {
    const value=controller.review();
    if(!reviewing||!value.can_confirm||controller.pending()) return false;
    return true;
  }
  async function retire(item) {
    const staged=controller.retire?.(todayWork.identity({...item,kind:'issue'}));
    if(!staged)return false;
    const title=String(item?.title||'Completed work');
    const refreshOverview=message=>{
      if(overviewing||reviewing)renderReview();
      qs('#week-review-status').textContent=message;
      qs('#edit-week-plan')?.focus?.();
    };
    try {
      await controller.flush();
      refreshOverview(title+' removed and Week Ahead saved.');
      return 'saved';
    } catch(_error) {
      refreshOverview(title+' removed · sync pending.');
      return 'pending';
    }
  }
  function adoptPull(value,message) {
    if(!value?.today)return false;
    t(value.today);todayWork.replace(value.today.ids);
    todayWork.replacePlanning({capacity_minutes:value.today.capacity_minutes??null,estimates:value.today.estimates||{}});
    refresh();warm();
    qs('#mobile-week-summary').textContent=controller.summary();
    qs('#my-work-action-status').textContent=message;
    return value;
  }
  function renderPullConflict({open=false}={}) {
    const detail=controller.pullConflict?.(),root=qs('#week-pull-conflict-review');
    if(!root)return false;
    root.hidden=!detail;
    if(!detail)return false;
    const item=qs('#week-pull-conflict-item'),context=qs('#week-pull-conflict-context');
    if(item)item.textContent=detail.title;
    if(context)context.textContent=(detail.source_date?'Planned for '+detail.source_date+' · ':'')+
      'Today has '+detail.today_items+' item'+(detail.today_items===1?'':'s')+' · '+detail.today_minutes+
      (detail.today_capacity?' of '+detail.today_capacity:'')+' min'+(detail.queue_length>1?' · '+detail.queue_length+' saved transfers waiting':'')+'.';
    if(open){openPlanner(null,false);const title=qs('#plan-today-title');if(title)title.textContent='Resolve Today transfer';}
    root.focus?.();
    return detail;
  }
  async function resolvePullConflict(action) {
    const status=qs('#week-pull-conflict-status'),add=qs('#rebase-week-pull'),keep=qs('#keep-week-pull');
    if(add)add.disabled=true;if(keep)keep.disabled=true;
    if(status)status.textContent=action==='add'?'Checking current plans…':'Keeping this work in Week Ahead…';
    try{
      const result=await controller.resolvePullConflict?.(action);
      if(!result)return false;
      adoptPull(result,action==='add'?'Saved transfer added using current plans.':'Work kept in Week Ahead.');
      if(controller.pullConflict?.())return renderPullConflict();
      const root=qs('#week-pull-conflict-review');if(root)root.hidden=true;
      overviewing=true;reviewing=true;setReviewMode(true);
      const reviewRoot=qs('#week-review');if(reviewRoot){reviewRoot.hidden=false;renderReview();}
      return result;
    }catch(error){
      if(status)status.textContent=error?.code==='today_full'?'Today is full. Finish or move work, then retry.':
        (error?.code==='today_over_capacity'?'Today would exceed its current capacity. Reopen Week Ahead and review Today first.':
          (error.message||'Current plans could not be checked.')+' Your saved transfer is still waiting.');
      return false;
    }finally{if(add)add.disabled=false;if(keep)keep.disabled=false;}
  }
  qs('#rebase-week-pull')?.addEventListener('click',()=>controller.resolvePullConflict&&resolvePullConflict('add'));
  qs('#keep-week-pull')?.addEventListener('click',()=>controller.resolvePullConflict&&resolvePullConflict('keep'));
  function resumePull() {
    const restored=controller.resumePull?.();
    if(!restored)return false;
    const adopted=adoptPull(restored,restored.conflict?'Today or Week Ahead changed. Choose how to finish the saved transfer.':
      'Week-to-Today pull saved on this phone · sync pending.');
    if(restored.conflict)renderPullConflict({open:true});
    return adopted;
  }
  async function flushPull() {
    const pendingPull=controller.pendingPull?.();
    if(!pendingPull)return false;
    if(pendingPull.conflict){
      const restored=adoptPull(pendingPull.current,'Today or Week Ahead changed on another device. Choose how to finish the saved transfer.');
      renderPullConflict({open:true});return restored;
    }
    try {
      const result=await controller.flushPull();
      const adopted=adoptPull(result,result.conflict?'Today or Week Ahead changed on another device. Choose how to finish the saved transfer.':
        'Week Ahead work added to Today.');
      if(result.conflict)renderPullConflict({open:true});
      return adopted;
    } catch(error) {
      qs('#my-work-action-status').textContent=(error.message||'Week-to-Today sync is unavailable.')+' Saved on this phone · sync pending.';
      return false;
    }
  }
  function finish(){reviewing=false;overviewing=false;setReviewMode(false);closeReflow();qs('#week-review').hidden=true;return true;}
  qs('#back-to-week-review')?.addEventListener('click',returnToReview);
  qs('#edit-week-plan')?.addEventListener('click',editWeek);
  qs('#open-week-reflow')?.addEventListener('click',openReflow);
  qs('#cancel-week-reflow')?.addEventListener('click',closeReflow);
  qs('#apply-week-reflow')?.addEventListener('click',applyReflow);
  qs('#retry-week-live')?.addEventListener('click',event=>open(event.currentTarget));
  async function promote(plan){
    try{await controller.load();const promoted=await controller.promote(plan.revision);if(!promoted)return false;blockedReviewOpen=false;todayWork.replace(promoted.ids);todayWork.replacePlanning({capacity_minutes:promoted.capacity_minutes??null,estimates:promoted.estimates||{}});refresh();warm();qs('#mobile-week-summary').textContent=controller.summary();qs('#my-work-action-status').textContent='Your saved Week Ahead plan is now Today.';return true;}
    catch(error){
      if(error?.code==='week_today_in_progress'){
        reconciliation={today:plan,week:controller.state()};
        qs('#my-work-action-status').textContent='Review unfinished Today with the due Week Ahead work before starting.';
        if(!blockedReviewOpen){blockedReviewOpen=true;openPlanner(null,false);}
        return false;
      }
      blockedReviewOpen=false;
      qs('#my-work-action-status').textContent=(error.message||'Week Ahead needs review before promotion.')+' Open Week Ahead to review.';return false;
    }
  }
  const workflow={open,save,advance,confirm,finish,promote,retire,resumePull,flushPull,editWeek,renderDates,renderReview,renderConflict,active:()=>Boolean(selectedDate)||reviewing||Boolean(reconciliation),
    i:id=>getItem(id)||controller.item?.(id),
    reviewing:()=>reviewing,overviewing:()=>overviewing,selectedDate:()=>selectedDate,
    day:()=>reconciliationDay()||(selectedDate?controller.day(selectedDate):(overviewing?{ids:[]}:null)),
    copy:()=>reconciliation?{title:"Start today's plan",heading:'Unfinished Today + due Week Ahead',available:'Available today',build:'Build combined Today'}:
      (overviewing?{title:'Week Ahead',heading:'Seven-day overview',available:'Planned work',build:'Edit week'}:
        (selectedDate?{title:'Plan Week Ahead',heading:selectedDate+', in order',available:'Available this day',build:'Build this day'}:null)),
    clear(){selectedDate=null;reviewing=false;overviewing=false;editingFromReview=false;reconciliation=null;setReviewMode(false);qs('#week-review').hidden=true;
      const back=qs('#back-to-week-review');if(back)back.hidden=true;renderDates();}};
  globalThis.addEventListener?.('stackchain:issue-closed',event=>retire(event.detail));
  if(typeof weekCalendarImport!=='undefined')weekCalendarImport.mount(controller,workflow,qs);
  if(x)mountTodayWeekReschedule({
    qs,week:controller,getToday:t,refresh:r,warm:w,api:controller.request,currentTarget:x,...controller.reschedule(),
    closeActions:()=>qs('#mobile-today-actions').close(),
    continueToday:()=>qs('[data-work-session-next]').click(),
    announce:message=>{qs('#my-work-action-status').textContent=message;},
    adoptToday:value=>{
      t(value);todayWork.replace(value.ids);
      todayWork.replacePlanning({capacity_minutes:value.capacity_minutes??null,estimates:value.estimates||{}});
    },
  });
  return workflow;
}
if(typeof module!=='undefined'&&module.exports){
  module.exports=createWeekPlan;
  module.exports.Workflow=createWeekPlanWorkflow;
}
