/* ============================================================================
   DREAMWEB — application shell and screen composition
   ----------------------------------------------------------------------------
   Composition only. Every value resolves to a Foundations token and every
   visual element is a Design System component from components.css.

   Shells
     .dw-login   unauthenticated  (Login / mobile, Desktop / Login)
     .dw-app     authenticated    (mobile: Header + Content + Bottom Navigation
                                   desktop: Sidebar + Main Area)

   Breakpoint: layout/breakpoint/desktop = 1024px
   ========================================================================== */

/* `hidden` tem de esconder mesmo.

   O atributo `hidden` do HTML vale `display: none` na folha do navegador, e
   qualquer `display` de uma classe do projeto ganha-lhe — `.dw-action` é
   `display: flex`, e o elemento continuava à vista depois de `hidden = true`.
   Um botão de submeter que devia ter desaparecido e não desapareceu é uma
   segunda submissão à espera de acontecer. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------------------
   App Background (07 - Background)
   Mobile: full page, cover, centred, no repeat, with the 15% scrim.
   Desktop: image at 72% opacity, screen blend, over Background/Primary.
   Both are pinned to the viewport so they never scroll with content, and
   there is never a second background container inside page content.
   ------------------------------------------------------------------------- */
.dw-bg-mobile::before,
.dw-bg-mobile::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.dw-bg-mobile::before {
  background-image: var(--app-background-image);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.dw-bg-mobile::after { background: var(--app-background-scrim); }

/* ---------------------------------------------------------------------------
   Login shell
   ------------------------------------------------------------------------- */
.dw-login {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-height: 100vh;
  min-height: 100dvh;
  isolation: isolate;
}

/* Login carries its own App Background at both breakpoints rather than
   sharing the app shell's, because the desktop treatment covers the whole
   viewport (there is no Sidebar to sit beside). */
.dw-login::before,
.dw-login::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.dw-login::before {
  background-image: var(--app-background-image);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.dw-login::after { background: var(--app-background-scrim); }

.dw-login__header {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: var(--spacing-40) var(--spacing-20) var(--spacing-16);
  flex: none;
}
.dw-login__header .dw-logo { width: 170px; }

.dw-login__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  padding: var(--spacing-64) var(--spacing-20) var(--spacing-48);
}

/* O grupo ocupa a largura toda do conteudo.

   `.dw-login__content` alinha a flex-start, e isso faz com que os filhos NAO
   estiquem: encolhem ate ao conteudo. O conteudo mais comprido aqui e o
   subtitulo -- "Acesse sua conta para continuar na plataforma." --, logo o
   formulario inteiro ficava com a largura desse texto, cerca de 332px.

   Ate aos 360px de ecra isso nao se via, porque o texto e mais largo do que o
   espaco e acaba na mesma encostado as duas margens. A partir dos 390 sobra
   espaco, o grupo para de crescer e fica um vao so do lado direito: os campos
   alinhados pelo texto em vez de pela pagina.

   O bloco do desktop ja declarava isto; faltava no telemovel, que e onde o
   ecra e mais estreito e o desalinhamento mais se nota. */
.dw-login__group { width: 100%; }

.dw-login__intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-08);
  width: 100%;
}
.dw-login__title {
  font-size: var(--heading-h1-size);
  line-height: var(--heading-h1-line);
  font-weight: var(--heading-h1-weight);
  color: var(--text-primary);
  margin: 0;
}
.dw-login__subtitle {
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  color: var(--text-secondary);
  margin: 0;
}

.dw-login__form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-24);
  width: 100%;
  padding: var(--spacing-32) 0;
}

/* Password Field and the link below it share one 8px group. */
.dw-login__password-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-08);
  width: 100%;
}
.dw-login__forgot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  height: 18px;
}
.dw-login__forgot a {
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  color: var(--text-primary);
  opacity: 0.8;
  text-decoration: none;
}
.dw-login__forgot a:hover { opacity: 1; }

/* O conteudo centra-se no espaco entre o cabecalho e o rodape.

   Antes havia um espacador a seguir ao conteudo com `flex: 1 0 auto`, e isso
   punha TODA a folga por baixo do botao. O ecra encolhe e cresce conforme o
   que ali esta -- o aviso de modo prototipo sao 80px que existem em
   desenvolvimento e nao existem em producao -- e o resultado era que a mesma
   pagina ficava equilibrada no localhost e com um vao de 240px no servidor.

   Centrando, a folga reparte-se em cima e em baixo e a composicao mantem-se
   estavel seja qual for a altura do conteudo. E o mesmo que o desktop ja fazia. */
.dw-login__container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1 0 auto;
  width: 100%;
}
/* Sem funcao: o container e que absorve a folga e empurra o rodape. Fica no
   DOM porque o desktop tambem o esconde e nao vale um ramo no JS. */
.dw-login__spacer { display: none; }

@media (min-width: 1024px) {
  /* Desktop / Login — centred container, no header band and no footer. */
  .dw-login {
    align-items: center;
    justify-content: center;
    padding: var(--layout-desktop-page-padding);
    background: var(--background-primary);
  }
  .dw-login::before {
    opacity: var(--app-background-opacity);
    mix-blend-mode: screen;
  }
  .dw-login::after { display: none; }

  .dw-login__container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-40);
    width: 420px;
    /* No telemovel o container estica para absorver a folga; aqui nao, que o
       proprio `.dw-login` ja centra uma caixa de largura fixa. */
    flex: none;
  }
  .dw-login__container .dw-logo { width: 200px; }

  .dw-login__header { display: none; }
  .dw-login__footer { display: none; }
  .dw-login__spacer { display: none; }

  .dw-login__content { padding: 0; }
  .dw-login__group { width: 100%; }
}

/* ---------------------------------------------------------------------------
   App shell
   Mobile  : Header -> Page / Content -> Bottom Navigation
   Desktop : Sidebar + Main Area
   ------------------------------------------------------------------------- */
.dw-app {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  isolation: isolate;
}

.dw-app__sidebar { display: none; }

.dw-main {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
  min-width: 0;
}

/* Page / Content — mobile paddings from the mobile reference screens. The
   gap differs per screen: Home uses 32, module pages use 24. */
.dw-page-content {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-24);
  width: 100%;
  padding: var(--spacing-32) var(--spacing-20) var(--spacing-40);
}
.dw-page-content--home { gap: var(--spacing-32); }

/* Breakpoint visibility utilities.
   Each one only ever *hides*, inside its own breakpoint, so an element keeps
   the display value its component defines and nothing has to be restored. */
@media (max-width: 1023.98px) {
  .dw-desktop-only { display: none !important; }
}


/* Content Body — holds the screen-specific content. Stays flexible. */
.dw-content-body {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-24);
  width: 100%;
}

/* ---------------------------------------------------------------------------
   Home screen
   ------------------------------------------------------------------------- */
.dw-home-hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-16);
  width: 100%;
  /* Só em cima. Em baixo, os 10px somavam-se ao intervalo de 32 do
     `.dw-page-content` e o espaço entre a barra de pesquisa e o bloco dos
     módulos lia 42px em vez dos 32 do desenho. Quem separa o hero do que vem
     a seguir é o intervalo do contentor, não um padding do próprio hero. */
  padding: 10px 0 0;
}
.dw-home-hero__user {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-08);
}
.dw-home-hero__greeting {
  font-size: var(--display-large-size);
  line-height: var(--display-large-line);
  font-weight: var(--display-large-weight);
  color: var(--text-primary);
  margin: 0;
}
.dw-home-hero__operation {
  font-size: var(--overline-size);
  line-height: var(--overline-line);
  font-weight: var(--overline-weight);
  letter-spacing: var(--overline-tracking);
  text-transform: uppercase;
  color: var(--action-primary);
  margin: 0;
}

.dw-modules {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-16);
  width: 100%;
  padding-bottom: var(--spacing-24);
}
/* With a module expanded the rows tighten to 12, as in the Figma
   Fiscalização Expanded frame. */
.dw-modules.is-expanded { gap: var(--spacing-12); }

/* A grelha dos módulos. O número de colunas é decidido aqui e não no JS: a
   referência de 390px do Figma dá dois cartões de 169px, e é o que `auto-fill`
   com um mínimo de 160px produz nessa largura. Acima disso acrescenta colunas
   em vez de deixar os cartões encolhidos à esquerda — era o que acontecia
   entre os 480px e os 1023px, onde chegavam a sobrar 633px de vazio.

   `auto-fill` e não `auto-fit`: com `auto-fit` as faixas vazias colapsam e os
   poucos cartões de um perfil restrito esticavam-se por todo o ecrã, cada um
   com um palmo de descrição no meio do nada. Com `auto-fill` a faixa mantém a
   largura e um Reader com dois módulos vê dois cartões do tamanho normal.

   `align-items: stretch` mantém-se pela razão de sempre: os cartões crescem
   com o que o título e a descrição ocupam, e os de uma linha têm de acabar
   ao mesmo nível para a linha se ler como uma banda. */
.dw-module-rows {
  display: grid;
  /* 150 e não 160: num ecrã de 360px sobram 320 para a grelha, e com um
     mínimo de 160 só cabia UMA coluna — um cartão de 320px de largura onde
     deviam estar dois. 150 mantém os dois em todos os telemóveis, incluindo
     os de 320px, e a 390 dá exatamente os 169 do Figma. */
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  align-items: stretch;
  gap: var(--spacing-12);
  width: 100%;
}

/* Passados os telemóveis, o mínimo sobe. Mantê-lo em 150 dava seis colunas a
   1023px — os seis módulos em fila, cada um com 152px para três linhas de
   descrição. O que falta em ecrãs médios é largura por cartão, não mais
   cartões por linha: daqui para cima são 2, 3 e 4 colunas, e cada cartão fica
   entre os 210 e os 275px. */
@media (min-width: 600px) {
  .dw-module-rows {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
}

/* ---------------------------------------------------------------------------
   Janelas baixas — dobráveis abertos, telemóveis deitados, janelas curtas.

   O ritmo vertical desta app vem das referências de 390x844: 32px de padding,
   32 de intervalo, uma saudação de 36px. Num ecrã de 312px de altura isso
   consome tudo — o cabeçalho, o "Olá" e a pesquisa enchem a dobra e os
   cartões ficam todos abaixo dela. Não era corte nem transbordo: era espaço a
   mais para a altura disponível.

   Aqui o mesmo conteúdo aperta-se. Nada desaparece, e nada disto toca na
   largura: um telemóvel ao alto nunca entra neste bloco.
   ------------------------------------------------------------------------- */
@media (max-height: 600px) {
  .dw-page-content {
    gap: var(--spacing-16);
    padding-top: var(--spacing-16);
    padding-bottom: var(--spacing-16);
  }
  .dw-page-content--home { gap: var(--spacing-16); }

  .dw-home-hero { padding: 0; gap: var(--spacing-12); }
  .dw-home-hero__user { gap: var(--spacing-04); }
  /* 36/40 é o display grande das referências altas. Aqui vale mais o que ele
     ocupa do que o impacto: 26/30 continua a ler-se como saudação. */
  .dw-home-hero__greeting { font-size: 26px; line-height: 30px; }

  .dw-modules { gap: var(--spacing-12); padding-bottom: var(--spacing-12); }
}

/* A lista de submódulos ocupa a linha toda, logo abaixo do cartão que a
   abriu, seja qual for o número de colunas. O painel do export também: abre
   por baixo do cartão da Verificação de parque e tem dois campos lado a
   lado, que numa coluna de 169px não cabiam. */
.dw-module-rows > .dw-submodule-list,
.dw-module-rows > .dw-export {
  grid-column: 1 / -1;
}

/* Desktop Home — Home Intro then the empty-state line. */
.dw-home-intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-04);
  width: 100%;
}
.dw-home-empty {
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  color: var(--neutral-400);
  margin: 0;
}

/* ---------------------------------------------------------------------------
   Module screens — Hero (mobile), form and action.
   Shared by Fiscalização / Ilegalidades and Fiscalização / Equipamentos.
   ------------------------------------------------------------------------- */
.dw-hero {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 10px 0;
}
.dw-hero__inner {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  width: 100%;
}
.dw-hero__back {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-decoration: none;
}
/* Icon/UI/Chevron rotated 180deg, exactly as the reference screens do. */
.dw-hero__back-icon { transform: rotate(180deg); color: var(--icon-secondary); }
.dw-hero__parent {
  flex: 1 0 0;
  min-width: 0;
  font-size: var(--label-medium-size);
  line-height: var(--label-medium-line);
  font-weight: var(--label-medium-weight);
  color: var(--text-secondary);
  margin: 0;
}
.dw-hero__titles {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  width: 100%;
}
.dw-hero__title {
  font-size: var(--heading-h1-size);
  line-height: var(--heading-h1-line);
  font-weight: var(--heading-h1-weight);
  color: var(--text-primary);
  margin: 0;
}
.dw-hero__purpose {
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  color: var(--text-secondary);
  margin: 0;
}

/* Record Data — one field per row, in the order the module defines. */
.dw-form {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-24);
  width: 100%;
  padding: 0 var(--spacing-16) var(--spacing-24);
}
.dw-form__group {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  width: 100%;
}
/* Uma pilha de campos DENTRO de um grupo, com o mesmo intervalo que o grupo
   dá aos filhos dele.

   Um bloco embrulhado num `div` conta como UM filho do grupo: o grupo separa-o
   do que está à volta, e por dentro dele fica tudo encostado. Era o que
   acontecia ao formulário de trocar a palavra-passe — os três campos colados
   uns aos outros e o botão "Definir nova palavra-passe" agarrado ao campo
   "Repita a nova", sem um pixel entre eles. Medido a 25/09/2026. */
.dw-form__pilha {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  width: 100%;
}

/* Section label — the Overline treatment used for "DADOS DO REGISTO". */
.dw-form__group-title {
  font-size: var(--overline-size);
  line-height: var(--overline-line);
  font-weight: var(--overline-weight);
  letter-spacing: var(--overline-tracking);
  text-transform: uppercase;
  color: var(--neutral-white);
  margin: 0;
}

/* Resultado de uma gravação: o que o servidor respondeu, e o caminho para
   registar outra. Segue a métrica da zona de ação, porque é a continuação
   dela — não é um bloco novo do Design System. */
.dw-form__resultado {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  width: 100%;
  padding: 0 var(--spacing-16) var(--spacing-24);
}
.dw-form__resultado:empty { display: none; }

/* Os botões que fecham um painel separam-se do último campo. O de anular
   porque é destrutivo e um botão encostado ao campo que se acabou de preencher
   carrega-se sem querer; o de gravar pela mesma razão de leitura: o campo
   acaba, e só depois começa a decisão.

   Por prefixo e não por nome exacto: a Prospeção trouxe `corrigir-local` e
   `anular-local` (migração 014) e a regra, presa a `corrigir` e `anular`,
   deixou de os apanhar — o botão saía encostado ao campo. Escrita assim,
   o próximo módulo que ganhe estes painéis já vem certo. */
[data-painel^="anular"] [data-acao^="confirmar-anulacao"],
[data-painel^="corrigir"] [data-acao^="gravar-correcao"] {
  margin-top: var(--spacing-16);
}

/* E o botão que ABRE cada painel separa-se do que vem antes dele — na
   Fiscalização o painel de correção fica entre os dois, e o "Anular" vinha
   colado ao "Gravar correção". São duas decisões diferentes e não se
   encostam. */
[data-acao^="abrir-anulacao"] {
  margin-top: var(--spacing-16);
}

.dw-action {
  display: flex;
  align-items: flex-start;
  width: 100%;
  padding: 0 var(--spacing-16) var(--spacing-24);
}
.dw-action .dw-button { flex: 1 0 0; min-width: 0; }

/* ---------------------------------------------------------------------------
   Perfil — preference rows (screens-perfil.js)
   One row per notification: what it is, then the cadence under it. Screen
   composition, not a Design System component, so it lives here: the Figma
   file has no Perfil frame and therefore no component for this.
   ------------------------------------------------------------------------- */
.dw-prefs {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  width: 100%;
}

.dw-pref {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  padding-bottom: var(--spacing-16);
  border-bottom: 1px solid var(--border-subtle);
}
/* The last row closes the group; the group title above it is the separator. */
.dw-pref:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.dw-pref__head {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-04);
}
.dw-pref__label {
  margin: 0;
  font-size: var(--label-large-size);
  line-height: var(--label-large-line);
  font-weight: var(--label-large-weight);
  color: var(--text-primary);
}
.dw-pref__origem {
  margin: 0;
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  color: var(--text-secondary);
}

/* Holds either a Choice (recipient picks the cadence) or Flags (the cadence
   the Admin defined, read-only). Wraps so two flags never overflow. */
.dw-pref__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-08);
}

.dw-pref__actions {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  width: 100%;
}

/* ---------------------------------------------------------------------------
   Observações de um registo — o texto que explica o que se encontrou

   Estava numa linha de `.dw-summary`, e o valor de um resumo é alinhado à
   direita, a negrito e feito para um dado curto: "Porto, Porto", "Hoje,
   13:34". Um parágrafo — "o painel tem uma coluna de pixéis apagada do lado
   direito, e o acrílico está rachado em baixo" — ficava ilegível ali.

   `pre-wrap` porque quem escreve isto no telemóvel escreve por linhas, uma
   observação por linha, e essas mudanças de linha são parte do que quis
   dizer. `overflow-wrap: anywhere` para uma palavra comprida sem espaços —
   um código, um URL — não alargar o bloco para fora do ecrã.

   E COM A MESMA SUPERFÍCIE DO `.dw-summary__rows`, que é a tabela logo
   acima. Sem ela o texto ficava pousado no degradê laranja do fundo, sem
   moldura nem contraste — lia-se pior do que uma linha do resumo, que era
   precisamente o que se queria evitar ao tirá-lo de lá. Os mesmos valores, e
   não uns parecidos: são o mesmo tipo de bloco no mesmo ecrã.
   ------------------------------------------------------------------------- */
.dw-observacoes {
  margin: 0;
  padding: var(--spacing-12);
  width: 100%;
  border-radius: var(--radius-medium);
  border: 1px solid var(--field-border);
  background: var(--field-surface);
  backdrop-filter: blur(var(--field-blur));
  -webkit-backdrop-filter: blur(var(--field-blur));
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  color: var(--text-primary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   Notificações do Perfil (screens-perfil.js)

   Eram `.dw-pref`, e `.dw-pref` continua a existir — o painel de Acessos dos
   Utilizadores usa-a. O que não servia era usá-la AQUI.

   Medido a 25/09/2026, a 1200px, antes:

     · a lista não tinha moldura nem superfície. Oito linhas separadas por
       filetes, a flutuar por cima do degradê laranja do fundo, que passava
       entre elas. Todas as outras listas do site têm a moldura do
       `.dw-record-list`; esta não tinha nenhuma;
     · linhas de 48 a 65px, conforme a direita fosse uma etiqueta ou três
       botões — 17px de diferença entre linhas do mesmo tipo;
     · `justify-content: space-between` com 1200px de largura deixava 700px
       de vazio entre "Contactos realizados" e "Mensal". A linha deixava de
       se ler como uma linha;
     · e a direita falava duas línguas: etiquetas pequenas em maiúsculas nas
       quatro de cima, três botões laranja grandes nas quatro de baixo.

   Passa a ser uma lista com a moldura das outras, e a direita passa a ser
   sempre uma coisa só: ou a cadência que o Admin fixou, ou o seletor. O que
   antes era uma segunda etiqueta vermelha desceu para o corpo.
   ------------------------------------------------------------------------- */
.dw-notifs {
  display: flex;
  flex-direction: column;
  width: 100%;
  border-radius: var(--radius-medium);
  border: 1px solid var(--list-border);
  background: var(--list-surface);
  backdrop-filter: blur(var(--field-blur));
  -webkit-backdrop-filter: blur(var(--field-blur));
  overflow: hidden;
}

.dw-notif {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  padding: var(--spacing-12) var(--spacing-16);
  border-bottom: 1px solid var(--list-border);
}
.dw-notif:last-child { border-bottom: 0; }

.dw-notif__body {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-04);
  flex: 1 1 auto;
  min-width: 0;
}
.dw-notif__label {
  font-size: var(--body-medium-size);
  line-height: var(--body-medium-line);
  font-weight: var(--label-large-weight);
  color: var(--text-primary);
  overflow-wrap: anywhere;
}
.dw-notif__meta {
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  color: var(--text-secondary);
}
/* Laranja, e não vermelho: falta uma configuração, não há erro nenhum. É a
   mesma cor com que o `.dw-notice--warning` diz "repara nisto". */
.dw-notif__aviso {
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  color: var(--action-primary);
}

.dw-notif__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-08);
  flex: none;
}
.dw-notif__estado {
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  color: var(--text-secondary);
}

/* ---------------------------------------------------------------------------
   CRM — um bloco dentro de um grupo (screens-crm.js)

   Os painéis da Direção Comercial juntam um rótulo, uma lista e os botões que
   agem sobre ela. Isso é um bloco só dentro do `.dw-form__group`, e o gap de
   16px do grupo não chega lá dentro — sem isto os botões nascem encostados à
   linha de cima. 12px: menos do que separa dois grupos, mais do que separa um
   campo da sua ajuda.
   ------------------------------------------------------------------------- */
.dw-crm-bloco {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-12);
  width: 100%;
}

/* Choice variant that wraps — the Design System Choice is one row, which is
   right for two or three values but not for the six profiles in the Perfil
   development block. Options size to their label instead of splitting the
   row evenly. */
.dw-choice--wrap {
  flex-wrap: wrap;
}
.dw-choice--wrap .dw-choice__option {
  flex: 0 1 auto;
}

/* ---------------------------------------------------------------------------
   Desktop — layout/breakpoint/desktop = 1024px
   Sidebar -> Submodule Navigation -> Page Content (Desktop Guidelines 03)
   Main Area -> Page Header -> Search Bar -> Submodule Navigation -> Content
   ------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .dw-app {
    flex-direction: row;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }

  /* The background belongs to the Main Area on desktop, not the viewport. */
  .dw-bg-mobile::before,
  .dw-bg-mobile::after { display: none; }

  .dw-app__sidebar { display: flex; }

  .dw-mobile-only { display: none !important; }

  .dw-main {
    position: relative;
    flex: 1 0 0;
    height: 100%;
    padding: var(--layout-desktop-page-padding);
    overflow-x: clip;
    overflow-y: auto;
    background: var(--background-primary);
  }
  /* Pinned to the Main Area viewport so it does not scroll with content,
     matching the Main Area frame in the desktop reference screens. */
  .dw-main::before {
    content: "";
    position: fixed;
    top: 0;
    bottom: 0;
    left: var(--layout-desktop-sidebar-width);
    right: 0;
    z-index: 0;
    pointer-events: none;
    background-image: var(--app-background-image);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    opacity: var(--app-background-opacity);
    mix-blend-mode: screen;
  }

  .dw-page-content,
  .dw-page-content--home {
    position: relative;
    z-index: 1;
    gap: var(--spacing-24);
    max-width: var(--layout-desktop-content-max-width);
    padding: 0;
  }

  .dw-content-body { padding-top: var(--spacing-32); }

  /* Form Container — 740px, as in the desktop reference screens. */
  .dw-form,
  .dw-action {
    max-width: 740px;
  }

  /* With 740px to work with, a preference reads as one line: what it is on
     the left, the cadence on the right. */
  .dw-pref {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-24);
  }
  .dw-pref__control { justify-content: flex-end; }

  /* Com largura, a notificação lê-se de ponta a ponta: o que é à esquerda, a
     cadência à direita. `margin-left: auto` e não `space-between`, para o
     corpo poder crescer sem que o controlo se afaste mais do que precisa. */
  .dw-notif {
    flex-direction: row;
    align-items: center;
    gap: var(--spacing-24);
  }
  .dw-notif__control {
    justify-content: flex-end;
    margin-left: auto;
  }

  .dw-pref__actions {
    flex-direction: row;
    flex-wrap: wrap;
  }
  .dw-pref__actions .dw-button { width: auto; }

  /* Com largura, as acções encostam à direita e a linha lê-se de uma ponta à
     outra: quem é à esquerda, o que se lhe faz à direita. Em telemóvel ficam
     onde nascem, à esquerda e a mudar de linha, porque empurrá-las para a
     direita num ecrã estreito só as afasta do polegar. */
  .dw-utilizador__accoes { justify-content: flex-end; }
}

/* ---------------------------------------------------------------------------
   Lista de utilizadores — Módulo 0 (screens-utilizadores.js)

   Era `.dw-pref`, a linha de preferência do Perfil, e não servia. Essa linha
   foi desenhada para UM controlo à direita de um rótulo — a cadência de um
   alerta —, e aqui o lado direito leva duas etiquetas e quatro botões. O
   resultado, medido a 25/09/2026 a 1500px: cinco linhas do mesmo tipo com
   42, 59, 65, 121 e 121px de altura, conforme o comprimento do nome fazia
   ou não o "Desativar" cair para a linha de baixo. E o nome, espremido pelos
   botões, partia-se ao meio ("dev@dreamweb.local ·" / "owner") com metade do
   ecrã vazia à direita.

   Passa a ser uma LISTA, com o desenho que as outras do site já têm: a
   moldura e a superfície do `.dw-record-list`, os mesmos filetes entre
   linhas, a mesma tipografia. O que muda por dentro é a ordem de leitura —
   quem é, em que estado está, e só depois o que se lhe pode fazer:

       Nome                                          [ATIVO] [DAC]
       email · perfil
                            Perfil  Acessos  Dar DAC  Desativar

   As acções em linha própria, alinhadas à direita. É isso que torna a altura
   da linha independente do comprimento do nome: já não há dois blocos a
   disputar a mesma largura.
   ------------------------------------------------------------------------- */
.dw-utilizador {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  padding: var(--spacing-12) var(--spacing-16);
  border-bottom: 1px solid var(--list-border);
}
.dw-utilizador:last-child { border-bottom: 0; }

/* As etiquetas descem para a linha de baixo quando deixam de caber, em vez
   de espremerem o nome. Medido a 390px sem isto: "SUPER-ADMIN" e
   "(protegida)" deixavam 130px ao nome e ele partia-se a meio da palavra --
   "CID — Super-administraç / ão". O `min-width` e o que decide quando: abaixo
   de 11rem o nome deixa de se ler, e a partir dai vale mais perder o
   alinhamento lateral do que o nome. */
.dw-utilizador__linha {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--spacing-08) var(--spacing-12);
}
.dw-utilizador__linha > .dw-record__body { min-width: 11rem; }

/* `margin-left: auto` e não `justify-content: space-between`: com o segundo,
   uma linha sem etiquetas deixava o nome a flutuar no meio. */
.dw-utilizador__estado {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--spacing-08);
  flex: none;
  margin-left: auto;
}

.dw-utilizador__accoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-08);
}

/* O botão que abriu o painel fica marcado. Sem isto, com quatro botões
   iguais por cima de um painel aberto, fechar era adivinhar qual deles o
   tinha aberto -- e carregar no errado trocava de painel em vez de fechar. */
.dw-utilizador__accoes .dw-button.is-aberto {
  background: var(--surface-secondary);
  border-color: var(--action-primary);
}

/* ---------------------------------------------------------------------------
   Painel de detalhe — Módulo 0 (screens-utilizadores.js)
   O resultado de uma ação fica dentro do cartão do utilizador a que diz
   respeito. Separá-los faria o Admin perder de vista sobre quem agiu.
   ------------------------------------------------------------------------- */
.dw-detalhe {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  width: 100%;
  padding-top: var(--spacing-08);
}
.dw-detalhe:empty { display: none; }

/* Uma ligação de ativação ou um segredo de MFA: texto para copiar, que não
   pode partir a caixa nem ficar cortado. */
.dw-detalhe__ligacao {
  margin: 0;
  padding: var(--spacing-12);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-medium);
  background: var(--surface-secondary);
  color: var(--text-primary);
  font-family: ui-monospace, "Consolas", monospace;
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  overflow-wrap: anywhere;
  user-select: all;
}

/* ---------------------------------------------------------------------------
   Mapa (scripts/mapa.js) e leitor de QR (screens-face-qr.js)
   ------------------------------------------------------------------------- */
.dw-mapa-bloco {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-08);
  width: 100%;
}

.dw-mapa {
  width: 100%;
  min-height: 240px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-medium);
  overflow: hidden;
  /* O Leaflet desenha por cima; sem fundo próprio, um tile que não chegue
     deixa ver o gradiente da app e parece avaria. */
  background: var(--surface-secondary);
}
.dw-mapa.is-indisponivel {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 120px;
}

.dw-mapa__estado {
  margin: 0;
  font-size: var(--body-small-size);
  line-height: var(--body-small-line);
  color: var(--text-secondary);
}

/* Pinos. Desenhados em CSS e não em imagem, para herdarem os tokens da
   marca e não desafinarem quando a paleta mudar.

   Desde 14/09/2026 uma face é um ponto com a cor do seu formato
   (CORES_POR_FORMATO, em data.js), e não um pino laranja como todos os
   outros: num mapa de 300 faces, saber que ali está um Mupi e não um Outdoor
   vale mais do que a cor da marca. O anel branco é o que mantém o ponto
   visível tanto sobre o cinzento do fundo como sobre outro ponto. */
.dw-mapa-pino {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-onbrand);
}
.dw-mapa-pino--face {
  border: none;
  box-shadow: none;
}
.dw-mapa-pino--face > span {
  display: block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--neutral-white);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
/* A legenda do que está à vista. Quebra em várias linhas no telemóvel em vez
   de crescer para fora do ecrã. */
.dw-mapa-legenda {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-08) var(--spacing-16);
  list-style: none;
  margin: 0;
  padding: 0;
}
.dw-mapa-legenda:empty { display: none; }
.dw-mapa-legenda__item {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  font-size: var(--caption-size);
  line-height: var(--caption-line);
  color: var(--text-secondary);
}

/* A legenda passou a ser o COMANDO do filtro: carregar num formato apaga-o do
   mapa. Por isso cada entrada é um botão a sério — chega pelo teclado e é
   anunciada com o seu estado — e não uma linha com um `onclick`.

   A área de toque é maior do que o texto (6px à volta, 24px de altura) porque
   isto usa-se na rua com o polegar, e os nomes são curtos. */
.dw-mapa-legenda__botao {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: 24px;
  padding: 2px var(--spacing-08);
  margin: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  background: transparent;
  font-family: var(--font-family);
  font-size: var(--caption-size);
  line-height: var(--caption-line);
  color: var(--text-secondary);
  cursor: pointer;
}
.dw-mapa-legenda__botao:hover { border-color: var(--field-border); }
.dw-mapa-legenda__botao:focus-visible {
  outline: 2px solid var(--action-primary);
  outline-offset: 1px;
}

/* Apagado: a cor esvazia-se e o texto esmorece. O nome e a contagem FICAM
   legíveis de propósito — é por eles que se volta a ligar o formato, e um
   botão que não se lê é um botão que não se encontra. */
.dw-mapa-legenda__botao.is-oculto { opacity: 0.45; }
.dw-mapa-legenda__botao.is-oculto .dw-mapa-legenda__cor {
  background: transparent !important;
  border-color: var(--text-disabled);
}
.dw-mapa-legenda__botao.is-oculto .dw-mapa-legenda__nome {
  color: var(--text-secondary);
  text-decoration: line-through;
}
.dw-mapa-legenda__cor {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid var(--neutral-white);
  flex: none;
}
.dw-mapa-legenda__nome { color: var(--text-primary); }
.dw-mapa-legenda__conta { color: var(--text-secondary); }

/* A bolha de um pino, onde nao ha rato.

   Num rato o tooltip do Leaflet chega: aparece ao passar por cima e some-se.
   Num telemovel nao ha passar por cima, e o toque ia direto para a ficha --
   quem anda na rua era o unico que tinha de entrar numa face para saber qual
   era. A bolha diz o que o tooltip dizia e leva a accao num botao, para o
   toque que mostra nao ser o mesmo que sai do mapa.

   O Leaflet traz a sua bolha branca com seta branca. Sobre um mapa claro isso
   ate passaria, mas o conteudo e texto da app, e texto da app sobre branco
   fica ilegivel -- as cores de texto daqui sao todas para fundo escuro. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--surface-primary);
  color: var(--text-primary);
  box-shadow: var(--card-shadow);
}
.leaflet-popup-content-wrapper { border-radius: var(--radius-medium); }
.leaflet-popup-content { margin: var(--spacing-12); }
.leaflet-container a.leaflet-popup-close-button { color: var(--text-secondary); }
.leaflet-container a.leaflet-popup-close-button:hover { color: var(--text-primary); }

.dw-mapa-bolha {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--spacing-04);
  min-width: 160px;
}
.dw-mapa-bolha__codigo {
  font-size: var(--label-large-size);
  line-height: var(--label-large-line);
  font-weight: var(--label-large-weight);
  color: var(--text-primary);
}
.dw-mapa-bolha__meta {
  font-size: var(--caption-size);
  line-height: var(--caption-line);
  color: var(--text-secondary);
}
/* O botao encosta ao texto de mais sem isto: os dois metas acima usam o gap
   de 4, e a accao merece respirar antes de ser tocada. */
.dw-mapa-bolha .dw-button { margin-top: var(--spacing-08); }

/* O Leaflet injecta os seus próprios controlos; alinhá-los com a app evita
   um bloco branco no meio de uma interface escura. */
/* O fundo do mapa passou a ser claro: uma caixa preta por cima dele lia-se
   pior do que o contrário. */
.dw-mapa .leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.75);
  color: #444;
}
.dw-mapa .leaflet-control-attribution a { color: #444; }

/* --- leitor de QR --- */
.dw-qr {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-medium);
  overflow: hidden;
  background: var(--neutral-950);
}
.dw-qr.is-indisponivel { aspect-ratio: auto; min-height: 80px; }
.dw-qr__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Alvo central: diz onde apontar sem tapar a imagem. */
.dw-qr__alvo {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 2px solid var(--action-primary);
  border-radius: var(--radius-medium);
  pointer-events: none;
}
.dw-qr.is-indisponivel .dw-qr__video,
.dw-qr.is-indisponivel .dw-qr__alvo { display: none; }
