"""Roda o JavaScript do copiloto da Ana Carolina num DOM falso e exercita telas e ações."""
import re
import sys

import quickjs

html = open(sys.argv[1], encoding='utf-8').read()
script = re.findall(r'<script>(.*?)</script>', html, re.S)[0]
fim = script.rfind('})();')
exporta = ('\nglobalThis.__t = { get estado() { return estado; }, ACOES, render, definirModo, temFoto, restam, paraEntregar, '
           'resumoDoDia, PAC, painelAntes, painelFotos, registrarFotosFeitas, URL_ESTACAO, painelFicha, renderHoje, renderFotos, renderAgenda, tituloDia, daData, '
           'visitasDe, chave, deChave, ajustarDia, diaEscolhido, HOJE, ULTIMO, horariosDoDia, padraoRetorno, LEMBRETE_SESSAO, '
           'get modo() { return modo; }, get emPe() { return emPe; } };\n')
script = script[:fim] + exporta + script[fim:]

SHIM = r'''
var __DataReal = globalThis.Date;
var __AGORA = new __DataReal(2026, 9, 6, 9, 20, 0).getTime();
globalThis.Date = function Date(a, b, c, d, e, f, g) {
  if (!(this instanceof Date)) return new __DataReal(__AGORA).toString();
  if (arguments.length === 0) return new __DataReal(__AGORA);
  if (arguments.length === 1) return new __DataReal(a);
  return new __DataReal(a, b, c === undefined ? 1 : c, d || 0, e || 0, f || 0, g || 0);
};
globalThis.Date.now = function () { return __AGORA; };
globalThis.Date.UTC = __DataReal.UTC;
globalThis.Date.parse = __DataReal.parse;
globalThis.Date.prototype = __DataReal.prototype;
var __handlers = {};
var __timers = [];
function setTimeout(fn) { __timers.push(fn); return __timers.length; }
function clearTimeout() {}
function __flush() { var n = 0; while (__timers.length && n < 200) { __timers.shift()(); n++; } }
function ListaClasses() { this.s = new Set(); }
ListaClasses.prototype.add = function (c) { this.s.add(c); };
ListaClasses.prototype.remove = function (c) { this.s.delete(c); };
ListaClasses.prototype.toggle = function (c, f) { if (f === undefined) f = !this.s.has(c); if (f) this.s.add(c); else this.s.delete(c); return f; };
ListaClasses.prototype.contains = function (c) { return this.s.has(c); };
function El(id) { this.id = id; this.classList = new ListaClasses(); this.innerHTML = ''; this.textContent = ''; this.hidden = false; this.dataset = {}; this.style = {}; this.attrs = {}; this.scrollTop = 0; this.scrollHeight = 0; this.value = ''; }
Object.defineProperty(El.prototype, 'className', { get: function () { return Array.from(this.classList.s).join(' '); }, set: function (v) { this.classList.s = new Set(String(v).split(/\s+/).filter(Boolean)); } });
Object.defineProperty(El.prototype, 'lastElementChild', { get: function () { return new El('ultimo'); } });
El.prototype.setAttribute = function (k, v) { this.attrs[k] = String(v); };
El.prototype.getAttribute = function (k) { return k in this.attrs ? this.attrs[k] : null; };
El.prototype.removeAttribute = function (k) { delete this.attrs[k]; };
El.prototype.hasAttribute = function (k) { return k in this.attrs; };
El.prototype.querySelector = function () { return null; };
El.prototype.querySelectorAll = function () { return []; };
El.prototype.closest = function () { return null; };
El.prototype.focus = function () {};
El.prototype.scrollIntoView = function () {};
El.prototype.insertAdjacentHTML = function (pos, h) { this.innerHTML += h; };
El.prototype.getBoundingClientRect = function () { return { top: 0, left: 0, width: 0, height: 0 }; };
El.prototype.appendChild = function (c) { return c; };
El.prototype.remove = function () {};
El.prototype.setSelectionRange = function () {};
var __els = {};
function getEl(id) { if (!__els[id]) __els[id] = new El(id); return __els[id]; }
var CONHECIDOS = ['tela', 'horaTopo', 'horaAparelho', 'painel', 'veu', 'toast', 'botaoTema', 'telaApp', 'botaoGirar', 'textoPrevia'];
['painel', 'veu', 'toast'].forEach(function (id) { getEl(id).hidden = true; });
var document = {
  documentElement: new El('html'),
  body: new El('body'),
  activeElement: null,
  querySelector: function (s) {
    var m = /^#([\w-]+)$/.exec(s);
    if (!m) return null;
    var id = m[1];
    if (CONHECIDOS.indexOf(id) >= 0) return getEl(id);
    var p = __els.painel;
    if (id === 'chatDigitando') {
      var cm = __els.chatMensagens;
      if (!cm || cm.innerHTML.indexOf('id="chatDigitando"') < 0) return null;
      var d = new El('chatDigitando');
      d.remove = function () { cm.innerHTML = cm.innerHTML.replace(/<div class="msg msg--ia" id="chatDigitando">[\s\S]*?<\/div><\/div>/, ''); };
      return d;
    }
    if (!p.hidden && p.innerHTML.indexOf('id="' + id + '"') >= 0) return getEl(id);
    if (__els.tela && __els.tela.innerHTML.indexOf('id="' + id + '"') >= 0) return getEl(id);
    return null;
  },
  querySelectorAll: function () { return []; },
  addEventListener: function (t, fn) { (__handlers[t] = __handlers[t] || []).push(fn); },
  contains: function () { return true; },
  createElement: function () { return new El('novo'); },
};
var window = globalThis;
window.innerWidth = 1440;
window.innerHeight = 900;
window.matchMedia = function (q) { return { matches: /min-width: (1024|760)px|pointer: fine/.test(q) }; };
window.scrollTo = function () {};
window.addEventListener = function (t, fn) { (__handlers[t] = __handlers[t] || []).push(fn); };
var location = { _h: '', get hash() { return this._h; }, set hash(v) { v = String(v); this._h = v ? (v[0] === '#' ? v : '#' + v) : ''; (__handlers.hashchange || []).forEach(function (fn) { fn(); }); } };
var localStorage = { getItem: function () { return null; }, setItem: function () {} };
var navigator = { clipboard: { writeText: function () { return Promise.resolve(); } } };
'''

TESTES = r'''
var __out = [];
var T = globalThis.__t;
function run(nome, fn) { try { fn(); __flush(); __out.push('ok    ' + nome); } catch (err) { __out.push('FALHA ' + nome + ': ' + err + ' | ' + String((err && err.stack) || '').split('\n').slice(0, 3).join(' / ')); } }
function tem(trecho, onde) { var h = onde === undefined ? __els.tela.innerHTML : onde; if (h.indexOf(trecho) < 0) throw new Error('não achei: ' + trecho); }
function naoTem(trecho, onde) { var h = onde === undefined ? __els.tela.innerHTML : onde; if (h.indexOf(trecho) >= 0) throw new Error('não devia aparecer: ' + trecho); }
function painel() { return __els.painel.innerHTML; }
function toastTxt() { var a = __els.painelAviso; return (a && !__els.painel.hidden ? a.innerHTML + ' ' : '') + __els.toast.innerHTML; }
function igual(a, b, msg) { if (a !== b) throw new Error((msg || 'valor') + ': esperava ' + b + ', veio ' + a); }
function botao(acao, dados, ctx) {
  var e = new El('botao');
  e.dataset = Object.assign({ acao: acao }, dados || {});
  e.closest = function (s) { if (s === '[data-contexto]') return ctx || null; return null; };
  return e;
}
function ir(h) { location.hash = h; }
function P(id) { return T.PAC(id); }
function logCom(trecho) { return T.estado.log.filter(function (l) { return l[1].indexOf(trecho) >= 0; }); }
function vezes(trecho, onde) { return onde.split(trecho).length - 1; }
function diasDepois(n) { var d = new Date(T.HOJE); d.setDate(d.getDate() + n); return d; }
function entreDatas(a, b) { return Math.round((T.deChave(b) - T.deChave(a)) / 86400000); }
function gerados() { return T.estado.agenda.filter(function (x) { return x.gerado; }); }
// O Hoje em grupos (Luiz, 13/10/2026): abre um grupo; com '', volta à página do Hoje.
function grupo(g) { T.ACOES['abrir-grupo'](botao('abrir-grupo', { gid: g })); }
function aba(a) { T.ACOES['aba-lado'](botao('aba-lado', { aba: a })); }
// A contagem que a linha do grupo mostra no Hoje.
function contaDaLinha(g) { var m = new RegExp('data-gid="' + g + '"><span class="lg-qtd num" title="(\\d+) [^"]*">(\\d+)</span>').exec(__els.tela.innerHTML); if (!m) throw new Error('sem a linha do grupo ' + g); return Number(m[2]); }
var GRUPOS = ['atencao', 'antes', 'fotos', 'homecare', 'terminei'];
var NOMES = { atencao: 'Pede atenção', antes: 'Antes da sessão', fotos: 'Fotos de hoje', homecare: 'Home care para entregar', terminei: 'Terminei: passar para a Fer' };

run('abre em Hoje com o resumo do copiloto e os cinco números do dia', function () {
  tem('Bom dia, Ana Carolina.'); tem('Atualizado às 09:20');
  tem('Hoje são <strong>8 sessões</strong>: 1 já terminou e 6 estão por vir.');
  tem('O Thiago Bastos não chegou às 9h, e a Fer está confirmando.');
  tem('<strong>2 pacientes têm fotos hoje</strong>: do Caio, na 3ª sessão e do Otávio, na 1ª sessão.');
  tem('<strong>Home care para entregar</strong>: Gabriela às 16h, que a Fer separa e passa para você.');
  tem('fim de ciclo do Roberto</strong>, com as fotos da 6ª sessão.');
  tem('Ver tudo o que o copiloto fez');
  // Os cinco números do dia, cada um levando para onde se resolve.
  tem('Pacientes de hoje'); tem('<strong class="dia-kpi-v num">7</strong><span>a chegar</span>'); tem('1 já atendido · 8 na agenda');
  tem('Fotos de hoje'); tem('<strong class="dia-kpi-v num">0</strong><span>de 18 tomadas</span>'); tem('3 pacientes: Caio, Otávio e Roberto');
  tem('Home care para entregar'); tem('<strong class="dia-kpi-v num">1</strong><span>paciente</span>'); tem('Gabriela às 16h</span>');
  tem('Passadas para a Fer'); tem('<strong class="dia-kpi-v num">1</strong><span>de 8 sessões</span>'); tem('Ricardo, às 08:50');
  tem('Próximo paciente'); tem('data-acao="abrir" data-id="caio"><span class="dia-kpi-rotulo">Próximo paciente</span>'); tem('<strong class="dia-kpi-v num">10:00</strong><span>Caio Ferraz</span>'); tem('Sessão 3 de 6 · confirmado · fotos hoje');
  ['data-gid="antes"', 'data-gid="fotos"', 'data-gid="homecare"', 'data-gid="terminei"'].forEach(function (g) { tem('class="dia-kpi" data-acao="abrir-grupo" ' + g); });
  tem('Sua agenda hoje'); tem('Esta semana'); tem('Para você saber');
  naoTem('Monielle'); naoTem('Relato'); naoTem('ermo de'); naoTem('Pedir à Dra. Suellen'); naoTem('insumos acabando');
});
run('padrão da Fer: cinco linhas no centro, uma por grupo, com o número, quando fazer e os tipos', function () {
  GRUPOS.forEach(function (g) { tem('data-acao="abrir-grupo" data-gid="' + g + '"'); });
  igual(contaDaLinha('atencao'), 2, 'Pede atenção: o Thiago não chegou e a Gabriela não confirmou');
  igual(contaDaLinha('antes'), 7, 'Antes da sessão: as 7 por começar');
  igual(contaDaLinha('fotos'), 3, 'Fotos de hoje: Caio, Otávio e Roberto');
  igual(contaDaLinha('homecare'), 1, 'Home care: a Gabriela');
  igual(contaDaLinha('terminei'), 0, 'Terminei: ninguém em atendimento');
  tem('class="lg lg--tem lg--atencao" data-acao="abrir-grupo" data-gid="atencao"');
  tem('<span class="lg-quando">Agora</span>'); tem('<span class="lg-quando">Antes de cada uma</span>'); tem('<span class="lg-quando">Na sessão</span>'); tem('<span class="lg-quando">Ao terminar</span>');
  tem('<span class="lg-tipo">Não chegou · a Fer confirma</span>'); tem('<span class="lg-tipo">Não confirmou</span>');
  tem('<span class="lg-tipo">Fotos hoje <b class="num">3</b></span>'); tem('<span class="lg-tipo">Fim de ciclo</span>'); tem('<span class="lg-tipo">Manutenção</span>'); tem('<span class="lg-tipo">Primeira sessão</span>');
  tem('<span class="lg-tipo">Entrega de home care</span>'); tem('<span class="lg-tipo">Remédio, só informação <b class="num">2</b></span>');
  tem('<span class="lg-tipo">Caio · 3ª sessão</span>'); tem('<span class="lg-tipo">Capilare Tonic</span>');
  // O progresso do dia, onde faz sentido.
  tem('1 de 8 começadas'); tem('0 de 3 fotografados'); tem('0 de 1 entregues'); tem('1 de 8 passadas para a Fer');
  tem('<span class="detalhe">Nenhuma sessão em atendimento agora.</span>');
});
run('padrão da Fer: tocar numa linha abre a tela do grupo, com o caminho, a contagem igual à da linha e o corpo', function () {
  GRUPOS.forEach(function (g) {
    ir('hoje'); grupo('');
    var n = contaDaLinha(g);
    grupo(g);
    igual(T.estado.grupo, g, 'grupo aberto');
    tem('<span class="grupo-trilha-atual">' + NOMES[g] + '</span>'); tem('Voltar ao Hoje'); tem('<h1 class="grupo-titulo" id="h-grupo">' + NOMES[g] + '</h1>');
    tem('<p class="grupo-conta"><strong class="num">' + n + '</strong> ');
    naoTem('class="dia-kpis"'); naoTem('class="lg-lista"');
  });
  grupo(''); igual(T.estado.grupo, null, 'voltou ao Hoje'); tem('class="lg-lista"');
  // Vindo de outra tela, abrir um grupo leva ao Hoje.
  ir('fotos'); grupo('antes'); igual(location.hash, '#hoje', 'foi para o Hoje'); tem('<span class="grupo-trilha-atual">Antes da sessão</span>');
  grupo('');
});
run('Pede atenção: quem não chegou, quem não confirmou e quem ficou sem as fotos', function () {
  grupo('atencao');
  tem('<strong class="num">2</strong> pedem atenção'); tem('class="grupo-cab cartao grupo-cab--atencao"');
  tem('Thiago Bastos'); tem('Não chegou às 9h. A Fer está confirmando: chegou, avisou atraso ou não veio. Se ele chegar, a sessão começa normalmente.');
  tem('Gabriela Siqueira'); tem('Não respondeu ao pedido de confirmação de ontem. A Fer confirma na chegada; a sessão das 16h segue marcada.');
  igual(vezes('data-acao="comecar"', __els.tela.innerHTML), 2, 'os dois podem começar'); tem('data-acao="ficha" data-id="thiago"');
  naoTem('Monielle'); naoTem('Ligar');
  grupo('');
});
run('Antes da sessão: o resumo de 30 segundos de cada paciente de hoje, com os botões do painel', function () {
  grupo('antes');
  tem('<strong class="num">7</strong> por começar · 1 de 8 começadas');
  igual(vezes('Antes da sessão, em 30 segundos', __els.tela.innerHTML), 7, 'um resumo por paciente');
  igual(vezes('data-acao="comecar"', __els.tela.innerHTML), 7, 'cada um com o Começar'); igual(vezes('data-acao="ficha"', __els.tela.innerHTML), 7, 'cada um com a ficha');
  igual(vezes('Fotos de hoje 0/6', __els.tela.innerHTML), 3, 'os três com fotos');
  tem('Sessão 3 de 6.</strong> O pacote prevê fototerapia e intradermoterapia.'); tem('Home care para entregar hoje:</strong> Capilare Tonic');
  tem('Capilare Fort (remédio): acaba em 52 dias.'); tem('O Capilare Fort é remédio: aqui é só a informação.'); naoTem('dose');
  tem('O protocolo ainda não está no cadastro, e a Fer completa na chegada.'); tem('Fecha o ciclo: depois vem a reavaliação com a Dra. Suellen.');
  tem('Ele não sugere conduta'); naoTem('Registrar a sessão');
  igual(vezes('Monielle', __els.tela.innerHTML), 1, 'o lembrete uma vez só, no alto do grupo');
  grupo('');
});
run('Fotos de hoje e Home care para entregar: os cartões que o protótipo já tinha', function () {
  grupo('fotos');
  tem('<strong class="num">3</strong> com fotos hoje · 0 de 3 fotografados'); tem('10:00 · Caio Ferraz'); tem('14:00 · Otávio Reis'); tem('17:00 · Roberto Albuquerque');
  igual(vezes('data-acao="fotos"', __els.tela.innerHTML), 3, 'um botão de fotos por paciente'); tem('Primeira sessão: estas fotos são o ponto de partida do antes e agora.'); tem('As 6 tomadas e as próximas fotos');
  grupo('homecare');
  tem('<strong class="num">1</strong> para entregar · 0 de 1 entregues'); tem('Gabriela Siqueira'); tem('<strong>Capilare Tonic</strong>: Capilare Tonic: acaba em 40 dias.');
  tem('A Fer separou e passa para você. Você entrega e explica o uso; a baixa é com a Fer, na saída.'); naoTem('Capilare Fort é remédio');
  tem('data-acao="abrir" data-id="gabriela"'); tem('data-acao="ficha" data-id="gabriela"');
  grupo('terminei');
  tem('Nenhuma sessão em atendimento agora.'); tem('<strong>Ricardo Sato</strong>: Passou para a Fer às 08:50. A Fer deu a baixa e registrou a saída.');
  grupo('');
});
run('coluna da direita: a agenda de hoje hora a hora, com a situação da recepção e o agora pelo relógio', function () {
  grupo('');
  var h = __els.tela.innerHTML;
  tem('id="rolagem-agenda"', h); tem('title="A situação de cada paciente vem da recepção.">8 sessões</span>', h);
  tem('Saiu', h); tem('Não chegou · a Fer confirma', h); tem('Não confirmou', h); tem('Confirmado', h); tem('Fotos hoje · 0/6', h); tem('Sessão 6 de 6 · fim de ciclo', h);
  tem('linha--proxima" data-acao="abrir" data-id="caio"', h); tem('class="linha linha-dia linha--feita" data-acao="abrir" data-id="ricardo"', h);
  tem('<li class="agora-marca"><span class="num">09:20</span> · agora</li>', h);
  var caixa = h.slice(h.indexOf('id="rolagem-agenda"'));
  var t = caixa.indexOf('data-id="thiago"'); var a = caixa.indexOf('agora-marca'); var c = caixa.indexOf('data-id="caio"');
  if (!(t >= 0 && t < a && a < c)) throw new Error('o agora fica entre as 9h e as 10h');
  naoTem('Fotos bloqueadas'); naoTem('Sem termo'); naoTem('data-acao="avisar-fer"'); naoTem('Ver os insumos'); naoTem('Livre:');
});
run('coluna da direita: Esta semana dia a dia, e Para você saber na outra aba', function () {
  grupo('');
  tem('aria-selected="true">Esta semana</button>'); tem('Semana de '); tem('<span class="dia-semana">seg</span>'); tem('Já passou');
  tem('<span class="linha-nome">8 sessões<span class="detalhe">hoje</span></span>'); tem('3 com fotos');
  tem('6 sessões'); tem('2 horários livres'); tem('2 com fotos'); tem('Toda a agenda, até três meses à frente');
  tem('data-acao="ver-dia" data-data="' + T.chave(diasDepois(1)) + '"'); naoTem('data-acao="ver-dia" data-data="' + T.chave(diasDepois(-1)) + '"');
  naoTem('quem cuida do estoque');
  aba('saber');
  tem('aria-selected="true">Para você saber</button>'); naoTem('Semana de ');
  tem('<strong>Fim de ciclo às 17h:</strong> as fotos da 6ª sessão do Roberto Albuquerque vão para o antes e agora da reavaliação com a Dra. Suellen.');
  tem('você passa para a Fer o que foi feito e o retorno. A Fer dá toda a baixa no sistema e marca a próxima.');
  // Insumos (Luiz, 07/10/2026): a gestão é da Fer; a Ana Carolina só avisa.
  tem('<strong>Insumos:</strong> quem cuida do estoque é a Fer. Se algo estiver acabando na sala, avise a Fer.');
  aba('semana'); tem('Semana de ');
  // Tocar num dia abre esse dia na Agenda.
  var k = T.chave(diasDepois(1));
  T.ACOES['ver-dia'](botao('ver-dia', { data: k }));
  igual(location.hash, '#agenda', 'foi para a Agenda'); igual(T.estado.agendaDia, k, 'no dia escolhido'); tem('data-data="' + k + '" aria-pressed="true"');
  ir('hoje');
});
run('Luiz, 07/10 · home care: a Ana Carolina entrega o kit que acaba antes da vinda seguinte; a Fer separa e dá baixa', function () {
  igual(T.paraEntregar(P('gabriela')).map(function (h) { return h[0]; }).join(), 'tonic', 'a Gabriela recebe o Capilare Tonic hoje');
  igual(T.paraEntregar(P('larissa')).map(function (h) { return h[0]; }).join(), 'tonic', 'a Larissa recebe o Capilare Tonic amanhã');
  igual(T.paraEntregar(P('caio')).length, 0, 'o kit do Caio dura até depois da próxima sessão');
  igual(T.paraEntregar(P('sergio')).length, 0, 'o Capilare Fort do Sérgio dura até depois da próxima manutenção');
  grupo(''); ir('hoje'); tem('Home care</span>'); tem('<span class="lg-tipo">Capilare Tonic</span>');
  T.ACOES.abrir(botao('abrir', { id: 'gabriela' }));
  tem('Home care para entregar hoje:</strong> Capilare Tonic, que a Fer separa e passa para você. Você entrega e explica o uso; a baixa é com a Fer, na saída.', painel());
  T.ACOES.abrir(botao('abrir', { id: 'larissa' }));
  tem('Home care para entregar nesta sessão:</strong> Capilare Tonic', painel());
  T.ACOES.fechar();
  ir('agenda'); tem('Entregar home care'); ir('hoje');
});
run('regras fixas: fotos, home care e o padrão do ciclo', function () {
  igual(T.temFoto(P('otavio')), true, 'otávio, 1ª sessão'); igual(T.temFoto(P('caio')), true, 'caio, 3ª'); igual(T.temFoto(P('roberto')), true, 'roberto, 6ª');
  igual(T.temFoto(P('lucas')), false, 'lucas, 2ª'); igual(T.temFoto(P('gabriela')), false, 'gabriela, 5ª'); igual(T.temFoto(P('sergio')), false, 'manutenção');
  // Durações definitivas (Luiz, 07/10/2026): Capilare Nutri e Capilare Fort, 120 dias; Capilare Tonic, 75.
  igual(T.restam(['nutri', 30]), 90, 'nutri'); igual(T.restam(['tonic', 30]), 45, 'tonic'); igual(T.restam(['fort', 68]), 52, 'fort');
  igual(T.padraoRetorno(P('otavio')), 20, 'da 1ª para a 2ª'); igual(T.padraoRetorno(P('caio')), 40, 'da 3ª para a 4ª');
  igual(T.padraoRetorno(P('roberto')), 45, 'depois da 6ª, a reavaliação'); igual(T.padraoRetorno(P('sergio')), 45, 'manutenção');
  // Saíram do copiloto da Ana Carolina (Luiz, 07/10/2026): o registro da sessão, os relatos, os termos e os insumos.
  ['registrar', 'salvar-sessao', 'alerta-passei', 'guardiao-alerta', 'guardiao-nao', 'avisar-fer', 'pedir-insumo', 'opcao', 'proc', 'qtd', 'retorno'].forEach(function (k) {
    if (T.ACOES[k]) throw new Error('ação que devia ter saído: ' + k);
  });
  if (T.estado.alertas || T.estado.insumos || T.estado.avisos) throw new Error('sobrou estado de relato, insumo ou termo');
});
run('antes da sessão: leitura de 30 segundos, sem termos, e o lembrete da Dra. Monielle', function () {
  T.ACOES.abrir(botao('abrir', { id: 'caio' }));
  tem('Antes da sessão, em 30 segundos', painel()); tem('Sessão 3 de 6.</strong> O pacote prevê fototerapia e intradermoterapia.', painel());
  tem('Hoje tem fotos:</strong> 3ª sessão, 6 tomadas.', painel());
  tem('Retorno pelo padrão:</strong> a sessão 4 vem 40 dias depois. No fim, você confirma ou ajusta e passa para a Fer, que marca.', painel());
  tem('Capilare Nutri: acaba em 90 dias. Capilare Tonic: acaba em 45 dias.', painel()); naoTem('Home care para entregar', painel());
  tem('Começar a sessão', painel()); tem('Fotos de hoje 0/6', painel()); tem('Ver a ficha', painel());
  tem('Ele não sugere conduta', painel()); tem('O prontuário continua no Clinicorp', painel());
  // Itens 39 e 44 (Luiz, 07/10/2026): na sessão, a Ana Carolina não comunica nada à Dra. Monielle, e nada fica registrado.
  tem('Se o paciente contar um sintoma ou tiver dúvida sobre remédio, ele fala direto com a Dra. Monielle. A clínica não orienta e não registra nada no sistema.', painel());
  igual(vezes('Monielle', painel()), 1, 'só o lembrete fala da Dra. Monielle');
  naoTem('ermo de', painel()); naoTem('Termo', painel()); naoTem('Relato', painel()); naoTem('Alerta', painel()); naoTem('Relatou algo', painel());
  naoTem('Depois do último MMP', painel()); naoTem('a Fer ligou', painel());
  T.estado.agenda.filter(function (x) { return !x.vaga; }).forEach(function (x) {
    igual(vezes('Monielle', T.painelAntes(x.id)), 1, 'só o lembrete, em ' + x.id);
  });
  T.ACOES.fechar();
});
run('antes da sessão: remédio, protocolo, reavaliação e quem já passou para a Fer', function () {
  T.ACOES.abrir(botao('abrir', { id: 'ricardo' }));
  tem('Passou para a Fer', painel()); tem('Às 08:50, você passou para a Fer o que foi feito e o retorno. A Fer dá toda a baixa no sistema e marca a próxima sessão.', painel());
  naoTem('Começar a sessão', painel()); naoTem('data-acao="terminar"', painel());
  T.ACOES.abrir(botao('abrir', { id: 'sergio' }));
  tem('Capilare Fort (remédio): acaba em 52 dias.', painel()); tem('O Capilare Fort é remédio: aqui é só a informação.', painel()); naoTem('prescrito', painel());
  tem('O protocolo ainda não está no cadastro, e a Fer completa na chegada.', painel()); naoTem('dose', painel()); tem('Na manutenção, só se a Dra. Suellen pedir.', painel());
  tem('Retorno pelo padrão:</strong> a próxima manutenção vem 45 dias depois.', painel());
  T.ACOES.abrir(botao('abrir', { id: 'roberto' }));
  tem('Fecha o ciclo: depois vem a reavaliação com a Dra. Suellen.', painel()); tem('a reavaliação com a Dra. Suellen vem 45 dias depois', painel());
  T.ACOES.abrir(botao('abrir', { id: 'otavio' }));
  tem('Hoje tem fotos:</strong> 1ª sessão, 6 tomadas.', painel()); naoTem('bloquead', painel()); naoTem('avisar-fer', painel());
  T.ACOES.abrir(botao('abrir', { id: 'thiago' }));
  tem('Não chegou · a Fer confirma', painel()); tem('Começar a sessão', painel());
  T.ACOES.fechar();
});
run('fotos de hoje: o paciente vem da agenda e o roteiro é fixo, sem trava de termo', function () {
  T.ACOES.fotos(botao('fotos', { id: 'caio' }));
  tem('Fotos de hoje · 0/6', painel()); tem('Da agenda das 10:00: <strong>Caio Ferraz</strong>', painel()); tem('Ninguém digita nome', painel());
  tem('A câmera fica na estação de fotos', painel()); tem('Abrir a estação de fotos', painel()); tem('#estacao/caio/3', painel()); naoTem('próxima entrega', painel());
  ['Topo', 'Coroa', 'Linha frontal', 'Lateral direita', 'Lateral esquerda', 'Tricoscopia em 4 pontos', 'Frontal, médio, vértex e occipital'].forEach(function (t) { tem(t, painel()); });
  naoTem('disabled', painel()); tem('Só a equipe da clínica vê: a Dra. Suellen, a Fer, a Ana Carolina e a Dra. Monielle.', painel()); tem('sem passar pelo WhatsApp', painel());
  // Item 38 (Luiz, 07/10/2026): os termos ficam para depois, e as fotos do Otávio não ficam mais bloqueadas.
  T.ACOES.fotos(botao('fotos', { id: 'otavio' }));
  tem('href="' + T.URL_ESTACAO + '#estacao/', painel()); naoTem('Bloqueado', painel()); naoTem('termo', painel());
  T.ACOES.fechar();
  T.ACOES.fotos(botao('fotos', { id: 'lucas' }));
  if (!__els.painel.hidden) throw new Error('abriu as fotos de quem não tem foto hoje');
  T.ACOES.fotos(botao('fotos', { id: 'murilo' }));
  if (!__els.painel.hidden) throw new Error('abriu as fotos de amanhã');
});
run('começar a sessão: o relógio anda, a recepção responde e o Hoje acompanha', function () {
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  igual(P('caio').status, 'atendimento', 'caio'); igual(T.estado.relogio, 600, 'relógio às 10:00');
  igual(P('thiago').status, 'nao-veio', 'thiago');
  tem('A Fer confirmou que Thiago não veio.', toastTxt()); tem('Sessão do Caio começou.', toastTxt());
  tem('Terminei: passar para a Fer', painel()); naoTem('Registrar a sessão', painel());
  T.ACOES.comecar(botao('comecar', { id: 'sergio' }));
  igual(P('sergio').status, 'confirmado', 'não começa duas ao mesmo tempo'); tem('Termine antes a sessão do Caio Ferraz.', toastTxt());
  T.ACOES.abrir(botao('abrir', { id: 'sergio' }));
  tem('Termine antes a sessão do Caio Ferraz.', painel()); naoTem('data-acao="comecar"', painel());
  T.ACOES.abrir(botao('abrir', { id: 'thiago' }));
  tem('A Fer confirmou a falta e cuida da remarcação.', painel());
  T.ACOES.fechar();
  grupo(''); ir('hoje');
  tem('Em atendimento desde 10:00'); tem('ao terminar, passe para a Fer'); tem('Não veio'); tem('O Caio Ferraz está em atendimento.');
  tem('O Thiago Bastos não veio, e a Fer cuida da remarcação.'); naoTem('não chegou às 9h');
  // Os números e as linhas acompanham: o Caio em atendimento, o Thiago fora da conta.
  tem('<span class="dia-kpi-rotulo">Em atendimento</span><span class="dia-kpi-valor"><strong class="dia-kpi-v num">10:00</strong><span>Caio Ferraz</span>');
  tem('<strong class="dia-kpi-v num">5</strong><span>a chegar</span>'); tem('1 já atendido · 1 em atendimento · 1 não veio · 8 na agenda');
  igual(contaDaLinha('atencao'), 1, 'só a Gabriela pede atenção'); igual(contaDaLinha('antes'), 5, 'cinco por começar'); igual(contaDaLinha('terminei'), 1, 'o Caio para passar');
  tem('<span class="lg-tipo">Em atendimento</span>'); tem('2 de 7 começadas');
  // O agora anda com o relógio: às 10h, a marca fica depois do Caio.
  var caixa = __els.tela.innerHTML; caixa = caixa.slice(caixa.indexOf('id="rolagem-agenda"'));
  var c = caixa.indexOf('data-id="caio"'); var a = caixa.indexOf('agora-marca'); var s = caixa.indexOf('data-id="sergio"');
  if (!(c >= 0 && c < a && a < s)) throw new Error('o agora das 10:00 fica entre o Caio e o Sérgio'); tem('<span class="num">10:00</span> · agora', caixa);
  grupo('terminei');
  tem('<strong class="num">1</strong> para passar · 1 de 7 passadas para a Fer'); tem('Em atendimento desde 10:00. Ao terminar, passe para a Fer o que foi feito e o retorno: pelo padrão, a sessão 4 vem 40 dias depois.');
  tem('data-acao="terminar" data-id="caio"');
  grupo('');
});
run('Luiz, 07/10 · terminar a sessão: a Ana Carolina passa para a Fer, que dá toda a baixa e marca a próxima', function () {
  T.ACOES.abrir(botao('abrir', { id: 'caio' }));
  tem('data-acao="terminar" data-id="caio"', painel());
  T.ACOES.terminar(botao('terminar', { id: 'caio' }));
  igual(P('caio').status, 'terminada', 'terminou'); igual(P('caio').termino.hora, '10:50', 'hora do fim');
  igual(T.estado.relogio, 650, 'relógio às 10:50'); igual(P('sergio').status, 'chegou', 'a Fer marcou a chegada do Sérgio');
  tem('Passado para a Fer: ela dá a baixa e marca a próxima sessão.', toastTxt()); tem('Sérgio Pacheco chegou.', toastTxt());
  if (!__els.painel.hidden) throw new Error('o painel devia fechar');
  igual(logCom('A Ana Carolina passou para a Fer o que foi feito e o retorno; a Fer dá toda a baixa no sistema e marca a próxima sessão.').length, 1, 'no registro');
  igual(logCom('Os cuidados depois da sessão de Caio Ferraz vão pela Fer, por mensagem').length, 1, 'cuidados depois da sessão pela Fer');
  igual(logCom('Relat').length, 0, 'nenhum relato registrado'); igual(logCom('Teve MMP').length, 0, 'sem ligação depois do MMP');
  grupo(''); ir('hoje');
  tem('Terminou · com a Fer'); tem('Fotos pendentes · 0/6'); tem('Próxima sessão · 11:00');
  tem('<strong class="dia-kpi-v num">2</strong><span>de 8 sessões</span>'); tem('Ricardo, às 08:50 e Caio, às 10:50');
  igual(contaDaLinha('atencao'), 2, 'a Gabriela e o Caio, que terminou sem as fotos'); tem('<span class="lg-tipo">Terminou sem as fotos</span>');
  igual(contaDaLinha('terminei'), 1, 'o Caio aguarda a saída'); tem('<span class="lg-tipo">Passou para a Fer · aguarda a saída</span>');
  grupo('atencao'); tem('A sessão terminou às 10:50 sem as fotos de hoje (0 de 6). Dá para fotografar antes de ele sair.'); tem('data-acao="fotos" data-id="caio"');
  grupo('terminei'); tem('Às 10:50, você passou para a Fer o que foi feito e o retorno.'); tem('data-acao="fotos" data-id="caio"'); naoTem('data-acao="terminar"');
  grupo('');
  aba('saber'); tem('<strong>Fotos pendentes:</strong> a sessão do Caio Ferraz terminou sem as fotos de hoje (0 de 6).'); aba('semana');
  T.ACOES.abrir(botao('abrir', { id: 'caio' }));
  tem('Passou para a Fer', painel()); tem('Às 10:50, você passou para a Fer o que foi feito e o retorno.', painel()); tem('data-acao="fotos"', painel());
  T.ACOES.fechar();
  T.ACOES.terminar(botao('terminar', { id: 'caio' }));
  igual(logCom('A sessão de Caio Ferraz terminou').length, 1, 'não termina duas vezes');
});
run('Luiz, 07/10 · agenda: os próximos dias em destaque', function () {
  ir('agenda');
  tem('Sua agenda.'); tem('Agenda lida do Clinicorp às 06:00');
  tem('Igor Batista'); tem('Confirmou'); tem('Aguardando resposta'); tem('A Fer pede hoje'); tem('A Fer liga para confirmar');
  tem('Horário vago: a Fer está chamando alguém.'); tem('Murilo Teixeira'); tem('Primeira sessão'); tem('Fotos · 1ª sessão'); tem('Fotos · 3ª sessão');
  tem('Wagner Pires'); naoTem('Pode seguir'); naoTem('Monielle');
  tem('Tem fotos do Murilo Teixeira (1ª sessão) e da Elaine Duarte (3ª sessão).');
  tem('1 paciente já confirmou e 1 está aguardando resposta');
  tem('3 com MMP'); tem('4 com MMP'); tem('2 horários vagos'); tem('<h2 id="h-dia-2">' + T.tituloDia(2) + '</h2>');
  T.ACOES.abrir(botao('abrir', { id: 'wagner' }));
  naoTem('Relato', painel()); naoTem('Começar a sessão', painel()); naoTem('Fotos de hoje', painel()); tem('Ver a ficha', painel());
  T.ACOES.abrir(botao('abrir', { id: 'murilo' }));
  tem('Tem fotos:</strong> 1ª sessão, 6 tomadas.', painel()); tem('A Fer pede hoje', painel());
  T.ACOES.fechar();
  // O endereço antigo abre a Agenda.
  ir('proximos'); tem('Sua agenda.'); ir('hoje');
});
run('Luiz, 07/10 · agenda de todo o período: até três meses à frente, com o ciclo de cada paciente', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  var g = gerados();
  if (g.length < 150) throw new Error('poucas sessões no período: ' + g.length);
  var hoje = T.chave(T.HOJE); var ultimo = T.chave(T.ULTIMO); var vistos = {};
  T.estado.agenda.forEach(function (x) {
    var c = x.data + ' ' + x.hora;
    if (vistos[c]) throw new Error('dois pacientes no mesmo horário: ' + c); vistos[c] = 1;
    if (x.data < hoje || x.data > ultimo) throw new Error('fora do período: ' + x.data);
    if (T.horariosDoDia(T.deChave(x.data)).indexOf(x.hora) < 0) throw new Error('fora do horário dela: ' + c);
  });
  // O ciclo: da 3ª para a 4ª, 40 dias; da 4ª para a 5ª, 45. A Fer marca no mesmo horário, ou no mais perto.
  var caio = T.visitasDe('caio');
  igual(caio[0].dia, 0, 'hoje'); igual(caio[1].sessao, 4, 'a 4ª sessão'); igual(caio[1].hora, '10:00', 'no mesmo horário');
  var d1 = entreDatas(caio[0].data, caio[1].data); if (d1 < 40 || d1 > 46) throw new Error('da 3ª para a 4ª: ' + d1);
  igual(caio[2].sessao, 5, 'a 5ª sessão'); var d2 = entreDatas(caio[1].data, caio[2].data); if (d2 < 45 || d2 > 51) throw new Error('da 4ª para a 5ª: ' + d2);
  // Depois da 6ª, a reavaliação é com a Dra. Suellen: não entra na agenda dela.
  igual(T.visitasDe('roberto').length, 1, 'o Roberto fecha o ciclo hoje');
  var se = T.visitasDe('sergio'); if (!se[1] || !se[1].manut || entreDatas(se[0].data, se[1].data) < 45) throw new Error('manutenção a cada 45 dias');
  // O home care anda junto: o Capilare Tonic que a Gabriela recebe hoje recomeça a conta.
  igual(T.visitasDe('gabriela')[1].hc.filter(function (h) { return h[0] === 'tonic'; })[0][1], 0, 'tonic entregue hoje');
  // Sempre o mesmo resultado.
  var antes = JSON.stringify(T.estado.agenda.map(function (x) { return [x.id, x.data, x.hora]; }));
  T.ACOES.reiniciar(botao('reiniciar'));
  igual(JSON.stringify(T.estado.agenda.map(function (x) { return [x.id, x.data, x.hora]; })), antes, 'a agenda gerada é sempre a mesma');
});
run('Luiz, 07/10 · agenda de todo o período: semana, atalhos de 15 a 60 dias e o dia escolhido', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  ir('agenda');
  tem('Todo o período'); tem('a agenda abre três meses à frente'); tem('Semana de '); tem('id="h-dia-escolhido"');
  ['Hoje', 'Daqui a 15 dias', 'Daqui a 30 dias', 'Daqui a 45 dias', 'Daqui a 60 dias'].forEach(function (t) { tem('>' + t + '</button>'); });
  var depois = T.estado.agenda.filter(function (x) { return x.dia === 2; })[0].data;
  if (!(T.diaEscolhido() > depois)) throw new Error('o período começa depois dos dois dias em destaque: ' + T.diaEscolhido());
  tem('data-data="' + T.diaEscolhido() + '" aria-pressed="true"');
  T.ACOES['agenda-ir'](botao('agenda-ir', { dias: '30' }));
  var alvo = T.chave(T.ajustarDia(diasDepois(30)));
  igual(T.estado.agendaDia, alvo, 'daqui a 30 dias'); tem('data-data="' + alvo + '" aria-pressed="true"');
  tem('horários livres:'); tem('A Fer marca aqui as próximas sessões.');
  T.ACOES['agenda-semana'](botao('agenda-semana', { passo: '1' }));
  igual(entreDatas(alvo, T.estado.agendaDia), 7, 'uma semana depois');
  T.ACOES['agenda-ir'](botao('agenda-ir', { dias: '60' }));
  for (var i = 0; i < 6; i += 1) T.ACOES['agenda-semana'](botao('agenda-semana', { passo: '1' }));
  if (T.estado.agendaDia > T.chave(T.ULTIMO)) throw new Error('passou dos três meses');
  tem('data-passo="1" disabled'); tem('Ainda não abriu');
  T.ACOES['agenda-ir'](botao('agenda-ir', { dias: '0' }));
  igual(T.estado.agendaDia, T.chave(T.HOJE), 'hoje'); tem('data-passo="-1" disabled'); tem('Não chegou · a Fer confirma');
  tem('Protocolo ainda não registrado: a Fer completa na chegada.'); tem('Passou para a Fer às 08:50. A Fer deu a baixa e registrou a saída.');
  // Uma sessão marcada lá na frente abre o resumo, sem começar nada, e com o lembrete.
  var g = gerados().filter(function (x) { return /^g/.test(x.id); })[0];
  T.ACOES.abrir(botao('abrir', { id: g.id }));
  tem('A Fer pede a confirmação na véspera', painel()); tem('Antes da sessão, em 30 segundos', painel()); naoTem('Começar a sessão', painel());
  tem(T.LEMBRETE_SESSAO, painel()); tem('Ver a ficha', painel());
  T.ACOES.fechar();
  ir('hoje');
});
run('Luiz, 07/10 · ficha inteira: contato, origem, pagamentos, contatos e fotos, sem alertas e sem termos', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.ACOES.ficha(botao('ficha', { id: 'caio' }));
  tem('Ficha do paciente', painel()); tem('(41) 90000-0623', painel()); tem('Barber Club Bigorrilho, parceira', painel()); tem('Confirmado', painel());
  tem('Agenda e sessões', painel()); tem('Sessão 1: fototerapia e intradermoterapia, com fotos.', painel()); tem('Sessão 2: fototerapia e MMP.', painel());
  tem('Sessão 4 de 6: fototerapia e MMP. Marcada pela Fer.', painel());
  tem('Pagamentos', painel()); tem('R$ 350', painel()); tem('R$ 2.600', painel()); tem('Protocolo completo (6 sessões) · cartão de crédito em 2x · há 40 dias', painel());
  tem('Se você receber um pagamento no atendimento, passe para a Fer lançar: o lançamento é com ela.', painel());
  tem('Contatos', painel()); tem('A Fer mandou o pedido de confirmação de hoje, e Caio confirmou.', painel()); tem('Fotos de evolução', painel()); tem('você abriu às', painel());
  naoTem('Termos', painel()); naoTem('Alertas', painel()); naoTem('Relato', painel()); tem('Observações da Dra. Monielle', painel()); naoTem('só o nome', painel()); naoTem('CPF', painel());
  igual(T.estado.aberturas.filter(function (a) { return a[1] === 'caio'; }).length, 1, 'abertura registrada');
  T.ACOES.ficha(botao('ficha', { id: 'sandra' })); tem('(41) 3300-2692', painel()); tem('Não: telefone fixo, a Fer liga', painel());
  T.ACOES.ficha(botao('ficha', { id: 'rogerio' })); tem('Prefere ligação', painel());
  T.ACOES.ficha(botao('ficha', { id: 'sergio' })); tem('Protocolo completo, com fotos na 1ª, na 3ª e na 6ª sessão.', painel()); tem('É remédio: aqui só a informação.', painel()); tem('R$ 390', painel()); tem('Manutenção · PIX', painel());
  T.ACOES.ficha(botao('ficha', { id: 'roberto' })); tem('Reavaliação com a Dra. Suellen, que fecha o ciclo. A Fer marca.', painel());
  T.ACOES.ficha(botao('ficha', { id: 'wagner' })); naoTem('Pode seguir', painel()); naoTem('Passado', painel());
  var g = gerados().filter(function (x) { return /^g/.test(x.id); })[0];
  T.ACOES.ficha(botao('ficha', { id: g.id })); tem('Ficha do paciente', painel()); tem('Marcada pela Fer', painel()); tem('R$ ', painel()); naoTem('undefined', painel());
  T.ACOES.fechar();
});
run('tela Fotos: quem fotografa hoje e o que vem', function () {
  ir('fotos');
  tem('Fotos de hoje.'); tem('As 6 tomadas, sempre nesta ordem'); tem('Próximas fotos'); tem('Murilo Teixeira'); tem('Pedro Antunes');
  tem('A estação de fotos'); tem('A câmera fica na estação de fotos'); tem('a Fer, a Ana Carolina e a Dra. Monielle');
  tem('<h3 class="dia-titulo">Amanhã · '); tem('<span class="linha-nota">1ª sessão: 6 tomadas.</span>');
  tem('<div class="acao-pe">'); tem('<li><span><strong>No físico:</strong>');
  naoTem('Termos de imagem que faltam'); naoTem('bloquead');
});
run('o dia inteiro: até a última sessão, e o fim de ciclo vai para a Fer', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  var guarda = 0;
  var pendente = function () { return T.estado.agenda.filter(function (x) { return x.dia === 0 && !x.vaga && ['confirmado', 'nao-confirmado', 'chegou'].indexOf(x.status) >= 0; })[0]; };
  while (pendente() && guarda < 10) {
    guarda += 1;
    var p = pendente();
    T.ACOES.comecar(botao('comecar', { id: p.id }));
    T.ACOES.terminar(botao('terminar', { id: p.id }));
    if (['terminada', 'saiu'].indexOf(P(p.id).status) < 0) throw new Error(p.id + ' não terminou');
    if (p.id === 'roberto') tem('marca a reavaliação com a Dra. Suellen', toastTxt());
  }
  ir('hoje');
  tem('Todas as sessões de hoje terminaram.'); tem('Boa tarde, Ana Carolina.'); tem('Hoje foram <strong>8 sessões</strong> na agenda');
  tem('<strong class="dia-kpi-v num">Fim</strong><span>do dia</span>'); tem('<strong class="dia-kpi-v num">0</strong><span>a chegar</span>');
  igual(contaDaLinha('antes'), 0, 'ninguém por começar'); tem('Todas as sessões de hoje já começaram.');
  igual(logCom('Fim de ciclo de Roberto Albuquerque').length, 1, 'fim de ciclo vai para a Fer');
  naoTem('fim de ciclo do Roberto');
  // O agora no fim do dia fica depois da última sessão.
  var caixa = __els.tela.innerHTML; caixa = caixa.slice(caixa.indexOf('id="rolagem-agenda"'));
  if (caixa.lastIndexOf('agora-marca') < caixa.indexOf('data-id="roberto"')) throw new Error('o agora fica depois das 17h');
});
run('textos: sem travessão, sem documento e sem o que saiu', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  var tudo = '';
  ['hoje', 'fotos', 'agenda'].forEach(function (h) { ir(h); tudo += __els.tela.innerHTML; });
  ir('hoje'); GRUPOS.forEach(function (g) { grupo(g); tudo += __els.tela.innerHTML; }); grupo('');
  aba('saber'); tudo += __els.tela.innerHTML; aba('semana');
  T.estado.agenda.filter(function (p) { return !p.vaga; }).forEach(function (p) { tudo += T.painelAntes(p.id) + T.painelFicha(p.id); });
  ['caio', 'otavio', 'roberto'].forEach(function (id) { tudo += T.painelFotos(id); });
  naoTem('—', tudo); naoTem('CPF', tudo); naoTem('garant', tudo); naoTem('undefined', tudo); naoTem('NaN', tudo);
  naoTem('ermo de', tudo); naoTem('Termo', tudo); naoTem('Alerta', tudo); naoTem('Passei', tudo); naoTem('Relatou', tudo);
  naoTem('Registrar a sessão', tudo); naoTem('Pedir à Dra. Suellen', tudo); naoTem('Kit padrão', tudo);
  ir('hoje');
});
run('noite 5: os números do dia batem com o resumo durante um atendimento', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  T.ACOES.fechar();
  ir('hoje');
  tem('1 já terminou e 5 estão por vir.'); tem('O Caio Ferraz está em atendimento.');
  tem('<strong class="dia-kpi-v num">5</strong><span>a chegar</span>'); tem('1 já atendido · 1 em atendimento · 1 não veio · 8 na agenda');
  tem('<strong class="dia-kpi-v num">1</strong><span>de 8 sessões</span>');
});
run('noite 5: terminar sem as fotos deixa as fotos pendentes; quem saiu aparece sem as fotos', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  T.ACOES.terminar(botao('terminar', { id: 'caio' }));
  igual(P('caio').status, 'terminada', 'terminou');
  ir('hoje');
  tem('Fotos pendentes · 0/6'); igual(contaDaLinha('fotos'), 3, 'as fotos do Caio seguem pendentes'); tem('<span class="lg-tipo">Caio · 3ª sessão</span>');
  aba('saber'); tem('<strong>Fotos pendentes:</strong> a sessão do Caio Ferraz terminou sem as fotos de hoje (0 de 6). Dá para fotografar antes de ele sair.'); aba('semana');
  T.ACOES.comecar(botao('comecar', { id: 'sergio' }));
  T.ACOES.fechar();
  ir('hoje');
  igual(P('caio').status, 'saiu', 'o Caio saiu');
  tem('Saiu sem as fotos · 0/6'); naoTem('Fotos pendentes · 0/6'); igual(contaDaLinha('fotos'), 2, 'o Caio saiu da conta'); tem('<span class="lg-tipo">Saiu sem as fotos</span>');
  aba('saber'); tem('<strong>Sem as fotos da 3ª sessão:</strong> Caio Ferraz saiu sem as fotos de hoje (0 de 6).'); aba('semana');
  grupo('atencao'); tem('Saiu sem as fotos da 3ª sessão (0 de 6). A Fer registrou a saída.'); naoTem('data-acao="fotos" data-id="caio"'); grupo('');
  ir('fotos');
  tem('Saiu sem as fotos · 0/6');
  ir('hoje');
});
run('noite 5: com o painel aberto, o aviso entra no painel e não cobre os botões', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  tem('id="painelAviso"', painel());
  tem('Sessão do Caio começou.', __els.painelAviso.innerHTML); igual(__els.painelAviso.hidden, false, 'aviso visível no painel');
  igual(__els.toast.hidden, true, 'o aviso solto fica escondido');
  T.ACOES.fechar();
  T.ACOES.reiniciar(botao('reiniciar'));
  igual(__els.toast.hidden, false, 'sem painel, o aviso volta a ser solto'); tem('Demonstração recomeçada.', __els.toast.innerHTML);
});
run('noite 5: botões no rodapé do painel e o celular com a próxima sessão logo abaixo do resumo', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.ACOES.abrir(botao('abrir', { id: 'caio' }));
  var h = painel();
  if (h.indexOf('<div class="painel-pe">') < 0 || h.indexOf('data-acao="comecar"') < h.indexOf('<div class="painel-pe">')) throw new Error('Começar fora do rodapé');
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  h = painel();
  if (h.indexOf('data-acao="terminar"') < h.indexOf('<div class="painel-pe">')) throw new Error('Terminar fora do rodapé');
  T.ACOES.fechar();
  T.ACOES.reiniciar(botao('reiniciar'));
  var hoje = T.renderHoje();
  // No padrão da Fer, os números do dia trazem o próximo paciente; no celular, que não tem os números, o cartão fica logo abaixo do resumo.
  var cel = hoje.indexOf('agora so-celular'); var resumo = hoje.indexOf('class="ola-resumo"'); var kpis = hoje.indexOf('class="dia-kpis"');
  if (cel < 0 || cel < resumo || cel > kpis) throw new Error('no celular, o cartão da próxima sessão vem depois do resumo e antes dos números');
  naoTem('agora so-tablet', hoje); tem('Próxima sessão · 10:00', hoje);
  tem('Abrir a estação de fotos', T.painelFotos('caio')); tem('aria-describedby="camera-explica"', T.painelFotos('caio'));
});
run('iPhone: os cinco grupos continuam, uma linha simples cada um, e a tela cheia volta', function () {
  T.ACOES.reiniciar(botao('reiniciar'));
  T.definirModo('iphone');
  if (!document.documentElement.classList.contains('modo-iphone') || !document.documentElement.classList.contains('preview')) throw new Error('sem o modo iPhone');
  igual(T.modo, 'iphone', 'modo');
  GRUPOS.forEach(function (g) { tem('data-acao="abrir-grupo" data-gid="' + g + '"'); });
  tem('agora so-celular'); tem('Sua agenda hoje'); tem('Esta semana');
  grupo('antes'); tem('Voltar ao Hoje'); tem('Antes da sessão, em 30 segundos'); grupo('');
  T.definirModo('tela');
  if (document.documentElement.classList.contains('preview') || document.documentElement.classList.contains('modo-iphone')) throw new Error('ficou em prévia');
  igual(T.modo, 'tela', 'de volta à tela cheia'); tem('class="dia-kpis"');
});
run('modos, tema e recomeçar: abre no claro, e o botão liga o modo noturno', function () {
  T.definirModo('iphone');
  if (!document.documentElement.classList.contains('modo-iphone')) throw new Error('sem o modo iPhone');
  T.ACOES.girar(botao('girar'));
  if (!document.documentElement.classList.contains('em-pe') || T.modo !== 'ipad') throw new Error('não girou o iPad');
  ir('ipad'); igual(T.modo, 'ipad', 'prévia do iPad'); igual(T.emPe, false, 'deitado');
  ir('ipad-em-pe'); igual(T.emPe, true, 'em pé');
  T.definirModo('tela');
  // Desde 09/10/2026, abre no claro, como a Fer e a doutora; o botão do menu liga o modo noturno.
  if (!document.documentElement.classList.contains('dia')) throw new Error('não abriu no claro');
  T.ACOES.tema(botao('tema'));
  if (!document.documentElement.classList.contains('noite') || document.documentElement.classList.contains('dia')) throw new Error('não foi para a noite');
  T.ACOES.tema(botao('tema'));
  if (!document.documentElement.classList.contains('dia')) throw new Error('não voltou para o dia');
  T.ACOES.comecar(botao('comecar', { id: 'caio' }));
  T.ACOES['agenda-ir'](botao('agenda-ir', { dias: '15' }));
  grupo('fotos'); aba('saber');
  T.ACOES.reiniciar(botao('reiniciar'));
  igual(T.estado.relogio, 560, 'relógio'); igual(T.estado.agendaDia, null, 'dia da agenda'); igual(P('caio').status, 'confirmado', 'agenda');
  igual(T.estado.grupo, null, 'sem grupo aberto'); igual(T.estado.abaLado, 'semana', 'aba Esta semana');
  tem('Bom dia, Ana Carolina.'); tem('Demonstração recomeçada.', toastTxt());
});
run('13/10 · estação de fotos: a página Fotos abre a estação com o paciente e a sessão, e a volta registra as tomadas', function () {
  var h = T.painelFotos('caio');
  if (h.indexOf(T.URL_ESTACAO + '#estacao/caio/3') < 0) throw new Error('sem o link da estação para o Caio na 3ª sessão');
  if (h.indexOf('entrega 2') >= 0 || h.indexOf('disabled') >= 0) throw new Error('o botão antigo continuou');
  if (!T.registrarFotosFeitas('caio:6')) throw new Error('não registrou');
  if (T.PAC('caio').fotos !== 6) throw new Error('fotos do Caio: ' + T.PAC('caio').fotos);
  T.registrarFotosFeitas('otavio:2');
  if (T.PAC('otavio').fotos !== 2) throw new Error('fotos do Otávio: ' + T.PAC('otavio').fotos);
  if (T.registrarFotosFeitas('ninguem:3')) throw new Error('aceitou paciente que não existe');
  T.registrarFotosFeitas('caio:9');
  if (T.PAC('caio').fotos !== 6) throw new Error('passou de 6');
  T.ACOES.reiniciar(botao('reiniciar'));
});

'''

ctx = quickjs.Context()
ctx.eval(SHIM)
ctx.eval(script)
ctx.eval(TESTES)
while ctx.execute_pending_job():
    pass
print(ctx.eval('__out.join("\\n")'))
