/* Digital Aid — the layer on top of Pico.
   Pico owns typography, forms, tables, buttons and dark mode. This file owns only what is specific
   to this app: the Clients grid, status colours, Time Left, the code display and the tab bar.
   Everything here reads Pico's custom properties so both themes follow automatically. */

:root {
  --pico-font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  --pico-border-radius: 0.75rem;
  --pico-form-element-spacing-vertical: 0.6rem;

  /* Status colours — the same vocabulary the Client Page timeline uses (client.ejs), so a colour
     means the same thing wherever it appears. Deliberately fixed rather than theme-derived. */
  --da-active: #2e8b57;
  --da-grant: #2b5cd9;
  --da-locked: #9aa4b6;
  --da-blocked: #c0392b;
  --da-offline: #9aa4b6;
  --da-warn-fg: #a06000;
  --da-warn-bg: #fff4e0;
  --da-warn-border: #f0d49a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --da-active: #4cae7a;
    --da-grant: #6d92ea;
    --da-blocked: #e05c4c;
    --da-warn-fg: #f0c078;
    --da-warn-bg: #3a2e18;
    --da-warn-border: #6b5326;
  }
}

/* Pico makes buttons full-width; here most buttons sit next to prose, so opt back out and let the
   few that should stretch say so with .wide. */
button, input[type="submit"] { width: auto; }
button.wide, .wide button { width: 100%; }
button.danger { --pico-background-color: var(--da-blocked); --pico-border-color: var(--da-blocked); }
button.danger:hover { filter: brightness(1.08); }
button.quiet {
  --pico-background-color: transparent;
  --pico-color: var(--pico-primary);
  --pico-border-color: var(--pico-muted-border-color);
}

main.container { padding-block: 1.5rem; }
h1 { margin-bottom: 0.25rem; }
/* Inline monospace runs are usually short codes in prose, and breaking "123456" across a line makes
   an example unreadable. Only table payloads, which can be arbitrarily long, may break. */
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
td.mono { white-space: normal; word-break: break-all; }
.error { color: var(--da-blocked); }
.ok { color: var(--da-active); }
.warn {
  color: var(--da-warn-fg); background: var(--da-warn-bg);
  border: 1px solid var(--da-warn-border); border-radius: 0.4rem;
  padding: 0.05rem 0.4rem; font-size: 0.85em;
}
.muted { color: var(--pico-muted-color); }

/* --- Navigation ---------------------------------------------------------- */
/* Three destinations. Top bar on desktop, thumb-reachable bottom bar on phones — the PWA is
   installed on a phone mostly to hand out an Admin Code, so that bar is the primary navigation. */

.topbar {
  display: flex; align-items: center; gap: 1.5rem;
  padding: 0.7rem 1.2rem; padding-top: max(0.7rem, env(safe-area-inset-top));
  background: var(--pico-card-background-color);
  border-bottom: 1px solid var(--pico-card-border-color);
}
.topbar .brand { display: flex; align-items: center; gap: 0.5rem; font-weight: 700; text-decoration: none; color: var(--pico-color); }
/* The same mark as the favicon and the installed app: a phone that has this on its home screen
   should find the same clock at the top of the page it opens. Its own tile, so it keeps its
   background in both themes rather than dissolving into the bar. */
.topbar .brand img { width: 1.5rem; height: 1.5rem; display: block; border-radius: 0.35rem; }
.topbar nav { display: flex; gap: 1.2rem; align-items: center; margin-left: auto; }
.topbar nav a { text-decoration: none; color: var(--pico-muted-color); }
.topbar nav a:hover { color: var(--pico-color); }
.topbar nav a.on { color: var(--pico-primary); font-weight: 600; }

.tabbar { display: none; }

@media (max-width: 46rem) {
  .topbar nav { display: none; }
  .tabbar {
    display: flex; position: fixed; inset: auto 0 0 0; z-index: 20;
    background: var(--pico-card-background-color);
    border-top: 1px solid var(--pico-card-border-color);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .tabbar a {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
    padding: 0.5rem 0.25rem; min-height: 3.25rem;
    text-decoration: none; color: var(--pico-muted-color); font-size: 0.72rem;
  }
  .tabbar a.on { color: var(--pico-primary); }
  .tabbar svg { width: 22px; height: 22px; }
  /* Keep the last card clear of the bar. */
  main.container { padding-bottom: 5rem; }
}

/* --- Clients grid -------------------------------------------------------- */

.client-grid {
  display: grid; gap: 1rem; margin-top: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

.client-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--pico-card-background-color);
  border: 1px solid var(--pico-card-border-color);
  border-radius: var(--pico-border-radius);
  padding: 1rem 1.1rem;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.client-card:hover { transform: translateY(-2px); box-shadow: var(--pico-card-box-shadow); }
.client-card:focus-visible { outline: 2px solid var(--pico-primary); outline-offset: 2px; }

.client-card .head { display: flex; align-items: center; gap: 0.5rem; }
.client-card .name {
  font-weight: 600; font-size: 1.05rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}

/* Connectivity only — never state. A Disabled Client is online, and a green "all is well" dot
   would be a lie about a PC that is enforcing nothing. */
.dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; flex: none; background: var(--da-offline); }
.dot.online { background: var(--da-active); box-shadow: 0 0 0 3px color-mix(in srgb, var(--da-active) 22%, transparent); }

.badges { display: flex; gap: 0.3rem; margin-left: auto; flex-wrap: wrap; }
/* A badge only ever means: the Admin did this deliberately and it is still in effect. */
.badge {
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
  padding: 0.1rem 0.4rem; border-radius: 0.35rem;
  border: 1px solid currentColor; color: var(--pico-muted-color);
}
.badge.locked { color: var(--da-locked); }
.badge.disabled { color: var(--da-warn-fg); }
.badge.revoked { color: var(--da-blocked); }

.client-card .timeleft {
  font-size: 1.5rem; font-weight: 700; margin-top: 0.65rem; line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
.timeleft.s-active { color: var(--da-active); }
.timeleft.s-grant { color: var(--da-grant); }
.timeleft.s-blocked { color: var(--da-blocked); }
.timeleft.s-locked { color: var(--da-locked); }
.timeleft.s-downtime, .timeleft.s-unknown, .timeleft.s-locked { font-size: 1.15rem; }
.timeleft.s-downtime, .timeleft.s-unknown { color: var(--pico-muted-color); }
/* Offline: last-known, dimmed and stamped — stale-but-labelled beats a confident lie or a blank. */
.client-card.is-offline .timeleft { opacity: 0.55; }

/* What has been used, under what is left. Quiet on purpose: Time Left is the number the card is
   for, this is the context behind it. */
.client-card .used-today {
  margin: 0.15rem 0 0; font-size: 0.85rem; color: var(--pico-muted-color);
  font-variant-numeric: tabular-nums;
}

.client-card .meta {
  display: flex; gap: 0.6rem; flex-wrap: wrap;
  margin-top: 0.5rem; font-size: 0.78rem; color: var(--pico-muted-color);
}
.client-card.is-revoked { opacity: 0.6; }

/* The events themselves moved to the logs page; what is left on the day card is the mark on the
   timeline and this one way through to the rest. */
.day-foot { margin: 1rem 0 0; }

/* --- Block Screen Backgrounds -------------------------------------------- */

.bg-slots { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 40rem) { .bg-slots { grid-template-columns: 1fr 1fr; } }
.bg-slot h4 { margin-bottom: 0.15rem; }
.bg-slot form { margin: 0.4rem 0 0; }
.bg-slot input[type="file"] { margin-bottom: 0.4rem; }

/* 16:9 because that is the shape most of these end up cropped to on a monitor. object-fit matches
   the UniformToFill the client draws with, so the preview crops where the real thing crops. */
.bg-thumb {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: 0.5rem; border: 1px solid var(--pico-muted-border-color); background: #121624;
}
.bg-thumb-empty { background: #121624; }
.legend .legend-sep { border-left: 1px solid var(--pico-muted-border-color); padding-left: 0.75rem; }
.legend i.mark { background: #a06000; border-radius: 2px; }

/* --- Admin Code --------------------------------------------------------- */

.code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: clamp(2.4rem, 12vw, 3.4rem); font-weight: 700; letter-spacing: 0.1em;
  margin: 0.3rem 0; line-height: 1.1; font-variant-numeric: tabular-nums;
}
.code-row, .row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }

/* The two codes are told apart by colour first and by length second. Blue is not decoration: it is
   the same blue the timeline uses for extra time, so "extra time" is one colour across the whole
   product. Red is the app's blocked/danger colour and lands on the code that can stop the app.
   Deliberately not red-and-green — that is the one pair a colour-blind reader cannot separate, and
   these get read out in a hurry. The digit count is the tell that survives colour entirely. */
.code-admin { color: var(--da-blocked); }
.code-extra { color: var(--da-grant); }

.code-tag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 0.15rem 0.5rem; border-radius: 2rem; vertical-align: middle; margin-left: 0.4rem;
  border: 1px solid currentColor;
}
.code-tag-admin { color: var(--da-blocked); }
.code-tag-extra { color: var(--da-grant); }

/* Louder than .muted and quieter than an error: this is a standing property of the Admin Code, not
   something that has gone wrong. */
.warn-note {
  color: var(--da-warn-fg); background: var(--da-warn-bg);
  border: 1px solid var(--da-warn-border); border-radius: 0.5rem;
  padding: 0.6rem 0.75rem; font-size: 0.9rem; margin: 0.5rem 0;
}

/* Presets. The last choice is remembered on the device — most households give the same amount
   most days, so the common case is zero taps. */
.chips { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; margin: 0.5rem 0; }
.chips button {
  padding: 0.35rem 0.85rem; min-height: 2.6rem; border-radius: 2rem;
  --pico-background-color: transparent;
  --pico-color: var(--pico-color);
  --pico-border-color: var(--pico-muted-border-color);
}
.chips button.on {
  --pico-background-color: var(--pico-primary);
  --pico-border-color: var(--pico-primary);
  --pico-color: var(--pico-primary-inverse);
}
.chips input { width: 7rem; margin: 0; }

/* Validity is drawn, not counted down in text: verification allows ±1 step, so a shown code is
   good for ~30–60s and a ticking "7s left" reads as panic where none is warranted. */
.validity { display: flex; align-items: center; gap: 0.5rem; color: var(--pico-muted-color); font-size: 0.85rem; }
.ring { width: 1.15rem; height: 1.15rem; flex: none; transform: rotate(-90deg); }
.ring circle { fill: none; stroke-width: 3; }
.ring .track { stroke: var(--pico-muted-border-color); }
.ring .value { stroke: var(--pico-primary); transition: stroke-dashoffset 0.9s linear; }

.copied { color: var(--da-active); font-size: 0.85rem; }
.grant-says { font-size: 0.95rem; color: var(--pico-muted-color); margin: 0.15rem 0 0; }

.offline-strip {
  display: flex; align-items: center; gap: 0.5rem;
  background: var(--da-warn-bg); color: var(--da-warn-fg);
  border: 1px solid var(--da-warn-border); border-radius: var(--pico-border-radius);
  padding: 0.6rem 0.9rem; margin-bottom: 1rem; font-size: 0.9rem;
}

/* --- Client Page --------------------------------------------------------- */

.timeleft-hero { font-size: 1.8rem; font-weight: 700; margin: 0.2rem 0 0.1rem; }

/* Quick actions: the three live commands, above the day card, each its own form posting to its own
   endpoint. One wrapping row rather than a grid — the message box is the only elastic thing here, so
   it takes the slack on a wide screen and the row breaks into three stacked ones on a phone by
   itself. */
.quick h2 { font-size: 1.1rem; margin-bottom: 0.6rem; }
.quick-row { display: flex; flex-wrap: wrap; gap: 0.6rem 1.2rem; align-items: center; }
.quick-row form { display: flex; align-items: center; gap: 0.4rem; margin: 0; }
.quick-row select, .quick-row input { margin: 0; width: auto; }
.quick-message { flex: 1 1 16rem; }
.quick-message input { flex: 1 1 auto; min-width: 0; }
.quick-give select { flex: none; }
/* Icons ride with the label, not instead of it: the lock button is a toggle and a padlock alone
   cannot say which way it is about to go. */
.quick-row button { display: inline-flex; align-items: center; gap: 0.45rem; white-space: nowrap; }
.quick-row button svg { width: 1.1em; height: 1.1em; flex: none; }

/* Each action is its own form. The caption under a button explains what that button does, so it
   belongs on its own line rather than colliding with the next one. */
.actions form { margin-bottom: 1rem; }
.actions form:last-child { margin-bottom: 0; }
.actions form > small { display: block; margin-top: 0.3rem; }
.actions label { margin-bottom: 0.35rem; }
.actions hr { margin: 1.2rem 0; }
/* What the day added up to, under the heading. Muted, but not shrunk to a footnote — it is the one
   number on this card that does not need the timeline read to be understood. */
.day-used { font-size: 1.05rem; }

.day-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
.day-head h2 { margin: 0; }
.inline { margin: 0; } .inline input { margin: 0; }
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.scroll-x { overflow-x: auto; }
svg.timeline { width: 100%; min-width: 640px; height: 40px; display: block; }
svg.bars { width: 100%; min-width: 640px; height: 140px; display: block; margin-top: 0.4rem; }
.legend { display: flex; gap: 1rem; flex-wrap: wrap; font-size: 0.8rem; color: var(--pico-muted-color); margin-top: 0.3rem; }
.legend i { display: inline-block; width: 12px; height: 12px; border-radius: 2px; vertical-align: -1px; margin-right: 0.25rem; }
.apps { display: flex; flex-direction: column; gap: 0.3rem; margin-top: 0.3rem; }
.app-row { display: grid; grid-template-columns: 8rem 1fr 4rem; align-items: center; gap: 0.5rem; }
.app-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-bar { background: var(--pico-secondary-background); border-radius: 3px; height: 14px; }
.app-bar i { display: block; height: 14px; background: var(--da-grant); border-radius: 3px; }
.app-min { text-align: right; font-variant-numeric: tabular-nums; color: var(--pico-muted-color); font-size: 0.85rem; }

/* A row that has just arrived on a poll gets a brief highlight and nothing more. Alerting is a
   separate feature with its own transport. */
@keyframes da-flash { from { background: color-mix(in srgb, var(--pico-primary) 22%, transparent); } to { background: transparent; } }
.is-new { animation: da-flash 1.6s ease-out; }

.tabs { display: flex; gap: 0.5rem; margin: 0.5rem 0; }
.tabs a { padding: 0.35rem 0.8rem; border: 1px solid var(--pico-muted-border-color); border-radius: 0.5rem; text-decoration: none; }
.tabs a.on { background: var(--pico-primary); color: var(--pico-primary-inverse); border-color: var(--pico-primary); }
.pager { display: flex; gap: 1rem; margin-top: 0.6rem; }

@media (max-width: 46rem) {
  .columns { grid-template-columns: 1fr; }
  input, select { font-size: 16px; }   /* 16px stops iOS zooming in on focus */
  table { display: block; overflow-x: auto; white-space: nowrap; }
  .app-row { grid-template-columns: 6rem 1fr 3.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .client-card, .ring .value, .is-new { transition: none; animation: none; }
}

/* --- Requests -------------------------------------------------------------
   The one screen where someone else puts work on the Admin, so the count rides the nav on every
   page. Filled by a live fragment, and empty (so invisible) when nobody is waiting. */
.ask-badge:empty { display: none; }
.ask-badge .badge.ask {
  color: var(--pico-primary-inverse); background: var(--da-blocked); border-color: var(--da-blocked);
  margin-left: 0.3rem; font-variant-numeric: tabular-nums;
}
.tabbar .ask-badge { position: absolute; top: 0.15rem; margin-left: 1.6rem; }
.tabbar a { position: relative; }

.request header { display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
.request .badge.offline { color: var(--da-offline); }
.request .decide { display: flex; gap: 0.5rem; align-items: center; margin: 0.6rem 0 0; flex-wrap: wrap; }
.request .decide input { width: 6rem; margin: 0; }
.request .decide button { width: auto; margin: 0; }
.request + .request { margin-top: 0.8rem; }

.quiet-empty { color: var(--pico-muted-color); }
.past-requests td { padding: 0.25rem 0.6rem 0.25rem 0; border: 0; }
.past-requests tr.s-lapsed { color: var(--pico-muted-color); }
.past-requests tr.s-approved td:last-child { color: var(--da-active); }
.past-requests tr.s-declined td:last-child { color: var(--da-blocked); }
