/* ============================================================
   Admin styles.

   THERE IS NOT ONE COLOUR VALUE IN THIS FILE.

   Every colour is var(--token), and every token is emitted into
   <head> from the settings table by app/includes/theme.php. That
   is what makes the rebrand an afternoon rather than a migration.

   If you are about to type a # followed by six hex digits in
   here: don't. Add a token instead. The last build of this kind
   had half its colours written longhand, and the day the palette
   moved, six buttons turned unreadable and nobody noticed until a
   screenshot arrived.

   The display face is a webfont loaded with display=swap, for
   headings and numbers only. Body text stays on the system stack.
   This is the screen someone opens when something is wrong, on
   whatever connection they have — the words have to arrive first
   and they still do; the face catches up.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

/* The header is sticky, so anything scrolled to the top of the
   viewport lands underneath it and cannot be clicked. Reserve its
   height for every scroll the browser performs itself — jumping to
   an anchor, focusing a field, or scrolling a control into view. */
html { scroll-padding-top: 74px; }

body {
  margin: 0;
  background: var(--void);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* The same grid as the public site and the portal, same pitch.
     Three surfaces, one company. */
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 64px 64px, 64px 64px;
  background-attachment: fixed;
}

/* The display face. Headings, the big counts, and buttons — the
   things that should read as struck rather than typed. */
.display, h1, .stuck-card .n, .brand-word, button, .btn, .signin-card .mark {
  font-family: "Saira Condensed", "Oswald", "Arial Narrow", system-ui, sans-serif;
}

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

/* ---------- Layout ---------- */
.wrap { max-width: 1180px; margin: 0 auto; padding: 26px 24px 80px; }
.page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 22px;
}
/* Chrome fill, with the per-line background-size that stops a
   wrapped heading fading to grey at the bottom. Same rule as the
   public site — see site.css for the full reasoning. */
h1, .brand-word {
  color: var(--chrome-1);
  background-image: linear-gradient(180deg,
              var(--chrome-1) 0%, var(--chrome-1) 26%,
              var(--chrome-2) 52%, var(--chrome-3) 74%,
              var(--chrome-2) 100%);
  background-repeat: repeat;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
h1 {
  font-size: 30px; margin: 0; letter-spacing: 0.005em; font-weight: 800;
  text-transform: uppercase; line-height: 1.1; background-size: 100% 1.1em;
}
h2 { font-family: "Saira", system-ui, sans-serif;
     font-size: 11px; margin: 0 0 12px; text-transform: uppercase;
     letter-spacing: 0.16em; color: var(--text-faint); font-weight: 700; }
.sub { color: var(--text-dim); font-size: 13px; margin: 4px 0 0; }

/* ---------- The four counts ---------- */
/* Deliberately the first thing on the dashboard: each one is a
   specific job someone has to go and do, not a metric to admire. */
.stuck-row {
  /* auto-fit rather than a fixed count: the row grew from four to
     five when renewals arrived, and a hardcoded 4 wrapped the fifth
     onto a line of its own. It will grow again. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; margin-bottom: 26px;
}
@media (max-width: 460px) { .stuck-row { grid-template-columns: minmax(0,1fr); } }

.stuck-card {
  background: var(--panel); border: 1px solid var(--edge);
  border-left: 3px solid var(--edge-loud);
  padding: 16px 17px; border-radius: 6px; display: block; color: inherit;
  transition: background .12s ease, border-color .12s ease;
}
.stuck-card:hover { background: var(--rise); text-decoration: none; border-color: var(--edge-loud); }
/* The count carries the status colour, so it never wears the chrome
   fill — a number that means "three visits are overdue" has to stay
   the colour that says so. */
.stuck-card .n { font-size: 40px; font-weight: 800; line-height: 0.95; letter-spacing: 0.01em; }
.stuck-card .lbl {
  font-family: "Saira", system-ui, sans-serif;
  font-size: 11px; color: var(--text-dim); margin-top: 8px;
  text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600;
}
.stuck-card.is-zero { opacity: 0.5; }
.stuck-card.is-zero .n { color: var(--text-faint); }

/* Each stuck state gets its own status colour, and those four
   colours mean nothing else anywhere in the product. */
.stuck-card.k-unproposed { border-left-color: var(--waiting); }
.stuck-card.k-unproposed .n { color: var(--waiting); }
.stuck-card.k-unanswered { border-left-color: var(--finding); }
.stuck-card.k-unanswered .n { color: var(--finding); }
.stuck-card.k-missed     { border-left-color: var(--overdue); }
.stuck-card.k-missed .n  { color: var(--overdue); }
.stuck-card.k-unreported { border-left-color: var(--accent); }
.stuck-card.k-unreported .n { color: var(--accent); }

/* Both of these sit before a report goes out. Waiting on a reviewer
   is the calm one — somebody just has not looked yet — so it wears
   the waiting colour. Sent back is a fault that was caught and is
   now sitting on the writer, so it wears the finding colour: it
   needs doing, and it is not urgent the way a missed window is. */
.stuck-card.k-unreviewed { border-left-color: var(--waiting); }
.stuck-card.k-unreviewed .n { color: var(--waiting); }
.stuck-card.k-returned   { border-left-color: var(--finding); }
.stuck-card.k-returned .n { color: var(--finding); }

/* Renewals get their own class rather than borrowing one of the four
   above. Those name a specific stuck state, and reusing k-unanswered
   for a colour made a renewals card indistinguishable from a stuck
   visit to anything selecting on it. */
.stuck-card.k-renewal { border-left-color: var(--waiting); }
.stuck-card.k-renewal .n { color: var(--waiting); }
.stuck-card.k-renewal.is-late { border-left-color: var(--overdue); }
.stuck-card.k-renewal.is-late .n { color: var(--overdue); }

/* ---------- Panels and tables ---------- */
.panel {
  background: var(--panel); border: 1px solid var(--edge);
  border-radius: 6px; padding: 20px 22px; margin-bottom: 20px;
  position: relative; overflow: hidden;
}
.panel::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--glow), transparent);
  opacity: .4;
}
/* An inset block INSIDE a panel — the site editor, and anything else
   that opens in place. Deliberately a step up from the panel it sits
   in rather than another panel: a second identical card reads as a
   separate object and flattens the hierarchy. */
.panel-inset {
  background: var(--rise); border: 1px solid var(--edge);
  border-left: 3px solid var(--accent);
  border-radius: 4px; padding: 18px 20px;
}
.panel-inset h3 {
  font-size: 14px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text); margin-bottom: 14px;
}

/* Moving or calling off a check is the exception, not the daily job,
   so it folds away. Native <details> — no JavaScript, which the CSP
   forbids and which would fail on bad signal anyway. */
.visit-more { margin-top: 12px; border-top: 1px solid var(--edge-soft); padding-top: 10px; }
.visit-more > summary {
  cursor: pointer; font-size: 12px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-faint);
  font-family: "Saira Condensed", "Arial Narrow", sans-serif;
  list-style: none; width: max-content;
}
.visit-more > summary::-webkit-details-marker { display: none; }
.visit-more > summary::before { content: "+ "; color: var(--accent); }
.visit-more[open] > summary::before { content: "– "; }
.visit-more > summary:hover { color: var(--text); }
.visit-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.visit-more .btn-row { margin-top: 10px; }
.visit-more .hint { margin-top: 6px; }

.table-scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--text-faint);
  padding: 0 12px 9px; border-bottom: 1px solid var(--edge); white-space: nowrap;
}
td { padding: 11px 12px; border-bottom: 1px solid var(--edge-soft); vertical-align: top; }
tbody tr:hover { background: var(--rise); }
tbody tr:last-child td { border-bottom: none; }
/* `.tbl` marks a data table — rows you scan and act on, as against a
   table used for layout. It was on four tables for months while being
   defined nowhere: they still looked right because the bare `table`
   rules above carried them, which is exactly why nobody noticed. It
   now earns its place by doing the one thing those rules cannot —
   sizing the leading and trailing columns so a thumbnail or a row of
   buttons does not stretch the column it sits in. */
.tbl { table-layout: auto; }
.tbl td:last-child { white-space: nowrap; }

.empty { color: var(--text-faint); padding: 22px 12px; }
.nowrap { white-space: nowrap; }

/* ---------- State pills ---------- */
.pill {
  display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; padding: 3px 8px; border-radius: 3px;
  border: 1px solid currentColor;
}
.pill.due       { color: var(--text-faint); }
.pill.proposed  { color: var(--finding); }
.pill.confirmed { color: var(--accent); }
.pill.attended  { color: var(--waiting); }
.pill.reported  { color: var(--clear); }
.pill.cancelled { color: var(--text-faint); opacity: 0.7; }
.pill.missed    { color: var(--overdue); }
/* No status colour: an archived visit has no per-item outcome, and
   any of the four status colours would claim one. */
.pill.archive   { color: var(--text-faint); text-transform: none; letter-spacing: 0.02em; }

/* The old system's report, shown verbatim and never edited. */
.archive-log {
  margin: 0; padding: 15px 17px; overflow-x: auto;
  background: var(--rise); border: 1px solid var(--edge); border-radius: 6px;
  font: 13px/1.65 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--text); white-space: pre-wrap; word-break: break-word;
}

.res-clear   { color: var(--clear); }
.res-finding { color: var(--finding); }
.res-none    { color: var(--text-faint); }

/* A finding that keeps coming back. Falls out of the structure
   rather than being tracked by anyone. */
.repeat {
  display: inline-block; font-size: 11px; font-weight: 700;
  color: var(--overdue); border: 1px solid var(--overdue);
  padding: 1px 6px; border-radius: 3px; margin-left: 8px;
}

/* ---------- Forms ---------- */
label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 5px; }
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=datetime-local], input[type=time], select, textarea {
  width: 100%; background: var(--void); color: var(--text);
  border: 1px solid var(--edge); border-radius: 5px;
  padding: 9px 11px; font: inherit; font-size: 14px;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--edge-soft);
}
textarea { min-height: 80px; resize: vertical; }
.field { margin-bottom: 15px; }
.row-2 { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 15px; }
@media (max-width: 620px) { .row-2 { grid-template-columns: minmax(0,1fr); } }
.hint { font-size: 12px; color: var(--text-faint); margin: 5px 0 0; }

/* `a.btn-quiet` is in this selector deliberately. A <button> picks up
   these base styles from the element name; an <a class="btn-quiet">
   did not, so it rendered as a bare bordered scrap of text with no
   padding and the wrong font. The calendar's prev / Today / next
   controls were sitting like that unnoticed.
   Fixing it here rather than adding "btn" to each anchor means the
   next one written works too — the same reason setting_raw() exists. */
/* SHAPE and FILL are separate rules, and that is the whole point.
   They used to be one: `a.btn-quiet` was bolted onto the filled rule
   so that anchors picked up the padding and typography. It works —
   and it also paints every quiet LINK in the accent colour, because
   `a.btn-quiet` scores higher than the `.btn-quiet` meant to override
   it. Result: Back, Clear, Today, the calendar arrows and every quick
   filter rendered as a primary action, all shouting equally.

   So: everything that looks like a button gets the shape; the accent
   fill goes only to things that are genuinely the primary action. */
.btn, button, input[type=submit], a.btn-quiet {
  padding: 10px 18px; border-radius: 4px; font-size: 14px;
  font-family: "Saira Condensed", "Oswald", "Arial Narrow", system-ui, sans-serif;
  font-weight: 700; cursor: pointer;
  text-transform: uppercase; letter-spacing: 0.07em;
  text-decoration: none; display: inline-block; line-height: 1.2;
}
.btn, button, input[type=submit] {
  background: var(--accent); color: var(--void); border: 1px solid var(--accent);
  box-shadow: 0 8px 22px -12px var(--glow);
}
.btn:hover, button:hover { background: var(--accent-dark); color: var(--text); text-decoration: none; }
.btn-quiet, a.btn-quiet {
  background: var(--rise); color: var(--text-dim); border: 1px solid var(--edge);
  box-shadow: none;
}
.btn-quiet:hover { background: var(--rise-2); color: var(--text); border-color: var(--edge-loud); }
/* The filter you are currently looking at. Without this, a filtered
   list looks identical to an unfiltered one and you read a subset
   as the whole record. */
.btn-quiet.on {
  background: var(--rise-2); color: var(--text); border-color: var(--accent);
}
.btn-danger { background: transparent; color: var(--overdue); border: 1px solid var(--overdue);
               box-shadow: none; }
.btn-danger:hover { background: var(--overdue); color: var(--void); }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
/* A button that has to look like a link keeps the BODY font and no
   chrome at all — the display face and the glow would make it read
   as the primary action, which is exactly what it is not. */
.link-btn {
  background: none; border: none; color: var(--accent);
  padding: 0; font: inherit; font-size: 13px; cursor: pointer;
  text-transform: none; letter-spacing: normal; box-shadow: none;
}
.link-btn.danger { color: var(--overdue); }

/* ---------- Flashes ---------- */
.flash {
  padding: 10px 14px; border-radius: 5px; margin: 0 0 16px; font-size: 14px;
  border: 1px solid var(--edge); background: var(--rise);
}
.flash-ok    { border-color: var(--clear);   color: var(--clear); }
.flash-warn  { border-color: var(--finding); color: var(--finding); }
.flash-error { border-color: var(--overdue); color: var(--overdue); }

/* ---------- Sign in ---------- */
.signin-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 24px; position: relative;
}
.signin-wrap::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(closest-side at 50% 34%, var(--glow-soft), transparent 70%);
  pointer-events: none;
}
.signin-card {
  width: 100%; max-width: 390px; background: var(--panel);
  border: 1px solid var(--edge); border-radius: 8px; padding: 32px 30px;
  position: relative; overflow: hidden;
  box-shadow: 0 30px 70px -40px var(--void);
}
.signin-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--glow), transparent);
}
.signin-card .mark { text-align: center; margin-bottom: 8px; font-size: 19px; }
/* The stacked lockup on a sign-in card. A grid rather than a
   column: the crest takes a row of its own, and the name and its
   badge stay side by side on the next one — as a column the badge
   dropped onto a third line and read as a stray blue chip. */
.signin-card .mark .brand-lockup {
  display: grid; grid-template-columns: auto auto;
  justify-content: center; align-items: center; gap: 12px 7px;
}
.signin-card .mark .brand-crest { grid-column: 1 / -1; justify-self: center; }
.signin-card .mark .brand-crest { height: 64px; }
.signin-card .mark .brand-word { font-size: 21px; }
.signin-card .mark .brand-tick { margin-left: 0; }
/* .tag here is the tagline under the mark, NOT a status pill. It
   predates the pills and shares their name; scoped to the card so
   the two never collide. */
.signin-card .tag {
  display: block;
  text-align: center; color: var(--accent); font-size: 11px;
  font-family: "Saira", system-ui, sans-serif; font-weight: 700;
  letter-spacing: 0.22em; text-transform: uppercase; margin-bottom: 26px;
  border: none; padding: 0;
}

/* ---------- Write-up checklist ---------- */
.check-item {
  border: 1px solid var(--edge); border-radius: 6px;
  padding: 14px 16px; margin-bottom: 12px; background: var(--rise);
}
.check-head { display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.check-label { font-weight: 600; }
.check-detail { font-size: 12.5px; color: var(--text-faint); margin-top: 3px; }
.choices { display: flex; gap: 6px; flex-wrap: wrap; }
.choices label {
  /* position:relative matters more than it looks: the radio below
     is absolutely positioned, and without a positioned ancestor it
     is placed against the page instead of its own label — landing
     somewhere arbitrary, usually underneath the sticky header,
     where it cannot be clicked at all. */
  position: relative;
  margin: 0; cursor: pointer; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em;
  padding: 5px 11px; border-radius: 4px;
  border: 1px solid var(--edge); color: var(--text-dim);
}
/* The radio is invisible but still exactly where it should be, and
   still the thing being clicked. Hiding it with display:none or
   pointer-events:none would take it out of the keyboard order and
   away from screen readers. */
.choices input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; margin: 0; cursor: pointer;
}
.choices label:focus-within { border-color: var(--accent); }
.choices label:has(input[value=clear]:checked)   { border-color: var(--clear);   color: var(--clear); }
.choices label:has(input[value=finding]:checked) { border-color: var(--finding); color: var(--finding); }
.choices label:has(input[value=not_checked]:checked) { border-color: var(--edge-loud); color: var(--text); }
.check-note { margin-top: 11px; }

/* ---------- Incident block ---------- */
/* A checkbox and its words on one line. A bare <label> wrapping an
   <input type=checkbox> inherits the block form styling and ends up
   with the box on its own line above the text, which reads as two
   separate controls. */
.check-inline {
  display: flex; align-items: center; gap: 9px; margin: 12px 0;
  font-size: 14px; color: var(--text-dim); cursor: pointer;
}
.check-inline input { width: auto; margin: 0; }

/* Label-over-value pairs for a record being READ rather than edited.
   Same shape as the portal's, because a client reading their report
   and an operator reading a sent one are doing the same thing. */
.facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 15px; margin-top: 6px;
}
.fact .k {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-faint);
}
.fact .v { font-size: 15px; margin-top: 3px; }

/* ---------- Audit log ---------- */
/* The quick filter you are currently on. */
.btn-quiet.is-on {
  color: var(--accent); border-color: var(--accent);
}
/* A refusal is the row you are usually looking for, so it is the
   only one tinted. Tinting five families would be tinting nothing. */
tr.aud-refused td { background: var(--rise); }
tr.aud-refused td:first-child { box-shadow: inset 2px 0 0 var(--overdue); }

/* ---------- Ask before doing it ----------
   A destructive action, guarded without a line of JavaScript. The
   summary is the closed control; opening it shows the consequence in
   plain words and the button that actually does the thing. Clicking
   the label again closes it, which is the cancel.

   This replaced ten inline onsubmit="return confirm(...)" guards. The
   CSP is script-src 'self' with no 'unsafe-inline', so not one of
   them had ever run: Retire, Void, Remove and Revoke all fired on the
   first click. A <details> needs no script, works on a keyboard, and
   cannot be silently switched off by a header. */
.ask { display: inline-block; }
.ask > summary {
  list-style: none; cursor: pointer; display: inline-block;
  padding: 7px 13px; border: 1px solid var(--edge); border-radius: 5px;
  font-size: 13px; color: var(--text-dim); background: transparent;
  user-select: none;
}
.ask > summary::-webkit-details-marker { display: none; }
.ask > summary:hover { color: var(--overdue); border-color: var(--overdue); }
.ask[open] > summary { color: var(--overdue); border-color: var(--overdue); }
.ask-body {
  margin-top: 8px; padding: 12px 14px; border: 1px solid var(--overdue);
  border-radius: 6px; background: var(--rise);
}
.ask-q { margin: 0 0 10px; font-size: 13.5px; line-height: 1.45; }
.ask-body form { display: inline; }

/* ---------- Photos ---------- */
.thumb {
  width: 54px; height: 54px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--edge); display: block; background: var(--void);
}
.thumb-missing {
  display: flex; align-items: center; justify-content: center;
  color: var(--overdue); font-weight: 700; border-style: dashed;
}
/* Row-height, for a list whose job is identifying which photo to
   remove rather than showing what is in it. */
.thumb-sm { width: 44px; height: 44px; }
.photo-strip { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* A photograph of something that was NOT on the checklist has no
   item label above it to say what it is, so the caption has to do
   that work — which means it needs room, and the picture needs to
   be big enough to read without opening it. */
.photo-fig { margin: 0; width: 128px; }
.photo-fig .thumb { width: 128px; height: 96px; }
.photo-fig figcaption {
  font-size: 12px; color: var(--text-faint); margin-top: 5px; line-height: 1.35;
}


/* ============================================================
   THE SIDE RAIL

   A top bar runs out of room; a rail does not. Eleven screens fit
   with their names visible, and the twelfth costs nothing.

   There is no JavaScript in it. On a phone it opens with a hidden
   checkbox and a <label>, because the CSP here is script-src 'self'
   and an inline handler would be refused outright.
   ============================================================ */
:root { --rail-w: 208px; }

.rail-toggle { position: absolute; opacity: 0; pointer-events: none; }

.rail {
  position: fixed; top: 0; left: 0; bottom: 0; width: var(--rail-w);
  z-index: 40; display: flex; flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--edge);
  box-shadow: 1px 0 0 var(--glow-soft);
  overflow-y: auto; overscroll-behavior: contain;
}
.rail-mark {
  display: block; padding: 18px 16px 16px; color: inherit;
  border-bottom: 1px solid var(--edge-soft);
}
.rail-mark:hover { text-decoration: none; }
/* 208px is not enough for badge-and-name on one line, so they
   stack. The short tag is hidden here on purpose: it exists to
   identify the company where the badge is absent, and the badge is
   right above it. */
.rail-mark .brand-lockup {
  flex-direction: column; align-items: flex-start; gap: 9px;
}
.rail-mark .brand-crest { height: 34px; }
.rail-mark .brand-word {
  font-size: 15px; white-space: nowrap; line-height: 1.15;
  background-size: 100% 1.15em;
}
.rail-mark .brand-tick { display: none; }

.rail-items { padding: 10px 8px; display: flex; flex-direction: column; gap: 1px; flex: 1; }

.rail-link {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border-radius: 5px;
  color: var(--text-dim); font-size: 13.5px; font-weight: 500;
  position: relative; white-space: nowrap;
}
.rail-link:hover { background: var(--rise); color: var(--text); text-decoration: none; }
.rail-link .nav-ico { flex: none; opacity: .72; }
.rail-link:hover .nav-ico { opacity: 1; }

/* The current screen. A filled block plus a bar on the leading
   edge — colour alone would be the only signal for anybody who
   cannot separate these two blues. */
.rail-link.on {
  background: var(--rise-2); color: var(--text); font-weight: 600;
}
.rail-link.on::before {
  content: ""; position: absolute; left: -8px; top: 6px; bottom: 6px;
  width: 3px; border-radius: 0 3px 3px 0;
  background: var(--accent); box-shadow: 0 0 12px var(--glow);
}
.rail-link.on .nav-ico { opacity: 1; color: var(--accent); }

.rail-split {
  height: 1px; margin: 9px 11px; background: var(--edge-soft);
}

.rail-foot { padding: 10px 8px 16px; border-top: 1px solid var(--edge-soft); }
.rail-who {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 11px 12px;
}
.rail-avatar {
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--rise-2); border: 1px solid var(--edge-loud);
  font-family: "Saira Condensed", "Oswald", system-ui, sans-serif;
  font-weight: 800; font-size: 14px; color: var(--text);
}
.rail-who-t { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.rail-who-t strong {
  font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-who-t span {
  font-family: "Saira", system-ui, sans-serif;
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--accent); font-weight: 700;
}

/* The page sits beside the rail rather than under it. */
body.has-rail .wrap {
  margin-left: var(--rail-w); max-width: none;
  padding: 26px 32px 80px;
}
body.has-rail .wrap > * { max-width: 1180px; }

/* The phone bar. Hidden on a desktop, where the rail is always up. */
.railbar { display: none; }
.rail-scrim { display: none; }

@media (max-width: 900px) {
  .rail {
    transform: translateX(-100%);
    transition: transform .18s ease;
    box-shadow: 0 0 60px -10px var(--void);
  }
  .rail-toggle:checked ~ .rail { transform: none; }

  .railbar {
    display: flex; align-items: center; gap: 14px;
    position: sticky; top: 0; z-index: 30; min-height: 58px; padding: 0 16px;
    background: var(--panel); border-bottom: 1px solid var(--edge);
    box-shadow: 0 1px 0 var(--glow-soft);
  }
  .railbar-mark { color: inherit; }
  .railbar-mark .brand-crest { height: 28px; }
  .railbar-mark .brand-word { font-size: 15px; }

  /* Three lines, drawn rather than typed, so it is the same shape
     in every browser. 44px square: a thumb target, not a pixel. */
  .rail-burger {
    width: 44px; height: 44px; flex: none; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--edge); border-radius: 5px;
  }
  .rail-burger span,
  .rail-burger span::before,
  .rail-burger span::after {
    content: ""; display: block; width: 18px; height: 2px;
    background: var(--text); border-radius: 2px; position: relative;
  }
  .rail-burger span::before { position: absolute; top: -6px; }
  .rail-burger span::after  { position: absolute; top: 6px; }

  .rail-toggle:checked ~ .rail-scrim {
    display: block; position: fixed; inset: 0; z-index: 35;
    background: var(--void); opacity: .6;
  }

  body.has-rail .wrap { margin-left: 0; padding: 20px 18px 70px; }
}

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

/* ---------- The permissions grid ---------- */
.perm-grid td { vertical-align: middle; }
.perm-grid .sub { font-size: 12px; margin-top: 2px; }
.perm-box { display: inline-flex; padding: 8px; cursor: pointer; margin: 0; }
.perm-box input { width: 17px; height: 17px; margin: 0; cursor: pointer; accent-color: var(--accent); }
.perm-yes { color: var(--clear); font-weight: 700; }
.perm-locked { color: var(--text-faint); }

/* ============================================================
   THE CALENDAR

   A due window covers SEVERAL days, on purpose: the cadence says
   how often, never when. So a due entry is drawn on every day of
   its window rather than on one guessed day.
   ============================================================ */
.cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px; background: var(--edge-soft);
  border: 1px solid var(--edge); border-radius: 6px; overflow: hidden;
}
.cal-dow {
  background: var(--rise); padding: 8px 10px;
  font-family: "Saira", system-ui, sans-serif;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.14em;
  font-weight: 700; color: var(--text-faint);
}
.cal-cell {
  background: var(--panel); min-height: 104px; padding: 7px 8px 9px;
  display: flex; flex-direction: column; gap: 3px;
}
.cal-cell.is-out { background: var(--void); }
.cal-cell.is-today { box-shadow: inset 0 0 0 1px var(--accent); }
.cal-cell.is-today .cal-n { color: var(--accent); font-weight: 800; }
.cal-n {
  font-family: "Saira Condensed", "Oswald", system-ui, sans-serif;
  font-size: 14px; font-weight: 700; color: var(--text-dim); line-height: 1;
  margin-bottom: 2px;
}

.cal-ev {
  display: block; font-size: 11.5px; line-height: 1.3;
  padding: 3px 6px; border-radius: 3px;
  background: var(--rise); color: var(--text-dim);
  border-left: 3px solid var(--edge-loud);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-ev:hover { background: var(--rise-2); color: var(--text); text-decoration: none; }
.cal-t { color: var(--text-faint); margin-right: 4px; }

/* The middle days of a due window. The band continues so the range
   still reads, but the name is printed once per week row instead of
   once per day — a fortnightly cadence was writing one client's name
   fourteen times and burying everything else. Kept the full height of
   a normal row's worth of space so the bar lines up with the labelled
   day beside it rather than floating. */
.cal-ev.cal-cont {
  height: 7px; padding: 0; border-radius: 0 2px 2px 0;
  border-left: 3px solid var(--edge-loud);
  opacity: .55; margin-top: 5px;
}
.cal-ev.cal-cont:hover { opacity: 1; }

/* The same five states, in the same colours, as everywhere else.
   A calendar with its own palette is a second vocabulary to learn. */
.cal-ev.is-due       { border-left-color: var(--text-faint); }
.cal-ev.is-proposed  { border-left-color: var(--finding); }
.cal-ev.is-confirmed { border-left-color: var(--accent); }
.cal-ev.is-attended  { border-left-color: var(--waiting); }
.cal-ev.is-reported  { border-left-color: var(--clear); }
.cal-ev.is-missed    { border-left-color: var(--overdue); color: var(--overdue); }

.cal-key {
  display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 14px;
  font-size: 12px; color: var(--text-faint);
}
.cal-key i.k {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  margin-right: 6px; vertical-align: -1px; background: var(--edge-loud);
}
.cal-key i.k.due       { background: var(--text-faint); }
.cal-key i.k.proposed  { background: var(--finding); }
.cal-key i.k.confirmed { background: var(--accent); }
.cal-key i.k.attended  { background: var(--waiting); }
.cal-key i.k.reported  { background: var(--clear); }
.cal-key i.k.missed    { background: var(--overdue); }

/* On a phone a seven-column month grid is unreadable. The columns
   stay but the cells shrink to the number and a colour bar — the
   detail is one tap away on the day itself. */
@media (max-width: 720px) {
  .cal-cell { min-height: 62px; padding: 5px 4px; }
  .cal-dow { padding: 6px 4px; font-size: 9px; letter-spacing: 0.08em; text-align: center; }
  .cal-ev { font-size: 0; padding: 0; height: 5px; border-radius: 2px;
            border-left: none; }
  .cal-ev.is-due       { background: var(--text-faint); }
  .cal-ev.is-proposed  { background: var(--finding); }
  .cal-ev.is-confirmed { background: var(--accent); }
  .cal-ev.is-attended  { background: var(--waiting); }
  .cal-ev.is-reported  { background: var(--clear); }
  .cal-ev.is-missed    { background: var(--overdue); }
}

/* ---------- The rail's own card ---------- */
a.rail-who { color: inherit; border-radius: 6px; }
a.rail-who:hover { background: var(--rise); text-decoration: none; }
a.rail-who.on { background: var(--rise); }
a.rail-who.on .rail-who-t strong { color: var(--accent); }
.rail-avatar { overflow: hidden; }
.rail-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ============================================================
   THE CARD

   An account has a name and a hash. A person has a face, a job
   title, and a count of the things they have actually done — which
   for the field tech here goes back four years through the imported
   archive, because those visits were done by the same people.
   ============================================================ */
.me-card {
  display: flex; gap: 22px; align-items: center; flex-wrap: wrap;
}
.me-face {
  width: 88px; height: 88px; flex: none; border-radius: 8px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--rise-2); border: 1px solid var(--edge-loud);
  box-shadow: 0 0 0 1px var(--glow-soft), 0 14px 30px -18px var(--void);
}
.me-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.me-face span {
  font-family: "Saira Condensed", "Oswald", system-ui, sans-serif;
  font-size: 38px; font-weight: 800; color: var(--text);
}

.me-who { min-width: 0; flex: 1 1 220px; }
.me-name { margin: 0 0 2px; font-size: 26px; background-size: 100% 1.1em; }
.me-title {
  font-family: "Saira", system-ui, sans-serif;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.14em;
  font-weight: 700; color: var(--accent);
}
.me-role { font-size: 12.5px; color: var(--text-faint); margin-top: 2px; }
.me-lines { margin-top: 10px; font-size: 14px; line-height: 1.6; }

.me-stats {
  display: grid; grid-template-columns: repeat(4, minmax(86px, 1fr));
  gap: 10px; flex: 0 1 auto;
}
@media (max-width: 720px) { .me-stats { grid-template-columns: repeat(2, minmax(0,1fr)); width: 100%; } }
.me-stat {
  background: var(--rise); border: 1px solid var(--edge-soft);
  border-radius: 6px; padding: 12px 13px; text-align: center;
}
.me-stat b {
  display: block; line-height: 1;
  font-family: "Saira Condensed", "Oswald", system-ui, sans-serif;
  font-size: 26px; font-weight: 800; color: var(--text);
}
.me-stat span {
  display: block; margin-top: 6px;
  font-family: "Saira", system-ui, sans-serif;
  font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-faint); font-weight: 600;
}
