@fayz-ai/pos 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -46,15 +46,22 @@
46
46
  --pos-info: #5B8DEF;
47
47
  --pos-danger: #DC5C55;
48
48
 
49
- /* --- category identity: assigned per category by data, never a status -- */
50
- --pos-tone-mint: #BDE3CD;
51
- --pos-tone-lilac: #D9CDED;
52
- --pos-tone-sky: #C2DDF1;
53
- --pos-tone-periwinkle: #CDD3F3;
54
- --pos-tone-blush: #F1C2D5;
55
- --pos-tone-sand: #E3D9CF;
56
- --pos-tone-teal: #B6E4D6;
57
- --pos-tone-rose: #EFC4C4;
49
+ /* --- category identity: assigned per category by data, never a status --
50
+ Saturados, na mesma força do mapa de mesas. Os pastéis vinham de quando o
51
+ salão também era discreto; com as mesas pintadas em cheio, uma grade de
52
+ cartões leitosos ao lado lia como outra tela, de outro produto.
53
+ Continuam LONGE dos quatro tons de status em matiz — nenhum verde-livre,
54
+ nenhum vermelho-atenção — porque cor aqui é identidade de categoria e lá é
55
+ estado; iguais, a mesma mancha significaria duas coisas na mesma sessão.
56
+ Todos escuros o bastante para texto `--pos-on-light` por cima passar AA. */
57
+ --pos-tone-mint: #59C08A;
58
+ --pos-tone-lilac: #A98BE0;
59
+ --pos-tone-sky: #4FA3DE;
60
+ --pos-tone-periwinkle: #7B86E4;
61
+ --pos-tone-blush: #E0719E;
62
+ --pos-tone-sand: #C9A268;
63
+ --pos-tone-teal: #3FB8A0;
64
+ --pos-tone-rose: #E07A6B;
58
65
 
59
66
  /* --- shape: softer than an instrument panel, tighter than a phone app -- */
60
67
  --pos-r: 10px;
@@ -74,6 +81,8 @@
74
81
  their hairlines and baselines missed each other by a few pixels — enough
75
82
  to read as a seam. */
76
83
  --pos-header-h: 72px;
84
+ /* Altura da barra de navegação do celular. Ver a casca de celular abaixo. */
85
+ --pos-navbar-h: 58px;
77
86
 
78
87
  --pos-t: 140ms;
79
88
  --pos-e: cubic-bezier(0.2, 0, 0, 1);
@@ -239,6 +248,11 @@
239
248
  Retaguarda de quem abre o PDV de dentro de um back office). */
240
249
  .pos-rail__top { padding: var(--pos-2x) var(--pos-1x) 0; }
241
250
 
251
+ /* A busca, logo abaixo daquela chave. Só existe com a trilha por extenso: numa
252
+ trilha de 68px de ícones o campo não cabe, e ali ele volta para a faixa da
253
+ view (ver `useWideRail` na tela). */
254
+ .pos-rail__search { padding: var(--pos-2x) var(--pos-1x) var(--pos-1x); }
255
+
242
256
  /* O disco com a inicial de quem atende. Sobrou da lista de turno que saiu do
243
257
  rail — a escolha do atendente ao abrir a mesa ainda o usa. */
244
258
  .pos-person__av {
@@ -302,16 +316,54 @@
302
316
  /* No page title here. The rail's highlighted entry already says which screen
303
317
  this is, and repeating it across the top is a breadcrumb for a product with
304
318
  nothing to breadcrumb — one level, no hierarchy, no way to get lost. */
319
+ /* Três colunas, laterais com a MESMA medida (1fr): é isso que põe a busca no
320
+ meio da faixa, e não no meio do que sobra depois do título. Com flex +
321
+ `margin: auto` o campo andaria para os lados conforme o título ficasse mais
322
+ curto ou as abas de área aparecessem — e elas são condicionais. */
305
323
  .pos-head {
306
- display: flex; align-items: center; gap: var(--pos-3x);
307
- height: var(--pos-header-h); padding: 0 var(--pos-4x);
324
+ display: grid;
325
+ /* `auto | 1fr | auto`, e não três colunas simétricas: com os dois
326
+ segmentados à direita, a coluna de 420px do meio ficava espremida entre
327
+ eles e o título, e a busca — que é a casa do cursor — encolhia para ~200px
328
+ enquanto sobrava vão à esquerda. Aqui os lados pedem o que precisam e o
329
+ campo fica com o resto. */
330
+ grid-template-columns: auto minmax(0, 1fr) auto;
331
+ align-items: center; gap: var(--pos-3x);
332
+ height: var(--pos-header-h);
333
+ /* A direita cede o canto que o hospedeiro ocupa. Aberto dentro de um
334
+ Takeover, o ✕ dele flutua sobre esta faixa — e a busca, que vai de ponta a
335
+ ponta, ficava por baixo do botão. Fora de um takeover a variável não
336
+ existe e o valor cai em 0. */
337
+ padding: 0 calc(var(--pos-4x) + var(--fz-takeover-inset-right, 0px)) 0 var(--pos-4x);
308
338
  border-bottom: 1px solid var(--pos-line);
309
339
  }
310
- .pos-head__title { font-size: 17px; font-weight: 650; letter-spacing: -0.015em; white-space: nowrap; }
340
+ /* Coluna 1: a view e a unidade, lado a lado. `min-width: 0` para que seja o
341
+ TÍTULO a encurtar com reticências, e não a unidade — o nome da unidade é o
342
+ dado que não se adivinha, e numa rede ler o caixa errado é o erro mais caro
343
+ que esta tela oferece. */
344
+ .pos-head__where {
345
+ grid-column: 1;
346
+ display: flex; align-items: center; gap: var(--pos-2x); min-width: 0;
347
+ }
348
+ .pos-head__title {
349
+ min-width: 0; flex: 0 1 auto;
350
+ font-size: 17px; font-weight: 650; letter-spacing: -0.015em;
351
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
352
+ }
311
353
 
312
354
  .pos-seg { display: inline-flex; gap: 2px; padding: 3px; background: var(--pos-2); border-radius: 999px; }
313
- /* As áreas encostam na direita; o título fica na esquerda. */
314
- .pos-head > .pos-seg { margin-left: auto; }
355
+ /* Título à esquerda, busca no meio, e à direita TODOS os segmentados — os de
356
+ forma (Grade|Planta) e os de área (Salão|Balcão|…) numa fila só, e não um
357
+ empurrando o outro para uma segunda linha. */
358
+ .pos-head__views {
359
+ grid-column: 3; justify-self: end;
360
+ display: flex; align-items: center; gap: var(--pos-2x); min-width: 0;
361
+ }
362
+ .pos-head__views:empty { display: none; }
363
+ /* A busca só cai na faixa quando a trilha colapsa (<=1180px). Acima disso a
364
+ coluna do meio fica vazia e o `1fr` dela é justamente o ar entre o título e
365
+ os segmentados. */
366
+ .pos-head > .pos-field { grid-column: 2; }
315
367
  .pos-seg__btn {
316
368
  min-height: 36px; padding: 0 var(--pos-4x);
317
369
  border: none; border-radius: 999px; background: transparent;
@@ -327,9 +379,9 @@
327
379
  and cost a tap plus the sight of the control that answers "where is this
328
380
  item" — code+Enter is the fastest path in the product, so the field it
329
381
  types into must never be something to go find. */
330
- /* Largura da trilha, não da barra: o campo virou linha do rail (a mesma
331
- posição que a busca ocupa no back office), então ele preenche a coluna. */
332
- width: 100%; min-width: 0; margin: var(--pos-2x) var(--pos-1x);
382
+ /* Preenche a coluna do meio da faixa, que já é quem define a largura (e o
383
+ centro). Sem margem própria: a `gap` do grid é que separa. */
384
+ width: 100%; min-width: 0; margin: 0;
333
385
  height: 36px; padding: 0 var(--pos-2x);
334
386
  /* Superfície tonal, sem contorno — a mesma leitura da busca do back office.
335
387
  Numa trilha escura um 1px é uma aresta dura brigando com todas as outras;
@@ -398,10 +450,62 @@
398
450
  /* No edge here. The status pill on the row already says it, and a coloured
399
451
  bar plus a coloured pill is the same fact charged twice. */
400
452
  .pos-dchip::before { display: none; }
401
- .pos-tbl[data-status='free'] { --pos-edge: var(--pos-ok); }
402
- .pos-tbl[data-status='occupied'] { --pos-edge: var(--pos-warn); }
403
- .pos-tbl[data-status='bill_requested'] { --pos-edge: var(--pos-info); }
404
- .pos-tbl[data-status='attention'] { --pos-edge: var(--pos-danger); }
453
+ /* O cartão INTEIRO na cor do estado, não uma listra de 4px.
454
+ *
455
+ * É a convenção do setor (SaiPOS, TakeAt, Colibri): o mapa de mesas é lido de
456
+ * pé, a três metros, de relance e no meio do salão — e a essa distância o que
457
+ * chega é a mancha de cor, não uma faixa fina na borda nem uma pastilha de
458
+ * 12px. Com a listra, "ocupada" e "conta pedida" eram dois cartões cinzas com
459
+ * detalhes diferentes; pintados, são dois blocos que a periferia da visão já
460
+ * separa antes de o garçom focar em qualquer um.
461
+ *
462
+ * LIVRE fica sem pintura de propósito: se toda mesa tem cor, nenhuma cor chama.
463
+ * O que precisa saltar é o que pede AÇÃO — ocupada, conta pedida, atenção —, e
464
+ * a mesa livre é o fundo contra o qual as outras se destacam.
465
+ *
466
+ * Texto escuro sobre âmbar e branco sobre azul/vermelho: são os mesmos pares
467
+ * que a planta já usa, e os que passam contraste. A pastilha continua escrita,
468
+ * porque cor sozinha nunca carrega o estado — 8% dos homens não separa
469
+ * vermelho de verde, e um deles está no salão. */
470
+ .pos-tbl[data-status='free'] {
471
+ --pos-edge: var(--pos-ok);
472
+ }
473
+ .pos-tbl[data-status='occupied'] {
474
+ --pos-edge: var(--pos-warn);
475
+ background: var(--pos-warn); border-color: var(--pos-warn); color: var(--pos-on-light);
476
+ }
477
+ .pos-tbl[data-status='bill_requested'] {
478
+ --pos-edge: var(--pos-info);
479
+ background: var(--pos-info); border-color: var(--pos-info); color: #fff;
480
+ }
481
+ .pos-tbl[data-status='attention'] {
482
+ --pos-edge: var(--pos-danger);
483
+ background: var(--pos-danger); border-color: var(--pos-danger); color: #fff;
484
+ }
485
+
486
+ /* Dentro de um cartão pintado, todo texto herda a cor da frente: os cinzas
487
+ (`--pos-fg-2/3`) foram calculados contra o fundo escuro e somem no âmbar. */
488
+ .pos-tbl[data-status='occupied'] :is(.pos-tbl__seats, .pos-tbl__who, .pos-tbl__time, .pos-tbl__total),
489
+ .pos-tbl[data-status='bill_requested'] :is(.pos-tbl__seats, .pos-tbl__who, .pos-tbl__time, .pos-tbl__total),
490
+ .pos-tbl[data-status='attention'] :is(.pos-tbl__seats, .pos-tbl__who, .pos-tbl__time, .pos-tbl__total) {
491
+ color: inherit; opacity: 0.85;
492
+ }
493
+ .pos-tbl[data-status='occupied'] .pos-tbl__total,
494
+ .pos-tbl[data-status='bill_requested'] .pos-tbl__total,
495
+ .pos-tbl[data-status='attention'] .pos-tbl__total { opacity: 1; }
496
+
497
+ /* A pastilha, sobre o cartão pintado, vira um vidro do próprio tom em vez de
498
+ um segundo bloco de cor brigando com o primeiro. */
499
+ .pos-tbl[data-status='occupied'] .pos-badge,
500
+ .pos-tbl[data-status='bill_requested'] .pos-badge,
501
+ .pos-tbl[data-status='attention'] .pos-badge {
502
+ background: rgba(0, 0, 0, 0.18); color: inherit;
503
+ }
504
+
505
+ /* O hover não pode clarear um cartão já pintado — ele apagaria o estado. */
506
+ .pos-tbl[data-status='occupied']:hover,
507
+ .pos-tbl[data-status='bill_requested']:hover,
508
+ .pos-tbl[data-status='attention']:hover { filter: brightness(1.06); }
405
509
 
406
510
  .pos-badge {
407
511
  display: inline-flex; align-items: center; gap: 5px; align-self: flex-start;
@@ -498,6 +602,24 @@
498
602
  .pos-item[data-filled='true']::before { width: 100%; }
499
603
  /* Content rides above the growing edge. */
500
604
  .pos-item > * { position: relative; z-index: 1; }
605
+
606
+ /* A foto sangra até as bordas do cartão: margens negativas para cancelar o
607
+ padding, inclusive os 5px extras que a faixa lateral reserva. Um retângulo
608
+ flutuando com respiro em volta leria como um anexo, não como o cartão.
609
+ `aspect-ratio` fixo para que fotos de proporções diferentes não façam a
610
+ grade serrilhar, e `object-fit: cover` para cortar em vez de distorcer —
611
+ um prato achatado é pior que um prato aparado.
612
+ Ela some quando o item entra na comanda: com a faixa tonal preenchendo o
613
+ cartão inteiro, a foto por cima brigaria com o estado que ela anuncia. */
614
+ /* Miniatura + identidade lado a lado; o passo-a-passo segue embaixo, inteiro. */
615
+ .pos-item__top { display: flex; align-items: flex-start; gap: var(--pos-2x); min-width: 0; }
616
+ .pos-item__id { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
617
+ .pos-item__img {
618
+ flex: none; display: block; overflow: hidden;
619
+ width: 52px; height: 52px; border-radius: var(--pos-r-sm, 8px);
620
+ background: var(--pos-3);
621
+ }
622
+ .pos-item__img img { width: 100%; height: 100%; object-fit: cover; display: block; }
501
623
  .pos-item { cursor: pointer; }
502
624
  .pos-item:hover { background: var(--pos-3); }
503
625
  .pos-item:active { transform: scale(0.99); }
@@ -571,6 +693,16 @@
571
693
 
572
694
  /* --- right rail: order + table detail (permanent) ----------------------- */
573
695
 
696
+ .pos-aside__grab { display: none; }
697
+ .pos-sheet-scrim { display: none; }
698
+
699
+ .pos-tkt__collapse {
700
+ display: none; margin-left: auto; place-items: center;
701
+ height: 34px; width: 34px; border: none; border-radius: 999px;
702
+ background: none; color: var(--pos-fg-2); cursor: pointer;
703
+ }
704
+ .pos-tkt__collapse:hover { background: var(--pos-2); color: var(--pos-fg); }
705
+
574
706
  .pos-aside {
575
707
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
576
708
  background: var(--pos-1); border-left: 1px solid var(--pos-line);
@@ -627,11 +759,13 @@
627
759
 
628
760
 
629
761
  .pos-ln {
630
- display: grid; grid-template-columns: 22px minmax(0, 1fr) auto auto;
762
+ /* A coluna do marcador encolheu de 22px para 14px: com o ponto no lugar da
763
+ bolha, o resto era recuo pago pelo NOME do prato, que é o que se lê. */
764
+ display: grid; grid-template-columns: 14px minmax(0, 1fr) auto auto;
631
765
  align-items: center; gap: var(--pos-2x);
632
- min-height: 56px; margin: 0 var(--pos-3x) var(--pos-2x);
633
- padding: var(--pos-2x) var(--pos-3x);
634
- border: 1px solid transparent; border-radius: var(--pos-r);
766
+ min-height: 58px; margin: 0 var(--pos-3x) var(--pos-2x);
767
+ padding: var(--pos-2x) var(--pos-3x) var(--pos-2x) var(--pos-2x);
768
+ border: 1px solid var(--pos-line); border-radius: var(--pos-r);
635
769
  background: var(--pos-2); text-align: left; cursor: pointer;
636
770
  transition: background var(--pos-t) var(--pos-e), border-color var(--pos-t) var(--pos-e);
637
771
  }
@@ -639,7 +773,7 @@
639
773
  .pos-ln:hover { background: var(--pos-3); }
640
774
  .pos-ln[aria-current='true'] { background: var(--pos-3); border-color: var(--pos-line-strong); }
641
775
  .pos-ln__i { font-size: 12px; font-weight: 650; color: var(--pos-fg-3); }
642
- .pos-ln__n { display: block; font-size: 14px; font-weight: 550; line-height: 1.3; }
776
+ .pos-ln__n { display: block; font-size: 14.5px; font-weight: 600; line-height: 1.25; letter-spacing: -0.005em; }
643
777
  .pos-ln__note { display: block; font-size: 12px; color: var(--pos-fg-2); }
644
778
  .pos-ln__p { font-size: 14px; font-weight: 600; min-width: 78px; text-align: right; }
645
779
  .pos-ln .pos-step__b { border-color: var(--pos-line-strong); opacity: 1; color: var(--pos-fg); width: 32px; height: 32px; }
@@ -782,8 +916,6 @@
782
916
  .pos-rail { padding: var(--pos-3x) var(--pos-2x); }
783
917
  .pos-rail__label { display: none; }
784
918
  .pos-rail__top { display: none; }
785
- .pos-field { justify-content: center; padding: 0; margin: var(--pos-2x) 0; }
786
- .pos-field input, .pos-field__hint, .pos-field__clear { display: none; }
787
919
  .pos-nav__item { justify-content: center; padding: 0; }
788
920
  .pos-nav__text { display: none; }
789
921
  .pos-nav__count { position: absolute; top: 4px; right: 6px; margin: 0; }
@@ -803,8 +935,319 @@
803
935
  @media (max-width: 640px) {
804
936
  .pos-floor { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); padding: var(--pos-3x); }
805
937
  .pos-items { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); padding: var(--pos-3x); }
806
- .pos-head { flex-wrap: wrap; }
807
- .pos-field { max-width: none; margin-left: 0; width: 100%; }
938
+ /* No telefone a faixa vira duas linhas: título (com as áreas ao lado) em
939
+ cima, busca inteira embaixo. Centralizar num espaço que não sobra só
940
+ espremeria o campo. */
941
+ .pos-head {
942
+ grid-template-columns: 1fr auto;
943
+ height: auto; padding-top: var(--pos-2x); padding-bottom: var(--pos-2x);
944
+ }
945
+ .pos-head__where { grid-column: 1; }
946
+ .pos-head > .pos-field { grid-column: 1 / -1; }
947
+ }
948
+
949
+
950
+ /* --- casca de celular: abas no rodapé + gaveta "Mais" ---------------------
951
+ Portado do PDV do V1 (docs/v1-guide/restaurante/pdv-balcao no beautyplace).
952
+ Acima de 640px nada disto existe: a trilha da esquerda volta a ser a
953
+ navegação, que é o desenho certo para um terminal de balcão de 15".
954
+ -------------------------------------------------------------------------- */
955
+ .pos-navbar { display: none; }
956
+ .pos-more { display: none; }
957
+
958
+ @media (max-width: 640px) {
959
+ /* A trilha SAI. Colapsada em 60px de ícones sem rótulo ela ocupava a coluna
960
+ mais difícil de alcançar com o polegar para dizer menos do que a barra de
961
+ baixo diz com a mesma área. */
962
+ .pos-rail { display: none; }
963
+ .pos-body { grid-template-columns: minmax(0, 1fr); }
964
+
965
+ /* A tira de mesas abertas também SAI. Na tela larga ela é o atalho para o
966
+ que está em andamento enquanto se olha outra coisa; no celular a Grade já
967
+ é essa lista — número, quem atende, há quanto tempo e quanto consumiu, nos
968
+ mesmos cartões — e a tira repetia tudo numa faixa que rola de lado, logo
969
+ acima das abas, que é justamente onde a barra da comanda precisa morar. */
970
+ .pos-dock { display: none; }
971
+
972
+ /* O título da view SAI da faixa: a aba acesa no rodapé já diz "Salão", e
973
+ repetir o nome gastava a primeira linha da tela mais estreita que o
974
+ produto tem. É o que o V1 faz — lá a faixa é só a busca. */
975
+ .pos-head__where { display: none; }
976
+ /* Busca em cima ocupando a largura inteira, áreas na linha de baixo — a
977
+ forma do V1. Dividindo a linha com o segmentado, "Buscar" cabia em 100px
978
+ e virava "Bu…": o campo que é a casa do cursor não pode ser o que encolhe. */
979
+ .pos-head {
980
+ grid-template-columns: minmax(0, 1fr);
981
+ height: auto; padding-top: var(--pos-2x); padding-bottom: var(--pos-2x);
982
+ }
983
+ .pos-head > .pos-field { grid-column: 1; }
984
+ /* Os dois segmentados dividem UMA linha: empilhados, busca + forma + área
985
+ gastavam três faixas antes do primeiro cartão de mesa. */
986
+ /* --- categorias no celular: FILA, não grade ----------------------------
987
+ Uma casa com 18 categorias (e há: "Bebidas", "Couvert", "Almoço
988
+ Executivo", "Categoria iFood"…) enchia a tela inteira de filtro, e o
989
+ primeiro item do cardápio começava abaixo da dobra — a tela do cardápio
990
+ sem nenhum prato à vista.
991
+ Em fila, as categorias ocupam uma faixa de 44px e os itens começam logo
992
+ abaixo; escolher uma é rolar de lado, que é o gesto que a faixa anuncia.
993
+ É o desenho do V1, e ele existe por este motivo. */
994
+ .pos-cats {
995
+ /* TRÊS LINHAS, colunas correndo para o lado: nove categorias à vista e o
996
+ resto a um deslize. A fila de uma linha só resolvia a altura e cobrava
997
+ caro em rolagem — com 19 categorias, achar "Sobremesas" era passar por
998
+ quinze. Em 3×3 o mesmo espaço mostra três vezes mais, e o que sobra
999
+ continua na direção que a faixa anuncia. */
1000
+ /* `flex: none` ANTES de tudo: a faixa é item de um flex em coluna (o
1001
+ contêiner do CatalogView), e item de flex ENCOLHE por padrão. Com 19
1002
+ categorias e a grade de itens disputando o mesmo eixo, ela era
1003
+ espremida até virar a tira de cor de 8px — as três linhas de 42px
1004
+ estavam lá; o contêiner é que as achatava. */
1005
+ flex: none;
1006
+ display: grid; grid-auto-flow: column;
1007
+ /* `none` obrigatório: a regra base declara `grid-template-columns`, e
1008
+ coluna EXPLÍCITA vence a automática — sem zerar, as primeiras colunas
1009
+ continuavam com a medida da grade antiga e a paginação de três não
1010
+ acontecia. */
1011
+ grid-template-columns: none;
1012
+ /* Altura de linha DEFINIDA, não `1fr`: a faixa vive dentro de um
1013
+ contêiner de rolagem sem altura própria, e `1fr` ali resolve contra
1014
+ espaço indefinido — as três linhas colapsavam para alguns pixels e
1015
+ sobrava uma tira de cor no topo do cardápio. */
1016
+ grid-template-rows: repeat(3, 42px);
1017
+ align-content: start;
1018
+ grid-auto-columns: calc((100% - var(--pos-2x) * 2) / 3);
1019
+ overflow-x: auto;
1020
+ gap: var(--pos-2x); padding: var(--pos-2x) var(--pos-3x);
1021
+ /* `mandatory` e não `proximity`: a grade tem páginas de três colunas, e
1022
+ parar no meio de uma corta a terceira ao meio em toda parada. */
1023
+ scroll-snap-type: x mandatory;
1024
+ -webkit-overflow-scrolling: touch;
1025
+ scrollbar-width: none;
1026
+ }
1027
+ .pos-cats::-webkit-scrollbar { display: none; }
1028
+ .pos-cat {
1029
+ min-height: 0; height: 100%; min-width: 0;
1030
+ padding: 0 var(--pos-2x); border-width: 1px;
1031
+ scroll-snap-align: start;
1032
+ }
1033
+ /* Nome em duas linhas no máximo: "Guarnição s-acompanhamentos" existe, e numa
1034
+ coluna de um terço da tela ele precisa quebrar em vez de estourar. */
1035
+ .pos-cat__name {
1036
+ white-space: normal; display: -webkit-box;
1037
+ -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
1038
+ line-height: 1.15;
1039
+ }
1040
+ /* O ícone sai: numa pílula de 40px ele é o que sobra depois do nome, e o
1041
+ nome é o que se procura. */
1042
+ .pos-cat__icon { display: none; }
1043
+ .pos-cat__name { font-size: 12.5px; }
1044
+ .pos-cat__n { font-size: 10.5px; }
1045
+ /* Sem régua: a faixa já se separa do que vem abaixo pela própria cor. */
1046
+ .pos-cats-rule { display: none; }
1047
+
1048
+ .pos-head__views { grid-column: 1; justify-self: stretch; }
1049
+ .pos-head__views .pos-seg { flex: 1; min-width: 0; }
1050
+ .pos-head__views .pos-seg__btn { flex: 1; min-width: 0; padding: 0 var(--pos-2x); }
1051
+ /* Um segmentado só no celular (as áreas): o de Grade|Planta não é montado
1052
+ aqui — ver `usePhone` em PosScreen. */
1053
+
1054
+ /* O rodapé é fixo, então o conteúdo precisa terminar acima dele — inclusive
1055
+ do indicador de home do iPhone. */
1056
+ .pos-main { padding-bottom: calc(var(--pos-navbar-h) + env(safe-area-inset-bottom)); }
1057
+
1058
+ .pos-navbar {
1059
+ position: fixed; inset: auto 0 0 0; z-index: 40;
1060
+ display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
1061
+ height: var(--pos-navbar-h); padding-bottom: env(safe-area-inset-bottom);
1062
+ background: var(--pos-1); border-top: 1px solid var(--pos-line);
1063
+ }
1064
+ .pos-navbar__item {
1065
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
1066
+ gap: 3px; border: none; background: none; cursor: pointer;
1067
+ color: var(--pos-fg-3); font: inherit;
1068
+ transition: color var(--pos-t) var(--pos-e);
1069
+ }
1070
+ .pos-navbar__item[aria-current='page'] { color: var(--pos-accent); }
1071
+ .pos-navbar__icon { position: relative; display: grid; place-items: center; }
1072
+ .pos-navbar__text { font-size: 11px; font-weight: 550; letter-spacing: 0.01em; }
1073
+ .pos-navbar__count {
1074
+ position: absolute; top: -4px; right: -10px;
1075
+ min-width: 16px; padding: 0 4px; border-radius: 999px;
1076
+ background: var(--pos-accent); color: var(--pos-accent-fg);
1077
+ font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
1078
+ }
1079
+
1080
+ .pos-more { display: block; position: fixed; inset: 0; z-index: 50; }
1081
+ .pos-more__scrim {
1082
+ position: absolute; inset: 0; border: none; padding: 0;
1083
+ background: rgba(0, 0, 0, 0.55); cursor: pointer;
1084
+ }
1085
+ /* Entra pela ESQUERDA, de onde a trilha saiu: é a mesma navegação, guardada
1086
+ no mesmo lado em que ela morava na tela larga. */
1087
+ .pos-more__panel {
1088
+ position: absolute; inset: 0 auto 0 0; width: min(19rem, 86vw);
1089
+ display: flex; flex-direction: column; gap: var(--pos-3x);
1090
+ padding: var(--pos-4x) var(--pos-3x);
1091
+ padding-bottom: calc(var(--pos-4x) + env(safe-area-inset-bottom));
1092
+ background: var(--pos-1); border-right: 1px solid var(--pos-line);
1093
+ animation: pos-more-in 220ms var(--pos-e);
1094
+ }
1095
+ @keyframes pos-more-in { from { transform: translateX(-100%) } to { transform: none } }
1096
+ @media (prefers-reduced-motion: reduce) {
1097
+ .pos-more__panel { animation: none; }
1098
+ }
1099
+ .pos-more__head { display: flex; align-items: center; gap: var(--pos-2x); }
1100
+ .pos-more__close {
1101
+ margin-left: auto; display: grid; place-items: center;
1102
+ height: 36px; width: 36px; border: none; border-radius: 999px;
1103
+ background: none; color: var(--pos-fg-2); cursor: pointer;
1104
+ }
1105
+ .pos-more__close:hover { background: var(--pos-2); color: var(--pos-fg); }
1106
+ .pos-more__nav { display: flex; flex-direction: column; gap: 2px; }
1107
+
1108
+ /* A regra de trilha COLAPSADA (@1180px) esconde rótulo, centraliza o ícone e
1109
+ joga o badge no canto — desenho certo para uma coluna de 68px, e errado
1110
+ aqui dentro, onde há 19rem e o item é uma linha de menu. Ela alcança o
1111
+ celular por largura, então é desfeita explicitamente na gaveta. */
1112
+ .pos-more .pos-nav__text,
1113
+ .pos-more .pos-brand__name,
1114
+ .pos-more .pos-user__txt { display: inline; }
1115
+ .pos-more .pos-nav__item {
1116
+ justify-content: flex-start;
1117
+ gap: var(--pos-2x);
1118
+ padding: var(--pos-2x) var(--pos-2x);
1119
+ }
1120
+ .pos-more .pos-nav__count {
1121
+ position: static; margin-left: auto;
1122
+ }
1123
+ .pos-more .pos-brand__name { font-size: 15px; font-weight: 650; }
1124
+ .pos-more .pos-user__txt { font-size: 13px; color: var(--pos-fg-2); }
1125
+ .pos-more__foot {
1126
+ margin-top: auto; display: flex; align-items: center; gap: var(--pos-2x);
1127
+ padding-top: var(--pos-3x); border-top: 1px solid var(--pos-line);
1128
+ }
1129
+
1130
+ /* --- a comanda no celular ---------------------------------------------
1131
+ Fechada, é uma barra acima das abas. Aberta, é a TELA INTEIRA — e não o
1132
+ painel de 88vw do tablet: lançar item e conferir a conta são dois momentos
1133
+ diferentes, e o segundo não divide atenção com o catálogo atrás. */
1134
+ .pos-cartbar {
1135
+ position: fixed; z-index: 41;
1136
+ inset: auto 0 calc(var(--pos-navbar-h) + env(safe-area-inset-bottom)) 0;
1137
+ display: flex; align-items: center; gap: var(--pos-3x);
1138
+ padding: var(--pos-3x) var(--pos-4x);
1139
+ border: none; border-top: 1px solid color-mix(in srgb, var(--pos-accent) 40%, transparent);
1140
+ background: var(--pos-accent); color: var(--pos-accent-fg);
1141
+ font: inherit; text-align: left; cursor: pointer;
1142
+ }
1143
+ .pos-cartbar__id { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
1144
+ .pos-cartbar__where { font-size: 14px; font-weight: 650; }
1145
+ .pos-cartbar__meta { font-size: 12px; opacity: 0.75; }
1146
+ .pos-cartbar__total { font-size: 17px; font-weight: 700; }
1147
+
1148
+ .pos-aside {
1149
+ position: fixed; inset: 0; width: 100%; z-index: 60;
1150
+ transform: translateY(100%); box-shadow: none;
1151
+ transition: transform 240ms var(--pos-e);
1152
+ }
1153
+ .pos-aside[data-open='true'] { transform: none; }
1154
+ /* A alça É o controle de minimizar; a seta ao lado seria o mesmo verbo
1155
+ duas vezes, e no canto onde o hospedeiro põe o ✕ de sair do PDV. */
1156
+ .pos-tkt__collapse { display: none; }
1157
+
1158
+ /* Pedido | Atividade some no celular. Em tela cheia a comanda JÁ é a tela —
1159
+ não há um segundo painel disputando com ela, e duas abas logo acima do
1160
+ "Fechar conta" competem com o único botão que importa ali. O histórico
1161
+ continua na coluna larga, onde há espaço para os dois. */
1162
+ .pos-aside .pos-tabs { display: none; }
1163
+
1164
+ /* A comanda é uma FOLHA que sobe, com alça para arrastar de volta — o gesto
1165
+ padrão de celular. Assim ela não encosta no topo, o ✕ do hospedeiro segue
1166
+ visível e alcançável, e o caminho de volta deixa de ser um botão disputando
1167
+ canto com ele: é puxar para baixo, que é o que a mão já sabe fazer. */
1168
+ /* Vidro: o fundo é translúcido e o que passa por baixo é DESFOCADO, não
1169
+ apagado — a folha fica por cima do catálogo sem fingir que ele sumiu, e o
1170
+ contexto de onde o item veio continua legível na borda. O raio é grande
1171
+ (22px) porque num canto de 16px sobre conteúdo o arredondamento some. */
1172
+ /* Com a folha aberta, o ✕ de sair do PDV SOME.
1173
+ Ele fica no canto para onde a mão vai quando quer fechar o que está na
1174
+ frente — e o que está na frente é a folha. Clicando ali o operador sairia
1175
+ do PDV inteiro no meio de uma comanda, que é um erro caro e silencioso.
1176
+ Some enquanto a folha existe; volta quando ela desce. */
1177
+ .saas-takeover-panel:has(.pos[data-cart-open]) ~ .saas-takeover-close { display: none; }
1178
+
1179
+ .pos-sheet-scrim {
1180
+ display: block; position: fixed; inset: 0; z-index: 55;
1181
+ border: none; padding: 0;
1182
+ background: rgba(0, 0, 0, 0.45);
1183
+ -webkit-backdrop-filter: blur(10px) saturate(115%);
1184
+ backdrop-filter: blur(10px) saturate(115%);
1185
+ }
1186
+ .pos-aside {
1187
+ top: max(4rem, env(safe-area-inset-top));
1188
+ border-radius: 22px 22px 0 0;
1189
+ border-top: 1px solid rgba(255, 255, 255, 0.10);
1190
+ background: color-mix(in srgb, var(--pos-1) 88%, transparent);
1191
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
1192
+ backdrop-filter: blur(24px) saturate(160%);
1193
+ box-shadow: 0 -18px 48px rgba(0, 0, 0, 0.55);
1194
+ }
1195
+ /* A alça: 36×5 centrada, com respiro acima e abaixo — a medida do iOS, e a
1196
+ que o polegar reconhece como "isto se arrasta" sem nenhum rótulo. */
1197
+ .pos-aside__grab {
1198
+ display: flex; align-items: center; justify-content: center;
1199
+ height: 26px; flex: none; cursor: grab; touch-action: none;
1200
+ }
1201
+ .pos-aside__grab::before {
1202
+ content: ''; width: 36px; height: 5px; border-radius: 999px;
1203
+ background: rgba(255, 255, 255, 0.28);
1204
+ }
1205
+ .pos-aside[data-dragging='true'] { transition: none; }
1206
+ .pos-aside[data-dragging='true'] .pos-aside__grab { cursor: grabbing; }
1207
+ @media (prefers-reduced-motion: reduce) {
1208
+ .pos-aside { transition: none; }
1209
+ }
1210
+ }
1211
+
1212
+ /* O "fechar mesmo assim" com o aviso embutido: duas linhas num alvo só, em
1213
+ vez de uma faixa de alerta acima e um botão abaixo dizendo o mesmo número. */
1214
+ .pos-btn--warn { flex-direction: column; gap: 2px; padding-top: 8px; padding-bottom: 8px; height: auto; }
1215
+ .pos-btn__l1 { font-size: 14px; font-weight: 600; }
1216
+ .pos-btn__l2 {
1217
+ display: inline-flex; align-items: center; gap: 5px;
1218
+ font-size: 11.5px; font-weight: 550; color: var(--pos-warn);
1219
+ }
1220
+
1221
+ /* "Abrindo no nome de X" — a linha que substituiu a lista de atendentes. */
1222
+ .pos-sheet__who {
1223
+ margin: var(--pos-2x) 0 0; font-size: 13px; color: var(--pos-fg-2);
1224
+ }
1225
+ .pos-sheet__who strong { color: var(--pos-fg); font-weight: 650; }
1226
+
1227
+ /* --- esqueleto de carregamento ------------------------------------------
1228
+ Reusa a casca real (`.pos-tbl`, `.pos-item`, `.pos-cat`) e troca só o
1229
+ conteúdo por barras: a grade que aparece é a mesma que vai ficar, então
1230
+ quando o dado chega nada se desloca. */
1231
+ .pos-skel { pointer-events: none; cursor: default; }
1232
+ .pos-skel::before { display: none; }
1233
+ .pos-skel__b {
1234
+ display: block; border-radius: 6px;
1235
+ background: linear-gradient(
1236
+ 90deg,
1237
+ var(--pos-3) 25%,
1238
+ color-mix(in srgb, var(--pos-4) 70%, transparent) 37%,
1239
+ var(--pos-3) 63%
1240
+ );
1241
+ background-size: 320% 100%;
1242
+ animation: pos-skel-wipe 1.25s ease-in-out infinite;
1243
+ }
1244
+ @keyframes pos-skel-wipe {
1245
+ from { background-position: 120% 0 }
1246
+ to { background-position: -20% 0 }
1247
+ }
1248
+ /* Quem pediu menos movimento recebe a mesma forma, parada. */
1249
+ @media (prefers-reduced-motion: reduce) {
1250
+ .pos-skel__b { animation: none; background: var(--pos-3); }
808
1251
  }
809
1252
 
810
1253
  /* --- floor: plan layout -------------------------------------------------
@@ -968,9 +1411,13 @@
968
1411
 
969
1412
 
970
1413
  .pos-ln__state {
971
- width: 22px; height: 22px; flex: none;
1414
+ width: 14px; height: 22px; flex: none;
972
1415
  display: grid; place-items: center; border-radius: 50%;
973
- background: var(--pos-3); color: var(--pos-fg-2);
1416
+ color: var(--pos-fg-3);
1417
+ }
1418
+ .pos-ln[data-state='pending'] .pos-ln__state::before {
1419
+ content: ''; width: 8px; height: 8px; border-radius: 50%;
1420
+ background: currentColor;
974
1421
  }
975
1422
  /* Pending is the only state that still needs someone to do something, so it
976
1423
  carries the edge and the marker. It does NOT carry a word: right after
@@ -986,11 +1433,19 @@
986
1433
  }
987
1434
  .pos-ln[data-state='pending'] { padding-left: calc(var(--pos-4x) + 4px); }
988
1435
  .pos-ln[data-state='pending'] .pos-ln__state {
989
- background: var(--pos-warn); color: #14100A;
1436
+ /* Um PONTO, não uma bolha com anel dentro: a linha já carrega a faixa âmbar
1437
+ na borda esquerda, e o marcador só confirma. Menor que o ícone dos outros
1438
+ estados de propósito — "ainda não saiu" é ausência de evento, e desenhar
1439
+ ausência com mais tinta que presença inverte a leitura. */
1440
+ background: transparent; color: var(--pos-warn);
1441
+ box-shadow: none;
990
1442
  }
991
1443
  .pos-ln[data-state='fired'] .pos-ln__state { background: color-mix(in srgb, var(--pos-warn) 22%, transparent); }
992
1444
  .pos-ln[data-state='delivered'] .pos-ln__state { background: color-mix(in srgb, var(--pos-ok) 22%, transparent); }
993
- .pos-ln[data-state='pending'] .pos-ln__state { color: var(--pos-fg-3); }
1445
+ /* O ponto é `currentColor`, então a cor AQUI é a cor do marcador. Cinza
1446
+ sobrava de quando pendente era uma bolha âmbar com glifo escuro — com
1447
+ a bolha fora, o cinza apagava justamente o estado que pede ação. */
1448
+ .pos-ln[data-state='pending'] .pos-ln__state { color: var(--pos-warn); }
994
1449
  .pos-ln[data-state='fired'] .pos-ln__state { color: var(--pos-warn); }
995
1450
  .pos-ln[data-state='delivered'] .pos-ln__state { color: var(--pos-ok); }
996
1451
  /* A delivered line is history: it stays readable but stops competing with the
@@ -1413,6 +1868,18 @@
1413
1868
  border: none; border-radius: var(--pos-r);
1414
1869
  background: var(--pos-danger); color: #fff;
1415
1870
  font-size: 12px; font-weight: 650; cursor: pointer;
1871
+ /* Escondido enquanto ninguém arrasta.
1872
+ Ele mora ATRÁS da linha e era desenhado sempre, contando com a linha para
1873
+ cobri-lo. Ao trocar de mesa, a comanda inteira remonta: o fundo vermelho
1874
+ pinta um quadro antes de a linha nova chegar por cima, e o que se via era
1875
+ um flash vermelho em cada troca. Invisível por padrão, ele não tem o que
1876
+ vazar — e `pointer-events` acompanha para não roubar o toque da linha. */
1877
+ opacity: 0; pointer-events: none;
1878
+ transition: opacity 120ms var(--pos-e);
1879
+ }
1880
+ .pos-ln-wrap[data-open='true'] .pos-swipe-bg,
1881
+ .pos-ln-wrap[data-swiping='true'] .pos-swipe-bg {
1882
+ opacity: 1; pointer-events: auto;
1416
1883
  }
1417
1884
  .pos-swipe-bg:hover { filter: brightness(1.08); }
1418
1885
  /* The row rides above it; sliding left is what uncovers it. */
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "fayz": {
4
4
  "status": "preview"
5
5
  },
6
- "version": "0.20.0",
6
+ "version": "0.20.2",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -36,7 +36,7 @@
36
36
  "zustand": "^4.5.0 || ^5.0.0"
37
37
  },
38
38
  "dependencies": {
39
- "@fayz-ai/core": "^0.22.0"
39
+ "@fayz-ai/core": "^0.26.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/react": "^18.3.0",