/* =====================================================================
 *  DASHBOARD FFS · chrome
 *  Estende dashboard.css (editorial RankPanda). Montserrat + mono.
 * ===================================================================== */

/* ---- Intro header (padrão módulo) --------------------------------- */
.ffs-intro{background:#0a0a0a;color:#f3efe4;padding:26px 0 34px;position:relative}
.ffs-intro::after{content:"";position:absolute;left:0;right:0;bottom:0;height:5px;
  background:repeating-linear-gradient(90deg,var(--accent) 0 16px,transparent 16px 22px)}
.ffs-intro .inner{max-width:calc(var(--maxw) + (var(--pad) * 2));margin:0 auto;padding:0 var(--pad);position:relative}
.ffs-intro .rp-home{display:inline-block;margin-bottom:22px;line-height:0;opacity:1;transition:opacity .15s}
.ffs-intro .rp-home:hover{opacity:.75}
.ffs-intro .rp-logo{height:20px;display:block}
.ffs-intro .eyebrow{display:flex;align-items:center;gap:10px;margin-bottom:16px}
.ffs-intro .eyebrow .live{width:8px;height:8px;border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 0 rgba(134,194,50,.6);animation:pulse 2s infinite}
.ffs-intro .eyebrow .txt{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:3px;color:var(--accent);
  text-transform:uppercase;font-weight:500}
.ffs-intro h1{font-family:var(--sans);font-weight:800;font-size:var(--t-d1);line-height:1.03;
  letter-spacing:-1.4px;margin:0;max-width:20ch}
.ffs-intro h1 .it{color:var(--accent);font-style:italic;font-weight:600}
.ffs-intro .sub{font-size:var(--t-b2);color:#bdb6a3;max-width:680px;margin-top:16px;line-height:1.55}
.ffs-intro .sub b{color:#f3efe4;font-weight:600}
.ffs-intro .who{display:flex;align-items:center;gap:12px;margin-top:22px;padding-top:20px;border-top:1px solid #1f1f1f}
.ffs-intro .who .nm{font-family:var(--sans);font-size:var(--t-b2);font-weight:700;color:#f3efe4}
.ffs-intro .who .av{width:42px;height:42px;border-radius:10px;background:#242424;color:#f3efe4;
  display:flex;align-items:center;justify-content:center;font-weight:800;font-size:var(--t-b2);overflow:hidden;
  border:1px solid #333}
.ffs-intro .who .av img{width:24px;height:24px;object-fit:contain;display:block}
.ffs-intro .who .nm{font-weight:700;font-size:var(--t-b2)}
.ffs-intro .who .rl{font-family:var(--mono);font-size:var(--t-m2);color:#a39c8d;letter-spacing:.4px}
.ffs-intro .who .hd{margin-left:auto;font-family:var(--mono);font-size:var(--t-m1);color:var(--accent);font-weight:600}
.ffs-usage{margin-left:0;display:flex;align-items:center;gap:18px;border:1px solid #2a2a2a;border-radius:10px;padding:9px 14px 9px 12px;background:#0e0e0e}
.ffs-usage-col{margin-left:auto;display:flex;flex-direction:column;gap:10px;flex-shrink:0}
.ffs-usage .cl{width:18px;height:18px;object-fit:contain;flex-shrink:0;opacity:.95}
.ffs-usage .u{display:grid;grid-template-columns:auto 66px auto;grid-template-rows:auto auto;column-gap:9px;align-items:center}
/* Esta caixa tem muito menos conteúdo do que as do Claude e do Codex, mas fica
   com a largura delas (a coluna estica todas por igual). Sem o `flex:1` o `.u`
   é do tamanho do conteúdo, o `margin-left:auto` do saldo não tem espaço
   nenhum para empurrar dentro dele, e ficava tudo encostado à esquerda com a
   folga toda à direita.
   Com o `.u` a ocupar a caixa e margens automáticas dos DOIS lados, o saldo
   centra-se no espaço que sobra em vez de se encostar a uma das pontas. */
.ffs-usage-credit .u{display:flex;align-items:center;gap:10px;flex:1;min-width:0}
.ffs-usage-credit .u .lbl-col{display:flex;flex-direction:column;gap:2px;flex-shrink:0}
.ffs-usage-credit .up.credit{font-family:var(--mono);font-size:var(--t-b2);font-weight:800;color:var(--accent);margin:0 auto}
/* Com o aviso activo, o saldo e o ponto centram-se JUNTOS, como um par: o
   `auto` da direita passa do saldo para depois do ponto. Sem isto o `auto`
   ficava entre os dois e atirava o ponto para o extremo da caixa, longe do
   número a que diz respeito. */
.ffs-usage-credit.saldo-baixo .up.credit{margin-right:0}
/* Caixa com UMA janela só: o Codex num plano do ChatGPT que não tem limite de
   5h (medido num `prolite` — ver renderCodexUsage, que esconde a barra de
   sessão em vez de a deixar a zero).
   Herda a distribuição da caixa do DataForSEO acima, e pela mesma razão: sobra
   metade da caixa, e sem isto ficava a barra encostada à esquerda com a folga
   toda à direita, desalinhada das outras duas caixas da coluna. A etiqueta e o
   reset empilham-se à esquerda (como o `lbl-col` do saldo) e as margens
   automáticas dos dois lados centram a barra e a percentagem no espaço que
   sobra — em par, como lá. */
.ffs-usage.uma-janela .u{display:flex;align-items:center;gap:10px;flex:1;min-width:0}
.ffs-usage.uma-janela .u .lbl-col{display:flex;flex-direction:column;gap:2px;flex-shrink:0}
.ffs-usage.uma-janela .u .ubar{width:66px;flex-shrink:0;margin-left:auto}
.ffs-usage.uma-janela .u .up{margin-right:auto}
.ffs-usage.uma-janela .u .ur{margin-top:0}
.ffs-usage .u .ut{grid-column:1;grid-row:1;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1.2px;text-transform:uppercase;color:#c9c2b0;font-weight:600}
.ffs-usage .u .ubar{grid-column:2;grid-row:1;height:6px;background:#242424;border-radius:999px;overflow:hidden}
.ffs-usage .u .ubar i{display:block;height:100%;background:var(--accent);border-radius:999px;transform-origin:left;animation:ffsprog 1s cubic-bezier(.2,.7,.2,1) both}
.ffs-usage .u .up{grid-column:3;grid-row:1;font-family:var(--sans);font-size:var(--t-b2);font-weight:800;letter-spacing:-.3px;color:#f3efe4}
.ffs-usage .u .ur{grid-column:1 / -1;grid-row:2;font-family:var(--mono);font-size:var(--t-m2);color:#9c9585;margin-top:3px;letter-spacing:.2px}
/* Sem conta Claude ligada: o pipeline está parado, tem de saltar à vista e não
   parecer só mais um "—" à espera de dados (ver renderClaudeUsage). */
.ffs-usage .u.u-off .ur{color:var(--warn-amber);font-weight:600}
.ffs-intro .who .hpill{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.5px;color:#e2ddd0;border:1px solid #2a2a2a;border-radius:999px;padding:7px 13px;display:flex;align-items:center;gap:7px;background:#0e0e0e;flex-shrink:0}
.ffs-intro .who .hpill b{color:#f3efe4;font-weight:700}
.ffs-intro .who .hpill .d{width:7px;height:7px;border-radius:50%;background:var(--accent);flex-shrink:0}
@media(max-width:820px){.ffs-usage{flex-wrap:wrap;gap:12px 16px}}

/* ---- Hamburger menu ------------------------------------------------- */
.ffs-hamb{position:absolute;top:0;right:var(--pad);width:38px;height:38px;border-radius:9px;border:1px solid #2a2a2a;
  background:#0e0e0e;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;z-index:30}
.ffs-hamb span{display:block;width:16px;height:2px;background:#e2ddd0;border-radius:2px;transition:.15s}
.ffs-hamb[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.ffs-hamb[aria-expanded="true"] span:nth-child(2){opacity:0}
.ffs-hamb[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.ffs-hmenu{position:absolute;top:44px;right:var(--pad);z-index:31;background:#0e0e0e;border:1px solid #2a2a2a;border-radius:10px;
  padding:6px;display:flex;flex-direction:column;gap:2px;min-width:180px;opacity:0;pointer-events:none;transform:translateY(-6px);transition:.15s;box-shadow:0 12px 30px rgba(0,0,0,.4)}
.ffs-hmenu.on{opacity:1;pointer-events:auto;transform:translateY(0)}
.ffs-hitem{appearance:none;background:none;border:none;display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:7px;
  color:#e2ddd0;font-family:var(--sans);font-size:var(--t-b2);font-weight:600;cursor:pointer;text-align:left}
.ffs-hitem:hover{background:#1a1a1a}
.ffs-hitem .n{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;color:var(--accent);letter-spacing:1px}
.ffs-hitem.logout{border-top:1px solid #1f1f1f;margin-top:4px;padding-top:12px;border-radius:0 0 7px 7px}
.ffs-hitem.logout .n{color:#a39c8d}
.ffs-hitem:disabled{opacity:.5;cursor:default}

/* ---- Credenciais ----------------------------------------------------- */
.ffs-cred-list{display:flex;flex-direction:column;border:2px solid var(--ink);background:#fff}
.ffs-cred-row{display:flex;align-items:center;gap:14px;padding:16px 18px;border-bottom:1px solid var(--paper-3)}
.ffs-cred-row:last-child{border-bottom:none}
.cred-icon{width:22px;height:22px;object-fit:contain;flex-shrink:0;border-radius:4px}
.cred-name{font-family:var(--sans);font-size:var(--t-b2);font-weight:700;color:var(--ink);width:180px;flex-shrink:0}
.cred-input-wrap{flex:1;display:flex;align-items:center;gap:6px;border:1.5px solid var(--paper-3);border-radius:7px;padding:0 6px 0 12px;background:var(--paper-2)}
.cred-input{flex:1;border:none;background:none;font-family:var(--mono);font-size:var(--t-m1);color:var(--muted);padding:9px 0;letter-spacing:.3px}
.cred-input:focus{outline:none}
.cred-eye{appearance:none;border:none;background:none;color:var(--muted);cursor:pointer;padding:6px;display:flex}
.cred-eye:hover{color:var(--ink)}
.cred-status{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.6px;text-transform:uppercase;padding:5px 10px;border-radius:999px;flex-shrink:0;white-space:nowrap}
.cred-status.on{background:var(--accent-soft);color:var(--accent-ink)}
.cred-status.off{background:var(--paper-2);color:var(--muted)}
.cred-oauth{flex:1;font-family:var(--mono);font-size:var(--t-m1);color:var(--muted);letter-spacing:.2px}
.cred-oauth b{color:var(--ink)}

/* ---- OAuth connect modal --------------------------------------------- */
.ffs-oauth-modal{position:fixed;inset:0;background:rgba(10,10,10,.6);z-index:420;display:flex;align-items:center;justify-content:center;
  opacity:0;pointer-events:none;transition:.15s}
.ffs-oauth-modal.on{opacity:1;pointer-events:auto}
.oauth-card{width:min(380px,calc(100vw - 32px));background:#fff;border-radius:14px;padding:24px;box-shadow:0 24px 60px rgba(0,0,0,.35)}
/* Credenciais: emails de domínio próprio são longos e não se podem cortar */
.oauth-card.wide{width:min(460px,calc(100vw - 32px))}
/* Sites negativos: este modal tem três campos e uma explicação com exemplos
   de domínios pelo meio. Nos 380px do molde estreito a explicação partia-se
   em colunas de duas palavras e não se lia. */
.oauth-card.sites{width:min(540px,calc(100vw - 32px));padding:28px}
.oauth-card.sites .dfs-campos{gap:14px;margin:18px 0 6px}
.oauth-card.sites .oauth-status{display:block;line-height:1.65;padding:10px 12px;
  background:var(--paper-2);border-radius:8px;margin-top:12px}
.oauth-card.sites .oauth-status code{font-family:var(--mono);font-size:var(--t-m2);
  color:var(--ink);background:#fff;border:1px solid var(--paper-3);
  border-radius:4px;padding:1px 5px;white-space:nowrap}
.oauth-card.sites .oauth-btns{margin-top:18px}
.oauth-hd{display:flex;align-items:flex-start;gap:12px;margin-bottom:18px}
.oauth-hd img{width:32px;height:32px;object-fit:contain;border-radius:6px;flex-shrink:0;margin-top:2px}
.oauth-t{font-family:var(--sans);font-size:var(--t-b2);font-weight:800;color:var(--ink)}
.oauth-s{font-family:var(--sans);font-size:var(--t-m1);color:var(--muted);margin-top:3px;line-height:1.4}
.oauth-scopes{display:flex;flex-direction:column;gap:9px;padding:14px;background:var(--paper-2);border-radius:9px;margin-bottom:14px}
.oauth-scope{display:flex;align-items:center;gap:9px;font-family:var(--sans);font-size:var(--t-m1);color:var(--ink-soft)}
.oauth-scope svg{color:var(--accent-ink);flex-shrink:0}
.oauth-status{min-height:18px;display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:var(--t-m2);color:var(--muted)}
.oauth-status:empty{margin-bottom:0}
.oauth-spin{width:12px;height:12px;border-radius:50%;border:2px solid var(--paper-3);border-top-color:var(--accent-ink);animation:ffsspin .7s linear infinite;flex-shrink:0}
.oauth-btns{display:flex;justify-content:flex-end;gap:8px;margin-top:14px}

/* ---- Decisões à espera de uma pessoa -------------------------------------
   Vive no topo da página inicial, com a paleta de aviso que o resto do
   dashboard já usa (a mesma do `.cat-aviso`). Não é um erro — é trabalho
   parado à espera de quem o pode desbloquear, e por isso tem os botões todos
   à vista em vez de um link para outro sítio. */
.ffs-decisoes{border:1.5px solid #f0dcb8;background:#fdf3e0;border-radius:10px;
  padding:16px 18px;margin:0 0 26px}
.ffs-decisoes .dec-cab{display:flex;align-items:flex-start;gap:12px}
.ffs-decisoes .dec-sino{flex-shrink:0;width:30px;height:30px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;color:#8a5a00;
  background:#f7e4c4;border:1px solid #f0dcb8}
.ffs-decisoes .dec-cab b{display:block;font-family:var(--sans);font-size:var(--t-b2);
  font-weight:800;color:var(--ink);letter-spacing:-.2px}
.ffs-decisoes .dec-cab p{margin:4px 0 0;font-family:var(--sans);font-size:var(--t-m1);
  line-height:1.5;color:#8a5a00;max-width:76ch}
/* Com muitos casos a caixa engolia a página. A partir de uns cinco, rola —
   continuam lá todos, mas o dashboard continua a ser um dashboard. */
.ffs-decisoes .dec-lista{margin-top:14px;display:flex;flex-direction:column;gap:10px;
  max-height:340px;overflow-y:auto}
.ffs-decisoes .dec-linha{background:#fff;border:1px solid #f0dcb8;border-radius:8px;
  padding:12px 14px;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.ffs-decisoes .dec-nome{font-family:var(--sans);font-size:var(--t-b2);font-weight:700;
  color:var(--ink);letter-spacing:-.2px}
.ffs-decisoes .dec-nome .q{margin-left:7px;font-family:var(--mono);font-size:var(--t-m2);
  font-weight:500;color:var(--muted)}
.ffs-decisoes .dec-pagina{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);
  text-decoration:underline;overflow-wrap:anywhere;flex:1;min-width:180px}
.ffs-decisoes .dec-pagina:hover{color:var(--ink)}
.ffs-decisoes .dec-botoes{display:flex;gap:7px;margin-left:auto;flex-wrap:wrap}
/* Quem chega pelo link do email cai numa página inteira e não sabe onde olhar. */
.ffs-decisoes.a-piscar{animation:ffs-piscar 1.1s ease-in-out 2}
@keyframes ffs-piscar{0%,100%{box-shadow:0 0 0 0 rgba(138,90,0,0)}
  50%{box-shadow:0 0 0 5px rgba(138,90,0,.18)}}

/* ---- Tabs --------------------------------------------------------- */
.ffs-tabs{background:var(--paper);border-bottom:1px solid var(--rule);position:sticky;top:0;z-index:20}
.ffs-tabs-inner{max-width:calc(var(--maxw) + (var(--pad) * 2));margin:0 auto;padding:0 var(--pad);display:flex;align-items:stretch}
.ffs-tabs-btns{display:flex;align-items:stretch}
.ffs-tabs .lbl{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1.5px;text-transform:uppercase;
  color:var(--muted);padding:18px 24px 18px 0;border-right:1px solid var(--rule);display:flex;
  align-items:center;flex-shrink:0;font-weight:600;white-space:nowrap}
.ffs-tab{appearance:none;background:none;border:none;border-right:1px dashed var(--paper-3);cursor:pointer;
  text-align:left;padding:15px 26px;position:relative;transition:.12s;min-width:170px}
.ffs-tab .n{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.5px;color:var(--muted-2);
  display:flex;align-items:center;gap:7px}
.ffs-tab .t{font-family:var(--sans);font-weight:700;font-size:var(--t-b2);letter-spacing:-.3px;color:var(--muted);margin-top:5px}
.ffs-tab:hover .t{color:var(--ink)}
.ffs-tab.active{background:var(--paper-2)}
.ffs-tab.active::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:3px;background:var(--accent)}
.ffs-tab.active .n{color:var(--accent-ink)}
.ffs-tab.active .t{color:var(--ink)}
.ffs-tabs .count{margin-left:auto;display:flex;align-items:center;gap:10px;padding:0 4px 0 20px}
.ffs-tabs .count .pill{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.6px;color:var(--muted);
  border:1px solid var(--paper-3);border-radius:999px;padding:6px 12px;display:flex;align-items:center;gap:7px}
.ffs-tabs .count .pill b{color:var(--ink);font-weight:700}
.ffs-tabs .count .pill .d{width:7px;height:7px;border-radius:50%;background:var(--accent)}

/* ---- Views -------------------------------------------------------- */
.ffs-main{max-width:calc(var(--maxw) + (var(--pad) * 2));margin:0 auto;padding:36px var(--pad) 90px}
.ffs-view{display:none}
.ffs-view.active{display:block;animation:ffsfade .28s ease}
@keyframes ffsfade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.ffs-vhead{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:4px}
.ffs-vhead h2{font-family:var(--sans);font-weight:800;font-size:var(--t-d3);letter-spacing:-.7px;margin:0}
.ffs-vhead .n{font-family:var(--mono);font-size:var(--t-m1);color:var(--muted-2);letter-spacing:2px;margin-right:10px}
.ffs-vsub{font-size:var(--t-b2);color:var(--muted);margin:0 0 26px;max-width:780px;line-height:1.5}

/* ---- Select controls (estilo unificado, igual ao calendário) ------ */
.ffs-select,.cal-sel,.fld-select,.tb-sel,.ffs-runform select{appearance:none;border:1.5px solid var(--paper-3);border-radius:7px;background:#fff;font-family:var(--mono);font-size:var(--t-m2);
  font-weight:700;letter-spacing:.2px;text-transform:none;color:var(--ink);padding:9px 30px 9px 12px;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%235d564a' stroke-width='3'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat:no-repeat;background-position:right 10px center;flex-shrink:0}
.ffs-select:hover,.cal-sel:hover,.fld-select:hover,.tb-sel:hover,.ffs-runform select:hover{background-color:var(--paper-2)}
.ffs-select:focus,.cal-sel:focus,.fld-select:focus,.tb-sel:focus,.ffs-runform select:focus{outline:none;border-color:var(--accent-ink)}

.ffs-vhead .ffs-check{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);letter-spacing:.3px;white-space:nowrap}
.ffs-vhead .ffs-check b{color:var(--ink);font-weight:700}
.ffs-check{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);letter-spacing:.3px;margin:-20px 0 28px;
  display:inline-flex;align-items:center;gap:7px;font-weight:400}
.ffs-check::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--muted-2)}
.ffs-check b{color:var(--muted);font-weight:500}
/* separador a preto (padrão dos outros dashboards) */
.ffs-divider{background:#0a0a0a;color:#f3efe4;display:flex;align-items:center;gap:14px;padding:13px 20px;
  margin:40px 0 20px;position:relative}
.ffs-divider::after{content:"";position:absolute;left:0;right:0;bottom:-5px;height:5px;
  background:repeating-linear-gradient(90deg,var(--accent) 0 16px,transparent 16px 22px)}
.ffs-divider .n{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:2px;color:var(--accent);font-weight:700}
.ffs-divider h3{font-family:var(--sans);font-weight:800;font-size:var(--t-b1);letter-spacing:-.3px;margin:0}
.ffs-divider .note{margin-left:auto;font-family:var(--mono);font-size:var(--t-m2);color:#a39c8d;letter-spacing:.2px}

/* ---- Date range filter --------------------------------------------- */
.ffs-daterow{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:0 0 20px}
.ffs-daterow .ffs-check{margin:0 0 0 auto}
/* Data do snapshot + "Atualizar", encostados à direita da mesma barra dos
   filtros (ver chipDoCatalogo em ffs-app.js). */
.ffs-catalogo{display:flex;align-items:center;gap:10px;margin-left:auto}
.ffs-daterow .ffs-catalogo .ffs-check{margin:0}
.ffs-check.a-calcular{color:var(--accent-ink)}
.ffs-check.a-calcular::before{display:none}
.ffs-check .pdot{width:6px;height:6px;border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 0 rgba(134,194,50,.6);animation:pulse 1.6s infinite}
.datechip:disabled{cursor:default;background:var(--paper-2);color:var(--muted)}
.datechip:disabled:hover{background:var(--paper-2)}
.ffs-seg{display:flex;border:1.5px solid var(--ink);border-radius:8px;overflow:hidden;flex-shrink:0}
.segbtn{appearance:none;border:none;border-right:1px solid var(--paper-3);background:#fff;font-family:var(--mono);
  font-size:var(--t-m2);font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--muted);padding:9px 14px;cursor:pointer;transition:.12s}
.segbtn:last-child{border-right:none}
.segbtn:hover{background:var(--paper-2);color:var(--ink)}
.segbtn.active{background:var(--ink);color:var(--paper)}
.ffs-datewrap{position:relative;flex-shrink:0}
.datechip{appearance:none;display:flex;align-items:center;gap:8px;border:1.5px solid var(--ink);border-radius:8px;background:#fff;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;color:var(--ink);padding:8.5px 14px;cursor:pointer;transition:.12s;white-space:nowrap}
.datechip:hover{background:var(--paper-2)}
.datechip.active{background:var(--accent-soft);border-color:var(--accent-ink)}
.ffs-calpop{position:absolute;top:calc(100% + 8px);left:0;z-index:40;background:#fff;border:1.5px solid var(--ink);
  box-shadow:0 12px 30px rgba(0,0,0,.16);padding:14px;width:266px}
.cal-hd{display:flex;align-items:center;gap:6px;margin-bottom:10px}
.cal-nav{appearance:none;border:1px solid var(--paper-3);background:#fff;width:24px;height:24px;border-radius:4px;
  cursor:pointer;font-size:var(--t-b2);color:var(--muted);flex-shrink:0}
.cal-nav:hover{background:var(--paper-2);color:var(--ink)}
.cal-sel{flex:1;min-width:0;font-size:var(--t-m2);padding:6px 26px 6px 8px}
.cal-dow{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px}
.cal-dow span{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.4px;color:var(--muted-2);text-align:center;font-weight:700}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.cal-d{appearance:none;border:none;background:none;height:28px;font-family:var(--mono);font-size:var(--t-m2);color:var(--ink);
  cursor:pointer;border-radius:4px;font-weight:600}
.cal-d.empty{cursor:default}
.cal-d:not(.empty):hover{background:var(--paper-2)}
.cal-d.today{color:var(--accent-ink);font-weight:800}
.cal-d.insel{background:var(--accent-soft)}
.cal-d.selstart,.cal-d.selend{background:var(--ink);color:var(--paper)}
.cal-ft{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px;padding-top:10px;border-top:1px dashed var(--paper-3)}
.cal-hint{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);letter-spacing:.2px}
.ffs-ovnote{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);font-style:italic}
.ffs-ovfoot{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:12px}

/* ---- Execução manual ------------------------------------------------ */
.ffs-runwrap{display:grid;grid-template-columns:300px 1fr;gap:22px}
.ffs-runform{border:2px solid var(--ink);background:#fff;padding:22px;display:flex;flex-direction:column;gap:14px}
.ffs-runform .fl{display:flex;flex-direction:column;gap:6px;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1px;text-transform:uppercase;color:var(--muted);font-weight:700}
.ffs-runform input{font-family:var(--sans);font-size:var(--t-b2);font-weight:600;color:var(--ink);border:1.5px solid var(--paper-3);padding:10px 12px;border-radius:6px;background:#fff}
.ffs-runform input:focus,.ffs-runform select:focus{outline:none;border-color:var(--accent-ink)}
.ffs-runform .fl-hint{font-family:var(--mono);font-size:var(--t-m2);line-height:1.5;letter-spacing:.2px;text-transform:none;color:var(--muted-2);font-weight:400;margin-top:-2px}
/* O visto de "Produtos publicados". Herda a tipografia do `.fl` mas põe a caixa
   ao lado do texto em vez de por cima — um campo de escrever tem a etiqueta
   acima, um visto lê-se da esquerda para a direita. A dica passa para baixo dos
   dois com `flex-basis:100%`, senão ficava espremida na coluna do texto. */
.ffs-runform .fl-check{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1px;text-transform:uppercase;color:var(--muted);font-weight:700;cursor:pointer}
.ffs-runform .fl-check input{width:16px;height:16px;padding:0;accent-color:var(--accent-ink);cursor:pointer;flex:none}
.ffs-runform .fl-check .fl-hint{flex-basis:100%;margin-top:0}

/* ---- Campo com pesquisa (coleção / fornecedor) ----------------------
   2039 coleções e 1911 fornecedores não cabem num <select>. */
.ffs-combo{position:relative}
.ffs-combo .combo-input{width:100%;box-sizing:border-box;padding-right:30px}
.ffs-combo .combo-x{position:absolute;right:8px;top:50%;transform:translateY(-50%);appearance:none;border:none;background:none;
  color:var(--muted-2);font-size:var(--t-b1);line-height:1;cursor:pointer;padding:2px 4px}
.ffs-combo .combo-x:hover{color:var(--ink)}
.combo-list{position:absolute;z-index:40;left:0;right:0;top:calc(100% + 4px);max-height:260px;overflow-y:auto;
  background:#fff;border:1.5px solid var(--ink);border-radius:7px;box-shadow:0 10px 24px rgba(10,10,10,.13)}
.combo-item{display:flex;align-items:center;gap:10px;padding:8px 12px;cursor:pointer;
  font-family:var(--sans);font-size:var(--t-m1);font-weight:600;color:var(--ink);text-transform:none;letter-spacing:0}
.combo-item:hover,.combo-item.on{background:var(--paper-2)}
.combo-item .combo-nome{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.combo-item .combo-n{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;color:var(--muted-2);flex-shrink:0}
.combo-vazio,.combo-mais{padding:9px 12px;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.2px;
  color:var(--muted-2);text-transform:none;font-weight:400}
.combo-mais{border-top:1px solid var(--paper-3)}
.ffs-combo.filtro{flex:1 1 190px;min-width:150px;max-width:260px}
.ffs-combo.filtro .combo-input{padding:9px 30px 9px 12px;text-transform:none}
.ffs-combo.filtro .combo-input::placeholder{color:var(--ink);opacity:.75}
.ffs-runform .ffs-btn{margin-top:4px}
/* A ALTURA VEM DA COLUNA DO LADO, e o log NÃO VOTA nela.
   Esta caixa já teve as duas respostas erradas para o mesmo problema, e vale a
   pena guardar as duas porque parecem certas:

   1) `max-height:78vh` — o tecto era do ECRÃ e não do formulário, portanto numa
      janela de 1000px o log ficava com 780px contra os 997 do formulário: 217px
      de desalinho, apanhados a olho pelo Rui quando o formulário ganhou mais
      uma caixa.
   2) Tirar o tecto e ficar só com `height:100%` na célula da grelha — alinhava
      com o formulário, sim, mas com a linha da grelha a crescer com as linhas
      do log: a grelha estica os DOIS itens até ao mais alto, e a partir da
      trigésima e tal linha o mais alto passava a ser o log. As colunas ficavam
      alinhadas uma com a outra enquanto empurravam juntas o "Registo de
      produtos" para fora do ecrã. É o que o Rui descreve como "a janela do log
      vai crescendo para baixo".

   3) Copiar a altura do formulário e ficar por aí — o que esteve cá entre
      02/09 11h51 e 12h30. Deixou de crescer com as linhas, sim, e o teste
      passava; mas o formulário tem 997px e NENHUM ecrã tem 997px de viewport
      (medido: 1440x800, 1512x830, 1920x950 — a caixa dá sempre 997). Ou seja a
      caixa nunca cabia no ecrã: as linhas novas iam para um fundo que estava
      por baixo da dobra, e a barra de scroll interna também. Quem estava a
      correr um lote via o log a encher para baixo e a sair do ecrã, que é
      exactamente a mesma queixa de (2) com outra causa.

   O que resolve são DUAS coisas, e é preciso as duas:

   a) O log não vota na altura da linha da grelha. A célula (`.ffs-runlog-cell`)
      é o que a grelha mede, e mede-a a zero (só o `min-height` de guarda)
      porque o que está lá dentro sai do fluxo em `absolute`. Quem dita a altura
      da linha é sempre o formulário — três linhas de log ou três mil.
   b) O painel tem altura de ECRÃ e cola-se a ele. A `.ffs-runlog-stick` é o
      caixote absoluto que herda a altura da célula (é contra ela que o `100%`
      de baixo resolve, e é ela que delimita até onde o sticky pode descer); o
      `.ffs-runlog` lá dentro fica em `sticky` e acompanha o scroll enquanto se
      percorre a coluna do formulário. Sem (b), (a) sozinho é o caso 3.

   O `height` é `min()` dos dois: nunca passa do ecrã (descontando a barra de
   separadores, que é sticky e taparia o cabeçalho do log — daí o `--tabs-h`) e
   nunca passa da célula, para em ecrãs altos não sobrar caixa por baixo do
   formulário. Os 16px de folga em cima e em baixo são para a caixa não ficar
   encostada às arestas do viewport.

   Daí para dentro é o `.rl-body` que faz o scroll, como sempre fez. O
   `min-height:0` que lá está é cinto e suspensórios: um filho flex só tem
   `min-height:auto` a impedi-lo de encolher enquanto o `overflow` for
   `visible`, e o `.rl-body` tem `overflow-y:auto` desde sempre. Medido a
   2026-08-06 com e sem a declaração: mesma altura, mesmo scroll, ao pixel. */
.ffs-runlog-cell{position:relative;min-height:280px}
.ffs-runlog-stick{position:absolute;inset:0}
.ffs-runlog{position:sticky;top:calc(var(--tabs-h) + 16px);
  height:min(100%, calc(100vh - var(--tabs-h) - 32px));
  border:2px solid #0a0a0a;background:#0a0a0a;display:flex;flex-direction:column}
.rl-hd{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid #242424;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1px;text-transform:uppercase;color:#c9c2b0;font-weight:700}
.rl-hd .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 0 rgba(134,194,50,.6);animation:pulse 1.6s infinite}
.rl-clear{appearance:none;border:none;background:none;color:#a39c8d;font-family:var(--mono);font-size:var(--t-m2);text-transform:uppercase;letter-spacing:.5px;cursor:pointer}
.rl-clear:hover{color:#f3efe4}
/* Sem run a correr o "Abortar" está escondido e é o "Limpar" que tem de ser
   empurrado para a direita. Ver a nota do `.rl-abort`. */
.rl-abort[hidden] + .rl-clear{margin-left:auto}
/* Abortar é irmão do "Limpar", não um botão sólido — pedido do Rui, e bem: um
   botão vermelho cheio ao lado de um log preto pedia para ser carregado, e o
   que ele faz é deitar fora o resto de um lote a meio.

   O vermelho não é o `var(--warn)` (#c83e2c) do resto do dashboard: esse foi
   escolhido para ler sobre papel claro e sobre este fundo (#0a0a0a) fica quase
   castanho, ilegível ao tamanho de 10px. Este é o mesmo vermelho puxado para
   cima em luminosidade até contrastar com o preto, mantendo-se dessaturado o
   suficiente para não competir com o verde do `.dot` de "a correr".

   Fica à ESQUERDA do "Limpar" para o "Limpar" não mudar de sítio: quem já o usa
   por memória muscular não passa a acertar no abortar.

   O `margin-left:auto` está NUM SÓ dos dois, e essa é a correcção de
   2026-08-06. A primeira versão punha-o nos dois, com o argumento de que "o
   primeiro come o espaço livre" — e o flexbox não faz isso: reparte o espaço
   livre IGUALMENTE por todas as margens automáticas (CSS Flexbox §8.1). O
   resultado medido no browser era o "Abortar" a 374px do "Limpar", plantado no
   meio da barra preta, em vez de ao lado dele.

   Como o auto passou a viver no "Abortar", que é o que se esconde, é preciso
   devolvê-lo ao "Limpar" quando não há run a correr — senão o "Limpar" ficava
   colado ao título. É o que faz a regra do `[hidden] + .rl-clear`: um elemento
   escondido continua no DOM e continua a casar com o selector de irmão. */
.rl-abort{margin-left:auto;appearance:none;border:none;background:none;color:#e08b7d;font-family:var(--mono);font-size:var(--t-m2);text-transform:uppercase;letter-spacing:.5px;cursor:pointer;font-weight:700}
.rl-abort:hover{color:#f6b4a6}
.rl-abort[hidden]{display:none}
.rl-abort:disabled{color:#8a6a63;cursor:default}
.rl-body{flex:1;min-height:0;overflow-y:auto;padding:14px 16px;font-family:var(--mono);font-size:var(--t-m1);line-height:1.8}
/* A barra de scroll fica À VISTA, ao contrário da do sistema.
   Agora que a caixa tem altura fixa, o que está fora dela não tem outro sinal
   de que existe: no macOS a barra é overlay e só aparece enquanto se mexe na
   roda, portanto um log com trezentas linhas parecia um log de trinta que
   acabava ali. A barra permanente é o que diz "há mais acima" — e é também a
   régua de quão longe vai o lote.
   As cores são as do painel e não as do `.repo-tree`: aquelas foram escolhidas
   sobre papel claro e aqui desapareciam no preto. */
.rl-body::-webkit-scrollbar{width:10px}
.rl-body::-webkit-scrollbar-track{background:#141414}
.rl-body::-webkit-scrollbar-thumb{background:#3d3d3d;border:2px solid #141414}
.rl-body::-webkit-scrollbar-thumb:hover{background:#555}
/* O Firefox não tem os `::-webkit-*`; tem estas duas, que dão o mesmo recado
   com menos controlo. */
.rl-body{scrollbar-width:thin;scrollbar-color:#3d3d3d #141414}
.rl-empty{color:#8a8275;font-style:italic}
.rl-line{color:#d7f0d8}
.rl-line .t{color:#8a8275;margin-right:10px}
.rl-line.ok{color:var(--accent);font-weight:700}
/* "Correu, mas não foi publicado." Não é um erro — nada avariou, uma guarda
   fez o seu trabalho — e não é um "ok", que é precisamente a confusão que esta
   classe veio desfazer: até 2026-08-31 uma secção de ingredientes recusada
   escrevia-se a verde por cima de "optimizado e publicado".

   O âmbar não é o `var(--warn-amber)` (#d97706) do resto do dashboard, pela
   mesma razão que o `.rl-abort` não usa o `var(--warn)`: essas cores foram
   escolhidas para ler sobre papel claro, e sobre o preto deste painel ficam
   apagadas. Este é o mesmo âmbar puxado em luminosidade até destacar do
   #d7f0d8 das linhas normais sem chegar ao vermelho do abortar — quem passa os
   olhos tem de distinguir "não entrou" de "deitou o lote abaixo". */
.rl-line.aviso{color:#e8b465;font-weight:700}
/* Numa coluna só não há formulário ao lado para copiar a altura, e um log
   inteiro entre o formulário e a tabela obrigava a passar o ecrã todo a raspar
   para chegar ao que já saiu: aqui a célula tem uma altura fixa, mais baixa do
   que a do desktop, de propósito. É `height` e não `max-height` porque o painel
   lá dentro está fora do fluxo e um máximo sobre uma célula sem conteúdo a
   medir dava-lhe zero.

   O `sticky` do desktop fica cá, mas não tem trabalho: só se cola dentro do
   caixote que o delimita, e aqui o caixote tem os mesmos 400px do painel. Numa
   coluna só o log já está entre o formulário e a tabela, à distância de um
   polegar — não há coluna comprida ao lado para o deixar para trás. */
@media(max-width:820px){.ffs-runwrap{grid-template-columns:1fr}.ffs-runlog-cell{height:400px}}

/* ---- OVERVIEW ----------------------------------------------------- */
.ffs-kpi{display:grid;grid-template-columns:1fr 1fr;gap:0;border:2px solid var(--ink);background:#fff;margin-bottom:14px}
.ffs-kpi .big{padding:28px 32px;border-right:1px solid var(--paper-3);display:flex;flex-direction:column;justify-content:center}
.ffs-kpi .big .lbl{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:2.5px;text-transform:uppercase;color:var(--muted);font-weight:700}
.ffs-kpi .big .vrow{display:flex;align-items:flex-end;gap:20px;margin:14px 0 20px}
.ffs-kpi .big .v{font-family:var(--sans);font-weight:800;font-size:var(--t-heroi);line-height:.82;letter-spacing:-5px;color:var(--ink)}
.ffs-kpi .big .v sup{font-family:var(--mono);font-size:var(--t-d3);font-weight:600;color:var(--muted);vertical-align:top;margin-left:4px;letter-spacing:-1px}
.ffs-kpi .big .vmeta{padding-bottom:12px;line-height:1.25}
.ffs-kpi .big .vmeta b{display:block;font-family:var(--sans);font-weight:800;font-size:var(--t-d3);letter-spacing:-1px;color:var(--ink)}
.ffs-kpi .big .vmeta span{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);letter-spacing:.3px}
.ffs-kpi .big .prog{height:14px;background:var(--paper-2);border:1.5px solid var(--ink);position:relative;overflow:hidden}
.ffs-kpi .big .prog i{position:absolute;inset:0 auto 0 0;background:var(--accent);
  background-image:repeating-linear-gradient(45deg,rgba(0,0,0,.10) 0 6px,transparent 6px 12px);
  transform-origin:left;animation:ffsprog 1s cubic-bezier(.2,.7,.2,1) both}
@keyframes ffsprog{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.ffs-kpi .big .legend{display:flex;gap:20px;margin-top:14px}
.ffs-kpi .big .legend .lg{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);font-weight:600}
.ffs-kpi .big .legend .lg i{width:11px;height:11px;border:1.5px solid var(--ink);background:var(--paper-2)}
.ffs-kpi .big .legend .lg i.on{background:var(--accent)}
/* Activos vs rascunho — a divisão que explica o número grande (ver
   blocoEstados em ffs-app.js). O ".on" é o estado que o filtro do topo tem
   seleccionado. */
.ffs-kpi .big .estados{display:grid;grid-template-columns:1fr 1fr;margin-top:18px;border-top:1px solid var(--paper-3)}
.ffs-kpi .big .estados .est{padding:14px 18px 0 0}
.ffs-kpi .big .estados .est+.est{padding:14px 0 0 18px;border-left:1px solid var(--paper-3)}
.ffs-kpi .big .estados .est .k{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1.4px;text-transform:uppercase;color:var(--muted);font-weight:700}
.ffs-kpi .big .estados .est .n{font-family:var(--sans);font-weight:800;font-size:var(--t-d3);line-height:1;letter-spacing:-1.2px;margin-top:8px}
.ffs-kpi .big .estados .est .s{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-top:6px}
.ffs-kpi .big .estados .est .s b{color:var(--ink)}
.ffs-kpi .big .estados .est.on .k,.ffs-kpi .big .estados .est.on .n{color:var(--accent-ink)}
.ffs-kpi .cells{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr}
.ffs-kpi .cell{padding:20px 22px;border-right:1px solid var(--paper-3);border-bottom:1px solid var(--paper-3)}
.ffs-kpi .cell:nth-child(2n){border-right:none}
.ffs-kpi .cell:nth-child(n+3){border-bottom:none}
.ffs-kpi .cell .k{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1.4px;text-transform:uppercase;color:var(--muted);font-weight:700;display:flex;align-items:center;gap:7px}
.ffs-kpi .cell .vv{font-family:var(--sans);font-weight:800;font-size:var(--t-d2);line-height:1;letter-spacing:-1.5px;margin-top:10px}
.ffs-kpi .cell .vv.warn{color:var(--warn)}
.ffs-kpi .cell .fn{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-top:8px}
/* Variante do KPI para o 04.1 (custo por produto): sem o painel grande da
   esquerda e com seis células em vez de quatro. Reaproveita `.cell` de
   propósito — é a mesma leitura ("um número com rótulo e nota de rodapé") e
   dois blocos de estatística com aspecto diferente no mesmo produto lêem-se
   como duas coisas diferentes. Só as regras de borda é que tinham de mudar:
   as originais assumem uma grelha 2×2 fixa. */
.ffs-kpi.custos{grid-template-columns:1fr}
.ffs-kpi.custos .cells{grid-template-columns:repeat(3,1fr);grid-template-rows:auto}
.ffs-kpi.custos .cell{border-right:1px solid var(--paper-3);border-bottom:1px solid var(--paper-3)}
.ffs-kpi.custos .cell:nth-child(3n){border-right:none}
.ffs-kpi.custos .cell:nth-child(n+4){border-bottom:none}
.ffs-kpi.custos .cell .fn b{color:var(--ink);font-weight:700}

.ffs-kpi .cell.live{background:#0a0a0a;color:#f3efe4;position:relative;overflow:hidden}
.ffs-kpi .cell.live>*{position:relative;z-index:1}
.ffs-kpi .cell.live .k{color:var(--accent)}
.ffs-kpi .cell.live .fn{color:#a39c8d}
.ffs-kpi .cell.live .pdot{width:8px;height:8px;border-radius:50%;background:var(--accent);
  box-shadow:0 0 0 0 rgba(134,194,50,.6);animation:pulse 1.6s infinite}

.ffs-drill{display:grid;grid-template-columns:1fr 1fr;gap:22px;align-items:stretch}
/* A segunda fila (os painéis do stock) afasta-se da primeira com o mesmo
   espaço que separa as colunas — quatro caixas iguais coladas liam-se como
   uma grelha só. */
.ffs-drill + .ffs-drill{margin-top:22px}
.ffs-panel{border:2px solid var(--ink);background:#fff;display:flex;flex-direction:column}
.ffs-panel .ph{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1.5px solid var(--ink);background:var(--paper-2)}
.ffs-panel .ph .pt{font-family:var(--sans);font-weight:800;font-size:var(--t-b2);letter-spacing:-.3px}
.ffs-panel .ph .pm{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.6px;text-transform:uppercase;color:var(--muted);margin-left:auto}
.ffs-panel .ph .pm-sort{appearance:none;border:none;background:none;margin-left:auto;padding:2px 0;cursor:pointer;
  display:inline-flex;align-items:center;gap:4px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;
  letter-spacing:.6px;text-transform:uppercase;color:var(--muted);transition:.12s}
.ffs-panel .ph .pm-sort:hover{color:var(--ink)}
.ffs-panel .ph .pm-sort .arrow{font-size:var(--t-m2);opacity:.75}
/* Linha dos filtros do painel: pesquisa por nome + "Esconder 100%". */
.ffs-panel .pf{display:flex;align-items:center;gap:8px;padding:10px 18px;border-bottom:1px dashed var(--paper-3)}
.ffs-panel .pf .dsearch{flex:1;min-width:0;display:flex;align-items:center;gap:7px;
  border:1.5px solid var(--paper-3);border-radius:6px;background:var(--paper-2);padding:0 8px 0 10px;transition:.12s}
.ffs-panel .pf .dsearch:focus-within{border-color:var(--ink);background:#fff}
.ffs-panel .pf .dsearch svg{flex-shrink:0;color:var(--muted-2)}
.ffs-panel .pf .dsearch input{flex:1;min-width:0;border:none;outline:none;background:none;
  font-family:var(--sans);font-size:var(--t-m1);color:var(--ink);padding:8px 0}
.ffs-panel .pf .dsearch input::placeholder{color:var(--muted-2)}
.ffs-panel .pf .dclear{appearance:none;border:none;background:none;cursor:pointer;flex-shrink:0;
  color:var(--muted-2);font-size:var(--t-b2);line-height:1;padding:2px 3px}
.ffs-panel .pf .dclear:hover{color:var(--ink)}
.ffs-panel .pf .dclear[hidden]{display:none}
.ffs-panel .pf .dchip{appearance:none;flex-shrink:0;border:1.5px solid var(--paper-3);border-radius:6px;background:#fff;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.6px;text-transform:uppercase;
  color:var(--muted);padding:8px 11px;cursor:pointer;transition:.12s;white-space:nowrap}
.ffs-panel .pf .dchip:hover{background:var(--paper-2);color:var(--ink)}
.ffs-panel .pf .dchip.on{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.ffs-panel .pb{padding:8px 18px 16px;flex:1}
.ffs-panel .pb .drill-pager{margin-top:12px}
/* A barra tinha 120px. Com o nome da categoria a subir de 13px para 14px, o
   mais comprido do catálogo ("SUPLEMENTOS ALIMENTARES E PRODUTOS NATURAIS")
   passou a precisar de 389px numa coluna de 373 e ficava cortado — medido, não
   suposto. Os 20px saem da barra, que a 100px continua a ler-se na mesma: o
   que ali interessa é a proporção preenchida, e essa vê-se igual. */
.ffs-drow{display:grid;grid-template-columns:1fr 100px 42px 58px;gap:12px;align-items:center;padding:11px 0;border-bottom:1px dashed var(--paper-3)}
.ffs-drow:last-child{border-bottom:none}
.ffs-drow .nm{font-size:var(--t-b2);font-weight:600;letter-spacing:-.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ffs-drow .bar{height:8px;background:var(--paper-2);border:1px solid var(--paper-3);position:relative;overflow:hidden}
.ffs-drow .bar i{position:absolute;inset:0 auto 0 0;transition:width .7s cubic-bezier(.2,.7,.2,1)}
.ffs-drow .bar i.hi{background:var(--accent)}
.ffs-drow .bar i.mid{background:var(--warn-amber)}
.ffs-drow .bar i.lo{background:var(--warn)}
.ffs-drow .pc{font-family:var(--sans);font-weight:800;font-size:var(--t-b2);letter-spacing:-.5px;text-align:right}
.ffs-drow .pc.hi{color:var(--accent-ink)}.ffs-drow .pc.mid{color:#8a5a00}.ffs-drow .pc.lo{color:var(--warn)}
.ffs-drow .ct{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);text-align:right;white-space:nowrap}

/* ---- Toolbar (busca / filtros) ------------------------------------ */
.ffs-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.ffs-bar2{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:22px}
/* O botão "Filtros" NÃO EXISTE no desktop: aqui há largura para os filtros
   todos ao mesmo tempo, e escondê-los atrás de um botão seria acrescentar um
   clique para não ganhar nada. Só se mostra em ecrãs estreitos — ver o
   Bloco 13 do ffs-mobile.css. */
.ffs-filtros-btn{display:none}
.ffs-seg.sm .segbtn{padding:7px 11px;font-size:var(--t-m2)}
.datechip.sm{padding:6px 11px;font-size:var(--t-m2)}
.sort-select{margin-left:auto;appearance:none;border:none;background:none;font-family:var(--mono);font-size:var(--t-m2);
  color:var(--muted);cursor:pointer;padding:4px 20px 4px 2px;font-weight:600;text-transform:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%235d564a' stroke-width='3'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat:no-repeat;background-position:right center}
.sort-select:hover{color:var(--ink)}
.sort-select:focus{outline:none}
.ffs-search{flex:1;min-width:240px;display:flex;align-items:center;gap:10px;border:1.5px solid var(--ink);border-radius:7px;
  background:#fff;padding:0 14px}
.ffs-search svg{flex-shrink:0;color:var(--muted)}
.ffs-search input{flex:1;border:none;outline:none;background:none;font-family:var(--sans);font-size:var(--t-b2);
  color:var(--ink);padding:12px 0}
.ffs-search input::placeholder{color:var(--muted-2)}
.ffs-filters{display:flex;gap:0;border:1.5px solid var(--ink);background:#fff;overflow:hidden;flex-shrink:0}
.ffs-filters button{appearance:none;border:none;border-right:1px solid var(--paper-3);background:#fff;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.6px;text-transform:uppercase;
  color:var(--muted);padding:11px 15px;cursor:pointer;transition:.12s;white-space:nowrap}
.ffs-filters button:last-child{border-right:none}
.ffs-filters button:hover{background:var(--paper-2);color:var(--ink)}
.ffs-filters button.on{background:var(--ink);color:var(--paper)}
.ffs-filters button.on .cnt{color:rgba(255,255,255,.6)}
.ffs-filters button .cnt{color:var(--muted-2);font-weight:600;margin-left:5px}

/* ---- Status badge ------------------------------------------------- */
.ffs-st{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.5px;text-transform:uppercase;
  padding:4px 9px;border-radius:3px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.ffs-st .d{width:7px;height:7px;border-radius:50%}
.ffs-st.publicado{background:var(--accent-soft);color:var(--accent-ink)}
.ffs-st.publicado .d{background:var(--accent)}
.ffs-st.pendente{background:#fef3c7;color:#8a5a00}
.ffs-st.pendente .d{background:var(--warn-amber)}
.ffs-st.needs_review{background:var(--info-soft);color:#2d3f8f}
.ffs-st.needs_review .d{background:var(--info)}
.ffs-st.erro{background:var(--warn-soft);color:var(--warn)}
.ffs-st.erro .d{background:var(--warn)}

/* ---- Log table ---------------------------------------------------- */
/* A tabela cabe na largura disponível em vez de arrastar para o lado: as
   colunas têm largura proporcional (table-layout:fixed) e o texto quebra em
   várias linhas. Antes havia min-width:920px + overflow-x, e ler uma linha
   obrigava a andar para trás e para a frente. O overflow-x fica como rede
   para ecrãs muito estreitos. */
.ffs-tablewrap{border:2px solid var(--ink);background:#fff;overflow-x:auto}
table.ffs-log{border-collapse:collapse;width:100%;table-layout:fixed;min-width:660px}
table.ffs-log thead th{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.1px;
  text-transform:uppercase;color:var(--muted);text-align:center;padding:14px 10px;background:var(--paper-2);
  border-bottom:1.5px solid var(--ink);white-space:normal;position:sticky;top:0}

/* Larguras: o título fica com o espaço que sobra. */
table.ffs-log .th-check,table.ffs-log .c-check{width:34px}
/* Um product id desta loja tem 14 dígitos e, com o M à frente, a 12% não
   cabia — ficava cortado com reticências, e um id cortado não serve para
   nada. O que sobra sai do título, que é a coluna elástica. */
table.ffs-log .th-id,table.ffs-log .c-id{width:14%}
table.ffs-log .th-thumb,table.ffs-log .c-thumb{width:110px}
table.ffs-log .th-coll,table.ffs-log .c-coll{width:15%}
table.ffs-log .th-vendor,table.ffs-log .c-vendor{width:13%}
table.ffs-log .th-status,table.ffs-log .c-status{width:12%}
table.ffs-log .th-time,table.ffs-log .c-time{width:11%}
table.ffs-log .th-go,table.ffs-log .c-go{width:30px}
table.ffs-log thead th.sortable{cursor:pointer;user-select:none}
table.ffs-log thead th.sortable:hover{color:var(--ink)}
table.ffs-log thead th .sort-ic{opacity:.6;margin-left:2px}
.th-check{width:40px}
.c-check{width:40px;text-align:center}
.row-check,#ffs-check-all{width:15px;height:15px;cursor:pointer;accent-color:var(--ink)}
.bulk-btn{display:flex;align-items:center;gap:8px}
.bulk-btn img{border-radius:3px}
.bulk-btn:disabled{opacity:.4;cursor:not-allowed}
.bulk-btn .bulk-n{font-weight:800}
table.ffs-log tbody td{padding:13px 10px;border-bottom:1px solid var(--paper-2);font-size:var(--t-b2);vertical-align:middle;
  overflow-wrap:break-word}
table.ffs-log tbody tr{cursor:pointer;transition:background .1s}
table.ffs-log tbody tr:last-child td{border-bottom:none}
table.ffs-log tbody tr:hover{background:#faf7ee}
table.ffs-log tbody tr.sel{background:var(--accent-soft)}
table.ffs-log .c-id{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);font-weight:600;text-align:left}
table.ffs-log .c-id{display:table-cell}
table.ffs-log td.c-id{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
table.ffs-log .c-id .pidlink{display:inline-flex;align-items:center;gap:4px;color:var(--accent-ink);font-weight:700;
  border-bottom:1px solid transparent;transition:.12s}
table.ffs-log .c-id .pidlink:hover{border-bottom-color:var(--accent-ink)}
/* Produto corrido a pedido de uma pessoa (secção 02 ou "Correr novamente"),
   e não pelo webhook. Aparece na tabela e no cabeçalho do drawer. */
/* O círculo tinha 14px para uma letra de 8,5px. Com a letra a 11px (o chão da
   escala) o M encostava-se às paredes; o círculo cresce com ela. */
.c-id .mbadge,.ffs-dh .pid .mbadge{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;
  background:var(--info);color:#fff;font-family:var(--mono);font-size:var(--t-m2);font-weight:800;margin-right:5px;cursor:help;vertical-align:middle;flex-shrink:0}
.ffs-dh .pid .mbadge{width:18px;height:18px;font-size:var(--t-m2);margin-right:6px;letter-spacing:0}
table.ffs-log .c-id .pidlink .ext{font-size:var(--t-m2)}
/* Em ecrãs mais apertados a coluna encolhe com a tabela: aperta-se o número
   antes de o cortar. */
@media (max-width:1200px){
  table.ffs-log td.c-id{font-size:var(--t-m2);padding-left:6px;padding-right:6px}
  .c-id .mbadge{margin-right:4px}
}
/* Mais apertado ainda: entre a setinha e o id completo, fica o id. A célula
   inteira continua a abrir a loja. */
@media (max-width:1080px){
  table.ffs-log .c-id .pidlink .ext{display:none}
}

/* A MINIATURA É SEMPRE DO MESMO TAMANHO, e o tamanho é o da linha mais alta.
   Medido a 2026-09-01 na linha do "La Roche-Posay Hyalu B5 Creme 50ml + Mela B3
   Sérum + Anthelios UVMune 400 SPF50+", que é o pior caso do catálogo: título a
   três linhas (60px) + handle (15px) + pill do stock (21px) = 96px de conteúdo.

   Fixo e não `height:100%`: com altura elástica, cada linha tinha a foto de um
   tamanho diferente conforme o comprimento do título, e uma tabela de fotos de
   tamanhos diferentes lê-se pior do que uma de fotos pequenas. O `object-fit:
   contain` garante que nenhuma foto se deforma para caber. */
.c-thumb-box{width:96px;height:96px;border:1.5px solid var(--paper-3);background:#fff;border-radius:6px;overflow:hidden;
  position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.c-thumb-box img{width:100%;height:100%;object-fit:contain;position:relative;z-index:1;background:#fff}
.c-thumb-box .fb{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--sans);font-weight:800;font-size:var(--t-b1);color:var(--muted-2);background:var(--paper-2);z-index:0}
.c-thumb-box.noimg{border-style:dashed}
table.ffs-log .c-title{font-weight:700;letter-spacing:-.2px;line-height:1.35}
table.ffs-log .c-title .h{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);font-weight:500;
  margin-top:3px;line-height:1.4;overflow-wrap:anywhere}
table.ffs-log .c-tag{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;color:var(--accent-ink);
  background:var(--accent-soft);padding:3px 8px;border-radius:3px;display:inline-block;line-height:1.4;
  overflow-wrap:anywhere}
table.ffs-log .c-coll{font-size:var(--t-m1);color:var(--muted);line-height:1.4}
table.ffs-log .c-time{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);line-height:1.45}
.c-time .execs{display:inline-block;margin-top:4px;font-size:var(--t-m2);font-weight:700;letter-spacing:.3px;
  color:var(--muted-2);background:var(--paper-2);border-radius:3px;padding:2px 6px;cursor:help}
table.ffs-log .c-status .ffs-st{white-space:normal;text-align:left}
table.ffs-log .c-go{font-family:var(--mono);font-size:var(--t-b2);color:var(--muted-2);text-align:center}
table.ffs-log tbody tr:hover .c-go{color:var(--accent-ink)}
.ffs-empty{padding:60px 24px;text-align:center;font-family:var(--mono);font-size:var(--t-m1);color:var(--muted)}
.ffs-tfoot{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-top:12px;letter-spacing:.3px;
  display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px}
.ffs-pager{display:flex;align-items:center;justify-content:center;gap:16px;margin-top:16px}
.pg-nav{appearance:none;border:1.5px solid var(--ink);background:#fff;border-radius:7px;font-family:var(--mono);font-size:var(--t-m2);
  font-weight:700;letter-spacing:.4px;color:var(--ink);padding:9px 16px;cursor:pointer;transition:.12s}
.pg-nav:hover:not(:disabled){background:var(--paper-2)}
.pg-nav:disabled{opacity:.35;cursor:not-allowed}
.pg-info{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted)}
.pg-info b{color:var(--ink)}

/* ---- Lightbox ------------------------------------------------------- */
.ffs-lightbox{position:fixed;inset:0;background:rgba(10,10,10,.85);z-index:400;display:flex;align-items:center;justify-content:center;
  opacity:0;pointer-events:none;transition:.18s}
.ffs-lightbox.on{opacity:1;pointer-events:auto}
.ffs-lightbox img{max-width:88vw;max-height:88vh;border-radius:6px;box-shadow:0 20px 60px rgba(0,0,0,.5)}

/* ---- Detail drawer ------------------------------------------------ */
.ffs-drawer-scrim{position:fixed;inset:0;background:rgba(10,10,10,.5);z-index:300;opacity:0;
  pointer-events:none;transition:.2s}
.ffs-drawer-scrim.on{opacity:1;pointer-events:auto}
.ffs-drawer{position:fixed;top:0;right:0;bottom:0;width:min(680px,94vw);background:var(--paper);
  border-left:2px solid var(--ink);z-index:301;transform:translateX(100%);transition:transform .26s cubic-bezier(.2,.7,.2,1);
  display:flex;flex-direction:column;box-shadow:-16px 0 50px rgba(0,0,0,.25)}
.ffs-drawer.on{transform:translateX(0)}
.ffs-dh{background:#0a0a0a;color:#f3efe4;padding:32px 26px 20px;flex-shrink:0}
.ffs-dh .top{display:flex;align-items:center;gap:14px}
.ffs-dh .thumb{width:48px;height:48px;border-radius:10px;background:#1a1a1a;border:1px solid #2a2a2a;position:relative;overflow:hidden;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:var(--sans);font-weight:800;font-size:var(--t-b1);color:var(--accent)}
.ffs-dh .thumb .fb{position:relative;z-index:0}
.ffs-dh .thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1;cursor:zoom-in}
.ffs-dh .ti{min-width:0}
.ffs-dh .pid{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:1px;color:#a39c8d}
.ffs-dh .pid .storelink{color:var(--accent);font-weight:700}
.ffs-dh .pid .storelink:hover{text-decoration:underline}
.ffs-dh h3{font-family:var(--sans);font-weight:800;font-size:var(--t-h1);letter-spacing:-.5px;margin:6px 0 0;line-height:1.2}
.ffs-dh .dh-actions{display:flex;justify-content:flex-end;gap:4px;margin-top:16px;padding-top:16px;border-top:1px solid #1f1f1f}
.ffs-dh .dh-actions .x{margin-left:8px}
.ffs-dh .rf,.ffs-dh .x{background:none;border:1px solid #2a2a2a;color:#a39c8d;height:34px;
  font-size:var(--t-b1);cursor:pointer;line-height:1;border-radius:8px;flex-shrink:0;transition:.12s}
.ffs-dh .rf{display:flex;align-items:center;gap:7px;padding:0 14px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase}
.ffs-dh .rf img{border-radius:3px}
.ffs-dh .rf.running{opacity:.65;cursor:wait}
.ffs-dh .rf .ic{font-size:var(--t-b2)}
.ffs-dh .x{width:34px;font-size:var(--t-h1)}
.ffs-dh .rf:hover,.ffs-dh .x:hover{background:#1a1a1a;color:#f3efe4}
.ffs-dh .rf.spin{animation:ffsspin .7s linear infinite;color:var(--accent)}
@keyframes ffsspin{to{transform:rotate(360deg)}}
.ffs-dh .meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.ffs-dh .meta span{font-family:var(--mono);font-size:var(--t-m2);color:#c9c2b0;background:#161616;border:1px solid #262626;
  border-radius:999px;padding:5px 11px;display:inline-flex;align-items:center;gap:5px}
.ffs-dh .meta b{color:#f3efe4;font-weight:700}
.ffs-dh .meta .hnd{color:var(--accent);background:#132009;border-color:#20330f}
.ffs-dbody{flex:1;overflow-y:auto;padding:22px 26px 40px}
.ffs-review{border:1.5px solid var(--warn);background:var(--warn-soft);padding:12px 14px;margin-bottom:20px;
  font-size:var(--t-m1);color:var(--warn);line-height:1.45}
.ffs-review.info{border-color:var(--info);background:var(--info-soft);color:#2d3f8f}
.ffs-review b{font-weight:700}
/* Cofres (conjuntos de vários produtos). O que interessa mostrar é a lista do
   que o sistema apurou que vem na caixa — é essa que foi para a descrição da
   loja e a única parte desta funcionalidade que não se vê só a ler o texto. */
.cofre-bloco .cofre-porque{display:block;font-size:var(--t-m2);font-style:italic;opacity:.8;margin-top:2px}
.cofre-lista{margin:9px 0 0;padding-left:17px;display:flex;flex-direction:column;gap:5px}
.cofre-lista li{font-size:var(--t-m1);line-height:1.4}
.cofre-lista .cofre-qtd{font-family:var(--mono);font-size:var(--t-m2);opacity:.75}
.cofre-lista .cofre-para{display:block;font-size:var(--t-m2);opacity:.8}
.cofre-vazio{margin:8px 0 0;font-size:var(--t-m1);font-style:italic;opacity:.85}
/* Produtos que ficaram sem foto. É a excepção — a regra é o pipeline
   encontrá-la sozinho — mas tem de se ver, senão ficam publicados assim. */
/* CENTRADO NA CAIXA, e não pendurado na borda de baixo.
   Estava a `bottom:-3px` com `nowrap`, ou seja meio corpo fora de uma caixa de
   52px que tem `overflow:hidden` — resultado, a etiqueta aparecia cortada ao
   meio. Visto no "Advancis Desinfetante Mãos", que é precisamente o produto que
   ela existe para assinalar.
   Agora ocupa o centro do lugar onde estaria a fotografia, que é onde o olho já
   está a olhar, e a caixa inteira serve-lhe de fundo.

   E EM DUAS LINHAS, que foi a segunda tentativa. A primeira manteve o
   `white-space:nowrap` com um `max-width`, e continuou cortada: o max-width
   limita o ELEMENTO, mas o texto que não parte transborda para fora dele e é o
   `overflow:hidden` da caixa que o corta. Pior, o `getBoundingClientRect` mede
   o elemento limitado e não o texto — pelo que a medição dizia que cabia
   enquanto a imagem mostrava o "O" cortado ao meio.

   "SEM" por cima de "FOTO" cabe nos 52px com folga em qualquer tipo de letra,
   e não depende de a métrica dar exactamente o que se esperava. */
.c-thumb-box .sem-img{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  z-index:2;max-width:calc(100% - 6px);text-align:center;line-height:1.15;
  /* 8,5px eram os do desenho original deste crachá (beec45b). Subiu para o
     chão da escala: abaixo de 11px o aviso existe mas não se lê, e é
     precisamente um aviso — o produto foi publicado sem foto e alguém tem de
     lá ir. Medido depois de subir: "FOTO" a 11px ocupa 34px dos 52 da
     miniatura, por isso a restrição do desenho ("SEM" por cima de "FOTO" tem
     de caber") continua satisfeita com folga. */
  font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.2px;text-transform:uppercase;
  background:var(--warn);color:#fff;border-radius:3px;padding:2px 4px;pointer-events:none}
.c-thumb-box{position:relative}
.semimg-btn.on{border-color:var(--warn);color:var(--warn);background:var(--warn-soft)}
/* O banner de revisão: a página de referência e o que fazer a seguir.
   O link quebra por qualquer sítio de propósito — um URL de loja com
   parâmetros de campanha não cabe na largura do drawer e, sem isto,
   esticava a caixa e empurrava o resto do detalhe para fora do ecrã. */
.ffs-review .rev-fonte{margin:8px 0 0;font-size:var(--t-b3, 12px);line-height:1.5}
.ffs-review .rev-fonte a{color:var(--accent-ink);word-break:break-all}
.ffs-review .rev-fonte i{color:var(--muted)}
.ffs-review .rev-acoes{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:12px}
/* O par de fotografias da proposta de packshot: a que está na loja e a que o
   Codex gerou. LADO A LADO e do mesmo tamanho, porque a decisão que se pede é
   uma comparação — e um rótulo desfocado ao lado de um nítido só se compara
   se os dois estiverem à mesma escala. Em ecrã estreito passam a uma coluna,
   que é pior para comparar mas melhor do que duas imagens de 80px.
   Fundo axadrezado não: estas fotos são de fundo branco por definição, e o
   que interessa ver é o rótulo. */
.ffs-packshot .pk-par{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:12px 0 4px}
.ffs-packshot .pk-par figure{margin:0}
.ffs-packshot .pk-par img{width:100%;aspect-ratio:1;object-fit:contain;
  background:#fff;border:1px solid var(--paper-3);border-radius:6px;display:block;
  /* Clicar amplia. O cursor é o que diz isso, porque não há mais nada a
     dizê-lo, e sem ampliar não se lê o rodapé de um rótulo de suplemento. */
  cursor:zoom-in}
.ffs-packshot .pk-par img:hover{border-color:var(--accent-ink)}
.ffs-packshot .pk-par figcaption{margin-top:5px;font-size:var(--t-m2);
  color:var(--muted);text-align:center}
@media (max-width:640px){ .ffs-packshot .pk-par{grid-template-columns:1fr} }
/* A lista do que foi lido no rótulo. Fica ENTRE as fotos e os botões, porque é
   a coisa contra a qual se confere, e a ordem de leitura é essa: vejo as duas,
   leio a lista, decido. Fundo branco para se destacar do azul do banner sem
   parecer outro alerta. */
.ffs-packshot .pk-lista{margin:12px 0 0;padding:10px 12px;background:#fff;
  border:1px solid var(--paper-3);border-radius:6px;font-size:var(--t-b3, 12px)}
.ffs-packshot .pk-lista ul{margin:6px 0 0;padding-left:18px;line-height:1.6}
.ffs-packshot .pk-lista li{font-family:var(--mono);color:var(--ink)}
.ffs-packshot .pk-marca{font-family:var(--sans, inherit);font-size:var(--t-m2);
  color:var(--muted);text-transform:lowercase}
.ffs-packshot .pk-fora{margin:8px 0 0;color:var(--muted);line-height:1.5}
.ffs-review .rev-nota{font-size:var(--t-m2);color:var(--muted);flex:1;min-width:200px;line-height:1.45}
/* O campo da página certa herda o do "Otimizar" dos ingredientes: é o mesmo
   gesto (indicar uma fonte à mão) e não há razão para parecer outro. */
.ffs-review .rev-acoes .fonte-url{flex:1;min-width:220px;font-family:var(--mono);
  font-size:var(--t-m2);color:var(--ink);border:1px solid var(--paper-3);
  border-radius:6px;padding:6px 9px;background:#fff}
.ffs-review .rev-acoes .fonte-url::placeholder{color:var(--muted-2)}
.ffs-review .rev-acoes .fonte-url:focus{outline:none;border-color:var(--accent-ink)}
/* O aviso de que o site colado está na lista dos que nos bloqueiam. Ocupa a
   linha toda por baixo do campo (o `.rev-acoes` é flex), para não empurrar o
   botão para fora. Âmbar porque é um aviso e não um impedimento: quem indicou
   a página pode saber mais do que a lista, e o botão continua a funcionar. */
/* Os alertas laterais do banner de revisão: o que se passa com o produto além
   do motivo registado (esgotado, sem foto). Ver alertasDaRevisao. */
.ffs-review .rev-alerta{margin:8px 0 0;font-size:var(--t-m2);line-height:1.5;
  color:var(--muted);border-left:3px solid var(--warn-amber);padding-left:9px}

/* O stock na tabela, por baixo do handle: uma pill preta.
   Esgotado a cyan, o resto a branco — pedido do Rui a 2026-09-01. O cyan sobre
   preto tem contraste de sobra e não colide com nenhuma das cores de estado do
   dashboard (verde publicado, azul por rever, âmbar reservas), por isso lê-se
   como o que é: informação sobre o produto, não sobre o processamento.

   `width:fit-content` porque a célula do título é larga: sem isso a pill
   esticava-se pela coluna toda e deixava de parecer uma etiqueta. */
.c-title .stock-pill{display:inline-block;width:fit-content;margin-top:3px;
  padding:1px 7px;border-radius:999px;background:var(--ink);color:#fff;
  /* A letra fica no --t-m2: o design marca-o como o chão do legível e uma
     pill de stock não é sítio para o furar. Encolhe-se pelo espaço à volta. */
  font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.2px;
  font-weight:600;line-height:1.45}
.c-title .stock-pill.esgotado{color:#f472b6}

.ffs-review .rev-acoes .fonte-aviso{flex-basis:100%;margin:2px 0 0;
  font-family:var(--mono);font-size:var(--t-m2);line-height:1.5;
  color:var(--muted);border-left:3px solid var(--warn-amber);padding-left:9px}

/* Publicado com reservas — o produto foi para a loja mas ficou com alguma
   coisa por confirmar (ver badgeReservas em ffs-app.js).

   Âmbar e não vermelho, de propósito: vermelho é o estado "Erro", e aqui não
   se partiu nada — correu tudo e o resultado é que pode não prestar. Se isto
   gritasse como um erro, ou se aprendia a ignorar o vermelho, ou se ia à caça
   de avarias que não existem. É a mesma paleta do `.cat-aviso` (o aviso das
   categorias), que já resolve exactamente esta distinção no drawer.

   `display:block` + `width:fit-content` para cair POR BAIXO do "Publicado" em
   vez de disputar com ele os 12% da coluna — é a mesma solução do `.pbadge`
   nas colecções. O texto quebra (`white-space:normal`): um motivo cortado com
   reticências obrigava a ir ao tooltip para saber qual era, e o tooltip é para
   o detalhe, não para o essencial. */
.c-status .rbadge{display:block;width:fit-content;max-width:100%;margin-top:6px;padding:2px 6px;border-radius:3px;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.3px;line-height:1.4;cursor:help;
  color:#8a5a00;background:#fdf3e0;border:1px solid #f0dcb8;white-space:normal;text-align:left}
.reservas-btn.on{border-color:#8a5a00;color:#8a5a00;background:#fdf3e0}

/* O SINAL DE "SEM LISTA DE INGREDIENTES", pedido do Rui a 2026-09-05.

   O `jar-label` do Phosphor: um frasco com rótulo, que é literalmente o que
   falta ao produto. Em MAGENTA porque é a única cor que este dashboard não
   usa para mais nada — o verde é publicado, o laranja é reserva, o vermelho é
   erro. Uma cor já tomada fazia o sinal ler-se como um estado, e não é.

   Ao lado das outras pills e não por cima: as reservas e o estado continuam a
   ser a informação principal da coluna. Este é um sinal a mais, não um a
   substituir. */
.linha-stock{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.linha-stock .sem-ingredientes{display:inline-flex;align-items:center;justify-content:center;
  cursor:help;color:#c2117d;line-height:0}
.linha-stock .sem-ingredientes svg{display:block}
/* Acrescentar uma foto deixou de ser o remédio para quem ficou sem nenhuma e
   passou a estar sempre disponível — e com isso a caixa de aviso, com moldura,
   parágrafo e botão verde grande, deixou de fazer sentido: pesava mais do que
   a galeria que era suposto acompanhar.
   O que fica é o aviso reduzido a uma linha (só quando há mesmo o que avisar)
   e a via do URL escondida atrás de uma linha discreta. O gesto principal é
   largar o ficheiro na galeria; colar um endereço é o caminho menos usado dos
   dois e não tem que competir com ele. */
.img-aviso{margin:9px 0 0;font-size:var(--t-m1);line-height:1.5;color:var(--warn)}
.img-aviso b{font-weight:700}

.img-url{margin-top:9px}
.img-url-abrir{background:none;border:0;padding:0;cursor:pointer;
  font-family:var(--sans);font-size:var(--t-m2);color:var(--muted);
  text-decoration:underline;text-underline-offset:2px;
  text-decoration-color:var(--paper-3);transition:color .12s ease}
.img-url-abrir:hover,.img-url-abrir:focus-visible{color:var(--ink);
  text-decoration-color:var(--muted-2);outline:none}
.img-url-linha{display:flex;gap:6px;align-items:center;margin-top:7px}
.img-url-linha input{flex:1;font-family:var(--mono);font-size:var(--t-m2);
  padding:7px 9px;color:var(--ink);background:var(--paper-2);
  border:1px solid transparent;border-radius:6px;transition:background .12s ease,border-color .12s ease}
.img-url-linha input::placeholder{color:var(--muted-2)}
.img-url-linha input:focus{outline:none;background:#fff;border-color:var(--accent)}
.img-url-ok{font-family:var(--sans);font-size:var(--t-m2);font-weight:600;
  letter-spacing:.2px;padding:7px 12px;cursor:pointer;border-radius:6px;
  color:var(--accent-ink);background:var(--accent-soft);
  border:1px solid transparent;transition:background .12s ease,border-color .12s ease}
.img-url-ok:hover{border-color:var(--accent)}
.img-url-ok:disabled{opacity:.55;cursor:default}

/* Cada foto da galeria com o seu apagar, que só aparece ao passar o rato —
   apaga no Shopify e não há como desfazer, não é para se carregar por engano.
   Em ecrãs de toque não há hover, por isso aí fica sempre visível. */
/* A zona de largar fecha a grelha, no lugar onde estaria a foto seguinte, e
   toma o mesmo quadrado e o mesmo raio dos <image-slot> ao lado — é para ler
   como mais um lugar da galeria, não como um formulário à parte. */
.img-drop{aspect-ratio:1;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:5px;text-align:center;cursor:pointer;
  border:1.5px dashed var(--paper-3);border-radius:8px;background:var(--paper-2);
  color:var(--ink);opacity:.62;transition:opacity .12s ease,border-color .12s ease,background .12s ease}
.img-drop:hover,.img-drop:focus-visible{opacity:1;border-color:var(--accent);outline:none}
.img-drop.sobre{opacity:1;border-color:var(--accent);background:var(--accent-soft);border-style:solid}
.img-drop .mais{font-size:var(--t-h1);line-height:1;font-weight:300}
.img-drop .txt{font-size:var(--t-m2);line-height:1.35;padding:0 6px}
.img-drop .txt i{font-style:normal;opacity:.62}
/* Enquanto sobe, deixa de aceitar cliques e diz que está a trabalhar. */
.img-drop.a-enviar{pointer-events:none;opacity:.5;border-style:solid}
.img-drop.a-enviar .mais{animation:ffs-pulse 1s ease-in-out infinite}
@keyframes ffs-pulse{0%,100%{opacity:.35}50%{opacity:1}}

.img-slot-wrap{position:relative}
.img-apagar{position:absolute;top:4px;right:4px;width:22px;height:22px;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--paper-3);border-radius:50%;background:rgba(255,255,255,.94);
  color:var(--ink);font-size:var(--t-b2);line-height:1;cursor:pointer;padding:0;
  opacity:0;transition:opacity .12s ease,color .12s ease,border-color .12s ease}
.img-slot-wrap:hover .img-apagar,.img-apagar:focus-visible{opacity:1}
.img-apagar:hover{color:var(--warn);border-color:var(--warn)}
.img-apagar:disabled{cursor:default;opacity:.4}
@media (hover:none){.img-apagar{opacity:1}}
/* O "Otimizar" publica no Shopify de imediato. Sem isto, o "Guardar" fica
   desactivado a seguir a uma regeneração e parece que não gravou nada. */
.ffs-fld .publicado{font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.3px;
  text-transform:uppercase;color:var(--accent);border:1px solid var(--accent);
  border-radius:3px;padding:1px 5px;white-space:nowrap}
.publicado-nota{margin:8px 0 0;font-size:var(--t-m2);line-height:1.4;color:var(--muted);opacity:.9}
.publicado-nota b{color:var(--ink);font-weight:600}
.ffs-fld.flash{animation:ffsflash 1.2s ease}
@keyframes ffsflash{0%,100%{background:transparent;margin:0;padding:18px 0}20%,60%{background:var(--warn-soft);margin:0 -16px;padding:18px 16px}}
.ffs-fld{border-top:1px solid var(--paper-3);padding:18px 0}
.ffs-fld:first-of-type{border-top:none;padding-top:4px}
.ffs-fld.dirty{background:var(--accent-soft);margin:0 -16px;padding:18px 16px;border-top-color:transparent}
.ffs-fld .fh{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.ffs-fld .fh .dot{width:6px;height:6px;border-radius:50%;background:var(--paper-3);margin-left:auto;flex-shrink:0}
.ffs-fld.dirty .fh .dot{background:var(--warn)}
.ffs-fld .kind.orig{color:var(--muted);background:var(--paper-2);border-color:var(--paper-3)}
.ffs-fld [data-editable]{cursor:text}
.ffs-fld [data-editable]:focus{outline:2px solid var(--accent-ink);outline-offset:2px}
.ffs-fld .plain[data-editable]{cursor:text}
.ffs-fld .code[data-editable]:focus,.ffs-fld .render[data-editable]:focus,.ffs-fld .plain[data-editable]:focus{outline:2px solid var(--accent-ink);outline-offset:2px}
.ffs-pact{display:flex;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.fld-count{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);margin-top:6px;text-align:right}
.fld-count.over{color:var(--warn);font-weight:700}
.seo-row .fld-count{margin-top:5px}
.rerun-btn{display:flex;align-items:center;gap:7px;margin-right:auto}
.rerun-btn img{border-radius:3px;flex-shrink:0}
.rerun-btn.running{opacity:.7;cursor:wait}
.fld-tb{display:flex;align-items:center;gap:4px;margin-bottom:8px}
.fld-tb button{appearance:none;border:1px solid var(--paper-3);background:#fff;width:26px;height:26px;border-radius:5px;
  font-family:var(--sans);font-size:var(--t-m2);font-weight:700;color:var(--muted);cursor:pointer;transition:.12s}
.fld-tb button:hover{background:var(--paper-2);color:var(--ink)}
.ffs-fld.original{background:var(--paper-2);margin:0 -16px;padding:18px 16px}
.orig-note{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);margin-bottom:8px;font-style:italic}
.orig-box{background:#fff;border:1.5px dashed var(--paper-3);padding:12px 15px;font-size:var(--t-b2);
  font-weight:600;color:var(--muted);font-family:var(--mono);letter-spacing:.2px;resize:vertical;overflow:auto;min-height:44px;display:block}
.img-gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:10px}
.img-gallery image-slot{aspect-ratio:1}
.fld-select{width:100%;padding:10px 30px 10px 12px}
.seo-row{margin-bottom:12px}
.seo-row:last-child{margin-bottom:0}
.seo-l{display:block;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
.seo-row .plain{font-size:var(--t-b2)}
.tb-sel{height:28px;font-size:var(--t-m2);padding:0 24px 0 9px;background-position:right 7px center}
.tb-sep{width:1px;background:var(--paper-3);margin:2px 2px}
.ffs-fld .render table{border-collapse:collapse;width:100%;margin:8px 0}
.ffs-fld .render td,.ffs-fld .render th{border:1px solid var(--paper-3);padding:8px 10px}
.ffs-fld .fh .fn{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--muted)}
.ffs-fld .fh .kind{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.6px;text-transform:uppercase;
  padding:2px 7px;border-radius:3px;background:var(--paper-2);color:var(--muted-2);border:1px solid var(--paper-3)}
.ffs-fld .fh .kind.html{color:var(--accent-ink);background:var(--accent-soft);border-color:transparent}
.ffs-fld .fh .kind.json{color:#2d3f8f;background:var(--info-soft);border-color:transparent}
.ffs-fld .render{background:#fff;border:1.5px solid var(--paper-3);padding:14px 16px;font-size:var(--t-b2);
  line-height:1.55;color:var(--ink-soft);resize:vertical;overflow:auto;min-height:60px}
.ffs-fld .render h2,
/* O tema da loja não usa <h2>: os títulos de secção da descrição são
   <p class="h2-style"> (ver montar_descricao no backend). Sem esta regra
   apareciam no drawer como texto corrido, indistinguíveis do resto. */
.ffs-fld .render .h2-style{font-family:var(--sans);font-size:var(--t-b1);font-weight:800;
  margin:18px 0 8px;letter-spacing:-.3px;color:var(--ink);padding-bottom:5px;
  border-bottom:1.5px solid var(--paper-3)}
.ffs-fld .render h2:first-child,.ffs-fld .render .h2-style:first-child{margin-top:0}
.ffs-fld .render h3,.ffs-fld .render h4{font-family:var(--sans);font-size:var(--t-b2);font-weight:700;margin:12px 0 5px;letter-spacing:-.2px}
.ffs-fld .render h3:first-child,.ffs-fld .render h4:first-child{margin-top:0}
.ffs-fld .render strong,.ffs-fld .render b{font-weight:700}
.ffs-fld .render p{margin:0 0 9px}.ffs-fld .render p:last-child{margin-bottom:0}
.ffs-fld .render ul,.ffs-fld .render ol{margin:0 0 9px;padding-left:20px}
.ffs-fld .render li{margin-bottom:4px}
.ffs-fld .code{background:#0e0e0e;color:#c9e89a;border:none;font-family:var(--mono);font-size:var(--t-m1);
  line-height:1.5;padding:14px 16px;white-space:pre-wrap;word-break:break-word;resize:vertical;overflow:auto;min-height:60px;display:block}
.ffs-fld .vazio{font-family:var(--mono);font-size:var(--t-m1);color:var(--warn);background:var(--warn-soft);
  border:1.5px dashed var(--warn);padding:11px 14px}
.ffs-fld .plain{background:#fff;border:1.5px solid var(--paper-3);padding:12px 15px;font-size:var(--t-b2);
  font-weight:600;color:var(--ink);resize:vertical;overflow:auto;min-height:44px;display:block}

/* ---- SETTINGS · prompts ------------------------------------------- */
.ffs-stage{margin-bottom:34px}
.ffs-stage .sth{display:flex;align-items:baseline;gap:14px;margin-bottom:4px;padding-bottom:12px;
  border-bottom:2px solid var(--ink)}
.ffs-stage .sth .num{font-family:var(--sans);font-weight:800;font-size:var(--t-d3);letter-spacing:-1.5px;color:var(--ink)}
.ffs-stage .sth .nm{font-family:var(--sans);font-weight:800;font-size:var(--t-h1);letter-spacing:-.4px}
.ffs-stage .sth .nt{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-left:auto;
  letter-spacing:.2px;text-align:right}
.ffs-prompts{display:flex;flex-direction:column;gap:14px;margin-top:16px}
.ffs-prompt{border:2px solid var(--ink);background:#fff}
.ffs-prompt.dirty{box-shadow:5px 5px 0 var(--accent)}
.ffs-prompt .ph{display:flex;align-items:center;gap:12px;padding:14px 18px;cursor:pointer;
  border-bottom:1.5px solid transparent;transition:.12s;background:#fff}
.ffs-prompt.open .ph{border-bottom-color:var(--paper-2);background:var(--paper-2)}
.ffs-prompt .ph .tag{font-family:var(--mono);font-size:var(--t-m1);font-weight:800;letter-spacing:.5px;color:#0a0a0a;
  background:var(--accent);padding:6px 11px;border-radius:4px;white-space:nowrap}
.ffs-prompt .ph .ref{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);letter-spacing:.2px}
.ffs-prompt .ph .out{font-size:var(--t-m1);color:var(--muted-2);margin-left:6px;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;flex:1}
.ffs-prompt .ph .dot{width:8px;height:8px;border-radius:50%;background:var(--accent);flex-shrink:0;display:none}
.ffs-prompt.dirty .ph .dot{display:block}
.ffs-prompt .ph .chev{font-family:var(--mono);font-size:var(--t-b2);color:var(--muted-2);transition:transform .18s;flex-shrink:0}
.ffs-prompt.open .ph .chev{transform:rotate(90deg)}
.ffs-prompt .pbody{display:none;padding:18px}
.ffs-prompt.open .pbody{display:block}
.ffs-io{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
.ffs-io .box{border:1.5px solid var(--paper-3);background:var(--paper-2);padding:11px 14px}
.ffs-io .box .l{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--muted);margin-bottom:8px}
.ffs-io .chips{display:flex;flex-wrap:wrap;gap:6px}
.ffs-io .chips .c{font-family:var(--mono);font-size:var(--t-m2);font-weight:600;color:var(--ink-soft);background:#fff;
  border:1px solid var(--paper-3);padding:3px 8px;border-radius:3px}
.ffs-io .otext{font-size:var(--t-m1);color:var(--ink-soft);line-height:1.4;font-weight:600}
.ffs-io .otext .kk{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.5px;text-transform:uppercase;
  background:var(--ink);color:var(--paper);padding:2px 6px;border-radius:3px;margin-left:8px;vertical-align:middle}
.ffs-io .fb{font-family:var(--mono);font-size:var(--t-m2);color:var(--warn);margin-top:8px}
.ffs-plabel{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px}
.ffs-plabel .l{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted)}
.ffs-plabel .sref{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2)}
.ffs-prompt textarea{width:100%;min-height:180px;resize:vertical;font-family:var(--mono);font-size:var(--t-m1);
  line-height:1.6;color:var(--ink);background:#0e0e0e;color:#e8e6dd;border:1.5px solid var(--ink);
  padding:14px 16px;outline:none}
.ffs-prompt textarea:focus{border-color:var(--accent)}
.ffs-pact{display:flex;align-items:center;gap:10px;margin-top:14px}
.ffs-pact .chars{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);margin-left:auto}
/* A página de onde tirar a composição, ao lado do Otimizar.

   `flex:1` com `min-width` para encher o espaço que sobra entre o botão e
   o contador de caracteres, mas quebrar para a linha de baixo em vez de
   espremer os botões quando o drawer é estreito (o `.ffs-pact` já tem
   `flex-wrap`). Discreto de propósito: é um campo opcional que a maioria
   dos produtos nunca precisa, e não pode competir com o botão que está ao
   lado. */
.ffs-pact .fonte-url{flex:1;min-width:210px;font-family:var(--mono);font-size:var(--t-m2);
  color:var(--ink);border:1px solid var(--paper-3);border-radius:6px;padding:6px 9px;background:#fff}
.ffs-pact .fonte-url::placeholder{color:var(--muted-2)}
.ffs-pact .fonte-url:focus{outline:none;border-color:var(--accent-ink)}
.ffs-btn{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.8px;text-transform:uppercase;
  padding:10px 16px;border:1.5px solid var(--ink);background:#fff;color:var(--ink);cursor:pointer;transition:.12s}
.ffs-btn:hover{background:var(--ink);color:var(--paper)}
.ffs-btn.green{background:var(--accent);border-color:var(--accent);color:#0a0a0a}
.ffs-btn.green:hover{background:#9bd83e;border-color:#9bd83e}
.ffs-btn.ghost{background:transparent;border-color:var(--paper-3);color:var(--muted)}
.ffs-btn.ghost:hover{background:var(--paper-2);color:var(--ink);border-color:var(--muted-2)}
.ffs-btn:disabled{opacity:.4;cursor:not-allowed}
.ffs-btn.sm{padding:7px 12px;font-size:var(--t-m2)}

/* fontes read-only */
.ffs-sources{border:2px solid var(--paper-3);background:var(--paper-2);margin-top:8px}
.ffs-sources .sh{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--muted);padding:13px 18px;border-bottom:1px solid var(--paper-3)}
.ffs-src{display:flex;align-items:center;gap:14px;padding:12px 18px;border-bottom:1px dashed var(--paper-3);font-size:var(--t-b2)}
.ffs-src:last-child{border-bottom:none}
.ffs-src .nm{font-weight:700;min-width:190px}
.ffs-src .rf{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2)}
.ffs-src .nt{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-left:auto;text-align:right}
.ffs-src.warn .nt{color:var(--warn);font-weight:700}

/* ---- Save bar (flutuante) ----------------------------------------- */
.ffs-savebar{position:fixed;bottom:24px;left:50%;transform:translate(-50%,120px);z-index:120;
  background:#0a0a0a;color:#f3efe4;border:2px solid var(--accent);display:flex;align-items:center;gap:18px;
  padding:12px 14px 12px 22px;box-shadow:0 14px 44px rgba(0,0,0,.35);transition:transform .24s cubic-bezier(.2,.7,.2,1)}
.ffs-savebar.on{transform:translate(-50%,0)}
.ffs-savebar .txt{font-family:var(--mono);font-size:var(--t-m1);font-weight:600;letter-spacing:.3px}
.ffs-savebar .txt b{color:var(--accent)}

/* ---- toast -------------------------------------------------------- */
.ffs-toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%) translateY(90px);
  background:#0a0a0a;color:#f3efe4;font-family:var(--mono);font-size:var(--t-m1);font-weight:600;
  padding:14px 22px;border-radius:8px;z-index:400;opacity:0;transition:.3s;display:flex;align-items:center;gap:12px;
  box-shadow:0 12px 40px rgba(0,0,0,.3)}
.ffs-toast.show{transform:translateX(-50%) translateY(0);opacity:1}
.ffs-toast .dot{width:8px;height:8px;border-radius:50%;background:var(--accent)}

@media(max-width:820px){
  .ffs-io{grid-template-columns:1fr}
  .ffs-tab{min-width:130px;padding:14px 16px}
  .ffs-intro h1{font-size:var(--t-d2)}
  .ffs-kpi,.ffs-drill{grid-template-columns:1fr}
  .ffs-kpi .big{border-right:none;border-bottom:1px solid var(--paper-3)}
}

/* ---- Campo de etiquetas (coleções / tags) ---------------------------- */
.chips-campo{background:#fff;border:1.5px solid var(--paper-3);border-radius:8px;padding:12px 14px;
  display:flex;flex-direction:column;gap:10px}
/* `chips-lista` e não `chips`: o crachá do tipo de campo no cabeçalho fica
   com class="kind chips" e um querySelector(".chips") apanhava-o primeiro — as
   etiquetas iam parar lá dentro. */
.chips-lista{display:flex;flex-wrap:wrap;gap:7px;min-height:26px;align-items:flex-start}
.chips-lista .chip{display:inline-flex;align-items:center;gap:6px;background:var(--accent-soft);color:var(--accent-ink);
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;padding:5px 6px 5px 10px;border-radius:4px;
  max-width:100%;overflow-wrap:anywhere;line-height:1.35}
.chips-lista .chip-x{appearance:none;border:none;background:none;color:var(--accent-ink);opacity:.55;cursor:pointer;
  font-size:var(--t-b2);line-height:1;padding:0 3px}
.chips-lista .chip-x:hover{opacity:1}
.chips-lista .chip-vazio{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);font-style:italic}
.chips-campo .ffs-combo.filtro{max-width:none;min-width:0;flex:none}
.chips-nota{margin:0;font-family:var(--mono);font-size:var(--t-m2);line-height:1.5;color:var(--muted-2);letter-spacing:.2px}
.chips-nota b{color:var(--warn-amber)}

/* Fotos que o Shopify recusou. Barra lateral âmbar, como os outros avisos da
   casa: não é erro do sistema (nada partiu), é trabalho para uma pessoa. Sem
   isto a galeria aparecia vazia e ninguém sabia que tinha havido tentativa. */
.img-falhou{margin:10px 0 0;padding:9px 12px;border-left:3px solid var(--warn-amber);
  background:var(--paper-2);border-radius:0 5px 5px 0;
  font-family:var(--mono);font-size:var(--t-m2);line-height:1.55;color:var(--muted)}
.img-falhou b{display:block;color:var(--ink);margin-bottom:4px}
.img-falhou p{margin:0}
.img-falhou p + p{margin-top:4px}
.img-falhou .img-falhou-nota{margin-top:6px;color:var(--muted-2)}

/* Campo a ser optimizado por um agente (botão "Otimizar" do próprio campo). */
.ffs-fld.a-correr{opacity:.75;position:relative}
.ffs-fld.a-correr::after{content:"";position:absolute;inset:0;border-radius:8px;
  background:repeating-linear-gradient(135deg,rgba(134,194,50,.08) 0 12px,transparent 12px 24px);
  pointer-events:none}

/* ---- Definições · 03 Tags -------------------------------------------- */
/* A lista é a tabela do log (.ffs-tablewrap + table.ffs-log), com as larguras
   próprias — a tabela é `table-layout:fixed`, por isso as colunas têm mesmo de
   ser declaradas. Até 2026-08-17 isto era uma pilha de cartões que só mostrava
   arrumação interna do vocabulário (nível na árvore, ramo, quantas tags o
   mapeamento encontrou); passou a dizer o que enche, quantos produtos tem e
   em que estado está. */
/* As células são `ct-*` e não `c-*`: `table.ffs-log .c-tag` já é o chip verde
   das etiquetas do log, e reaproveitar o nome metia cada nome de tag dentro de
   uma pastilha estreita, cortado a meio. */
/* A Coleção fica SEM largura declarada, de propósito: é a coluna elástica que
   absorve a sobra — mesmo padrão do título no log. Com todas declaradas, o que
   faltasse para os 100% ia parar ao Estado e abria um rasgo vazio entre o
   crachá e a seta. É também a coluna com os títulos mais compridos. */
table.ffs-log.ffs-tags .th-nome,table.ffs-log.ffs-tags .ct-nome{width:26%;text-align:left}
table.ffs-log.ffs-tags .th-col,table.ffs-log.ffs-tags .ct-col{text-align:left}
table.ffs-log.ffs-tags .th-produtos,table.ffs-log.ffs-tags .ct-produtos{width:13%;text-align:center}
table.ffs-log.ffs-tags .th-estado,table.ffs-log.ffs-tags .ct-estado{width:14%;text-align:center}
/* Alinhado ao topo: a coluna da tag tem três linhas e as outras uma — a
   ancorar ao meio, o nome da tag e a coleção dele deixavam de estar à mesma
   altura, que é o que faz a tabela ler-se de lado. */
table.ffs-log.ffs-tags tbody td{vertical-align:top;padding-top:12px;padding-bottom:12px}
/* Cabeçalhos e crachás numa linha só. Num ecrã estreito o "3 por rever"
   partia-se em duas linhas e deixava de se ler como um crachá; a tabela tem
   `min-width` e o invólucro rola na horizontal, por isso não há nada a
   ganhar em deixá-los quebrar. */
table.ffs-log.ffs-tags thead th{white-space:nowrap}
.ffs-tags .ct-estado>span{display:inline-block;white-space:nowrap}

/* Os nomes QUEBRAM em vez de serem cortados. "Vitaminas, Minerais e Produtos
   Naturais" com reticências não se distingue de "Vitaminas, Minerais e
   Produtos Nacionais" — e o nome da tag é a coluna que se lê primeiro. */
.ffs-tags .ct-nome b{display:block;font-family:var(--sans);font-size:var(--t-b2);font-weight:700;
  color:var(--ink);line-height:1.35;overflow-wrap:anywhere}
/* A tag como está escrita na loja. Só aparece quando difere do nome da ficha
   — ver linhaDaTag. */
.ffs-tags .ct-real{display:block;margin-top:4px;font-family:var(--mono);font-size:var(--t-m2);line-height:1.4;
  color:var(--accent-ink);cursor:help;overflow-wrap:anywhere}
.ffs-tags .ct-ramo{display:block;margin-top:4px;font-family:var(--mono);font-size:var(--t-m2);
  letter-spacing:.3px;text-transform:uppercase;color:var(--muted-2);overflow-wrap:anywhere}
.ffs-tags .ct-col{font-family:var(--sans);font-size:var(--t-m1);line-height:1.4;color:var(--ink);
  overflow-wrap:anywhere}
.ffs-tags .ct-semcol{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);cursor:help}
.ffs-tags .ct-dup{font-style:normal;margin-left:6px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;
  color:#8a5a00;background:#fdf3e0;border-radius:3px;padding:1px 5px;cursor:help}
.ffs-tags .ct-produtos{font-family:var(--mono);font-size:var(--t-b2);font-weight:700;cursor:help;
  color:var(--ink);font-variant-numeric:tabular-nums}
/* Quando a tag não enche colecção nenhuma o número é o da própria etiqueta, e
   a célula tem de o dizer — senão passava pelo número da colecção, que não
   existe. */
.ffs-tags .ct-fonte{display:block;margin-top:2px;font-style:normal;font-size:var(--t-m2);font-weight:400;
  letter-spacing:.3px;text-transform:uppercase;color:var(--muted-2)}
.ffs-tags .ct-produtos.deEtiqueta{color:var(--muted)}
/* "—" nunca é zero: é "não sabemos". Fica cinzento para não se confundir com
   um número medido. */
.ffs-tags .ct-semnum{font-style:normal;color:var(--muted-2);cursor:help;font-weight:400}
.ffs-tags tr.cat-linha.off .ct-nome b{text-decoration:line-through;color:var(--muted-2)}
.ffs-tags tr.cat-linha.on{background:var(--paper-2)}
/* A ficha aberta é uma linha da tabela, mas não é clicável nem se ilumina — o
   que lá está dentro são campos de edição. */
.ffs-tags tr.cat-editor,.ffs-tags tr.cat-editor:hover{background:#fff;cursor:default}
.ffs-tags tr.cat-editor>td{padding:0}
/* De quando são as contagens. Não são ao vivo. */
.cat-fonte{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);margin:-10px 0 12px}
.cat-desligada{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  padding:2px 7px;border-radius:3px;cursor:help;color:var(--muted-2);background:var(--paper-2);
  border:1px solid var(--paper-3)}
.cat-corpo{padding:4px 16px 18px;display:flex;flex-direction:column;gap:14px}
.cat-campo{display:flex;flex-direction:column;gap:5px}
.cat-et{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--muted)}
.cat-et.aviso{color:var(--warn-amber)}
.cat-campo textarea{font-family:var(--sans);font-size:var(--t-b2);line-height:1.5;color:var(--ink);
  border:1.5px solid var(--paper-3);border-radius:7px;padding:10px 12px;background:#fff;resize:vertical}
.cat-campo textarea::placeholder{color:var(--muted-2);opacity:.7;font-size:var(--t-m1);line-height:1.5}
.cat-campo.por-preencher textarea{background:#fffdf7;border-color:#f0dcb8}
.cat-falta{font-style:normal;margin-left:8px;color:#8a5a00;background:#fdf3e0;border-radius:3px;
  padding:1px 6px;font-size:var(--t-m2);letter-spacing:.4px}
.cat-ajuda{font-family:var(--sans);font-size:var(--t-m1);color:var(--muted-2);line-height:1.45;margin:-2px 0 2px}
.cat-campo textarea:focus{outline:none;border-color:var(--accent-ink)}
.cat-accoes{display:flex;align-items:center;gap:16px}
.cat-activa{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);margin-right:auto}
.cat-activa input{width:15px;height:15px;accent-color:var(--ink);cursor:pointer}

/* ---- Proposta de categorias (classificador) --------------------------- */
.sug,.sug-vazio{border:1.5px dashed var(--paper-3);border-radius:7px;padding:11px 13px;background:var(--paper-2)}
.sug-vazio{display:flex;align-items:center;gap:12px;justify-content:space-between;
  font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2)}
.sug-cab{display:flex;align-items:center;gap:10px;margin-bottom:9px;
  font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.4px;text-transform:uppercase;color:var(--muted)}
.sug-cab b{color:var(--ink);font-weight:700}
.sug-ramo{color:var(--accent-ink);background:var(--accent-soft);border-radius:3px;padding:2px 7px}
.sug-cab .rerun-btn{margin-left:auto}
.sug-lista{display:flex;flex-wrap:wrap;gap:7px;align-items:flex-start}
.sug-chip{appearance:none;text-align:left;border:1.5px dashed var(--accent-ink);background:#fff;
  color:var(--accent-ink);border-radius:5px;padding:6px 10px;cursor:pointer;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;display:flex;flex-direction:column;gap:3px;max-width:100%}
.sug-chip:hover{background:var(--accent-soft)}
.sug-porque{font-weight:400;font-size:var(--t-m2);color:var(--muted);text-transform:none;letter-spacing:0;
  max-width:260px;white-space:normal;line-height:1.35}
.sug-todas{appearance:none;border:none;background:var(--ink);color:var(--paper);border-radius:5px;
  padding:7px 12px;cursor:pointer;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;align-self:center}
.sug-nada,.sug-recusadas{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);line-height:1.5}
.sug-recusadas{margin-top:9px;padding-top:8px;border-top:1px solid var(--paper-3)}
.sug-recusadas b{color:var(--warn-amber);display:block;margin-bottom:5px}
/* Uma recusa por linha, com o porquê por baixo do nome. Era uma lista de nomes
   separados por vírgula com a justificação num title= — ver o comentário no
   ffs-app.js. */
.sug-recusada{display:flex;flex-direction:column;gap:2px;padding:5px 0}
.sug-recusada + .sug-recusada{border-top:1px dashed var(--paper-3)}
.sug-recusada-nome{color:var(--ink);font-weight:700}
/* O .sug-porque nasceu dentro de um chip, onde 260px é a largura do chip. Aqui
   tem a linha toda, e a justificação do revisor é mais comprida. */
.sug-recusadas .sug-porque{max-width:none}

/* Produtos com categorias propostas à espera de aprovação. */
/* Vai por baixo das colecções, não do estado: a proposta é sobre ONDE o
   produto aparece na loja, não sobre como correu o processamento. */
.c-coll .pbadge{display:block;width:fit-content;margin-top:6px;padding:2px 6px;border-radius:3px;
  background:var(--accent-ink);color:#fff;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;
  letter-spacing:.3px;cursor:help;line-height:1.4}
.prop-btn.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.prop-btn.on img{filter:brightness(0) invert(1)}
.sug-chip .sug-tags{font-family:var(--mono);font-size:var(--t-m2);font-weight:400;color:var(--muted-2);
  text-transform:none;letter-spacing:0;white-space:normal;max-width:260px;line-height:1.35}
.sug-chip .sug-tags.aviso{color:var(--warn-amber)}
.sug-chip.inerte{border-style:dotted;opacity:.75}
.cat-aviso{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  padding:2px 7px;border-radius:3px;flex-shrink:0;cursor:help;color:#8a5a00;background:#fdf3e0;border:1px solid #f0dcb8}
.cat-origem{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  padding:2px 7px;border-radius:3px;flex-shrink:0;cursor:help;color:#2d3f8f;background:var(--info-soft)}
.cat-problemas{border:1.5px solid #f0dcb8;background:#fdf3e0;border-radius:7px;padding:10px 13px;
  font-family:var(--mono);font-size:var(--t-m2);color:#8a5a00;line-height:1.6}
.cat-problemas b{display:block;margin-bottom:4px;letter-spacing:.5px;text-transform:uppercase;font-size:var(--t-m2)}
.cat-problemas ul{margin:0;padding-left:16px}
.cat-mapa{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted);line-height:1.6;
  background:var(--paper-2);border-radius:7px;padding:9px 13px}
.cat-mapa b{color:var(--ink)}
.cat-mapa p{margin:0}
.cat-mapa p+p{margin-top:7px}
/* A repartição por etiqueta. Vive aqui, e não na tabela, porque quase todas as
   colecções desta loja exigem duas etiquetas ao mesmo tempo — na tabela era
   uma pilha de números por linha, sem espaço para dizer o que são. */
/* O número fica ao lado do nome da etiqueta, não encostado à direita da
   caixa: são duas ou três linhas, e com a caixa a ocupar a largura toda o
   número ficava a meio metro do nome a que pertence — o olho tinha de
   atravessar o vazio para os juntar. */
.cat-porTag{list-style:none;margin:7px 0;padding:0;border-top:1px solid var(--paper-3);
  border-bottom:1px solid var(--paper-3)}
.cat-porTag li{display:flex;align-items:baseline;justify-content:flex-start;gap:10px;
  padding:5px 0}
.cat-porTag li+li{border-top:1px dotted var(--paper-3)}
.cat-porTag span{overflow-wrap:anywhere}
.cat-porTag b{font-variant-numeric:tabular-nums;flex-shrink:0;color:var(--ink)}
.cat-porTag b::after{content:" produtos";font-weight:400;color:var(--muted-2)}

/* Progresso da revisão das tags — com 262 para rever, saber quanto falta é
   metade do trabalho. */
.cat-progresso{display:flex;align-items:center;gap:14px;margin:-8px 0 20px;
  font-family:var(--mono);font-size:var(--t-m2);color:var(--muted)}
.cat-progresso .cat-barra{flex:0 0 220px;height:7px;background:var(--paper-3);border-radius:999px;overflow:hidden}
.cat-progresso .cat-barra i{display:block;height:100%;background:var(--accent);border-radius:999px}
.cat-progresso b{color:var(--ink);font-weight:700}
.cat-progresso b.av{color:#8a5a00}
.cat-ok{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  padding:2px 7px;border-radius:3px;flex-shrink:0;cursor:help;color:var(--accent-ink);background:var(--accent-soft)}

/* Campos escritos pelo sistema, à espera de confirmação humana. */
.cat-campo.sugerido textarea{border-color:var(--info);background:#fbfcff}
.cat-campo.confirmado textarea{border-color:var(--accent-ink);background:#fbfdf7}
.cat-rascunho{font-style:normal;margin-left:8px;color:#2d3f8f;background:var(--info-soft);
  border-radius:3px;padding:1px 6px;font-size:var(--t-m2);letter-spacing:.4px}
.cat-aceitar{appearance:none;border:1px solid var(--info);background:#fff;color:#2d3f8f;
  border-radius:3px;padding:1px 8px;margin-left:6px;cursor:pointer;
  font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase}
.cat-aceitar:hover{background:var(--info-soft)}
.cat-aceitar.aceite{border-color:var(--accent-ink);color:var(--accent-ink);background:var(--accent-soft)}

/* ---- Definições · 04 Utilizadores ------------------------------------ */
table.ffs-users{table-layout:fixed}
table.ffs-users .th-nome,table.ffs-users .c-title{width:22%}
table.ffs-users .th-email,table.ffs-users .c-uemail{width:26%}
table.ffs-users .th-func,table.ffs-users .c-func{width:17%}
table.ffs-users .th-estado,table.ffs-users .c-status{width:13%}
table.ffs-users .th-acesso,table.ffs-users .c-time{width:17%}
table.ffs-users .th-go,table.ffs-users .c-go{width:30px}
table.ffs-users .c-uemail{font-family:var(--mono);font-size:var(--t-m1);color:var(--muted);overflow-wrap:anywhere}
table.ffs-users .c-func select{width:100%}

/* Tabela dos sites negativos (Definições > 02). Mesmas medidas da de
   utilizadores: o domínio é o que se lê primeiro, o motivo é o que se lê a
   seguir, e o resto encolhe. */
table.ffs-sites{table-layout:fixed}
table.ffs-sites .th-site,table.ffs-sites .c-site{width:22%}
table.ffs-sites .th-motivo,table.ffs-sites .c-motivo{width:25%}
table.ffs-sites .th-origem,table.ffs-sites .c-origem{width:10%}
table.ffs-sites .th-vezes,table.ffs-sites .c-vezes{width:7%}
table.ffs-sites .th-quando,table.ffs-sites .c-time{width:13%}
table.ffs-sites .th-estado,table.ffs-sites .c-status{width:11%}
table.ffs-sites .th-tirar,table.ffs-sites .c-tirar{width:12%;text-align:right}
table.ffs-sites .c-site{font-family:var(--mono);font-size:var(--t-m1);font-weight:700;
  letter-spacing:-.2px;overflow-wrap:anywhere}
table.ffs-sites .c-origem,table.ffs-sites .c-vezes{font-family:var(--mono);font-size:var(--t-m1);color:var(--muted)}
/* As quatro colunas do meio são valores curtos — números, uma palavra, um
   badge. Alinhadas à esquerda ficavam a boiar num espaço largo de mais. */
table.ffs-sites .th-origem,table.ffs-sites .c-origem,
table.ffs-sites .th-vezes,table.ffs-sites .c-vezes,
table.ffs-sites .th-quando,table.ffs-sites .c-time,
table.ffs-sites .th-estado,table.ffs-sites .c-status{text-align:center}
table.ffs-sites .c-status .ffs-st{justify-content:center}
table.ffs-sites .c-motivo .h{display:block;margin-top:3px;font-family:var(--mono);
  font-size:var(--t-m2);color:var(--muted);font-weight:500;overflow-wrap:anywhere}
.c-title .u-eu{margin-left:7px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;
  text-transform:uppercase;color:var(--accent-ink);background:var(--accent-soft);border-radius:3px;padding:2px 6px}
.ffs-adminonly{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);letter-spacing:.2px}

/* Drawer do utilizador */
.udata{background:#fff;border:1.5px solid var(--paper-3);border-radius:8px;overflow:hidden}
.ud-row{display:flex;align-items:baseline;gap:14px;padding:11px 15px;border-bottom:1px solid var(--paper-2)}
.ud-row:last-child{border-bottom:none}
.ud-l{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.8px;text-transform:uppercase;
  color:var(--muted-2);width:120px;flex-shrink:0}
.ud-v{font-family:var(--sans);font-size:var(--t-b2);color:var(--ink);overflow-wrap:anywhere}

/* Log de actividade: linha do tempo, e não uma tabela de etiquetas. São
   acontecimentos por ordem — a forma tem de dizer isso mesmo. */
.ulog{display:flex;flex-direction:column;border-left:2px solid var(--paper-3);padding-left:16px;margin-left:4px}
.ul-row{position:relative;padding:9px 0}
.ul-row::before{content:"";position:absolute;left:-21px;top:14px;width:7px;height:7px;border-radius:50%;
  background:var(--accent);border:2px solid #fff}
/* Sessões são o fundo do log — o que interessa é o que se fez, não quem entrou. */
.ul-row[data-tipo="sessao"]::before{background:var(--paper-3)}
.ul-row[data-tipo="credencial"]::before,.ul-row[data-tipo="conta"]::before{background:var(--info)}
.ul-t{display:block;font-family:var(--mono);font-size:var(--t-m2);letter-spacing:.6px;text-transform:uppercase;
  color:var(--muted-2);margin-bottom:3px}
.ul-x{font-family:var(--sans);font-size:var(--t-b2);color:var(--ink-soft);line-height:1.45;overflow-wrap:anywhere}
.ul-x b{font-weight:700;color:var(--ink)}
.ul-nada{font-family:var(--sans);font-size:var(--t-m1);color:var(--muted);line-height:1.5;padding:9px 0}
.ffs-dh .dh-actions .rf.danger{color:var(--err);border-color:#f0c9c9}
.ffs-dh .dh-actions .rf.danger:hover{background:#fdf0f0}

/* Convidar */
.inv-form{display:flex;flex-direction:column;gap:13px;padding:4px 0 6px}
.inv-form .fl{display:flex;flex-direction:column;gap:6px;font-family:var(--mono);font-size:var(--t-m2);
  letter-spacing:1px;text-transform:uppercase;color:var(--muted);font-weight:700}
.inv-form input,.inv-form select{font-family:var(--sans);font-size:var(--t-b2);font-weight:600;color:var(--ink);
  border:1.5px solid var(--paper-3);padding:10px 12px;border-radius:6px;background:#fff}
.inv-form input:focus,.inv-form select:focus{outline:none;border-color:var(--accent-ink)}
.inv-cred{font-family:var(--mono);font-size:var(--t-m1);font-weight:700;background:var(--paper-2);
  border-radius:4px;padding:3px 8px;display:inline-block;margin-top:4px;overflow-wrap:anywhere}
.inv-erro{color:var(--err)}

/* Estado do email das credenciais (sai em segundo plano, ver auth.py) */
.u-mail{display:block;margin-top:5px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;
  letter-spacing:.4px;text-transform:uppercase}
.u-mail.aguarda{color:var(--muted-2)}
.u-mail.feito{color:var(--muted)}
.u-mail.falhou{color:var(--err)}
/* Label em cima, valor em baixo a toda a largura: o email do cliente não cabe
   ao lado da label e cortá-lo dava uma credencial que não se pode copiar. */
.inv-cred-box{background:var(--paper-2);border:1.5px solid var(--paper-3);border-radius:8px;
  padding:12px 14px;margin:4px 0 2px;display:flex;flex-direction:column;gap:10px}
.inv-cred-box .cr-row{display:flex;flex-direction:column;align-items:stretch;gap:5px;min-width:0}
.inv-cred-box .cr-row + .cr-row{border-top:1px solid var(--paper-3);padding-top:10px}
.inv-cred-box .cr-l{font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.8px;
  text-transform:uppercase;color:var(--muted-2)}
.inv-cred-box .inv-cred{display:block;margin-top:0;background:#fff;border:1px solid var(--paper-3);
  padding:8px 10px;line-height:1.45;user-select:all;-webkit-user-select:all;overflow-wrap:break-word}

/* Execução manual · listas coladas do Excel/Sheets */
.ffs-runform textarea{font-family:var(--mono);font-size:var(--t-m1);line-height:1.6;color:var(--ink);
  border:1.5px solid var(--paper-3);border-radius:6px;padding:10px 12px;background:#fff;
  resize:vertical;min-height:74px}
.ffs-runform textarea:focus{outline:none;border-color:var(--accent-ink)}
.ffs-runform textarea::placeholder{color:var(--muted-2);font-size:var(--t-m1)}
.ffs-lote{font-family:var(--sans);font-size:var(--t-m1);color:var(--ink);background:var(--accent-soft);
  border-radius:6px;padding:9px 12px;line-height:1.5}
.ffs-lote:empty{display:none}
.ffs-lote .lote-nota{color:var(--muted);font-size:var(--t-m1)}

/* ── DataForSEO: login + password, e não OAuth ─────────────────────────────
   O modal do Claude é só texto e um código para colar; este tem campos, e a
   password nunca aparece preenchida (a API nunca a devolve — trocar a
   password é escrever as duas outra vez). */
.dfs-campos{display:flex;flex-direction:column;gap:10px;margin:14px 0 4px}
.dfs-campo{display:flex;flex-direction:column;gap:5px}
.dfs-campo span{font-size:var(--t-m2);font-weight:600;letter-spacing:.3px;
  text-transform:uppercase;color:var(--muted)}
.dfs-campo input{font-family:var(--mono);font-size:var(--t-m1);padding:9px 11px;
  color:var(--ink);background:var(--paper-2);border:1px solid transparent;
  border-radius:6px;transition:background .12s ease,border-color .12s ease}
.dfs-campo input::placeholder{color:var(--muted-2)}
.dfs-campo input:focus{outline:none;background:#fff;border-color:var(--accent)}
/* ── Saldo do DataForSEO a acabar ─────────────────────────────────────────
   Sem saldo o pipeline deixa de ter pesquisa e os produtos passam a ser
   escritos às cegas — e isso não dá erro nenhum, sai conteúdo pior e mais
   nada. Um número a mudar de cor passa despercebido num header com outros
   três indicadores; daí o ponto a piscar, que é uma chamada à acção.

   Vive no header escuro, por isso o balão é claro sobre fundo escuro. */
.ffs-usage-credit.saldo-baixo .up.credit{color:var(--warn)}
/* A animação mexe na COR e nunca na `opacity` do ponto. Um elemento com
   opacity < 1 cria um stacking context, e à primeira versão isso deu duas
   avarias de uma vez: o balão (que é `::after` deste elemento) herdava o
   piscar, e ficava preso dentro do contexto — por baixo da `.ffs-tabs`, que
   é sticky com fundo sólido, o que o fazia parecer cortado exactamente na
   fronteira entre o header e a secção de baixo. */
.saldo-ponto{position:relative;width:8px;height:8px;flex-shrink:0;
  border-radius:50%;background:var(--warn);cursor:help;outline:none;
  animation:saldo-pisca 1.6s ease-in-out infinite;
  /* Fecha o par com o saldo: o `auto` fica DEPOIS do ponto, e o `gap` de 10px
     do `.u` encolhe para 6, para o ponto ler-se como parte do número e não
     como um sinal solto ao lado. */
  margin-left:-4px;margin-right:auto}
/* O halo que abre e desvanece, para dar por ele com o canto do olho. A
   `opacity` aqui é do próprio pseudo-elemento e não trapa o balão. */
.saldo-ponto::before{content:"";position:absolute;inset:-3px;border-radius:50%;
  background:var(--warn);opacity:.5;animation:saldo-halo 1.6s ease-out infinite}
@keyframes saldo-pisca{0%,100%{background:var(--warn)}50%{background:#e8836f}}
@keyframes saldo-halo{0%{transform:scale(.6);opacity:.55}70%,100%{transform:scale(2.1);opacity:0}}

/* O balão da mensagem. `pointer-events:none` para não roubar o rato a quem
   passa por cima, e `z-index` acima da `.ffs-tabs` (20) — o balão abre para
   baixo e passa mesmo por cima da barra, que é onde ele tem espaço. */
.saldo-ponto::after{content:attr(data-msg);position:absolute;top:calc(100% + 9px);
  right:-6px;width:250px;padding:9px 11px;border-radius:8px;
  background:var(--paper);color:var(--ink);
  font-family:var(--sans);font-size:var(--t-m2);font-weight:500;line-height:1.5;
  letter-spacing:0;text-align:left;text-transform:none;
  box-shadow:0 6px 22px rgba(0,0,0,.4);
  opacity:0;visibility:hidden;transform:translateY(-3px);
  transition:opacity .14s ease,transform .14s ease,visibility .14s;
  pointer-events:none;z-index:200}
.saldo-ponto:hover::after,.saldo-ponto:focus-visible::after{
  opacity:1;visibility:visible;transform:translateY(0)}
/* Em foco por teclado o ponto tem de se ver, senão o balão aparece sem se
   perceber de onde. */
.saldo-ponto:focus-visible{box-shadow:0 0 0 2px var(--paper),0 0 0 4px var(--warn)}
/* Quem prefere menos movimento continua a ver o ponto, só que quieto. */
@media (prefers-reduced-motion:reduce){
  .saldo-ponto,.saldo-ponto::before{animation:none}
  .saldo-ponto::before{opacity:.3}
}

/* ---- Detalhe de evento (log do sistema) ------------------------------ */
table.ffs-log.sys-log td{text-align:left}
.sys-row.open{background:var(--paper-2)}
.sys-detail{display:none}
.sys-detail.open{display:table-row}
.sys-detail td{padding:0;border-bottom:1px solid var(--paper-3)}
.sys-detail-inner{padding:22px 24px;border-left:3px solid var(--accent);background:var(--paper-2)}
.sys-detail-inner .ud-row{display:grid;grid-template-columns:110px 1fr;column-gap:16px;align-items:center;padding:9px 0;border-bottom:1px solid var(--paper-3);background:none}
.sys-detail-inner .ud-row:last-child{border-bottom:none}
.sys-detail-inner .ud-l{width:auto}
.sys-detail-inner .ud-v{font-weight:400}
.sys-llm{margin-top:18px;padding-top:18px;border-top:1px dashed var(--paper-3)}
.sys-llm-hd{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.sys-llm-hd img{width:14px;height:14px;flex-shrink:0}
.sys-llm pre{margin:0 0 10px 0;background:#0a0a0a;color:#d7f0d8;font-family:var(--mono);font-size:var(--t-m1);line-height:1.8;padding:14px 16px;border-radius:7px;white-space:pre-wrap;word-break:break-word;font-weight:400}
.sys-llm-copy{appearance:none;border:1.5px solid var(--paper-3);background:#fff;border-radius:6px;padding:7px 13px;font-family:var(--mono);font-size:var(--t-m2);font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;gap:6px}
.sys-llm-copy:hover{background:var(--paper-2);color:var(--ink);border-color:var(--ink)}

@media(max-width:720px){
  .sys-detail-inner{padding:16px}
  .sys-detail-inner .ud-row{grid-template-columns:1fr;row-gap:3px}
}

/* ---- Tasks RankPanda · 02 Ideias a implementar ----------------------
   Bloco de notas do ecrã do plano. Fica aqui e não no plano-de-accao.css
   porque é do dashboard (renderTasks no ffs-app.js), não do plano: usa o
   .ffs-btn da casa e não vive dentro do .plano-scope. */
.ffs-ideas{border:1.5px solid var(--ink);background:#fff}
.idea-add{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1.5px solid var(--ink);background:var(--paper-2)}
.idea-add img{width:20px;height:20px;border-radius:4px;flex-shrink:0}
.idea-add input{flex:1;border:1.5px solid var(--paper-3);background:#fff;font-family:var(--sans);font-size:var(--t-b2);color:var(--ink);padding:9px 12px;outline:none}
.idea-add input:focus{border-color:var(--accent-ink)}
.idea-list{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto}
.idea-row{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--paper-3)}
.idea-row:last-child{border-bottom:none}
.idea-txt{flex:1;font-family:var(--sans);font-size:var(--t-b2);color:var(--ink);line-height:1.5}
.idea-date{font-family:var(--mono);font-size:var(--t-m2);color:var(--muted-2);letter-spacing:.5px;flex-shrink:0}
.idea-del{appearance:none;border:none;background:none;color:var(--muted-2);font-size:var(--t-b2);cursor:pointer;padding:4px;flex-shrink:0;transition:.12s}
.idea-del:hover{color:var(--warn)}
.idea-empty{padding:28px 16px;text-align:center;font-family:var(--sans);font-size:var(--t-b2);color:var(--muted-2)}
@media(max-width:720px){.idea-add{flex-wrap:wrap}.idea-add input{flex:1 1 100%;order:1}.idea-add img{order:0}.idea-add button{order:2;margin-left:auto}.idea-date{display:none}}
