/*
 * Lane S3 screens (book, view, reschedule, cancel), aluno app.
 * Imported by base.html AFTER app.css: tokens.css then app.css carry
 * the base and the shared components (.cartao, .botao, .numero-heroi, .rotulo,
 * .dado, .brand); this file only adds what is specific to the
 * booking screens. No color outside tokens.css, no gradient,
 * no radius larger than --raio-grande (24px). Post-phase V fix
 * (workstream 1): HARD elevation (--marca-elevacao, zero blur) is
 * allowed on cards/days/slots, never a glow, never a diffuse
 * shadow.
 */

/* Single fixed wrapper, at the bottom of the column (lane E3, delta): carries
   the fixed positioning that .nav-principal held on its own before this lane.
   It always contains the nav, and sometimes an extra block above it
   (today: the Mensagens form, mensagens.css) stacked in plain
   normal flow INSIDE this single fixed element -- never two
   independent "position: fixed" layers to stack, which avoids a fragile
   calculation (env(safe-area-inset-bottom) varies by device). Capped at the same
   width as .app-coquilha and centered the same way, so it stays
   aligned with the column at every screen width. Modest z-index: nothing
   else in the app overlaps it (no modal, no overlay). */
.app-rodape-fixo {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  max-width: 430px;
  margin: 0 auto;
  background: var(--marca-superficie-cartao);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Main navigation, tab bar AT THE BOTTOM (phase U, plan delta point
   2: replaces the old underlined top bar). The fixed positioning now
   lives on .app-rodape-fixo above (lane E3); this rule now carries
   only the internal layout of the bar itself. */
.nav-principal {
  display: flex;
  border-top: 1px solid var(--marca-linha);
  background: var(--marca-fundo-pagina);
}

/* Line icons ABOVE the labels (home design fix, point 6):
   the same files as the tile grid (Marcar, Sessoes) or the twin
   icon already used for the same destination (Mensagens reuses tuila-coach.svg,
   already pointing to /mensagens in the grid itself) -- no new color: the
   colors are baked into the SVG itself (.il-tinta #0C1F26, .il-framboesa
   #0C6255), identical in every state (no attempt to
   recolor via CSS, the files do not use currentColor). The active STATE
   is still conveyed by the label (color + weight, a pair already
   verified) and by a REDUNDANT decorative detail in TealClaro (see
   DECISIONS D24 point 3: TealClaro does not pass 4.5:1 on this white
   bar, so it is never the ONLY state cue). */
/* Baptiste fix (31/08, reference app): icon above a small
   rotulo, normal text (normal case), never mono or uppercase. */
.nav-principal__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 44px;
  padding: var(--espaco-micro) var(--espaco-nano);
  font-family: var(--fonte-texto);
  font-weight: 500;
  font-size: 11px;
  color: var(--marca-texto-atenuado);
  border-top: 2px solid transparent;
  margin-top: -1px;
}

.nav-principal__icone {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}

/* Active state: accent + weight + top rule, literal to the reference app's active
   tab style (number/tab underlined in black in the screenshots, here
   the brand accent). */
.nav-principal__item[aria-current="page"] {
  color: var(--marca-acento-texto);
  font-weight: 700;
  border-top-color: var(--marca-acento);
}

/* Cabecalho do ecra Marcar (LANE M, referencia Planning/Fitness Park
   transposta ao estudio) : titre display em maiusculas por CSS UNICAMENTE (o
   texto semantico no HTML continua "Marcar", nunca "MARCAR" em duro --
   leitores de ecra, copiar-colar) ; mesma escala h1 ja existente
   (--tamanho-h1/--fonte-display, tokens.css), nenhum patamar novo. */
.titulo-tela {
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* Unit line: line-art location picto (ilustracoes/marcar-unidade.svg,
   same family as tuila-*.svg) + rotulo, replaces the plain standalone .rotulo
   that existed before this lane. */
.marcar-unidade {
  display: flex;
  align-items: center;
  gap: var(--espaco-nano);
  margin-bottom: var(--espaco-medio);
}

.marcar-unidade__icone {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.marcar-unidade .rotulo {
  margin: 0;
}

/* Escolha do dia : 7 opcoes, rolamento horizontal a 390px. As 7 pastilhas
   ultrapassam a largura util do ecra (correction post-critique em cego,
   finition 6b) : padding-right dá respiro depois da ultima pastilha (a
   "marge droite" pedida) E reforca o indice de defilement honesto, sem
   gradiente nem sombra desfocada (proibidos por DESIGN-ALUNO.md) -- a
   pastilha seguinte, parcialmente visivel antes de rolar, ja e o proprio
   indicio de que a lista continua. */
.dias-lista {
  display: flex;
  gap: var(--espaco-micro);
  overflow-x: auto;
  padding-bottom: var(--espaco-nano);
  padding-right: var(--espaco-medio);
  margin-bottom: var(--espaco-largo);
}

/* DA2 (.days/.day) : pastilhas RETANGULARES lisere fino, letra em mono
   acima, numero grande abaixo -- litteral, remplace le cercle. O ALVO
   TACTIL continua o <a> inteiro (min-height >= 44px). */
.dia-opcao {
  flex: 0 0 auto;
  min-width: 48px;
  min-height: 64px;
  padding: 11px 0 10px;
  border: 1px solid var(--marca-linha);
  border-radius: var(--raio-pequeno);
  text-align: center;
}

.dia-opcao__letra {
  display: block;
}

.dia-opcao__numero {
  display: block;
  font-size: 17px;
  font-variation-settings: "wdth" 112, "wght" 600;
  line-height: 1.3;
  margin-top: 2px;
}

/* Hoje, NAO selecionado : lisere marcado (TextoAtenuado), nunca um
   preenchimento -- reserve ao estado selecionado, abaixo. */
.dia-opcao--hoje {
  border-color: var(--marca-linha-alta);
}

/* Selecionada : PLAQUE PLENA CLARA (--marca-acento-claro, tokens-marca.css),
   texto ESCURO por cima -- "evident state" literal from the DA (.day.on). Contrast
   pair already verified (PAIRS_MARCA). Baptiste fix (31/08, reference-app
   clone) : TextoInvertido switched to WHITE in the new light
   palette (text placed on the solid/near-black accent, tokens-marca.css) --
   using it here on AcentoClaro (near-white background) made the selected
   day number invisible (1.19:1, verified with scripts/contrast_check.py).
   AcentoTexto (already the "dark text on accent-tinted surface" role elsewhere
   in the app) replaces it, 6.37:1. */
.dia-opcao--selecionada {
  background: var(--marca-acento-claro);
  border-color: var(--marca-acento-claro);
}

.dia-opcao--selecionada .dia-opcao__letra,
.dia-opcao--selecionada .dia-opcao__numero {
  color: var(--marca-acento-texto);
}

.dia-opcao--selecionada .dia-opcao__numero {
  font-weight: 700;
}

/* DA2 (.slotline) : FLAT list sorted por hora, one row per slot
   (bv.montar_horarios) -- replaces the grouping by treinador with a
   heading. Same card level (c) as .creneau before (surface/border), a
   fixed-width mono hora column, aligned, with the coach name next to it. */
.creneau-lista {
  display: flex;
  flex-direction: column;
  gap: var(--espaco-micro);
}

/* Colored left rule (Baptiste fix, 31/08, reference-app analysis) :
   fast visual scanning (disponivel/esgotado) inspired by the reference app's
   calendar, but on OUR two-color vocabulary (never a red for
   "esgotado" -- already settled in D26, red stays reserved for the
   destructive). Never a percentage : /slots does not expose total
   capacity, only free_places (TODO:HUMAIN, see the benchmark in docs/). */
.creneau {
  display: flex;
  align-items: center;
  gap: var(--espaco-medio);
  min-height: 52px;
  padding: var(--espaco-curto) var(--espaco-medio);
  border: 1px solid var(--marca-linha-alta);
  border-left-width: 2px;
  border-left-color: var(--marca-acento-secundario);
  border-radius: var(--raio-pequeno);
  background: var(--marca-superficie-cartao);
  box-shadow: var(--sombra-cartao);
  transition: transform var(--duracao-transicao) ease;
}

.creneau--esgotado {
  border-left-color: var(--marca-linha-alta);
}

.creneau:active {
  transform: scale(0.98);
}

.creneau__hora {
  flex: none;
  width: 52px;
  font-variant-numeric: tabular-nums;
}

.creneau__corpo {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.creneau__nome {
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto);
}

.creneau__sub {
  color: var(--marca-texto-atenuado);
}

.creneau__vagas {
  flex: none;
  color: var(--marca-acento-texto);
  text-align: right;
}

/* DA2 : "cheio" is never a red/destructive state (red is
   reserved for Cancelar/Recusa) -- neutral gray, the same shade as .creneau__sub
   above. Baptiste fix (31/08, verified with scripts/contrast_check.py) :
   LinhaAlta (a rule token, never meant for text) measures 1.36-1.52:1 as
   text -- TextoAtenuado (already the "secondary text" role established in this
   same file) replaces it, 4.54-5.07:1. */
.creneau__vagas--esgotado {
  color: var(--marca-texto-atenuado);
}

/* Extratos de Treinos / Registos (reference app, 31/08) : the same
   .creneau component reused for history -- left rule and status
   label, never a new component. No new color : the
   three tokens below are already verified (scripts/contrast_check.py).
   "booked" in the past (a reserva never closed, neither attended nor missed) stays on
   the DEFAULT rule/label of .creneau/.creneau__vagas -- never
   merged with "attended" (explicit fix requested by
   magda-platform). */
.creneau--alerta {
  border-left-color: var(--marca-recusa);
}

.creneau__vagas--alerta {
  color: var(--marca-recusa-texto);
}

/* A4 (docs/TESTE-MATINAL.md) : rotulo "Ja marcada", texto (nunca cor
   isolada), so revelado por app.js apos cruzar /marcar/marcadas. Sem
   JavaScript o atributo hidden permanece e nada muda no ecra. */
.creneau__marcada {
  color: var(--marca-texto-atenuado);
}

/* Creneau ja passado hoje (DA2 : .slotline.disabled) : filet visual
   apenas, o link permanece real e clicavel -- a API decide sozinha o que
   e efectivamente reservavel, nunca este calculo local. */
.creneau--passou {
  opacity: 0.4;
}

/* Ja reservado pelo membro (app.js, cruzamento com /marcar/marcadas) :
   lisere Acento a esquerda, litteral da DA (le slot #17:00 "ja e seu"). */
.creneau--marcada {
  border-color: var(--marca-acento-secundario);
  border-left-width: 2px;
}

/* ============================================================
   Metas (Baptiste fix, 31/08) : reuses .track (DA2) but with
   an Acento fill (confirmed/positive), never DiscAmarelo -- that
   yellow stays reserved for an imminent session (one color, one
   meaning, DA2 rule). Progresso_pct computed on the Python side (app/main.py),
   never in Jinja.
   ============================================================ */
.metas-progresso {
  margin-top: var(--espaco-medio);
}

.metas-progresso .dado {
  margin-bottom: var(--espaco-nano);
}

.track--metas {
  margin: var(--espaco-curto) 0;
}

.track--metas .track__preenchimento {
  background: var(--marca-acento-secundario);
}

/* ============================================================
   Closed-choice forms (Metas, Questionario) : native select,
   native radio/checkbox with accent-color -- never a custom
   component, the DA's restraint comes from the type and the rules, not
   from new controls.
   ============================================================ */
.formulario {
  display: flex;
  flex-direction: column;
  gap: var(--espaco-medio);
  margin-top: var(--espaco-largo);
}

/* Wagon D1 (coach) : email and palavra-passe fall under the SAME rule as
   the existing text fields (44px, 17px, same tokens) instead of a
   second field style. Without adding them here, these two types fell
   back on the browser's native rendering : height under 44px and text under 16px
   on mobile, hence automatic zoom on focus in iOS. */
.formulario select,
.formulario input[type="text"],
.formulario input[type="email"],
.formulario input[type="password"] {
  min-height: 44px;
  padding: 0 var(--espaco-curto);
  border: 1px solid var(--marca-linha-alta);
  border-radius: var(--raio-pequeno);
  background: var(--marca-superficie-cartao);
  color: var(--marca-texto);
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-corpo);
}

/* Access code (LOT A1, /entrar): same text field as above, only
   letter spacing between digits to make a short code easier to read,
   not a new component, just tracking on the same rule. */
#entrar-codigo {
  letter-spacing: 4px;
  font-variant-numeric: tabular-nums;
}

.formulario__grupo {
  border: none;
  padding: 0;
  margin: 0;
}

.formulario__grupo legend {
  padding: 0;
  margin-bottom: var(--espaco-curto);
}

.formulario input[type="radio"],
.formulario input[type="checkbox"] {
  accent-color: var(--marca-acento-secundario);
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.formulario__escala {
  display: flex;
  gap: var(--espaco-micro);
}

.formulario__opcao {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espaco-nano);
  min-height: 44px;
  padding: var(--espaco-micro) 0;
  border: 1px solid var(--marca-linha);
  border-radius: var(--raio-pequeno);
  cursor: pointer;
}

.formulario__opcao span {
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: 14px;
  color: var(--marca-texto-atenuado);
}

.formulario__linha {
  display: flex;
  align-items: center;
  gap: var(--espaco-micro);
  min-height: 44px;
  padding: var(--espaco-nano) 0;
  cursor: pointer;
}

/* DA2 (.footnote) : nota discreta apos a lista de horarios. */
.footnote {
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
  line-height: 1.55;
  padding-top: var(--espaco-medio);
}

/* ============================================================
   DA2 (.receipt/.diff): "proof of what she did", reused only on real data
   (diff_deslocamento, app/main.py), never on a simulated "call". The only
   block in this app with a card background (the receipt itself, unlike
   plate, IS a card in the DA).
   ============================================================ */
.receipt {
  border: 1px solid var(--marca-linha);
  border-radius: var(--raio-pequeno);
  margin-top: var(--espaco-medio);
  background: var(--marca-superficie-cartao);
}

.receipt-h {
  padding: var(--espaco-curto) var(--espaco-medio);
  border-bottom: 1px solid var(--marca-linha);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.receipt-ok {
  color: var(--marca-acento-texto);
}

.diff {
  padding: var(--espaco-medio);
}

.diff-line {
  display: flex;
  gap: var(--espaco-micro);
  padding: 7px 0;
  align-items: baseline;
}

.diff-k {
  width: 64px;
  flex: none;
}

.diff-old {
  font-family: var(--fonte-texto);
  font-size: 13px;
  color: var(--marca-texto-atenuado);
  text-decoration: line-through;
  text-decoration-color: var(--marca-recusa);
}

.diff-new {
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: 13px;
}

/* Safety/method copy: a discreet line UNDER THE GRID of tiles on the
   home screen, never a badge and never an invented scientific proof
   (docs/NORMES-APP.md pattern 25, associated TODO:HUMAIN). Baptiste
   correction (31/08, reference app): TextoAtenuado, no longer the white meant
   for the old dark background (unreadable on the light theme). */
.nota-seguranca-inicio {
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
  text-align: center;
  margin-top: var(--espaco-micro);
}

/* Session sign-out (LOT A1), footer of the home screen: centred like the
   safety copy above, never a button, .link-discreto is already established. */
.rodape-sair {
  text-align: center;
  margin-top: var(--espaco-largo);
}

/* C7 render review (390x844, 08/09): « Sair » measured 23.8 x 45 px in the
   real render. .link-discreto holds its 44 px floor in HEIGHT, but nothing
   held the WIDTH, and that label is the shortest in the app (four letters).
   The word stays centred: the width is added on both sides, the line does
   not move. Rule limited to this footer: elsewhere .link-discreto lives
   INSIDE a sentence, where a minimum width would open a gap between words. */
.rodape-sair .link-discreto {
  min-width: 44px;
  justify-content: center;
}

/* ============================================================
   DA2 ("The plate, not the tile"): eyebrow, plate, track, acts, taken
   literally. No card, no shadow: the structure comes from the hairlines.
   Replaces .cartao-heroi--inicio (LANE E5/AR, now removed).
   ============================================================ */

/* Entete de section (correction Baptiste, 31/08, reference app) : texte
   normal, casse normale, jamais mono/majuscules. */
.eyebrow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--espaco-medio);
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
  padding-bottom: 9px;
  border-bottom: 1px solid var(--marca-linha);
  margin: var(--espaco-medio) 0 0;
}

.eyebrow:first-child {
  margin-top: 0;
}

.eyebrow em {
  font-style: normal;
  color: var(--marca-texto-atenuado);
}

.plate {
  padding: var(--espaco-medio) 0 var(--espaco-curto);
  border-bottom: 1px solid var(--marca-linha);
}

.plate-state {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-disc-amarelo);
  margin-bottom: 10px;
}

.plate-state::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.dado.plate-meta {
  font-family: var(--fonte-texto);
  font-weight: 400;
  font-size: var(--tamanho-etiqueta);
  line-height: 1.6;
  color: var(--marca-texto-secundario);
  margin-top: var(--espaco-curto);
}

.plate-meta .link-treinador {
  color: var(--marca-texto);
}

/* Countdown rule, 2px, fills as the slot approaches (progresso_contagem,
   main.py/timezones.py, computed server-side on every render from the two
   real timestamps, never animated in JS). */
.track {
  height: 2px;
  background: var(--marca-linha);
  margin: var(--espaco-medio) 0 var(--espaco-nano);
  position: relative;
  overflow: hidden;
}

.track__preenchimento {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--marca-disc-amarelo);
  width: var(--progresso, 0%);
}

/* Action row (heroi AND empty state): the FILLED button (.botao-
   primario) takes the remaining space, the ghost buttons keep their natural
   width, never two full widths side by side (Baptiste verdict, lesson
   learned from .opcoes-sessao). */
.acts {
  display: flex;
  gap: var(--espaco-micro);
  margin-top: var(--espaco-medio);
}

.acts .botao-primario {
  width: auto;
  flex: 1;
  /* Tighter padding (DA2, .btn-fill: 16px, against the generic 28px
     of .botao): in a row of three, "Ver o cartao" needs the space that
     "Deslocar"/"Cancelar" do not take, otherwise the label wraps onto
     two lines (measured on the real render, 390px). */
  padding: 0 var(--espaco-curto);
}

.acts .botao-fantasma {
  flex-shrink: 0;
}

.botao-fantasma--perigo {
  color: var(--marca-recusa-texto);
  border-color: var(--marca-recusa);
}

/* Empty state (.empty, DA2): same hairline as the plate, two actions instead
   of one (Ver horarios / Ligar a Magda), never a card. */
.empty {
  padding: var(--espaco-medio) 0 var(--espaco-largo);
  border-bottom: 1px solid var(--marca-linha);
  text-align: center;
}

.empty .estado-vazio__ilustracao {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--espaco-micro);
}

.empty-h {
  margin-bottom: var(--espaco-nano);
}

.empty-p {
  color: var(--marca-texto-atenuado);
  margin-bottom: 0;
}

.empty .acts {
  justify-content: center;
}

/* Empty state that invites: the hero becomes the button. Dedicated
   illustration per context (docs/DESIGN-ALUNO.md, section i), never the
   generic mascot: see aviso.html for the use on the platform outage. */
.estado-vazio {
  text-align: center;
  padding: var(--espaco-amplo) 0;
}

.estado-vazio__ilustracao {
  width: 80px;
  height: 80px;
  margin: 0 auto var(--espaco-medio);
}

.estado-vazio h1 {
  margin-bottom: var(--espaco-micro);
}

.estado-vazio p {
  color: var(--marca-texto-atenuado);
  margin-bottom: var(--espaco-largo);
}

/* Compact variant: empty state nested in a screen that already has a title
   and a form below (mensagens, equipa, marcar without horario): smaller
   illustration, less vertical emptiness, never a button (the next action is
   already visible elsewhere on the screen). */
/* Alinhamento a esquerda (correction post-fase V, chantier 5) : a variante
   compacta vive imbricada num ecra ja alinhado a esquerda (titulo, aviso,
   formulario) ; um paragrafo centrado isolado nesse contexto lia-se como
   um acidente de composicao, nao uma decisao (achado no ecra Mensagens,
   13-mensagens.png). A variante plena .estado-vazio (paginas de boas-
   vindas inteiras, inicio/sessoes vazios) continua centrada : e a unica
   coisa no ecra, a centragem ai e uma decisao deliberada, nao revista aqui. */
.estado-vazio--compacto {
  padding: var(--espaco-largo) 0;
  text-align: left;
}

.estado-vazio--compacto .estado-vazio__ilustracao {
  width: 56px;
  height: 56px;
  margin: 0 0 var(--espaco-micro);
}

.estado-vazio--compacto p {
  margin-bottom: 0;
}

/* Acoes por sessao (deslocar / cancelar) */
.opcoes-sessao {
  display: flex;
  gap: var(--espaco-micro);
  margin-top: var(--espaco-medio);
}

.opcoes-sessao .botao {
  flex: 1;
}

/* Marcar de novo (lane C3, point 1): action distinct from Deslocar/Cancelar
   (full-width row below, never squeezed into the same three-button row,
   which would make each label unreadable at 390px). */
.sessao-marcar-de-novo {
  display: flex;
  width: 100%;
  margin-top: var(--espaco-micro);
}

/* Discreet link (lane C3, point 3: calendario): never a full button
   (level a) nor an outlined one (level b), just underlined text, in a
   muted color already verified (PAIRS_MARCA). It does not compete with
   the card's main CTA.

   Touch target >= 44px (lane E4, B9, measured by scripts/capture_lane_e4.py:
   142x21px before this fix). The visible text stays small: vertical
   padding widens only the CLICKABLE BOX (part of the real bounding box,
   not an invisible trick); the matching negative margin cancels the
   effect in the flow, so the visible space before/after the link stays
   IDENTICAL to before. Needed because this link also lives in the hero
   of Inicio (home.html), whose 390x844 vertical budget without scrolling
   is critical (DECISIONS D19, real margin of only 11px): widening the
   layout there would break that already verified measurement. */
.link-discreto {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 12px 0;
  margin: calc(var(--espaco-micro) - 12px) 0 -12px;
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  font-weight: 600;
  color: var(--marca-texto-atenuado);
  text-decoration: underline;
  background: none;
  border: none;
  cursor: pointer;
}

/* Carte coach (lane C3, ponto 5) : avatar do sistema de illustration (nunca
   uma fotografia), centrado, mesmo palier 80px de utilizacao plena pagina
   ja definido em DESIGN-ALUNO.md secao i. */
.treinador-cartao {
  text-align: center;
}

.treinador-cartao__avatar {
  width: 80px;
  height: 80px;
  margin: 0 auto var(--espaco-micro);
}

.treinador-dia {
  margin-bottom: var(--espaco-largo);
}

.treinador-dia h2 {
  margin-bottom: var(--espaco-curto);
}

/* Coach card (lane C3, point 5): the coach name, already shown inline
   in .dado, becomes a link to their mini-card. Same color as the
   surrounding text (inherited), only the underline signals interactivity:
   never an accent color that would duplicate the CTAs. */
.link-treinador {
  text-decoration: underline;
}

/* Ecra resultado : familia de estatuto pelo texto e pela borda, nunca so
   pela cor (o texto "Sucesso" / "Não foi possível" porta o sentido). */
.cartao--sucesso {
  border-color: var(--marca-sucesso);
}

.cartao--recusa {
  border-color: var(--marca-recusa);
}

/* Point 8 (phase U): the registry sentence (verbatim, never rewritten)
   becomes the MAIN element of the resultado screen, never a raw banner
   under a small status word. On a success, the celebration block below
   already adds weight; on a refusal, this rule is what keeps the
   sentence from reading like a plain error line. */
.resultado-mensagem {
  margin-top: var(--espaco-micro);
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: var(--tamanho-h2);
  line-height: 1.4;
  color: var(--marca-texto);
}

/* Celebracao unica (ponto 5 do addendum S2+) : servida apenas pelo patron
   flash cote servidor (app/main.py, GET /marcar/resultado), nunca por
   JavaScript nem localStorage. Entrada sobria, sem confete nem cascata ;
   --duracao-celebracao cai para 0ms sob prefers-reduced-motion
   (tokens.css), o bloco fica entao estatico desde o primeiro rendering. */
.resultado-celebracao {
  display: flex;
  align-items: center;
  gap: var(--espaco-micro);
  margin-bottom: var(--espaco-largo);
  animation: resultado-celebracao-entrada var(--duracao-celebracao) ease-out;
}

.resultado-celebracao__mascote {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
}

/* TextoAtenuado, not Acento (fix after phase V, workstream 4): the teal
   accent is reserved for ONE detail (button, border), never for the body
   of a whole sentence; TextoAtenuado is already an AA verified pair
   (PAIRS_MARCA, scripts/contrast_check.py, 9.53:1 on FundoPagina, nearly
   double the 7.06:1 of Acento) and it reads full, never faded. */
.resultado-celebracao__carimbo {
  font-family: var(--fonte-display);
  font-weight: 600;
  color: var(--marca-texto-atenuado);
}

@keyframes resultado-celebracao-entrada {
  from {
    opacity: 0;
    transform: translateY(6px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.resultado-links {
  display: flex;
  flex-direction: column;
  gap: var(--espaco-micro);
}

/* Avisos honestos da propria app (config por resolver, plataforma indisponivel) */
.aviso-mensagem {
  color: var(--marca-texto-atenuado);
  margin-bottom: var(--espaco-largo);
}

/* ERROR variant. Without it, a refusal message carried exactly the same
   color and the same weight as the screen's help texts
   ("Opcional. O teu coach le antes da sessao."): after a refusal, nothing
   showed what needed fixing. The system error token already existed and
   was not used (--marca-recusa-texto, 6.12:1 on the page background,
   verified by scripts/contrast_check.py). */
.aviso-mensagem--erro {
  color: var(--marca-recusa-texto);
  font-weight: 600;
}

/* Espacamento entre secoes empilhadas de um mesmo ecra (cartao, formulario,
   links de resultado, titulo de seccao) */
.cartao + form,
.cartao + a.botao,
form + .resultado-links,
form + a.botao,
h1 + .cartao,
h1 + .dias-lista,
h1 + form,
.eyebrow + .dias-lista,
.eyebrow + .cartao,
.eyebrow + form {
  margin-top: var(--espaco-largo);
}

h2 {
  margin-top: var(--espaco-largo);
}

/* Wagon 3.2 (anular.html) : o bloco de retorno depois de um formulario OU do
   bloco de contacto, sempre ao mesmo espaco ; so este ecra o usa. */
.resultado-links--apos-bloco {
  margin-top: var(--espaco-largo);
}

/* Cartao de membro (LANE E2, app/templates/cartao.html) : mesmo nivel (c)
   que .cartao (superficie + lisere + elevacao, herdado), centrado como
   .treinador-cartao (unico bloco da pagina). Nenhuma cor nova : nome e
   unidade reutilizam Texto/TextoAtenuado, ja verificados. */
.cartao-membro {
  text-align: center;
}

/* DA2: full-screen Cartão, "the member's gesture on arrival". Centered
   vertically in main (flex, like .app-content--inicio), the QR in the
   middle, the identity details below as a visual check. */
.cartao-tela {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--espaco-largo);
}

.cartao-id {
  border-top: 1px solid var(--marca-linha-alta);
  padding-top: var(--espaco-medio);
  text-align: center;
}

.cartao-id__nome {
  font-family: var(--fonte-display);
  font-variation-settings: "wdth" 108, "wght" 600;
  font-weight: 600;
  font-size: var(--tamanho-h2);
  color: var(--marca-texto);
  margin-bottom: var(--espaco-nano);
}

.cartao-membro__nome {
  font-family: var(--fonte-display);
  font-weight: 600;
  font-size: var(--tamanho-h1);
  line-height: var(--altura-h1);
  color: var(--marca-texto);
  margin-bottom: var(--espaco-nano);
}

.cartao-membro__unidade {
  font-family: var(--fonte-texto);
  color: var(--marca-texto-atenuado);
  margin-bottom: var(--espaco-largo);
}

/* Bloco QR (LANE E2), reutilizado no cartao de membro e em cada cartao de
   sessao (sessoes.html) : mesmo rotulo, mesma sobriedade, nenhuma cor fora
   dos jetons de marca ja verificados (app/qr.py gera o SVG diretamente com
   --marca-texto sobre --marca-superficie-cartao, par ja verificado
   "Texto sobre SuperficieCartao", PAIRS_MARCA). O SVG (largura/altura fixas
   pelo gerador) nunca ultrapassa a largura do cartao : max-width garante o
   mesmo em qualquer largura de ecra. */
.qr-bloco {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espaco-micro);
}

.qr-bloco svg {
  max-width: 100%;
  height: auto;
}

.qr-bloco__legenda {
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
  margin-bottom: 0;
}

/* Session card (sessoes.html): the QR block comes after the actions and
   the .ics link, with the same generous spacing that separates the other
   card elements from each other. */
.qr-bloco--sessao {
  margin-top: var(--espaco-medio);
}

/* "Início" breadcrumb (lane E4, B9): first content element on almost
   every screen (base.html), reuses .link-discreto (already verified,
   PAIRS_MARCA) instead of inventing a new color or component. Resets the
   margin-top built into .link-discreto (designed to come AFTER other
   content): here it is always the first element, so the right breathing
   room is a margin-bottom before the next title or card. */
.fio-ariane {
  margin-bottom: var(--espaco-medio);
}

.fio-ariane .link-discreto {
  /* Simetrico (em vez da formula generica acima) : este link e sempre o
     primeiro elemento do conteudo, nenhum espaco extra e necessario nem
     antes nem depois (o padding-top de <main> e o margin-bottom de
     .fio-ariane ja bastam). */
  margin: -12px 0;
  /* Alvo tatil >= 44px EM LARGURA tambem (B9) : o texto "Início" sozinho
     media apenas 33.3px de largura (a altura ja cumpria via a regra
     generica acima) -- unico link desta app curto o suficiente para
     precisar disto, os outros usos de .link-discreto ja ultrapassam 44px
     de largura naturalmente. */
  min-width: 44px;
}

/* ============================================================
   Baptiste fix (31/08, reference app, real screenshot of the home screen
   sent in reply: photo/nome/QR at the top, 2 column grid of full
   tiles Agenda Cliente/Reservas Online/Extratos de Treinos/
   Questionarios/Metas/Registos). Replaces the tile grid removed in
   D26: rebuilt HERE, literally, against the real screenshot this
   time (not against an "inspiration"). Scroll anchor for the fixed
   nav: see .app-content--inicio, already defined.
   ============================================================ */

/* Profile card: photo (illustrated, NEVER a photograph, the same
   rule already established for treinador-avatar.svg), nome, and the app's
   ONE QR (icon leading to /cartao, never a second QR SVG rendered here,
   D28, "a single QR code, never duplicated"). On FundoPagina, real
   shadow (--sombra-cartao) like every card of the reference app. */
.perfil-cartao {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espaco-medio);
  padding: var(--espaco-medio);
  margin-top: var(--espaco-curto);
  background: var(--marca-superficie-cartao-2);
  border-radius: var(--raio-grande);
  box-shadow: var(--sombra-cartao);
}

.perfil-cartao__id {
  display: flex;
  align-items: center;
  gap: var(--espaco-micro);
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

/* Illustrated avatar: initial on a neutral background, never an
   invented photograph (no photo data exists on the plataforma side). */
.perfil-cartao__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--marca-superficie-cartao);
  color: var(--marca-texto);
  font-family: var(--fonte-display);
  font-weight: 700;
  font-size: 18px;
}

.perfil-cartao__texto {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.perfil-cartao__nome {
  font-family: var(--fonte-display);
  font-weight: 700;
  font-size: var(--tamanho-dado);
  color: var(--marca-texto);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.perfil-cartao__unidade {
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
}

.perfil-cartao__qr {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
}

.perfil-cartao__qr-icone {
  width: 26px;
  height: 26px;
}

/* 2 column grid of full tiles (literal to the reference screenshot). Each
   tile links to a REAL destination: never a functional tile without a
   screen behind it (see .tuila--brevemente below for the two
   destinations missing on the plataforma side, see the benchmark in docs/). */
.tuilas {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--espaco-curto);
  margin-top: var(--espaco-largo);
}

.tuila {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--espaco-micro);
  aspect-ratio: 1 / 0.9;
  padding: var(--espaco-curto);
  border-radius: var(--raio-grande);
  background: var(--marca-acento-secundario);
  color: var(--marca-branco);
  text-decoration: none;
  text-align: center;
}

.tuila__icone {
  width: 32px;
  height: 32px;
  /* Existing icons (ilustracoes/*.svg illustration system) drawn
     as dark strokes on a transparent background (never currentColor), the
     same technique already approved for the studio logo (app.css, .brand-logo):
     darken then invert to white, no new file. */
  filter: brightness(0) invert(1);
}

/* Metas: no existing icon represents a goal/alvo, drawn
   in pure CSS (concentric rings), the same logic already approved as the
   chevron of the Magda painel (magda.css): never a new SVG file. */
.tuila__icone--alvo {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid var(--marca-branco);
  position: relative;
}

.tuila__icone--alvo::before {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  border: 3px solid var(--marca-branco);
}

.tuila__icone--alvo::after {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: 50%;
  background: var(--marca-branco);
}

/* O meu treino: no existing illustration represents a
   strength training session. Drawn in pure CSS, same logic as the
   Metas target and the Registos record: two discs joined by a
   bar, read as a dumbbell at display size. */
.tuila__icone--halteres {
  width: 30px;
  height: 30px;
  position: relative;
}

.tuila__icone--halteres::before,
.tuila__icone--halteres::after {
  content: "";
  position: absolute;
  top: 7px;
  width: 8px;
  height: 16px;
  border-radius: 2px;
  background: var(--marca-branco);
}

.tuila__icone--halteres::before {
  left: 0;
}

.tuila__icone--halteres::after {
  right: 0;
}

/* The center bar, drawn with a border on the container itself to
   avoid a third pseudo-element that does not exist. */
.tuila__icone--halteres {
  border-top: 3px solid transparent;
}

.tuila__icone--halteres > i {
  position: absolute;
  top: 13px;
  left: 7px;
  right: 7px;
  height: 4px;
  background: var(--marca-branco);
  display: block;
}

/* Registos: no existing icon represents an entry/presenca,
   same logic as .tuila__icone--alvo (circle + stroke in pure CSS), a
   simple "check" inside a ring. */
.tuila__icone--registo {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid var(--marca-branco);
  position: relative;
}

.tuila__icone--registo::after {
  content: "";
  position: absolute;
  left: 9px;
  top: 13px;
  width: 6px;
  height: 10px;
  border-right: 3px solid var(--marca-branco);
  border-bottom: 3px solid var(--marca-branco);
  transform: rotate(40deg) translate(-1px, -2px);
}

.tuila__rotulo {
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: 13px;
  line-height: 1.25;
}

/* Extratos de Treinos and Registos (historico de presenca): the reference
   screenshot shows them, but neither has an endpoint on the magda-platform
   side (see the benchmark in docs/, dedicated items), never built in
   silence, and never removed in silence either: tile present,
   clearly neutralized, never an href (nothing to open). */
.tuila--brevemente {
  background: var(--marca-superficie-cartao);
  color: var(--marca-texto-atenuado);
  cursor: default;
}

.tuila--brevemente .tuila__rotulo {
  color: var(--marca-texto-atenuado);
}

.tuila__brevemente-etiqueta {
  font-family: var(--fonte-texto);
  font-size: 11px;
  color: var(--marca-texto-atenuado);
}

/* ============================================================
   DA2 ("The plate, not the tile", Baptiste's exs-direction-artistique.html,
   25/08/2026): row list, replaces the home screen tile grid
   for SECONDARY destinations (Nutricao, talk to the
   team). Sessoes and Magda become nav tabs (plan delta,
   "four entries, no more duplication"), they no longer need
   a row here.
   ============================================================ */
.rows {
  margin-top: var(--espaco-curto);
}

.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espaco-medio);
  padding: var(--espaco-medio) 0;
  border-bottom: 1px solid var(--marca-linha-alta);
  color: var(--marca-texto);
  text-decoration: none;
  transition: color var(--duracao-transicao) ease;
}

.row:last-child {
  border-bottom: none;
}

.row:hover {
  color: var(--marca-acento-texto);
}

.row__rotulo {
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-corpo);
}

.row__seta {
  font-family: var(--fonte-texto);
  font-size: 13px;
  color: var(--marca-texto-atenuado);
  flex-shrink: 0;
}

/* ============================================================
   DA2: Magda block, a single row that states what it does, with two
   verbs (Ligar/Escrever). On the home screen ONLY: the thin bar under
   the header (base.html, .magda-painel) remains the discreet contextual
   guide present on every screen, this block is the full entry point, on
   the same footing as the Magda tab in the nav.
   ============================================================ */
.magda-caixa {
  border: 1px solid var(--marca-linha-alta);
  border-radius: var(--raio-medio);
  padding: var(--espaco-medio);
  margin-top: var(--espaco-largo);
  background: var(--marca-superficie-cartao);
}

.magda-caixa__topo {
  display: flex;
  align-items: center;
  gap: var(--espaco-micro);
  margin-bottom: var(--espaco-curto);
}

.magda-caixa__mascote {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
}

.magda-caixa__nome {
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: var(--tamanho-h2);
  color: var(--marca-texto);
}

.magda-caixa__descricao {
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto-atenuado);
  line-height: 1.5;
  margin-bottom: var(--espaco-medio);
}

.magda-caixa__acoes {
  display: flex;
  gap: var(--espaco-micro);
}

.magda-caixa__acoes .botao-fantasma {
  flex: 1;
}

/* LOT A1 (2026-09-03) : ecra /entrar, a frase de ajuda respira debaixo do botao. */
.entrar-ajuda {
  margin-top: var(--espaco-largo);
}

/* Wagon C2 (bem-estar) : texto corrido de varios paragrafos dentro de um
   .cartao (politica de consentimento, confirmacao de revogacao). A regra
   global `p { margin: 0 }` (app.css) cola os paragrafos uns aos outros, e
   estes sao os primeiros ecras desta app com mais de dois paragrafos
   seguidos de ler. Espacamento apenas: nenhuma cor, nenhuma tipografia e
   nenhum componente novo. */
.texto-corrido p + p {
  margin-top: var(--espaco-curto);
}

/* Wagon C2, rendering review of 08/09: consentimento screen at 390x844.
   « Aceitar » had its bottom at 1009.6 px for a usable fold of 785.5 px, so
   224 px of scrolling before the expected action was reachable. This is
   the FIRST screen the member sees of this feature, and the only one
   where the action was out of view.

   The policy becomes a SCROLLING REGION rather than a shortened text:
   nothing is removed, reordered or folded behind a trigger, and the
   text can be read in full by scrolling it in place.

   The ceiling is CALCULATED, not picked by judgment: above the card
   live the breadcrumb, the eyebrow and the title (259 px), below it the
   checkbox, the button and the link (144 px), and the fixed bar eats 58.5 px of
   the fold. 461.5 px for those three items: the ceiling reserves 500, that is
   about 38 px of margin under the button. The floor of 160 px
   prevents a very short window from reducing the policy to nothing.

   `dvh` first where it exists: on a real phone, `vh` counts the
   browser toolbar as visible and the button would fall back
   below the fold. The `vh` declaration stays as a fallback. */
/* SECOND BATCH OF 08/09: the ceiling above was measured, but nothing
   TOLD the member there was more to read. Remeasured in Chromium 390x844:
   342 px visible for 597 px of content, so 255 px out of view (42.7 %),
   including paragraph 4 (revogacao) and paragraph 5 (apagamento), that is
   the two RIGHTS the policy promises. And `scrollbar-width`
   was `auto`: the bar was painted AS AN OVERLAY, so invisible
   at rest, as on every phone. The only hint of continuation was
   paragraph 3 cut off mid block, and the « Li e aceito » checkbox sat
   right below it.

   After: 327 px visible for the same 597 px, thin bar and status
   line, last paragraph reachable at 299.5 px within the region, and
   « Aceitar » at 763.7 px for a usable fold of 785.5 px. The four
   figures are measured in Chromium by tests/test_c7_rendu_real.py.

   Two markers, on the existing tokens, no gradient, no blurred shadow and
   no new component: the bar made thin and colored (it
   only appears if there is overflow, the browser decides that), and
   the .politica-transborda status line under the region (that one is always
   rendered below 600 px: see the calculation in app/bem_estar/textos.py, overflow
   is certain there for this text). */
.politica {
  /* 515 and not 500: the status line added under the region costs 23.6 px
     of the fold, and « Aceitar » must not fall back into it (measurement from point 5, bottom
     of the button at 755.1 px for a usable fold of 785.5 px). Those 15 px given back
     to the fold are taken from the region, not from the text: no paragraph
     disappears, the overflow is simply marked. */
  max-height: max(160px, calc(100vh - 515px));
  max-height: max(160px, calc(100dvh - 515px));
  overflow-y: auto;
  /* The status line BELONGS to the region: it sits flush underneath, and it is the one that then carries the usual card gap. Without this, the card gap (28 px) would have been added TWICE around a single 19.6 px line, for a total cost of 51.6 px instead of 23.6. */
  margin-bottom: var(--espaco-nano);
  /* Once the bottom of the policy is reached, the page does not start scrolling in its place: the gesture stays inside the region as long as it still has text. */
  overscroll-behavior: contain;
  scrollbar-width: thin;
  /* Cursor on TextoAtenuado, transparent track: the card keeps its background, only the position in the text shows. The rule token (--marca-linha-alta, #D1D1D6) would have given back an almost invisible bar on the card surface (#F2F2F5), which is exactly the flaw being fixed; TextoAtenuado reaches 4.54:1 there (scripts/contrast_check.py, pair « TextoAtenuado sobre SuperficieCartao »), above the 3:1 floor for non-text components. */
  scrollbar-color: var(--marca-texto-atenuado) transparent;
}

/* Status line, in .rotulo (no new size or color), flush under the region: it belongs to the region, not to the form that follows, hence the card gap carried HERE and not between the two. */
.politica-transborda {
  margin-bottom: var(--espaco-largo);
}

/* From 600 px up the column is wider and the text shorter: the button fits without a cap, and the policy returns to its natural height (the screenshot is also what proves the text is complete). The status line disappears along with the cap: nothing overflows anymore, so it would be wrong. */
@media (min-width: 600px) {
  .politica {
    max-height: none;
    overflow-y: visible;
  }

  .politica-transborda {
    display: none;
  }
}

/* ============================================================
   Wagon C3 (bem-estar) : check-in do membro.
   Three additions only, all on existing tokens and with no
   hardcoded color: the emoji row (single selection, 52px touch
   target), the dor zone chips, and the two collapsible blocks
   (<details>, no JavaScript).
   ============================================================ */

/* One scale = one fieldset. Same spacing as .formulario__grupo, from which it inherits the border and margin reset. */
.escala {
  border: none;
  padding: 0;
  margin: 0;
}

/* The three required questions are the core content of the screen. Rendered in .rotulo they came out at 14px in the palest tone of the system, under 52px boxes: smaller and paler than body text. The secondary captions (Onde?, Lado, Intensidade, Fadiga fisica, Fadiga mental) stay in .rotulo, hence the hierarchy. */
.escala:not(.escala--numeros) > legend {
  font-size: var(--tamanho-corpo);
  font-weight: 600;
  color: var(--marca-texto);
}

/* Grid of five equal columns: the row fits within 390px with no horizontal scrolling. `position: relative` serves twice: it hides the native radios without removing them from the DOM, and it anchors the caption of the selected value right under the row. That caption lives INSIDE this container: that is what lets the general sibling selector below reveal it without a single line of JavaScript. */
.escala__opcoes {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--espaco-micro);
}

/* The native control stays in the DOM (keyboard navigation with the arrow keys, checked state exposed to screen readers); only its default box disappears, visually replaced by its <label>. */
.escala__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

/* Outline: --marca-texto-atenuado (5.07:1 on the page background), never --marca-linha-alta. The box background (#FFFFFF) is the page background: the border is therefore the ONLY visual boundary of the component, and --marca-linha-alta gave only 1.52:1 there, below the 3:1 floor of WCAG 2.1 AA criterion 1.4.11 (Non-text Contrast). Locked by test_contorno_dos_botoes_de_resposta_atinge_o_plancher_de_componente. */
.escala__botao {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: var(--espaco-micro) 0;
  border: 1px solid var(--marca-texto-atenuado);
  border-radius: var(--raio-pequeno);
  background: var(--marca-superficie-cartao-2);
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-corpo);
  color: var(--marca-texto);
  cursor: pointer;
}

.escala__emoji {
  font-size: 26px;
  line-height: 1;
}

/* Selection: the only PERCEIVABLE signal across the three emoji rows is the
   thickness of the inner ring, raised from 2px to 3px for that reason.
   The selected background (--marca-acento-claro) measures only 1.19:1 against
   the page background, and weight has no effect on a color emoji glyph,
   which has no bold variant: neither of those signals anything here. They
   stay useful on the .escala--numeros variant, where the digit is real
   text. The ring is an inner shadow: no reflow, so no grid jolt on
   selection. */
.escala__radio:checked + .escala__botao {
  box-shadow: inset 0 0 0 3px var(--marca-acento-secundario);
  background: var(--marca-acento-claro);
  font-weight: 700;
}

.escala__radio:focus-visible + .escala__botao {
  outline: var(--foco-largura) solid var(--marca-acento-texto);
  outline-offset: 2px;
}

/* Label of the chosen value, OUT OF FLOW. As a full grid row, it reserved
   22 px plus the 8 px gutter per scale, that is 90 px for the three
   mandatory questions, all taken above the « Enviar ao coach » button and
   therefore above the 390x844 fold. Placed under the emoji row, it takes
   the 20 px gutter that .formulario and .dobra__corpo already leave between
   two groups: nothing is reserved any more, and nothing jumps on selection
   either, since an absolute element takes part in no height calculation.
   It stays a child of .escala__opcoes, the only positioned container and
   the only carrier of the general sibling selector that reveals the right
   label without a line of JavaScript. */
.escala__legenda {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  font-weight: 600;
  color: var(--marca-texto-atenuado);
}

.escala__legenda span {
  display: none;
}

/* Labels of BOTH extremes, PERMANENT (D3 batch, rendering review of
   09/07). Both registo scales are nullable in the contract, so empty on
   open: the legend above revealed nothing and the coach saw two identical
   rows of five glyphs under two different questions. A full grid row, child
   of .escala__opcoes: the legend, placed at top:100% of the same container,
   then falls BELOW this line instead of covering it. TextoAtenuado on
   FundoPagina, a pair already verified by scripts/contrast_check.py. */
.escala__extremos {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  gap: var(--espaco-micro);
  margin: 0;
}

/* A chosen value ERASES the extremes line without freeing its space
   (visibility, never display: an element removed from the grid would make
   it jump on every selection). Both scales of the member feedback (C4) are
   `required` in contract 2.1, so a value is always chosen before sending:
   the label revealed by .escala__legenda fell in the left column RIGHT
   UNDER the label of extreme 1, at 14 px, in the same color, with two
   different alignments. The same word was printed twice (« Máximo » under
   « Muito leve / Máximo ») and nothing said which one was the answer. Both
   coach registo scales (D3) stay nullable: as long as nothing is chosen,
   the extremes line is there, and that is exactly what it was added for. */
.escala__opcoes .escala__radio:checked ~ .escala__extremos {
  visibility: hidden;
}

.escala__opcoes .escala__radio:nth-of-type(1):checked ~ .escala__legenda span:nth-of-type(1),
.escala__opcoes .escala__radio:nth-of-type(2):checked ~ .escala__legenda span:nth-of-type(2),
.escala__opcoes .escala__radio:nth-of-type(3):checked ~ .escala__legenda span:nth-of-type(3),
.escala__opcoes .escala__radio:nth-of-type(4):checked ~ .escala__legenda span:nth-of-type(4),
.escala__opcoes .escala__radio:nth-of-type(5):checked ~ .escala__legenda span:nth-of-type(5) {
  display: inline;
}

/* .escala__legenda is OUT OF FLOW: it takes the 20 px gutter that
   .formulario leaves between two groups. That is enough between two
   fieldsets, not when the next group is the submit button: Chromium
   390x844 measurement, legend 601-622 and button at 621, that is 0 px of
   air between the answer label and the black button. Eight pixels more, and
   only in this case (the button comes right after the last mandatory scale
   in C3 as in C4). */
.escala + .botao {
  margin-top: var(--espaco-micro);
}

/* Numbered variant (intensidade da dor, fadiga): same grid, same target,
   digit instead of the emoji. */
.escala--numeros .escala__botao {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Zones of dor and lado: multiple or single choices as pills that wrap.
   The native checkbox or radio stays VISIBLE: it carries the checked
   state, without depending on a color. */
.dor-zonas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espaco-micro);
}

/* Same component floor as .escala__botao above: the badge background is the page background, its border is its only boundary. */
.dor-zona {
  display: inline-flex;
  align-items: center;
  gap: var(--espaco-micro);
  min-height: 44px;
  padding: 0 var(--espaco-curto);
  border: 1px solid var(--marca-texto-atenuado);
  border-radius: var(--raio-pequeno);
  background: var(--marca-superficie-cartao-2);
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-etiqueta);
  color: var(--marca-texto);
  cursor: pointer;
}

/* Native collapsible block (dor, fadiga): <details> + <summary>, no
   JavaScript, closed by default. Same hairline as .plate, same 44px
   target as the rest of the controls. */
.dobra {
  border-top: 1px solid var(--marca-linha);
}

/* AFFORDANCE. `display: flex` on a <summary> replaces the
   `display: list-item` from the user agent stylesheet and therefore
   removes the native ::marker on Chromium, Firefox and Safari 16.4+.
   With no replacement, the two blocks read as two inert bold headings and
   the dor declaration, the only path from this screen to a pain report,
   was never reached. Same pattern as the Magda panel in base.html
   (magda.css): marker explicitly neutralised in both syntaxes, plus a
   chevron drawn in CSS. */
.dobra__gatilho {
  display: flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--fonte-texto);
  font-weight: 600;
  font-size: var(--tamanho-corpo);
  color: var(--marca-texto);
  cursor: pointer;
  list-style: none;
}

.dobra__gatilho::-webkit-details-marker {
  display: none;
}

.dobra__chevron {
  margin-left: auto;
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--duracao-transicao) ease;
}

.dobra[open] .dobra__chevron {
  transform: rotate(45deg);
}

.dobra__corpo {
  display: flex;
  flex-direction: column;
  gap: var(--espaco-medio);
  padding-bottom: var(--espaco-medio);
}

/* Nota para o coach: same field as the other form inputs (no new style, only the height of a multiline field). */
.formulario textarea {
  /* Without width, a <textarea> falls back to its default `cols` attribute
     (20 characters): 205 px measured in a 350 px column at 390x844, where
     every other control in the same form is full width. `box-sizing` keeps
     padding and border inside that 100%. */
  width: 100%;
  box-sizing: border-box;
  min-height: 88px;
  padding: var(--espaco-micro) var(--espaco-curto);
  border: 1px solid var(--marca-linha-alta);
  border-radius: var(--raio-pequeno);
  background: var(--marca-superficie-cartao);
  color: var(--marca-texto);
  font-family: var(--fonte-texto);
  font-size: var(--tamanho-corpo);
  resize: vertical;
}

/* « Sou coach » link at the bottom of /entrar: same spacing as the help
   sentence just above (.entrar-ajuda), with no new margin of its own. */
.entrar-coach {
  margin-top: var(--espaco-micro);
}

/* ---------------------------------------------------------------------------
   PROGRESSO DO MEMBRO (wagon C5) and the coach's fiche aluno (wagon D4, same
   partial _bem_estar_progresso.html). Layout only: no new color, so no new
   pair for
   scripts/contrast_check.py. The dots reuse AcentoSecundario, already
   used by the tendencia mini-charts (D2, D3); the labels and
   the data reuse .rotulo and .dado on the same backgrounds.
   --------------------------------------------------------------------------- */

/* Period selector: two .botao side by side, so already 52 px tall.
   The active state is carried by botao-primario AND by aria-current, never by
   color alone. */
.progresso-periodos {
  display: flex;
  gap: var(--espaco-micro);
  margin-bottom: var(--espaco-largo);
}

.progresso-periodo {
  flex: 1;
  margin: 0;
}

.progresso-linha {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espaco-micro);
  margin-top: var(--espaco-micro);
}

.progresso-seccao {
  margin-bottom: var(--espaco-largo);
}

/* A full series (label and bounds, chart, values, dates) takes
   the whole width of the shell: at 390 px, two series side by side
   would make the dots impossible to tell apart. */
.progresso-serie {
  display: flex;
  flex-direction: column;
  gap: var(--espaco-nano);
  margin-bottom: var(--espaco-medio);
}

.progresso-serie__grafico {
  width: 100%;
  height: 60px;
  fill: var(--marca-acento-secundario);
}

.progresso-serie__valores {
  font-size: var(--tamanho-etiqueta);
}

/* ------------------------------------------------------------------ */
/* O meu treino (ecran /treino, 2026-09-09)                            */
/*                                                                      */
/* Hierarchy through size, weight and space, like the rest of  */
/* l app. Aucun degrade, aucune ombre portee, aucune pastille. La       */
/* the coach's instruction is the only italic on the screen: it is  */
/* a sentence addressed to the member, not engine data.                 */
/* ------------------------------------------------------------------ */

.treino-sessao__titulo {
  margin: 0;
  font-size: 1.0625rem;
}

.treino-sessao__meta {
  margin: 2px 0 0;
  color: var(--marca-texto-atenuado);
  font-size: 0.875rem;
}

.treino-lista {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}

.treino-exercicio {
  padding: 12px 0;
  border-top: 1px solid var(--marca-linha);
}

.treino-exercicio:first-child {
  border-top: 0;
  padding-top: 4px;
}

.treino-exercicio__nome {
  margin: 0;
  font-weight: 600;
}

.treino-exercicio__series {
  margin: 4px 0 0;
  color: var(--marca-texto-secundario);
  font-size: 0.9375rem;
}

/* The instruction, set apart by a rule rather than by a color:
   it must stand out without ever reading as an alert. */
.treino-exercicio__dica {
  margin: 8px 0 0;
  padding-left: 10px;
  border-left: 2px solid var(--marca-acento);
  color: var(--marca-texto-secundario);
  font-size: 0.9375rem;
  font-style: italic;
}

.treino-nota {
  margin-top: 16px;
}

/* ------------------------------------------------------------------ */
/* Treino em curso                                                      */
/*                                                                      */
/* Redone on 2026-09-09 after a screenshot. The first version put the    */
/* two fields and the button in a flex, ignoring that                   */
/* `.botao-primario` carries `width: 100%`: the button covered the       */
/* fields and the two labels overlapped into "kgepeticoes". Nine        */
/* overlaps measured on a single screen.                                */
/*                                                                      */
/* Entry is now a grid: two fields side by side, the                    */
/* button below across the full width, which is also the safest         */
/* thumb gesture between two sets.                                       */
/* ------------------------------------------------------------------ */

.treino-exec {
  background: var(--marca-superficie-cartao-2);
}

.treino-exec__cabecalho {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.treino-exec__nome {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
}

.treino-exec__conta {
  font-variant-numeric: tabular-nums;
  color: var(--marca-texto-atenuado);
  font-size: 0.9375rem;
  white-space: nowrap;
}

.treino-exec__conta--completo {
  color: var(--marca-acento-texto);
  font-weight: 600;
}

/* The suggested load is the first thing the member looks for
   when arriving at the machine. So it is readable from a distance. */
.treino-exec__carga {
  margin: 12px 0 2px;
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.treino-exec__carga--indefinida {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--marca-texto-secundario);
}

.treino-exercicio__series {
  margin: 4px 0 0;
  color: var(--marca-texto-secundario);
  font-size: 0.9375rem;
}

.treino-exec__alvo {
  margin: 4px 0 0;
  color: var(--marca-texto-atenuado);
  font-size: 0.875rem;
}

/* Sets already done: compact, set back, never stealing attention
   from the entry field that follows. */
.treino-exec__feitas {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  border-top: 1px solid var(--marca-linha);
}

.treino-exec__feitas li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--marca-linha);
  font-variant-numeric: tabular-nums;
}

.treino-exec__feitas li span:first-child {
  color: var(--marca-texto-atenuado);
}

/* The entry grid. The button spans both columns, below the fields. */
.treino-exec__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 14px;
}

.treino-exec__campo {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.treino-exec__campo span {
  font-size: 0.8125rem;
  color: var(--marca-texto-atenuado);
}

.treino-exec__campo input {
  width: 100%;
  min-height: 48px;
  padding: 8px 12px;
  border: 1px solid var(--marca-texto);
  border-radius: 8px;
  background: var(--marca-superficie-cartao-2);
  color: var(--marca-texto);
  font-size: 17px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.treino-exec__form .botao,
.treino-exec__form button {
  grid-column: 1 / -1;
  width: 100%;
  margin-top: 2px;
}

/* Removing a wrong entry: present, but discreet. */
.treino-exec__desfazer {
  display: inline-block;
  margin-top: 10px;
  padding: 6px 0;
  min-height: 44px;
  line-height: 32px;
  border: 0;
  background: transparent;
  color: var(--marca-texto-atenuado);
  font-family: inherit;
  font-size: 0.875rem;
  text-decoration: underline;
  cursor: pointer;
}


/* Modulo Nutricao. No verdict color: no green for a low total, no
   red for a high total. A number is a number, not a grade. */
.nutricao-total {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.nutricao-modo {
  align-items: flex-start;
  min-height: 44px;
  padding: 10px;
  margin-bottom: 8px;
}

.nutricao-modo .check-text {
  font-weight: 400;
}

.nutricao-linha {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.nutricao-linha__texto {
  flex: 1 1 auto;
  min-width: 0;
}

.nutricao-linha__acao {
  flex: 0 0 auto;
}

/* ------------------------------------------------------------------ */
/* Diario alimentar                                                     */
/*                                                                      */
/* Redone 2026-09-09 AFTER capturing the real render at 390 px. What    */
/* the capture showed and a code read-through would not have revealed:  */
/* the ring lost in the middle of white space, macro tiles with no      */
/* meaning, flat meal cards, and three bottom buttons                   */
/* touching each other.                                                 */
/*                                                                      */
/* AUCUNE COULEUR DE VERDICT nulle part : ni vert de reussite, ni rouge */
/* alert. The macro bar shows a BREAKDOWN, not a                        */
/* progression toward a target, because a bar that fills toward a       */
/* protein goal is an order, not information.                           */
/* ------------------------------------------------------------------ */

/* Header: a solid block that gives the ring the room it deserves and
   cleanly separates the summary from the log. No gradient. */
.nut-topo {
  background: var(--marca-acento-claro);
  border-radius: 12px;
  padding: 20px 16px 18px;
  margin-bottom: 20px;
}

.nut-anel-bloco {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.nut-anel {
  width: 196px;
  height: 196px;
  display: block;
}

.nut-anel__fundo {
  fill: none;
  stroke: var(--marca-branco);
  stroke-width: 9;
}

.nut-anel__valor {
  fill: none;
  stroke: var(--marca-acento-texto);
  stroke-width: 9;
  stroke-linecap: round;
}

.nut-anel__texto {
  position: absolute;
  top: 62px;
  text-align: center;
  pointer-events: none;
}

.nut-anel__numero {
  margin: 0;
  font-size: 2.75rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--marca-texto);
}

.nut-anel__unidade {
  margin: 4px 0 0;
  color: var(--marca-acento-texto);
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
}

.nut-anel__meta {
  margin: 12px 0 0;
  color: var(--marca-texto-secundario);
  font-size: 0.9375rem;
}

/* Macros: a breakdown bar, then the three values below it. The bar
   says how the calories are split, which is a fact. */
.nut-macros {
  margin-top: 18px;
}

.nut-barra {
  display: flex;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--marca-branco);
}

.nut-barra__parte {
  height: 100%;
}

.nut-barra__parte--hidratos { background: var(--marca-acento-texto); }
.nut-barra__parte--proteina { background: var(--marca-acento); }
.nut-barra__parte--gordura  { background: #B9C9BE; }

.nut-legenda {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.nut-legenda li {
  flex: 1 1 0;
  min-width: 0;
}

.nut-legenda__ponto {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: 6px;
  vertical-align: baseline;
}

.nut-legenda__nome {
  color: var(--marca-texto-secundario);
  font-size: 0.8125rem;
}

.nut-legenda__valor {
  margin: 2px 0 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Titre de section, discret. */
.nut-titulo {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--marca-texto-atenuado);
  margin: 0 0 10px;
  font-weight: 600;
}

/* Meal card: white space, air, and a single rule per row. */
.nut-refeicao {
  background: var(--marca-superficie-cartao-2);
  padding: 0;
  overflow: hidden;
}

.nut-refeicao__cabecalho {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 16px 12px;
}

.nut-refeicao__nome {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
}

.nut-refeicao__kcal {
  color: var(--marca-texto-atenuado);
  font-variant-numeric: tabular-nums;
  font-size: 0.9375rem;
  white-space: nowrap;
}

.nut-refeicao__vazio {
  margin: 0;
  padding: 0 16px 12px;
  color: var(--marca-texto-atenuado);
}

.nut-itens {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nut-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 8px 12px 16px;
  border-top: 1px solid var(--marca-linha);
}

.nut-item__texto {
  flex: 1 1 auto;
  min-width: 0;
}

.nut-item__nome {
  margin: 0;
  font-weight: 600;
  line-height: 1.3;
}

.nut-item__detalhe {
  margin: 3px 0 0;
  color: var(--marca-texto-atenuado);
  font-size: 0.875rem;
}

.nut-item__origem {
  margin: 3px 0 0;
  color: var(--marca-texto-atenuado);
  font-size: 0.8125rem;
  font-style: italic;
}

/* Full touch target, with a discreet mark: deleting a row must not be
   the most visible gesture on the card. */
.nut-item__apagar {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--marca-texto-atenuado);
  font-size: 1.125rem;
  line-height: 1;
  cursor: pointer;
}

.nut-item__apagar:hover {
  background: var(--marca-superficie-cartao);
}

/* Add: full width, at the foot of the card, separated by a rule. It no
   longer floats to the left against the content. */
.nut-juntar {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: 14px 16px;
  border: 0;
  border-top: 1px solid var(--marca-linha);
  background: transparent;
  color: var(--marca-acento-texto);
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.nut-juntar:hover {
  background: var(--marca-superficie-cartao);
}

.nut-juntar::before {
  content: "+";
  display: inline-block;
  margin-right: 10px;
  font-weight: 700;
}

/* Footer: full-width rows, and no more pills touching. */
.nut-ligacoes {
  padding: 0;
}

.nut-ligacao {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 52px;
  padding: 14px 16px;
  border-top: 1px solid var(--marca-linha);
  color: var(--marca-texto);
  text-decoration: none;
  font-weight: 500;
}

.nut-ligacao:first-child {
  border-top: 0;
}

.nut-ligacao::after {
  content: "";
  width: 8px;
  height: 8px;
  border-top: 2px solid var(--marca-texto-atenuado);
  border-right: 2px solid var(--marca-texto-atenuado);
  transform: rotate(45deg);
  flex: 0 0 auto;
}

/* Formulaire de macros : deux colonnes au pouce, quatre champs courts. */
.nut-macros-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 8px 0 12px;
}

.nut-scan {
  width: 100%;
  max-height: 260px;
  border-radius: 8px;
  border: 1px solid var(--marca-linha);
  background: var(--marca-texto);
  margin-bottom: 8px;
}

/* ------------------------------------------------------------------ */
/* Text fields and buttons: measured fix on 2026-09-09                  */
/*                                                                      */
/* The 390 px capture showed a flaw that goes beyond the nutrition      */
/* screens: `select` and `input[type=number]` did carry the 44px        */
/* touch target required, but NOT the text fields, which dropped to     */
/* 32 px. And a button placed after a `.field` touched it, at zero      */
/* pixels. Both show up in an image and in no code review.              */
/*                                                                      */
/* The rule is therefore carried by `.field`, for every screen, and not   */
/* only where the flaw was spotted: a field that is too small is too small */
/* everywhere it appears.                                                 */
/* ------------------------------------------------------------------ */

.field input[type="text"],
.field input:not([type]),
.field input[type="tel"],
.field input[type="email"],
.field input[type="search"] {
  font-family: var(--font-ui, inherit);
  font-size: 16px;
  min-height: 44px;
  padding: 8px 10px;
  color: var(--marca-texto);
  background: var(--marca-superficie-cartao-2);
  border: 1px solid var(--marca-texto);
  border-radius: 4px;
  width: 100%;
}

/* A button never sits flush against the field before it. */
.field + .botao,
.field + .botao-primario,
.field + .botao-secundario,
.field + .botao-fantasma,
.field + button {
  margin-top: 14px;
}

/* Two stacked buttons do not touch each other either. */
.botao + .botao,
.botao + .botao-fantasma,
.botao-fantasma + .botao-fantasma,
form + form > .botao-fantasma {
  margin-top: 8px;
}

/* Light card.
   `.botao-secundario` is a flat fill of --marca-superficie-cartao, which is
   EXACTLY the background of `.cartao`: on an ordinary card, a secondary
   button disappears. The 2026-09-09 screenshot showed this unambiguously,
   "Abrir a camara" and "Procurar valores" looked like headings there.
   Screens that carry secondary buttons therefore lay their cards out in
   white, where the grey of the button is visible. */
.cartao--branco {
  background: var(--marca-superficie-cartao-2);
}
