/* DomoPi — thème "tableau électrique"
   Palette : graphite #12161c / panneau #1b2129 / trait #2b3442
             ambre énergie #e8a13c / cuivre #b06a2c / signal vert #58c470
             texte #dfe6ee / texte secondaire #8b97a5 */
:root {
  --bg: #12161c;
  --panel: #1b2129;
  --panel-2: #222a35;
  --line: #2b3442;
  --amber: #e8a13c;
  --copper: #b06a2c;
  --ok: #58c470;
  --warn: #e0b13f;
  --err: #e05b4f;
  --txt: #dfe6ee;
  --muted: #8b97a5;
  --mono: ui-monospace, "Cascadia Mono", "DejaVu Sans Mono", Menlo, monospace;
  --sans: system-ui, "Segoe UI", Roboto, sans-serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
  background: var(--bg);
  color: var(--txt);
  font-family: var(--sans);
  font-size: 15px;
}
a { color: var(--amber); text-decoration: none; }
button, input, select, textarea { font: inherit; }
/* L'attribut hidden doit gagner même sur les éléments ayant un display
   explicite (ex. #banner et #login-view en flex). */
[hidden] { display: none !important; }

/* ---------------- bandeau système ---------------- */
#banner {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: .6rem;
  padding: .45rem .8rem;
  background: linear-gradient(180deg, #1d242e, #161c24);
  border-bottom: 2px solid var(--copper);
}
#banner .brand {
  font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  font-size: .95rem; color: var(--amber); white-space: nowrap;
}
#banner .led {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--ok); box-shadow: 0 0 6px var(--ok);
}
#banner .led.err { background: var(--err); box-shadow: 0 0 6px var(--err); }
#banner nav { display: flex; gap: .25rem; flex: 1; overflow-x: auto; }
#banner nav a, #banner .bbtn {
  color: var(--txt); background: transparent; border: 1px solid transparent;
  border-radius: 6px; padding: .3rem .6rem; cursor: pointer; white-space: nowrap;
}
#banner nav a:hover, #banner .bbtn:hover { border-color: var(--line); background: var(--panel-2); }
#banner nav a.active { border-color: var(--copper); color: var(--amber); }
#banner .who { color: var(--muted); font-size: .85rem; white-space: nowrap; }
#banner .brand-sub { color: var(--muted); letter-spacing: .08em; white-space: nowrap;
  text-transform: uppercase; font-size: .8rem; }
.show-sm { display: none; }   /* libellés courts affichés uniquement en mobile */
/* sous-bandeau à onglets (page Paramètres) */
#tabs { position: sticky; top: 46px; z-index: 40; display: flex; gap: .3rem;
  padding: .35rem .8rem; background: var(--panel);
  border-bottom: 1px solid var(--line); overflow-x: auto; }
#tabs .tab { background: transparent; color: var(--txt);
  border: 1px solid transparent; border-radius: 6px; padding: .3rem .7rem;
  cursor: pointer; white-space: nowrap; }
#tabs .tab:hover { border-color: var(--line); background: var(--panel-2); }
#tabs .tab.active { border-color: var(--copper); color: var(--amber); }

/* ---------------- structure ---------------- */
main { padding: 1rem; max-width: 1100px; margin: 0 auto; }
.page-bg { min-height: calc(100vh - 46px); background-size: cover; background-position: center; }
h1 { font-size: 1.25rem; margin: .2rem 0 1rem; letter-spacing: .04em; }
h2 { font-size: 1.02rem; color: var(--amber); margin: 1.4rem 0 .6rem;
     text-transform: uppercase; letter-spacing: .1em; border-bottom: 1px solid var(--amber);
     padding-bottom: .3rem; }
h3 { font-size: .88rem; color: var(--amber); margin: 1rem 0 .4rem;
     letter-spacing: .04em; }
.muted { color: var(--muted); }
.crumbs { color: var(--muted); font-size: .85rem; margin-bottom: .8rem; }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--amber); }

/* ---------------- cartes / widgets ---------------- */
.grid { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.grid .wide { grid-column: 1 / -1; }
.card {
  position: relative;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--line); border-radius: 10px;
  padding: .7rem; display: flex; flex-direction: column; gap: .4rem;
  min-height: 96px;
}
/* Toutes les tuiles d'une page ont la même hauteur (les textes libres .wide
   et les graphes gardent la leur). min-width: 0 : sans lui, un libellé long
   en nowrap (ex. stop « Surround Decoder ») fixe le min-content de la tuile
   et élargit sa colonne 1fr — l'ellipse ne joue jamais. */
.grid > .card { height: 122px; min-height: 0; min-width: 0; }
.grid > .card.wide { height: auto; }
/* Tuile pilotable sur échelle : éclaircie depuis le bas à hauteur de la
   consigne (--lvl = position min->max) — sombre au mini, claire au maxi. */
.card.lvl { background-image: linear-gradient(to top,
  rgba(255,255,255,.10) var(--lvl, 0%), rgba(255,255,255,0) var(--lvl, 0%)); }
.card.clickable { cursor: pointer; user-select: none;
  -webkit-user-select: none; -webkit-touch-callout: none; }
.card.clickable:hover { border-color: var(--copper); }
/* appui long (mobile) : le cadre grossit légèrement pendant la vibration */
.card.pressing { transform: scale(1.06); border-color: var(--amber); }
.card .icon { height: 44px; display: flex; align-items: center; justify-content: center; }
.card .icon img { height: 42px; width: 42px; filter: drop-shadow(0 0 2px rgba(0,0,0,.5)); }
/* État partiel (gradateur / ouverture partielle) : l'icône "on" est révélée
   depuis le bas à hauteur de --pct, par-dessus l'icône "off". */
.card .icon .stack { position: relative; width: 42px; height: 42px; display: block; }
.card .icon .stack img { position: absolute; inset: 0; }
.card .icon .stack img.top { clip-path: inset(calc(100% - var(--pct, 0%)) 0 0 0); }
input[type=range] { accent-color: var(--amber); }
.card .name { font-size: .82rem; color: var(--muted); text-align: center;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card .value {
  font-family: var(--mono); font-size: 1.25rem; text-align: center;
  color: var(--amber); font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card .value.off { color: var(--muted); }
/* capteur calculé incalculable (division par zéro, référence absente...) */
.card .value .invalid { color: var(--muted); font-style: italic; font-size: .95rem; }
/* badge en surimpression : ne change pas la hauteur de la tuile */
.card .stale { position: absolute; top: .25rem; right: .45rem;
  color: var(--err); font-size: .72rem; text-align: right; }
.card.link { align-items: center; justify-content: center; border-style: dashed; }
.card.link .name { color: var(--txt); font-size: .95rem; }
/* tuile lien : dossier au premier plan, icône de page derrière à 50 % */
.card.link .lstack { position: relative; width: 46px; height: 46px;
  display: flex; align-items: center; justify-content: center; }
.card.link .lstack img { position: absolute; inset: 2px;
  width: 42px; height: 42px; opacity: .5; }
.card.link .lstack .folder { position: relative; font-size: 1.5rem;
  filter: drop-shadow(0 0 3px rgba(0,0,0,.65)); }
#root-nav .navicon { width: 16px; height: 16px; vertical-align: -3px;
  margin-right: .3rem; }

/* Tuiles d'action du dialogue « Tester et exporter » (admin) : exports ODS et
   effacement de l'historique. Même cadre que les tuiles de page, mais le
   libellé se replie (les tuiles du visualiseur sont en nowrap + ellipse) et la
   hauteur suit le contenu à partir de la hauteur commune. */
.card.act { align-items: center; justify-content: center; border-style: dashed;
  text-decoration: none; color: inherit; }
.grid > .card.act { height: auto; min-height: 122px; }
.card.act .act-glyph { font-size: 1.5rem; line-height: 1;
  filter: drop-shadow(0 0 3px rgba(0,0,0,.65)); }
.card.act .name { white-space: normal; color: var(--txt); font-size: .84rem;
  line-height: 1.2; }
.card.act .hint { color: var(--muted); font-size: .72rem; text-align: center;
  line-height: 1.15; }
.card.act-danger:hover { border-color: var(--err); }
.card.act-danger .name { color: var(--err); }
/* Diagnostic de la fenêtre « Tester et exporter » : libellé à gauche, valeur
   à chasse fixe à droite (ce sont des topics et des charges utiles, qui
   doivent pouvoir se couper n'importe où sans élargir la fenêtre). */
table.diag { width: 100%; table-layout: fixed; }
table.diag th { width: 40%; text-align: left; vertical-align: top;
  font-weight: 400; color: var(--muted); font-size: .82rem; padding: .18rem .5rem .18rem 0; }
table.diag td { font-family: var(--mono); font-size: .8rem; vertical-align: top;
  overflow-wrap: anywhere; padding: .18rem 0; }
.diag-note { color: var(--amber); font-size: .82rem; margin: .4rem 0 0;
  border-left: 2px solid var(--amber); padding-left: .5rem; }
#pb-live { color: var(--muted); }

/* Les 4 tuiles tiennent sur une ligne dans la largeur du dialogue (680 px) :
   150 px de piste minimum n'en laissaient que 3, la dernière restait seule.
   En mobile, la règle des 3 colonnes fixes (plus bas) doit rester maîtresse. */
@media (min-width: 701px) {
  #pb-grid { grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); }
}

/* ---------------- graphes ---------------- */
.chart-box { background: color-mix(in srgb, var(--panel) 92%, transparent);
  border: 1px solid var(--line); border-radius: 10px; padding: .6rem; }
/* Deux cellules côte à côte : titre + valeur à gauche (1 ou 2 lignes), boutons
   de plage à droite (repliés sur 1 ou 2 lignes, alignés à droite) — les boutons
   ne passent jamais sous le titre, la hauteur reste minimale sur mobile. */
.chart-head { display: flex; align-items: center; gap: .4rem; }
.chart-head .title { font-size: .9rem; flex: 1 1 auto; min-width: 0;
  /* 2 lignes maxi, ellipse au-delà : le titre ne pousse jamais l'en-tête
     à 3 lignes */
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden; }
.chart-head .ranges { display: flex; flex-wrap: wrap; gap: .3rem;
  justify-content: flex-end; margin-left: auto; flex: 0 1 auto; }
.chart-head button { background: var(--panel-2); border: 1px solid var(--line);
  color: var(--txt); border-radius: 6px; padding: .15rem .5rem; cursor: pointer; font-size: .78rem; }
.chart-head button.active { border-color: var(--copper); color: var(--amber); }
.chart-svg { width: 100%; height: auto; display: block;
  /* Sans ça, un glissé tactile est capté par le défilement de page avant
     que le navigateur ne livre les pointermove au curseur de survol. */
  touch-action: none;
  /* Le rectangle de zoom se trace au clic maintenu : pas de sélection de
     texte parasite pendant le glissé. */
  -webkit-user-select: none; user-select: none; }
.legend { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem .9rem;
  font-size: .75rem; color: var(--muted); padding: .2rem .4rem; }
.legend i { display: inline-block; width: 10px; height: 3px; margin-right: 4px; vertical-align: middle; }
/* Commandes de zoom : apparaissent (hidden retiré par charts.js) dès qu'une
   vue est empilée, alignées à droite de la légende. */
.legend .zoom-ctl { display: flex; gap: .3rem; margin-left: auto; flex: 0 0 auto; }
.legend .zoom-ctl[hidden] { display: none; }
.legend .zoom-ctl button { background: var(--panel-2); border: 1px solid var(--line);
  color: var(--txt); border-radius: 6px; padding: .1rem .45rem;
  cursor: pointer; font-size: .72rem; white-space: nowrap; }
.legend .zoom-ctl button:hover { border-color: var(--copper); color: var(--amber); }

/* ---------------- formulaires ---------------- */
label { display: block; font-size: .82rem; color: var(--muted); margin: .5rem 0 .15rem; }
input[type=text], input[type=password], input[type=number],
input[type=datetime-local], select, textarea {
  width: 100%; background: var(--bg); color: var(--txt);
  border: 1px solid var(--line); border-radius: 7px; padding: .45rem .6rem;
}
/* Champ grisé (identifiants FTP en mode anonyme) */
input:disabled { opacity: .5; }
input:focus, select:focus, textarea:focus, button:focus-visible {
  outline: 2px solid var(--copper); outline-offset: 1px;
}
.btn { background: var(--panel-2); color: var(--txt); border: 1px solid var(--line);
  border-radius: 7px; padding: .45rem .9rem; cursor: pointer; }
.btn:hover { border-color: var(--copper); }
.btn.primary { background: var(--copper); border-color: var(--copper); color: #fff; }
.btn.danger { border-color: var(--err); color: var(--err); background: transparent; }
.btn.sm { padding: .2rem .55rem; font-size: .8rem; }
/* Lien présenté en bouton (téléchargement d'une sauvegarde) : même gabarit */
a.btn { display: inline-block; text-decoration: none; line-height: normal; }
/* bouton « Parcourir » des champs fichier : même style que les .btn */
input[type=file]::file-selector-button { background: var(--panel-2);
  color: var(--txt); border: 1px solid var(--line); border-radius: 7px;
  padding: .45rem .9rem; cursor: pointer; margin-right: .6rem; font: inherit; }
input[type=file]::file-selector-button:hover { border-color: var(--copper); }
.row { display: flex; gap: .6rem; align-items: end; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 130px; }
.row > .fix { flex: 0 0 auto; min-width: 0; }

/* ---------------- tableaux ---------------- */
table { width: 100%; border-collapse: collapse; font-size: .85rem; }
th { text-align: left; color: var(--muted); font-weight: 600; padding: .4rem .5rem;
     border-bottom: 1px solid var(--line); }
td { padding: .35rem .5rem; border-bottom: 1px solid color-mix(in srgb, var(--line) 55%, transparent); }
tr:hover td { background: color-mix(in srgb, var(--panel-2) 60%, transparent); }
/* tri + filtres par colonne (tableau des périphériques) ; les deux lignes
   d'en-tête restent visibles au défilement (sous le bandeau + les onglets,
   sticky : 46 px + ~38 px, avec léger recouvrement pour éviter tout jour) */
#dev-head th { position: sticky; top: 84px; background: var(--bg); z-index: 6; }
#dev-filters th { position: sticky; top: 113px; background: var(--bg); z-index: 5; }
#dev-head th[data-sort] { cursor: pointer; user-select: none; white-space: nowrap; }
#dev-head th[data-sort]:hover { color: var(--txt); }
#dev-head th .arrow { color: var(--amber); }
#dev-filters th { padding: .15rem .3rem; }
#dev-filters input, #dev-filters select {
  width: 100%; min-width: 64px; font-size: .78rem; padding: .2rem .35rem;
}
td .mono { font-family: var(--mono); }
/* capteurs virtuels : synthèse de formule tronquée (max-width borne aussi la
   largeur que l'algorithme de table accorde à la colonne — sans elle, une
   formule longue en nowrap élargit la colonne et décale tout le tableau) */
.fsyn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--mono); font-size: .82rem; }
/* max-width réduite de 300 à 238 px le 27/07/2026 pour financer la colonne
   « Type » (61 px) sans élargir le tableau : c'est cette borne, et non la
   largeur en % de l'en-tête, qui décide de la largeur réelle de la colonne. */
.fcell { display: flex; align-items: center; gap: .4rem; position: relative;
  min-width: 120px; max-width: 238px; }
.fcell .fsyn { flex: 1 1 auto; min-width: 0; }
/* formule complète en infobulle multiligne au survol du texte tronqué */
.fcell[data-full]:has(.fsyn:hover)::after {
  content: attr(data-full);
  position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 30;
  width: max-content; max-width: min(70vw, 480px);
  white-space: pre-wrap; overflow-wrap: break-word;
  font-family: var(--mono); font-size: .8rem; line-height: 1.4;
  background: #161c24; color: var(--txt);
  border: 1px solid var(--line); border-radius: 6px;
  padding: .4rem .6rem; box-shadow: 0 5px 16px rgba(0,0,0,.45);
  pointer-events: none;
}
#vf-text { width: 100%; min-height: 4.2rem; font-family: var(--mono);
  font-size: .9rem; resize: vertical; }
#vf-status { min-height: 1.3rem; font-size: .85rem; margin: .25rem 0 .5rem; }
#vf-status.ok { color: #6fbf73; }
#vf-status.err { color: var(--err); }
.vf-lists { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.vf-lists .pane { border: 1px solid var(--line); border-radius: 8px;
  padding: .45rem; min-width: 0; }
.vf-lists h4 { margin: 0 0 .3rem; font-size: .8rem; color: var(--amber);
  text-transform: uppercase; letter-spacing: .06em; }
.vf-lists ul { list-style: none; margin: 0; padding: 0; max-height: 180px;
  overflow-y: auto; }
.vf-lists li { padding: .22rem .3rem; border-radius: 5px; cursor: pointer;
  font-size: .84rem; line-height: 1.25; }
.vf-lists li:hover { background: var(--panel-2); }
.vf-lists li .mono { font-size: .82rem; }
.vf-lists li small { color: var(--muted); display: block; }
@media (max-width: 700px) { .vf-lists { grid-template-columns: 1fr; } }
.lvl-ERROR { color: var(--err); } .lvl-WARNING { color: var(--warn); }
.lvl-INFO { color: var(--txt); } .lvl-DEBUG { color: var(--muted); }

/* ---------------- login ---------------- */
#login-view { display: flex; min-height: 80vh; align-items: center; justify-content: center; }
#login-view .panel { width: min(340px, 92vw); background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 1.4rem;
  border-top: 3px solid var(--copper); }

/* ---------------- divers ---------------- */
.toast { position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
  background: var(--panel-2); border: 1px solid var(--copper); color: var(--txt);
  padding: .5rem 1rem; border-radius: 8px; z-index: 100; }
.icon-pick { display: grid; grid-template-columns: repeat(auto-fill, minmax(52px,1fr));
  gap: .4rem; max-height: 220px; overflow: auto; padding: .4rem;
  border: 1px solid var(--line); border-radius: 8px; }
.icon-pick img { width: 40px; height: 40px; padding: 4px; border-radius: 6px;
  cursor: pointer; border: 1px solid transparent; }
.icon-pick img:hover, .icon-pick img.sel { border-color: var(--amber); background: var(--panel-2); }
/* aperçus du dialogue « Générer par IA » (chaque icône en 42 et 96 px) */
.ai-icons { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: .7rem; }
.ai-ic { border: 1px solid var(--line); border-radius: 8px; padding: .5rem;
  display: flex; flex-direction: column; align-items: center; gap: .4rem;
  background: var(--panel-2); }
.ai-ic .prev { display: flex; align-items: flex-end; gap: .6rem; }
.ai-ic .prev span { display: block; }
.ai-ic .prev svg { display: block; width: 100%; height: 100%; }
.ai-ic input[type=text] { width: 9.5rem; font-size: .85rem; }
/* boutons de consigne du dialogue de réglage sur échelle (2 à 20 valeurs) */
.scale-btns { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: .4rem; margin-top: .7rem; }
.scale-btns .btn { display: flex; flex-direction: column; align-items: center;
  gap: .15rem; padding: .4rem .2rem; min-width: 0; }
.scale-btns .btn img { width: 22px; height: 22px; }
.scale-btns .btn span { max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: .82rem; }
/* éditeur d'échelle (admin) : lignes valeur / texte / icône de la série */
.stop-row { display: flex; gap: .4rem; align-items: center; margin-bottom: .35rem; }
.stop-row .st-v { flex: 0 0 92px; }
.stop-row .st-l { flex: 1; min-width: 0; }
.stop-row .st-i { width: 30px; height: 30px; flex: 0 0 30px; cursor: pointer;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px;
  padding: 3px; }
.stop-row .st-x { flex: 0 0 auto; }
dialog { background: var(--panel); color: var(--txt); border: 1px solid var(--line);
  border-radius: 12px; max-width: min(680px, 94vw); width: 100%; }
.mono { font-family: var(--mono); }
/* aide configuration MQTT du WES (découverte vide ou en échec) */
.wes-help .wes-panel { border: 1px solid var(--line); border-radius: 8px;
  overflow: hidden; margin: .5rem 0; }
.wes-help .wes-head { background: #314a5b; color: #fff; padding: .4rem .7rem;
  font-size: .88rem; }
.wes-help th { width: 45%; }
.wes-help small { display: block; color: var(--muted); font-weight: 400; }
.wes-help .wes-on { display: inline-block; background: #5cb85c; color: #fff;
  border-radius: 999px; padding: .02rem .6rem; font-size: .78rem; font-weight: 600; }
.wes-help tr.crucial th, .wes-help tr.crucial td { color: var(--amber); }
.wes-help .dot { display: inline-block; width: .8rem; height: .8rem;
  border-radius: 50%; vertical-align: -.08rem; }
dialog::backdrop { background: rgba(0,0,0,.6); }
/* bouton de fermeture [X] en haut à droite des dialogues */
.dlg-x { position: absolute; top: .45rem; right: .55rem; z-index: 1;
  background: transparent; border: 1px solid transparent; color: var(--muted);
  font-size: 1.05rem; line-height: 1; padding: .3rem .5rem; border-radius: 6px;
  cursor: pointer; }
.dlg-x:hover { color: var(--txt); border-color: var(--line); background: var(--panel-2); }
@media (max-width: 700px) {
  main { padding: .6rem; }
  /* 3 colonnes fixes : 4 colonnes (iPhone 14 en portrait) rendaient les
     tuiles trop serrées, et on garde bien 3 colonnes dès 320 px */
  .grid { grid-template-columns: repeat(3, 1fr); gap: .5rem; }
  .card .value { font-size: 1.05rem; }
  #banner .who { display: none; }
  #banner { flex-wrap: wrap; row-gap: .2rem; }
  #banner .brand-sub { display: none; }
  #banner .bbtn, #banner nav a { padding: .3rem .45rem; font-size: .88rem; }
  .hide-sm { display: none; }
  .show-sm { display: inline; }
  /* pages racines du visualiseur : véritable second bandeau en portrait.
     Spécificité #banner #root-nav requise pour battre "#banner nav{flex:1}". */
  #banner #root-nav { order: 10; flex: 0 0 100%;
    margin: .3rem -.8rem -.45rem; padding: .3rem .8rem;
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--panel-2) 55%, transparent); }
  #banner #root-nav:empty { display: none; }
  /* Boutons de plage des graphes : ~10 % de moins en largeur et hauteur pour
     tenir sur 2 lignes maxi face au titre. */
  .chart-head button { padding: .12rem .42rem; font-size: .7rem; }
}
@media (prefers-reduced-motion: no-preference) {
  .card { transition: border-color .15s, transform .15s; }
}
