/* © 2026 Jose Carvalho. Nova Ceasa - ERP interno. Obra protegida (Lei 9.610/98 e
   9.609/98). Sem licenca de uso, copia ou obra derivada: ver LICENSE. NCE-7556A9 */
/* ============================================================
   Impressora de recibo.

   Porte do experimento de dqnamo.com/experiments/receipt-printer para o nosso
   sistema. Aquilo e React com Framer Motion e Tailwind; aqui e CSS e DOM puro,
   porque o ERP nao tem build e a CSP nao aceita script de fora. O que foi
   transplantado sao os VALORES do original, nao uma aproximacao minha:

   - clip-path de 40 dentes de 4px, identico (conferido caractere a caractere);
   - os 20 quadros do avanco do papel e os 20 tempos, identicos;
   - duracao 1,75s com easing linear, que e o que produz o tranco de motor de
     passo (deslizar liso parece cortina, nao impressora);
   - papel com altura minima de 20rem, recuo 1,5rem nas laterais, 1,75rem em
     cima e 2rem embaixo;
   - saida de altura fixa 32rem, puxada 1rem para dentro da maquina, com
     overflow escondido;
   - textura em mistura soft-light.

   O que mudou: as cores, que sao as da Nova Ceasa, e a marca.
   ============================================================ */

.imp {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 24rem;
  margin: 0 auto;
  --imp-raio: 1.5rem;
  --imp-recuo: 0.75rem;
  --imp-raio-interno: calc(var(--imp-raio) - var(--imp-recuo));
}

/* ---------- a maquina ---------- */
.imp-maquina {
  position: relative;
  isolation: isolate;
  width: 100%;
  overflow: hidden;
  border-radius: var(--imp-raio);
  border: 1px solid var(--forest);
  /* Quase preta, como a do original. Verde sobre verde deixava a maquina e o
     papel no mesmo tom e o objeto sumia; o contraste entre carcaca escura e
     papel claro e metade do efeito. */
  background: color-mix(in oklab, var(--green-900) 22%, var(--forest));
  padding: var(--imp-recuo);
  padding-bottom: 2rem;
  box-shadow:
    0 20px 36px -20px color-mix(in oklab, var(--forest) 55%, transparent),
    0 6px 14px -8px color-mix(in oklab, var(--forest) 24%, transparent),
    inset 0 1px 0 color-mix(in oklab, var(--lime) 14%, transparent),
    inset 0 -1px 0 color-mix(in oklab, var(--forest) 55%, transparent);
}
/* ruido de plastico na carcaca */
.imp-maquina::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0.3;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23p)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

.imp-cabeca {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  height: 2.75rem;
  color: color-mix(in oklab, var(--lime) 85%, #fff);
  font-family: var(--mono);
  font-size: var(--fs-12);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* O simbolo entra por mascara: a forma vem do alfa do PNG e a cor vem daqui.
   Um arquivo so serve nos dois temas, sem precisar de uma versao negativa. */
.imp-simbolo {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0.25rem 0 0 0.25rem;
  background: color-mix(in oklab, var(--lime) 88%, #fff);
  -webkit-mask: url("../img/favicon-192.png") center / contain no-repeat;
  mask: url("../img/favicon-192.png") center / contain no-repeat;
  filter: drop-shadow(0 0 6px color-mix(in oklab, var(--lime) 45%, transparent));
}
/* Luz de estado. E ela que faz o objeto parecer aparelho ligado: pisca enquanto
   o papel anda e fica acesa quando termina. O experimento original nao tem. */
.imp-luz {
  width: 7px;
  height: 7px;
  margin: 0.6rem 0.3rem 0 0;
  border-radius: 50%;
  background: color-mix(in oklab, var(--lime) 30%, var(--forest));
  box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.6);
  transition: background-color var(--dur-media) var(--curva), box-shadow var(--dur-media) var(--curva);
}
.imp[data-fase="imprimindo"] .imp-luz {
  background: var(--lime);
  box-shadow: 0 0 8px 1px color-mix(in oklab, var(--lime) 70%, transparent);
  animation: impPisca 620ms steps(1, end) infinite;
}
.imp[data-fase="pronto"] .imp-luz {
  background: var(--lime);
  box-shadow: 0 0 10px 1px color-mix(in oklab, var(--lime) 55%, transparent);
}
@keyframes impPisca {
  50% {
    background: color-mix(in oklab, var(--lime) 35%, var(--forest));
    box-shadow: none;
  }
}

/* ---------- o visor ---------- */
.imp-visor {
  position: relative;
  z-index: 1;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--imp-raio-interno);
  border: 1px solid var(--forest);
  background: var(--forest);
  padding: 1rem;
  color: #eef5f0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.imp-visor::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 0 24px 4px color-mix(in oklab, #000 52%, transparent);
}
.imp-visor-topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.imp-visor-ctx {
  min-width: 0;
}
.imp-visor-ctx b {
  display: block;
  font-family: var(--body);
  font-size: var(--fs-15);
  font-weight: 600;
  line-height: 1.2;
}
.imp-visor-ctx span {
  display: block;
  font-family: var(--body);
  font-size: var(--fs-12);
  color: color-mix(in oklab, var(--lime) 55%, #fff);
}
.imp-visor-tot {
  flex: none;
  text-align: right;
}
.imp-visor-rot {
  display: block;
  font-family: var(--body);
  font-size: var(--fs-12);
  color: color-mix(in oklab, var(--lime) 55%, #fff);
}
.imp-visor-val {
  display: block;
  font-family: var(--body);
  font-size: var(--fs-20);
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.imp-status {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: var(--fs-12);
  font-weight: 500;
  color: color-mix(in oklab, var(--lime) 62%, #fff);
}
.imp-status span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- a boca ---------- */
.imp-boca {
  position: absolute;
  left: 1.5rem;
  right: 1.5rem;
  bottom: var(--imp-recuo);
  z-index: 40;
  height: 0.5rem;
  border-radius: 0.25rem;
  border: 1px solid var(--forest);
  background: var(--forest);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.85);
}

/* ---------- a saida ---------- */
/* Altura fixa de 32rem com overflow escondido: e por isso que o papel andando
   nao empurra nada na tela. Puxada 1rem para dentro da maquina, para o papel
   nascer atras da carcaca. */
.imp-saida {
  position: relative;
  z-index: 50;
  margin-top: -1rem;
  /* auto, e nao 32rem. O papel esta no fluxo normal dentro desta caixa (o
     transform que o move nao ocupa espaco), entao "auto" ja vale exatamente a
     altura dele desde a primeira pintura. O JavaScript so congela esse mesmo
     numero depois. Com 32rem de reserva a altura pulava de 512 para 510 quando
     a medida chegava, e isso e mudanca de layout: 2px, mas e a regra que este
     arquivo inteiro promete nao quebrar. Foi o teste que pegou. */
  height: var(--imp-h, auto);
  width: calc(80% + 3rem);
  max-width: 100%;
  overflow: hidden;
  padding: 0 1.5rem;
}
/* Barra escura e borrada na boca da saida: e a sombra que o papel projeta ao
   passar pela fenda. Sem ela o papel parece colado por cima da maquina, e nao
   saindo de dentro. Estava faltando na minha primeira versao. */
.imp-saida::before {
  content: "";
  position: absolute;
  left: 1.5rem;
  right: 1.5rem;
  top: -0.25rem;
  z-index: 20;
  height: 0.5rem;
  pointer-events: none;
  opacity: 0;
  background: rgba(4, 21, 14, 0.75);
  filter: blur(6px);
  transition: opacity var(--dur-rapida) var(--curva);
}
.imp[data-fase="imprimindo"] .imp-saida::before,
.imp[data-fase="pronto"] .imp-saida::before {
  opacity: 1;
}
.imp-folha {
  position: relative;
  isolation: isolate;
  opacity: 0;
  transform: translateY(calc(-100% + 2px));
}
/* sombra do papel sobre o fundo */
.imp-folha::before {
  content: "";
  position: absolute;
  inset: 0.75rem 0.75rem 1rem;
  z-index: 0;
  pointer-events: none;
  border-radius: 2px;
  box-shadow: 0 8px 24px color-mix(in oklab, var(--forest) 24%, transparent);
}
/* sombra de contato no pe do papel */
.imp-folha::after {
  content: "";
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: 0;
  z-index: 0;
  height: 0.75rem;
  transform: translateY(0.375rem);
  border-radius: 999px;
  background: color-mix(in oklab, var(--forest) 10%, transparent);
  filter: blur(12px);
  pointer-events: none;
}

.imp-papel {
  position: relative;
  z-index: 10;
  min-height: 20rem;
  padding: 1.75rem 1.5rem 2rem;
  background-color: #f7f5ef;
  color: #14261b;
  font-family: var(--mono);
  font-size: var(--fs-12);
  line-height: 1.55;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.68' numOctaves='4'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23f)'/%3E%3C/svg%3E");
  background-size: cover;
  background-blend-mode: soft-light;
  clip-path: polygon(
    0 0, 100% 0, 100% calc(100% - 4px), 98.75% 100%, 97.5% calc(100% - 4px), 96.25% 100%, 95% calc(100% - 4px),
    93.75% 100%, 92.5% calc(100% - 4px), 91.25% 100%, 90% calc(100% - 4px), 88.75% 100%, 87.5% calc(100% - 4px),
    86.25% 100%, 85% calc(100% - 4px), 83.75% 100%, 82.5% calc(100% - 4px), 81.25% 100%, 80% calc(100% - 4px),
    78.75% 100%, 77.5% calc(100% - 4px), 76.25% 100%, 75% calc(100% - 4px), 73.75% 100%, 72.5% calc(100% - 4px),
    71.25% 100%, 70% calc(100% - 4px), 68.75% 100%, 67.5% calc(100% - 4px), 66.25% 100%, 65% calc(100% - 4px),
    63.75% 100%, 62.5% calc(100% - 4px), 61.25% 100%, 60% calc(100% - 4px), 58.75% 100%, 57.5% calc(100% - 4px),
    56.25% 100%, 55% calc(100% - 4px), 53.75% 100%, 52.5% calc(100% - 4px), 51.25% 100%, 50% calc(100% - 4px),
    48.75% 100%, 47.5% calc(100% - 4px), 46.25% 100%, 45% calc(100% - 4px), 43.75% 100%, 42.5% calc(100% - 4px),
    41.25% 100%, 40% calc(100% - 4px), 38.75% 100%, 37.5% calc(100% - 4px), 36.25% 100%, 35% calc(100% - 4px),
    33.75% 100%, 32.5% calc(100% - 4px), 31.25% 100%, 30% calc(100% - 4px), 28.75% 100%, 27.5% calc(100% - 4px),
    26.25% 100%, 25% calc(100% - 4px), 23.75% 100%, 22.5% calc(100% - 4px), 21.25% 100%, 20% calc(100% - 4px),
    18.75% 100%, 17.5% calc(100% - 4px), 16.25% 100%, 15% calc(100% - 4px), 13.75% 100%, 12.5% calc(100% - 4px),
    11.25% 100%, 10% calc(100% - 4px), 8.75% 100%, 7.5% calc(100% - 4px), 6.25% 100%, 5% calc(100% - 4px),
    3.75% 100%, 2.5% calc(100% - 4px), 1.25% 100%, 0% calc(100% - 4px)
  );
}

/* ---------- o avanco ---------- */
/* Os 20 quadros e os 20 tempos sao os do original, com easing linear: e a pausa
   entre os passos que faz parecer motor, e nao deslize. */
.imp[data-fase="imprimindo"] .imp-folha,
.imp[data-fase="pronto"] .imp-folha {
  opacity: 1;
  animation: impAvanco 1.75s linear forwards;
}
@keyframes impAvanco {
  0% { transform: translateY(calc(-100% + 2px)); }
  7.5% { transform: translateY(-91%); }
  10.5% { transform: translateY(-91%); }
  18% { transform: translateY(-81%); }
  21% { transform: translateY(-81%); }
  28.5% { transform: translateY(-70%); }
  31.5% { transform: translateY(-70%); }
  39% { transform: translateY(-58%); }
  42% { transform: translateY(-58%); }
  49.5% { transform: translateY(-45%); }
  52.5% { transform: translateY(-45%); }
  60% { transform: translateY(-32%); }
  63% { transform: translateY(-32%); }
  70.5% { transform: translateY(-20%); }
  73.5% { transform: translateY(-20%); }
  81% { transform: translateY(-10%); }
  84% { transform: translateY(-10%); }
  91.5% { transform: translateY(-3%); }
  94.5% { transform: translateY(-3%); }
  100% { transform: translateY(0%); }
}

/* ---------- conteudo do recibo ---------- */
/* A hierarquia e o respiro sao o que separam um recibo de uma lista de campos:
   selo no topo, item forte com o valor na mesma linha, descricao em cinza
   menor, e o total grande. Minha primeira versao punha tudo no mesmo tamanho e
   ficava apertada. */
.imp-selo {
  display: block;
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto 0.25rem;
  border-radius: 6px;
  background: #14261b;
  -webkit-mask: none;
  mask: none;
  position: relative;
}
.imp-selo::after {
  content: "";
  position: absolute;
  inset: 0.55rem;
  background: #f7f5ef;
  -webkit-mask: url("../img/favicon-192.png") center / contain no-repeat;
  mask: url("../img/favicon-192.png") center / contain no-repeat;
}
.imp-tracejado {
  border: 0;
  border-top: 1px dashed rgba(20, 38, 27, 0.28);
  margin: 1.25rem 0;
}
.imp-item {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: var(--fs-12);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.imp-item span:last-child {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}
.imp-desc {
  margin: 0.2rem 0 0;
  font-size: var(--fs-11);
  /* 0,62 de opacidade dava 4,41:1 sobre o papel #f7f5ef, e o piso e 4,5:1.
     Papel termico desbota mesmo, mas desbotar ate nao dar para ler deixa de ser
     estilo. 0,70 sobe para 5,70:1 e continua com cara de impressao fraca. */
  color: rgba(20, 38, 27, 0.7);
}
.imp-linha {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: var(--fs-11);
  color: rgba(20, 38, 27, 0.62);
  margin-top: 0.3rem;
}
.imp-linha span:first-child {
  flex: 0 0 auto;
}
.imp-linha span:last-child {
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;
}
.imp-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 1rem;
}
.imp-total span:first-child {
  font-size: var(--fs-12);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.imp-total span:last-child {
  font-size: var(--fs-20);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.imp-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.45rem 1rem;
  font-size: var(--fs-11);
}
.imp-meta dt {
  /* mesmo motivo do .imp-desc: 4,41:1 nao passava */
  color: rgba(20, 38, 27, 0.7);
}
.imp-meta dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.imp-pix-rot {
  margin: 0 0 0.6rem;
  text-align: center;
  font-size: var(--fs-11);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.imp-qr {
  display: block;
  width: 9.5rem;
  height: 9.5rem;
  margin: 0 auto 0.6rem;
  /* nitidez: o QR e um desenho de pixel, e suavizar borra os modulos e derruba
     a leitura no celular */
  image-rendering: pixelated;
}
/* O "copia e cola" inteiro, quebrando em qualquer ponto, como vem no recibo de
   banco. E o caminho de quem nao consegue apontar a camera. */
.imp-pe {
  margin: 0;
  text-align: center;
  /* Era 8px com 0,55 de opacidade: 3,60:1, bem abaixo do piso de 4,5:1, e esta
     e justamente a linha de quem NAO consegue apontar a camera e vai ter que
     ler o codigo. Miudo continua sendo (recibo de banco e assim), mas 9px com
     0,75 da 6,66:1 e se le. */
  font-size: 9px;
  line-height: 1.35;
  letter-spacing: 0.02em;
  color: rgba(20, 38, 27, 0.75);
  overflow-wrap: anywhere;
}

/* ---------- estado ---------- */
.imp-anel {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--lime) 30%, transparent);
  border-top-color: var(--lime);
  animation: impGira 700ms linear infinite;
}
@keyframes impGira {
  to { transform: rotate(360deg); }
}
.imp-ok {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--lime);
  transform: scale(0.6);
  opacity: 0;
  /* duracao e curva da regua (secao 5): eram 240, 160 e 260ms com duas curvas proprias */
  transition:
    transform var(--dur-media) var(--curva),
    opacity var(--dur-media) var(--curva);
}
.imp[data-fase="pronto"] .imp-ok { transform: scale(1); opacity: 1; }

/* ---------- impressao de verdade ----------
   Duas coisas atrapalham o papel e as duas moram aqui. A primeira e o resto da
   pagina: barra lateral, topo, a moldura do dialogo. A segunda e o proprio
   <dialog>, que fica na camada de cima do navegador com altura maxima e sombra,
   e em papel isso corta o comprovante no meio. */
@media print {
  body * { visibility: hidden; }
  .imp-papel, .imp-papel * { visibility: visible; }
  .imp-papel {
    /* Na tela o papel tem uma textura de grao (feTurbulence) em soft-light, que
       e o que da cara de papel termico. No PAPEL de verdade ela vira chuvisco
       colorido: suja a leitura, gasta tinta e engordou o PDF para 3,5MB.
       Some na impressao, junto com a serrilha, que so faz sentido na tela.
       Centralizado e com margem para o comprovante nao sair encostado no canto
       da folha. */
    background-image: none;
    background-blend-mode: normal;
    box-shadow: none;
    /* static, e nao absolute no canto: com a maquina em display: none o papel e
       a unica coisa na folha, entao ele pode simplesmente fluir e se centralizar.
       Ancorado em "inset: 0 auto auto 0" ele saia grudado no canto de cima a
       esquerda, com 307px numa folha A4 de 794px, minusculo. */
    position: static;
    margin: 24px auto;
    width: 420px;
    max-width: 100%;
    clip-path: none;
    transform: none !important;
    animation: none !important;
  }
  /* a saida existe so para esconder o papel acima da boca enquanto imprime */
  .imp-saida {
    height: auto;
    overflow: visible;
  }
  .imp-saida::before {
    display: none;
  }
  .imp-folha {
    /* opacity: 1 explicito: o estado base da folha e opacity 0, e quem a acende
       sao as regras de fase. Se alguem mandar imprimir antes de a impressao
       terminar, sem isto sai uma folha em branco. */
    opacity: 1;
    transform: none !important;
    animation: none !important;
  }
  /* os ancestrais tinham a largura da maquina (307px) e prendiam o papel, porque
     ele e "max-width: 100%". Soltos, o papel usa os 420px que pedi. */
  .imp,
  .imp-saida,
  .imp-folha {
    width: auto;
    max-width: none;
  }
  .erp-dialogo::backdrop { display: none; }
  dialog.erp-dialogo {
    position: static;
    box-shadow: none;
    border: none;
    max-height: none;
    width: auto;
    margin: 0;
  }
  .erp-dialogo-corpo { padding: 0; overflow: visible; }
  /* invisivel ainda ocupa espaco, e espaco em branco vira folha em branco */
  .erp-dialogo-topo,
  .imp-maquina,
  .imp-demo,
  .imp-acoes { display: none; }
}

/* ---------- toque ---------- */
@media (pointer: coarse) {
  .imp .btn { min-height: 44px; }
}

/* ---------- o dialogo em volta da impressora ----------
   Estava em modulos/financeiro.css, que so carrega na tela do administrador. O
   portal do permissionario tira a mesma segunda via e nao carrega aquela folha,
   entao o bloco veio para ca junto com a impressora que ele embala. */
.imp-bloco {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.imp-demo {
  font-size: var(--fs-11);
  color: var(--muted-foreground);
  margin-top: 4px;
}
.imp-acoes {
  display: flex;
  justify-content: center;
}

/* ---------- ULTIMA PALAVRA: menos movimento ---------- */
@media (prefers-reduced-motion: reduce) {
  .imp[data-fase="imprimindo"] .imp-folha,
  .imp[data-fase="pronto"] .imp-folha {
    animation: none !important;
    transform: none;
    opacity: 1;
  }
  .imp-anel { animation: none !important; border-top-color: transparent; }
  .imp-ok { transition: none; }
  .imp-luz { animation: none !important; transition: none; }
}
