/* Page-level layout for the desk pages rebuilt from the v2 boards.
 *
 * Only layout that one page needs and no other page shares. Anything two
 * pages both want belongs in fnw.css's board kit instead, and colours come
 * from fnw.css's tokens — never a hex typed here.
 *
 * Loaded by the desk's index.html alone (signup and reset load fnw.css
 * without it), so what is restyled here is the desk and nothing else.
 */

/* ── the shell: rail ───────────────────────────────────────────────────
 * D-Dashboard's rail: 264px on the deep surface, the brand row, the
 * "Working in" box, 36px items with an inset violet stripe on the one you
 * are on, and quiet uppercase section heads. */
#app { grid-template-columns: 264px minmax(0, 1fr); }

.rail {
  background: var(--deep);
  padding: 18px 14px;
  gap: 2px;
}
.rail .brandrow { display: flex; align-items: center; gap: 10px; padding: 4px 8px 16px; }
.rail .brandrow .mark { flex: 0 0 auto; display: block; border-radius: 14px; }
.rail .brandrow .brand-logo { width: 40px; height: 40px; object-fit: contain; border-radius: 12px; }
.rail .brand-text { min-width: 0; }
.rail .brand-text b { font-weight: 600; font-size: 14.5px; color: var(--deep-ink); line-height: 1.25; letter-spacing: -0.005em; }
.rail .brand-text small {
  font-size: 12px; color: var(--deep-dim); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* The "Working in" box — .rail .net on the board. */
.rail-net { padding: 0 0 8px; }
.rail-net > .net {
  padding: 10px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.07);
  color: var(--rail-fg);
}
.rail-net > .net svg { width: 18px; height: 18px; flex: 0 0 18px; color: var(--deep-dim); }
.rail-net .net-label { color: var(--deep-dim); }
.rail-net .net-name { color: var(--deep-ink); }
.rail-net .net-menu { left: 0; right: 0; }
.rail-net .net-menu .net-manage { border-top: 1px solid var(--line); border-radius: 0 0 10px 10px; margin-top: 4px; padding-top: 11px; }
.rail-net .net-menu .net-manage .netmark { background: var(--sunk); color: var(--ink-2); }

nav { padding: 0; }
nav .group { margin-top: 0; }
nav .group > h6 {
  margin: 0;
  padding: 14px 10px 6px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--deep-dim);
}
nav .group > h6::after { display: none; }
nav button.it {
  height: 36px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 10px;
  margin: 0 0 2px;
  border-radius: 10px;
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--rail-fg);
  opacity: 1;
}
nav button.it span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
nav button.it svg {
  width: 18px; height: 18px; flex: 0 0 18px;
  stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.85;
}
nav button.it:hover { background: rgba(255, 255, 255, 0.06); }
nav button.it[aria-current="true"] {
  background: rgba(255, 255, 255, 0.08);
  color: var(--deep-ink);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--deep-edge);
}
nav button.it[aria-current="true"] svg { opacity: 1; }
nav button.it[data-soon] { opacity: 0.45; }
/* Locked: the row is still there — it is how an operator sees what they are
 * locked out of — and it no longer looks like a door that opens. Pressing it
 * still says why, which is better than nothing happening. */
nav button.it[data-locked] { opacity: 0.4; }

.rail-footer { margin: auto 0 0; padding: 16px 10px 4px; border-top: 0; font-size: 12px; line-height: 1.5; color: var(--rail-fg-dim); }
/* What the server is running. The quietest thing on the page until the day
 * it is the only thing worth reading, so: the rail's dim ink, the mono face
 * a commit belongs in, and `auto` top margin so it sits on the floor of the
 * rail whether or not the business set a footer above it. */
.rail-build {
  margin: auto 0 0;
  padding: 4px 10px 2px;
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: 0.02em;
  color: var(--rail-fg-dim);
  opacity: 0.6;
}
/* The deploy is half-done. Still small — it is not the operator's emergency
 * — but no longer something the eye slides over. */
.rail-build.stale { color: var(--rail-warn); opacity: 1; cursor: help; }
/* With a business footer above it, that line owns the floor and this one
 * tucks under it rather than fighting for the same `auto` margin. */
.rail-footer:not(.hidden) + .rail-build { margin-top: 0; }

/* ── the shell: top bar ────────────────────────────────────────────────
 * 68px: the page's name, what it counts, its own buttons, the search, the
 * operator. */
.topbar {
  min-height: 68px;
  height: 68px;
  gap: 14px;
  padding: 0 32px;
  background: var(--surface);
  backdrop-filter: none;
}
.topbar .page-title {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 0 1 auto;
}
.topbar .rail-toggle { display: none; }
.topbar .page-title { flex-shrink: 0; max-width: 40%; }
.topbar .page-meta { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; overflow: hidden; }
.topbar .page-meta .t-caption { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar .page-meta:empty, .topbar .page-tools:empty { display: none; }
.topbar .page-tools { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.topbar .spacer { flex: 1 1 auto; }
.topbar .search { width: min(380px, 30vw); flex: 0 1 auto; }
.topbar .search .ico-s { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: 0 0 18px; }
.topbar .search input { padding: 0; border: 0; background: transparent; box-shadow: none; font-size: 13.5px; }
.topbar .search input:focus { box-shadow: none; }
.topbar .search input::-webkit-search-cancel-button { display: none; }
.topbar .who { gap: 8px; }
.topbar .user .av { width: 38px; height: 38px; border-radius: 12px; }
/* The board draws the operator as their initials alone; the name is the
   avatar's tooltip. Kept in the page for a screen reader. */
.topbar .user span#who { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.topbar .user { position: relative; }
.topbar .user span#who.unused { font-size: 13px; color: var(--ink-2); white-space: nowrap; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }
.topbar #uilang { height: 38px; }
.topbar .iconbtn .ico { width: 20px; height: 20px; }
.ico-s { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }

/* ── the page ──────────────────────────────────────────────────────── */
main#view { padding: 28px 32px 72px; max-width: 1640px; }
main#view > .lede, .lede { margin: 0 0 20px; color: var(--ink-2); font-size: 14.5px; line-height: 1.55; max-width: 76ch; }
.loading-page { display: flex; flex-direction: column; gap: 12px; }
.loading-page .skel { height: 96px; border-radius: 14px; }
.loading-page .skel + .skel { height: 260px; border-radius: 20px; }

/* What every page already draws, in the board's words.
 *
 * The pages without their own board (plans, vouchers, stock, pools, NAT,
 * alerts, applications, monitoring, ACS, settings) are built from card(),
 * table(), pill() and plain form fields. Restyling those here puts the whole
 * desk in one design without rewriting ten screens' markup. */
#view .card { border-radius: 20px; padding: 20px 22px; margin-bottom: 16px; }
#view .card > strong:first-child { display: block; font-size: 15px; font-weight: 600; color: var(--ink); }
#view .card.tight { padding: 12px 14px; }
#view .card.empty { display: flex; flex-direction: column; gap: 6px; }
#view .card.empty .t-caption { margin: 0; }
#view .vstack > .card, #view .hrow > .card, #view .between > .card,
#view .grid2 > .card, #view .grid3 > .card, #view .grid4 > .card, #view .split > .card,
#view .kanban .card, #view .grid-auto > * { margin-bottom: 0; }

#view h1 { font-size: 26px; font-weight: 600; letter-spacing: -0.022em; line-height: 1.15; }
#view h2 { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; }

#view table { font-size: 13.5px; }
#view th {
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em;
  padding: 10px 14px; background: var(--surface-2); border-bottom: 1px solid var(--line);
}
#view td { padding: 12px 14px; border-top: 0; border-bottom: 1px solid var(--line); color: var(--ink-2); }
#view tbody tr:last-child td { border-bottom: 0; }
#view td:first-child { color: var(--ink); font-weight: 600; }
#view table.board td:first-child { font-weight: inherit; color: inherit; }
#view table.board td.strong { color: var(--ink); font-weight: 600; }
#view td.num .btn + .btn, #view td .btn + .btn { margin-left: 6px; }
#view td.muted { color: var(--dim); }
#view td code, #view .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
#view .card:has(> table, > .table-scroll) > :not(table):not(.table-scroll) { padding-left: 22px; padding-right: 22px; }

/* The status pill, as the board's chip. */
#view .pill {
  height: 24px; padding: 0 9px; gap: 6px;
  font-size: 11.5px; font-weight: 600; line-height: 1;
  background: var(--sunk); color: var(--ink-2);
}
#view .pill::before { opacity: 0.9; }
#view .pill.good { background: var(--good-tint); color: var(--good-ink); }
#view .pill.warn { background: var(--warn-tint); color: var(--warn-ink); }
#view .pill.bad  { background: var(--bad-tint);  color: var(--bad-ink); }
#view .pill.info { background: var(--info-tint); color: var(--info-ink); }

/* The old tile, as the board's stat. */
#view .tiles { gap: 16px; margin-bottom: 20px; }
#view .tile { border-radius: 14px; padding: 18px; }
#view .tile .k { font-size: 12px; }
#view .tile .v { font-size: 30px; margin-top: 6px; line-height: 1.1; }
#view .tile .n { font-size: 11.5px; margin-top: 2px; }
#view .tile::after { inset: 0 14px auto 14px; }

/* Tabs, as the board's segmented control. */
#view .tabs { align-self: flex-start; background: var(--sunk); padding: 4px; gap: 4px; border-radius: 12px; margin-bottom: 20px; }
#view .tabs button { height: 34px; padding: 0 16px; border-radius: 9px; font-size: 13px; color: var(--ink-2); }
#view .tabs button[aria-selected="true"] { color: var(--ink); }

/* Buttons at the board's two sizes. The desk's plain .btn is the board's
 * ghost: surface, hairline border. The board's .ghost is that too. */
.btn { min-height: 38px; padding: 0 14px; border-radius: 11px; font-size: 13.5px; font-weight: 600; }
.btn.primary { padding: 0 16px; }
.btn.sm { min-height: 32px; padding: 0 11px; font-size: 12.5px; border-radius: 9px; }
.btn.lg { min-height: 50px; padding: 0 20px; font-size: 15px; border-radius: 14px; }
.btn.block { width: 100%; }
.btn.ghost { background: var(--surface); border: 1px solid var(--line-strong); color: var(--ink); }
.btn.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--primary); color: var(--primary-ink); }
.btn.quiet { background: var(--sunk); border-color: transparent; color: var(--ink); }
.btn .ico-s { width: 16px; height: 16px; }
.textlink:disabled { opacity: 0.5; cursor: default; }

/* Fields, as the board's .field/.input. */
#view label { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
#view input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), #view select {
  min-height: 42px; border-radius: 12px; background: var(--surface); padding: 9px 12px;
}
#view input[type="file"] { padding: 8px; border-radius: 12px; background: var(--surface); }
#view textarea { border-radius: 12px; background: var(--surface); }
#view .row { gap: 14px; }
#view .note { border-radius: 12px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { margin: 0; }
.help { font-size: 12px; color: var(--dim); line-height: 1.45; }

/* ── sign in: D-SignIn ─────────────────────────────────────────────── */
.signin-wrap { grid-template-columns: minmax(420px, 620px) minmax(0, 1fr); }
.signin-art {
  background: var(--deep);
  color: var(--deep-ink);
  padding: 64px;
  justify-content: flex-start;
  gap: 28px;
}
.signin-art .art-brand { gap: 12px; }
.signin-art .art-brand svg { border-radius: 14px;  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08); }
.signin-art .art-brand b { font-weight: 600; font-size: 17px; letter-spacing: -0.01em; }
.signin-art .art-copy { display: flex; flex-direction: column; gap: 18px; margin-top: 80px; max-width: none; }
.signin-art .art-copy .t-eyebrow { color: var(--deep-dim); }
.signin-art h2 { margin: 0; font-size: 48px; font-weight: 600; letter-spacing: -0.035em; line-height: 1.05; color: var(--deep-ink); }
.signin-art p { margin: 0; font-size: 16px; line-height: 1.6; color: var(--deep-dim); max-width: 460px; }
.signin-art .art-foot { margin-top: auto; color: var(--deep-dim); font-size: 12.5px; }

.signin-form { background: var(--bg); padding: 40px 24px; }
.signin-form .inner.card { width: 440px; max-width: 100%; padding: 36px; display: flex; flex-direction: column; gap: 18px; margin: 0; border-radius: 20px; }
.signin-form .signin-head { gap: 6px; }
.signin-form h1.t-h1 { margin: 0; font-size: 26px; }
.signin-form .field + .field { margin-top: 0; }
.signin-form .field input { min-height: 52px; border-radius: 14px; background: var(--surface); font-size: 15px; padding: 0 14px; }
.signin-form .btn.primary { width: 100%; margin-top: 0; min-height: 50px; border-radius: 14px; font-size: 15px; }
.signin-form .btn.ghost { min-height: 50px; border-radius: 14px; font-size: 14px; text-decoration: none; white-space: normal; text-align: center; }
.signin-form .signin-row { align-items: flex-start; }
.signin-form .signin-row .t-caption { max-width: 60%; }
.signin-form .or-rule { gap: 10px; }
.signin-form .or-rule .hair { flex: 1 1 auto; }
#signin-note:empty { display: none; }
#signin-note .note { margin: 0; }

/* ── dashboard: D-Dashboard ────────────────────────────────────────── */
.dash { display: flex; flex-direction: column; gap: 20px; }
.dash .stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.dash .stats.six { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.dash .stats .tile { text-align: left; font: inherit; color: inherit; cursor: pointer; width: 100%; }
/* The dashboard's figures are still the old .tile buttons underneath —
   tests and the keyboard both hold them to that — so the tile's own grid
   span and hairline are undone here and the stat's are used. */
.dash .stats .tile { grid-column: auto !important; border-radius: 14px; padding: 18px; }
.dash .stats .tile::after { display: none; }
.dash .stats .tile::before { content: ""; position: absolute; top: 0; left: 14px; right: 14px; height: 2px; border-radius: 0 0 2px 2px; background: var(--line-strong); }
.dash .stats .tile[data-stat="good"]::before { background: var(--good); }
.dash .stats .tile[data-stat="warn"]::before { background: var(--warn); }
.dash .stats .tile[data-stat="bad"]::before { background: var(--bad); }
.dash .stats .tile .l { font-size: 12px; color: var(--dim); font-weight: 500; }
.dash .stats .tile .v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 30px; font-weight: 500; letter-spacing: -0.03em; margin-top: 6px; color: var(--ink); line-height: 1.15; }
.dash .stats .tile .n { font-size: 11.5px; color: var(--dim); margin-top: 2px; }
.dash .stats .tile:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
.dash .stats .tile:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.dash .main { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.dash .panel { padding: 22px; display: flex; flex-direction: column; gap: 14px; margin: 0; }
.dash .panel.needs { gap: 12px; }
.dash .alertcard { background: var(--bad-tint); border: 0; border-radius: 14px; padding: 14px; display: flex; gap: 12px; align-items: flex-start; }
.dash .alertcard.warn { background: var(--warn-tint); }
.dash .alertcard .ico-s { color: var(--bad-ink); margin-top: 1px; }
.dash .alertcard.warn .ico-s { color: var(--warn-ink); }
.dash .alertcard .t-strong { font-size: 14px; }
.dash .needs .li { padding: 10px 0; min-height: 0; }
.dash .needs .li .name { font-size: 14px; }
.dash .nets { display: flex; flex-direction: column; gap: 10px; }
.dash .nets .grid4 { gap: 16px; }
.card.netcard { display: flex; align-items: center; gap: 12px; padding: 16px; margin: 0 !important; text-align: left; font: inherit; width: 100%; }
.netcard .vstack { min-width: 0; }
.netcard .t-strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
button.card.link { color: inherit; }

/* ── customers: D-Customers / D-Customer ───────────────────────────── */
.book { display: flex; flex-direction: column; gap: 16px; }
.book .table-card { padding: 0 !important; overflow: hidden; margin: 0; }
.book .table-scroll { overflow-x: auto; }
.book td .nameLink { font: inherit; font-weight: 600; color: var(--ink); background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
.book td .nameLink:hover { color: var(--primary-ink); text-decoration: underline; text-underline-offset: 3px; }
.book td.owes { color: var(--bad-ink); font-weight: 500; }
.book td.credit { color: var(--good-ink); }
.hidden-q { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* The book's own search box: the top bar's search types into it on a wide
   screen, so it is out of sight there; on a phone it is the search. */
.book .page-q { display: none; }
.book .page-q input { border: 0; background: transparent; box-shadow: none; min-height: 0 !important; padding: 0 !important; }
.showing-detail > :not(#detail) { display: none !important; }
#view:has(.showing-detail) > .tabs { display: none; }

.cust { display: flex; flex-direction: column; gap: 20px; }
.cust .head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cust .head .who { display: flex; flex-direction: column; min-width: 0; }
.cust .head .who .t-title { font-size: 18px; }
.cust .head .acts { display: flex; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.cust .body { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 20px; align-items: start; }
.cust .deep { padding: 24px; margin: 0; }
.cust .deep .planname { font-size: 28px; font-weight: 600; letter-spacing: -0.02em; }
.cust .deep .price { font-size: 26px; }
.cust .side { padding: 20px; display: flex; flex-direction: column; gap: 14px; margin: 0; }
.cust .kv { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13.5px; }
.cust .kv > :first-child { color: var(--ink-2); }
.cust .kv > :last-child { font-weight: 500; color: var(--ink); text-align: right; }
.cust .opts { display: flex; flex-direction: column; gap: 14px; }
.cust .stat .v.word { font-family: var(--font-body); font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.cust .side .hair:has(+ #detail-out:empty) { display: none; }
.cust .opts .row { margin: 0 !important; }
#detail-out:empty { display: none; }

/* ── new connection: D-NewConnection ───────────────────────────────── */
.nc { display: grid; grid-template-columns: 240px minmax(0, 1fr) 360px; gap: 24px; align-items: start; }
.nc .steplist { display: flex; flex-direction: column; gap: 6px; position: sticky; top: 96px; }
.nc .step { display: flex; align-items: center; gap: 12px; padding: 12px; border-radius: 14px; border: 0; background: transparent; font: inherit; text-align: left; cursor: pointer; color: inherit; width: 100%; }
.nc .step:hover { background: var(--surface-3); }
.nc .step .dot { width: 28px; height: 28px; flex: 0 0 28px; border-radius: 999px; border: 1.5px solid var(--line-strong); display: inline-flex; align-items: center; justify-content: center; font-size: 13px; color: var(--dim); }
.nc .step.done .dot { background: var(--good); border-color: var(--good); color: var(--primary-fg); }
.nc .step.done .dot svg { width: 14px; height: 14px; stroke-width: 3; }
.nc .step.on { background: var(--primary-tint); }
.nc .step.on .dot { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); font-weight: 600; }
.nc .step .vstack span:first-child { font-size: 13.5px; font-weight: 600; }
.nc .step.on .vstack span:first-child { color: var(--primary-ink); }
.nc .step:not(.on):not(.done) .vstack span:first-child { font-weight: 500; color: var(--ink-2); }
.nc .pane { display: flex; flex-direction: column; gap: 18px; }
.nc .pane .grid2 { gap: 16px; }
.nc .pane .grid3 { gap: 12px; }
.nc #file { min-height: 50px; border-radius: 14px; font-size: 15px; }
.nc .plancard { cursor: pointer; transition: transform 200ms var(--ease), box-shadow 200ms var(--ease), border-color 200ms; }
.nc .plancard:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); border-color: var(--line-strong); }
.nc .plancard { display: flex; flex-direction: column; gap: 6px; text-align: left; font: inherit; color: inherit; width: 100%; padding: 16px; }
.nc .plancard.on { border: 2px solid var(--primary); background: var(--primary-tint); }
.nc .plancard .num { font-size: 22px; }
.nc .plancard .ico-s { color: var(--primary); }
.nc .navrow { display: flex; gap: 12px; margin-top: 8px; }
.nc .aside { padding: 22px; display: flex; flex-direction: column; gap: 12px; margin: 0; position: sticky; top: 96px; }
.nc .aside .tick { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; color: var(--ink-2); }
.nc .aside .tick .ico-s { color: var(--good); margin-top: 1px; }
.nc .aside .total .num { font-size: 22px; font-weight: 600; }
.nc-creds .credbox { font-family: var(--font-mono); font-size: 17px; padding: 10px 12px; background: var(--surface-2); border-radius: 12px; user-select: all; }

/* ── access network: D-Access ──────────────────────────────────────── */
.an { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 20px; align-items: start; }
.an .col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.an .olt { padding: 20px; display: flex; flex-direction: column; gap: 14px; margin: 0; }
.an .legend { display: flex; gap: 14px; flex-wrap: wrap; }
.an .legend span { display: inline-flex; align-items: center; gap: 6px; }
.an .legend i, .an .sq { width: 10px; height: 10px; border-radius: 3px; display: inline-block; background: var(--sunk); border: 1px solid var(--line-strong); }
.an .sq.good, .an .legend i.good { background: var(--good); border-color: var(--good); }
.an .sq.warn, .an .legend i.warn { background: var(--warn); border-color: var(--warn); }
.an .sq.bad, .an .legend i.bad { background: var(--bad); border-color: var(--bad); }
.an .port { display: flex; align-items: center; gap: 14px; width: 100%; padding: 8px; margin: 0 -8px; border-radius: 10px; border: 0; background: transparent; font: inherit; color: inherit; cursor: pointer; text-align: left; box-sizing: content-box; }
.an .port:hover { background: var(--surface-2); }
.an .port.on { background: var(--primary-tint); }
.an .port.bad { background: var(--bad-tint); }
.an .port .pname { width: 64px; flex: 0 0 64px; font-family: var(--font-mono); font-size: 12.5px; color: var(--dim); }
.an .port .strip { flex: 1 1 auto; display: grid; grid-template-columns: repeat(32, minmax(0, 1fr)); gap: 3px; min-height: 18px; align-items: center; }
.an .port .strip i { height: 18px; border-radius: 3px; background: var(--sunk); }
.an .port .strip i.good { background: var(--good); }
.an .port .strip i.warn { background: var(--warn); }
.an .port .strip i.bad { background: var(--bad); }
.an .port .strip .t-caption { grid-column: 1 / -1; }
.an .port .cnt { width: 64px; flex: 0 0 64px; text-align: right; font-size: 12.5px; color: var(--dim); }
.an .aside { padding: 22px; display: flex; flex-direction: column; gap: 14px; margin: 0; position: sticky; top: 96px; }
.an .aside .table-scroll { margin: 0 -22px; border-top: 1px solid var(--line); }
.an .aside table.board td, .an .aside table.board th { padding: 10px 12px; }

/* ── faults: D-Faults ──────────────────────────────────────────────── */
.kanban { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; align-items: start; }
.kanban > section { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.kanban .fcard { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; text-align: left; font: inherit; color: inherit; width: 100%; }
.kanban .fcard .t-strong { font-size: 14px; }
.kanban .fcard.late { border-color: var(--bad-tint); }
.kanban .fcard-done { display: flex; flex-direction: column; opacity: 0.75; gap: 6px; padding: 14px 16px; }
.kanban .fcard-done .ico-s { color: var(--good); }
.kanban button.fcard { cursor: pointer; transition: transform 200ms var(--ease), box-shadow 200ms var(--ease), border-color 200ms; }
.kanban button.fcard:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); border-color: var(--line-strong); }
.kanban #raise { width: 100%; }
.kanban .raisebox { display: flex; flex-direction: column; gap: 12px; padding: 18px; }
.kanban .raisebox .row { margin: 0 !important; flex-direction: column; align-items: stretch; gap: 10px; }
.kanban .raisebox .row > * { flex: 0 0 auto; }
.kanban .infocard { background: var(--info-tint); border-color: transparent; padding: 12px 14px; }
.kanban .infocard .t-caption { color: var(--info-ink); }
#clearbox:empty, #fault-note:empty { display: none; }

/* ── collections: D-Collections ────────────────────────────────────── */
.coll { display: flex; flex-direction: column; gap: 20px; }
.coll .top { display: grid; grid-template-columns: minmax(0, 1.2fr) repeat(3, minmax(0, 1fr)); gap: 16px; }
.coll .top .deep { padding: 22px; margin: 0; }
.coll .top .deep .num { font-size: 38px; font-weight: 500; letter-spacing: -0.04em; margin-top: 8px; display: block; }
.coll .top .stat .v { font-size: 26px; }
.coll .tabs { margin-bottom: 0 !important; }
.coll .table-card { padding: 0 !important; overflow: hidden; margin: 0; }
.coll td.amt { color: var(--bad-ink); font-weight: 600; }
#run-note:empty { display: none; }

/* ── wallet: D-Wallet ──────────────────────────────────────────────── */
.wal { display: grid; grid-template-columns: 420px minmax(0, 1fr); gap: 20px; align-items: start; }
.wal .col { display: flex; flex-direction: column; gap: 16px; }
.wal .deep { padding: 26px; margin: 0; }
.wal .deep .v { font-size: 48px; margin-top: 10px; }
.wal .deep .meter { margin-top: 16px; }
.wal .topup { display: flex; flex-direction: column; gap: 12px; margin: 0; }
.wal .topup .grid4 { gap: 8px; }
.wal .topup .filter { justify-content: center; }
.wal .ledger { padding: 0 !important; overflow: hidden; margin: 0; }
.wal .ledger .between { padding: 16px; }
.wal td.in { color: var(--good-ink); }
.wal td.out { color: var(--bad-ink); }

/* ── reports: D-Reports ────────────────────────────────────────────── */
.rep { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.rep .col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.rep .repcard { display: flex; align-items: center; gap: 14px; padding: 14px 16px; margin: 0; }
.rep .repcard .vstack { flex: 1 1 auto; min-width: 0; }
.rep .repcard .t-strong { font-size: 14px; }
.rep .repcard .acts { display: flex; gap: 6px; flex: 0 0 auto; }
.rep .range { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.rep .range .field { flex: 1 1 150px; }
.rep .out { display: flex; flex-direction: column; gap: 16px; }
.rep .out .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
.rep .out .table-card { padding: 0 !important; overflow: hidden; margin: 0; }
#report-out:empty { display: none; }

/* ── staff: D-Staff ────────────────────────────────────────────────── */
.staff { display: flex; flex-direction: column; gap: 20px; }
.staff .table-card { padding: 0 !important; overflow: hidden; margin: 0; }
.staff table.board th.c, .staff table.board td.c { text-align: center; }
.staff .ok { color: var(--good); }
.staff table.board th.c, .staff table.board td.c { padding-left: 6px; padding-right: 6px; }
.staff .av { width: 32px; height: 32px; border-radius: 10px; font-size: 12px; }
.staff tr.off td { opacity: 0.55; }

/* ── networks: D-Networks ──────────────────────────────────────────── */
.nets2 { display: flex; flex-direction: column; gap: 20px; }
.nets2 .grid2 { gap: 16px; }
.nets2 .netbig { padding: 22px; display: flex; flex-direction: column; gap: 14px; margin: 0; }
.nets2 .netbig.on { border: 1.5px solid var(--primary); box-shadow: var(--shadow-2); }
.nets2 .netbig.warn { border-color: var(--warn-tint); }
.nets2 .netbig .hrow .vstack { flex: 1 1 auto; min-width: 0; }
.nets2 .figs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.nets2 .figs .num { font-size: 20px; }
.nets2 .figs-slot:empty { display: none; }
.nets2 .tip { background: var(--sunk); border-color: transparent; display: flex; align-items: center; gap: 12px; margin: 0; }
.netmark.xl { width: 56px; height: 56px; flex-basis: 56px; font-size: 17px; border-radius: 18px; }

/* ── Airtel: D-Airtel ──────────────────────────────────────────────── */
.at { display: flex; flex-direction: column; gap: 20px; }
.at .top { display: grid; grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr)); gap: 16px; }
.at .top .deep { padding: 22px; margin: 0; }
.at .top .deep .num { font-size: 42px; font-weight: 500; letter-spacing: -0.04em; margin-top: 8px; display: block; }
.at .top .stat .v { font-size: 28px; }
.at .main { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.at .table-card { padding: 0 !important; overflow: hidden; margin: 0; }
.at .table-card > .between { padding: 16px; }
.at .col { display: flex; flex-direction: column; gap: 16px; }
.at .col .card { display: flex; flex-direction: column; gap: 10px; margin: 0; }
.at .free0 { color: var(--bad-ink); font-weight: 600; }
.at .freeLow { color: var(--warn-ink); font-weight: 600; }
.at .red { color: var(--bad-ink); font-weight: 600; }

/* ── FNW Uni's own bill, and the desk when it is not paid ──────────────
 *
 * What the operator pays US. The figure sits on the one deep panel with its
 * arithmetic printed underneath, because a per-customer price means the total
 * changes every month and an operator has to be able to check it against
 * their own book rather than trusting ours. The right column explains the
 * sum; the pay slot is a card of its own so the real button drops into it
 * when there is one. */
.bill { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.bill .col { display: flex; flex-direction: column; gap: 16px; }
.bill .card { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.bill .deep { margin: 0; padding: 24px; }
/* The whole product's price, so it is read from across a counter. */
.bill .deep .v { font-size: 52px; margin-top: 12px; }
.bill .deep .sum { font-size: 15px; color: var(--deep-ink); margin-top: 8px; }
.bill .deep .sub { margin-top: 10px; max-width: 46ch; }
.bill .trial { margin: 0; color: var(--ink); }
.bill p { margin: 0; }
/* Where the Pay button goes once there is a payment route to put behind it.
 * Quiet, and the full width of the column, so adding the control later is
 * one line of markup and no layout change. */
.bill .payslot { border-style: dashed; border-color: var(--line-strong); box-shadow: none; }
.bill .how .grid2 { gap: 12px; }

/* The locked desk: one card, centred, nothing else to look at. */
.lockout { display: flex; justify-content: center; padding: 24px 0 64px; }
.lockout .card { max-width: 560px; display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 32px; }
.lockout .card h2 { margin: 0; }
.lockout .card p { margin: 0; }
.lockout .reason { color: var(--ink); font-weight: 600; }
.lockout .btn { align-self: flex-start; margin-top: 6px; }

/* ── an operator's own account ─────────────────────────────────────────
 *
 * The one screen in this product about the person reading it rather than
 * about their business, so it is deliberately narrower than the working
 * pages: two columns, the left one doing the editing and the right one
 * answering "why can I not see that button". */
.me { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.me .col { display: flex; flex-direction: column; gap: 16px; }
.me .card { margin: 0; display: flex; flex-direction: column; gap: 10px; }
.me .who { flex-direction: row; align-items: center; gap: 14px; }
.me .who .t-title { display: block; }
.me .who p { margin: 2px 0 0; }
.me .row { align-items: flex-end; }
.me .row input { flex: 1 1 220px; }
.me .grants { display: flex; flex-wrap: wrap; gap: 6px; }
.me .btn.primary { align-self: flex-start; }
.me .note:empty { display: none; }

/* The top bar's avatar is a door now, not a label. It has to look like one
 * on hover without becoming a button-shaped thing in a row of icons. */
.topbar .user { background: none; border: 0; padding: 0; cursor: pointer; }
.topbar .user:hover .av, .topbar .user:focus-visible .av { box-shadow: 0 0 0 2px var(--primary); }

/* ── narrow screens ────────────────────────────────────────────────── */
@media (max-width: 1280px) {
  .nc { grid-template-columns: 200px minmax(0, 1fr); }
  .nc .aside { grid-column: 1 / -1; position: static; }
  .an, .cust .body, .wal, .rep, .at .main, .dash .main, .bill, .me { grid-template-columns: minmax(0, 1fr); }
  .an .aside, .nc .steplist { position: static; }
  .kanban { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .coll .top, .at .top { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .topbar #who { display: none; }
}
@media (max-width: 1100px) {
  .dash .stats, .dash .stats.six { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .topbar .search { width: 220px; }
}
@media (max-width: 980px) {
  #app { grid-template-columns: 1fr; }
  .rail { width: 264px; }
  .topbar { padding: 0 16px; gap: 10px; }
  .topbar .rail-toggle { display: inline-flex; }
  .topbar .user span.av { display: inline-flex; }
  .topbar .page-meta, .topbar #who { display: none; }
  .topbar .search { display: none; }
  .book .page-q { display: flex; width: 100%; }
  main#view { padding: 20px 16px 64px; }
  .signin-wrap { grid-template-columns: 1fr; }
  .nc { grid-template-columns: minmax(0, 1fr); }
  .nc .steplist { flex-direction: row; overflow-x: auto; }
  .nc .step { width: auto; flex: 0 0 auto; }
}
@media (max-width: 720px) {
  .kanban, .coll .top, .at .top, .dash .stats, .dash .stats.six, .nets2 .grid2 { grid-template-columns: minmax(0, 1fr); }
  .topbar .page-tools .btn { padding: 0 10px; }
  .topbar #uilang { max-width: 88px; }
  .signin-form .inner.card { padding: 24px; }
}
.an #olt-out { display: flex; flex-direction: column; gap: 14px; }
.an #olt-out .card { margin: 0; }
.cust .opts:not(:has(.row)) { display: none; }

/* ── the first screen a brand-new business sees ─────────────────────────
   Shaped like the locked screen rather than like a page of the desk: one
   question, centred, with nothing else competing for the operator's
   attention. They have been in this product for ninety seconds and have no
   idea yet what the rail is for.

   No rail at all until they have said who they buy bandwidth from. Not a
   dimmed one: eighteen grey rows that do nothing are not an explanation to
   somebody who has never seen this desk, they are clutter the one question on
   screen has to compete with. The phone's first-run screen has no navigation
   either.

   The top bar stays, because it is not the desk — the language picker, the
   operator's own account and sign out all work for somebody who has chosen
   nothing, and sign out must never sit behind a step. */
#app.first-run { grid-template-columns: minmax(0, 1fr); }
#app.first-run .rail { display: none; }
#app.first-run .topbar .rail-toggle { display: none; }
/* Room for the sticky action bar, so the last row of cards is not sitting
   under it when the grid is scrolled to the bottom. */
#app.first-run main#view { padding-bottom: 132px; }

#view .firstrun { display: flex; flex-direction: column; gap: 28px; max-width: 920px; margin: 0 auto; padding: 16px 0 24px; }
#view .firstrun .first-head { gap: 10px; }
#view .firstrun .first-head h1 { margin: 0; font-size: 34px; letter-spacing: -0.02em; line-height: 1.12; }
#view .firstrun .first-head p { margin: 0; color: var(--ink-2); max-width: 58ch; font-size: 15.5px; }
/* The step counter as a pill rather than an eyebrow: it is the one piece of
   the page that says how long this takes, and an operator deciding whether to
   abandon a setup reads it first. */
#view .firstrun .step-pill {
  align-self: flex-start; display: inline-flex; align-items: center;
  padding: 5px 11px; border-radius: 999px;
  background: var(--primary-tint); color: var(--primary-ink);
  font-size: 11.5px; font-weight: 650; letter-spacing: 0.07em; text-transform: uppercase;
}

/* ── finding one among twelve ──────────────────────────────────────── */
#view .firstrun .pfind { position: relative; display: flex; align-items: center; }
#view .firstrun .pfind svg { position: absolute; left: 14px; width: 17px; height: 17px; color: var(--dim); pointer-events: none; }
#view .firstrun .pfind input.pq {
  width: 100%; padding: 12px 14px 12px 40px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface-2); font-size: 15px; color: var(--ink);
}
#view .firstrun .pfind input.pq:focus { outline: none; border-color: var(--primary); background: var(--surface); box-shadow: var(--ring); }
#view .firstrun .pnone { margin: 0; text-align: center; padding: 28px 0; }

/* ── the cards ─────────────────────────────────────────────────────── */
#view .firstrun .pgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
#view .firstrun .pcard {
  position: relative; display: flex; flex-direction: column; gap: 10px;
  width: 100%; text-align: left; cursor: pointer;
  padding: 18px 18px 16px; border-radius: 16px;
  background: var(--surface); border: 1.5px solid var(--line);
  box-shadow: var(--shadow-1);
  transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease, background .16s ease;
}
#view .firstrun .pcard:hover { border-color: var(--line-strong); box-shadow: var(--shadow-2); transform: translateY(-2px); }
#view .firstrun .pcard:focus-visible { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
/* Chosen. The tick is the signal; the tint and the border are there so a
   scan of the grid shows how many are on without reading any of them. */
#view .firstrun .pcard.on { border-color: var(--primary); background: var(--primary-tint); box-shadow: var(--shadow-2); }
#view .firstrun .pcard.on .ptick { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); transform: scale(1); }
#view .firstrun .ptick {
  position: absolute; top: 14px; right: 14px; width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px; border: 1.5px solid var(--line-strong);
  background: var(--surface); color: transparent; transform: scale(0.9);
  transition: background .16s ease, border-color .16s ease, color .16s ease, transform .16s ease;
}
#view .firstrun .ptick svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }

#view .firstrun .phead { display: flex; align-items: center; gap: 13px; padding-right: 30px; }
#view .firstrun .pname { min-width: 0; gap: 2px; }
#view .firstrun .pname .t-title { font-size: 16px; letter-spacing: -0.01em; }
#view .firstrun .pname .t-caption { line-height: 1.45; }

/* The provider's own mark. The lettered tile is the shipping state; a real
   logo replaces it when one is on disk, which the gateway reports on the
   catalogue rather than the browser discovering by 404. See
   brand/providers/README.md for why this product ships letters instead of
   drawing imitations of other companies' logos.

   The box is LANDSCAPE, and that is the whole reason these are legible. An
   ISP logo is almost never a square symbol — it is a lockup, a mark beside a
   wordmark beside a tagline, and the ones here run from 0.92:1 (BSNL) to
   4.95:1 (GTPL's hathway wordmark). Fitted into a square, a 5:1 lockup gets
   56/4.95 = eleven pixels of height and may as well not be there. One fixed
   landscape box for every provider, logo or letters, so the names beside them
   still line up down the column.

   White ground rather than the provider's colour: these are supplied as
   full-colour marks, and a dark wordmark on a coloured tile is unreadable
   often enough to be the wrong default. */
#view .firstrun .pmark {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 92px; height: 56px;
}
#view .firstrun .pmark .netmark { margin: 0; }
#view .firstrun .pmark .plogo {
  width: 100%; height: 100%; object-fit: contain; padding: 4px 6px;
  border-radius: 12px; background: var(--surface); border: 1px solid var(--line);
}

/* Amber, not red: a provider that needs a human in the loop is a property of
   that supplier, not a fault, and nothing is wrong with choosing it. */
#view .firstrun .pwarn { font-size: 12.5px; line-height: 1.5; color: var(--warn-ink); }
#view .firstrun .pnote { font-size: 12.5px; line-height: 1.5; color: var(--dim); }

#view .firstrun .first-soon { gap: 10px; padding-top: 4px; border-top: 1px solid var(--line); }

/* ── the sign-in step ──────────────────────────────────────────────── */
#view .firstrun .pform { margin: 0; border-radius: 16px; transition: border-color .2s ease, opacity .2s ease; }
#view .firstrun .pform .phead { padding-right: 0; margin-bottom: 2px; }
#view .firstrun .pform .pdone { margin-left: auto; flex: 0 0 auto; width: 26px; height: 26px; display: none; align-items: center; justify-content: center; border-radius: 999px; background: var(--good); color: var(--primary-fg); }
#view .firstrun .pform .pdone svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
/* Connected on this pass. Dimmed rather than removed, so a second attempt at
   the one that failed does not make the page jump about. */
#view .firstrun .pform.done { border-color: var(--good); opacity: 0.65; }
#view .firstrun .pform.done .pdone { display: flex; }

/* ── the sticky action bar ─────────────────────────────────────────── */
#view .pbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 24px; max-width: 920px; margin: 0 auto 18px;
  border-radius: 16px; background: var(--surface);
  border: 1px solid var(--line); box-shadow: var(--shadow-3);
  transform: translateY(0); transition: border-color .18s ease;
}
#view .pbar.on { border-color: var(--primary); }
#view .pbar #pcount { font-size: 14.5px; }
#view .pbar .btn { min-width: 150px; }
@media (max-width: 980px) { .pbar { left: 16px; right: 16px; padding: 12px 16px; } }
@media (max-width: 720px) {
  #view .firstrun .pgrid { grid-template-columns: minmax(0, 1fr); }
  #view .firstrun .first-head h1 { font-size: 27px; }
  #view .pbar .btn { min-width: 0; }
}
