/* Thermostat console — dark petrol ground, ember for heat, glacier for cool.
   The colour of the interface always means something: it is the state of the
   system, never decoration. */

:root {
    --ground:      #0E1A1C;
    --ground-2:    #142427;
    --ground-3:    #1B3035;
    --line:        #27454B;
    --ink:         #EAF2F1;
    --ink-dim:     #7E9B9E;
    --ember:       #FF7A3D;
    --glacier:     #5EC8EF;
    --eco:         #86D69B;
    --danger:      #F2565C;
    --violet:      #B08CE8;
    --amber:       #E8C46A;
    --magenta:     #DE7FB5;
    /* Deeper twins of ember and glacier, for filled surfaces where the bright
       versions would glare. */
    --ember-deep:  #C4531F;
    --glacier-deep: #2C7E9E;
    --accent:      var(--ink-dim);

    --font-display: 'Archivo', 'Helvetica Neue', system-ui, sans-serif;
    --font-body:    'Public Sans', system-ui, sans-serif;
    --font-data:    'DM Mono', ui-monospace, monospace;

    --radius: 3px;
    --gap: 20px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
}

/* The ambient wash: readable from across the room, like the ring on the
   thermostat itself. Driven by [data-hvac] on <body>. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(120% 55% at 50% 0%, transparent 40%, transparent 100%);
    transition: background 900ms ease;
    z-index: 0;
}
body[data-hvac="HEATING"]::before {
    background: radial-gradient(120% 55% at 50% 0%, rgba(255, 122, 61, 0.16) 0%, transparent 62%);
}
body[data-hvac="COOLING"]::before {
    background: radial-gradient(120% 55% at 50% 0%, rgba(94, 200, 239, 0.16) 0%, transparent 62%);
}

.app, .gate, .notice {
    position: relative;
    z-index: 1;
}

/* --- Gate and notice screens ------------------------------------------- */

.gate, .notice {
    max-width: 420px;
    margin: 0 auto;
    padding: 22vh 24px 40px;
    text-align: left;
}

.gate-eyebrow, .notice-eyebrow {
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin: 0 0 12px;
}

.gate-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 30px;
    line-height: 1.1;
    margin: 0 0 24px;
}

.gate-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.notice-body {
    margin: 0 0 24px;
    line-height: 1.55;
    color: var(--ink);
}

.gate-error {
    margin-top: 14px;
    font-family: var(--font-data);
    font-size: 13px;
    color: var(--ember);
}

/* Every text-like field shares one look. Listing types by name means a new
   one — email here — silently falls back to the browser's default until it is
   added, which is exactly what happened. */
input[type="password"], input[type="text"], input[type="email"],
input[type="time"], input[type="number"], input[type="date"],
input[type="search"], input[type="tel"], input[type="url"], select, textarea {
    width: 100%;
    padding: 12px 14px;
    background: var(--ground-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 16px;
}

input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
    outline: 2px solid var(--glacier);
    outline-offset: 2px;
}

.button {
    display: inline-block;
    padding: 12px 20px;
    background: var(--ink);
    color: var(--ground);
    border: none;
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-weight: 600;
    font-size: 15px;
    text-decoration: none;
    text-align: center;
    cursor: pointer;
}
.button:hover { background: #ffffff; }
.button-small { padding: 7px 12px; font-size: 13px; }

.link-quiet {
    display: inline-block;
    margin-top: 16px;
    color: var(--ink-dim);
    font-size: 13px;
    text-decoration: none;
    border-bottom: 1px solid var(--line);
    background: none;
    border-width: 0 0 1px 0;
    padding: 0 0 2px;
    cursor: pointer;
    font-family: var(--font-body);
}
.link-quiet:hover { color: var(--ink); }

/* --- Application shell -------------------------------------------------- */

.app {
    max-width: 760px;
    margin: 0 auto;
    padding: 18px 20px 60px;
}

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--line);
}

.topbar-identity {
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
}

.status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--ink-dim);
    flex: none;
    align-self: center;
}
.status-dot[data-online="true"] { background: var(--eco); }

.device-label {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 15px;
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.device-room {
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
    letter-spacing: 0.06em;
}

.clock {
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--glacier);
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.custom-duration {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}
.custom-duration input {
    width: 92px;
    padding: 8px 10px;
    font-family: var(--font-data);
    font-size: 14px;
}

.topbar-meta {
    display: flex;
    align-items: center;
    gap: 14px;
    flex: none;
}
.topbar-meta .link-quiet { margin-top: 0; }

/* --- Section navigation -------------------------------------------------- */

/* Sticky: a menu you have to scroll back up to reach is not a menu. */
.section-nav {
    position: sticky;
    top: 0;
    z-index: 6;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0 -20px;
    padding: 8px 20px;
    background: var(--ground);
    border-bottom: 1px solid var(--line);
}

.section-nav a {
    padding: 6px 10px;
    border-radius: var(--radius);
    color: var(--ink-dim);
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.04em;
    text-decoration: none;
    white-space: nowrap;
}
.section-nav a:hover { color: var(--ink); background: var(--ground-2); }
.section-nav a[aria-current="true"] {
    color: var(--ink);
    background: var(--ground-3);
}

/* A plain marker. The offset used to live here AND in scroll-padding-top,
   which double-counted it and left a hundred-pixel gap above the heading.
   The scroll is now computed in JS from the bar's measured height. */
.anchor {
    display: block;
    height: 0;
    visibility: hidden;
}

.section-head {
    margin-top: 22px;
    padding-bottom: 2px;
}
.section-head h2 {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 15px;
    letter-spacing: 0.02em;
    margin: 0;
}

/* Scrolling is driven from JS so the offset can follow the real bar height;
   scroll-padding covers the cases JS does not handle, such as a pasted #hash. */
html { scroll-padding-top: 56px; }

/* --- Schedule pause ------------------------------------------------------ */

/* Red, not green: a paused schedule is an exception state, and the banner
   should read as "something is off" rather than "all good". */
/* Loud on purpose: while the schedule is paused, every setpoint drawn further
   down is a plan that will not run.
   It follows the page too, riding just under the navigation: scroll far enough
   to reach the schedule grid and the warning about that grid would otherwise
   be off screen. --nav-height is measured in JS because the bar wraps to two
   rows on a narrow screen. */
.pause-banner {
    position: sticky;
    top: var(--nav-height, 44px);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 10px 14px;
    flex-wrap: wrap;
    margin-top: 16px;
    padding: 12px 14px;
    border: 1px solid var(--danger);
    border-left-width: 4px;
    border-radius: var(--radius);
    /* Opaque: content scrolls underneath it. */
    background: #2A1A1D;
    box-shadow: 0 6px 14px rgba(14, 26, 28, 0.55);
}

/* On a phone the banner stacks and, pinned, ate a third of the screen. The
   consequence sentence is dropped there — the badge and the resume button
   carry the message, and the full text is still at the top of the page. */
@media (max-width: 620px) {
    .pause-banner {
        gap: 8px 10px;
        padding: 9px 12px;
    }
    .pause-text { flex-basis: 100%; font-size: 13px; }
    .pause-text .pause-consequence { display: none; }
    .pause-resume { padding: 6px 12px; font-size: 11px; }
}

.pause-badge {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--ground);
    background: var(--danger);
    padding: 4px 9px;
    border-radius: var(--radius);
    white-space: nowrap;
}

.pause-text {
    flex: 1 1 280px;
    font-size: 14px;
    line-height: 1.55;
    color: var(--ink);
}
.pause-text b { color: var(--ink); font-weight: 600; }

/* The button is the way OUT of the exception, so it stays green. */
.pause-resume {
    padding: 6px 12px;
    border: 1px solid var(--eco);
    background: transparent;
    color: var(--eco);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    cursor: pointer;
    flex: none;
}
.pause-resume:hover { background: rgba(134, 214, 155, 0.16); }

/* The dot that used to mark this line is gone: the badge beside it now says
   the same thing in words, and two markers for one state is one too many. */

.pause-choices {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.pause-choice {
    padding: 9px 13px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 12px;
    cursor: pointer;
}
.pause-choice[aria-pressed="true"] {
    background: var(--ground-3);
    border-color: var(--ink-dim);
    color: var(--ink);
}

/* --- Alerts ---------------------------------------------------------------- */

.conditions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.condition-row {
    display: grid;
    grid-template-columns: 74px minmax(0, 1fr) 82px minmax(0, 1fr) auto;
    gap: 8px;
    align-items: center;
}

.condition-join {
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.08em;
    color: var(--ink-dim);
    text-align: center;
}
/* Room for the arrow: at 54 px the browser clipped "ET" to a stub. */
select.condition-join { padding: 8px 6px; text-align: left; font-size: 12px; }

/* Actions carry the colour of what they do: amber suspends, green resumes,
   blue edits. */
.rule-actions .act-pause { color: var(--amber); border-color: rgba(232, 196, 106, 0.4); }
.rule-actions .act-pause:hover { color: var(--amber); border-color: var(--amber); }

.rule-actions .act-resume { color: var(--eco); border-color: rgba(134, 214, 155, 0.4); }
.rule-actions .act-resume:hover { color: var(--eco); border-color: var(--eco); }

.rule-actions .act-edit { color: var(--glacier); border-color: rgba(94, 200, 239, 0.4); }
.rule-actions .act-edit:hover { color: var(--glacier); border-color: var(--glacier); }

/* Three states, three colours, no two alike:
     vert   ACTIVE       — checked and delivered
     ambre  EN PAUSE     — suspended here, on purpose, one click from running
     rouge  NON ENVOYÉE  — the recipient unsubscribed; needs someone's attention
   Paused and blocked shared a colour and were mistaken for each other. */
.alert-badge[data-blocked="true"] {
    color: var(--danger);
    border-color: var(--danger);
    background: rgba(242, 86, 92, 0.14);
}

.alert-blocked {
    margin-top: 6px;
    padding: 7px 10px;
    border-radius: var(--radius);
    border-left: 3px solid var(--amber);
    background: rgba(232, 196, 106, 0.08);
    font-size: 12px;
    color: var(--ink);
}

.link-inline {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--glacier);
    cursor: pointer;
    text-decoration: underline;
}
.link-inline:hover { color: var(--ink); }

.alert-id {
    margin-left: 8px;
    font-family: var(--font-data);
    font-size: 10px;
    color: var(--ink-dim);
    letter-spacing: 0.06em;
}

.alert-kind {
    margin-left: 8px;
    padding: 2px 7px;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.06em;
    color: var(--ink-dim);
    vertical-align: 2px;
}

.alert-note {
    font-size: 12px;
    color: var(--ink);
    background: var(--ground-3);
    border-radius: var(--radius);
    padding: 6px 9px;
    margin-top: 6px;
    display: inline-block;
}

.condition-row select,
.condition-row input {
    width: 100%;
    padding: 8px 10px;
    font-size: 13px;
}

.condition-drop {
    padding: 8px 10px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    cursor: pointer;
    line-height: 1;
}
.condition-drop:hover { color: var(--danger); border-color: var(--danger); }

/* Its own columns: the shared .rule grid reserves a 62 px slot for a time,
   which squeezed the alert's text into one word per line. */
.alert-row {
    grid-template-columns: 104px minmax(0, 1fr) auto;
    align-items: start;
}
.alert-row .rule-name { margin-bottom: 2px; }
.alert-state { padding-top: 2px; }

.alert-badge {
    display: inline-block;
    padding: 3px 8px;
    border-radius: var(--radius);
    /* Amber by default, which is the paused state: suspended on purpose, and
       one click from running again. Grey read as broken. */
    border: 1px solid rgba(232, 196, 106, 0.5);
    background: rgba(232, 196, 106, 0.10);
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.1em;
    font-weight: 600;
    color: var(--amber);
}
.alert-badge[data-on="true"] {
    color: var(--eco);
    border-color: var(--eco);
    background: rgba(134, 214, 155, 0.16);
}

/* In the blocked list these badges name a scope, not a state; amber there
   would suggest something is wrong with each entry. */
#blocklist .alert-badge {
    color: var(--ink-dim);
    border-color: var(--line);
    background: none;
}

.alert-row[data-enabled="false"] .rule-name,
.alert-row[data-enabled="false"] .rule-detail { opacity: 0.55; }


.alert-fired {
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}
.alert-check { font-family: var(--font-data); font-size: 12px; }
.alert-check[data-fires="true"] { color: var(--ember); }
.alert-check[data-fires="false"] { color: var(--eco); }

/* --- Quick setpoints ------------------------------------------------------ */

.quick-temps {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
}

.quick-label {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

.quick-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.quick-chip {
    padding: 8px 14px;
    border: 1px solid var(--line);
    background: var(--ground-2);
    color: var(--ink);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 14px;
    cursor: pointer;
    font-variant-numeric: tabular-nums;
}
.quick-chip:hover { border-color: var(--accent, var(--ink-dim)); }

.quick-add {
    color: var(--ink-dim);
    padding: 8px 12px;
}
.quick-add:hover { color: var(--ink); }

/* A custom value can be removed; a schedule-derived one cannot, since it
   simply reflects what the schedule uses. */
.quick-chip[data-custom="true"] { border-style: dashed; }

#quick-pause { margin-left: auto; }

/* The one already in force reads as a state, not an offer. */
.quick-chip[aria-pressed="true"] {
    background: var(--accent, var(--ink));
    border-color: var(--accent, var(--ink));
    color: var(--ground);
    font-weight: 600;
    cursor: default;
}

/* --- Summary --------------------------------------------------------------- */

/* The first thing read on the page: what the house is doing, what changes
   next, and what the weather will ask of the system. */
.summary {
    position: relative;
    margin-top: 12px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent, var(--ink-dim));
    border-radius: var(--radius);
    background: var(--ground-2);
}

/* Added only while the last section is being read, so the page does not
   permanently end in a field of nothing. */
.scroll-spacer { height: 0; }

.summary-copy {
    position: absolute;
    top: 10px;
    right: 10px;
    display: inline-flex;
    padding: 6px;
    border: 1px solid var(--line);
    background: var(--ground-2);
    color: var(--ink-dim);
    border-radius: var(--radius);
    cursor: pointer;
    line-height: 0;
}
.summary-copy:hover { color: var(--ink); border-color: var(--ink-dim); }
.summary-copy[data-done="true"] { color: var(--eco); border-color: var(--eco); }

.summary-line {
    margin: 0;
    padding-right: 34px;
    font-size: 15px;
    line-height: 1.6;
    color: var(--ink);
}
.summary-line + .summary-line { margin-top: 7px; }

.summary-next, .summary-weather {
    font-size: 14px;
    color: var(--ink-dim);
}
.summary-line b {
    font-weight: 600;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
.summary-line .accent { color: var(--accent, var(--ink)); }
.summary-line .warn {
    color: var(--amber);
    border-bottom: 1px dotted rgba(232, 196, 106, 0.5);
}
.summary-line svg {
    vertical-align: -4px;
    margin: 0 2px;
}

.date-bar-spacer { flex: 1 1 auto; }

/* Reserved line: it fills as the pointer moves over the connector, so the
   drawing can be read without waiting for a native tooltip. */
.wiring-readout {
    margin: 4px 0 0;
    min-height: 20px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}
.wiring-readout b { color: var(--ink); font-weight: 600; }
.wiring-readout .wire-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--wire, var(--ink-dim));
    margin-right: 6px;
    vertical-align: -1px;
}

#event-more { margin-top: 10px; }

.legend-box {
    margin-top: 18px;
    border-top: 1px solid var(--line);
    padding-top: 12px;
}
.legend-box summary {
    cursor: pointer;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
    list-style: none;
}
.legend-box summary::before { content: '▸ '; }
.legend-box[open] summary::before { content: '▾ '; }
.legend-box summary:hover { color: var(--ink); }

.legend-list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 14px;
    margin: 14px 0 0;
    font-size: 13px;
}
.legend-list dt {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink);
    white-space: nowrap;
}
.legend-list dt::before {
    content: '';
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--wire, var(--ink-dim));
}
.legend-list dd { margin: 0; color: var(--ink-dim); }
.legend-list dt[data-wired="true"] { color: var(--ink); }
.legend-list dt[data-wired="false"] { opacity: 0.5; }
.legend-list dd[data-wired="false"] { opacity: 0.5; }

/* --- Console: gauge column + controls ---------------------------------- */

.console {
    display: grid;
    grid-template-columns: 140px 1fr;
    gap: 26px;
    padding: 26px 0 4px;
    align-items: stretch;
}

.gauge {
    width: 100%;
    height: 100%;
    max-height: 420px;
    display: block;
}

.controls {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 26px;
    min-width: 0;
}

.readout-eyebrow, .setpoint-eyebrow {
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin: 0 0 4px;
}

.readout-ambient {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: clamp(56px, 16vw, 88px);
    line-height: 0.9;
    margin: 0;
    font-variant-numeric: tabular-nums;
}

.readout-ambient .degree,
.setpoint-value .degree {
    color: var(--accent);
    margin-left: 2px;
}

.readout-state {
    margin: 10px 0 0;
    font-family: var(--font-data);
    font-size: 13px;
    letter-spacing: 0.04em;
    color: var(--accent);
}

.setpoint-row {
    display: flex;
    align-items: center;
    gap: 14px;
}

.setpoint-value {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 40px;
    line-height: 1;
    margin: 0;
    font-variant-numeric: tabular-nums;
    min-width: 96px;
}

.stepper {
    width: 52px;
    height: 52px;
    border: 1px solid var(--line);
    background: var(--ground-2);
    color: var(--ink);
    border-radius: var(--radius);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
    font-family: var(--font-body);
}
.stepper:hover { background: var(--ground-3); }
.stepper:disabled { opacity: 0.35; cursor: not-allowed; }

.range-row {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

.range-chip {
    padding: 7px 12px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 12px;
    cursor: pointer;
}
.range-chip[aria-pressed="true"] {
    border-color: var(--accent);
    color: var(--ink);
}

.modes {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

/* Each mode carries its own colour: ember heats, glacier cools, green saves,
   grey stops. Inactive modes are tinted, the active one is filled solid. */
.mode-button {
    padding: 9px 15px;
    border: 1px solid var(--mode-edge, var(--line));
    background: var(--mode-tint, transparent);
    color: var(--mode-color, var(--ink-dim));
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-size: 14px;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease;
}
.mode-button:hover {
    background: var(--mode-tint-strong, var(--ground-3));
    border-color: var(--mode-color, var(--ink-dim));
}
/* The active mode is physically larger, not just filled: at a glance across a
   room the size difference carries further than the colour. */
.mode-button[aria-pressed="true"] {
    background: var(--mode-color, var(--ink));
    border-color: var(--mode-color, var(--ink));
    color: var(--ground);
    font-weight: 600;
    font-size: 16px;
    padding: 12px 20px;
    box-shadow: 0 0 0 3px var(--mode-tint, rgba(255, 255, 255, 0.10));
}

.mode-button[data-mode="HEAT"] {
    --mode-color: var(--ember);
    --mode-tint: rgba(255, 122, 61, 0.14);
    --mode-tint-strong: rgba(255, 122, 61, 0.26);
    --mode-edge: rgba(255, 122, 61, 0.45);
}
.mode-button[data-mode="COOL"] {
    --mode-color: var(--glacier);
    --mode-tint: rgba(94, 200, 239, 0.14);
    --mode-tint-strong: rgba(94, 200, 239, 0.26);
    --mode-edge: rgba(94, 200, 239, 0.45);
}
/* Separates the stop button from the running modes. */
.mode-gap {
    width: 1px;
    align-self: stretch;
    background: var(--line);
    margin: 4px 6px;
}

/* Red for Arrêt: it is the only mode that stops the system outright. It also
   sits slightly back — a way out rather than one of the choices being weighed
   against each other. */
.mode-button[data-mode="OFF"] {
    font-size: 13px;
    padding: 9px 14px;
    opacity: 0.82;
    --mode-color: var(--danger);
    --mode-tint: rgba(242, 86, 92, 0.14);
    --mode-tint-strong: rgba(242, 86, 92, 0.26);
    --mode-edge: rgba(242, 86, 92, 0.45);
}
.mode-button[data-mode="OFF"]:hover,
.mode-button[data-mode="OFF"][aria-pressed="true"] { opacity: 1; }

.mode-button[data-mode="ECO"] {
    --mode-color: var(--eco);
    --mode-tint: rgba(134, 214, 155, 0.14);
    --mode-tint-strong: rgba(134, 214, 155, 0.26);
    --mode-edge: rgba(134, 214, 155, 0.45);
}

/* Auto runs both ways, so the button itself runs ember to glacier. */
.mode-button[data-mode="HEATCOOL"] {
    --mode-color: var(--ink);
    --mode-edge: rgba(126, 155, 158, 0.45);
    background: linear-gradient(100deg, rgba(196, 83, 31, 0.42), rgba(44, 126, 158, 0.42));
    color: var(--ink);
}
.mode-button[data-mode="HEATCOOL"]:hover {
    background: linear-gradient(100deg, rgba(196, 83, 31, 0.62), rgba(44, 126, 158, 0.62));
}
.mode-button[data-mode="HEATCOOL"][aria-pressed="true"] {
    background: linear-gradient(100deg, var(--ember-deep), var(--glacier-deep));
    border-color: transparent;
    color: #FFFFFF;
}

.facts {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    margin: 0;
    padding-top: 18px;
    border-top: 1px solid var(--line);
}
.fact dt {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-bottom: 5px;
}
.fact dd {
    margin: 0;
    font-family: var(--font-data);
    font-size: 15px;
}

/* A fact that is also a control: it must look pressable. */
.fact-button {
    padding: 5px 11px;
    margin: -3px -4px;
    border: 1px solid var(--line);
    background: var(--ground-2);
    color: var(--ink);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 14px;
    cursor: pointer;
}
.fact-button:hover { border-color: var(--ink-dim); background: var(--ground-3); }
.fact-button[data-running="true"] {
    border-color: var(--glacier);
    color: var(--glacier);
}

.button-danger {
    background: var(--danger);
    color: var(--ground);
    margin-right: 8px;
}
.button-danger:hover { background: #FF6D73; }

.feedback {
    margin: 0;
    min-height: 18px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}
.feedback[data-tone="error"] { color: var(--ember); }

/* --- Panels: history and schedule --------------------------------------- */

.panel {
    margin-top: 34px;
    padding-top: 18px;
    border-top: 1px solid var(--line);
}

.panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}
.panel-head > h2 { flex: 1 1 auto; min-width: 0; }
/* The tools sit beside the heading on a wide screen and wrap under it on a
   narrow one. Fixed at their natural width, three or four buttons pushed the
   page sideways. */
.panel-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    flex: 0 1 auto;
    min-width: 0;
}

.panel-head h2 {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 15px;
    letter-spacing: 0.02em;
    margin: 0;
}

.panel-scope {
    font-family: var(--font-data);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: var(--ink-dim);
    margin-left: 8px;
}

.panel-note {
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
    margin: 12px 0 0;
}

/* Reserve a band above the chart so the readout never covers the curves —
   just enough for its two lines, no more. */
.chart-wrap {
    position: relative;
    padding-top: 4px;
}

/* Floats over the top of the chart rather than reserving a band above it: the
   first pixels of the plot area are empty anyway, above every curve. */
.chart-cursor {
    position: absolute;
    top: 4px;
    left: 0;
    transform: translate(-50%, -100%);
    padding: 7px 10px;
    background: var(--ground-3);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    line-height: 1.55;
    color: var(--ink);
    white-space: nowrap;
    pointer-events: none;
    z-index: 3;
}
.chart-cursor b { font-weight: 600; color: var(--ink); }
.chart-cursor .cursor-row { display: block; }
.chart-cursor .cursor-key { color: var(--ink-dim); }

.chart-crosshair-line {
    stroke: var(--ink);
    stroke-width: 1;
    opacity: 0.5;
    pointer-events: none;
}
#chart-crosshair-h {
    opacity: 0.25;
    stroke-dasharray: 3 4;
}

.history-chart {
    width: 100%;
    height: 230px;
    display: block;
}

.series-toggle {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}

.series-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 10px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    cursor: pointer;
}
.series-chip:hover { color: var(--ink); }
.series-chip[aria-pressed="false"] { opacity: 0.45; }
.series-chip[aria-pressed="true"] {
    color: var(--ink);
    border-color: var(--ink-dim);
}

.series-swatch {
    width: 14px;
    height: 2px;
    background: currentColor;
    flex: none;
}
.series-swatch[data-style="dashed"] {
    height: 0;
    border-top: 2px dashed currentColor;
    background: none;
}
.series-swatch[data-style="band"] {
    height: 11px;
    border-radius: 2px;
    opacity: 0.4;
}
.series-swatch[data-style="marker"] {
    width: 2px;
    height: 11px;
}

.event-list {
    list-style: none;
    margin: 12px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.event-row {
    display: grid;
    grid-template-columns: 62px 74px 1fr;
    gap: 10px;
    align-items: baseline;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}
.event-row .event-when { color: var(--ink); }
.event-row .event-source { letter-spacing: 0.06em; text-transform: uppercase; font-size: 10px; }
.event-row[data-source="external"] .event-source { color: var(--ember); }
.event-row[data-source="manual"] .event-source { color: var(--glacier); }
.event-row[data-source="rule"] .event-source { color: var(--eco); }
.event-row[data-source="skipped"] .event-source { color: var(--ink-dim); }

.rules {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    background: var(--line);
    border-block: 1px solid var(--line);
}

.rule {
    display: grid;
    grid-template-columns: 62px 1fr auto;
    align-items: center;
    gap: 14px;
    padding: 13px 4px;
    background: var(--ground);
}
/* Disabled rules stay legible; they lose emphasis, not visibility. */
.rule[data-enabled="false"] .rule-time,
.rule[data-enabled="false"] .rule-name { color: var(--ink-dim); }

.rule-time {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 19px;
    font-variant-numeric: tabular-nums;
}

.rule-name { font-size: 14px; }

.rule-detail {
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
    margin-top: 3px;
    letter-spacing: 0.04em;
}

.rule-actions {
    display: flex;
    gap: 8px;
}
.rule-actions button {
    background: none;
    border: 1px solid var(--line);
    color: var(--ink-dim);
    border-radius: var(--radius);
    padding: 6px 10px;
    font-size: 12px;
    font-family: var(--font-data);
    cursor: pointer;
}
.rule-actions button:hover { color: var(--ink); }
.rule-actions button:disabled {
    opacity: 0.32;
    cursor: not-allowed;
}
.rule-actions button:disabled:hover { color: var(--ink-dim); }

.rules-empty {
    padding: 20px 4px;
    background: var(--ground);
    font-family: var(--font-data);
    font-size: 13px;
    color: var(--ink-dim);
}

/* --- Rule editor -------------------------------------------------------- */

.editor {
    width: min(460px, calc(100vw - 32px));
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
    color: var(--ink);
    padding: 22px;
}
.editor::backdrop { background: rgba(5, 12, 13, 0.72); }

.editor h3 {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 17px;
    margin: 0 0 18px;
}

.editor-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.field { display: block; }
.field > span {
    display: block;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-bottom: 6px;
}

.field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.days {
    border: none;
    padding: 0;
    margin: 0;
}
.days legend {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-bottom: 8px;
    padding: 0;
}

input[type="range"] {
    width: 100%;
    accent-color: var(--glacier);
    margin: 4px 0 2px;
}

.range-value {
    display: block;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink);
}

.rule[data-kind="fan"] .rule-time { color: var(--glacier); font-size: 15px; }

.copy-targets {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.copy-target {
    display: block;
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}
.copy-target[aria-pressed="true"] {
    background: var(--ground-3);
    border-color: var(--ink-dim);
    color: var(--ink);
}
.copy-target-meta {
    display: block;
    margin-top: 2px;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}

.day-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 5px;
}

.day-toggle {
    padding: 9px 0;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 12px;
    cursor: pointer;
}
/* Day selection is neutral: --accent means system state, not selection. */
.day-toggle[aria-pressed="true"] {
    background: var(--ground-3);
    border-color: var(--ink-dim);
    color: var(--ink);
}

.editor-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 6px;
}
.editor-actions .link-quiet { margin-top: 0; }

.panel-footnote {
    margin-top: 18px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
}

.credits {
    margin-top: 26px;
    padding: 20px 0 8px;
    border-top: 1px solid var(--line);
    text-align: center;
}

.credits-line {
    margin: 0;
    font-family: var(--font-data);
    font-size: 11px;
    line-height: 1.9;
    color: var(--ink-dim);
}
.credits-line strong { color: var(--ink); font-weight: 600; }
.credits-heart { color: var(--danger); }
.credits-flag {
    vertical-align: -2px;
    margin-left: 4px;
    border-radius: 1px;
}
.credits-flag {
    vertical-align: -3px;
    margin-left: 5px;
    border-radius: 1px;
}
.credits-legal a {
    color: var(--ink-dim);
    text-decoration: none;
    border-bottom: 1px solid var(--line);
}
.credits-legal a:hover { color: var(--ink); }


.footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 40px;
    padding-top: 18px;
    border-top: 1px solid var(--line);
}

.footer-links {
    display: flex;
    gap: 20px;
    flex-wrap: wrap;
}

/* Leaving the page is a deliberate act, not a link among links. */
.footer-signout {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 7px 14px;
    font-family: var(--font-data);
    font-size: 11px;
}
.footer-signout:hover { border-color: var(--ink-dim); }



.editor-narrow { width: min(380px, calc(100vw - 32px)); }

/* While a modal is open the page behind must not scroll: a dialog in the top
   layer blocks clicks but not the wheel. */
body.modal-open { overflow: hidden; }

.confirm-body {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
}

.confirm-warning {
    margin: 0;
    padding: 10px 12px;
    background: var(--ground-3);
    border-left: 2px solid var(--ember);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 12px;
    line-height: 1.5;
    color: var(--ink);
}

/* --- Thermostat switcher ------------------------------------------------ */

.topbar-identity { position: relative; }

.device-switch {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 2px 4px;
    margin-left: -4px;
    background: none;
    border: none;
    border-radius: var(--radius);
    color: inherit;
    cursor: pointer;
    text-align: left;
    min-width: 0;
}
.device-switch:hover { background: var(--ground-2); }

.device-caret {
    font-size: 10px;
    color: var(--ink-dim);
    transform: translateY(-1px);
}

.device-menu {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 8;
    min-width: 210px;
    margin: 0;
    padding: 4px;
    list-style: none;
    background: var(--ground-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.45);
}

.device-option {
    display: block;
    width: 100%;
    padding: 9px 10px;
    background: none;
    border: none;
    border-radius: var(--radius);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}
.device-option:hover { background: var(--ground-3); }
.device-option[aria-selected="true"] { color: var(--eco); }

.device-menu-rule {
    height: 1px;
    margin: 4px 0;
    background: var(--line);
}

/* The overview entry is a link, not a button; it needs the same box. */
a.device-option { text-decoration: none; }

.device-option-meta {
    display: block;
    margin-top: 2px;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}


/* --- Configuration sheet --------------------------------------------------- */

.config-body {
    display: flex;
    flex-wrap: wrap;
    gap: 26px;
    align-items: flex-start;
}

.config-wiring { flex: 0 0 340px; }
.config-details { flex: 1 1 300px; min-width: 260px; }

.wiring-diagram {
    width: 100%;
    max-width: 340px;
    display: block;
}

/* The air-conditioning unit, distinct from the thermostat above it. */
.config-unit { color: var(--ink-dim); }

.config-system {
    font-family: var(--font-data);
    font-size: 13px;
    color: var(--ink);
    margin: 0 0 16px;
}

.config-specs {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 16px;
    margin: 0;
    font-family: var(--font-data);
    font-size: 12px;
}
.config-specs dt { color: var(--ink-dim); white-space: nowrap; }
.config-specs dd { margin: 0; color: var(--ink); word-break: break-all; }

.config-empty {
    font-family: var(--font-data);
    font-size: 13px;
    color: var(--ink-dim);
    margin: 0;
}

.terminal-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 5px;
}

.terminal-toggle {
    padding: 9px 2px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    cursor: pointer;
}
.terminal-toggle[aria-pressed="true"] {
    background: var(--ground-3);
    border-color: var(--wire, var(--ink-dim));
    color: var(--ink);
}

/* --- Date navigation and seasons ------------------------------------------- */

.date-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 0;
}
.date-bar input[type="date"] {
    width: auto;
    padding: 7px 10px;
    font-family: var(--font-data);
    font-size: 12px;
}
.date-bar .chip-button { padding: 7px 12px; }
.date-bar .chip-button:disabled { opacity: 0.35; cursor: not-allowed; }

/* A strip of its own: choosing which schedule to look at is a different act
   from editing the one on screen, and the two were reading as one toolbar. */
.schedule-strip {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
}

.schedule-strip-label {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-right: 2px;
}

.season-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    flex: 1 1 auto;
    margin: 0;
}

.season-chip {
    padding: 7px 12px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    cursor: pointer;
}
.season-chip[aria-pressed="true"] {
    background: var(--ground-3);
    border-color: var(--ink-dim);
    color: var(--ink);
}
/* The running schedule, whichever one is being viewed. Green throughout: it is
   the one answer the eye should find without reading. */
.season-chip[data-active="true"] {
    color: var(--eco);
    border-color: rgba(134, 214, 155, 0.5);
}
.season-chip[data-active="true"][aria-pressed="true"] {
    background: rgba(134, 214, 155, 0.14);
    border-color: var(--eco);
}
.season-chip[data-active="true"]::after {
    content: ' · ACTIF';
    font-size: 9px;
    letter-spacing: 0.08em;
    font-weight: 600;
}

.season-row[data-active="true"] { border-left-color: var(--eco); }

/* The row whose grid is on screen. Distinct from "active", which is the one
   the cron obeys — the two are independent and both need to be visible. */
.season-row[data-shown="true"] { background: var(--ground-3); }
.season-shown-tag {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.08em;
    color: var(--glacier);
    margin-left: 8px;
}
.season-row[data-active="true"] .rule-name { color: var(--eco); }
.schedule-active-tag { color: var(--eco); }

/* Every row reserves the same left rule, coloured only when it applies:
   giving it to the active row alone indented that one line and made the list
   look ragged. */
.season-row {
    display: grid;
    grid-template-columns: 92px 1fr auto;
    gap: 12px;
    align-items: center;
    padding: 10px 4px 10px 11px;
    border-left: 3px solid transparent;
    background: var(--ground);
}

/* The leftmost cell answers one question — is this the schedule that runs? —
   with either the badge or the button that makes it so. Keeping it apart from
   the three editing actions stops "activer" being clicked by reflex. */
.season-state { justify-self: start; }

.season-activate {
    padding: 6px 12px;
    border: 1px solid rgba(134, 214, 155, 0.5);
    background: rgba(134, 214, 155, 0.10);
    color: var(--eco);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    cursor: pointer;
    white-space: nowrap;
}
.season-activate:hover { background: rgba(134, 214, 155, 0.22); border-color: var(--eco); }

.season-badge {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 7px;
    border: 1px solid var(--eco);
    border-radius: var(--radius);
    background: rgba(134, 214, 155, 0.18);
    color: var(--eco);
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.1em;
    font-weight: 600;
    vertical-align: 2px;
}
.season-row .rule-actions { flex-wrap: wrap; justify-content: flex-end; }

/* Four actions plus a name never fit one phone line; stack them instead of
   letting "supprimer" run off the edge. */
@media (max-width: 620px) {
    /* Two rows rather than one column: the button keeps its place on the left
       of the name, and the editing actions stay to the right underneath.
       Explicit placement is required — an empty state cell on the active row
       let the browser reflow the others into the same track. */
    .season-row .rule-actions { justify-content: flex-start; }
    .schedule-dialog .editor-form { padding-right: 2px; }
}
.season-row .rule-detail { margin-top: 3px; }

/* Reads out whichever terminal the pointer is on: a native title tooltip is
   slow to appear and never fires on a touch screen. */
.wiring-readout {
    margin: 12px 0 0;
    min-height: 20px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink);
}
.wiring-readout:empty::before {
    content: 'Touche une borne du schéma pour connaître son rôle.';
    color: var(--ink-dim);
}
.wiring-readout .wire-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 7px;
    vertical-align: -1px;
}
.wiring-diagram .terminal-hit { cursor: help; }

/* --- Statistics ----------------------------------------------------------- */

/* The history date bar sits flush against its chart on purpose; the stats one
   is followed by cards and needs the gap back. */
.stats-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 14px;
}

.stat-card {
    flex: 1 1 260px;
    min-width: 240px;
    padding: 16px 18px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
}
.stat-card[data-current="true"] { border-color: var(--ink-dim); }

.stat-name {
    display: block;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 14px;
    margin-bottom: 14px;
}

.stat-figures {
    display: flex;
    gap: 26px;
    margin-bottom: 14px;
}

.stat-figure dt {
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-bottom: 4px;
}
.stat-figure dd {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 24px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.stat-figure[data-kind="running"] dd { color: var(--ember); }
.stat-figure[data-kind="running"][data-mode="cool"] dd { color: var(--glacier); }
.stat-figure[data-kind="idle"] dd { color: var(--ink-dim); }

/* Proportion at a glance: the bar says more than the two numbers alone. */
.stat-bar {
    display: flex;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    background: var(--ground-3);
}
.stat-bar span { display: block; height: 100%; }
.stat-bar .bar-heating { background: var(--ember); }
.stat-bar .bar-cooling { background: var(--glacier); }

.stat-meta {
    display: block;
    margin-top: 10px;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}
.stat-meta[data-good="true"] { color: var(--eco); }
.stat-meta[data-warn="true"] { color: var(--amber); }

/* --- Temperature sensors ------------------------------------------------- */

.sensor-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.sensor-card {
    flex: 1 1 128px;
    min-width: 128px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
}

.sensor-name {
    display: block;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin-bottom: 6px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sensor-temp {
    display: block;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 26px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.sensor-meta {
    display: block;
    margin-top: 6px;
    font-family: var(--font-data);
    font-size: 10px;
    color: var(--ink-dim);
}
.sensor-meta[data-low="true"] { color: var(--danger); }

/* --- Documentation blocks ------------------------------------------------ */

.doc-intro {
    margin: 0 0 14px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--ink);
}

.doc-sub {
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
    margin: 22px 0 10px;
}
.doc-sub:first-of-type { margin-top: 0; }

.doc-list {
    margin: 0;
    padding-left: 18px;
    font-size: 14px;
    line-height: 1.62;
}
.doc-list li { margin-bottom: 9px; }
.doc-list li:last-child { margin-bottom: 0; }

.doc-wrap {
    white-space: normal;
    line-height: 1.5;
    margin-top: 5px;
}

code {
    font-family: var(--font-data);
    font-size: 12px;
    background: var(--ground-2);
    padding: 1px 5px;
    border-radius: 2px;
}

kbd {
    font-family: var(--font-data);
    font-size: 11px;
    background: var(--ground-3);
    border: 1px solid var(--line);
    border-bottom-width: 2px;
    border-radius: 3px;
    padding: 1px 6px;
}

.doc-code {
    margin: 0 0 14px;
    padding: 14px 16px;
    background: var(--ground-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 12px;
    line-height: 1.55;
    color: var(--ink);
    overflow-x: auto;
    white-space: pre;
}

.sensor-serial {
    display: block;
    margin-top: 4px;
    font-family: var(--font-data);
    font-size: 9px;
    color: var(--ink-dim);
    opacity: 0.55;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- 14-day forecast strip ---------------------------------------------- */

.forecast {
    margin-bottom: 18px;
}

.forecast-strip {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 6px;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

.forecast-day {
    flex: 1 0 54px;
    min-width: 54px;
    padding: 8px 4px 9px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
    text-align: center;
    cursor: default;
}
.forecast-day:hover { border-color: var(--ink-dim); }

/* Alternating bands mark each seven-day block. */
.forecast-day[data-week="1"] {
    background: transparent;
}
.forecast-day[data-week-start="true"] {
    border-left: 1px solid var(--ink-dim);
}
.forecast-day[data-today="true"] {
    border-color: var(--eco);
}

.forecast-day-offset {
    display: block;
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.08em;
    color: var(--ink-dim);
    opacity: 0.7;
}
.forecast-day[data-today="true"] .forecast-day-offset {
    color: var(--eco);
    opacity: 1;
}

.forecast-day-name {
    display: block;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.06em;
    color: var(--ink-dim);
}

.forecast-day-date {
    display: block;
    font-family: var(--font-data);
    font-size: 9px;
    color: var(--ink-dim);
    opacity: 0.75;
    margin-bottom: 3px;
}

.forecast-day svg {
    display: block;
    margin: 0 auto 3px;
}

.forecast-day-high {
    display: block;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

.forecast-day-low {
    display: block;
    font-family: var(--font-data);
    font-size: 10px;
    color: var(--ink-dim);
}

.forecast-day-felt {
    display: block;
    font-family: var(--font-data);
    font-size: 9px;
    color: var(--ember);
    opacity: 0.85;
    min-height: 11px;
}

.forecast-day-rain {
    display: block;
    font-family: var(--font-data);
    font-size: 9px;
    color: var(--glacier);
    min-height: 12px;
}

.forecast-note {
    margin: 8px 0 0;
    font-family: var(--font-data);
    font-size: 10px;
    color: var(--ink-dim);
}

/* Wide enough for a seven-day grid, and scrollable: the panel now carries its
   own grid, so it no longer needs the page behind it. */
.schedule-dialog {
    width: min(940px, calc(100vw - 32px));
    max-width: none;
    max-height: 88vh;
    overflow-y: auto;
    /* Stops the wheel from carrying on to the page once the dialog reaches
       its own end. */
    overscroll-behavior: contain;
}
/* One scroller, not two: the dialog itself scrolls. Capping the form as well
   produced a scrollbar inside a scrollbar. */
.schedule-dialog .editor-form {
    max-height: none;
    overflow: visible;
}

/* Seasons are managed inline rather than in a dialog: the weekly grid sits
   directly below, so the schedule being described is already on screen and
   editable. A modal would have hidden exactly what it talks about. */
.season-panel { padding: 0; border: none; background: none; margin: 0; }

.season-panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}
.season-panel h3 {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 14px;
}
.season-panel h4 {
    margin: 0 0 10px;
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

/* Name plus two dates fit one line on a wide screen and stack on a phone. */
.season-form .field-row {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    gap: 12px;
}
@media (max-width: 620px) {
    .season-form .field-row { grid-template-columns: 1fr; }
}

.draft-block {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
}

/* Bubbles need room: fourteen of them across a phone would touch and become
   unreadable, so the grid scrolls sideways instead of shrinking. */
.draft-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

.draft-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}
.draft-head h4 { margin: 0; }

.draft-state {
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}
.draft-state[data-dirty="true"] { color: var(--ember); }

.season-form {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
}

/* Breathing room between the last field and the buttons. */
.season-form .week-actions { margin-top: 14px; }

/* Shown when the displayed period has no rules of its own: an empty grid
   offers nothing to click, so it has to say what to do. */
.grid-hint {
    margin: 0 0 12px;
    padding: 11px 14px;
    border: 1px solid rgba(255, 122, 61, 0.35);
    border-left: 3px solid var(--ember);
    border-radius: var(--radius);
    background: rgba(255, 122, 61, 0.08);
    font-size: 13px;
    line-height: 1.55;
    color: var(--ink);
}
.grid-hint b { font-weight: 600; }

/* Empty day tracks accept a click to create a rule at that hour. */
.day-track { cursor: crosshair; }
.day-track:hover { filter: brightness(1.35); }

/* --- Weekly schedule grid ----------------------------------------------- */

.panel-tools {
    display: flex;
    align-items: center;
    gap: 10px;
}

.view-toggle {
    display: flex;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
}

.view-button {
    padding: 7px 12px;
    white-space: nowrap;
    background: transparent;
    border: none;
    color: var(--ink-dim);
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.06em;
    cursor: pointer;
}
.view-button + .view-button { border-left: 1px solid var(--line); }
.view-button[aria-pressed="true"] {
    background: var(--ground-3);
    color: var(--ink);
}

.schedule-grid {
    width: 100%;
    display: block;
}

.schedule-grid .segment { cursor: pointer; }
.schedule-grid .segment:hover { opacity: 0.75; }

.week-legend {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
    flex-wrap: wrap;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}

.week-fan-key::before {
    content: '';
    width: 14px;
    height: 5px;
    border-radius: 2px;
    background: var(--glacier);
    opacity: 0.9;
}
.week-fan-key {
    display: flex;
    align-items: center;
    gap: 6px;
}

.week-ramp {
    width: 84px;
    height: 8px;
    border-radius: 4px;
    /* Same alpha as the segments, so the legend matches what is on screen. */
    background: linear-gradient(90deg, rgba(94, 200, 239, 0.5), rgba(255, 122, 61, 0.5));
}


/* Reserved band at the top so the cursor readout never covers a day row. */
.week {
    position: relative;
    padding-top: 30px;
}

.week-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
}

/* Actions grouped by purpose, separated by a rule rather than by colour. */
/* One row per purpose. Side-by-side groups with vertical dividers looked fine
   until they wrapped, which left an orphan divider opening the next line. */
.action-groups {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}

.action-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* Separated by space, not by lines: the rows already read as groups. */
.action-group + .action-group {
    padding-top: 8px;
    border-top: 1px solid var(--line);
}

.chip-button {
    display: inline-block;
    text-decoration: none;
    padding: 7px 12px;
    border: 1px solid var(--line);
    background: transparent;
    color: var(--ink-dim);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.04em;
    cursor: pointer;
}
.chip-button:hover { color: var(--ink); border-color: var(--ink-dim); }

/* Colour only where it means something. The two "add" buttons carry the same
   hues as the things they create in the grid; the two "copy" buttons do the
   same job, so they stay neutral. */
.chip-heat {
    color: var(--ember);
    border-color: rgba(255, 122, 61, 0.45);
    background: rgba(255, 122, 61, 0.10);
}
.chip-heat:hover { background: rgba(255, 122, 61, 0.20); border-color: var(--ember); }

.chip-fan {
    color: var(--glacier);
    border-color: rgba(94, 200, 239, 0.45);
    background: rgba(94, 200, 239, 0.10);
}
.chip-fan:hover { background: rgba(94, 200, 239, 0.20); border-color: var(--glacier); }

.chip-danger {
    color: var(--danger);
    border-color: rgba(242, 86, 92, 0.45);
    background: rgba(242, 86, 92, 0.10);
}
.chip-danger:hover { background: rgba(242, 86, 92, 0.20); border-color: var(--danger); }

/* The two copy actions differ by scope: inside this thermostat, or across to
   another one. Green stays local, violet crosses over. */
.chip-local {
    color: var(--eco);
    border-color: rgba(134, 214, 155, 0.45);
    background: rgba(134, 214, 155, 0.10);
}
.chip-local:hover { background: rgba(134, 214, 155, 0.20); border-color: var(--eco); }

.chip-cross {
    color: var(--violet);
    border-color: rgba(176, 140, 232, 0.45);
    background: rgba(176, 140, 232, 0.10);
}
.chip-cross:hover { background: rgba(176, 140, 232, 0.20); border-color: var(--violet); }

/* The two exports differ by what you do with the file: restore, or read. */
/* Filled rather than another hue: the palette already carries seven accents,
   and one more would dilute what each of them means. */
.chip-solid {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--ground);
    font-weight: 600;
}
.chip-solid:hover { background: #FFFFFF; border-color: #FFFFFF; color: var(--ground); }

/* PDF joins JSON and XLS: three formats, three colours, same family. */
.chip-pdf {
    color: var(--danger);
    border-color: rgba(242, 86, 92, 0.45);
    background: rgba(242, 86, 92, 0.10);
}
.chip-pdf:hover { background: rgba(242, 86, 92, 0.20); border-color: var(--danger); }

.chip-json {
    color: var(--amber);
    border-color: rgba(232, 196, 106, 0.45);
    background: rgba(232, 196, 106, 0.10);
}
.chip-json:hover { background: rgba(232, 196, 106, 0.20); border-color: var(--amber); }

.chip-xls {
    color: var(--magenta);
    border-color: rgba(222, 127, 181, 0.45);
    background: rgba(222, 127, 181, 0.10);
}
.chip-xls:hover { background: rgba(222, 127, 181, 0.20); border-color: var(--magenta); }


/* Cursor readout: follows the pointer across the grid. */
.week-cursor {
    position: absolute;
    top: 0;
    left: 0;
    transform: translate(-50%, -100%);
    padding: 5px 9px;
    background: var(--ground-3);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink);
    white-space: nowrap;
    pointer-events: none;
    z-index: 3;
}

.crosshair-line {
    stroke: var(--ink);
    stroke-width: 1;
    opacity: 0.55;
    pointer-events: none;
}
/* Dashed and faint, so it guides the eye without striking through the labels. */
#crosshair-h {
    opacity: 0.3;
    stroke-dasharray: 3 4;
}
.crosshair-frame {
    fill: none;
    stroke: var(--ink);
    stroke-width: 1;
    opacity: 0.45;
    pointer-events: none;
}

/* --- Credits --------------------------------------------------------------- */

.credits {
    margin-top: 26px;
    padding-top: 18px;
    border-top: 1px solid var(--line);
    text-align: center;
}

.credits-line {
    margin: 0 0 4px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}
.credits-line strong { color: var(--ink); font-weight: 600; }
.credits-heart { color: var(--danger); }
.credits-flag {
    vertical-align: -2px;
    margin-left: 4px;
    border-radius: 1px;
}
.credits-flag {
    vertical-align: -3px;
    margin-left: 5px;
    border-radius: 1px;
}

.credits-small { font-size: 11px; opacity: 0.8; margin-top: 8px; }
/* A plain button, styled as the link it replaces. */
.credits-cookies {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--ink-dim);
    cursor: pointer;
    border-bottom: 1px solid var(--line);
}
.credits-cookies:hover { color: var(--ink); }

.credits-small a {
    color: var(--ink-dim);
    text-decoration: none;
    border-bottom: 1px solid var(--line);
}
.credits-small a:hover { color: var(--ink); }

/* --- Narrow screens ----------------------------------------------------- */

@media (max-width: 620px) {
    .console {
        grid-template-columns: 96px 1fr;
        gap: 16px;
    }
    .gauge { max-height: 340px; }
    .setpoint-value { font-size: 34px; min-width: 78px; }
    .stepper { width: 46px; height: 46px; }
    .rule { grid-template-columns: 54px 1fr; }
    .rule-actions { grid-column: 1 / -1; }
    .panel-head { flex-wrap: wrap; }
    .forecast-day { flex: 0 0 48px; min-width: 48px; }
    .view-button { padding: 6px 10px; }
    .chip-button { padding: 7px 10px; }

    /* Four labels never fit across a phone. Swiping the bar sideways beats
       letting it stack into two rows and eat the top of the page. */
    .section-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }
    .section-nav::-webkit-scrollbar { display: none; }
    .section-nav a { flex: none; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before { transition: none !important; animation: none !important; }
}

/* --- Narrow screens, continued ------------------------------------------- */

/* Placed at the end on purpose: media queries carry no extra specificity, so
   an override declared before the rule it overrides simply loses. */
@media (max-width: 620px) {
    /* Two rows: the activate button keeps its place to the left of the name,
       the editing actions stay right-aligned underneath.
       A fixed first track, not auto — the actions row spans both columns and
       its contribution to the sizing was squeezing a 72 px button into 54 px. */
    .season-row {
        grid-template-columns: 84px minmax(0, 1fr);
        row-gap: 8px;
    }
    .season-state { grid-column: 1; grid-row: 1; }
    .season-row > div:not(.season-state):not(.rule-actions) { grid-column: 2; grid-row: 1; }
    .season-row .rule-actions {
        grid-column: 1 / -1;
        grid-row: 2;
        justify-content: flex-end;
    }
}

/* Alert rows stack on a phone. Declared here, after the base rule: a media
   query adds no specificity, so an override placed before it simply loses. */
@media (max-width: 620px) {
    .alert-row {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }
    .alert-row .rule-actions { flex-wrap: wrap; }
}

/* Navigation on a phone: two rows rather than one scrolling line. A row that
   scrolls sideways hides half its entries behind a gesture nobody thinks to
   try; wrapping shows them all at once. */
@media (max-width: 620px) {
    .section-nav {
        flex-wrap: wrap;
        overflow-x: visible;
        justify-content: center;
        row-gap: 4px;
        padding: 8px 10px;
    }
    .section-nav a {
        flex: 0 1 auto;
        font-size: 11px;
        padding: 7px 9px;
    }
}

.check-row {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 14px;
    color: var(--ink);
    cursor: pointer;
}
.check-row input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--ember);
    cursor: pointer;
}

/* --- Technical support ----------------------------------------------------- */

.support-sub {
    margin: 18px 0 8px;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.support-sub:first-of-type { margin-top: 4px; }

.support-row {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr) auto;
    gap: 12px;
    align-items: start;
}
.support-row[data-status="done"] .rule-name,
.support-row[data-status="done"] .rule-detail { opacity: 0.5; }
.support-row[data-status="done"] .rule-name { text-decoration: line-through; }

.support-outcome {
    margin-top: 5px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--eco);
}
.support-outcome[data-outcome="mitigated"] { color: var(--amber); }
.support-outcome[data-outcome="done_ok"] { color: var(--eco); }
.support-outcome[data-outcome="done_failed"] { color: var(--danger); }
.support-outcome[data-outcome="ignored"] { color: var(--ink-dim); }
.support-outcome[data-outcome="false_alarm"] { color: var(--glacier); }
.support-resolution {
    margin-top: 3px;
    font-family: var(--font-data);
    font-size: 12px;
    line-height: 1.55;
    color: var(--ink-dim);
    /* The field accepts several lines; showing them as one paragraph loses
       the structure the writer put there. */
    white-space: pre-line;
}

.support-badge {
    display: inline-block;
    padding: 3px 8px;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.1em;
    font-weight: 600;
    color: var(--ink-dim);
    white-space: nowrap;
}
.support-badge[data-urgency="normal"] { color: var(--glacier); border-color: rgba(94,200,239,0.45); }
.support-badge[data-urgency="high"] { color: var(--amber); border-color: rgba(232,196,106,0.5); }
.support-badge[data-urgency="critical"] {
    color: var(--danger);
    border-color: var(--danger);
    background: rgba(242, 86, 92, 0.14);
}

.support-when {
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink);
}
/* A visit inside the week reads differently from one next month. */
.support-when[data-soon="true"] { color: var(--amber); }
.support-when[data-past="true"] { color: var(--danger); }

@media (max-width: 620px) {
    .support-row {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }
}

/* --- Notices and landmarks ------------------------------------------------- */

.notice-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 12px;
    align-items: center;
}

.notice-line {
    margin: 0 0 8px;
    padding: 10px 13px;
    border-radius: var(--radius);
    border-left: 3px solid var(--ink-dim);
    background: var(--ground-2);
    font-size: 13px;
    line-height: 1.6;
    color: var(--ink);
}
.notice-line[data-level="info"] { border-left-color: var(--glacier); }
/* A watch that matches nothing today: present, but not shouting. */
.notice-line[data-level="quiet"] {
    border-left-color: var(--line);
    background: none;
    color: var(--ink-dim);
}
.watch-hit { color: var(--amber); }
.notice-line[data-level="warning"] {
    border-left-color: var(--amber);
    background: rgba(232, 196, 106, 0.08);
}
.notice-line[data-level="urgent"] {
    border-left-color: var(--danger);
    background: rgba(242, 86, 92, 0.10);
}

.notice-tag {
    display: inline-block;
    margin-right: 8px;
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.notice-line[data-level="warning"] .notice-tag { color: var(--amber); }
.notice-line[data-level="urgent"] .notice-tag { color: var(--danger); }

.notice-until { color: var(--ink-dim); font-size: 12px; }

.landmarks {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 28px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
}
.landmark { display: flex; flex-direction: column; gap: 2px; }
.landmark-value {
    font-family: var(--font-data);
    font-size: 13px;
    color: var(--ink);
}
/* Within a fortnight: worth noticing without reading the date. */
.landmark[data-soon="true"] .landmark-value { color: var(--amber); }

@media (max-width: 620px) {
    .notice-row { grid-template-columns: 1fr; }
}

/* The thermostats sit right under their title. */
.panel[data-live="cards"] .panel-head { margin-bottom: 8px; }

/* --- Overview: weather ----------------------------------------------------- */

.weather-panel .panel-head { margin-bottom: 14px; }

.wx-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px 34px;
}

.wx-now, .wx-day { display: flex; align-items: center; gap: 12px; }
.wx-now > div, .wx-day > div { display: flex; flex-direction: column; gap: 1px; }

.wx-big {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 40px;
    line-height: 1;
}
.wx-big .deg { font-size: 20px; color: var(--glacier); vertical-align: super; }

.wx-range {
    font-family: var(--font-data);
    font-size: 19px;
    color: var(--ink);
}
.wx-range b { font-weight: 600; }
.wx-min { color: var(--ink-dim); font-size: 14px; }

.wx-caption {
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}

.wx-sun {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 22px;
    margin: 14px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--line);
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}
.wx-sun-item { display: inline-flex; align-items: center; gap: 6px; }
.wx-sun-item b { color: var(--ink); font-weight: 600; }
.wx-sun-length { opacity: 0.75; }

.wx-verdict {
    margin: 12px 0 0;
    font-size: 14px;
    color: var(--ink);
}

.wx-joke {
    margin: 12px 0 0;
    padding: 11px 13px;
    border-radius: var(--radius);
    border-left: 3px solid var(--amber);
    background: rgba(232, 196, 106, 0.08);
    font-size: 13px;
    line-height: 1.6;
    color: var(--ink);
}
.wx-joke-label {
    display: block;
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--amber);
    margin-bottom: 3px;
}

/* --- Overview page --------------------------------------------------------- */

.masthead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    padding: 22px 0 16px;
}
.masthead h1 {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 22px;
    letter-spacing: -0.01em;
}
.masthead-sub {
    margin: 4px 0 0;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}

.home-weather {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 26px;
}
.home-weather-now { display: flex; align-items: baseline; gap: 10px; }
.home-temp {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 34px;
    color: var(--glacier);
}
.home-forecast { display: flex; flex-wrap: wrap; gap: 8px 26px; }
.home-forecast > div { display: flex; align-items: baseline; gap: 8px; }
.home-line, .home-meta {
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}

/* Inside a panel now, so the cards drop their own frame and are separated by
   a rule instead — two nested boxes read as clutter. */
.home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 4px 28px;
}

/* The cards are still .panel elements, which carry a 34 px top margin meant to
   space panels apart. Inside another panel that margin is a hole under the
   title, so it goes — along with the frame. */
.home-grid .home-card {
    margin: 0;
    padding: 0 0 4px;
    border: none;
    background: none;
    border-radius: 0;
}
@media (min-width: 760px) {
    /* A divider between the two columns, not around each card. */
    .home-grid .home-card + .home-card {
        padding-left: 28px;
        border-left: 1px solid var(--line);
    }
}
@media (max-width: 759px) {
    .home-grid .home-card + .home-card {
        border-top: 1px solid var(--line);
        margin-top: 10px;
    }
}

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

.home-mode {
    font-family: var(--font-data);
    font-size: 11px;
    letter-spacing: 0.08em;
    padding: 3px 9px;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    color: var(--ink-dim);
}
.home-mode[data-mode="HEAT"] { color: var(--ember); border-color: rgba(255,122,61,0.45); }
.home-mode[data-mode="COOL"] { color: var(--glacier); border-color: rgba(94,200,239,0.45); }
.home-mode[data-mode="OFF"] { color: var(--danger); border-color: rgba(242,86,92,0.45); }

.home-reading { display: flex; align-items: center; gap: 18px; margin-bottom: 14px; }
.home-ambient {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 52px;
    line-height: 1;
}
.home-ambient .deg { font-size: 24px; color: var(--glacier); vertical-align: super; }
.home-reading-side { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }

/* Running or idle is what the eye looks for first, so it reads as a state —
   a badge with a coloured dot — rather than as one grey line among three. */
.home-hvac {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 4px 10px;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: var(--font-data);
    font-size: 12px;
    letter-spacing: 0.04em;
    white-space: nowrap;
    color: var(--ink-dim);
}
.hvac-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

.home-hvac[data-hvac="HEATING"] {
    color: var(--ember);
    border-color: rgba(224, 122, 63, 0.5);
    background: rgba(224, 122, 63, 0.12);
}
.home-hvac[data-hvac="COOLING"] {
    color: var(--glacier);
    border-color: rgba(94, 200, 239, 0.5);
    background: rgba(94, 200, 239, 0.12);
}
.home-hvac[data-hvac="OFFLINE"] {
    color: var(--danger);
    border-color: rgba(242, 86, 92, 0.5);
    background: rgba(242, 86, 92, 0.12);
}

/* A pulse while the system is actually running: the dot is the only thing on
   the card that moves, so movement means "working". */
.home-hvac[data-hvac="HEATING"] .hvac-dot,
.home-hvac[data-hvac="COOLING"] .hvac-dot { animation: hvac-pulse 2s ease-in-out infinite; }

@keyframes hvac-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.45; transform: scale(0.82); }
}
@media (prefers-reduced-motion: reduce) {
    .home-hvac .hvac-dot { animation: none; }
}

/* One fact per line, each labelled: "consigne 24 ° · 42 % d'humidité" ran the
   two together and neither stood out. */
.home-fact {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-family: var(--font-data);
    font-size: 13px;
}
.home-fact-label {
    min-width: 72px;
    font-size: 10px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-dim);
}
.home-fact b { font-weight: 600; color: var(--ink); }

.home-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 8px 0 0;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--ink-dim);
}

/* The figures sit tighter here than in the statistics panel: a card is a
   summary, not the full reading. */
.home-card .stat-figures { margin: 0 0 10px; }
.home-card .stat-figure dd { font-size: 26px; }
.home-card .stat-bar { margin: 0; }
.home-card .stat-meta { margin: 8px 0 0; }

.home-paused {
    margin: 12px 0 0;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--danger);
}

.home-sub {
    margin: 16px 0 8px;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

.home-plan { list-style: none; margin: 0 0 14px; padding: 0; }
.home-plan li {
    display: grid;
    grid-template-columns: 58px 70px 1fr;
    gap: 10px;
    padding: 5px 0;
    font-family: var(--font-data);
    font-size: 12px;
    border-bottom: 1px solid rgba(39, 69, 75, 0.5);
}
.home-plan-time { color: var(--ink); }
.home-plan-value { color: var(--glacier); }
.home-plan-name { color: var(--ink-dim); }

.home-empty {
    margin: 0 0 14px;
    font-family: var(--font-data);
    font-size: 12px;
    color: var(--ink-dim);
}

/* The alert history reuses the event row, whose time column is sized for a
   bare clock; these carry a date too. */
/* The send history carries a date as well as a clock, and a wider status
   word, so it needs its own column widths — the shared row is sized for
   "14:05" and "MANUEL". */
.alert-history-row {
    grid-template-columns: 104px 76px 1fr;
    gap: 14px;
}
.alert-history-row .event-time { white-space: nowrap; }
.event-source[data-sent="true"] { color: var(--eco); }
.event-source[data-sent="false"] { color: var(--danger); }

@media (max-width: 620px) {
    .alert-history-row {
        grid-template-columns: 1fr;
        gap: 2px;
        padding-bottom: 8px;
    }
}

/* Multi-line fields: resizable vertically only, so dragging one cannot break
   the dialog's column layout. */
textarea {
    resize: vertical;
    min-height: 72px;
    line-height: 1.5;
    font-family: var(--font-body);
}

/* Settings page: a radio that reads like the day toggles elsewhere. */
.unit-choice {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 10px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
    cursor: pointer;
    font-size: 14px;
}
.unit-choice input { accent-color: var(--ember); width: 17px; height: 17px; cursor: pointer; }
.unit-choice:has(input:checked) { border-color: var(--ember); color: var(--ink); }

.unit-label { font-weight: inherit; }

/* A heading with its own action, inside a panel that already has one. */
.sub-head {
    align-items: baseline;
    margin: 20px 0 8px;
}
.sub-head .support-sub { margin: 0; }

/* Weather watches read like the notices they produce. */
.watch-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: center;
    padding: 4px 0;
}
.watch-row[data-enabled="false"] .notice-line { opacity: 0.45; }

.landmark-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: center;
    padding: 6px 0;
    border-bottom: 1px solid rgba(39, 69, 75, 0.5);
}
.landmark-auto {
    margin-left: 8px;
    font-family: var(--font-data);
    font-size: 9px;
    letter-spacing: 0.08em;
    color: var(--ink-dim);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 2px 6px;
}

@media (max-width: 620px) {
    .watch-row, .landmark-row { grid-template-columns: 1fr; }
}

/* An open draft whose schedule changed elsewhere: the two versions must not be
   mistaken for one. */
.draft-state[data-stale="true"] { color: var(--amber); }

/* --- Reports --------------------------------------------------------------- */

/* Breathing room under the date fields: the shortcuts sat against them and
   read as part of the second input. */
.report-shortcuts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 10px 0 18px;
}

/* The device toggles were reading as one control, and the Mesures label sat
   flush against them. */
#report-devices { gap: 14px; }

/* Two stacked groups in a panel need air between them; the first selector I
   wrote never matched anything, so only this one stays. */
.panel .days + .days { margin-top: 20px; }

/* Sixteen metrics is a page of scrolling before the buttons; folded away, the
   section fits on one screen and opens only when something is being chosen. */
.metric-menu {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--ground-2);
}

.metric-menu > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 14px;
    cursor: pointer;
    font-family: var(--font-data);
    font-size: 12px;
    letter-spacing: 0.1em;
    text-transform: none;
    letter-spacing: 0;
    color: var(--ink);
    list-style: none;
}
.metric-menu > summary::-webkit-details-marker { display: none; }
.metric-menu > summary::after {
    content: '▾';
    margin-left: auto;
    font-size: 14px;
    transition: transform 0.15s ease;
}
.metric-menu[open] > summary::after { transform: rotate(180deg); }
.metric-menu[open] > summary { border-bottom: 1px solid var(--line); }

.metric-count {
    font-size: 12px;
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink);
}

.metric-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 12px 14px 4px;
}

.metric-group {
    padding: 12px 14px 2px;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

/* One metric per row: each carries a sentence explaining what it contains, and
   a grid of columns would cut those sentences to ribbons. */
.report-metrics {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 100%;
    padding: 4px 6px 10px;
    max-height: 420px;
    overflow-y: auto;
}

.report-metric {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius);
    cursor: pointer;
}
.report-metric:hover { background: var(--ground-2); }
.report-metric input { margin-top: 3px; accent-color: var(--ember); width: 16px; height: 16px; }
.report-metric b { font-weight: 600; font-size: 14px; }
.report-metric span { display: block; font-size: 12px; color: var(--ink-dim); margin-top: 1px; }

/* Date inputs carry an intrinsic width that ignores the column they sit in. */
input[type="date"], input[type="time"] { min-width: 0; max-width: 100%; }
.field { min-width: 0; }

/* --- Export rows ----------------------------------------------------------- */

/* Every section that can be exported shows the same row, in the same place and
   the same order: PDF, XLS, JSON. Before this they lived in three different
   spots depending on the section — the heading, the date bar, or a group of
   actions further down — so the same three buttons had to be hunted for.
   Kept on the right, under the panel's own buttons, so the eye finds them in
   the same column as everything else it can act on. */
.export-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin: -6px 0 18px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--line);
}

@media (max-width: 620px) {
    /* Nothing to align against on a phone: the head's buttons wrap too. */
    .export-row { justify-content: flex-start; }
}

/* Disabled while the report has nothing to build. */
.export-row a[aria-disabled="true"] {
    opacity: 0.4;
    pointer-events: none;
}

/* A condition row driven by a linked alert: visible, but not editable here. */
.condition-locked { opacity: 0.62; }
.condition-locked select,
.condition-locked input { cursor: not-allowed; }

/* Decisions that follow an alert, listed under it. */
.alert-followers {
    margin-top: 6px;
    font-family: var(--font-data);
    font-size: 11px;
    color: var(--glacier);
}

/* The link state of a decision's conditions, with its way in or out. */
.link-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0 0 10px;
    padding: 9px 12px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--glacier);
    border-radius: var(--radius);
    background: var(--ground-2);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink-dim);
}
.link-banner span { flex: 1 1 240px; }

/* --- Overview: acting on every thermostat at once -------------------------- */

.bulk-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
}
.bulk-row + .bulk-row { margin-top: 10px; }

.bulk-label {
    flex: 0 0 auto;
    min-width: 180px;
    font-family: var(--font-data);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-dim);
}

.bulk-group {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-left: auto;
}

.bulk-flash {
    margin-top: 12px;
    color: var(--eco);
}

@media (max-width: 700px) {
    /* Under 700px the label and the buttons each take a full row, so the
       buttons keep their tap size instead of being squeezed. */
    .bulk-label { min-width: 100%; }
    .bulk-group { margin-left: 0; }
}
