/* =========================================================
   Solar Dashboard — modern dark theme
   ========================================================= */
:root {
  --bg-0: #07090d;
  --bg-1: #0d1118;
  --bg-2: #11161f;
  --card: linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0.01)) ,#11161f;
  --card-border: rgba(255,255,255,0.07);
  --card-border-hover: rgba(255,255,255,0.14);

  --text: #e6edf3;
  --muted: #8b94a3;
  --muted-2: #6b7280;
  --accent: #60a5fa;

  --pv: #fbbf24;       /* amber/yellow */
  --pv-glow: rgba(251,191,36,0.45);
  --house: #60a5fa;    /* blue */
  --house-glow: rgba(96,165,250,0.4);
  --grid: #f87171;     /* red */
  --grid-glow: rgba(248,113,113,0.45);
  --batt: #34d399;     /* green */
  --batt-glow: rgba(52,211,153,0.45);

  --good: #22c55e;
  --good-soft: rgba(34,197,94,0.15);
  --good-text: #86efac;       /* light green for text on dark bg */
  --bad: #ef4444;
  --bad-soft: rgba(239,68,68,0.15);
  --bad-text: #fca5a5;        /* light red for text on dark bg */
  --warn: #f59e0b;
  --warn-text: #fbbf24;       /* amber for warning text */

  /* Spacing scale (4/8/12/16/24) — usado em gaps/paddings de cards e
     forms para evitar gaps 2/3/4/6/7/9/11px ad-hoc. */
  --gap-xs: 4px;
  --gap-sm: 8px;
  --gap-md: 12px;
  --gap-lg: 16px;
  --gap-xl: 24px;

  /* Input sizing — 3 famílias canónicas usadas em forms da app. */
  --input-pad-sm: 8px 12px;   /* compact: 2FA mgmt, eredes, identificação, priority */
  --input-pad-md: 9px 12px;   /* default form-label */
  --input-pad-lg: 12px 14px;  /* large: setup 2FA centrado */
  --input-fs-sm: 13px;
  --input-fs-md: 14px;
  --input-fs-lg: 16px;

  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md: 0 4px 16px rgba(0,0,0,0.35);
  --shadow-lg: 0 12px 40px rgba(0,0,0,0.45);

  --radius-sm: 10px;
  --radius:    14px;
  --radius-lg: 18px;

  /* Unified sizing for every input/select/button that sits inside a control
     row (filters, tariff-potencia-row, omie-avg-controls, …). Keeps the whole
     dashboard reading as one coherent strip, regardless of which tab. */
  --control-h: 34px;
  --control-fs: 13px;
  --control-radius: 9px;
}

* { box-sizing: border-box; }

/* Enforce the HTML `hidden` attribute against explicit `display:` rules
   elsewhere in the stylesheet. Without this, `[hidden]` on an element that
   later gets `display: flex/grid/...` ends up visible — spec defines
   `[hidden] { display: none }` at user-agent level, which any author rule
   with the same specificity can override. !important here is intentional:
   `hidden` should always win, regardless of what the element looks like. */
[hidden] { display: none !important; }

html, body {
  margin: 0; padding: 0;
  background:
    radial-gradient(ellipse 80% 60% at 20% -10%, rgba(96,165,250,0.10), transparent 60%),
    radial-gradient(ellipse 70% 50% at 110% 20%, rgba(251,191,36,0.08), transparent 60%),
    radial-gradient(ellipse 60% 50% at 50% 100%, rgba(52,211,153,0.05), transparent 70%),
    var(--bg-0);
  background-attachment: fixed;
  color: var(--text);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Prevent the iPhone's "tap highlight" grey flash on every interactive
     element; we already have proper :hover/:active states. */
  -webkit-tap-highlight-color: transparent;
  /* Don't let iOS auto-zoom text when the device rotates to landscape. */
  -webkit-text-size-adjust: 100%;
          text-size-adjust: 100%;
  /* Avoid the rubber-band overscroll background bleed-through when the user
     pulls past the top/bottom — keeps the dark theme edge-to-edge. */
  overscroll-behavior-y: contain;
}

/* Most form fields scale fine, but Safari zooms the viewport whenever a
   focused input has font-size < 16px. Force 16px on text-like inputs only
   (date pickers, number, password, etc.) so a tap on a filter doesn't suddenly
   resize the layout. */
input[type="text"], input[type="number"], input[type="password"],
input[type="email"], input[type="search"], input[type="date"],
input[type="tel"], select, textarea {
  font-size: 16px;
}

/* Re-enable forwarding the iOS rubber-band on the body (we contained it on
   <html>); useful when long tab-panels need to scroll naturally. */
body { overscroll-behavior-y: auto; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.muted { color: var(--muted); }
.small { font-size: 12px; }

/* =========================================================
   TOP BAR
   ========================================================= */
.topbar {
  width: 100%;
  box-sizing: border-box;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 24px;
  background: #0d1118;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand .logo {
  display: inline-flex;
  width: 34px; height: 34px;
  align-items: center; justify-content: center;
  border-radius: 10px;
  background: linear-gradient(135deg, #fbbf24, #f59e0b);
  color: #1c1300;
  font-size: 20px;
  box-shadow: 0 6px 20px rgba(251,191,36,0.35);
}
.brand h1 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: 0.2px; }

/* ----- Topbar action buttons (right side) -----
   Two pills: Definições (gear → /2fa.html) and Terminar sessão (power). Same
   shape as the legacy .tick chips so the visual rhythm of the bar stays. */
.topbar-actions { display: flex; align-items: center; gap: 10px; }
.topbar-btn {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  padding: 8px 14px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
  cursor: pointer;
}
.topbar-btn:hover {
  background: rgba(255,255,255,0.08);
  border-color: var(--card-border-hover);
  text-decoration: none;
}
.topbar-btn-icon { font-size: 14px; line-height: 1; }
.topbar-btn-label { white-space: nowrap; }
.topbar-btn-logout {
  color: var(--bad);
  border-color: rgba(248,113,113,0.25);
}
.topbar-btn-logout:hover {
  background: rgba(248,113,113,0.12);
  border-color: rgba(248,113,113,0.45);
  color: var(--bad);
}

.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; box-shadow: 0 0 0 0 currentColor; }
.dot-on   { background: var(--good); box-shadow: 0 0 12px var(--good); animation: pulse 1.6s ease-in-out infinite; }
.dot-off  { background: var(--bad);  box-shadow: 0 0 10px var(--bad); }
.dot-warn { background: var(--warn); box-shadow: 0 0 10px var(--warn); }

@keyframes pulse {
  0%,100% { opacity: 1; transform: scale(1); }
  50%     { opacity: 0.55; transform: scale(0.85); }
}

/* =========================================================
   LAYOUT
   ========================================================= */
main {
  max-width: 1380px;
  margin: 0 auto;
  padding: 22px;
  display: grid;
  gap: 20px;
}

.grid { display: grid; gap: 18px; }
.grid-live          { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); }
.grid-2             { grid-template-columns: 1fr 1fr; }
/* When grid-2 holds an odd number of direct cards (e.g. 5 charts on Tab 4),
   the last one would sit alone at half-width with empty space beside it.
   Span it across both columns so the last row reads as a full-width strip. */
.grid-2 > .card:last-child:nth-child(odd) { grid-column: 1 / -1; }
.grid-kpis          { grid-template-columns: repeat(4, 1fr); }
.grid-energy-kpis   { grid-template-columns: repeat(6, 1fr); }
.grid-omie          { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* On desktop the 4 OMIE cards sit in one row. Group them by DAY (Hoje on
   the left half, Amanhã on the right half) instead of by TYPE — easier to
   compare today vs tomorrow at a glance. Mobile keeps DOM order (2×2 grid
   reading: simples row first, bi-horário row second) because the user
   confirmed that's the layout they prefer at narrow widths. */
@media (min-width: 721px) {
  .grid-omie [data-omie="today-simples"]    { order: 1; }
  .grid-omie [data-omie="today-bi"]         { order: 2; }
  .grid-omie [data-omie="tomorrow-simples"] { order: 3; }
  .grid-omie [data-omie="tomorrow-bi"]      { order: 4; }
}

.card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: var(--shadow-md);
  transition: border-color 200ms ease, transform 200ms ease;
}
.card:hover { border-color: var(--card-border-hover); }

.card-head {
  /* Default flex-start — h2 sozinho fica encostado à esquerda sem
     espaço morto à direita. Cards com botão/status ao lado do h2
     activam space-between via :has() abaixo, e o filho irmão é
     empurrado para a direita com margin-left:auto (fallback caso
     :has() não seja suportado, e.g. Safari < 15.4). */
  display: flex;
  justify-content: flex-start;
  align-items: baseline;
  margin-bottom: var(--gap-md);
  gap: var(--gap-md);
}
.card-head:has(> :nth-child(2)) { justify-content: space-between; }
.card-head > h2 + * { margin-left: auto; }
.card-head h2 {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
}
.card-head h3 { margin: 0; font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }

/* =========================================================
   FLOW DIAGRAM (SVG)
   ========================================================= */
.card-flow { padding-bottom: 14px; }
.flow-wrap { width: 100%; }
.flow-svg  { width: 100%; height: auto; max-height: 420px; display: block; }

.flow-svg .track {
  stroke: rgba(255,255,255,0.05);
  stroke-width: 2;
  stroke-linecap: round;
}
/* "Dots flowing" animation: stroke-dasharray with a near-zero dash length
   combined with stroke-linecap:round renders as discrete circles travelling
   along the path. Way smoother than rectangular dashes. */
.flow-svg .flow {
  stroke: rgba(255,255,255,0.95);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 0.01 18;
  animation: flow-dots 1.6s linear infinite;
  filter: drop-shadow(0 0 6px rgba(255,255,255,0.4));
  transition: stroke 220ms ease, opacity 220ms ease;
}
.flow-svg .flow.off    { opacity: 0; }
.flow-svg .flow.color-pv   { stroke: var(--pv);    filter: drop-shadow(0 0 6px var(--pv-glow)); }
.flow-svg .flow.color-grid { stroke: var(--grid);  filter: drop-shadow(0 0 6px var(--grid-glow)); }
.flow-svg .flow.color-batt { stroke: var(--batt);  filter: drop-shadow(0 0 6px var(--batt-glow)); }
.flow-svg .flow.reverse { animation-direction: reverse; }
@keyframes flow-dots { to { stroke-dashoffset: -36; } }

.flow-svg .node circle {
  fill: var(--bg-1);
  stroke: rgba(255,255,255,0.18);
  stroke-width: 2;
  transition: stroke 200ms ease, filter 200ms ease;
}
.flow-svg .node-pv   circle { stroke: var(--pv);   filter: drop-shadow(0 0 16px var(--pv-glow)); }
.flow-svg .node-house circle{ stroke: var(--house);filter: drop-shadow(0 0 16px var(--house-glow)); }
.flow-svg .node-grid circle { stroke: var(--grid); filter: drop-shadow(0 0 16px var(--grid-glow)); }
.flow-svg .node-batt circle { stroke: var(--batt); filter: drop-shadow(0 0 16px var(--batt-glow)); }

/* SVG icons inside each node — colour comes from the parent .node-* via currentColor */
.flow-svg .node-pv    { color: var(--pv); }
.flow-svg .node-house { color: var(--house); }
.flow-svg .node-grid  { color: var(--grid); }
.flow-svg .node-batt  { color: var(--batt); }

.flow-svg .node-label {
  font-size: 9px; text-anchor: middle; fill: var(--muted);
  text-transform: uppercase; letter-spacing: 1.6px; font-weight: 700;
}
.flow-svg .node-value {
  text-anchor: middle;
  font-family: 'JetBrains Mono', monospace;
  font-size: 17px;
  font-weight: 700;
  fill: var(--text);
}
.flow-svg .node-pv    .node-value { fill: var(--pv); }
.flow-svg .node-house .node-value { fill: var(--house); }
.flow-svg .node-grid  .node-value { fill: var(--grid); }
.flow-svg .node-batt  .node-value { fill: var(--batt); }

/* SoC% inside the battery icon. The text is rendered TWICE in two layers:
     • `.node-soc`     → green base, always visible everywhere
     • `.node-soc-inv` → dark overlay, clipped to the green-fill rect so it
       only shows on top of the fill, "punching out" the digits
   The visual result: characters over the fill appear in card-background
   dark, characters over the empty side stay battery-green — contrast is
   preserved at any SoC, character-by-character. */
.flow-svg .node-soc {
  text-anchor: middle;
  dominant-baseline: central;
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 800;
  fill: var(--batt);
}
.flow-svg .node-soc-inv {
  fill: var(--bg-2);
}

/* Smooth the battery fill animation. SVG `width` is a CSS geometry property
   in modern browsers, so a CSS transition tweens the attribute between
   updates (default WS push cadence) instead of snapping. Both the visible
   fill rect and its matching clip rect (used to mask the dark overlay text)
   transition together so the per-character contrast follows the bar edge
   smoothly. */
.flow-svg #bat-fill,
#bat-fill-clip-rect {
  transition: width 600ms cubic-bezier(0.25, 0.1, 0.25, 1);
}

/* Splits row under flow */
.splits {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  margin-top: 14px;
}
.split {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  display: flex; flex-direction: column; gap: 3px;
  text-align: left;
  transition: border-color 200ms ease, background 200ms ease;
}
.split-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600; }
.split-value { font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 700; }
.split-pv .split-value   { color: var(--pv); }
.split-batt .split-value { color: var(--batt); }
.split-grid .split-value { color: var(--grid); }
.split.dim { opacity: 0.4; }

/* =========================================================
   HERO CARDS
   ========================================================= */
.hero-stack { display: flex; flex-direction: column; gap: 18px; }
/* O .hero-stack (coluna direita do .grid-live) estende-se em altura
   para corresponder à coluna esquerda (flow card). Sem `flex: 1` no
   .hero-row, o espaço extra fica vazio em baixo do forecast. Com flex,
   o row de baixo cresce e os cards Poupança/Gasto ficam mais altos,
   alinhando o conjunto até ao fim do flow. */
.hero-stack > .hero-row > .hero { height: 100%; }
/* Variante compacta — sem sparkline, padding reduzido, valor menor.
   Usado pelos cards Poupança/Gasto/Rates depois de removidos os sparks
   para encaixarem 3 rows abaixo do forecast sem ficar tudo gigante. */
/* Cards compactos da hero-stack — mais discretos em altura para que o
   forecast (linha 1 com `flex: 1`) absorva a maior parte do espaço
   vertical e o chart fique grande o suficiente para mostrar bem picos
   ≥6kW. Valores e padding reduzidos sem comprometer leitura. */
.hero-compact { padding: 10px 16px; }
.hero-compact .hero-value { font-size: 26px; margin: 2px 0 0; }
.hero-compact .hero-label { font-size: 10px; }
.hero-compact .hero-sub { font-size: 11px; }
.hero-compact .rate-value { font-size: 22px; }
.hero-compact .rate-label { font-size: 10px; }

/* Centra verticalmente o conteúdo dos cards Poupança/Gasto — label,
   valor e sub ficam no meio da altura disponível em vez de empilhados
   no topo. */
.hero-savings.hero-compact,
.hero-spent.hero-compact {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* O card .hero-rates é UM card no .hero-stack mas tem 2 sub-colunas
   internas. Para essas sub-colunas alinharem com os cards Poupança e
   Gasto da row de cima (1fr 1fr, gap 18px), o .hero-rates abdica do
   seu padding e a .rates-row passa a herdar o mesmo grid + gap. As
   células internas levam o padding por dentro, mantendo o respiro
   visual idêntico. */
.hero-compact.hero-rates { padding: 0; }
.hero-rates .rates-row {
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-bottom: 0;
}
.hero-rates .rates-row > div {
  padding: 14px 18px;
}

/* Row de 2 cards lado-a-lado (Poupança + Gasto) — em desktop 1fr 1fr,
   em mobile colapsa para 1 coluna stacked. */
.hero-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}
@media (max-width: 720px) {
  .hero-row { grid-template-columns: 1fr; }
}
.hero { position: relative; overflow: hidden; padding: 22px 22px 8px; }
.hero-savings {
  background:
    radial-gradient(ellipse at top right, rgba(34,197,94,0.20), transparent 60%),
    var(--card);
  border-color: rgba(34,197,94,0.25);
}
.hero-spent {
  background:
    radial-gradient(ellipse at top right, rgba(245,158,11,0.20), transparent 60%),
    var(--card);
  border-color: rgba(245,158,11,0.25);
}
.hero-spent .hero-value { color: var(--warn); }
.hero-rates {
  background:
    radial-gradient(ellipse at top right, rgba(96,165,250,0.18), transparent 60%),
    var(--card);
}
.hero-label {
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600;
}
.hero-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 44px; font-weight: 800;
  margin: 6px 0 4px;
  color: var(--good);
  line-height: 1.1;
}
.hero-sub { font-size: 12px; color: var(--muted); }
.hero-spark { display: block; width: 100%; height: 60px; margin-top: 6px; }

/* Solar Forecast card — produção prevista vs real do dia. Estende
   horizontalmente todo o hero-stack (linha 1), com chart maior que
   o spark dos cards de baixo. `flex: 1` faz o card crescer
   verticalmente para preencher o espaço restante do .hero-stack (que
   por sua vez espelha a altura do flow card via grid stretch). */
.hero-forecast {
  background:
    radial-gradient(ellipse at top right, rgba(251,191,36,0.18), transparent 65%),
    var(--card);
  border-color: rgba(251,191,36,0.20);
  padding: 18px 22px 14px;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.hero-forecast-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.hero-forecast-totals {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  font-size: 13px;
}
.hero-forecast-total strong {
  font-family: 'JetBrains Mono', monospace;
  font-variant-numeric: tabular-nums;
  color: var(--pv);
  margin-left: 4px;
}
/* Body wrapper — canvas e empty-state partilham o mesmo espaço.
   `flex: 1` faz o body crescer verticalmente dentro do .hero-forecast
   (flex column) para o chart absorver o espaço sobrante quando a
   coluna direita corresponde à altura do flow card. min-height
   garante que o chart nunca fica esmagado quando a coluna é curta
   (e.g. zoom-in extremo do navegador). */
.hero-forecast-body {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 180px;
}
.hero-forecast-chart {
  position: absolute;
  inset: 0;
  display: block;
  width: 100% !important;
  height: 100% !important;
}
.hero-forecast-empty {
  position: absolute;
  inset: 20px 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 10px 16px;
  border: 1px dashed var(--card-border);
  border-radius: var(--control-radius);
}
@media (max-width: 720px) {
  .hero-forecast-body { min-height: 150px; flex: 0 0 auto; height: 150px; }
  .hero-forecast-totals { gap: 12px; font-size: 12px; }
  /* Em mobile o .hero-stack já é stack vertical natural; não há
     altura "extra" para o forecast absorver. Mantém altura fixa. */
  .hero-forecast { flex: 0 0 auto; }
}

.rates-row {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  margin-bottom: 4px;
}
.rate-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600; }
.rate-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 26px; font-weight: 700; line-height: 1.1; margin-top: 6px;
}
.rate-value.cost { color: var(--grid); }
.rate-value.sale { color: var(--good); }

/* =========================================================
   KPI CARDS (today)
   ========================================================= */
.grid-kpis .kpi {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 16px 18px;
  display: flex; align-items: center; gap: 14px;
  box-shadow: var(--shadow-sm);
  transition: border-color 200ms ease;
}
.grid-kpis .kpi:hover { border-color: var(--card-border-hover); }
.kpi-icon {
  width: 42px; height: 42px;
  flex: 0 0 auto;
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 18px;
  font-family: 'JetBrains Mono', monospace;
}
.kpi-icon-gross { background: rgba(248,113,113,0.15); color: var(--grid); }
.kpi-icon-real  { background: rgba(245,158,11,0.15); color: var(--warn); }
.kpi-icon-sale  { background: rgba(34,197,94,0.15);  color: var(--good); }
.kpi-icon-net   { background: rgba(96,165,250,0.15); color: var(--house); }
.kpi-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kpi-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600; }
.kpi-value { font-family: 'JetBrains Mono', monospace; font-size: 22px; font-weight: 800; }
.kpi-sub   { font-size: 11px; color: var(--muted); }

/* Bigger variant for the Contas tab (where these cards are the headline row,
   not a strip squeezed under the live flow). Vertical spacing comes from the
   tab-panel's own `gap` — don't add margin here or it doubles up. */
.grid-kpis-lg { gap: 20px; }
.grid-kpis-lg .kpi {
  padding: 22px 26px;
  gap: 18px;
  border-radius: var(--radius-lg);
}
.grid-kpis-lg .kpi-icon { width: 56px; height: 56px; font-size: 22px; border-radius: 14px; }
.grid-kpis-lg .kpi-label { font-size: 12px; }
.grid-kpis-lg .kpi-value { font-size: 30px; }
.grid-kpis-lg .kpi-sub   { font-size: 12px; }

/* =========================================================
   OMIE QUICK CARDS (Tarifário tab header)
   Four cards in a row: simples-hoje, simples-amanhã, bi-hoje, bi-amanhã.
   Each card shows a type label, a day pill, and either one big value
   (simples) or two side-by-side values (bi-horário vazio/fora-vazio).
   ========================================================= */
.omie-card {
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.omie-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.omie-type {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1 1 auto;
}
.omie-day { flex: 0 0 auto; }
.omie-day {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(96,165,250,0.15);
  color: var(--house);
  border: 1px solid rgba(96,165,250,0.25);
}
.omie-card.omie-bi .omie-day {
  background: rgba(52,211,153,0.15);
  color: var(--good);
  border-color: rgba(52,211,153,0.25);
}
/* Stacking order inside every OMIE card (top → bottom):
     head → date → value(s) → [min/max for simples]
   The date sits flush under the head so it lines up vertically across all
   four cards. `margin-top: auto` on the value (or the bi-values wrapper)
   pushes the main number(s) down to fill the remaining space, which keeps
   the visual rhythm consistent regardless of how much content sits below. */
.omie-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 26px;
  font-weight: 800;
  color: var(--text);
  line-height: 1.05;
  margin-top: auto;
}
.omie-date {
  font-size: 11px;
  color: var(--muted);
  /* Pull the date slightly closer to the head — the card's default flex gap
     leaves too much breathing room and the date felt disconnected from the
     OMIE title above. */
  margin-top: -6px;
}
.omie-bi-values {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: auto;
}
.omie-bi-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}
.omie-bi-label {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  font-weight: 700;
}
.omie-bi-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 22px;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
}

/* Max/Min strip below the simples value — two pills, label inline with value,
   colour-coded (red = max, green = min) to match Tiago Felícia's OMIE widget. */
.omie-minmax {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-top: 4px;
}
.omm {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 8px;
  background: rgba(255,255,255,0.025);
  border: 1px solid var(--card-border);
  min-width: 0;
}
.omm-label {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--muted);
}
.omm-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
}
.omm-max .omm-value { color: var(--bad); }
.omm-min .omm-value { color: var(--good); }

/* "Aguarda publicação OMIE" hint shown when tomorrow's prices aren't out yet
   (before ~14:30 local time). When this is visible, hide the placeholder
   values so the user doesn't see two contradictory readouts. */
.omie-empty {
  font-size: 11px;
  color: var(--muted);
  text-align: center;
  padding: 8px 6px;
  border-radius: 8px;
  background: rgba(245,158,11,0.08);
  border: 1px dashed rgba(245,158,11,0.25);
  /* When `.omie-no-data` hides every other body element, this is the only
     visible content under the head — `margin: auto 0` centres it vertically
     in the remaining card space. */
  margin: auto 0;
}
.omie-card.omie-no-data .omie-value,
.omie-card.omie-no-data .omie-date,
.omie-card.omie-no-data .omie-minmax,
.omie-card.omie-no-data .omie-bi-values { display: none; }

/* =========================================================
   OMIP FUTUROS TABLE (Tarifário tab)
   Three side-by-side tables (Diários / Semanais / Mensais) showing the
   futures contracts from the OMIP MIBEL feed. Each row: descrição, valor
   in €/MWh, variação as an up/down arrow + signed delta. Stacks vertically
   on mobile.
   ========================================================= */
.omip-card .card-head { align-items: center; }
.omip-tables {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}
.omip-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.omip-section-head {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--card-border);
}
.omip-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.omip-table th, .omip-table td {
  padding: 7px 8px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  text-align: left;
}
.omip-table thead th {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  background: transparent;
  position: static;
}
.omip-table th.num, .omip-table td.num { text-align: right; }
.omip-unit { color: var(--muted); font-weight: 500; font-size: 10px; }
.omip-table tbody td { font-weight: 600; }
.omip-value { font-family: 'JetBrains Mono', monospace; }
.omip-var {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
}
.omip-var-up    { color: var(--good); }
.omip-var-down  { color: var(--bad); }
.omip-var-equal { color: var(--muted); }
.omip-arrow {
  display: inline-block;
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
.omip-var-up .omip-arrow    { border-bottom: 7px solid var(--good); }
.omip-var-down .omip-arrow  { border-top:    7px solid var(--bad); }
.omip-var-equal .omip-arrow {
  border: none;
  width: 8px; height: 2px;
  background: var(--muted);
}
.omip-empty {
  padding: 14px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}

/* =========================================================
   OMIE PERIOD AVERAGE CALCULATOR (Tarifário tab, bottom card)
   Date range + Calcular button on top, three result rows below
   (Simples / Bi-horário Vazio / Bi-horário Fora-vazio).
   ========================================================= */
.omie-avg-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
  margin-bottom: 16px;
}
.omie-avg-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.omie-avg-field input[type="date"] {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  font-family: inherit;
  outline: none;
  transition: border-color 200ms ease;
  color-scheme: dark;
}
.omie-avg-field input[type="date"]:focus { border-color: var(--accent); }

/* Unified sizing — same height/font/radius as .filters and .tariff-potencia-row
   so the whole dashboard keeps a single control-row rhythm. */
.omie-avg-controls input,
.omie-avg-controls select,
.omie-avg-controls .btn {
  height: var(--control-h);
  box-sizing: border-box;
  padding: 0 12px;
  font-size: var(--control-fs);
  line-height: 1;
  border-radius: var(--control-radius);
}
.omie-avg-controls input[type="date"] { width: 150px; }

.omie-avg-results {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.omie-avg-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 10px 14px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: 10px;
}
.omie-avg-label {
  color: var(--muted);
  font-size: 13.5px;
}
.omie-avg-value {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 700;
  font-size: 16px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.omie-avg-meta { margin-top: 4px; }

/* energy KPI strip (kWh) */
.grid-energy-kpis .ek {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}
.ek-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 600; }
.ek-value { font-family: 'JetBrains Mono', monospace; font-size: 18px; font-weight: 700; margin-top: 4px; }

/* =========================================================
   CHART CARDS
   ========================================================= */
.chart-h300 { height: 300px; position: relative; }
.chart-h280 { height: 280px; position: relative; }

.mix-wrap { display: grid; grid-template-columns: 1fr 200px; gap: 16px; align-items: center; }
.mix-legend { display: flex; flex-direction: column; gap: 12px; }
.mix-row { display: flex; align-items: center; gap: 10px; }
.mix-swatch { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
.mix-text { display: flex; flex-direction: column; gap: 2px; }
.mix-name { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.mix-val  { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 14px; }

.chart-card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 16px;
}
.chart-card h3 { margin: 0 0 10px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; }

/* =========================================================
   PERIOD CARDS
   ========================================================= */
.period-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}
.period-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  transition: border-color 200ms ease, transform 200ms ease;
}
.period-card:hover { border-color: var(--card-border-hover); transform: translateY(-2px); }
.period-title { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.7px; font-weight: 700; }

/* Big number = either net savings (green) or net cost (red). The label above
   changes in lockstep so the user sees instantly whether they're winning or paying. */
.period-big-label {
  font-size: 10px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  font-weight: 700;
  margin-top: -2px;
}
.period-big {
  font-family: 'JetBrains Mono', monospace;
  font-size: 26px;
  font-weight: 800;
  line-height: 1.1;
}
.period-big-good { color: var(--good); }
.period-big-bad  { color: var(--grid); }
/* Linha que mete o €/kWh poupado/pago À FRENTE do valor grande em euros
   (mesma row): big number à esquerda, sub à direita.
   Em mobile podem quebrar para duas linhas (flex-wrap). */
.period-big-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}
/* Sub-text com média €/kWh — preço médio do consumo. Explica variação na
   poupança: preço médio mais alto → cada kWh de sol poupa mais. */
.period-big-sub {
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.2;
  white-space: nowrap;
}
.period-rows { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; font-size: 12px; }
.period-rows .lbl { color: var(--muted); }
.period-rows .val { font-family: 'JetBrains Mono', monospace; font-weight: 600; text-align: right; }

/* =========================================================
   FILTERS / BUTTONS / TABLE
   ========================================================= */
.filters {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
  margin-bottom: 18px;
}
.filters label {
  display: inline-flex; flex-direction: column;
  font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600;
  gap: 6px;
}
.filters input, .filters select {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  font-family: inherit;
  outline: none;
  transition: border-color 200ms ease;
}
.filters input:focus, .filters select:focus { border-color: var(--accent); }

/* Unified sizing for every interactive control inside .filters so the row reads
   as a single coherent strip — same height, same radius, same font size.
   Uses the --control-* tokens defined in :root so every control row across the
   app stays in lockstep. */
.filters input,
.filters select,
.filters .btn {
  height: var(--control-h);
  box-sizing: border-box;
  padding: 0 12px;
  font-size: var(--control-fs);
  line-height: 1;
  border-radius: var(--control-radius);
}
.filters .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
}

.btn {
  background: rgba(255,255,255,0.06);
  color: var(--text);
  border: 1px solid var(--card-border);
  padding: 9px 14px;
  border-radius: 9px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 150ms ease;
  font-family: inherit;
}
.btn:hover { background: rgba(255,255,255,0.10); border-color: var(--card-border-hover); }
.btn-primary {
  background: linear-gradient(180deg, #3b82f6, #2563eb);
  border-color: rgba(96,165,250,0.4);
  color: #fff;
}
.btn-primary:hover { filter: brightness(1.1); }
.filters .quick { display: inline-flex; gap: 6px; margin-left: auto; flex-wrap: wrap; }

/* Read-only tariff label inside the price-history filter strip — matches the
   visual weight of the inputs without being editable. */
.ph-tariff-readout {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  border-radius: 9px;
  text-transform: none;
  letter-spacing: 0;
  max-width: 460px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hist-totals {
  display: flex; gap: 18px; flex-wrap: wrap;
  margin: 14px 0 8px;
  padding: 12px 16px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.hist-totals .ht { display: inline-flex; flex-direction: column; gap: 2px; }
.hist-totals .ht-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; font-weight: 700; }
.hist-totals .ht-value { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 16px; }
.hist-totals .ht.savings .ht-value { color: var(--good); }

.table-wrap { overflow-x: auto; border-radius: var(--radius-sm); border: 1px solid var(--card-border); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
/* fixed layout: every data column gets the same width regardless of header
   text length. Without this, "EXPORTADO (KWH)" eats space and "VENDA (€)"
   gets squashed. Scoped to the wide energy/cost tables — the tariff history
   on Tab 5 is a different shape, so it keeps natural sizing. */
#hist-table { table-layout: fixed; }
#hist-table th:first-child, #hist-table td:first-child { width: 110px; }
th, td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--card-border);
  text-align: left;
}
th {
  color: var(--muted); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.5px;
  font-size: 11px;
  background: rgba(255,255,255,0.03);
  position: sticky; top: 0;
}
tbody tr:hover { background: rgba(255,255,255,0.02); }
tbody tr:last-child td { border-bottom: none; }
td.num, th.num { text-align: right; font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }

.foot {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  color: var(--muted);
  font-size: 12px;
  padding: 16px;
}
.foot-conn { display: inline-flex; align-items: center; gap: 6px; }
.sep { opacity: 0.4; margin: 0 6px; }

/* =========================================================
   LOGIN PAGE
   ========================================================= */
.login-body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.login-card {
  width: min(380px, 100%);
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 36px 32px 32px;
  box-shadow: var(--shadow-lg);
  text-align: center;
}
.login-logo {
  width: 56px; height: 56px;
  margin: 0 auto 18px;
  border-radius: 14px;
  background: linear-gradient(135deg, #fbbf24, #f59e0b);
  color: #1c1300;
  font-size: 28px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 10px 28px rgba(251,191,36,0.35);
}
.login-card h1 {
  margin: 0 0 6px;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.1px;
}
.login-sub {
  margin: 0 0 26px;
  color: var(--muted);
  font-size: 13px;
}
.login-card form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.login-card input[type="password"],
.login-card input[type="text"] {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 16px;            /* 16px keeps iOS Safari from auto-zooming */
  font-family: inherit;
  text-align: center;
  outline: none;
  transition: border-color 200ms ease;
}
.login-card input[type="password"]:focus,
.login-card input[type="text"]:focus {
  border-color: var(--accent);
}
/* `:not([hidden])` so the `hidden` HTML attribute actually hides the group.
   Without it, the bare `display: flex` wins over the user-agent's
   `display: none` for `[hidden]`, leaving the TOTP field visible on every
   login page render — even when 2FA isn't configured at all. */
.login-totp:not([hidden]) { display: flex; flex-direction: column; gap: 6px; margin-top: 2px; }
.login-totp input {
  /* Inherit font + size + left alignment from the password input above —
     no overrides needed. Kept as an explicit empty rule for future tweaks. */
}

/* ===== Security / 2FA page (/2fa.html) =====
   Uses the dashboard's regular chrome (topbar + main + footer) instead of the
   login-card style. The card is centred in `main`, sized for comfortable
   reading on desktop but goes full-width on phones (via the existing mobile
   overrides for .card). */
.sec-card {
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
}
/* Same trick as before: `display: flex` (or grid) on author CSS overrides the
   user-agent's `display: none` for `[hidden]`. Constrain the rule with
   `:not([hidden])` so hiding actually works. */
.sec-section:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.sec-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.sec-actions .btn { min-width: 140px; }
.sec-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sec-form input[type="text"] {
  /* Match the login form: Inter font, centred text, no monospace +
     letter-spacing. Keeps both auth surfaces visually consistent. */
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: var(--input-pad-lg);
  font-size: var(--input-fs-lg);
  font-family: inherit;
  text-align: center;
  outline: none;
  transition: border-color 200ms ease;
}
.sec-form input[type="text"]:focus { border-color: var(--accent); }

/* Compact variant — usado na gestão 2FA (regenerar códigos / desativar)
   onde o user já está autenticado e o input + botão cabem na mesma linha.
   Sem o centramento/tamanho da setup-flow (.sec-form acima), que faz
   sentido na 1ª configuração mas é desproporcional num painel de
   gestão. */
.sec-form-compact {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
.sec-form-compact:last-child { margin-bottom: 0; }
.sec-form-compact input[type="text"] {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: var(--control-radius);
  padding: var(--input-pad-sm);
  font-size: var(--input-fs-sm);
  font-family: inherit;
  outline: none;
  transition: border-color 200ms ease;
  text-align: left;
}
.sec-form-compact input[type="text"]:focus { border-color: var(--accent); }
/* Buttons alinhados com o input do lado: mesmo padding vertical,
   font-size, border-radius, line-height → mesma "espessura" visual.
   min-width fixo iguala largura entre rows ("Gerar" vs "Desativar"). */
.sec-form-compact .btn {
  min-width: 110px;
  padding: var(--input-pad-sm);
  font-size: var(--input-fs-sm);
  line-height: 1.2;
  border-radius: var(--control-radius);
  box-sizing: border-box;
}

/* v3: E-REDES + Fatura settings form (lives on 2fa.html).
   2-col grid on desktop, 1-col on mobile. Inputs reuse the .sec-form base
   styles but allow password/number variants and left-align (CPE / cookie
   are long ASCII strings, not 6-digit TOTP codes). */
.eredes-form .form-label {
  font-size: 12px;
  color: var(--muted);
  font-weight: 600;
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.eredes-form .form-label-inline {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  text-transform: none;
  letter-spacing: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}
.eredes-form input[type="text"],
.eredes-form input[type="password"],
.eredes-form input[type="number"] {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: var(--control-radius);
  padding: var(--input-pad-sm);
  font-size: var(--input-fs-sm);
  line-height: 1.2;
  font-family: inherit;
  text-align: left;
  outline: none;
  transition: border-color 200ms ease;
  width: 100%;
  box-sizing: border-box;
}
.eredes-form input:focus { border-color: var(--accent); }
.eredes-form input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--good);
  cursor: pointer;
}
.eredes-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-md);
  /* `.sec-form { gap: 12px }` já dá espaço entre este grid e o próximo
     irmão (.sec-actions); não precisa de margin-bottom extra. */
  margin-bottom: 0;
}
.eredes-grid .form-label:nth-of-type(1),
.eredes-grid .form-label:nth-of-type(2) {
  /* CPE + cookie span the full row each — they are long strings that need
     the full width to be readable. */
  grid-column: 1 / -1;
}
@media (max-width: 720px) {
  .eredes-grid { grid-template-columns: 1fr; gap: 10px; }
  .eredes-grid .form-label:nth-of-type(1),
  .eredes-grid .form-label:nth-of-type(2) { grid-column: auto; }
  .eredes-form input[type="text"],
  .eredes-form input[type="password"],
  .eredes-form input[type="number"] {
    padding: 9px 10px;
    font-size: 13px;
  }
}

/* Backup codes block — shown ONCE after enrolment or regeneration. Two-column
   monospace grid; on mobile collapses to a single column. */
.backup-codes {
  list-style: none;
  padding: 12px 14px;
  margin: 0 0 14px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 18px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  border-radius: 10px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
}
.backup-codes li {
  margin: 0;
  padding: 4px 0;
  letter-spacing: 1px;
  color: var(--text);
  user-select: all;
}
.backup-codes code {
  font: inherit;
  background: transparent;
}
@media (max-width: 560px) {
  .backup-codes { grid-template-columns: 1fr; }
}

/* Enrolment block: two columns on desktop (instructions left, QR right);
   stacks vertically on phones via the existing .grid-2 mobile rule below. */
.sec-enrol {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 20px;
  align-items: start;
}
.sec-enrol-info { min-width: 0; }
@media (max-width: 560px) {
  .sec-enrol { grid-template-columns: 1fr; }
  .qr-wrap { justify-self: center; }
}

.setup-steps {
  margin: 0 0 14px;
  padding-left: 22px;
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
}
.setup-steps li { margin-bottom: 4px; }
.qr-wrap {
  background: #fff;
  border-radius: 12px;
  padding: 12px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.qr-wrap svg { width: 240px; height: 240px; max-width: 100%; display: block; }
.setup-manual { font-size: 13px; }
.setup-manual summary { cursor: pointer; color: var(--accent); padding: 6px 0; }
.manual-secret {
  display: block;
  margin-top: 6px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  letter-spacing: 0.15em;
  word-break: break-all;
  user-select: all;
  background: rgba(255,255,255,0.05);
  border: 1px solid var(--card-border);
  border-radius: 8px;
  padding: 10px;
}
.status-ok {
  margin: 0;
  padding: 12px 14px;
  background: rgba(34,197,94,0.10);
  border: 1px solid rgba(34,197,94,0.30);
  color: var(--good-text);
  border-radius: var(--radius-sm);
  font-size: 14px;
  text-align: center;
}
.status-err {
  margin: 0;
  padding: 12px 14px;
  background: rgba(239,68,68,0.10);
  border: 1px solid rgba(239,68,68,0.30);
  color: var(--bad-text);
  border-radius: var(--radius-sm);
  font-size: 14px;
  text-align: center;
}

/* Sub-headings dentro de cards (e.g. "Novo utilizador", "Nova
   automação", nome do automatismo em edição). Em vez de inline
   style="margin/font-size", usa esta classe para coerência. */
.sec-subhead {
  margin: 0 0 var(--gap-md) 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}
.sec-subhead-tight { margin: 0 0 var(--gap-xs) 0; font-size: 16px; }
.sec-subhead-caption { margin: 0 0 var(--gap-md) 0; }
.btn-danger {
  background: rgba(248,113,113,0.15);
  border: 1px solid rgba(248,113,113,0.40);
  color: var(--bad-text);
}
.btn-danger:hover { background: rgba(248,113,113,0.22); }

.login-submit {
  height: 42px;
  padding: 0 14px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 10px;
}
.login-error {
  margin: 14px 0 0;
  padding: 10px 12px;
  background: rgba(248,113,113,0.10);
  border: 1px solid rgba(248,113,113,0.30);
  color: var(--bad-text);
  border-radius: var(--radius-sm);
  font-size: 13px;
}

/* =========================================================
   TARIFÁRIO CARD
   ========================================================= */
.tariff-card { border-left: 3px solid var(--accent); }

.tariff-current {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 18px;
  align-items: center;
  padding: 8px 0 18px;
  border-bottom: 1px solid var(--card-border);
}
.tariff-current-label {
  font-size: 10px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.8px;
  font-weight: 700;
}
.tariff-current-name {
  font-size: 22px;
  font-weight: 700;
  margin: 4px 0 4px;
  letter-spacing: 0.1px;
}
.tariff-current-since { color: var(--muted); font-size: 13px; }

.tariff-current-prices {
  display: flex;
  gap: 14px;
}
.tprice {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  min-width: 130px;
}
.tprice-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; font-weight: 600; }
.tprice-value { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 16px; margin-top: 4px; }

/* v3: inline potência contratada editor on the Tarifário card */
.tariff-potencia-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  padding: 12px 14px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
}
.tariff-potencia-label {
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 700;
}
.tariff-potencia-label .muted { text-transform: none; letter-spacing: 0; font-weight: 400; margin-top: 2px; }
.tariff-potencia-select {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  font-family: inherit;
  outline: none;
}
.tariff-potencia-select:focus { border-color: var(--accent); }
.btn-sm { padding: 8px 14px; font-size: 13px; }
#tariff-potencia-status { margin-left: auto; }
#sell-price-form-status { margin-left: auto; }

/* Unified sizing across every input/select/button inside a tariff-potencia-row,
   matching the .filters strip used elsewhere (Histórico de preços do kWh): same
   height, padding, font, radius — reads as one coherent control row. */
.tariff-potencia-row input,
.tariff-potencia-row select,
.tariff-potencia-row .btn {
  height: var(--control-h);
  box-sizing: border-box;
  padding: 0 12px;
  font-size: var(--control-fs);
  line-height: 1;
  border-radius: var(--control-radius);
  min-width: 0;
}
.tariff-potencia-row .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
}
/* Discreet widths so each control fits its content without overflowing the row. */
.tariff-potencia-row input[type="date"] { width: 150px; }
.tariff-potencia-row input[type="number"] { width: 110px; }
.tariff-potencia-row input[type="text"] { width: 170px; }
.tariff-potencia-row select { min-width: 140px; }

/* Sub-row that keeps two controls horizontal even when the parent
   .tariff-potencia-row stacks on mobile. Used for "input + Guardar" and
   "valor + etiqueta" pairs that the user wants kept on the same line to
   save vertical space. */
.tariff-inline-pair {
  display: flex;
  gap: 8px;
  align-items: center;
}
.tariff-inline-pair > * { min-width: 0; }
.tariff-inline-pair input { flex: 1 1 auto; }
.tariff-inline-pair .btn { flex: 0 0 auto; }

/* Inverter priority grid — 2 colunas para emparelhar Grid First lado a
   lado e Battery First lado a lado, com a checkbox-row a ocupar a linha
   inteira. Não usa `.eredes-grid` porque essa classe tem regras
   nth-of-type específicas para CPE+cookie que não se aplicam aqui. */
.inv-priority-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin-bottom: 14px;
}
/* Sizing uniforme em todo o priority form: inputs e botões com a
   mesma espessura (8px vertical padding, font-size 13px, line-height
   1.2, border-radius 9px). Idêntico ao .sec-form-compact para
   coerência visual entre 2FA, Identificação e Prioridade. */
#inverter-priority-form input[type="number"],
#inverter-priority-form .btn {
  padding: var(--input-pad-sm);
  font-size: var(--input-fs-sm);
  line-height: 1.2;
  border-radius: var(--control-radius);
  box-sizing: border-box;
}
/* Botão Guardar do priority form tem o mesmo min-width das outras
   compact forms para alinhar as larguras nas duas colunas. */
#inverter-priority-form .btn { min-width: 110px; }
/* O .inv-toggle (checkbox cell) também segue o mesmo padding para
   alinhar com o botão Guardar à direita. */
#inverter-priority-form .inv-toggle { padding: var(--input-pad-sm); }
.inv-priority-grid > .form-label:last-child {
  /* Última row (checkbox "Carregar bateria da rede") ocupa as 2 colunas. */
  grid-column: 1 / -1;
}
@media (max-width: 720px) {
  .inv-priority-grid { grid-template-columns: 1fr; gap: 10px; }
  .inv-priority-grid > .form-label:last-child { grid-column: auto; }
}

/* Inverter priority toggle row — mesma estrutura visual da row de
   cima ([input numerico bordered][btn Guardar]): a "cell" do checkbox
   imita o estilo do input com border+padding, e o Guardar fica à
   direita alinhado com o Guardar das outras rows. */
.inv-toggle {
  /* `flex: 1` faz a cell crescer para empurrar o Guardar até à direita,
     replicando o que `flex: 1` faz nos inputs nas rows acima. */
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 9px 12px;
  background: rgba(255,255,255,0.05);
  border: 1px solid var(--card-border);
  border-radius: 9px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
}
.inv-toggle input[type="checkbox"] {
  /* Override:
     - `.tariff-inline-pair input { flex: 1 1 auto }` faria o checkbox
       crescer e empurrava "Activar" para a direita.
     - `.form-label input { width: 100%; padding/border/background }`
       pintava o checkbox como um input full-width (caixa enorme em vez
       de quadrado pequeno).
     Override explícito repõe o aspecto nativo de checkbox. */
  flex: 0 0 auto;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}

@media (max-width: 720px) {
  .tariff-potencia-row { flex-direction: column; align-items: stretch; gap: 10px; }
  .tariff-potencia-row input,
  .tariff-potencia-row select,
  .tariff-potencia-row .btn { width: 100%; min-width: 0; }
  /* The inline-pair itself stretches across the row, but its children
     share the row equally instead of stacking. */
  .tariff-potencia-row .tariff-inline-pair { width: 100%; }
  .tariff-potencia-row .tariff-inline-pair input,
  .tariff-potencia-row .tariff-inline-pair .btn { width: auto; }
  #tariff-potencia-status,
  #sell-price-form-status { margin-left: 0; text-align: center; }
}

.tariff-history { margin-top: 14px; }
.tariff-history summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  font-weight: 600;
  padding: 4px 0;
  list-style: none;
  user-select: none;
}
.tariff-history summary::-webkit-details-marker { display: none; }
.tariff-history summary::before { content: "▸ "; opacity: 0.6; }
.tariff-history[open] summary::before { content: "▾ "; }
.tariff-history summary:hover { color: var(--text); }

.kind-pill {
  display: inline-block;
  font-size: 10px;
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: 0.5px;
  padding: 3px 8px;
  border-radius: 999px;
}
/* Colour coding: blue = indexed-horário (price varies per slot), yellow =
   indexed-monthly (price varies per cycle but is the same across the cycle),
   green = fixo (price stays the same indefinitely). Keeps the visual hierarchy
   consistent with how volatile each kind actually is. */
.kind-pill.indexed             { background: rgba(96,165,250,0.18);  color: var(--house); }
.kind-pill.indexed_monthly_avg { background: rgba(251,191,36,0.18);  color: var(--pv); }
.kind-pill.indexed_avg_omie    { background: rgba(251,191,36,0.18);  color: var(--pv); }
.kind-pill.fixed_simple        { background: rgba(34,197,94,0.18);   color: var(--good); }
.kind-pill.fixed_bihorario     { background: rgba(34,197,94,0.18);   color: var(--good); }
.kind-pill.catalog_fixo        { background: rgba(34,197,94,0.18);   color: var(--good); }

.btn-danger {
  background: rgba(248,113,113,0.15);
  color: var(--grid);
  border-color: rgba(248,113,113,0.3);
}
.btn-danger:hover { background: rgba(248,113,113,0.25); }
.btn-current-active {
  background: rgba(34,197,94,0.15); color: var(--good);
  border-color: rgba(34,197,94,0.3); cursor: default;
}

/* =========================================================
   MODAL (HTML <dialog>)
   ========================================================= */
.modal {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  background: var(--bg-1);
  color: var(--text);
  width: min(640px, 92vw);
  max-height: 92vh;
  box-shadow: 0 30px 80px rgba(0,0,0,0.6), 0 0 0 1px var(--card-border);
}
.modal::backdrop {
  background: rgba(0,0,0,0.62);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.modal[open] {
  animation: modal-in 200ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes modal-in {
  from { opacity: 0; transform: translateY(-12px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.modal form { margin: 0; }

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--card-border);
}
.modal-head h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}
.modal-close {
  background: transparent;
  border: 0;
  color: var(--muted);
  font-size: 22px;
  cursor: pointer;
  width: 32px; height: 32px;
  border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
}
.modal-close:hover { background: rgba(255,255,255,0.06); color: var(--text); }

.modal-body {
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: 70vh;
  overflow-y: auto;
}

.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--card-border);
  background: rgba(0,0,0,0.18);
}

.form-group { display: flex; flex-direction: column; gap: 8px; }
.form-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 11px;
  color: var(--muted);
  /* Flex children default to `min-width: auto` which equals their intrinsic
     content width — for a <select> that's the longest option. Reset to 0 so
     the child select honours its parent's width instead of pushing past it. */
  min-width: 0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}
.form-label input, .form-label select {
  background: rgba(255,255,255,0.05);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: 9px;
  padding: 9px 12px;
  font-size: 14px;
  font-family: inherit;
  outline: none;
  transition: border-color 200ms ease;
  /* `select` natively auto-sizes to its longest <option>. Catalog tariff names
     like "EDP | Eletricidade · Digital · Campanha -20% | DD + FE" easily blow
     past the modal width — force the element to honour its container and clip
     overflowing option text instead. */
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  text-overflow: ellipsis;
}
.form-label input:focus, .form-label select:focus { border-color: var(--accent); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-hint { color: var(--muted); font-size: 12px; margin: 4px 0 0; line-height: 1.5; }
.form-hint-inline { color: var(--muted); font-size: 11px; margin-top: -4px; }
.form-divider { border: 0; border-top: 1px solid var(--card-border); margin: 4px 0; }
.form-error {
  background: rgba(248,113,113,0.15);
  color: var(--bad-text);
  border: 1px solid rgba(248,113,113,0.3);
  padding: 10px 14px;
  border-radius: var(--control-radius);
  font-size: 13px;
}
/* Amber banner used in the tariff modal to surface new monthly-indexed
   tariffs detected in Tiago's XLSX that don't yet have a Python formula. */
.form-banner {
  background: rgba(251,191,36,0.12);
  color: var(--warn-text);
  border: 1px solid rgba(251,191,36,0.3);
  padding: 10px 14px;
  border-radius: var(--control-radius);
  font-size: 13px;
  margin-bottom: 14px;
}
.form-banner code {
  background: rgba(255,255,255,0.08);
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 12px;
}

.kind-options {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
/* When 5+ options, the last one wraps onto its own row — make it span both columns */
.kind-options .kind-option:nth-child(5):last-child {
  grid-column: 1 / -1;
}
.kind-option { cursor: pointer; }
.kind-option input { position: absolute; opacity: 0; pointer-events: none; }
.kind-card {
  border: 1.5px solid var(--card-border);
  border-radius: 10px;
  padding: 10px;
  transition: all 150ms ease;
  background: rgba(255,255,255,0.02);
}
.kind-option:hover .kind-card { border-color: var(--card-border-hover); }
.kind-option input:checked + .kind-card {
  border-color: var(--accent);
  background: rgba(96,165,250,0.10);
}
.kind-title { font-size: 13px; font-weight: 700; margin-bottom: 2px; }
.kind-desc  { font-size: 11px; color: var(--muted); line-height: 1.4; }

.when-options {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: 9px;
  padding: 10px 14px;
}
.when-option {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  cursor: pointer;
  padding: 4px 0;
}
.when-option strong { color: var(--text); }
.when-option input[type="date"] {
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: 7px;
  padding: 5px 8px;
  font-size: 12px;
  margin-left: 6px;
}

@media (max-width: 640px) {
  .tariff-current { grid-template-columns: 1fr; }
  .tariff-current-prices { flex-wrap: wrap; }
  .kind-options { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
}

/* =========================================================
   PRICE HISTORY (lookup)
   ========================================================= */
.price-history-card { border-left: 3px solid var(--pv); }

.ph-stats {
  display: grid;
  /* auto-fit + minmax adapts to 5 cards (simples / horário) or 6 cards
     (bi-horário) without per-card CSS — tiles always fill the row. */
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
.ph-stat {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
/* The mobile media query below sets `.ph-stat { display: flex }`, which has
   higher specificity than the user-agent `[hidden] { display: none }` and
   would otherwise leak hidden tiles back into the layout. Force display:none
   when the `hidden` attribute is present, in either viewport. */
.ph-stat[hidden] { display: none; }
.ph-stat-label {
  font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 700;
}
.ph-stat-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 16px; font-weight: 700; margin-top: 4px;
}

@media (max-width: 900px) {
  /* From tablet down: 2-col grid with label STACKED ABOVE value so the
     12-char prices ("0.1379 €/kWh") fit comfortably without any text
     truncation. Explicit `display: block` overrides any inherited flex
     layout from older deploys / cached CSS. */
  .ph-stats { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
  .ph-stat {
    display: block;
    padding: 8px 10px;
    min-width: 0;
    overflow: hidden;
  }
  .ph-stat-label {
    font-size: 9.5px;
    margin-bottom: 3px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ph-stat-value {
    font-size: 13px;
    margin-top: 0;
    white-space: nowrap;
  }
  /* IVA tile spans both columns so it sits alone on the last row, matching
     the "pares + IVA sozinho" layout the user asked for. The single-band
     Médio tile gets the same treatment so it doesn't leave an orphan slot
     when bi-horário's two-band cards are hidden. */
  .ph-stat-iva,
  #ph-stat-avg-wrap:not([hidden]) {
    grid-column: 1 / -1;
  }
}
@media (max-width: 380px) {
  /* Ultra-narrow phones: shrink fonts slightly so even the longest label
     ("M. F. vazio") fits without ellipsis. */
  .ph-stat { padding: 6px 8px; }
  .ph-stat-label { font-size: 9px; }
  .ph-stat-value { font-size: 11.5px; }
}

/* =========================================================
   FATURA TAB (v3)
   ========================================================= */

/* Banner that appears when the integration needs user attention
   (CPE missing, cookie expired). Sits at the top of the tab. */
.fatura-banner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 14px;
  border-left: 3px solid var(--grid);
  background: rgba(248,113,113,0.06);
}
/* `display: grid` above wins over the user-agent `[hidden] { display: none }`
   by specificity. Without this rule the JS's `banner.hidden = true` has no
   visible effect — same bug pattern as .ph-stat[hidden] further up. */
.fatura-banner[hidden] { display: none; }
.fatura-banner-icon {
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--grid);
  color: #fff; font-weight: 800; font-size: 18px;
}
.fatura-banner-title {
  font-weight: 700; color: var(--text); font-size: 14px;
}

/* HERO: big invoice estimate + projection + IVA progress */
.fatura-hero {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 20px;
  align-items: stretch;
  border-left: 3px solid var(--pv);
}
.fatura-hero-label {
  font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 700;
  margin-bottom: 8px;
}

/* Cycle navigation row: ‹ label › | [Hoje] — keeps the label centred between
   the two arrows so the user reads "where am I" naturally. */
.fatura-hero-cycle-nav {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}
.fatura-nav-btn {
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: 8px;
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 18px; line-height: 1; font-weight: 700;
  cursor: pointer;
  transition: background 150ms ease, opacity 150ms ease;
}
.fatura-nav-btn:hover:not(:disabled) { background: rgba(255,255,255,0.08); }
.fatura-nav-btn:disabled { opacity: 0.3; cursor: not-allowed; }
.fatura-nav-now,
.fatura-nav-mode {
  width: auto; padding: 0 12px; font-size: 11px; font-weight: 600;
  letter-spacing: 0.4px; text-transform: uppercase;
  margin-left: auto;
}
/* The mode toggle gets a subtle accent tint so it stands out as the
   "view-swap" affordance vs the plain ‹ › cycle-nav arrows. */
.fatura-nav-mode {
  background: rgba(96,165,250,0.10);
  border-color: rgba(96,165,250,0.30);
  color: var(--accent);
}
.fatura-nav-mode:hover:not(:disabled) { background: rgba(96,165,250,0.18); }
.fatura-hero-cycle-nav .fatura-hero-label { margin-bottom: 0; }

/* Per-day row states: future days and past days with no data are muted. */
.fatura-day-future td,
.fatura-day-empty td { color: var(--muted); }
.fatura-day-future td.num,
.fatura-day-empty td.num { font-variant-numeric: tabular-nums; }
.fatura-hero-total {
  font-family: 'JetBrains Mono', monospace;
  font-size: 44px; font-weight: 800; color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: 1.0;
}
.fatura-hero-sub { margin-top: 8px; }
.fatura-hero-side {
  display: flex; flex-direction: column; gap: 14px;
  padding-left: 18px;
  border-left: 1px dashed var(--card-border);
}
.fatura-hero-projection-label { margin-bottom: 4px; }
.fatura-hero-projection-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 24px; font-weight: 700; color: var(--text);
}
.fatura-hero-iva-label { margin-bottom: 4px; }
.fatura-hero-iva-bar {
  height: 8px; border-radius: 999px; overflow: hidden;
  background: rgba(255,255,255,0.06);
}
.fatura-hero-iva-bar-fill {
  height: 100%; width: 0%;
  background: var(--good);
  transition: width 400ms ease, background 200ms ease;
}
.fatura-hero-iva-bar-fill.filled { background: var(--warn); }
.fatura-hero-iva-text { margin-top: 4px; }

/* Third column: cumulative year-to-date energy total. Mirrors the
   projection card's visual weight so the eye reads three balanced
   columns (current cycle | projection | year). */
.fatura-hero-year {
  display: flex; flex-direction: column; justify-content: center;
  padding-left: 18px;
  border-left: 1px dashed var(--card-border);
}
.fatura-hero-year-label { margin-bottom: 4px; }
.fatura-hero-year-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 24px; font-weight: 700; color: var(--text);
}
.fatura-hero-year-sub { margin-top: 4px; }

/* Per-operator invoice cards (rendered when a cycle spans multiple
   suppliers, e.g. user switched from Iberdrola to EDP mid-month). Each
   card looks like the single-supplier decomposição but is contained in
   its own section with the operator name at the top. */
.fatura-operator-card {
  border-left: 3px solid rgba(96,165,250,0.4);
  margin-top: 16px;
}
.fatura-operator-card .card-head h2 .fatura-operator-name {
  color: var(--text);
}
.fatura-operator-card .card-head .muted.small {
  margin-left: auto;
}

/* Per-band KPI strip — 4 tiles on desktop, 2x2 on mobile */
.grid-fatura-kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
/* Venda mode shows only 2-3 cards (no per-band breakdown for sell-side).
   Collapse the grid so they don't read as orphaned cells in a 4-col strip. */
.grid-fatura-kpis.grid-venda {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.fatura-kpi {
  padding: 14px 16px;
  display: flex; flex-direction: column; gap: 4px;
}
.fatura-kpi-label {
  font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 700;
}
.fatura-kpi-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 22px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.fatura-kpi-band[data-band="vazio"] { border-left: 3px solid var(--house); }
.fatura-kpi-band[data-band="fora"]  { border-left: 3px solid var(--pv); }

/* Breakdown sections (Energia / Potência / Taxas) */
.fatura-section { margin-top: 18px; }
.fatura-section:first-of-type { margin-top: 6px; }

/* Loading state — applied on the panel while the 3 Fatura API calls are in
   flight. Pulses the headline numbers so the user knows fresh data is
   being fetched (visible mostly after switching back from Definições). */
[data-tab-panel="fatura"].is-loading .fatura-hero-total,
[data-tab-panel="fatura"].is-loading .fatura-hero-projection-value,
[data-tab-panel="fatura"].is-loading .fatura-kpi-value {
  opacity: 0.5;
  animation: fatura-pulse 1.4s ease-in-out infinite;
}
@keyframes fatura-pulse {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 0.75; }
}

/* Inline editor for the per-invoice extra discount. Sits at the bottom of
   the Descontos section. Uses the same control sizing as every other
   control row in the app (--control-* tokens). */
.fatura-discount-editor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding: 10px 12px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
}
.fatura-discount-editor label {
  font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.5px; font-weight: 700;
}
.fatura-discount-editor input,
.fatura-discount-editor .btn {
  height: var(--control-h);
  box-sizing: border-box;
  padding: 0 12px;
  font-size: var(--control-fs);
  line-height: 1;
  border-radius: var(--control-radius);
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--card-border);
  color: var(--text);
  font-family: inherit;
  outline: none;
}
.fatura-discount-editor input:focus { border-color: var(--accent); }
.fatura-discount-editor input[type="number"] { width: 100px; }
.fatura-discount-editor input[type="text"] { width: 200px; }
.fatura-discount-editor .btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600;
}
/* .btn-danger normally wins over plain .btn, but the editor's `.btn` rule
   above has 2-class specificity and would otherwise override the danger
   colours back to the neutral white-on-dark. Restate at matching
   specificity so the Remover button reads as destructive. */
.fatura-discount-editor .btn.btn-danger {
  background: rgba(248,113,113,0.15);
  border-color: rgba(248,113,113,0.40);
  color: var(--bad-text);
}
.fatura-discount-editor .btn.btn-danger:hover { background: rgba(248,113,113,0.22); }
/* Amount + € grouped as a single flex item so the € symbol stays glued to
   the input on mobile (where the outer editor stacks each item as a row,
   the € would otherwise jump to its own full-width line). */
.fatura-discount-amount {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
#fatura-discount-status { margin-left: auto; }
/* The list takes the full editor width — each row inside it is a flex line
   with [note, amount, save, remove] so each rubric is editable in place. */
.fatura-discount-list {
  flex: 1 1 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fatura-discount-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.fatura-discount-row.is-dirty .btn-save { border-color: var(--accent); color: var(--accent); }

@media (max-width: 720px) {
  /* Tight 2-row layout on phones — same idea as the single-row v1 editor but
     applied per rubric:
       1) [number input + €]   [Guardar]
       2) [nota (opcional) full-width]
       3) [Remover full-width centred] */
  .fatura-discount-editor { gap: 8px; }
  .fatura-discount-editor > label { flex: 1 1 100%; }
  .fatura-discount-row { gap: 6px; }
  .fatura-discount-amount { flex: 1 1 auto; }
  .fatura-discount-amount input[type="number"] { flex: 1 1 auto; min-width: 0; width: auto; }
  .fatura-discount-row input[type="text"] { flex: 1 1 100%; width: 100%; }
  .fatura-discount-row .btn { flex: 0 0 auto; }
  #fatura-discount-status { margin-left: 0; flex: 1 1 100%; text-align: center; }
}
.fatura-section-title {
  font-size: 13px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.6px; font-weight: 700;
  margin: 0 0 8px;
  padding: 6px 12px;
  background: rgba(255,255,255,0.04);
  border-radius: var(--radius-sm);
}
.fatura-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
}
.fatura-table thead th {
  text-align: left;
  font-size: 10.5px;
  color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.4px; font-weight: 700;
  padding: 8px 10px;
  border-bottom: 1px solid var(--card-border);
}
.fatura-table th.num, .fatura-table td.num { text-align: right; }
.fatura-table tbody td {
  padding: 8px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.fatura-table tbody td.strong {
  font-weight: 700;
  font-family: 'JetBrains Mono', monospace;
}
.fatura-table .unit { color: var(--muted); font-size: 11px; }

/* Totals block under the three tables */
.fatura-totals {
  margin-top: 18px;
  display: flex; flex-direction: column; gap: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--card-border);
}
.fatura-totals-row {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.fatura-totals-row > span:first-child { color: var(--muted); }
.fatura-totals-row > span:last-child {
  font-family: 'JetBrains Mono', monospace; font-weight: 600; color: var(--text);
}
.fatura-totals-total {
  margin-top: 8px;
  padding-top: 12px;
  border-top: 1px dashed var(--card-border);
  font-size: 17px;
}
.fatura-totals-total > span:first-child {
  color: var(--text); font-weight: 700; letter-spacing: 0.4px;
}
.fatura-totals-total > span:last-child {
  font-size: 22px; font-weight: 800; color: var(--good);
}

/* Per-day list */
.fatura-days-table {
  width: 100%; border-collapse: collapse;
  font-size: 13px; font-variant-numeric: tabular-nums;
}
.fatura-days-table thead th {
  text-align: left;
  font-size: 10.5px;
  color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.4px; font-weight: 700;
  padding: 8px 10px;
  border-bottom: 1px solid var(--card-border);
}
.fatura-days-table th.num, .fatura-days-table td.num { text-align: right; }
.fatura-days-table tbody td {
  padding: 7px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.fatura-days-table .strong {
  font-weight: 700; font-family: 'JetBrains Mono', monospace;
}
.fatura-days-table tfoot td {
  padding: 8px 10px;
  border-top: 1px solid rgba(255,255,255,0.10);
  font-weight: 600;
  background: rgba(255,255,255,0.02);
}
.fatura-comparison-median td {
  font-family: 'JetBrains Mono', monospace;
}

/* Toggle in the Comparison card head */
.fatura-toggle {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer; user-select: none;
  font-size: 12px; color: var(--muted);
}
.fatura-toggle input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 36px; height: 20px; border-radius: 999px;
  background: rgba(255,255,255,0.08);
  position: relative; cursor: pointer; transition: background 200ms ease;
}
.fatura-toggle input[type="checkbox"]::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; transition: transform 200ms ease;
}
.fatura-toggle input[type="checkbox"]:checked { background: var(--good); }
.fatura-toggle input[type="checkbox"]:checked::after { transform: translateX(16px); }
.delta-pos { color: var(--good); font-weight: 600; }
.delta-neg { color: var(--grid); font-weight: 600; }


/* =========================================================
   RESPONSIVE
   ========================================================= */
@media (max-width: 1100px) {
  .grid-live { grid-template-columns: 1fr; }
  .grid-kpis { grid-template-columns: repeat(2, 1fr); }
  .grid-energy-kpis { grid-template-columns: repeat(3, 1fr); }
  .grid-2 { grid-template-columns: 1fr; }
  .period-grid { grid-template-columns: repeat(2, 1fr); }
  .mix-wrap { grid-template-columns: 1fr; }
  .mix-legend { flex-direction: row; flex-wrap: wrap; }
  /* Fatura: hero pilha numa coluna em laptop pequeno */
  .fatura-hero { grid-template-columns: 1fr; }
  .fatura-hero-side {
    padding-left: 0; padding-top: 14px;
    border-left: none; border-top: 1px dashed var(--card-border);
    flex-direction: row; flex-wrap: wrap; gap: 18px;
  }
  .fatura-hero-side > div { flex: 1 1 200px; min-width: 0; }
  /* Year card stacks below side panel on narrow screens, with a top
     divider mirroring the projection card's transition. */
  .fatura-hero-year {
    padding-left: 0; padding-top: 14px;
    border-left: none; border-top: 1px dashed var(--card-border);
  }
  .grid-fatura-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  main { padding: 14px; }
  .grid-kpis, .grid-energy-kpis { grid-template-columns: 1fr 1fr; }
  .splits { grid-template-columns: 1fr 1fr; }
  .period-grid { grid-template-columns: 1fr; }
  .hero-value { font-size: 36px; }
  .rate-value { font-size: 22px; }

  /* Mobile topbar: brand on the left, two icon-only buttons on the right.
     Labels collapse to icon-only via `.topbar-btn-label { display: none }`
     so the row stays single-line at 375 px. */
  .topbar {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 14px;
  }
  .brand { gap: 10px; }
  .brand h1 { font-size: 15px; }
  .brand .logo { width: 28px; height: 28px; font-size: 16px; border-radius: 8px; }

  .topbar-actions { gap: 6px; }
  .topbar-btn {
    padding: 8px 10px;
    font-size: 13px;
  }
  .topbar-btn-label { display: none; }
  .topbar-btn-icon { font-size: 16px; }
}


/* =========================================================
   v2: TAB NAVIGATION
   ========================================================= */
.tabs {
  display: flex;
  gap: 10px;
  padding: 8px 24px 0;
  background: #0d1118;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab-btn {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  padding: 10px 18px;
  border-radius: 10px 10px 0 0;
  cursor: pointer;
  position: relative;
  white-space: nowrap;
  transition: color .15s, background .15s;
}
.tab-btn:hover { color: var(--text); background: rgba(255,255,255,0.03); }
.tab-btn.active {
  color: var(--text);
  background: linear-gradient(180deg, rgba(96,165,250,0.10), rgba(96,165,250,0));
}
.tab-btn.active::after {
  content: "";
  position: absolute;
  left: 12px; right: 12px;
  bottom: -1px;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
}

.tab-panel { display: none; }
/* The active panel is itself a grid container so every direct <section> child
   gets consistent vertical spacing — otherwise cards stack flush against the
   chart cards below them and the dashboard feels cramped. */
.tab-panel.active { display: grid; gap: var(--gap-xl); }

@media (max-width: 720px) {
  .tabs { top: auto; padding: 6px 12px 0; gap: 6px; }
  .tab-btn { padding: 8px 14px; font-size: 13px; }
}


/* =========================================================
   v5: Role gate — admin-only elements
   ---------------------------------------------------------
   Default state: HIDDEN. The role-gate IIFE in app.js / 2fa.js
   probes /api/auth/check on page load and adds `.role-admin` to
   <body> if the user is admin — that's the only path that reveals
   admin-only controls. Without this CSS, viewers would see the
   buttons FLASH visible for ~50-200ms while JS catches up and
   hides them (FOUC).

   IMPORTANT — only ONE rule:
   When body does NOT have .role-admin, hide [data-admin-only].
   We do NOT add a `body.role-admin [data-admin-only] { display: ... }`
   counter-rule because:
     - For admin, simply letting the cascade run normally is correct
       (buttons → inline-block, divs → block, tab-panels → their own
       `.tab-panel { display: none }` rule which only shows the
       `.active` panel)
     - An override using `display: revert` would also revert the
       `.tab-panel { display: none }` causing admin to see ALL the
       admin-only tab-panels simultaneously (bug — actually hit on
       first prod deploy and reported by the operator).

   Failure mode: if /api/auth/check fails (network), body never gets
   .role-admin, admin-only stays hidden. Conservative — better than
   showing a viewer admin controls under network ambiguity. */
body:not(.role-admin) [data-admin-only] {
  display: none !important;
}


/* =========================================================
   v5: Responsive tables (Users / Inverter schedule / Automatismos)
   ---------------------------------------------------------
   No desktop são <table>s normais com 5-6 colunas. No mobile (≤720px)
   convertem-se a "card-per-row" — cada <tr> vira um cartão bordered,
   cada <td> empilha vertical com a label da coluna como caption acima.
   Cells com data-label="X" renderizam X como caption; a coluna final
   (acções) fica horizontal flex-wrap para os botões.

   Mantém o estilo existente do projecto: edge-to-edge cards, sem
   horizontal scroll (memória feedback_no_unrequested_scroll).
   ========================================================= */

/* Inputs/selects dentro das tabelas v5 — força font-size consistente
   com o resto da página. Selects nativos em macOS herdam font-size
   "system default" (~16-18px) ignorando o inline `font-size:0.9rem`
   da tabela, daí aparecerem maiores que os outros controlos. */
.users-table input,
.users-table select,
.automations-table input,
.automations-table select,
.inverter-schedule-table input,
.inverter-schedule-table select,
.wallboxes-table input,
.wallboxes-table select {
  font-size: 13px;
  font-family: inherit;
}

@media (max-width: 720px) {
  .users-table,
  .users-table tbody,
  .users-table thead,
  .users-table tr,
  .users-table td,
  .users-table th,
  .automations-table,
  .automations-table tbody,
  .automations-table thead,
  .automations-table tr,
  .automations-table td,
  .automations-table th,
  .inverter-schedule-table,
  .inverter-schedule-table tbody,
  .inverter-schedule-table thead,
  .inverter-schedule-table tr,
  .inverter-schedule-table td,
  .inverter-schedule-table th,
  .wallboxes-table,
  .wallboxes-table tbody,
  .wallboxes-table thead,
  .wallboxes-table tr,
  .wallboxes-table td,
  .wallboxes-table th {
    display: block;
    width: 100%;
    text-align: left !important;
  }
  .users-table thead,
  .automations-table thead,
  .inverter-schedule-table thead,
  .wallboxes-table thead {
    /* O <thead> tem os labels da coluna em desktop; em mobile usamos
       data-label nas cells, por isso o thead torna-se redundante. */
    display: none;
  }
  .users-table tr,
  .automations-table tr,
  .inverter-schedule-table tr,
  .wallboxes-table tr {
    border: 1px solid var(--card-border);
    border-radius: 8px;
    margin-bottom: 10px;
    padding: 10px;
    background: rgba(255, 255, 255, 0.02);
  }
  .users-table td,
  .automations-table td,
  .inverter-schedule-table td,
  .wallboxes-table td {
    padding: 4px 0 !important;
    border: 0 !important;
  }
  /* data-label="X" caption para cada td. Pular para `td:last-child`
     porque essa cell normalmente tem botões de acção sem caption útil. */
  .users-table td[data-label]::before,
  .automations-table td[data-label]::before,
  .inverter-schedule-table td[data-label]::before,
  .wallboxes-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
    letter-spacing: 0.04em;
    margin-bottom: 2px;
  }
  /* Última coluna (acções) — flex-wrap horizontal mesmo em mobile,
     buttons espaçados. */
  .users-table td:last-child,
  .automations-table td:last-child,
  .inverter-schedule-table td:last-child,
  .wallboxes-table td:last-child {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
    justify-content: flex-start !important;
  }
  /* Inputs/selects dentro das cells ficam full-width para serem
     toucháveis confortavelmente. */
  .users-table td input[type="text"],
  .users-table td input[type="password"],
  .users-table td input[type="number"],
  .users-table td select,
  .automations-table td input[type="text"],
  .automations-table td input[type="number"],
  .automations-table td select,
  .inverter-schedule-table td input[type="time"],
  .inverter-schedule-table td input[type="number"],
  .inverter-schedule-table td select,
  .wallboxes-table td input[type="text"],
  .wallboxes-table td select {
    width: 100%;
    box-sizing: border-box;
  }
  /* Step rows do editor de automatismos — já têm flex-wrap mas
     queremos botão remover de volta à esquerda em mobile (não auto). */
  .automation-step-row {
    gap: 6px !important;
  }
  .automation-step-row > .btn-danger {
    margin-left: 0 !important;
  }
  /* Mandatory-2FA banner — reduzir margins horizontais para edge-to-edge */
  .mandatory-2fa-banner {
    margin-left: 12px !important;
    margin-right: 12px !important;
  }
}

/* Fine-tuning para viewports pequenos (phones em portrait). Aperta
   font/padding das tabelas v5 — exclui .inverter-schedule-table que
   está congelada por preferência do user (memory). */
@media (max-width: 480px) {
  .users-table,
  .automations-table,
  .wallboxes-table {
    font-size: 12px;
  }
  .users-table td,
  .automations-table td,
  .wallboxes-table td {
    padding: 3px 0 !important;
  }
  .users-table tr,
  .automations-table tr,
  .wallboxes-table tr {
    padding: 8px 10px;
  }
  /* Botões de acção (Editar/Apagar) mais confortáveis para touch. */
  .users-table .btn-sm,
  .automations-table .btn-sm,
  .wallboxes-table .btn-sm,
  .users-table td:last-child .btn,
  .automations-table td:last-child .btn,
  .wallboxes-table td:last-child .btn {
    min-height: 34px;
    padding: 6px 12px;
  }
}


/* =========================================================
   v2: SYSTEM DETAIL CARDS (Inverter+Grid+Load / PV / Battery / Energy today)
   ========================================================= */
.grid-system-cards {
  display: grid;
  /* 4 cards on wide screens, gracefully wraps to 2 → 1 on narrow ones */
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px;
  /* Spacing between this section and its neighbours comes from .tab-panel's
     own gap — don't add margin here or it doubles up. */
}
.sys-card {
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
}
.sys-card .card-head {
  margin-bottom: 14px;
}
.sys-card h2 {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--muted);
}
.sys-card-pv h2     { color: var(--pv); }
.sys-card-batt h2   { color: var(--batt); }
.sys-card-energy h2 { color: var(--house); }

.sys-rows {
  display: flex;
  flex-direction: column;
  /* Tight vertical rhythm so the Solar PV card stays the same height with
     the extra MPPT peak rows. Applied to all 4 system cards for coherence. */
  gap: 8px;
  flex: 1;
}
.sys-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: 14px;
}
.sys-row-indent { padding-left: 18px; opacity: 0.85; }
.sys-label { color: var(--muted); }
.sys-val {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.sys-mode {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: 999px;
  background: rgba(96,165,250,0.15);
  color: #93c5fd;
  font-size: 12.5px;
  font-weight: 600;
}
.sys-sep {
  border: 0;
  border-top: 1px dashed rgba(255,255,255,0.10);
  margin: 6px 0 2px;
}
/* Sub-section header inside the merged "Inversor" card (Rede, Consumo) */
.sys-subhead {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed rgba(255,255,255,0.08);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--muted-2);
}


/* =========================================================
   v2: TOTALS TABLES (60d daily + 12m monthly)
   ========================================================= */
.totals-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  /* table-layout: fixed gives all data columns equal width regardless of
     header text length, so "EXPORTADO (KWH)" doesn't bloat its column. */
  table-layout: fixed;
}
.totals-table th:first-child, .totals-table td:first-child {
  width: 110px;
}
.totals-table thead th {
  position: sticky;
  top: 0;
  background: var(--bg-2);
  z-index: 1;
}
.totals-table th, .totals-table td {
  padding: 8px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  white-space: nowrap;
}
.totals-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 600;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.totals-table th.num, .totals-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.totals-table tbody tr:hover { background: rgba(255,255,255,0.025); }
.totals-table tr.row-total {
  background: rgba(96,165,250,0.05);
  font-weight: 700;
}
.totals-table td.day-cell { font-weight: 600; }
.totals-table td.weekday {
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 500;
}
/* Savings colour: green for poupança (positive), red for custo (negative),
   neutral white for ~zero. Applied to any <td> regardless of which table —
   the hist-table on the Contas tab uses these too. */
td.savings-pos { color: var(--good); }
td.savings-neg { color: var(--bad); }

/* Daily/monthly date label variants. Long form (e.g. "2026-05-13 qua") is the
   desktop default; short form ("13/05 qua" / "Mai 26") shows on phones via
   the mobile @media block, where every horizontal pixel matters. */
.lbl-short { display: none; }
/* Same toggle for the totals-table column headers: long ("Bat. descarga
   (kWh)") on desktop, short ("Bat. −") on mobile. */
.totals-table thead th .hd-short { display: none; }

/* Pager arrows for the totals tables (60d / 12m) */
.card-head-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.pager {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.btn-icon {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 1;
  font-weight: 600;
}
.btn-icon:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}


/* =========================================================
   v2: CHARTS TAB
   ========================================================= */
/* Date-range filter stays in its natural position at the top of the
   Gráficos tab and scrolls away with the content — no sticky/fixed
   behaviour. Same on desktop and mobile. */
.chart-h420 {
  height: 420px;
  position: relative;
}
@media (max-width: 720px) {
  .chart-h420 { height: 320px; }
}


/* =========================================================
   MOBILE (≤ 720px) — phones, narrow tablets in portrait
   =========================================================
   Everything below is purely overrides for handheld layouts so we don't
   touch desktop styles above. iOS-specific behaviours (safe-area insets,
   tap targets, prevent-zoom on inputs) are baked in here too. */

/* Safe-area insets so iPhone notch / home indicator don't overlap content
   when the dashboard is installed as a PWA. */
@supports (padding: env(safe-area-inset-top)) {
  .topbar {
    padding-top: max(14px, env(safe-area-inset-top));
    padding-left: max(24px, env(safe-area-inset-left));
    padding-right: max(24px, env(safe-area-inset-right));
  }
  main {
    padding-bottom: max(22px, calc(env(safe-area-inset-bottom) + 12px));
    padding-left: max(22px, env(safe-area-inset-left));
    padding-right: max(22px, env(safe-area-inset-right));
  }
  .foot { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
}

@media (max-width: 720px) {
  /* ----- Base spacing: less generous margins, edge-to-edge cards ----- */
  main { padding: 12px; gap: 14px; }
  .grid { gap: 12px; }
  .tab-panel.active { gap: 14px; }
  .card { padding: 14px 14px; border-radius: 14px; }

  /* ----- Prevent horizontal page overflow on Totais / Custos -----
     Grid items default to min-width: auto, which lets them grow PAST the
     grid track when their content is wider than the track (e.g. the 7-col
     totals tables or the 10-col histórico). The card was expanding to fit
     the table, and since the card grew, .table-wrap's own overflow:auto
     never kicked in — the whole page ended up wider than the viewport
     (Safari then auto-zoomed-out to fit, which also visually compressed
     the topbar/tabs).
     Forcing min-width: 0 lets the card stay at track width; the table-wrap
     inside then provides the horizontal scroll, as intended. */
  /* No `overflow-x` rule on html/body — both `hidden` and `clip` have been
     observed to break `position: sticky` on the chart-controls-card on iOS
     Safari (the former defeats it everywhere, the latter is iOS 16-only and
     ships with quirks). Horizontal containment is enforced on the
     tab-panel children and cards below via `min-width: 0; max-width: 100%`,
     plus per-table `.table-wrap { overflow-x: auto }`. */
  .tab-panel.active > * { min-width: 0; max-width: 100%; }
  .card { min-width: 0; max-width: 100%; }
  .table-wrap { max-width: 100%; }

  /* Sticky top elements also need to be capped at the viewport width so they
     don't pick up the same auto-zoom-out the page used to suffer from. */
  .topbar, .tabs { max-width: 100vw; box-sizing: border-box; }

  /* ----- TOP BAR / HEADER -----
     Single tidy row on phones: brand on the left, two icon-only action
     buttons (Definições + Terminar sessão) on the right. Labels are
     collapsed to icons via .topbar-btn-label{display:none} in the
     `<= 720 px` overrides below. */
  .topbar {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 14px;
  }
  .brand h1 { font-size: 16px; }
  .brand .logo { width: 30px; height: 30px; font-size: 18px; }

  /* ----- TABS -----
     Sete separadores (Dashboard, Gráficos, Totais, Custos, Tarifário, Fatura,
     Carro) — largura natural por aba (cada uma cresce até caber a palavra
     completa) com scroll horizontal quando a linha excede o viewport. Sem
     `flex: 1` para evitar que palavras compridas como "Dashboard" transbordem
     do contorno da aba. */
  .tabs {
    top: auto;
    padding: 4px 8px 0;
    gap: 4px;
    overflow-x: auto;
    scrollbar-width: none;
    flex-wrap: nowrap;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab-btn {
    flex: 0 0 auto;
    padding: 10px 12px;
    font-size: 12.5px;
    min-height: 44px;
    border-radius: 10px 10px 0 0;
    text-align: center;
    letter-spacing: -0.2px;
    white-space: nowrap;
  }

  /* ----- FLOW + HEROES -----
     Stack flow card on top, heroes underneath. Each hero is full-width
     so the spark + label + value have room to breathe. */
  .grid-live { grid-template-columns: 1fr; gap: 12px; }
  /* Flow card on mobile: bleed edge-to-edge of the viewport so the diagram
     is the visual centerpiece. The card retains its outer chrome (head + splits
     with inner padding), but the SVG itself spans the full screen width. */
  .card-flow { padding: 12px 0 10px; overflow: hidden; }
  .card-flow .card-head { padding: 0 14px; }
  .card-flow .splits { padding: 0 12px; }
  /* Bleed almost edge-to-edge but leave a small inset (~3 % each side) so the
     diagram doesn't visually fight the screen bezel. `margin: calc(50% - 47vw)`
     walks the wrapper out to ~3 % from the viewport edge regardless of the
     parent's padding. */
  .flow-wrap {
    width: 94vw;
    margin-left: calc(50% - 47vw);
    margin-right: calc(50% - 47vw);
  }
  .flow-svg { width: 100%; height: auto; display: block; }
  /* Boost the in-SVG text on mobile so the kW values are readable even when
     the diagram is full-bleed (and the viewBox was tightened to remove the
     side dead space). */
  .flow-svg .node-value { font-size: 20px; }
  .flow-svg .node-label { font-size: 10px; }
  .flow-svg .node-soc   { font-size: 12.5px; }
  .hero-stack { gap: 12px; }
  .hero { padding: 14px 16px; }
  .hero-value { font-size: 26px; }
  .hero-spark { height: 36px; }
  /* Splits under the flow: 2 cols. Label inline with value (single-line
     each) cuts the splits block from ~250 px to ~120 px on phones, letting
     the flow diagram dominate as the user requested. */
  .splits {
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    margin-top: 10px;
    padding: 0;
  }
  .split {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    padding: 6px 9px;
    gap: 6px;
  }
  .split-label { font-size: 9.5px; letter-spacing: 0.4px; }
  .split-value { font-size: 12px; white-space: nowrap; }

  /* ----- OMIE CARDS (Tarifário tab header) -----
     4 cards collapse to a 2×2 grid on phones, with tighter padding and
     smaller values so each card stays scannable at half-width. */
  .grid-omie { grid-template-columns: 1fr 1fr; gap: 8px; }
  .omie-card { padding: 10px 12px; gap: 6px; }
  .omie-head { gap: 4px; }
  .omie-type { font-size: 9.5px; letter-spacing: 0.3px; }
  .omie-day  { font-size: 9px; padding: 2px 6px; letter-spacing: 0.3px; }
  .omie-value { font-size: 19px; }
  .omie-date  { font-size: 10px; margin-top: -2px; }
  .omie-bi-values { gap: 4px; margin-top: 2px; }
  .omie-bi-label { font-size: 9.5px; letter-spacing: 0.4px; }
  .omie-bi-value { font-size: 16px; }
  /* On phones each max/min pill becomes a tiny stacked tile (label above
     value) so a 6-char €-value doesn't fight the label on a ~80 px column. */
  .omie-minmax { gap: 4px; margin-top: 2px; }
  .omm {
    flex-direction: column;
    align-items: flex-start;
    padding: 4px 6px;
    border-radius: 6px;
    gap: 1px;
  }
  .omm-label { font-size: 8.5px; letter-spacing: 0.3px; }
  .omm-value { font-size: 11px; }
  .omie-empty { font-size: 10px; padding: 6px 4px; }

  /* ----- OMIP FUTUROS (Tarifário tab) -----
     Stack the 3 sub-tables vertically on phones; shrink fonts/padding so
     each tile reads cleanly at full width. */
  .omip-tables { grid-template-columns: 1fr; gap: 14px; }
  .omip-section-head { font-size: 11px; padding-bottom: 4px; }
  .omip-table { font-size: 12px; }
  .omip-table th, .omip-table td { padding: 5px 6px; }
  .omip-table thead th { font-size: 9.5px; letter-spacing: 0.3px; }
  .omip-unit { font-size: 9px; }
  .omip-var { font-size: 11px; gap: 3px; }

  /* ----- OMIE PERIOD AVERAGE CALCULATOR -----
     Mirrors the .filters layout used by the price-history card: 2-col grid
     with minmax(0, 1fr) so the cells can shrink below content min-width
     (otherwise a long date string like "30/04/2026" forces each cell wider
     than half the card on small phones, pushing the right input off-canvas).
     Button spans full width below. */
  .omie-avg-controls {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
    align-items: stretch;
  }
  .omie-avg-field {
    flex: initial;
    width: auto;
    min-width: 0;
    font-size: 11px;
    gap: 4px;
    margin: 0;
  }
  /* iOS Safari renders date inputs with a native height that ignores plain
     `height`; -webkit-appearance: none strips that so our box behaves. */
  .omie-avg-field input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    min-width: 0;
    height: 32px;
    padding: 0 8px;
    font-size: 13px;
    line-height: 32px;
    box-sizing: border-box;
  }
  .omie-avg-field input[type="date"]::-webkit-date-and-time-value {
    text-align: left;
    height: 32px;
    line-height: 32px;
  }
  .omie-avg-field input[type="date"]::-webkit-calendar-picker-indicator {
    padding: 0;
    margin: 0;
    opacity: 0.55;
  }
  .omie-avg-controls .btn {
    grid-column: 1 / -1;
    width: 100%;
    min-height: 40px;
    margin: 0;
  }
  .omie-avg-row { padding: 9px 12px; }
  .omie-avg-label { font-size: 12.5px; }
  .omie-avg-value { font-size: 14px; }

  /* ----- SYSTEM CARDS (Energia/PV/Bateria/Inversor) -----
     `auto-fit, minmax(260px, 1fr)` already collapses to 1 col on phones.
     Just tighten the inner padding so labels don't break-wrap. */
  .grid-system-cards { gap: 12px; }
  .sys-card { padding: 14px 16px; }
  .sys-card h2 { font-size: 14px; }
  .sys-rows { gap: 7px; }               /* proportionally tighter than the 8px desktop value */
  .sys-row { font-size: 13.5px; }
  .sys-label { white-space: normal; }   /* allow wrap on very long PT labels */

  /* ----- COST KPI CARDS (Custos tab) -----
     Full-width on phones so each card mirrors the hero style on Dashboard:
     icon + label + big value + sub all readable in one glance. 2x2 of dense
     170 px tiles felt cramped on the Custos tab against everything else. */
  .grid-kpis,
  .grid-kpis-lg { grid-template-columns: 1fr; gap: 10px; }
  .grid-kpis .kpi,
  .grid-kpis-lg .kpi { padding: 14px 16px; gap: 14px; border-radius: 14px; }
  .grid-kpis-lg .kpi-icon { width: 48px; height: 48px; font-size: 20px; }
  .grid-kpis-lg .kpi-value { font-size: 26px; }
  .grid-kpis-lg .kpi-label { font-size: 11px; }
  .grid-kpis-lg .kpi-sub { font-size: 12px; }

  /* ----- TABLES -----
     7-10 column tables don't physically fit on a 390px screen. We keep the
     existing horizontal scroll via .table-wrap but add a subtle inset shadow
     on the right edge so the user knows there's more content off-screen. */
  .table-wrap {
    /* Pinned shadow on right edge that fades when the user scrolls all the
       way across (background-attachment: local + scroll). */
    background:
      linear-gradient(to right, rgba(13,17,24,0) 0%, rgba(13,17,24,0) 90%, rgba(13,17,24,0.95) 100%) right center / 30px 100% no-repeat local,
      var(--bg-1);
  }
  table { font-size: 12px; }
  th, td { padding: 8px 10px; }
  /* ----- TOTALS TABLES (Tab Totais) -----
     Solar Assistant-style layout: rotated column headers + narrow numeric
     columns so the whole table fits the iPhone width without horizontal
     scrolling. Sticky-first-column / overflow-scroll approach is reserved
     for the hist-table on the Custos tab (10 cols, too wide to rotate). */
  .totals-table {
    table-layout: fixed;
    width: 100%;
    font-size: 11px;
  }
  /* Date column — first th + tds. Keep horizontal, narrow width, left-align. */
  .totals-table th:first-child,
  .totals-table td:first-child {
    width: 78px;
    text-align: left;
    padding: 6px 6px;
    white-space: nowrap;
  }
  /* Numeric columns share the remaining width evenly. With 6 .num columns
     on a 320 px-ish card, each gets ~40 px — enough for "12.34" at 10.5 px.
     Centre both the rotated header and the value so the column reads as a
     single vertical line: header on top, values stacked below, all on the
     same x-axis. Right-aligned numbers (the desktop default) looked off
     because the rotated header lived at the left edge. */
  .totals-table th.num, .totals-table td.num {
    padding: 6px 2px;
    font-size: 10.5px;
    text-align: center;
  }
  /* Rotate the short header label 90° (reads bottom→top). Doing it on a span
     keeps the <th>'s own width/height intact for table layout. The long
     header is hidden on phones so the column stays narrow. */
  .totals-table thead th.num {
    height: 70px;
    vertical-align: bottom;
  }
  .totals-table thead th.num .hd-long { display: none; }
  .totals-table thead th.num .hd-short {
    display: inline-block;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
    font-size: 11px;
    letter-spacing: 0.3px;
    padding: 0 0 4px;
  }

  /* Switch from long date ("2026-05-13 qua") to short ("13/05 qua"). */
  .lbl-long  { display: none; }
  .lbl-short { display: inline; }
  /* Total row label wraps if needed (e.g. "Total 30 dias" at 11 px). */
  .totals-table tr.row-total td:first-child { white-space: normal; font-size: 10.5px; }
  .totals-table .weekday { font-size: 10px; }

  /* ----- HIST TABLE (Custos, 10 columns) -----
     Same SA-style rotated headers as .totals-table. With 9 numeric columns
     sharing the remaining width after the Período label, each cell is ~32 px
     — fits "12.3" (kWh @ 1 dp) and short €values without horizontal scroll.
     Slightly tighter padding + smaller font than the 6-column totals table. */
  #hist-table { table-layout: fixed; }
  #hist-table th:first-child,
  #hist-table td:first-child {
    width: 58px;
    font-size: 10.5px;
    padding: 6px 4px;
  }
  #hist-table th.num, #hist-table td.num {
    padding: 6px 2px;
    font-size: 10px;
  }
  /* Rotated header span: same vertical rotation as totals-table, but a touch
     smaller so 9 columns of headers don't visually fight each other. */
  #hist-table thead th.num .hd-short {
    font-size: 10.5px;
    letter-spacing: 0.2px;
  }

  /* ----- CHARTS -----
     Keep readable heights, shrink the legend so it doesn't dominate the tiny
     screen, and let the chart-controls-card filter row breathe. */
  .chart-h300 { height: 240px; }
  .chart-h280 { height: 220px; }
  .chart-h420 { height: 280px; }
  /* ----- FILTER ROWS (date inputs + apply + quick range) -----
     Switched from flex to a 2-col grid so we can pack the controls densely:
     row 1 = DE | ATÉ side-by-side
     row 2 = Resolução (full row)
     row 3 = Aplicar (full row)
     row 4 = quick-range buttons in a single flex row (5 buttons, 20% each)
     Total vertical footprint drops from ~370 px to ~220 px on a 393 px iPhone. */
  .chart-controls-card .filters { gap: 8px; }
  /* `minmax(0, 1fr)` (instead of plain `1fr`) lets each column shrink below
     its content's min-content width. Without it, a long readout like the
     tariff name on the Tarifário tab forces its column wider than the card,
     blowing the whole row past the card's right edge on a 375 px phone. */
  .filters {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
    align-items: stretch;
  }
  .filters label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--muted);
    margin: 0;
  }
  /* Resolução label appears 3rd among children, after DE and ATÉ — span full
     width so the dropdown isn't cramped to half. */
  .filters > label:nth-of-type(3) { grid-column: 1 / -1; }
  /* Force a single uniform height across every filter cell. The desktop rule
     bakes in `height: var(--control-h)` (34 px) for inputs/selects/buttons;
     we need to override `height` (not just `min-height`) for the mobile cells
     to actually shrink. Date inputs and the tariff readout span use the same
     height so the two adjacent grid cells line up visually. */
  .filters input, .filters select {
    height: 32px;
    padding: 6px 8px;
    font-size: 14px;
    box-sizing: border-box;
  }
  /* iOS Safari has its own native height for date inputs that ignores plain
     `height` on the element — the calendar icon + tap target inflate the
     rendered box well past 32 px. `-webkit-appearance: none` strips the
     native widget so our height/padding actually apply. */
  .filters input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    padding: 0 8px;
    font-size: 13px;
    line-height: 32px;
  }
  /* The native calendar/clear icons sit on a pseudo-element; with appearance
     reset they shrink to icon size and align vertically inside our 32 px
     box. */
  .filters input[type="date"]::-webkit-date-and-time-value {
    text-align: left;
    height: 32px;
    line-height: 32px;
  }
  .filters input[type="date"]::-webkit-calendar-picker-indicator {
    padding: 0;
    margin: 0;
    opacity: 0.55;
  }
  .filters .ph-tariff-readout {
    width: 100%;
    max-width: 100%;
    height: 32px;
    padding: 0 8px;
    font-size: 13px;
    border-radius: 8px;
    box-sizing: border-box;
  }
  .filters > button.btn-primary {
    grid-column: 1 / -1;
    min-height: 40px;
    margin: 0;
  }
  /* `grid-auto-flow: column; grid-auto-columns: 1fr` produces N equal-width
     columns for N children — works for the 5-button Custos quick row AND
     the 3-button Tarifário quick row without per-card overrides. */
  .filters .quick,
  .quick {
    grid-column: 1 / -1;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 6px;
    margin: 0;
  }
  .filters .quick .btn,
  .quick .btn {
    padding: 8px 4px;
    font-size: 11.5px;
    min-height: 32px;
    height: 32px;
    white-space: nowrap;
    min-width: 0;
  }

  /* ----- PAGERS (totals tables) -----
     Bigger arrows so a thumb can tap them precisely. */
  .pager { gap: 6px; }
  /* Pager arrows in the totals card head — see the post-`.btn` override
     below for the actual sizing (cascade order matters there). */

  /* When the card-head has a right-side group (Totais 60d / 12m), stack the
     pieces so we don't squeeze "Totais diários — últimos 30 dias" + range
     summary + 3 pager arrows into a single squashed row. Title goes on top
     full-width; meta and pager sit on a second row, meta-left / pager-right. */
  .card-head:has(.card-head-right) {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  /* Generic: allow the title + right-side readout to fall onto separate lines
     when they don't fit. Prevents overlap on cards like Fatura's
     "Decomposição da fatura" where the context string is long
     ("EDP Indexada Horária · Bi-horário - Ciclo Diário"). */
  .card-head { flex-wrap: wrap; }
  .card-head-right {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
  }

  /* ----- BUTTONS / TOUCH TARGETS ----- */
  .btn { min-height: 40px; padding: 10px 14px; font-size: 14px; }
  /* .btn-icon override placed AFTER `.btn` so it wins the cascade for
     `min-height` and `padding` (both share `.btn` class + selector specificity). */
  .btn-icon {
    width: 32px;
    height: 32px;
    min-height: 32px;
    padding: 0;
    font-size: 15px;
  }
  .btn-primary { min-height: 44px; }

  /* ----- MODAL (tariff change) -----
     Full-screen sheet on phones — easier to use than a tiny centred dialog. */
  .modal {
    width: 100vw;
    max-width: 100vw;
    height: 100vh;
    max-height: 100vh;
    margin: 0;
    border-radius: 0;
    inset: 0;
    padding: 0;
  }
  .modal::backdrop { background: rgba(0,0,0,0.85); }
  .modal form {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding-top: max(0px, env(safe-area-inset-top));
    padding-bottom: max(0px, env(safe-area-inset-bottom));
  }
  .modal-head { position: sticky; top: 0; z-index: 2; background: var(--bg-2); }
  .modal-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .modal-foot {
    position: sticky;
    bottom: 0;
    z-index: 2;
    background: var(--bg-2);
    flex-direction: column-reverse;   /* "Aplicar" on top of "Cancelar" */
    gap: 8px;
  }
  .modal-foot .btn { width: 100%; }
  .kind-options { grid-template-columns: 1fr; gap: 8px; }
  .form-row { grid-template-columns: 1fr; gap: 10px; }

  /* ----- PERIOD CARDS (resumo por período, Custos tab) -----
     7 label/value rows + a big number in a 170 px tile doesn't read well; go
     full-width on phones so each period card is comfortable to scan. */
  .period-grid { grid-template-columns: 1fr; gap: 12px; }
  .period-card { padding: 14px 16px; }
  .period-big { font-size: 28px; }

  /* ----- HISTORY TOTALS STRIP ----- */
  .hist-totals { gap: 6px; }
  .ht { padding: 6px 10px; font-size: 12px; }

  /* ----- TARIFF CARD (Tarifário tab) ----- */
  .tariff-current { grid-template-columns: 1fr; }

  /* ----- LOGIN & SETUP ----- */
  .login-card { padding: 28px 22px; margin: 22px 14px; max-width: 100%; }
  .login-card h1 { font-size: 22px; }
  .login-card input { padding: 14px; }
  .setup-wrap { margin: 16px auto; padding: 16px; max-width: 100%; }

  /* ----- FOOTER ----- */
  .foot { padding: 14px 12px; font-size: 12px; }
}

/* Very narrow phones (iPhone SE, iPhone 12 mini portrait ~ 375px). */
@media (max-width: 400px) {
  .grid-kpis { grid-template-columns: 1fr; }
  .grid-kpis-lg .kpi-value { font-size: 20px; }
  .period-grid { grid-template-columns: 1fr; }
  /* With 6 tabs (Dashboard, Gráficos, Totais, Custos, Tarifário, Fatura) the
     row no longer fits at 13 px font even on a 400 px viewport. Keep the 720
     px overrides (11.5 px / min-width 56 px) and let the row scroll
     horizontally — that's enabled in the 720 px block via overflow-x: auto.
     Restoring the old larger padding here would clip Tarifário+Fatura. */
  table { font-size: 11.5px; }
  th, td { padding: 6px 8px; }

  /* Fatura tab: numbers shrink so the hero KPI doesn't blow the card.
     The detailed tables already get the generic `table` rule above. */
  .fatura-hero-total { font-size: 34px; }
  .fatura-kpi-value { font-size: 18px; }
}

/* Fatura — tighten card layout below 720 px (mobile) */
@media (max-width: 720px) {
  .fatura-banner { grid-template-columns: auto 1fr; }
  .fatura-banner-action,
  .fatura-banner #fatura-banner-action {
    grid-column: 1 / -1;
    width: 100%;
    margin-top: 4px;
  }
  .fatura-hero-total { font-size: 36px; }
  .fatura-hero-projection-value { font-size: 20px; }
  .grid-fatura-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .fatura-kpi { padding: 10px 12px; }
  .fatura-kpi-value { font-size: 18px; }
  .fatura-section-title { font-size: 12px; padding: 5px 10px; }
  .fatura-table { font-size: 12px; }
  .fatura-table thead th, .fatura-table tbody td { padding: 6px 6px; }
  .fatura-table thead th { font-size: 9.5px; letter-spacing: 0.2px; }
  .fatura-table .unit { font-size: 10px; }
  /* Numbers + their unit should never break across lines — collapsing
     "4,27 €" to "4,27 \n €" is what looks broken on the user's screenshots. */
  .fatura-table td.num, .fatura-table th.num,
  .fatura-days-table td.num, .fatura-days-table th.num { white-space: nowrap; }
  /* Hide the intermediate "Sem IVA" + "IVA" columns on phones — desktop keeps
     them for auditing, but the cramped phone width forces every cell to wrap
     in ugly ways. The Descontos table has a different schema (Total spans 3
     cols) so we exclude it explicitly. */
  .fatura-section:not(#fatura-section-discounts) .fatura-table th:nth-child(4),
  .fatura-section:not(#fatura-section-discounts) .fatura-table th:nth-child(5),
  .fatura-section:not(#fatura-section-discounts) .fatura-table td:nth-child(4),
  .fatura-section:not(#fatura-section-discounts) .fatura-table td:nth-child(5) {
    display: none;
  }
  .fatura-days-table { font-size: 12px; }
  .fatura-days-table thead th, .fatura-days-table tbody td { padding: 6px 6px; }
  .fatura-totals-row { font-size: 13px; }
  .fatura-totals-total { font-size: 15px; }
  .fatura-totals-total > span:last-child { font-size: 19px; }
}

/* Standalone PWA (after "Add to Home Screen") — no Safari chrome means we
   own the whole viewport. Hide any "open in browser" affordances if we add
   them later, and tighten the top inset since the status bar is now ours. */
@media (display-mode: standalone) {
  .topbar { padding-top: max(10px, env(safe-area-inset-top)); }
}

/* =========================================================
   v4: CARRO tab — EV charging sessions + cost breakdown
   ========================================================= */

/* Reuse the fatura hero grid so the 4 KPI cards align with the rest of
   the dashboard's visual rhythm. Background tints distinguish them at a
   glance: yellow=charged, red=cost, green=mix (free), blue=year. */
.ev-hero-charged  { background: linear-gradient(135deg, rgba(251,191,36,0.12) 0%, rgba(251,191,36,0.02) 100%); border-left: 3px solid #fbbf24; }
.ev-hero-cost     { background: linear-gradient(135deg, rgba(248,113,113,0.12) 0%, rgba(248,113,113,0.02) 100%); border-left: 3px solid #f87171; }
.ev-hero-mix      { background: linear-gradient(135deg, rgba(52,211,153,0.12) 0%, rgba(52,211,153,0.02) 100%); border-left: 3px solid #34d399; }
.ev-hero-savings  { background: linear-gradient(135deg, rgba(96,165,250,0.12) 0%, rgba(96,165,250,0.02) 100%); border-left: 3px solid #60a5fa; }
.ev-hero-savings .hero-value { color: #60a5fa; }

/* Row titles above each hero strip (Mês / Ano / Histórico). Tightly coupled
   to the cards below — small uppercase label, no margin-top to feel like a
   group heading rather than a section break. */
.ev-row-title {
  margin: 16px 0 6px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: #94a3b8;
  font-weight: 600;
}
.ev-row-title:first-child { margin-top: 0; }

/* The stacked mix bar — solar / battery / grid in 3 segments. Used both
   in the hero card (full-width) and inside each session row's table cell
   (compact). Same colour tokens as the rest of the dashboard. */
.ev-mix-bar {
  display: flex;
  height: 8px;
  margin-top: 8px;
  background: rgba(255,255,255,0.06);
  border-radius: 4px;
  overflow: hidden;
}
.ev-mix-bar-row { height: 6px; margin-top: 4px; }
.ev-mix-seg { height: 100%; transition: width 300ms ease; }
.ev-mix-solar { background: #fbbf24; }   /* same as PV */
.ev-mix-bat   { background: #34d399; }   /* same as battery */
.ev-mix-grid  { background: #f87171; }   /* same as grid */

/* Sessions table — denser than the fatura tables because there are many
   rows. Tabular nums for kWh / € columns; the Mix column is wider to
   give the stacked bar room to read. */
.ev-sessions-card { margin-top: 16px; }
.ev-sessions-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.ev-sessions-table thead th {
  text-align: left;
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-weight: 700;
  padding: 8px 6px;
  border-bottom: 1px solid var(--card-border);
}
.ev-sessions-table th.num,
.ev-sessions-table td.num { text-align: right; }
/* Quando uma coluna não-num segue uma num (e.g. Fonte depois de €/kWh
   rede; Mix depois de Bateria), o valor right-aligned ficava colado ao
   conteúdo left-aligned da próxima coluna. Padding-left extra dá
   respiração entre os dois. */
.ev-sessions-table th.num + th:not(.num),
.ev-sessions-table td.num + td:not(.num) {
  padding-left: 14px;
}
.ev-sessions-table tbody td {
  padding: 8px 6px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.ev-sessions-table tbody tr:hover { background: rgba(255,255,255,0.02); }
.ev-sessions-table td.strong { font-weight: 700; }
.ev-mix-col { min-width: 180px; }

/* Hora column — "HH:MM → HH:MM" always on one line, tabular figures so
   columns align visually mesmo entre rows com horas de duracao diferente. */
.ev-hora-col,
.ev-hora-cell {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1px;
}

/* Real vs estimated provenance tags — green for real meter data, amber
   for TeslaMate-based estimation. Tooltip on hover (title attr in JS). */
.ev-tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3px;
}
.ev-tag-real  { background: rgba(52,211,153,0.16);  color: #34d399; }
.ev-tag-estim { background: rgba(251,191,36,0.16);  color: #fbbf24; }

/* Car-label cell in the sessions table — click to rename. "Outro" is the
   default fallback for wallbox sessions without a TM match; renaming it
   to anything else can unlock its own 8-card stats section. */
.ev-car-cell { cursor: pointer; }
.ev-car-cell:hover { background: rgba(96,165,250,0.10); outline: 1px solid rgba(96,165,250,0.3); }
.ev-car-named { color: #e6edf3; font-weight: 600; }
.ev-car-other { color: #94a3b8; font-style: italic; }

/* v6: bateria + km editáveis manualmente (para carros sem TeslaMate).
   Mesmo padrão visual do car_label — cursor pointer, hover sublime. */
.ev-bat-cell, .ev-km-cell { cursor: pointer; }
.ev-bat-cell:hover, .ev-km-cell:hover {
  background: rgba(96,165,250,0.10);
  outline: 1px solid rgba(96,165,250,0.3);
}

@media (max-width: 720px) {
  /* On phones we keep ALL columns visible and let the user scroll the
     table horizontally (the surrounding .table-wrap is overflow-x:auto).
     Older versions hid columns to fit the viewport, but that lost data;
     scrolling is the lesser evil. */
  .ev-sessions-table { font-size: 12px; min-width: 660px; }
  .ev-sessions-table thead th,
  .ev-sessions-table tbody td { padding: 5px 4px; }
  .ev-mix-col { min-width: 80px; }
  /* No mobile a hora "HH:MM → HH:MM" ocupa 11 chars + arrow; deixá-la
     respirar sem wrap mas com font ligeiramente menor para caber. */
  .ev-hora-cell { font-size: 10.5px; }
  /* Pin the Data column to the left while the rest scrolls under it —
     makes the dates always visible as the user pans across kWh/€/etc.
     Background is opaque so cells passing underneath are masked.
     `nowrap` on the date keeps "DD/MM HH:MM" on a single line so the
     sticky column stays as narrow as possible. */
  .ev-sessions-table thead th:first-child,
  .ev-sessions-table tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--card-bg, #161a22);
    box-shadow: 1px 0 0 var(--card-border);
    white-space: nowrap;
    width: 1%;
    padding-left: 4px;
    padding-right: 6px;
    font-size: 11.5px;
  }
  .ev-sessions-table thead th:first-child { z-index: 3; }
  /* Keep "48→100 %" and "X,XX €" on one line — avoids the wasted vertical
     padding caused by wrapping the " %" or " €" suffix to a new row. */
  .ev-sessions-table tbody td.num { white-space: nowrap; }
}

/* ============= Hero grid for the Carro tab (4 cards side-by-side) ============= */
.grid-ev-hero {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: stretch;
}
.grid-ev-hero > .card.hero {
  display: flex;
  flex-direction: column;
  /* The big colored value stays near the top, sub-info flows underneath. */
  justify-content: flex-start;
}
@media (max-width: 980px) {
  .grid-ev-hero { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .grid-ev-hero { grid-template-columns: 1fr; }
}

/* ============= Dense Carro cards (4×2 grid, multi-section per card) =============
   Each card combines month/year metrics in stacked rows so the user can compare
   scopes at a glance. Fonts are intentionally smaller than the .hero variant —
   the user feedback was that the previous "huge number" approach wasted space
   and didn't allow enough info per card. */
.grid-ev-cards {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}
.ev-card {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  border-left: 3px solid transparent;
}
.ev-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.ev-card-title {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: #cbd5e1;
  font-weight: 600;
}
/* `.ev-card-hint` (bola "?" 16×16 tooltip) removida — não é usada por
   nenhum card. O card Poupança tinha tooltip explicativo mas foi removido
   por feedback do utilizador, e o card Histórico usava esta class por
   engano para texto longo "desde MM/AAAA" (agora `muted small`). */
.ev-card-rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.ev-card-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ev-card-row-mix {
  gap: 4px;
}
.ev-card-row-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.ev-card-row-grid > div { display: flex; flex-direction: column; gap: 1px; }
.ev-card-row-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: #94a3b8;
}
.ev-card-row-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  color: #e6edf3;
}
.ev-card-row-value-small {
  font-family: 'JetBrains Mono', monospace;
  font-size: 1.05rem;
  font-weight: 600;
  color: #e6edf3;
}
.ev-card-row-sub {
  font-size: 0.78rem;
  color: #94a3b8;
  line-height: 1.4;
}
.ev-card-foot {
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px dashed rgba(255,255,255,0.06);
  font-size: 0.78rem;
  color: #cbd5e1;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Per-card themed accents (left border + tinted bg). Each metric area
   gets a unique colour so the eye scans by topic instead of having to
   read every title. */
.ev-card-energy   { border-left-color: #fbbf24; background: linear-gradient(135deg, rgba(251,191,36,0.08) 0%, rgba(251,191,36,0.01) 100%); }
.ev-card-energy   .ev-card-row-value { color: #fbbf24; }
.ev-card-cost     { border-left-color: #f87171; background: linear-gradient(135deg, rgba(248,113,113,0.08) 0%, rgba(248,113,113,0.01) 100%); }
.ev-card-cost     .ev-card-row-value { color: #f87171; }
.ev-card-savings  { border-left-color: #34d399; background: linear-gradient(135deg, rgba(52,211,153,0.08) 0%, rgba(52,211,153,0.01) 100%); }
.ev-card-savings  .ev-card-row-value { color: #34d399; }
.ev-card-mix      { border-left-color: #60a5fa; background: linear-gradient(135deg, rgba(96,165,250,0.08) 0%, rgba(96,165,250,0.01) 100%); }
.ev-card-bands    { border-left-color: #a78bfa; background: linear-gradient(135deg, rgba(167,139,250,0.08) 0%, rgba(167,139,250,0.01) 100%); }
.ev-card-typical  { border-left-color: #f472b6; background: linear-gradient(135deg, rgba(244,114,182,0.08) 0%, rgba(244,114,182,0.01) 100%); }
.ev-card-when     { border-left-color: #38bdf8; background: linear-gradient(135deg, rgba(56,189,248,0.08) 0%, rgba(56,189,248,0.01) 100%); }
.ev-card-history  { border-left-color: #94a3b8; background: linear-gradient(135deg, rgba(148,163,184,0.06) 0%, rgba(148,163,184,0.01) 100%); }

/* Mobile responsiveness — 4×2 collapses to 2×4 then 1×8. */
@media (max-width: 1200px) {
  .grid-ev-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .grid-ev-cards { grid-template-columns: 1fr; }
}

/* ============= Hero extras (sub-metrics inside the 4 primary cards) =============
   Stacked vertically below the big colored value — the user explicitly asked
   for vertical stacking so each piece of info gets its own line. */
.hero-extras {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 10px;
  font-size: 0.85rem;
  color: #cbd5e1;
}
.hero-extras > span { line-height: 1.4; }
.hero-extras .strong { color: #34d399; font-weight: 600; }

/* ============= Insight row (5 secondary cards) ============= */
.grid-ev-insights {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
  margin-top: 8px;
}
.ev-insight {
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-left: 3px solid transparent;
}
.ev-insight-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #94a3b8;
}
.ev-insight-value {
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
  color: #e6edf3;
}
.ev-insight-sub {
  font-size: 0.82rem;
  color: #94a3b8;
}
.ev-insight-savings    { background: linear-gradient(135deg, rgba(52,211,153,0.10) 0%, rgba(52,211,153,0.02) 100%); border-left-color: #34d399; }
.ev-insight-savings .ev-insight-value { color: #34d399; }
.ev-insight-bands      { background: linear-gradient(135deg, rgba(167,139,250,0.10) 0%, rgba(167,139,250,0.02) 100%); border-left-color: #a78bfa; }
.ev-insight-typical    { background: linear-gradient(135deg, rgba(96,165,250,0.10) 0%, rgba(96,165,250,0.02) 100%); border-left-color: #60a5fa; }
.ev-insight-validation { background: linear-gradient(135deg, rgba(251,191,36,0.10) 0%, rgba(251,191,36,0.02) 100%); border-left-color: #fbbf24; }
.ev-insight-co2        { background: linear-gradient(135deg, rgba(110,231,183,0.10) 0%, rgba(110,231,183,0.02) 100%); border-left-color: #6ee7b7; }
.ev-insight-co2 .ev-insight-value { color: #6ee7b7; }

/* Bandas bar (Vazio / Fora-vazio) */
.ev-band-bar {
  display: flex;
  height: 8px;
  margin-top: 6px;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(255,255,255,0.05);
}
.ev-band-seg { height: 100%; transition: width 0.3s ease; }
.ev-band-vazio { background: #6ee7b7; }
.ev-band-fora  { background: #fbbf24; }

/* ============= Heatmap dia × hora ============= */
.ev-heatmap-card { margin-top: 8px; }
.ev-heatmap {
  display: grid;
  grid-template-columns: 36px repeat(24, 1fr);
  grid-template-rows: 16px repeat(7, 22px);
  gap: 2px;
  margin-top: 8px;
  font-size: 0.7rem;
}
.ev-heatmap-corner { background: transparent; }
.ev-heatmap-hour, .ev-heatmap-day {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #94a3b8;
  font-variant-numeric: tabular-nums;
}
.ev-heatmap-day { justify-content: flex-end; padding-right: 4px; }
.ev-heatmap-cell {
  border-radius: 3px;
  background: rgba(255,255,255,0.04);
  position: relative;
  cursor: default;
}
.ev-heatmap-cell:hover { outline: 1px solid #60a5fa; }
.ev-heatmap-cell[title]:hover::after {
  content: attr(title);
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, -4px);
  background: #0f172a;
  border: 1px solid #334155;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 0.75rem;
  white-space: nowrap;
  pointer-events: none;
  z-index: 10;
  color: #e6edf3;
}

/* ============= Mobile responsiveness for insights + heatmap ============= */
@media (max-width: 980px) {
  .grid-ev-insights { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .grid-ev-insights { grid-template-columns: 1fr; }
  .hero-extras { font-size: 0.78rem; }
  .ev-heatmap {
    grid-template-columns: 28px repeat(24, 1fr);
    grid-template-rows: 14px repeat(7, 18px);
    font-size: 0.6rem;
  }
}


/* ============================================================
   DEBUG BATERIA — tab exclusivo da pasta /debug. Mostra todos os
   registos BMS lidos do bloco Modbus 3125-3249.
   ============================================================ */
.debug-bms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(310px, 1fr));
  gap: 14px;
  margin-top: 14px;
}
.debug-bms-card {
  background: rgba(255,255,255,0.02);
  border: 1px solid var(--card-border, rgba(255,255,255,0.08));
  border-radius: 10px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
}
.debug-bms-card h3 {
  margin: 0 0 10px 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.3px;
}
.debug-bms-rows {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px 12px;
  font-size: 12.5px;
  margin-bottom: 8px;
}
.debug-bms-rows .lbl { color: var(--muted, #94a3b8); }
.debug-bms-rows .val {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 600;
  text-align: right;
}
.debug-bms-rows .val.strong { font-weight: 800; font-size: 13.5px; }
.debug-bms-card > p.muted { font-size: 11px; line-height: 1.4; margin: 0; opacity: 0.7; }

/* Coloração subtle por categoria */
.debug-bms-card-health     { border-left: 3px solid #34d399; }
.debug-bms-card-cell       { border-left: 3px solid #fbbf24; }
.debug-bms-card-temp       { border-left: 3px solid #f87171; }
.debug-bms-card-limits     { border-left: 3px solid #60a5fa; }
.debug-bms-card-crosscheck { border-left: 3px solid #a78bfa; }
.debug-bms-card-errors     { border-left: 3px solid #f472b6; }
.debug-bms-card-status     { border-left: 3px solid #94a3b8; }

/* Códigos de erro/warning a 0 ficam em verde subtle, ≠0 a vermelho */
.debug-bms-rows .val.err-ok  { color: #34d399; }
.debug-bms-rows .val.err-bad { color: #f87171; font-weight: 800; }


/* Grid compacta para os 125 offsets do bloco 3250-3374. Cada célula
   mostra o offset (label) + valor uint16 + valor hex em letra pequena. */
.debug-mod-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
}
.debug-mod-cell {
  background: rgba(255,255,255,0.02);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 4px;
  padding: 4px 6px;
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}
.debug-mod-cell .off { color: var(--muted, #94a3b8); font-size: 9.5px; }
.debug-mod-cell .v   { font-weight: 700; font-size: 12px; }
.debug-mod-cell .hex { color: var(--muted, #94a3b8); font-size: 9.5px; }
.debug-mod-cell.zero { opacity: 0.4; }
.debug-mod-cell.nonzero { border-color: rgba(52,211,153,0.3); background: rgba(52,211,153,0.04); }

.btn-mini {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.12);
  color: var(--fg, #e6edf3);
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  cursor: pointer;
}
.btn-mini:hover { background: rgba(255,255,255,0.10); }
