/* =============================================================================
   Charge Planner — feuille de style unique, pensée mobile d'abord.
   Palette pilotée par variables : le thème sombre n'est qu'une redéfinition.
   ========================================================================== */

:root {
    color-scheme: light dark;

    --bg: #f4f6fb;
    --bg-elevated: #ffffff;
    --bg-sunken: #e9edf5;
    --border: #dde3ee;
    --text: #131722;
    --text-muted: #5c667d;
    --accent: #1f6feb;
    --accent-contrast: #ffffff;
    --accent-soft: #e6efff;
    --success: #0e8a5f;
    --success-soft: #dff5eb;
    --warning: #b45309;
    --warning-soft: #fdf0dc;
    --danger: #c2334d;
    --danger-soft: #fde8ec;

    --radius: 14px;
    --radius-sm: 9px;
    --shadow: 0 1px 2px rgb(16 24 40 / 6%), 0 8px 24px rgb(16 24 40 / 6%);
    --shadow-sm: 0 1px 2px rgb(16 24 40 / 8%);

    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --nav-height: 62px;
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: #0b1020;
        --bg-elevated: #151b2e;
        --bg-sunken: #0f1526;
        --border: #262f47;
        --text: #eef2fb;
        --text-muted: #98a3bd;
        --accent: #5b9bff;
        --accent-contrast: #06101f;
        --accent-soft: #17233d;
        --success: #34d399;
        --success-soft: #12291f;
        --warning: #fbbf24;
        --warning-soft: #2c2313;
        --danger: #fb7185;
        --danger-soft: #2e1620;
        --shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 10px 30px rgb(0 0 0 / 35%);
        --shadow-sm: 0 1px 2px rgb(0 0 0 / 45%);
    }
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    font-size: 16px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    margin: 0;
    line-height: 1.25;
    letter-spacing: -0.015em;
}

h1 { font-size: 1.35rem; font-weight: 650; }
h2 { font-size: 1.05rem; font-weight: 620; }
h3 { font-size: 0.95rem; font-weight: 600; }

a { color: var(--accent); }

/* --- Ossature ------------------------------------------------------------- */

.app-header {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
}

.app-header .brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 650;
}

/* Règle non scopée à l'en-tête : la même marque sert aussi les écrans de connexion et
   d'installation. L'image porte son propre fond à bord perdu, seuls les coins restent à
   arrondir ici — ce que l'icône ne fait délibérément pas elle-même (voir icon.svg). */
.bolt {
    display: block;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 9px;
}

main {
    display: block;
    max-width: 720px;
    margin: 0 auto;
    padding: 16px 16px calc(var(--nav-height) + var(--safe-bottom) + 24px);
}

.stack { display: grid; gap: 14px; }
.stack-sm { display: grid; gap: 8px; }

/* --- Navigation basse ----------------------------------------------------- */

.app-nav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 30;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    padding-bottom: var(--safe-bottom);
    background: color-mix(in srgb, var(--bg-elevated) 92%, transparent);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--border);
}

.app-nav a {
    display: grid;
    place-items: center;
    gap: 2px;
    height: var(--nav-height);
    color: var(--text-muted);
    font-size: 0.72rem;
    font-weight: 550;
    text-decoration: none;
}

.app-nav a .glyph { font-size: 1.15rem; line-height: 1; }
.app-nav a.active { color: var(--accent); }

/* --- Cartes --------------------------------------------------------------- */

.card {
    padding: 16px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 12px;
}

.card.highlight {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
    box-shadow: var(--shadow);
}

/* --- Formulaires ---------------------------------------------------------- */

.field { display: grid; gap: 6px; }

.field > label,
.field-label {
    color: var(--text-muted);
    font-size: 0.8rem;
    font-weight: 560;
}

.grid-2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
    /* Sans cet alignement, les deux colonnes s'étirent sur la hauteur de la plus haute : dès
       qu'un seul des deux champs porte une explication sous lui, l'autre voit sa zone de saisie
       grandir d'autant, et la paire n'a plus l'air d'être la même chose deux fois. */
    align-items: start;
}

/* Case à cocher et son libellé sur une ligne, la case alignée sur la première ligne de texte
   et non centrée sur un libellé qui peut tenir sur trois lignes. */
.check {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    cursor: pointer;
}

.check input[type="checkbox"] {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin-top: 1px;
    accent-color: var(--accent);
}

.check > span { display: grid; gap: 2px; }

input[type="text"],
input[type="number"],
input[type="password"],
input[type="time"],
input[type="date"],
select,
textarea {
    width: 100%;
    padding: 11px 12px;
    background: var(--bg-sunken);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: 1rem; /* 16px : empêche iOS de zoomer au focus */
}

input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Safari iOS centre mal le contenu d'un champ heure sans hauteur explicite. */
input[type="time"] {
    min-height: 44px;
    appearance: none;
}

input[type="range"] {
    width: 100%;
    height: 28px;
    accent-color: var(--accent);
    background: transparent;
}

.slider-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}

.slider-value {
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    font-size: 1.05rem;
}

/* --- Curseur double : une piste, deux poignées ----------------------------- */

/*
   Deux <input type="range"> superposés sur la même piste. C'est le seul montage qui
   conserve des poignées natives — donc le clavier, l'ordre de tabulation, le focus
   et la restitution vocale — là où un composant maison en <div> devrait tout
   réimplémenter, et le ferait moins bien.

   Les entrées ne captent pas le pointeur ; seules leurs poignées le font. Sans cette
   inversion, celle du dessus intercepterait toute la largeur de la piste et l'autre
   serait définitivement inatteignable. Contrepartie assumée : un appui sur la piste
   nue ne déplace plus rien, faute de connaître sa largeur côté serveur.
*/
.range-dual {
    --thumb: 26px;
    position: relative;
    display: flex;
    align-items: center;
    height: var(--thumb);
}

.range-dual .range-track,
.range-dual .range-fill {
    position: absolute;
    height: 6px;
    border-radius: 999px;
    pointer-events: none;
}

.range-dual .range-track {
    left: 0;
    right: 0;
    background: var(--bg-sunken);
    border: 1px solid var(--border);
}

/*
   Le centre d'une poignée ne parcourt pas toute la largeur : il va de thumb/2 à
   largeur − thumb/2. Un remplissage placé au pourcentage brut se décalerait donc
   d'une demi-poignée aux extrémités, d'où la conversion explicite. --from et --to
   sont des nombres nus (0 à 100) posés par le composant.
*/
.range-dual .range-fill {
    left: calc(var(--from) * (100% - var(--thumb)) / 100 + var(--thumb) / 2);
    right: calc((100 - var(--to)) * (100% - var(--thumb)) / 100 + var(--thumb) / 2);
    background: var(--accent);
}

.range-dual input[type="range"] {
    position: absolute;
    left: 0;
    right: 0;
    width: 100%;
    height: var(--thumb);
    margin: 0;
    padding: 0;
    appearance: none;
    background: transparent;
    pointer-events: none;
}

/* La poignée de départ passe devant dans la moitié haute de la plage : c'est là
   qu'elle risque d'être recouverte par la cible plaquée contre 100 %. */
.range-dual input[type="range"].on-top { z-index: 1; }

.range-dual input[type="range"]::-webkit-slider-runnable-track {
    height: var(--thumb);
    background: transparent;
}

.range-dual input[type="range"]::-moz-range-track {
    height: var(--thumb);
    background: transparent;
}

/* Un pseudo-élément inconnu invalide la liste de sélecteurs entière : les variantes
   WebKit et Gecko ne peuvent pas être groupées, elles doivent être dupliquées. */
.range-dual input[type="range"]::-webkit-slider-thumb {
    appearance: none;
    pointer-events: auto;
    box-sizing: border-box;
    width: var(--thumb);
    height: var(--thumb);
    border: 3px solid var(--accent);
    border-radius: 50%;
    background: var(--bg-elevated);
    box-shadow: var(--shadow-sm);
    cursor: grab;
}

.range-dual input[type="range"]::-moz-range-thumb {
    pointer-events: auto;
    box-sizing: border-box;
    width: var(--thumb);
    height: var(--thumb);
    border: 3px solid var(--accent);
    border-radius: 50%;
    background: var(--bg-elevated);
    box-shadow: var(--shadow-sm);
    cursor: grab;
}

/* La règle générique de focus dessinerait un cadre autour de toute la piste ; ici
   c'est la poignée qui a le focus, c'est donc elle qui doit le montrer. */
.range-dual input[type="range"]:focus-visible { outline: none; }

.range-dual input[type="range"]:focus-visible::-webkit-slider-thumb {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.range-dual input[type="range"]:focus-visible::-moz-range-thumb {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.range-legend {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    color: var(--text-muted);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}

.range-legend .range-span { color: var(--text); font-weight: 560; }

.validation-message,
.field-error {
    color: var(--danger);
    font-size: 0.8rem;
}

/* --- Boutons -------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 44px; /* cible tactile confortable */
    padding: 10px 16px;
    background: var(--bg-sunken);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    font-weight: 580;
    text-decoration: none;
    cursor: pointer;
}

.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
.btn-danger { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-muted); }
.btn-block { width: 100%; }
.btn-sm { min-height: 36px; padding: 6px 12px; font-size: 0.85rem; }

.btn-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* --- Badges et pastilles -------------------------------------------------- */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 620;
    white-space: nowrap;
}

.badge-accent { background: var(--accent-soft); color: var(--accent); }
.badge-success { background: var(--success-soft); color: var(--success); }
.badge-warning { background: var(--warning-soft); color: var(--warning); }
.badge-muted { background: var(--bg-sunken); color: var(--text-muted); }

/* --- Résultats de comparaison --------------------------------------------- */

.result {
    display: grid;
    gap: 12px;
    padding: 15px 16px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}

.result.best {
    border-color: color-mix(in srgb, var(--success) 55%, var(--border));
    background:
        linear-gradient(180deg, color-mix(in srgb, var(--success) 8%, transparent), transparent 45%),
        var(--bg-elevated);
}

.result-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.result-name { font-weight: 640; }

.result-sub {
    color: var(--text-muted);
    font-size: 0.82rem;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.result-figures {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
}

.figure {
    display: grid;
    gap: 2px;
    padding: 10px 12px;
    background: var(--bg-sunken);
    border-radius: var(--radius-sm);
}

.figure .label { color: var(--text-muted); font-size: 0.72rem; font-weight: 560; }
.figure .value { font-size: 1.22rem; font-weight: 660; font-variant-numeric: tabular-nums; }
.figure .hint { color: var(--text-muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; }

.breakdown {
    display: grid;
    gap: 4px;
    padding-top: 10px;
    border-top: 1px dashed var(--border);
    color: var(--text-muted);
    font-size: 0.8rem;
}

.breakdown div {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    font-variant-numeric: tabular-nums;
}

.breakdown .total { color: var(--text); font-weight: 620; }

details.details > summary {
    cursor: pointer;
    color: var(--text-muted);
    font-size: 0.8rem;
    font-weight: 560;
    list-style: none;
}

details.details > summary::-webkit-details-marker { display: none; }
details.details > summary::after { content: " ▾"; }
details.details[open] > summary::after { content: " ▴"; }

/* --- Historique des charges ----------------------------------------------- */

/* En-tête de mois : un bouton, et non un <details>, parce que le pli est piloté depuis
   le code (le mois le plus récent s'ouvre d'office, et le pli suit la charge qu'on
   vient d'enregistrer). Les styles neutralisent l'apparence native du bouton. */
.month-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    padding: 0;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.month-name {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 1.05rem;
    font-weight: 620;
    text-transform: capitalize;
}

.month-caret { color: var(--text-muted); font-size: 0.8rem; }

.month-figures {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    font-size: 0.82rem;
    font-weight: 560;
}

.session-list { margin-top: 12px; }

.session {
    display: grid;
    gap: 4px;
    padding: 10px 12px;
    background: var(--bg-sunken);
    border-radius: var(--radius-sm);
}

.session-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.session-name { font-weight: 580; }
.session-cost { font-weight: 640; }

.session-meta {
    color: var(--text-muted);
    font-size: 0.78rem;
}

.session .btn-row { margin-top: 6px; }

/* Le bandeau de suggestion tarifaire porte un bouton : il ne peut pas rester un bloc de
   texte comme les autres alertes. */
.alert-suggest {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

/* --- Divers --------------------------------------------------------------- */

/* --- Import depuis le catalogue ------------------------------------------- */

.search-row {
    display: flex;
    gap: 8px;
}

.search-row input { flex: 1 1 auto; }

input[type="search"] {
    width: 100%;
    padding: 11px 12px;
    background: var(--bg-sunken);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: 1rem;
}

.catalog-hit {
    display: grid;
    gap: 2px;
    width: 100%;
    padding: 10px 12px;
    text-align: left;
    background: var(--bg-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    cursor: pointer;
}

.catalog-hit:hover { border-color: var(--accent); }
.catalog-name { font-weight: 580; }
.catalog-specs { color: var(--text-muted); font-size: 0.78rem; font-variant-numeric: tabular-nums; }

/* Tarif déclaré par l'opérateur : information, jamais valeur de calcul. Passé la date de
   péremption il vire à l'ambre — une grille de trois ans ne vaut pas mieux qu'aucune. */
.catalog-tariff {
    color: var(--success);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}

.catalog-tariff.stale { color: var(--warning); }

/* --- État d'une opération distante ---------------------------------------- */

/*
   Le fond teinté porte le sens autant que la couleur du texte : un daltonisme rouge-vert
   distingue mal l'ambre du vert, mais la présence d'un bandeau et son pictogramme restent
   lisibles — et le texte, lui, dit toujours explicitement ce qui se passe.
*/
.op-status {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
}

.op-glyph { flex: 0 0 auto; }
.op-text { min-width: 0; }

.op-running {
    background: var(--bg-sunken);
    color: var(--text-muted);
}

.op-succeeded {
    background: var(--success-soft);
    color: var(--success);
}

.op-failed {
    background: var(--warning-soft);
    color: var(--warning);
}

/* --- Interactivité absente ------------------------------------------------ */

/*
   Replié à zéro puis révélé après huit secondes, sans une ligne de JavaScript. Le délai est
   la partie utile : le bandeau est présent dans le HTML prérendu de chaque page, et sur un
   téléphone en 4G le circuit met parfois deux ou trois secondes à s'ouvrir. L'afficher
   d'emblée le ferait clignoter à chaque navigation.

   Ni « display » ni « visibility » ne conviennent ici : le premier ne s'animait pas de façon
   fiable, le second laisse la place occupée pendant huit secondes. max-height et padding à
   zéro, avec overflow caché, ne prennent aucune hauteur.
*/
.circuit-warning {
    overflow: hidden;
    max-height: 0;
    padding: 0 12px;
    opacity: 0;
    border-radius: var(--radius-sm);
    background: var(--warning-soft);
    color: var(--warning);
    font-size: 0.85rem;
    animation: circuit-warning-reveal 200ms ease-out 8s forwards;
}

.circuit-warning code {
    font-size: 0.95em;
    /* Le fond du bandeau est déjà teinté : un second aplat rendrait le code illisible. */
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    padding: 1px 4px;
    border-radius: 4px;
}

@keyframes circuit-warning-reveal {
    to {
        max-height: 14rem;
        padding: 11px 12px;
        margin-bottom: 14px;
        opacity: 1;
    }
}

/* --- Éditeur de courbe de charge ------------------------------------------ */

.curve {
    margin: 0;
    display: grid;
    gap: 6px;
}

/* Graduations rendues en HTML plutôt qu'en <text> SVG : Razor réserve cette balise,
   et le texte HTML reste sélectionnable et lisible par les lecteurs d'écran. */
.curve-plot {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px;
    align-items: stretch;
}

.curve-scale {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 2px 0 10px;
    color: var(--text-muted);
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.curve-ticks {
    display: flex;
    justify-content: space-between;
    padding-left: 34px;
    color: var(--text-muted);
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
}

.curve svg {
    width: 100%;
    height: auto;
    background: var(--bg-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.curve-axis { stroke: var(--text-muted); stroke-width: 1; opacity: 0.5; }
.curve-grid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; }
.curve-line { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.curve-dot { fill: var(--accent); }

.curve-editor {
    display: grid;
    gap: 8px;
}

.curve-row {
    display: grid;
    grid-template-columns: 1fr 1fr auto;
    gap: 8px;
    align-items: center;
}

.curve-head {
    color: var(--text-muted);
    font-size: 0.75rem;
    font-weight: 560;
}

.input-suffix {
    display: flex;
    align-items: center;
    gap: 6px;
}

.input-suffix span {
    color: var(--text-muted);
    font-size: 0.78rem;
    white-space: nowrap;
}

.card-actions { margin-top: 12px; }
.glyph-lg { font-size: 2rem; line-height: 1; }
.muted { color: var(--text-muted); }
.small { font-size: 0.82rem; }
.nums { font-variant-numeric: tabular-nums; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

.empty {
    display: grid;
    gap: 10px;
    justify-items: center;
    padding: 32px 20px;
    text-align: center;
    color: var(--text-muted);
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
}

.alert {
    padding: 11px 13px;
    border-radius: var(--radius-sm);
    font-size: 0.88rem;
}

.alert-danger { background: var(--danger-soft); color: var(--danger); }
.alert-success { background: var(--success-soft); color: var(--success); }
.alert-info { background: var(--accent-soft); color: var(--accent); }
.alert-warning { background: var(--warning-soft); color: var(--warning); }

/* --- Écrans d'authentification -------------------------------------------- */

.auth-shell {
    display: grid;
    place-items: center;
    min-height: 100dvh;
    padding: 24px 18px;
}

.auth-card {
    width: 100%;
    max-width: 400px;
    display: grid;
    gap: 16px;
    padding: 26px 22px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.auth-card .lead { color: var(--text-muted); font-size: 0.88rem; }

/* La marque devient une ligne à part entière : l'icône ne se cale pas sur la ligne de base du
   texte comme le faisait l'emoji, elle se centre sur sa hauteur. */
.auth-brand {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* --- Blazor : bandeau d'erreur et modale de reconnexion ------------------- */

#blazor-error-ui {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1000;
    display: none;
    padding: 14px 18px;
    background: var(--warning-soft);
    color: var(--warning);
    box-shadow: var(--shadow);
    font-size: 0.88rem;
}

#blazor-error-ui .dismiss {
    float: right;
    cursor: pointer;
    font-weight: 700;
}

@media (min-width: 640px) {
    .result-figures { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}
