/* Folha de treino: a Semana, a folha do dia, a Consulta e a entrada.
 *
 * Papel quente, uma só tinta azul de caneta e números alinhados como numa lista
 * de partida. A cor aparece quando há uma decisão; o acento marca hoje e o foco,
 * nunca um estado. Nenhum estado depende só da cor: cada um tem palavra e forma.
 *
 * Três blocos de tokens com o mesmo conjunto: o claro, o escuro do sistema e o
 * escuro escolhido no botão. O escuro é uma paleta própria, não uma inversão.
 * Os valores são os da especificação (secção 5.1), com a correção da secção 9. */

:root {
  color-scheme: light;
  --bg: #F3F0E8;
  --surface: #FBF9F4;
  --surface2: #EFEBE2;
  --sunken: #E6E1D6;
  --ink: #1D1B17;
  --ink2: #55504A;
  --ink3: #675F55;
  --line: #E3DDD2;
  --line-strong: #C9C1B3;
  --field: #8E877B;
  --accent: #2446B8;
  --accent-soft: #E4E9F8;
  --on-accent: #FFFFFF;
  /* O #7A5C2E tinha croma abaixo do mínimo e lia-se como cinzento (secção 9). */
  --data2: #8C5A0E;
  /* As séries dos gráficos. No claro coincidem com o acento e a série secundária;
     no escuro descem de luminosidade, porque #94ABFF e #D8B98A saíam claros demais
     para linhas e colunas (secção 9). Servem a linha de FC e as zonas da folha do
     dia, e a carga e o volume da Consulta (corrida a --series1, total a --series2). */
  --series1: #2446B8;
  --series2: #8C5A0E;
  --ok: #23703F;
  --ok-soft: #E2EFE5;
  --warn: #8A5A00;
  --warn-soft: #F6EACB;
  --crit: #AE2A1E;
  --crit-soft: #F8E2DE;
  --on-crit: #FFFFFF;
  --shadow: 0 1px 2px rgba(29, 27, 23, .06);
  --scrim: rgba(29, 27, 23, .34);

  /* Derivados: só aqui. O var() resolve-se contra o valor que vencer no :root,
     onde vivem as três paletas, por isso não se repetem nos blocos escuros. */
  --hatch: repeating-linear-gradient(135deg, transparent 0 4px, var(--line-strong) 4px 5px);
  --miss: repeating-linear-gradient(135deg, transparent 0 6px, var(--crit-soft) 6px 7px);
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --bg: #131210;
    --surface: #1C1B18;
    --surface2: #25231F;
    --sunken: #0F0E0C;
    --ink: #EDE9E1;
    --ink2: #B3ACA2;
    --ink3: #9A9388;
    --line: #2F2C28;
    --line-strong: #4A453E;
    --field: #77716A;
    --accent: #94ABFF;
    --accent-soft: #1F2745;
    --on-accent: #10183A;
    --data2: #D8B98A;
    --series1: #6F8DE8;
    --series2: #C08738;
    --ok: #74CC95;
    --ok-soft: #17301F;
    --warn: #E9B94D;
    --warn-soft: #352A10;
    --crit: #FF8A7B;
    --crit-soft: #3A1B17;
    --on-crit: #1A0B09;
    --shadow: none;
    --scrim: rgba(0, 0, 0, .55);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #131210;
  --surface: #1C1B18;
  --surface2: #25231F;
  --sunken: #0F0E0C;
  --ink: #EDE9E1;
  --ink2: #B3ACA2;
  --ink3: #9A9388;
  --line: #2F2C28;
  --line-strong: #4A453E;
  --field: #77716A;
  --accent: #94ABFF;
  --accent-soft: #1F2745;
  --on-accent: #10183A;
  --data2: #D8B98A;
  --series1: #6F8DE8;
  --series2: #C08738;
  --ok: #74CC95;
  --ok-soft: #17301F;
  --warn: #E9B94D;
  --warn-soft: #352A10;
  --crit: #FF8A7B;
  --crit-soft: #3A1B17;
  --on-crit: #1A0B09;
  --shadow: none;
  --scrim: rgba(0, 0, 0, .55);
}

/* ------------------------------------------------------------------ base -- */

*, *::before, *::after { box-sizing: border-box; }

/* `hidden` tem de ganhar ao `display` das classes. Sem o !important, a folha do
   dia e os botões que só existem com JavaScript apareciam mal a página abria:
   o atributo diz «escondido» e o `display:flex` da classe diz o contrário. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  /* Um título do Tredict sem espaços não pode empurrar a página para o lado. */
  overflow-wrap: break-word;
}

/* O h4 também: é o título de cada sessão na folha do dia, e com a margem do browser
   ficava um palmo de papel em branco por cima e por baixo dele. */
h1, h2, h3, h4, p, ol, ul, figure, blockquote { margin: 0; padding: 0; }
ol, ul { list-style: none; }

button, input, select, textarea { font: inherit; color: inherit; }
/* Abaixo de 16 px o Safari do iPhone faz zoom ao tocar no campo. */
input, select, textarea { font-size: 16px; }

a { color: var(--accent); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }
.eyebrow { font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ink3); }
.small { font-size: 13px; }
.muted { color: var(--ink3); }
.sec { color: var(--ink2); }
.err { font-size: 13px; color: var(--crit); text-wrap: pretty; }
.loading { font-size: 13px; color: var(--ink3); }

/* Títulos que servem a leitores de ecrã e não ao olho. */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 16px;
  border: 1px solid var(--field); border-radius: 8px;
  background: transparent; color: var(--ink);
  font-size: 15px; line-height: 1.2; text-decoration: none; white-space: nowrap; cursor: pointer;
}
.btn:hover { background: var(--surface2); }
.btn:disabled { opacity: .6; cursor: progress; }
.btn.primary { background: var(--ink); border-color: var(--ink); color: var(--bg); font-weight: 600; }
.btn.quiet { border-color: transparent; color: var(--accent); padding: 0 8px; }

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-width: 44px; height: 44px; padding: 0;
  border: 0; border-radius: 8px; background: transparent; color: var(--ink2);
  font-size: 15px; font-weight: 500; cursor: pointer;
}
.iconbtn:hover { background: var(--surface2); }
.iconbtn:disabled { opacity: .6; cursor: progress; }
.iconbtn svg { flex: none; display: block; }

.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  padding: 16px; box-shadow: var(--shadow);
}

/* ----------------------------------------------------------- barra de topo -- */

.topbar {
  position: sticky; top: 0; z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding-top: env(safe-area-inset-top);
}
.bar {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  height: 52px; padding: 0 8px 0 16px;
}
.tabs { display: flex; gap: 24px; height: 100%; }
.tab {
  display: flex; align-items: center; height: 100%;
  font-size: 15px; font-weight: 500; color: var(--ink2); text-decoration: none;
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--ink); }
/* O separador ativo pinta-se pelo aria-current: o estilo e o que o leitor de ecrã
   anuncia não podem divergir. */
.tab[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }
.sync-word { display: none; }

/* A faixa dos passos da sincronização fica dentro da barra fixa, para se ver mesmo
   quando se toca no Sincronizar do cartão da manhã, mais abaixo. É uma região viva
   sempre presente e vazia sem nada para dizer: uma região que aparece de repente
   nem sempre é anunciada. */
.syncstrip {
  max-width: 640px; margin: 0 auto; padding: 8px 16px 12px;
  display: flex; flex-direction: column; gap: 4px;
  border-top: 1px solid var(--line);
  font-size: 13px; color: var(--ink2);
}
.syncstrip:empty { padding: 0; border-top: 0; }
.syncstrip .erro { color: var(--crit); font-weight: 600; }
.syncstrip .nota { color: var(--ink3); }

/* --------------------------------------------------------------- estrutura -- */

.wrap {
  max-width: 640px; margin: 0 auto; padding: 24px 16px 32px;
  display: flex; flex-direction: column; gap: 32px;
}
.footer {
  max-width: 640px; margin: 0 auto;
  padding: 0 16px calc(32px + env(safe-area-inset-bottom));
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  font-size: 13px; color: var(--ink3);
}
.footer form { margin: 0; }
.footer .btn.quiet { color: var(--ink3); }

.stack { display: flex; flex-direction: column; gap: 12px; }
.sechead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px; }
.sec-title { font-size: 17px; font-weight: 600; line-height: 1.3; }
.weeknav { display: flex; gap: 4px; }

/* ------------------------------------------------------ cabeçalho do dia -- */

.head { display: flex; flex-direction: column; gap: 4px; }
.head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.date { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -.01em; text-wrap: balance; }
.fresh { font-size: 13px; color: var(--ink3); }

/* O dorsal é o único ornamento. Cores por token, para mudar com o tema. */
.bib { flex: none; display: block; line-height: 0; border-radius: 4px; }
.bib svg { display: block; }
.bib-card { fill: var(--surface); stroke: var(--ink); }
.bib-hole { fill: var(--bg); stroke: var(--line-strong); }
.bib-name { fill: var(--ink2); letter-spacing: .08em; }
.bib-days { fill: var(--ink); font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }

/* ------------------------------------------------------------------ avisos -- */

.notices { display: flex; flex-direction: column; gap: 8px; }
.warnline {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px;
  padding: 8px 12px;
  background: var(--warn-soft); color: var(--ink);
  border-left: 4px solid var(--warn); border-radius: 0 8px 8px 0;
  font-size: 15px; text-decoration: none; text-wrap: pretty;
}
.warnline p { flex: 1 1 12rem; }
/* O resultado do «Já passou» ocupa uma linha inteira por baixo da frase. */
.warnline [role="status"] { flex-basis: 100%; }
a.warnline { flex-wrap: nowrap; min-height: 44px; }
a.warnline:hover { text-decoration: underline; }
.chev { flex: none; font-size: 17px; line-height: 1; color: var(--ink2); }

/* --------------------------------------------------------------- a manhã -- */

.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 4px; }

.nodata {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px;
  padding: 8px 0 12px;
}
.nodata p { flex: 1 1 12rem; font-size: 15px; }

.rule-row {
  display: grid; grid-template-columns: 96px minmax(0, 1fr) 84px; gap: 8px; align-items: center;
  min-height: 48px; padding: 4px 0;
  border-top: 1px solid var(--line);
}
.rule-row:first-child { border-top: 0; }
/* `overflow-wrap: normal` contra a regra geral da página: com a quebra dentro da
   palavra, «Temperatura» saía «Temperatur/a» na coluna estreita. Quatro rótulos
   fixos e conhecidos cabem na coluna; partir uma palavra é sempre pior. */
.rule-label { font-size: 15px; font-weight: 500; line-height: 1.2; overflow-wrap: normal; }
.rule-base { display: block; margin-top: 4px; font-size: 13px; font-weight: 400; color: var(--ink3); }

/* A régua é HTML com posições em percentagem vindas do charts.rule_scale. Num SVG
   esticado com preserveAspectRatio="none", o ponto virava elipse a 1280 px. A
   margem lateral deixa espaço ao ponto e à seta de um valor encostado à ponta. */
.rule { position: relative; height: 28px; margin: 0 16px; }
.rule-track {
  position: absolute; left: 0; right: 0; top: 11px; height: 6px; border-radius: 3px;
  background: var(--sunken);
}
/* Sem leitura da Oura: trama, para se ver que falta e não que está a zero. */
.rule-track.hatch { background: var(--hatch), var(--surface2); }
.rule-band {
  position: absolute; top: 11px; height: 6px; border-radius: 3px;
  background: var(--line-strong);
  left: var(--de, 0%); right: calc(100% - var(--ate, 0%));
}
.rule-dot {
  position: absolute; top: 14px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--ink); border: 2px solid var(--surface);
  /* A percentagem dá o centro do ponto, não a borda esquerda. */
  transform: translate(-50%, -50%);
}
.clamp { position: absolute; top: 14px; font-size: 11px; line-height: 1; color: var(--ink); transform: translateY(-50%); }
.clamp.low { right: calc(100% + 8px); }
.clamp.high { left: calc(100% + 8px); }
.missing { display: block; font-size: 13px; color: var(--ink3); }
/* 84 px e sem quebra: «+0,19 °C» não pode partir o «°C» para a linha de baixo. */
.rule-val { text-align: right; white-space: nowrap; font-size: 22px; font-weight: 600; line-height: 1.15; }
.rule-val .unit { margin-left: 4px; font-size: 13px; font-weight: 400; color: var(--ink2); }
.rule-val .none { color: var(--ink3); }
/* Alinhado com a coluna das réguas: 88 px do rótulo mais 8 de intervalo. */
.nights { padding: 4px 0 0 96px; font-size: 13px; color: var(--ink3); }

.quote {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 12px; padding: 4px 0 4px 12px;
  border-left: 2px solid var(--line-strong);
}
.quote p { font-size: 15px; text-wrap: pretty; }
.no-session { margin-top: 12px; font-size: 15px; color: var(--ink2); }

/* ----------------------------------------------------------- hoje e agenda -- */

.session { display: flex; flex-direction: column; gap: 4px; }
.sess-title { font-size: 17px; font-weight: 600; line-height: 1.3; text-wrap: pretty; }
.note { margin-top: 8px; font-size: 15px; white-space: pre-line; text-wrap: pretty; }
.linkrow {
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 44px; padding: 0; border: 0; background: transparent;
  color: var(--accent); font-size: 15px; font-weight: 500; cursor: pointer;
}
.linkrow:hover { text-decoration: underline; }
.empty-today { font-size: 15px; color: var(--ink2); }
.detail { display: block; margin-top: 4px; font-size: 13px; color: var(--ink2); }
.detail.crit { display: flex; align-items: center; gap: 8px; color: var(--crit); font-weight: 600; }
.detail.warn { color: var(--warn); }
.tri { flex: none; display: block; }

/* A linha de estado: glifo, palavra e partes. Cada parte leva o seu «·» e não parte
   por dentro, para o separador nunca ficar pendurado no fim de uma linha. */
.st {
  display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px;
  margin-top: 4px; font-size: 13px; line-height: 1.5; color: var(--ink2);
}
.glyph { flex: none; display: block; margin-right: 4px; }
.glyph.todo { color: var(--ink2); }
.glyph.done { color: var(--ink); }
.glyph.miss { color: var(--crit); }
.glyph.swap { color: var(--warn); }
.glyph.extra { color: var(--ink2); }
/* Só a palavra vai em caixa-alta. O «às 12:33» fica fora dela, senão saía «FEITO ÀS». */
.word { font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.word.crit { color: var(--crit); }
.word.warn { color: var(--warn); }
.when { white-space: nowrap; }
.part { white-space: nowrap; }

/* Etiquetas: alinhadas ao início, para não esticarem com a linha. */
.chips { align-self: flex-start; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px; margin-top: 4px; }
.chip {
  align-self: flex-start; display: inline-block;
  padding: 4px 8px; border: 1px solid var(--line-strong); border-radius: 999px;
  font-size: 11px; line-height: 1.15; color: var(--ink2);
}
.chip.warn { background: var(--warn-soft); border-color: transparent; color: var(--ink); }
.chip.muted { border-color: var(--line); color: var(--ink3); }

.no-plan { font-size: 13px; color: var(--ink3); }

/* A agenda é uma folha com filetes, não uma pilha de cartões. Sem overflow:hidden,
   para o contorno do foco não ser cortado: os cantos arredondam-se nas linhas. */
.agenda { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow); }
.agenda > li + li { border-top: 1px solid var(--line); }
.day {
  display: grid; grid-template-columns: 64px minmax(0, 1fr);
  width: 100%; min-height: 56px; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: transparent; color: inherit;
  font: inherit; text-align: left; cursor: pointer;
}
.agenda > li:first-child .day { border-radius: 11px 11px 0 0; }
.agenda > li:last-child .day { border-radius: 0 0 11px 11px; }
.day:hover { background: var(--surface2); }
.day.today {
  background: var(--accent-soft);
  background: color-mix(in oklab, var(--accent-soft) 35%, var(--surface));
  box-shadow: inset 3px 0 0 var(--accent);
}
.dcol { display: flex; flex-direction: column; padding: 8px 0 8px 16px; }
.wd { font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ink3); }
.dn { font-size: 22px; font-weight: 600; line-height: 1.15; }
.day.today .dn { color: var(--accent); }
.scol { display: flex; flex-direction: column; justify-content: center; gap: 12px; min-width: 0; padding: 8px 16px 8px 4px; }
.sess { display: flex; flex-direction: column; }
.stitle { font-size: 15px; font-weight: 500; line-height: 1.3; text-wrap: pretty; }
.empty { font-size: 13px; color: var(--ink3); }

/* --------------------------------------------------------- fecho da semana -- */

.ledger { display: flex; flex-direction: column; gap: 16px; }
.lrow-top { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 8px; }
.lrow-top .l { font-size: 13px; color: var(--ink2); }
.lrow-top .v { font-size: 22px; font-weight: 600; line-height: 1.15; text-align: right; }
.lrow-top .v small { font-size: 13px; font-weight: 400; color: var(--ink2); }
.lrow-top.warn .l, .lrow-top.warn .v { color: var(--warn); }

/* A barra segmentada: as larguras são o peso de cada sessão (km planeados), dado
   pelo Python no flex-grow. Os 2 px entre segmentos são da especificação. Não se
   tocam: 12 px não é alvo, e os dias da agenda já abrem a folha. */
.segbar { display: flex; gap: 2px; height: 12px; margin-top: 8px; }
.segbar:has(.seg-tag) { margin-bottom: 16px; }
.seg { position: relative; flex: 1 1 0; min-width: 6px; border-radius: 3px; }
.seg.done { background: var(--ink2); }
.seg.todo { border: 1.5px dashed var(--field); }
.seg.miss { background: var(--miss); border: 1.5px solid var(--crit); }
.seg.swap {
  background: repeating-linear-gradient(135deg, transparent 0 5px, var(--warn-soft) 5px 7px);
  border: 1.5px solid var(--warn);
}
.seg.extra { border: 1.5px solid var(--ink2); }
.seg-tag {
  position: absolute; top: calc(100% + 4px); right: 0;
  font-size: 11px; line-height: 1; color: var(--ink2); white-space: nowrap;
}

/* O medidor da bicicleta, com as percentagens do charts.cap_meter. A folga à
   direita para o número do teto já vem nelas: o trilho ocupa a largura toda. */
.meter { position: relative; height: 36px; margin-top: 8px; }
.meter-track, .meter-done, .meter-over { position: absolute; top: 20px; height: 8px; border-radius: 4px; }
.meter-track { left: 0; right: 0; background: var(--sunken); }
.meter-done { left: 0; background: var(--ink2); }
.meter-over { background: var(--crit); }
.meter-mark { position: absolute; top: 16px; width: 0; height: 16px; transform: translateX(-50%); }
.meter-mark.planned { border-left: 1.5px dashed var(--ink); }
.meter-mark.cap { border-left: 2px solid var(--crit); }
.meter-num {
  position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
  font-size: 11px; line-height: 1.15; color: var(--ink2); white-space: nowrap;
}
.meter-mark.cap .meter-num { color: var(--crit); font-weight: 600; }

/* ----------------------------------------------------- semanas à volta -- */

.lines { display: flex; flex-direction: column; gap: 8px; }
.oneline {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 44px; font-size: 15px; color: var(--ink); text-decoration: none; text-wrap: pretty;
}
a.oneline:hover { text-decoration: underline; }
a.oneline .chev { color: var(--accent); }
.oneline.muted { color: var(--ink3); }

/* ------------------------------------------------------------ folha do dia -- */

.com-folha { overflow: hidden; }
.scrim { position: fixed; inset: 0; z-index: 50; background: var(--scrim); }

/* No telemóvel a folha ocupa o ecrã e sobe do fundo; a partir de 960 px abre à
   direita sobre o fundo escurecido. A animação corre quando o `hidden` sai: um
   `display:none` que passa a flex recomeça a animação sem JavaScript. */
.sheet {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  height: 100vh; height: 100dvh;
  background: var(--surface); color: var(--ink);
  animation: folha-sobe 160ms ease-out;
}
.sheet-head {
  flex: none; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 12px 8px 12px 16px; padding-top: calc(12px + env(safe-area-inset-top));
  border-bottom: 1px solid var(--line);
}
.sheet-title { font-size: 22px; font-weight: 600; line-height: 1.25; }
/* O título vem do aria-label da linha, que começa em minúscula («terça, 15 de…»). */
.sheet-title::first-letter { text-transform: uppercase; }
.sheet-sub { font-size: 13px; color: var(--ink2); }
.sheet-body {
  flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  padding: 16px 16px calc(32px + env(safe-area-inset-bottom));
}

@keyframes folha-sobe { from { transform: translateY(100%); } to { transform: none; } }
@keyframes folha-entra { from { transform: translateX(100%); } to { transform: none; } }

/* O fragmento de /api/dia/<data>: os parciais, o que o relógio recebe, o registo e
   a troca de dia. O modelo não traz cor nenhuma — nem hexadecimal nem var() —, e é
   aqui que cada classe a recebe, para mudar com o tema. A geometria dos SVG vem
   calculada do charts.py; daqui só saem cores e traços. */
.day-detail { display: flex; flex-direction: column; gap: 24px; }

.dia-manha { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; font-size: 15px; }
.dia-manha .eyebrow { flex: none; }

/* Uma sessão por bloco, separadas por filete: a folha é uma folha, não uma pilha
   de cartões dentro de outro cartão. O filete vai em todos menos no primeiro, pelo
   `~`: um `:first-of-type` conta por elemento, e a secção do registo — a primeira
   `<section>` do fragmento — ficava sem filete por baixo das sessões. */
.dia-sessao { display: flex; flex-direction: column; gap: 8px; }
.dia-sessao ~ .dia-sessao { padding-top: 16px; border-top: 1px solid var(--line); }
.dia-titulo { font-size: 17px; font-weight: 600; line-height: 1.3; text-wrap: pretty; }
.dia-volume { font-size: 13px; color: var(--ink2); }
.dia-nota { display: flex; flex-direction: column; gap: 8px; font-size: 15px; white-space: pre-line; text-wrap: pretty; }
.dia-vazio { font-size: 15px; color: var(--ink2); }

/* «O que o relógio recebe» abre fechado: quem abre o dia quer primeiro saber o que
   é a sessão. O resumo é o alvo de toque, e tem 44 px. */
.dia-passos summary, .tira-lista summary {
  display: flex; align-items: center; min-height: 44px;
  font-size: 15px; color: var(--accent); cursor: pointer;
}
.tira-lista summary { font-size: 13px; }
.passos { margin-top: 4px; padding-left: 24px; list-style: decimal; }
.passos li { padding: 4px 0; font-size: 15px; }
.dia-passos .small { margin-top: 8px; }

/* Os factos: rótulo à esquerda, número à direita, e a nota por baixo do número —
   «de 24 planeados» pertence à distância, não a uma linha própria. */
.factos li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 12px;
  padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 15px;
}
.factos .k { font-size: 13px; color: var(--ink2); }
.factos .v { font-weight: 600; white-space: nowrap; text-align: right; }
.factos .n { grid-column: 2; font-size: 13px; color: var(--ink3); text-align: right; }

/* A tira dos parciais e a linha de FC. */
.tira, .grafico, .zonas, .mecanica { display: flex; flex-direction: column; gap: 4px; }
.tira-svg, .hr-svg { display: block; width: 100%; height: auto; }
.tira-grelha, .hr-grelha { stroke: var(--line); stroke-width: .8; }
.tira-fc, .tira-km, .hr-num { fill: var(--ink3); }
.tira-col { fill: var(--ink2); }
.tira-col.crit { fill: var(--crit); }
/* O teto por troço é da fase 2; a regra fica escrita para quando ele vier. */
.tira-teto { stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 5 3; }
.tira-teto-num { fill: var(--ink); font-weight: 600; }
/* Transparente: é só um alvo de dedo da altura toda por cima de cada coluna, porque
   uma coluna de quatro unidades de largura não se acerta. O toque deixa a marca. */
.tira-toque { fill: transparent; cursor: pointer; }
.tira-toque.escolhido { fill: var(--ink); fill-opacity: .12; }
/* A linha fixa do toque: 44 px de altura sempre, para a tira não saltar quando ela
   passa de «toca numa coluna» para o valor. */
.tira-linha { display: flex; align-items: center; min-height: 44px; font-size: 15px; font-weight: 600; }
.tira-lista ol { padding-left: 24px; list-style: decimal; }
.tira-lista li { padding: 2px 0; font-size: 13px; }
.hr-linha { fill: none; stroke: var(--series1); stroke-width: 1.6; stroke-linejoin: round; }

/* Deriva e desacoplamento: ▲ e cor só acima do limite, nunca a cor sozinha. */
.marcas { display: flex; flex-direction: column; gap: 4px; }
.marca { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink2); }
.marca.crit { color: var(--crit); font-weight: 600; }

/* As zonas levam o nome na legenda: a cor sozinha não distingue cinco níveis. */
.zbar { display: flex; height: 12px; overflow: hidden; border: 1px solid var(--line-strong); border-radius: 4px; }
.zseg { min-width: 2px; }
.zseg + .zseg { border-left: 1px solid var(--surface); }
.zdot { display: inline-block; width: 12px; height: 12px; margin-right: 8px; vertical-align: -2px; border-radius: 2px; }
.zseg.z1, .zdot.z1 { background: var(--surface2); }
/* A Z2 também leva contorno: no escuro, o --accent-soft (#1F2745) sobre a superfície
   (#1C1B18) deixava o quadrado da legenda invisível. */
.zdot.z1, .zdot.z2 { border: 1px solid var(--line-strong); }
.zseg.z2, .zdot.z2 { background: var(--accent-soft); }
.zseg.z3, .zdot.z3 { background: var(--series1); }
.zseg.z4, .zdot.z4 { background: var(--series2); }
.zseg.z5, .zdot.z5 { background: var(--crit); }

/* Registar: a grelha de 5x2 da especificação, com botões de 56 px. */
.registo-bloco { gap: 12px; }
.registo-feito { font-size: 15px; }
.registo { display: flex; flex-direction: column; gap: 12px; }
.rpe-grelha { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.rpe {
  min-height: 56px; border: 1px solid var(--field); border-radius: 8px;
  background: transparent; color: var(--ink); font-size: 17px; font-weight: 600; cursor: pointer;
}
.rpe:hover { background: var(--surface2); }
/* O escolhido lê-se pelo preenchimento e pelo aria-pressed, que é o que o leitor de
   ecrã anuncia: o estilo e o que se anuncia não podem divergir. */
.rpe[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.registo .btn.primary { align-self: flex-start; }
.forca { display: flex; align-items: center; gap: 12px; min-height: 44px; font-size: 15px; cursor: pointer; }
.forca input { width: 24px; height: 24px; accent-color: var(--ink); }

/* Trocar de dia. Nada vai ao Tredict antes de a barra ser lida e confirmada. */
.troca { gap: 12px; }
.troca select { width: 100%; }
.troca > .btn { align-self: flex-start; }
.troca-barra { display: flex; flex-direction: column; gap: 12px; padding: 12px; background: var(--surface2); border-radius: 8px; }
.troca-resumo { font-size: 15px; text-wrap: pretty; }
.troca-botoes { display: flex; flex-wrap: wrap; gap: 8px; }

/* --------------------------------------------------------------- consulta -- */

/* Seis blocos abertos, um cartão cada, com os 32 px do `.wrap` entre eles. O
   `scroll-margin-top` é por causa da barra fixa: o dorsal da Semana liga a
   `/consulta#provas`, e sem ele o cabeçalho das provas ficava debaixo da barra. */
.bloco {
  display: flex; flex-direction: column; gap: 12px;
  scroll-margin-top: calc(68px + env(safe-area-inset-top));
}
.vazio { font-size: 15px; color: var(--ink3); }
.sub { font-size: 13px; font-weight: 500; color: var(--ink2); }

.provas li { padding: 8px 0; border-top: 1px solid var(--line); font-size: 15px; text-wrap: pretty; }
.provas li:first-child { padding-top: 0; border-top: 0; }
.provas li:last-child { padding-bottom: 0; }
.prova-nome { font-weight: 600; }

/* Nome à esquerda, valor à direita, e por baixo o quando e a etiqueta. Não é mono:
   na direção visual o mono fica para a agenda, os volumes e a tabela da carga. */
.valores li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; align-items: baseline;
  padding: 8px 0; border-top: 1px solid var(--line);
}
.valores li:first-child { padding-top: 0; border-top: 0; }
.valores .k { font-size: 15px; font-weight: 500; }
.valores .v { font-size: 17px; font-weight: 600; line-height: 1.15; text-align: right; white-space: nowrap; }
.valores .v.none { font-size: 15px; font-weight: 400; color: var(--ink3); }
.valores .n {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px;
  font-size: 13px; color: var(--ink2);
}
/* «caducado ▲»: a palavra e o triângulo, nunca a cor sozinha. */
.caducado { display: inline-flex; align-items: center; gap: 4px; color: var(--warn); font-weight: 600; white-space: nowrap; }
.aerobia { display: flex; flex-direction: column; gap: 4px; }
.aerobia p { font-size: 15px; text-wrap: pretty; }

/* A tabela da carga: números em mono, alinhados à direita como numa lista de
   partida. O cabeçalho do total parte em duas linhas num telemóvel, e é melhor
   isso do que encolher os números. */
.carga { width: 100%; border-collapse: collapse; }
.carga th, .carga td { padding: 8px 0 8px 12px; border-top: 1px solid var(--line); text-align: right; vertical-align: baseline; }
.carga thead th { padding-top: 0; border-top: 0; font-size: 13px; font-weight: 400; line-height: 1.3; color: var(--ink2); }
.carga tbody th { padding-left: 0; text-align: left; font-size: 15px; font-weight: 500; }
.carga td { font-size: 17px; font-weight: 600; line-height: 1.15; white-space: nowrap; }
.carga td.crit { color: var(--crit); }
.acima { display: inline-flex; align-items: center; gap: 4px; }

.frases { display: flex; flex-direction: column; gap: 4px; font-size: 15px; }

/* Os dois gráficos. A geometria vem do charts.py; daqui saem cores e traços. O
   texto herda a pilha da página, com os algarismos de largura fixa. */
.carga-svg, .vol-svg { display: block; width: 100%; height: auto; overflow: visible; }
.graf-grelha { stroke: var(--line); stroke-width: .8; }
.graf-num { fill: var(--ink3); }
.carga-corrida { fill: none; stroke: var(--series1); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.carga-total { fill: none; stroke: var(--series2); stroke-width: 1.5; stroke-dasharray: 5 3; stroke-linejoin: round; }
/* O valor na ponta na cor da sua linha: sem legenda, é assim que o número se liga
   ao traço, além de ser o mesmo da tabela. As duas cores passam 4,5:1 nos dois
   temas sobre a superfície. */
.carga-ponta-corrida { fill: var(--series1); font-weight: 600; }
.carga-ponta-total { fill: var(--series2); font-weight: 600; }

/* A faixa inteira da semana é o alvo da ligação. Transparente e não `none`: sem
   pintura, o toque atravessava-a. */
.vol-alvo { fill: transparent; }
/* A semana em curso: contorno tracejado, nunca a vermelho — a meio da semana estar
   abaixo do planeado é o normal. */
.vol-alvo.atual { stroke: var(--field); stroke-width: 1; stroke-dasharray: 3 3; }
.vol-link { cursor: pointer; }
.vol-link:hover .vol-alvo { fill: var(--ink); fill-opacity: .06; }
/* O contorno do foco desenha-se na faixa: o `outline` de um elemento de SVG não
   aparece em todos os browsers, e sem ele o teclado não sabe onde está. */
.vol-link:focus { outline: none; }
.vol-link:focus-visible .vol-alvo { stroke: var(--accent); stroke-width: 2; stroke-dasharray: none; }
.vol-col { fill: var(--series1); }
.vol-plano { stroke: var(--ink); stroke-width: 2; }
.vol-bici { fill: var(--ink2); }
/* O rótulo passa por cima de três semanas e do tracejado da corrente: o contorno
   da cor do cartão abre-lhe espaço para se ler. */
.vol-rotulo {
  fill: var(--ink); font-weight: 600;
  paint-order: stroke; stroke: var(--surface); stroke-width: 3px; stroke-linejoin: round;
}

.lista summary {
  display: flex; align-items: center; min-height: 44px;
  font-size: 15px; color: var(--accent); cursor: pointer;
}
.lista li + li { border-top: 1px solid var(--line); }

.ultima { font-size: 17px; font-weight: 600; line-height: 1.3; text-wrap: pretty; }
.paragens li {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 12px;
  padding: 8px 0; border-top: 1px solid var(--line); font-size: 15px;
}
.paragens li:last-child { padding-bottom: 0; }
.dias { white-space: nowrap; color: var(--ink2); }

/* Registar. O campo tem letra de 16 px, do `input` da base: abaixo disso o Safari
   do iPhone faz zoom ao tocar.

   Margens e não `gap`: a linha do resultado está sempre na página, vazia até haver
   o que dizer, porque uma região viva que aparece de repente nem sempre é
   anunciada. Com `gap` ocupava um intervalo em branco; vazia e sem margem, não
   ocupa nada. */
.registar > * + *, .peso > * + * { margin-top: 8px; }
.registar > [data-saida]:empty, .peso > [data-saida]:empty { margin-top: 0; }
.peso-linha { display: flex; flex-wrap: wrap; gap: 8px; }
.peso-linha .input { flex: 1 1 8rem; width: auto; }
.botoes { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------------------------------------------------------------- entrada -- */

/* A entrada partilha a folha e os tokens de propósito: quando teve folha própria,
   ficou para trás num redesenho e passou meses a pedir variáveis que já não
   existiam. */
.gate {
  min-height: 100vh; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: calc(24px + env(safe-area-inset-top)) 16px calc(24px + env(safe-area-inset-bottom));
}
.gate-card {
  width: 100%; max-width: 360px;
  display: flex; flex-direction: column; gap: 16px;
  padding: 24px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow);
}
.gate-mark { display: block; }
.gate-mark rect { fill: var(--ink2); }
.gate-mark .accent { fill: var(--accent); }
.gate h1 { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -.01em; text-wrap: balance; }
.lead { margin-top: 4px; font-size: 13px; color: var(--ink2); text-wrap: pretty; }
.gate form { display: flex; flex-direction: column; gap: 8px; }
.gate label { font-size: 13px; color: var(--ink2); }
.input {
  width: 100%; min-height: 44px; padding: 8px 12px;
  border: 1px solid var(--field); border-radius: 8px;
  background: var(--surface); color: var(--ink); font-size: 16px;
}
.gate .btn.primary { width: 100%; margin-top: 8px; }
.alerta {
  padding: 8px 12px; border: 1px solid var(--crit); border-radius: 8px;
  background: var(--crit-soft); color: var(--ink); font-size: 13px; text-wrap: pretty;
}
.alerta b { font-weight: 600; color: var(--crit); }
.foot-note { font-size: 13px; color: var(--ink3); text-wrap: pretty; }

/* ------------------------------------------------------------- larguras -- */

@media (min-width: 600px) {
  .bar { padding: 0 8px 0 20px; }
  .wrap { padding-left: 20px; padding-right: 20px; }
  .footer { padding-left: 20px; padding-right: 20px; }
  .syncstrip:not(:empty) { padding-left: 20px; padding-right: 20px; }
  /* Com espaço, o Sincronizar diz o que faz em vez de ser só a seta. */
  .sync-word { display: inline; }
  .iconbtn.sync { padding: 0 12px; }
}

@media (min-width: 960px) {
  /* Largura máxima e não largura fixa: nada nesta folha pode obrigar a deslizar
     para o lado num ecrã estreito, e o teste mede-o. */
  .sheet { left: auto; width: 100%; max-width: 440px; border-left: 1px solid var(--line); animation-name: folha-entra; }
}

/* Só a folha do dia se move, e nem ela quando o sistema pede menos movimento. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
