/* ---------------------------------------------------------------------------
   SolarDesk - self contained stylesheet.

   No build step and no CDN: the application is often run on a laptop in a site
   office with poor connectivity, and a proposal has to render the same way
   whether or not the network is up.
   --------------------------------------------------------------------------- */

:root {
  /* --------------------------------------------------------------------
     Power & Sun brand palette, sampled from the logo artwork.
       amber  #FAAE43  the sun
       orange #F05A2D  the wordmark and the bolt
       blue   #134C97  the strapline and the rule
     The chart colours are deliberately the same three: solar is the amber
     of the sun, storage the orange of the bolt, the grid the blue. A reader
     who has seen the logo already knows what each colour means.
     -------------------------------------------------------------------- */
  --bg: #f5f7f9;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --border: #e2e6ec;
  --border-strong: #c7d0dc;
  --text: #14212e;
  --muted: #5f6e7f;
  --faint: #6b7682;

  --brand: #134C97;
  --brand-dark: #0e3a73;
  --brand-light: #e8eef8;

  --sun: #FAAE43;
  --sun-light: #fef3e2;
  --grid: #134C97;
  --grid-light: #e8eef8;
  --battery: #F05A2D;
  --battery-light: #fdeee8;
  --load: #5a6b7c;
  /* Generator energy. It had been drawn in --warn, which is the amber-brown the
     interface uses for "look at this": a hybrid plant running normally then read
     as a plant in trouble, and the colour sat awkwardly beside the two warm
     brand colours it was stacked on. Slate is cool against them, reads as
     machinery rather than caution, and is still clearly not the blue of grid
     import. */
  --genset: #44576b;
  --genset-light: #eceff3;

  --ok: #15803d;
  --ok-light: #e8f5ec;
  --warn: #9a5b00;
  --warn-light: #fdf1de;
  --bad: #b3261e;
  --bad-light: #fbeae8;

  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(16,24,32,.05), 0 1px 3px rgba(16,24,32,.04);
  --shadow-lg: 0 4px 16px rgba(16,24,32,.08);
  /* The sticky topbar's height, so anything else that sticks can clear it
     instead of sliding underneath. */
  --topbar-h: 52px;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

/* The `hidden` attribute has to win.
   It is implemented as a user-agent rule of ordinary weight, so any author
   rule that sets `display` - an inline style especially - silently overrides
   it, and an element marked hidden stays on screen. That is how a storage
   field survived being hidden on a grid design: it carried an inline
   display:flex. Scripts here toggle `el.hidden`, so this makes that mean what
   it says. */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3, h4 { margin: 0 0 .5rem; font-weight: 600; line-height: 1.25; letter-spacing: -.01em; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.125rem; }
h3 { font-size: .95rem; }
h4 { font-size: .85rem; }
p { margin: 0 0 .75rem; }
small { font-size: .8125rem; }

/* ---- Layout ------------------------------------------------------------ */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: 232px; flex: 0 0 232px;
  background: #0d2e56; color: #cfdaea;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar-brand {
  /* No side padding: the logo band runs the full width of the sidebar. */
  padding: 0 0 .9rem;
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.sidebar-logo {
  display: block;
  background: #fff;
  padding: .7rem .5rem;
  margin-bottom: .55rem;
}
.sidebar-logo img { display: block; width: 100%; height: auto; }
.sidebar-brand strong { color: #fff; font-size: 1.05rem; display: block; letter-spacing: -.02em; padding: 1.1rem 1.15rem 0; }
.sidebar-brand span { font-size: .75rem; color: #8fa8c6; padding: 0 1.15rem; display: block; }
.sidebar nav { padding: .75rem .6rem; flex: 1; }
.sidebar nav .group { font-size: .7rem; text-transform: uppercase; letter-spacing: .07em; color: #7d97b8; padding: .9rem .55rem .3rem; }
.sidebar nav a {
  display: flex; align-items: center; gap: .55rem;
  padding: .48rem .55rem; border-radius: var(--radius-sm);
  color: #cfdaea; font-size: .875rem; text-decoration: none;
}
.sidebar nav a:hover { background: rgba(255,255,255,.06); color: #fff; }
.sidebar nav a.active { background: var(--sun); color: #14212e; font-weight: 600; }
.sidebar nav a .ico { width: 16px; text-align: center; opacity: .85; font-size: .9rem; }
.sidebar-foot { padding: .85rem 1.15rem; border-top: 1px solid rgba(255,255,255,.08); font-size: .8rem; }
.sidebar-foot .who { color: #fff; }
.sidebar-foot .role { color: #8fa8c6; font-size: .75rem; text-transform: capitalize; }
.sidebar-foot button {
  margin-top: .5rem; background: none; border: 1px solid rgba(255,255,255,.16);
  color: #cfdaea; padding: .3rem .6rem; border-radius: var(--radius-sm);
  font-size: .78rem; cursor: pointer;
}
.sidebar-foot button:hover { background: rgba(255,255,255,.08); }
.locale-picker { margin-top: .55rem; }
.locale-picker label { color: #8fa8c6; margin-bottom: .2rem; }
.locale-picker select {
  padding: .25rem .4rem; font-size: .78rem;
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); color: #e6eef8;
}
.locale-picker select option { color: var(--text); background: #fff; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  background: var(--surface); border-bottom: 1px solid var(--border);
  padding: .85rem 1.5rem; display: flex; align-items: center; gap: 1rem;
  position: sticky; top: 0; z-index: 20;
  /* Everything that sticks below this one is positioned at --topbar-h, so the
     bar has to actually be that tall. It was not: a long design name wrapped
     the heading onto a second line and the sticky bars underneath then sat
     across the top of it. The title now truncates instead, and the full text is
     still in the tab title and the breadcrumb. */
  min-height: var(--topbar-h); box-sizing: content-box;
}
.topbar > div:first-of-type { min-width: 0; }
.topbar .crumbs {
  font-size: .8rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.topbar .crumbs a { color: var(--muted); }
.topbar h1 {
  margin: 0; font-size: 1.15rem; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.topbar .spacer { flex: 1; }
/* Actions keep their own line and never wrap into the title. */
.topbar .actions { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
/* Centred, so whatever a wide screen has spare is shared either side rather
   than left as one dead gutter on the right. The cap is generous because the
   pages that matter here are dense tables and side-by-side charts, which are
   easier to read wide than tall. */
.content { padding: 1.5rem; max-width: 1760px; width: 100%; margin-inline: auto; }
@media (max-width: 700px) { .content { padding: 1rem .85rem; } }

/* ---- Cards ------------------------------------------------------------- */

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  margin-bottom: 1.25rem;
}
.card-head {
  padding: .85rem 1.1rem; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
}
.card-head h2, .card-head h3 { margin: 0; }
.card-head .spacer { flex: 1; }
.card-head .hint { font-size: .8rem; color: var(--muted); font-weight: 400; }
.card-body { padding: 1.1rem; }
.card-body.tight { padding: .75rem 1.1rem; }
.card-foot { padding: .75rem 1.1rem; border-top: 1px solid var(--border); background: var(--surface-2); border-radius: 0 0 var(--radius) var(--radius); }

.grid { display: grid; gap: 1.25rem; }
.grid.c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.c4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid.side { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
@media (max-width: 1240px) { .grid.side { grid-template-columns: 1fr; } }
@media (max-width: 1100px) { .grid.c3, .grid.c4 { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 860px)  { .grid.c2 { grid-template-columns: 1fr; } }
@media (max-width: 720px)  { .grid.c2, .grid.c3, .grid.c4, .grid.side { grid-template-columns: 1fr; } }

/* ---- The sidebar as a drawer ------------------------------------------- */

/* Wide enough for both, the sidebar is a column and the toggle is not needed.
   Below that the column would take a quarter of the screen from the work, so
   it slides out over the page instead and gives all the width back. */
.nav-toggle { display: none; }

@media (max-width: 960px) {
  .nav-toggle {
    display: inline-flex; flex-direction: column; justify-content: center; gap: 4px;
    width: 34px; height: 34px; padding: 0 7px; flex: 0 0 auto;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-sm); cursor: pointer;
  }
  .nav-toggle span { display: block; height: 2px; background: var(--text); border-radius: 2px; }
  .nav-toggle:hover { background: var(--surface-2); }

  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100%;
    z-index: 60; transform: translateX(-100%);
    transition: transform .18s ease-out;
    box-shadow: var(--shadow-lg);
  }
  .shell.nav-open .sidebar { transform: none; }

  .nav-backdrop {
    position: fixed; inset: 0; z-index: 50;
    background: rgba(16, 33, 46, .45);
  }
}

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

/* ---- Stats ------------------------------------------------------------- */

.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: .9rem 1rem; box-shadow: var(--shadow); }
.stat .label { font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; color: var(--faint); }
.stat .value { font-size: 1.5rem; font-weight: 650; letter-spacing: -.02em; margin-top: .15rem; font-variant-numeric: tabular-nums; }
.stat .value small { font-size: .9rem; font-weight: 500; color: var(--muted); }
.stat .sub { font-size: .8rem; color: var(--muted); margin-top: .1rem; }
.stat.accent-sun { border-left: 3px solid var(--sun); }
.stat.accent-grid { border-left: 3px solid var(--grid); }
.stat.accent-battery { border-left: 3px solid var(--battery); }
.stat.accent-brand { border-left: 3px solid var(--brand); }

/* ---- Tables ------------------------------------------------------------ */

table { width: 100%; border-collapse: collapse; font-size: .875rem; }
thead th {
  text-align: left; font-weight: 600; font-size: .75rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--faint); padding: .5rem .7rem;
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
tbody td { padding: .55rem .7rem; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); font-size: .82rem; }
table.compact tbody td, table.compact thead th { padding: .35rem .55rem; }

/* Dense numeric tables: rows striped so a figure can be tracked across a wide
   row without losing which line it belongs to. */
table.data tbody tr:nth-child(even) { background: var(--surface-2); }
table.data tbody tr:hover { background: var(--brand-light); }
table.data thead th { border-bottom: 1px solid var(--border-strong); }

/* A sub-line under the head it belongs to, in a cost or BOQ breakdown. The
   compact selector has to be named too, or its own padding wins on specificity
   and the indent never shows. */
tbody td.indent, table.compact tbody td.indent { padding-left: 1.7rem; color: var(--muted); }

/* The line a breakdown adds up to. */
tr.total td { font-weight: 650; border-top: 1px solid var(--border-strong); background: var(--surface-2); }
tr.total:hover td { background: var(--surface-2); }

/* The size a value curve settles on, marked in the row rather than in a legend
   away from it. */
tr.row-best td { background: var(--sun-light); box-shadow: inset 3px 0 0 var(--sun); }
tr.row-best:hover td { background: var(--sun-light); }

/* A figure that leads its own small block, where a full .stat card would be
   more furniture than the number is worth. */
.value-sm { font-size: 1.05rem; font-weight: 650; letter-spacing: -.01em; margin-top: .1rem; }

/* Words carrying a state on their own, outside a badge or an alert. */
.warn { color: var(--warn); }
.ok { color: var(--ok); }
.bad { color: var(--bad); }
strong.warn, strong.ok, strong.bad { font-weight: 650; }

/* A filter over rows already on the page. */
.table-filter { display: flex; align-items: center; gap: .75rem; margin-bottom: .9rem; }
.table-filter input[type="search"] { flex: 1 1 260px; max-width: 420px; margin: 0; }

/* A glossary entry: term, then what it means. */
.glossary-term { padding: .6rem 0; border-bottom: 1px solid var(--border); }
.glossary-term:last-child { border-bottom: none; }
.glossary-term > strong { font-size: .9rem; }
.glossary-term > div { margin-top: .15rem; line-height: 1.6; }

/* A tornado: each driver swung either side of the base case, longest first. */
.tornado { display: flex; flex-direction: column; gap: .5rem; }
.tornado-row { display: grid; grid-template-columns: minmax(140px, 1.1fr) minmax(0, 2fr) minmax(150px, 1.2fr); gap: .85rem; align-items: center; }
.tornado-label strong { display: block; font-size: .82rem; font-weight: 600; }
.tornado-label span { display: block; }
.tornado-track { position: relative; height: 20px; background: var(--surface-2); border-radius: var(--radius-sm); }
.tornado-axis { position: absolute; left: 50%; top: -2px; bottom: -2px; width: 1px; background: var(--border-strong); }
.tornado-bar { position: absolute; top: 3px; bottom: 3px; border-radius: 2px; min-width: 1px; }
/* The low end of the swing and the high end, not good and bad: which one is
   welcome depends on the metric, and the figures beside the bar say which. */
.tornado-bar.low { background: var(--grid); }
.tornado-bar.high { background: var(--sun); }
.tornado-figures { display: flex; gap: .35rem; flex-wrap: wrap; align-items: baseline; }
@media (max-width: 860px) {
  .tornado-row { grid-template-columns: 1fr; gap: .25rem; }
  .tornado-track { height: 14px; }
}

/* Back and next along the project workflow, at the foot of a step. */
.workflow-nav {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin: 1.25rem 0 .5rem; padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.workflow-nav > div:first-child { margin-right: auto; }
.workflow-nav > div:last-child { margin-left: auto; }

/* An area marked out on a map. */
.boundary-map { margin-top: 1rem; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .7rem .85rem; }
.boundary-map > summary { cursor: pointer; font-size: .85rem; font-weight: 600; display: flex; align-items: center; gap: .5rem; }
.boundary-map[open] > summary { margin-bottom: .85rem; }
.boundary-canvas {
  height: 380px; width: 100%;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.boundary-readout { border-top: 1px solid var(--border); padding-top: .85rem; }
/* Leaflet puts its panes at a high z-index by default, which would otherwise
   lift the map over this application's own sticky header and drawer. */
.boundary-canvas .leaflet-pane,
.boundary-canvas .leaflet-top,
.boundary-canvas .leaflet-bottom { z-index: 1; }
@media (max-width: 700px) { .boundary-canvas { height: 280px; } }

/* Shadows falling across a surface, drawn axonometrically and to scale. */
.shadow-view { margin-top: .5rem; }
.shadow-controls { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; margin-bottom: .6rem; }
.shadow-controls select { width: auto; padding: .25rem .4rem; font-size: .8rem; }
.shadow-controls input[type="range"] { flex: 1 1 200px; max-width: 320px; }
.shadow-svg {
  width: 100%; max-height: 420px; display: block;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
/* Stroke widths are in metres, because the drawing is. */
.sv-face { fill: #fff; stroke: var(--border-strong); stroke-width: 0.06; }
.sv-module { fill: var(--grid); stroke: #0e3a73; stroke-width: 0.02; }
.sv-module.shaded { fill: #7f8c9b; }
.sv-shadow { fill: rgba(20, 33, 46, .26); stroke: none; }
.sv-box-top { fill: var(--sun); stroke: #b97c20; stroke-width: 0.03; }
.sv-box-front { fill: #d99433; stroke: #b97c20; stroke-width: 0.03; }
.sv-box-side { fill: #c07f22; stroke: #b97c20; stroke-width: 0.03; }

/* ---- Sign-in and registration ------------------------------------------ */

/* These pages stand outside the application shell, so they carry their own
   centring rather than borrowing the layout's. */
.auth-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 2rem 1rem; }
.auth-shell { width: 100%; max-width: 520px; }
.auth-shell.auth-wide { max-width: 760px; }
.auth-brand { text-align: center; margin-bottom: 1.5rem; }
.auth-brand img { max-width: 265px; max-height: 104px; margin-bottom: .6rem; }
.auth-brand h1 { margin-bottom: .15rem; }

/* A declaration is read one clause at a time, so each sits on its own line
   with the box beside the first word rather than floating in the middle. */
.check.declaration { align-items: flex-start; gap: .6rem; padding: .5rem 0; border-bottom: 1px solid var(--border); }
.check.declaration:last-child { border-bottom: none; }
.check.declaration input { margin-top: .18rem; flex: 0 0 auto; }
.check.declaration span { line-height: 1.6; }

.btn-block { width: 100%; }

@media (max-width: 640px) {
  .auth-page { padding: 1rem .75rem; align-items: flex-start; }
  .auth-brand img { max-width: 207px; }
}

/* ---- Delivery programme ------------------------------------------------ */

/* A plain bar chart against a date scale. No library: the server already knows
   where every bar starts and how long it runs, and a percentage is all CSS
   needs to place it. */
.gantt { position: relative; }
.gantt-months { position: relative; height: 1.1rem; margin-left: 180px; margin-right: 56px; }
.gantt-tick { position: absolute; font-size: .68rem; color: var(--faint); transform: translateX(-50%); }
.gantt-row { display: grid; grid-template-columns: 180px minmax(0, 1fr) 56px; gap: .6rem; align-items: center; margin-bottom: .25rem; }
.gantt-label {
  font-size: .78rem; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gantt-track { position: relative; height: 16px; background: var(--surface-2); border-radius: 3px; }
.gantt-gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border); }
.gantt-bar { position: absolute; top: 2px; bottom: 2px; border-radius: 3px; min-width: 4px; }
.gantt-bar.is-done { background: var(--ok); }
.gantt-bar.is-overdue { background: var(--bad); }
.gantt-bar.is-blocked { background: var(--warn); }
.gantt-bar.is-due_soon { background: var(--sun); }
.gantt-bar.is-open { background: var(--grid); }
.gantt-date { color: var(--faint); text-align: right; }

/* The legend's colour chips. */
.key { display: inline-block; width: .6rem; height: .6rem; border-radius: 2px; margin: 0 .2rem 0 .6rem; vertical-align: baseline; }
.key.done { background: var(--ok); }
.key.overdue { background: var(--bad); }
.key.blocked { background: var(--warn); }
.key.open { background: var(--grid); }

/* Editing a row in place rather than on a page of its own. */
.inline-edit > summary { cursor: pointer; list-style: none; }
.inline-edit[open] { display: block; min-width: 320px; }

@media (max-width: 860px) {
  .gantt-months { margin-left: 110px; margin-right: 44px; }
  .gantt-row { grid-template-columns: 110px minmax(0, 1fr) 44px; gap: .4rem; }
  .gantt-label { font-size: .7rem; }
}

/* A support conversation. The administrator's replies are tinted so a thread
   can be skimmed for who said what. */
.support-message { padding: .75rem 0; border-bottom: 1px solid var(--border); }
.support-message:last-of-type { border-bottom: none; }
.support-message.from-admin { background: var(--brand-light); margin: 0 -1.1rem; padding: .75rem 1.1rem; border-bottom: none; border-radius: var(--radius-sm); }
.support-body { font-size: .875rem; line-height: 1.65; white-space: pre-wrap; }

/* An assistant conversation. */
.chat-turn { padding: .75rem 0; border-bottom: 1px solid var(--border); }
.chat-turn:last-of-type { border-bottom: none; }
.chat-turn.from-assistant { background: var(--surface-2); margin: 0 -1.1rem; padding: .75rem 1.1rem; border-bottom: none; border-radius: var(--radius-sm); }
.chat-body { font-size: .875rem; line-height: 1.65; white-space: pre-wrap; }

/* A document previewed in the page rather than downloaded to find out what it
   says. Tall, because a report read through a letterbox is not a preview. */
.output-preview {
  width: 100%; height: 70vh; min-height: 480px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff;
}
@media (max-width: 700px) { .output-preview { height: 55vh; min-height: 320px; } }

/* ---- Settings ---------------------------------------------------------- */

/* The find-and-jump bar stays put while the groups scroll past under it. */
.settings-bar { position: sticky; top: var(--topbar-h); z-index: 10; }
.settings-find { display: flex; align-items: center; gap: .85rem; flex-wrap: wrap; }
.settings-find input[type="search"] { flex: 1 1 300px; min-width: 0; margin: 0; }
.settings-find .spacer { flex: 1; }
.settings-jump { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .6rem; }
.settings-jump a {
  font-size: .74rem; padding: .18rem .5rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border);
  color: var(--muted); text-decoration: none; white-space: nowrap;
}
.settings-jump a:hover { background: var(--brand-light); border-color: var(--brand-light); color: var(--brand-dark); }
.settings-jump a em { font-style: normal; font-weight: 650; color: var(--warn); margin-left: .25rem; }

/* Enough room to land under the sticky bar rather than behind it. */
[id^="group-"] { scroll-margin-top: calc(var(--topbar-h) + 5.5rem); }

.setting-key { font-size: .68rem; color: var(--faint); }
.settings-table th:nth-child(1) { width: 26%; }
.settings-table th:nth-child(2) { width: 16%; }
.settings-table th:nth-child(3) { width: 12%; }

/* Inputs sitting inside a dense table row. */
.input-sm { padding: .25rem .4rem; font-size: .8rem; margin: 0; }
.badge.tiny-badge { font-size: .62rem; }

/* Sortable column headings. The arrow is part of the link so the whole
   heading is one target, and a dim arrow on the unsorted columns says which
   headings can be clicked at all. */
th.sortable { padding: 0; }
th.sortable > a {
  display: flex; align-items: center; gap: .3rem;
  padding: .5rem .7rem; color: inherit; text-decoration: none; white-space: nowrap;
}
table.compact th.sortable > a { padding: .35rem .55rem; }
th.sortable.num > a { justify-content: flex-end; }
th.sortable > a:hover { background: var(--surface-2); color: var(--brand); }
th.sortable .sort-arrow { font-size: .62rem; opacity: .35; }
th.sortable.sorted > a { color: var(--brand); }
th.sortable.sorted .sort-arrow { opacity: 1; }

/* ---- Badges ------------------------------------------------------------ */

.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .12rem .5rem; border-radius: 999px;
  font-size: .74rem; font-weight: 600; letter-spacing: .01em;
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
  white-space: nowrap;
}
.badge.ok { background: var(--ok-light); color: var(--ok); border-color: transparent; }
.badge.warn { background: var(--warn-light); color: var(--warn); border-color: transparent; }
.badge.bad { background: var(--bad-light); color: var(--bad); border-color: transparent; }
.badge.brand { background: var(--brand-light); color: var(--brand-dark); border-color: transparent; }
.badge.info { background: var(--grid-light); color: var(--grid); border-color: transparent; }
.badge.sun { background: var(--sun-light); color: #8a5a00; border-color: transparent; }
/* A count sitting at the end of a nav row. */
.badge.count { margin-left: auto; font-size: .68rem; }

/* ---- Buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .45rem .85rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); font-size: .85rem; font-weight: 500;
  cursor: pointer; text-decoration: none; line-height: 1.4;
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn.primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn.primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.btn.danger { background: #fff; border-color: #e7bcbc; color: var(--bad); }
.btn.danger:hover { background: var(--bad-light); }
.btn.small { padding: .28rem .6rem; font-size: .78rem; }
/* A secondary action beside a primary one - clearing a search, cancelling out
   of a form - that should not compete with it for attention. */
.btn.ghost { background: none; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text); }
.btn[disabled], .btn.disabled { opacity: .5; pointer-events: none; }
.btn-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
/* A form that sits in a line of text rather than blocking it. */
.inline { display: inline; }

/* ---- Forms ------------------------------------------------------------- */

label { display: block; font-size: .8rem; font-weight: 500; color: var(--muted); margin-bottom: .25rem; }
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=url], input[type=search], select, textarea {
  width: 100%; padding: .45rem .6rem; font-size: .875rem; font-family: inherit;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: #fff; color: var(--text);
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-light);
}
/* Buttons, links and tabs were reachable by keyboard but showed nothing when
   they got there, because the reset removed the user-agent outline and only
   form controls got it back. :focus-visible means a mouse click still looks
   clean. */
a:focus-visible, button:focus-visible, summary:focus-visible,
[role="tab"]:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--radius-sm);
}
textarea { min-height: 84px; resize: vertical; }
.field { margin-bottom: .85rem; }
/* A field whose control sits on the baseline of the row beside it. */
.field-inline { display: flex; align-items: flex-end; }
.field .help { font-size: .75rem; color: var(--faint); margin-top: .2rem; }
/* Required fields: a red star after the label, and the message the
   browser-side check puts under a field it stops on. */
.req { color: var(--bad); font-weight: 700; margin-left: .1rem; }
.is-invalid, .is-invalid:focus { border-color: var(--bad) !important; box-shadow: 0 0 0 3px var(--bad-light) !important; }
.field-error { font-size: .75rem; color: var(--bad); margin-top: .2rem; }
.field-row { display: grid; gap: .85rem; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.check { display: flex; align-items: center; gap: .45rem; font-size: .85rem; color: var(--text); font-weight: 400; }
.check input { width: auto; }
fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .85rem; margin: 0 0 1rem; }
legend { font-size: .78rem; font-weight: 600; color: var(--muted); padding: 0 .35rem; text-transform: uppercase; letter-spacing: .04em; }

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

.alert { padding: .7rem .95rem; border-radius: var(--radius-sm); margin-bottom: 1rem; font-size: .875rem; border: 1px solid transparent; }
.alert.ok { background: var(--ok-light); color: #14532d; border-color: #c3e6cd; }
.alert.bad { background: var(--bad-light); color: #7f1d1d; border-color: #f0c4c2; }
.alert.warn { background: var(--warn-light); color: #78350f; border-color: #f0dcb8; }
.alert.info { background: var(--grid-light); color: #303f9f; border-color: #ccd5fb; }
.alert ul { margin: .35rem 0 0; padding-left: 1.1rem; }

/* ---- Misc -------------------------------------------------------------- */

.muted { color: var(--muted); }
.faint { color: var(--faint); }
.tiny { font-size: .78rem; }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.mt0 { margin-top: 0; } .mt1 { margin-top: .5rem; } .mt2 { margin-top: 1rem; } .mt3 { margin-top: 1.5rem; }
.mb0 { margin-bottom: 0; } .mb1 { margin-bottom: .5rem; } .mb2 { margin-bottom: 1rem; }
.flex { display: flex; gap: .75rem; align-items: center; }
.flex.between { justify-content: space-between; }
.flex.wrap { flex-wrap: wrap; }
.empty { padding: 2rem 1rem; text-align: center; color: var(--faint); font-size: .875rem; }

.kv { display: grid; grid-template-columns: auto 1fr; gap: .3rem 1rem; font-size: .85rem; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-weight: 500; text-align: right; font-variant-numeric: tabular-nums; }

.tabs { display: flex; gap: .25rem; border-bottom: 1px solid var(--border); margin-bottom: 1.25rem; flex-wrap: wrap; }
.tabs a {
  padding: .5rem .85rem; font-size: .875rem; color: var(--muted);
  border-bottom: 2px solid transparent; text-decoration: none; font-weight: 500;
}
.tabs a:hover { color: var(--text); text-decoration: none; }
.tabs a.active { color: var(--brand); border-bottom-color: var(--brand); }

.pill-row { display: flex; gap: .35rem; flex-wrap: wrap; }

.progress { height: 6px; background: var(--border); border-radius: 999px; overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--brand); }

.check-row { display: flex; gap: .7rem; padding: .55rem 0; border-bottom: 1px solid var(--border); align-items: flex-start; }
.check-row:last-child { border-bottom: none; }
.check-row .mark { flex: 0 0 18px; font-weight: 700; line-height: 1.4; }
.check-row.pass .mark { color: var(--ok); }
.check-row.warning .mark { color: var(--warn); }
.check-row.fail .mark { color: var(--bad); }
.check-row .body strong { display: block; font-size: .85rem; }
.check-row .body span { font-size: .8rem; color: var(--muted); }

.legend { display: flex; gap: 1rem; flex-wrap: wrap; font-size: .78rem; color: var(--muted); }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: .3rem; vertical-align: -1px; }

.scroll-x { overflow-x: auto; }

.roof-canvas { background: repeating-linear-gradient(45deg, #f1f4f8 0 8px, #eaeff5 8px 16px); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); }

.pagination { display: flex; gap: .25rem; flex-wrap: wrap; margin-top: 1rem; font-size: .85rem; }
.pagination a, .pagination span { padding: .3rem .6rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; }
.pagination .active span { background: var(--brand); color: #fff; border-color: var(--brand); }

/* ---- Print ------------------------------------------------------------- */

@media print {
  .sidebar, .topbar, .no-print { display: none !important; }
  body { background: #fff; font-size: 11pt; }
  .content { padding: 0; max-width: none; }
  .card { box-shadow: none; border: 1px solid #d4d8dc; break-inside: avoid; page-break-inside: avoid; }
  .page-break { page-break-before: always; break-before: page; }
  a { color: inherit; text-decoration: none; }
}

/* ---- Inline actions inside tables -------------------------------------- */
/* A remove or a tick inside a row is a button for accessibility and keyboard
   use, but it should read as text rather than compete with the real buttons
   on the page. */
.link, .link-danger {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: .78rem; text-decoration: underline;
}
.link { color: var(--brand); }
.link-danger { color: #b4453a; }
.link:hover, .link-danger:hover { text-decoration: none; }

/* ---- Pipeline board ------------------------------------------------------ */
.board { display: flex; gap: .75rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.board .column {
  flex: 0 0 250px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px;
}
.board .column > header {
  padding: .6rem .75rem; border-bottom: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: baseline; gap: .5rem;
}
.board .column > header strong { font-size: .82rem; }
.board .column > header .count { font-size: .75rem; color: var(--muted); }
.board .column .cards { padding: .5rem; display: flex; flex-direction: column; gap: .5rem; }
.board .card-mini {
  background: #fff; border: 1px solid var(--border); border-radius: 6px;
  padding: .55rem .6rem; font-size: .8rem;
}
.board .card-mini .ref { font-size: .72rem; color: var(--muted); }
.board .column .empty { padding: .75rem; font-size: .75rem; color: var(--muted); text-align: center; }

/* ---- Sticky save on long forms ------------------------------------------ */
/* A surface form runs to several screens once the survey questions are on it.
   A save button that only exists at the bottom is one the designer scrolls
   past, so it follows them down and stays legible over the fields behind. */
.save-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: 1rem -1.1rem -1.1rem; padding: .7rem 1.1rem;
  background: var(--surface); border-top: 1px solid var(--border);
  box-shadow: 0 -6px 14px rgba(16, 32, 52, .06);
}
.card-foot.save-bar { margin: 0; }
@media print { .save-bar { display: none; } }

/* A row whose figure has not been confirmed by anyone here. */
.row-warn > td { background: var(--warn-light); }

/* The winning figure in a comparison row. */
td.best { font-weight: 600; color: var(--ok); }

/* ---- Google Solar irradiance layers ------------------------------------ */

/* The flux raster sits on the aerial image, so a designer sees the shading
   against the building that causes it rather than as an abstract shape. */
.layer-figure { margin: 1rem 0 .75rem; position: relative; display: inline-block; max-width: 100%; }
.layer-figure img { display: block; max-width: 100%; height: auto; image-rendering: pixelated; border-radius: var(--radius-sm); }
.layer-figure .layer-flux { position: absolute; inset: 0; width: 100%; height: 100%; }
/* With no aerial image to sit on, the raster is the figure itself. */
.layer-figure:not(:has(.layer-base)) .layer-flux { position: static; }
.layer-figure figcaption { margin-top: .5rem; position: relative; }
.layer-key { margin-left: .5rem; }
.layer-key i {
  display: inline-block; width: .7rem; height: .7rem; border-radius: 2px;
  margin: 0 .2rem 0 .5rem; vertical-align: -1px;
}

/* ---- Step header and numbered stage tabs -------------------------------- */

/* The tab strip is the order the work is done in, so it is numbered and it
   shows which steps are behind you. */
.tabs a .tab-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-right: .45rem; border-radius: 50%;
  background: var(--border); color: var(--surface);
  font-size: .68rem; font-weight: 700; vertical-align: -3px;
}
.tabs a.done .tab-n { background: var(--sun); }
.tabs a.active .tab-n { background: var(--brand); }
.tabs a.done { color: var(--muted); }
.tabs a.tab-aside { margin-left: auto; color: var(--faint); }

.step-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1.5rem; margin: 0 0 1.25rem;
}
.step-head-main { min-width: 0; }
.step-head-k {
  margin: 0 0 .15rem; font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--brand);
}
.step-head-next { color: var(--faint); font-weight: 500; text-transform: none; letter-spacing: 0; }
.step-head h2 { margin: 0 0 .25rem; font-size: 1.3rem; letter-spacing: -.01em; }
.step-head-lede { margin: 0; font-size: .88rem; color: var(--muted); max-width: 78ch; }

/* Controls a page puts beside the progress rule, e.g. a design's currency. */
.step-head-side { display: flex; align-items: flex-start; gap: 1rem; flex: 0 0 auto; }
.step-head-currency select { min-width: 12rem; }

/* A plain progress rule. Deliberately not interactive: the tabs above already
   navigate, and two things that look clickable in the same place is worse than
   one. */
.step-head-bar { display: flex; gap: 3px; flex: 0 0 auto; padding-top: .5rem; }
.step-head-bar span { width: 26px; height: 4px; border-radius: 2px; background: var(--border); }
.step-head-bar span.done { background: var(--sun); }
.step-head-bar span.on { background: var(--brand); }

@media (max-width: 700px) {
  .step-head { flex-direction: column; gap: .6rem; }
  /* Side controls and the progress rule stack full width, never wider. */
  .step-head-side { flex: 1 1 auto; flex-direction: column; width: 100%; gap: .6rem; }
  .step-head-currency, .step-head-currency select { min-width: 0; width: 100%; }
  .step-head-bar { padding-top: 0; }
  .step-head-bar span { width: 100%; flex: 1; }
  .step-head-bar { width: 100%; }
  .tabs a .tab-n { margin-right: .3rem; }
}

/* A number on each menu group, because the groups are the order the work is
   done in rather than a filing system. */
.sidebar .group { display: flex; align-items: center; gap: .45rem; }
.sidebar .group-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 50%; flex: 0 0 16px;
  background: rgba(255,255,255,.14); color: #e6eef8; font-size: .62rem; font-weight: 700;
}

/* Privacy and cookies, at the foot of the sidebar. Quiet, but present: the
   people entering other people's details are the ones who most need them. */
.sidebar-legal { display: flex; flex-wrap: wrap; gap: .3rem .9rem; margin-top: .7rem; }
.sidebar-legal a {
  padding: 0; font-size: .72rem; color: rgba(230,238,248,.45);
  text-decoration: none; display: inline;
}
.sidebar-legal a:hover { color: rgba(230,238,248,.8); text-decoration: underline; background: none; }

/* Sign-in and registration: the form beside a photograph on a wide screen. */
.auth-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-height: 100vh; margin: 0; }
.auth-split-wide { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); }
.auth-split-form { display: flex; align-items: center; justify-content: center; padding: 1.5rem 0; }
.auth-split .auth-shell { margin: 0 auto; }
.auth-photo { position: sticky; top: 0; height: 100vh; overflow: hidden; }
.auth-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.auth-photo-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 3rem 2.4rem 2.4rem; color: #fff;
  background: linear-gradient(to top, rgba(12, 21, 40, .78), rgba(12, 21, 40, 0)); }
.auth-photo-cap strong { display: block; font-size: 1.45rem; line-height: 1.25; max-width: 22ch; }
.auth-photo-tag { display: block; font-family: "Snell Roundhand", "Segoe Script", "Brush Script MT", "Apple Chancery", cursive; font-size: 1.5rem; margin-bottom: .3rem; color: #f3d9a4; }
@media (max-width: 900px) {
  .auth-split, .auth-split-wide { grid-template-columns: 1fr; }
  .auth-photo { display: none; }
}

/* "Beta version" badge beside the logo (04-Nov-2026): a gentle blink, still for reduced motion. */
.beta-badge {
  display: inline-flex; align-items: center; align-self: center; width: max-content;
  padding: .2em .65em; border-radius: 999px;
  background: #f59e0b; color: #1f1300;
  font-size: 11px; font-weight: 700; line-height: 1.4; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap;
  animation: beta-blink 1.6s ease-in-out infinite;
}
.beta-bar { display: flex; justify-content: center; align-items: center; padding: 5px 12px; background: #14223d; border-bottom: 1px solid rgb(255 255 255 / .08); }
.auth-brand .beta-badge { margin: .5rem 0; }
/* Under the sidebar logo: smaller, black text; beats the sidebar's own span colours (app.css + theme.css). */
.sidebar-brand .beta-badge, .sidebar-brand > span.beta-badge {
  display: flex; width: max-content; margin: .45rem auto .2rem; padding: .12em .5em; color: #000;
  font-family: inherit; font-size: 9px !important; line-height: 1.4; letter-spacing: .04em; text-transform: uppercase;
}
@keyframes beta-blink {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgb(245 158 11 / .55); }
  50% { opacity: .55; box-shadow: 0 0 0 6px rgb(245 158 11 / 0); }
}
@media (prefers-reduced-motion: reduce) { .beta-badge { animation: none; } }
