/* ===== dispositivos =====
   As media queries ficam ao lado das regras que anulam: ganha quem vier mais abaixo. */

/* O painel de detalhe enche o cartão, para as duas listas terem altura para crescer. Não se
   usa `d-flex` aqui porque o `d-none` que o esconde é do mesmo peso e perdia. */
.device-detail-open {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/* A divisória entre as duas listas só existe quando estão mesmo lado a lado. */
@media (min-width: 1200px) {
    .border-start-xl {
        border-left: 1px solid var(--bs-border-color);
    }
}

/* Entre o `lg` e o `xl` os quinze dígitos do IMEI não cabem a `h4`: encolhe em vez de cortar. */
@media (min-width: 992px) and (max-width: 1199.98px) {
    #selectedDeviceTitle {
        font-size: var(--fs-lg);
    }
}

/* O `table-sm` aperta as linhas a .25rem e uma pastilha de 26px toca nas vizinhas. A última
   linha não leva divisória: por baixo dela vem logo o contorno do cartão. */
.telemetry-table > :not(caption) > * > * {
    /* .5rem: em quinze linhas poupa trinta pixéis, e a pastilha de 26px continua com folga. */
    padding: .5rem .75rem .5rem 0;
}

.telemetry-table > tbody > tr:last-child > * {
    border-bottom-width: 0;
}

.telemetry-table > tbody > tr > *:last-child {
    padding-right: 0;
}

/* As colunas não medem pelo conteúdo, para a lista não mudar de altura com o tipo do evento;
   o que não cabe corta com `text-truncate` e fica completo no `title`. */
/* Os dois painéis da atividade são separadores até ao `xl` e colunas lado a lado a partir
   dele. O Bootstrap desenha um separador com `display: block`, e estes são colunas flex. */
/* A régua cola debaixo da barra de navegação, para trocar de painel sem subir a lista. */
.activity-tabs {
    position: sticky;
    top: var(--navbar-height, 3.5rem);
    z-index: 3;
    background-color: var(--bs-body-bg);
}

/* No telemóvel quem escolhe é a régua do topo, que tem também a telemetria. Esconde-se por
   media query e não com `d-none`, que é o que o detalhe liga e desliga por haver pedidos. */
@media (max-width: 991.98px) {
    .activity-tabs {
        display: none;
    }
}

/* No telemóvel a lista já se separa da busca pelo cabeçalho do dia; a linha só existe no ecrã
   largo. */
@media (min-width: 992px) {
    .activity-section {
        border-top: 1px solid var(--bs-border-color);
    }
}

.activity-tab-content > .tab-pane.active {
    display: flex;
}

@media (min-width: 1200px) {
    .activity-tab-content > .tab-pane {
        display: flex;
    }
}

.telemetry-table {
    table-layout: fixed;
}

/* Três colunas medidas e o nome com o resto. A do valor é a calha onde a pastilha de estado
   de um pedido encosta: mede o que a mais larga pede -- «substituído», «desconhecido». */
.telemetry-col-icon { width: 2.125rem; }
.telemetry-col-value { width: 8rem; }

/* A hora mede o que a hora precisa, mais a seta da linha que abre, que vive nesta célula. */
.telemetry-col-time { width: 5rem; }

/* O cabeçalho de um dia atravessa a tabela toda e não é uma linha de dados: sem linha em
   baixo, e colado ao rolar para se saber sempre em que dia se está. */
.telemetry-day > td {
    position: sticky;
    top: var(--sticky-top, 0px);
    z-index: 1;
    background: var(--bs-tertiary-bg);
    border-bottom-width: 0;
    letter-spacing: .06em;
    padding-left: .75rem;
}

.telemetry-day-count {
    color: var(--bs-secondary-color);
}

/* A altura de duas linhas fica no contentor e não numa linha vazia: sem detalhes, o valor
   centra-se em vez de encostar ao topo. */
.telemetry-row-stack {
    min-height: 2.4rem;
}

/* A linha que se pode abrir. O cursor e o realce dizem que responde ao clique, e a seta
   confirma-o para quem não passa o rato -- num telemóvel não há hover nenhum. */
.telemetry-row-openable {
    cursor: pointer;
}

.telemetry-row-openable:hover,
.telemetry-row-openable:focus-visible {
    background: var(--bs-tertiary-bg);
}

.telemetry-row-caret {
    font-size: var(--fs-micro);
    transition: transform .15s ease;
}

.telemetry-row-openable[aria-expanded="true"] .telemetry-row-caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .telemetry-row-caret {
        transition: none;
    }
}

/* O que a linha aberta mostra: o texto por inteiro, com quebra, recuado até onde começa o
   nome para se ler como pertencendo à linha de cima e não como uma linha nova da lista. */
.telemetry-row-panel > td {
    padding: 0 0 .625rem 2.125rem;
    color: var(--bs-secondary-color);
    font-size: var(--fs-sm);
    line-height: 1.45;
    white-space: normal;
}

/* Sem a coluna lado a lado a linha reorganiza-se: ícone, nome com a hora por baixo, e o valor
   à direita. Tem de vir depois das regras que anula. */
@media (max-width: 991.98px) {
    /* Sem algoritmo de tabela: as linhas são grelhas, e o `colgroup` não lhes mede as colunas. */
    .telemetry-table,
    .telemetry-table > tbody {
        display: block;
    }

    .telemetry-table > tbody > tr {
        display: grid;
        /* O `minmax(0, ...)` deixa um valor longo ceder quando a linha não dá, em vez de levar a
           lista para fora do ecrã. */
        grid-template-columns: 2.125rem minmax(0, 1fr) minmax(0, max-content) auto;
        align-items: center;
        padding: .5rem 0;
        border-bottom: 1px solid var(--bs-border-color);
    }

    .telemetry-table > tbody > tr:last-child {
        border-bottom: 0;
    }

    .telemetry-table > tbody > tr > td {
        width: auto;
        border-bottom: 0;
        padding: 0 .5rem 0 0;
    }

    /* As larguras fixas das colunas não se aplicam a uma grelha: aqui quem manda são as
       faixas do `tr`, e cada celula volta a medir o que a sua faixa lhe der. */
    .telemetry-table > tbody > tr > td:nth-child(1) {
        grid-column: 1;
        grid-row: 1;
        width: auto;
    }

    .telemetry-table > tbody > tr > td:nth-child(2) {
        grid-column: 2;
        grid-row: 1;
        width: auto;
    }

    /* O valor encosta à direita, que é o espaço que a hora deixou ao descer. */
    .telemetry-table > tbody > tr > td:nth-child(3) {
        grid-column: 3;
        grid-row: 1;
        text-align: right;
        width: auto;
        min-width: 0;
        overflow: hidden;
    }

    /* Só a seta: a hora daqui esconde-se, e quem a mostra é a linha debaixo do nome. */
    .telemetry-table > tbody > tr > td:nth-child(4) {
        grid-column: 4;
        grid-row: 1;
        padding-right: 0;
        width: auto;
    }

    /* A linha aberta tem uma célula só, numa faixa que vai de ponta a ponta. */
    .telemetry-table > tbody > tr.telemetry-row-panel {
        grid-template-columns: minmax(0, 1fr);
        padding-top: 0;
    }

    /* Aqui quem cola é a linha e não a célula: fora do algoritmo de tabela o bloco que a
       prende passa a ser o `tr`, que tem a altura do próprio cabeçalho. */
    /* A faixa do dia atravessa o cartão de lado a lado, como divisória da lista. */
    .telemetry-table > tbody > tr.telemetry-day {
        grid-template-columns: minmax(0, 1fr);
        position: sticky;
        top: var(--sticky-top, 0px);
        z-index: 1;
        background-color: var(--bs-tertiary-bg);
        margin-inline: -1rem;
        padding-inline: 1rem;
    }

    /* O nome do dia alinha com o ícone das linhas, e não com o recuo que o desktop lhe dá. */
    .telemetry-table > tbody > tr.telemetry-day > td {
        position: static;
        padding-left: 0;
    }

    /* O recuo até onde começa o nome perde-se com a regra que zera o preenchimento das
       células nesta largura, e é ele que faz o texto ler-se como parte da linha de cima. */
    .telemetry-table > tbody > tr.telemetry-row-panel > td {
        padding-left: 2.125rem;
    }

    /* A divisória fica no fim do conjunto e não entre a linha e o que ela abre, senão os dois
       leem-se como eventos diferentes. */
    .telemetry-table > tbody > tr.telemetry-row-openable[aria-expanded="true"] {
        padding-bottom: 0;
        border-bottom: 0;
    }

    /* Nos pedidos o valor é a pastilha de estado, numa calha da largura da mais larga: a pastilha
       não se corta, e o nome corta com reticências. */
    #downlinkRequests .telemetry-table > tbody > tr:not(.telemetry-day):not(.telemetry-row-panel) {
        grid-template-columns: 2.125rem minmax(0, 1fr) 8rem auto;
    }

    #downlinkRequests .telemetry-table > tbody > tr > td:nth-child(3) {
        text-align: left;
    }

    /* A altura de duas linhas serve a coluna estreita do ecrã grande; aqui os detalhes têm
       linha própria. */
    .telemetry-row-stack {
        min-height: 0;
    }
}

/* A truncagem destas duas vive na marcação, em `d-block text-truncate`; aqui fica só o que o
   Bootstrap não dá. */
.telemetry-row-details,
.telemetry-row-time {
    font-size: var(--fs-sm);
}

.form-label-sm {
    /* `block` para um controlo que não seja de bloco não subir para o lado da etiqueta. */
    display: block;
    margin-bottom: .5rem;
}

/* O asterisco de obrigatório vem do CSS, e assim os leitores de ecrã não o anunciam. */
.form-label-sm.required::after {
    content: " *";
    color: var(--bs-danger);
}

/* O bootstrap só mostra o `.invalid-feedback` a seguir aos seus próprios controlos, e o
   fornecedor de um modelo escolhe-se num grupo de botões e não num campo. */
.is-invalid ~ .invalid-feedback {
    display: block;
}

/* As ligações: uma faixa ligada a verde com as quebras a vermelho, que também diferem na
   luminosidade. Abaixo do `lg` cada quebra junta o «caiu» e o «voltou» numa coluna. */
.connection-band {
    height: 12px;
    background: rgba(var(--bs-success-rgb), .6);
}

.connection-band-empty {
    background: var(--bs-tertiary-bg);
    border: 1px dashed var(--bs-border-color);
}

.connection-band-gap {
    z-index: 1;
    background: var(--bs-danger-text-emphasis);
}

.connection-band-scale,
.connection-summary {
    font-size: var(--fs-sm);
}

.connection-toggle {
    width: 1.75rem;
    height: 1.75rem;
    margin: -.5rem 0;
    font-size: var(--fs-sm);
}

.connection-outage {
    display: grid;
    grid-template-columns: .625rem 1fr auto;
    grid-template-areas: "dot from span" "dot to span";
    column-gap: .75rem;
    align-items: center;
    padding: .5rem 0;
    border-top: 1px solid var(--bs-border-color-translucent);
}

.connection-outage-dot {
    grid-area: dot;
    width: .625rem;
    height: .625rem;
    background: var(--bs-danger-text-emphasis);
}

.connection-outage-from { grid-area: from; }
.connection-outage-to { grid-area: to; font-size: var(--fs-sm); }
.connection-outage-span { grid-area: span; }

@media (max-width: 991.98px) {
    .connection-toggle {
        width: 2.75rem;
        height: 2.75rem;
        margin: -1rem 0;
    }
}

@media (min-width: 992px) {
    .connection-outage {
        grid-template-columns: .625rem 1fr 1fr 7rem;
        grid-template-areas: "dot from to span";
    }

    .connection-outage-to { font-size: inherit; }
}

.connection-outage-head {
    padding-top: .25rem;
}

/* A pastilha do ícone numa linha de evento: a mesma ideia do cartão, em 26px. */
.telemetry-row-icon {
    width: 26px;
    height: 26px;
    background: var(--tone-bg, var(--bs-tertiary-bg));
    color: var(--tone-fg, var(--bs-secondary-color));
    font-size: var(--fs-sm);
}

/* O cartão que é o próprio pedido. Sem sombra: o que responde ao clique diz-se em repouso
   pelo avião de papel no canto, e sob o rato pelo tom da categoria a acender. */
.telemetry-card-action {
    cursor: pointer;
    transition: border-color .12s ease, background-color .12s ease;
}

/* O tom da categoria a acender, e não os tokens genéricos, que apagariam a cor do mosaico. */
.telemetry-card-action:hover:not(:disabled),
.telemetry-card-action:focus-visible {
    border-color: var(--tone-fg, var(--bs-primary));
    background: var(--tone-bg, var(--bs-tertiary-bg));
}

/* A pastilha do ícone inverte: sobre o cartão tingido, o branco destaca-a. */
.telemetry-card-action:hover:not(:disabled) .telemetry-card-icon,
.telemetry-card-action:focus-visible .telemetry-card-icon {
    background: var(--bs-body-bg);
    color: var(--tone-fg, var(--bs-body-color));
}

/* O avião de papel: presente em repouso, mas quase apagado -- oito mosaicos com um ícone
   nítido no canto leem-se como oito avisos. Sob o rato ganha o tom da categoria. */
.telemetry-card-hint {
    font-size: var(--fs-label);
    line-height: 1.6;
    color: var(--bs-secondary-color);
    opacity: .4;
    transition: opacity .12s ease, color .12s ease;
}

.telemetry-card-action:hover:not(:disabled) .telemetry-card-hint,
.telemetry-card-action:focus-visible .telemetry-card-hint {
    color: var(--tone-fg, var(--bs-body-color));
    opacity: 1;
}

.telemetry-card-action:disabled {
    cursor: default;
    opacity: .75;
}

@media (prefers-reduced-motion: reduce) {
    .telemetry-card-hint,
    .telemetry-card-action {
        transition: none;
    }
}

/* ===== escolher dispositivo ===== */

/* Altura fixa, para os filtros não colapsarem a caixa debaixo do rato; em fracção do ecrã para
   caber num portátil pequeno. */
/* Altura definida e não `min-height`/`max-height`: uma coluna só sabe onde rolar se a altura
   do pai for um comprimento resolvido, senão transborda em vez de rolar. */
.device-selector-body {
    height: clamp(26rem, 62vh, 46rem);
}

.device-selector-body > * {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    /* A margem negativa das listas de opções chega ao limite da caixa: nada aqui rola de lado. */
    overflow-x: hidden;
}

/* Cada coluna rola por si: os filtros e a lista de dispositivos são independentes. */
/* Quem rola é a lista de filtros lá dentro, para o título e o botão do rodapé ficarem
   quietos enquanto se marca. */
.device-filter-column {
    border-right: 1px solid var(--bs-border-color);
    overflow: hidden;
}

/* O `contain` não passa o fim da lista de filtros ao modal por trás. */
.device-filter-scroll {
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
}

/* O `d-lg-block` do Bootstrap traz `display: block !important` e apagava a coluna flex: este
   `!important` responde a esse, e não é preguiça de especificidade. */
@media (min-width: 992px) {
    .device-filter-column.collapse {
        display: flex !important;
    }
}

@media (max-width: 991.98px) {
    /* Abaixo de lg o modal é o ecrã inteiro e as colunas empilham: a lista rola com a
       página, e não por dentro. */
    .device-selector-body {
        height: auto;
    }

    .device-selector-body > * {
        height: auto;
        overflow-y: visible;
    }

    /* O painel cobre a lista em vez de a empurrar: filtra-se e fecha-se sem sair do sítio, e
       fica um scroll só, o dele. Fixo porque abaixo de lg o modal já é o ecrã inteiro. */
    .device-filter-column {
        position: fixed;
        inset: 0;
        z-index: 3;
        height: auto;
        border-right: 0;
        overflow: hidden;
    }

    /* O `collapse` anima a altura, e a altura deste painel vem dos cantos. */
    .device-filter-column.collapsing {
        height: auto !important;
        transition: none;
    }
}

/* O mosaico de tipos na coluna 4/12 do selector: três por linha, com o nome curto do catálogo. */
.device-type-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* A mesma grelha fora da coluna estreita do selector, com mais colunas onde há largura. */
.device-type-grid.is-wide {
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}

/* O estado vem do `shell.css`, com as outras caixas que se escolhem. */
.device-type-tile {
    padding: .8125rem .5rem .6875rem;
}

/* Um tipo sem dispositivos fica à vista e apagado: saber que a frota não tem pulseiras é informação. */
.device-type-tile:disabled {
    opacity: .45;
    cursor: default;
}

.device-type-tile-icon { font-size: var(--fs-lg); }

.device-type-tile-name {
    font-size: var(--fs-sm);
}

/* A caixa de marcar do mosaico e a da lista de filtros são a mesma, à parte da medida: a do
   mosaico é uma marca no canto, e a da lista é o que o dedo acerta. */
.device-type-tile-check,
.filter-option-box {
    place-items: center;
    border: 1px solid var(--bs-border-color);
    border-radius: 3px;
    background: var(--bs-body-bg);
    color: transparent;
    font-size: var(--fs-micro);
}

.device-type-tile-check {
    width: 14px;
    height: 14px;
    top: .375rem;
    right: .375rem;
}

.filter-option-box {
    width: 20px;
    height: 20px;
    border-radius: 4px;
}

.device-type-tile.selected .device-type-tile-check,
.filter-option.selected .filter-option-box,
.filter-option.partial .filter-option-box {
    border-color: var(--bs-primary);
    background: var(--bs-primary);
    color: var(--brand-on);
}

/* Uma lista de opções sem altura máxima nem rolamento próprio: quem rola é a coluna. */
.filter-list {
    gap: 1px;
    /* As caixas alinham à esquerda com a etiqueta do grupo e com a pesquisa, apesar do `padding`
       da linha que deixa o realce respirar. */
    margin-inline: -.4375rem;
}

/* Sem `width: 100%`: somada à indentação das licenças empurraria a contagem para fora da
   coluna. A borda e o fundo ficam aqui porque os estados abaixo os trocam. */
/* 44px é o alvo de toque mínimo. */
.filter-option {
    gap: .625rem;
    min-height: 44px;
    padding: .3125rem .4375rem;
    border: 1px solid transparent;
    background: transparent;
    font-size: var(--fs-sm);
    cursor: pointer;
}

.filter-option:hover,
.filter-option:focus-visible {
    background: var(--bs-body-bg);
}

/* Um tipo sem dispositivos fica à vista e apagado: saber que a frota não tem pulseiras é informação. */
.filter-option:disabled {
    opacity: .45;
    cursor: default;
}

.filter-option.selected {
    background: var(--bs-body-bg);
    border-color: var(--bs-primary-border-subtle);
    font-weight: 500;
}

/* A licença por baixo da empresa a que pertence, com o traço que as liga. */
.filter-option-nested {
    margin-left: 1.25rem;
    font-size: var(--fs-sm);
}

.filter-branch {
    gap: 1px;
}

/* O traço desce pelo meio das caixas de marcar e pára no centro da última. */
.filter-branch::before {
    content: "";
    position: absolute;
    left: 1.0625rem;
    top: 0;
    bottom: 1.375rem;
    width: 1px;
    background: var(--bs-border-color);
}

/* O mesmo desenho dos filtros, mas de escolha única: a marca leva `rounded-circle` na
   marcação, para não prometer que se podem marcar várias. A empresa é só o cabeçalho. */
.license-picker-company {
    padding: .5rem .4375rem .125rem 2.3125rem;
}

/* ===== cartão de dispositivo =====
   Cada campo cai na mesma abcissa e a lista lê-se a descer por coluna. */
/* O alinhamento, o espaço e o enchimento mudam no telefone: um utilitário do Bootstrap traz
   `!important` e ganharia à regra de baixo. O estado vem do `shell.css`. */
.device-card {
    grid-template-columns: 40px 104px minmax(0, 1fr) 11.75rem 7rem;
    align-items: center;
    gap: .875rem;
    padding: .625rem .875rem;
    border-radius: .5rem;
}

/* O esqueleto empresta a geometria do cartão, mas não é um alvo. */
.device-card-skeleton {
    cursor: default;
}

/* A miniatura empresta a medida e o raio ao esqueleto, mas não o fundo: o `modals.css` vem
   depois do Bootstrap e ganharia ao `.placeholder`. */
.device-card-skeleton .device-card-thumb {
    background-color: currentcolor;
}

/* A barra que substitui a pastilha de estado não tem texto, e por isso leva medida. */
.device-card-skeleton-pill {
    width: 5.25rem;
    height: 1.25rem;
}

/* O mesmo para a barra do «há 8d», com a largura de «sem registo», o mais comprido dos três. */
.device-card-skeleton-when {
    width: 3.75rem;
    height: .6875rem;
    margin-inline-start: .125rem;
}

/* A lista só estica enquanto tem o esqueleto dentro, para não empurrar a paginação para o
   fundo; o `:has` desliga-se sozinho quando chegam os cartões. */
.device-card-list:has(.device-card-skeleton-list) {
    flex: 1 1 auto;
    min-height: 0;
}

/* Ao pedir de novo, o que está no ecrã é o resultado anterior. Esbatido diz que ainda está a
   mudar, e deixa-o legível -- trocar a lista por um esqueleto a cada filtro custa mais. */
.device-card-list[aria-busy="true"] {
    opacity: .55;
}

.device-card-when {
    font-size: var(--fs-sm);
    padding-inline-start: .125rem;
}

/* O `display: contents` promove os dois campos a itens da grelha do cartão, para alinharem a
   descer. No telefone volta a contentor. */
.device-card-fields {
    display: contents;
}

.device-card-thumb {
    width: 40px;
    height: 40px;
    place-items: center;
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}

/* A chapa acompanha a fotografia no escuro, como as chapas pequenas do `modals.css`. O esqueleto
   fica de fora: pinta-se com `currentcolor`. */
[data-bs-theme="dark"] .device-card-thumb:not(.placeholder):has(img) {
    background: #fff;
    filter: brightness(.84);
}

.device-card.offline .device-card-thumb { opacity: .5; }
.device-card.offline .device-card-imei { color: var(--bs-secondary-color); }

/* Duas medidas para as cinco colunas: `--fs-body` no que identifica, `--fs-sm` no que
   qualifica. O IMEI destaca-se por peso, não por tamanho. */
.device-card-meta,
.device-card-field-note {
    font-size: var(--fs-sm);
}

/* Sem o prefixo do cartão: o mesmo par empresa/licença aparece no painel do dispositivo
   escolhido, onde o valor não leva as classes do cartão. */
.license-number {
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
}

.license-separator {
    color: var(--bs-border-color);
    padding: 0 1px;
}

/* No telefone as cinco colunas não cabem, mas continua grelha: em coluna simples a miniatura
   ficaria sozinha numa linha. */
@media (max-width: 767.98px) {
    .device-card {
        grid-template-columns: 40px minmax(0, 1fr);
        grid-template-areas:
            "thumb state"
            "thumb identity"
            "fields fields";
        align-items: start;
        gap: .25rem .625rem;
        padding: .625rem .75rem;
    }

    .device-card-thumb { grid-area: thumb; }
    .device-card > .state-badge { grid-area: state; }
    .device-card-identity { grid-area: identity; min-width: 0; }

    /* O SIM pede a largura que precisa: um número português com indicativo não cabe em 5.25rem.
       A licença fica com o resto e encolhe. */
    .device-card-fields {
        grid-area: fields;
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        gap: .5rem;
        margin-top: .3125rem;
        border-top: 1px solid var(--bs-border-color);
        padding-top: .5rem;
    }

    .device-card-field {
        min-width: 0;
    }
}

/* Mede-se pelo contentor e não pelo ecrã: a segunda coluna só aparece com 11rem para ela. */
.telemetry-card-grid {
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

/* No telemóvel a coluna cabe duas vezes mas o valor não: «2 pessoas» parte-se a meio da
   palavra. Abaixo do `sm` é sempre uma. */
@media (max-width: 575.98px) {
    .telemetry-card-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* O `span: 12` da casca: o mosaico que pede a linha toda. */
.telemetry-card-wide {
    grid-column: 1 / -1;
}

/* A leitura mais recente, sob o nome da categoria, na cor da categoria. */
.telemetry-card-value {
    font-size: var(--fs-lg);
    margin-top: .0625rem;
    color: var(--tone-fg, inherit);
}

/* O ícone de um cartão de telemetria, cinzento por omissão: a categoria lê-se no ícone e não
   na cor, e a cor fica disponível para o estado. */
.telemetry-card-icon {
    width: 36px;
    height: 36px;
    background: var(--tone-bg, var(--bs-tertiary-bg));
    color: var(--tone-fg, var(--bs-secondary-color));
}

/* O relâmpago de uma bateria a carregar, num disco ao canto do azulejo. O anel da cor do
   cartão separa-o do ícone por baixo; sem ele os dois desenhos tocam-se e lêem-se como um. */
.telemetry-card-badge {
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: .95em;
    height: .95em;
    background: var(--bs-warning-text-emphasis);
    color: var(--bs-body-bg);
    font-size: 1em;
    box-shadow: 0 0 0 2px var(--bs-body-bg);
}

.telemetry-card-badge > i {
    font-size: .5em;
}

/* ===== a planta da divisão de um radar =====
   Ver `devices/radar/radar-map-modal.js`, `radar-scene.js` e `radar-vitals.js`. */

/* Mais largo do que o `modal-xl`, para a planta ter altura; o `min()` com `vw` não o deixa
   passar do ecrã num portátil. */
#radarMapModal .modal-dialog {
    --bs-modal-width: min(1680px, 96vw);
}

/* Aberta das configurações, a planta fica por cima do modal do dispositivo, e o segundo fundo
   entre os dois. */
#radarMapModal {
    z-index: 1065;
}

.modal-backdrop ~ .modal-backdrop {
    z-index: 1060;
}

/* A altura sai da proporção da divisão e é o `radar-scene.js` que a escreve, depois de saber
   quanto mede a sala. Aqui fica só o chão para o instante entre abrir e desenhar. */
.radar-map-canvas {
    min-height: 11.25rem;
}

/* A faixa do estado do sono não cresce com a coluna -- é contexto e não uma leitura --, mas tem chão. */
.radar-sleep-state {
    min-height: 4.5rem;
}

/* O azulejo de um sinal vital: como o `.telemetry-card-icon`, mas no tom da categoria, que
   aqui distingue os dois cartões. */
.radar-vital-icon {
    width: 36px;
    height: 36px;
}

/* O chão de um gráfico de sinal vital; daí para cima cresce com o cartão. Ver `radar-vitals.js`. */
.radar-vital-chart {
    position: relative;
    min-height: 6rem;
}

/* Seis pixéis em baixo, para o canto do cartão não cortar o rótulo mais baixo do eixo. */
.radar-vital-frame {
    position: absolute;
    inset: 4px 0 6px;
    display: flex;
    color: var(--radar-vital-color);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Os rótulos empilham-se na mesma célula: a coluna fica com a largura do mais largo. */
.radar-vital-axis {
    display: grid;
    grid-template: 100% / auto;
    font-size: 11px;
}

.radar-vital-axis span {
    position: relative;
    grid-area: 1 / 1;
    align-self: start;
    padding: 3px 5px;
    text-align: right;
    opacity: 0.65;
    transform: translateY(-50%);
}

.radar-vital-plot {
    position: relative;
    flex: 1;
    min-width: 0;
}

.radar-vital-plot svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.radar-vital-fill {
    fill: var(--radar-vital-color);
    fill-opacity: 0.2;
}

.radar-vital-line {
    fill: none;
    stroke: var(--radar-vital-color);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.radar-vital-tooltip {
    --radar-vital-tooltip-bg: color-mix(in srgb, var(--radar-vital-color) 90%, transparent);
    position: absolute;
    z-index: 1;
    padding: 9px 10px 8px;
    border: 1px solid rgb(255 255 255 / 90%);
    border-radius: 4px;
    background: var(--radar-vital-tooltip-bg);
    color: #fff;
    font-size: 14px;
    line-height: 16px;
    white-space: nowrap;
    pointer-events: none;
    transform: translate(-50%, calc(-100% - 4px));
}

.radar-vital-tooltip::after {
    content: "";
    position: absolute;
    top: 100%;
    left: calc(50% + var(--radar-vital-pointer, 0px) - 4px);
    border: 4px solid transparent;
    border-top-color: var(--radar-vital-tooltip-bg);
    border-bottom: 0;
}

.radar-vital-tooltip-below {
    transform: translate(-50%, 4px);
}

.radar-vital-tooltip-below::after {
    top: auto;
    bottom: 100%;
    border-top: 0;
    border-bottom: 4px solid var(--radar-vital-tooltip-bg);
}

/* O editor das áreas: ver `devices/radar/radar-area-editor.js`. A cor do tipo vem na borda esquerda. */
.radar-area-chip {
    gap: 0;
    line-height: 1.2;
    padding: .3rem .6rem;
    background: var(--bs-body-bg);
}

.radar-area-chip.active {
    background: var(--brand-subtle);
    border-color: var(--brand) !important;
}

.radar-area-chip-name {
    font-size: .8rem;
    white-space: nowrap;
}

.radar-area-chip.deleted .radar-area-chip-name {
    text-decoration: line-through;
    opacity: .6;
}

.radar-area-chip-type {
    font-size: .65rem;
    white-space: nowrap;
}

.radar-area-chip-dot {
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: var(--bs-warning);
}

.radar-area-inspector {
    width: 250px;
    z-index: 3;
}

.radar-map-canvas:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

/* O nome ocupa duas linhas antes de cortar, e leva o valor inteiro no `title`. */
.telemetry-card-title {
    font-size: var(--fs-label);
    letter-spacing: .04em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* «Localização» não cabe numa calha de quatro colunas e tem de partir. Com hifenização
       parte onde a palavra parte -- «localiza-ção» -- e não a meio de uma sílaba. */
    hyphens: auto;
    overflow-wrap: break-word;
}

/* A cor da categoria define-se uma vez por tom, e o ícone, a borda e o valor bebem dela. */
.telemetry-card-tone-info { --tone-bg: rgba(var(--bs-info-rgb), .12); --tone-fg: var(--bs-info-text-emphasis); --tone-border: var(--bs-info-border-subtle); }
.telemetry-card-tone-success { --tone-bg: rgba(var(--bs-success-rgb), .12); --tone-fg: var(--bs-success-text-emphasis); --tone-border: var(--bs-success-border-subtle); }
.telemetry-card-tone-warning { --tone-bg: rgba(var(--bs-warning-rgb), .18); --tone-fg: var(--bs-warning-text-emphasis); --tone-border: var(--bs-warning-border-subtle); }
.telemetry-card-tone-danger { --tone-bg: rgba(var(--bs-danger-rgb), .12); --tone-fg: var(--bs-danger-text-emphasis); --tone-border: var(--bs-danger-border-subtle); }
.telemetry-card-tone-primary { --tone-bg: var(--bs-primary-bg-subtle); --tone-fg: var(--bs-primary-text-emphasis); --tone-border: var(--bs-primary-border-subtle); }
.telemetry-card-tone-secondary { --tone-bg: var(--bs-secondary-bg); --tone-fg: var(--bs-secondary-color); --tone-border: var(--bs-border-color); }

/* A borda do cartão acompanha o tom. */
.card[class*="telemetry-card-tone-"] {
    border-color: var(--tone-border);
}

/* ===== modal do dispositivo: configuração =====
   O que veste o cabeçalho e a régua das secções está no `config.css`. */

/* A zona perigosa só existe onde há dispositivo para eliminar, para o aviso não ficar sem botão. */
#deviceDangerZone:has(#deleteDeviceBtn.d-none) {
    display: none;
}

/* As duas leituras de uma definição -- a entrega ao aparelho e a edição por enviar -- são
   desenhadas juntas, e o `data-config-edited` escolhe qual se vê. */
.config-when-changed {
    display: none;
}

[data-config-edited] .config-when-clean {
    display: none;
}

[data-config-edited] .config-when-changed {
    display: inline-flex;
}

/* O campo alterado diz-se também na borda: a pastilha está no topo do bloco e o número que
   se acabou de escrever pode estar a três linhas dela. */
[data-config-edited] .form-control,
[data-config-edited] .form-select,
[data-config-edited] .input-group-text {
    border-color: var(--bs-warning-border-subtle);
}

/* A linha fechada de um alarme. O triângulo do browser sai: o que abre e fecha é a linha
   inteira, e a seta à direita é que o diz. */
.alarm-line {
    list-style: none;
    cursor: pointer;
}

.alarm-line::-webkit-details-marker {
    display: none;
}

.alarm-line-chevron {
    transition: transform .15s ease-in-out;
}

.alarm-line-body[open] > .alarm-line .alarm-line-chevron {
    transform: rotate(180deg);
}
