/* Synovia Fusion :: PTI Portal
   One stylesheet, no framework. The portal is two screens; a CSS framework
   would be larger than the pages it styles. Colours are tokens so a dark
   variant is a block of overrides rather than a rewrite. */
/* ── Flow 3 tokens ─────────────────────────────────────────────────────
   Verbatim from SYNOVIA_BRAND_DEFINITION.md §1.1, which states: "For PTI
   work, use Flow 3." This portal is PTI. Previously these were the right
   NAMES with drifted hexes — --ink #16202b against the brand's #132133,
   --bad var(--bad) against #F04438 — which read as almost-right, the hardest
   kind of wrong to notice.

   Flow 3 and the older Bootstrap "Fusion" theme are deliberately
   incompatible and must never be mixed on one page. Nothing here loads
   fusion_theme.css. */
:root {
  /* brand darks */
  --abyss: #07152B; --navy: #0F2A4A; --navy2: #143458;
  /* brand accents */
  --flow: #13DAC6; --flow-deep: #0AB6A6;
  --fusion: #FF7A45; --fusion2: #FF9A6B;
  --sky: #3BA0FF;
  /* surfaces */
  --bg: #EEF3FA; --surface: #FFFFFF; --surface2: #F7FAFF;
  /* ink */
  --ink: #132133; --muted: #5E7085; --hairline: #DCE6F2;
  /* status */
  --good: #17B26A; --warn: #F79009; --bad: #F04438; --info: #7A5AF8;
  /* shape */
  --radius: 16px; --radius-sm: 11px; --pill: 999px;
  --shadow: 0 1px 2px rgba(15,42,74,.06), 0 8px 24px rgba(15,42,74,.06);
  --shadow-lg: 0 18px 50px rgba(7,21,43,.16);
  --font: 'Montserrat', 'Segoe UI', system-ui, -apple-system, sans-serif;

  /* Aliases for markup written before the tokens were adopted. Kept so
     this change restyles nothing by accident; migrate and delete. */
  --canvas: var(--bg);
  --accent: var(--flow);
}

/* Self-hosted: no Google Fonts call from a portal that posts delivery
   records. TTF rather than woff2 only because the converter was not
   available — see the brand README. */
@font-face { font-family: 'Montserrat'; font-weight: 400; font-display: swap;
  src: url('/brand/fonts/Montserrat-Regular.ttf') format('truetype'); }
@font-face { font-family: 'Montserrat'; font-weight: 600; font-display: swap;
  src: url('/brand/fonts/Montserrat-SemiBold.ttf') format('truetype'); }
@font-face { font-family: 'Montserrat'; font-weight: 700; font-display: swap;
  src: url('/brand/fonts/Montserrat-Bold.ttf') format('truetype'); }

* { box-sizing: border-box; }
body { margin: 0; font-family: var(--font); color: var(--ink);
       background: var(--canvas); font-size: 14px; }
body.centred { display: grid; place-items: center; min-height: 100vh; }

.top { display: flex; align-items: center; gap: 16px; padding: 0 26px;
       min-height: 58px; flex-wrap: wrap;
       background: var(--surface); border-bottom: 1px solid var(--hairline); }
.top nav { flex-wrap: wrap; }
.brand { font-weight: 700; font-size: 16px; text-decoration: none; color: var(--ink); }
/* --flow is 1.77:1 on white and cannot carry text. It was the brand's
   own accent and I used it for the product name, which made the one
   word identifying the portal the least readable thing on the page.
   Flow stays as a FILL below; the word takes navy at 14.48:1. */
.brand span { color: var(--navy); }
.top nav { display: flex; gap: 6px; }
.top nav a { padding: 7px 13px; border-radius: var(--radius-sm); text-decoration: none;
             color: var(--muted); font-size: 13px; }
.top nav a:hover { background: var(--canvas); }
.top nav a.on { background: var(--ink); color: #fff; }
.spacer { flex: 1; }
.lookup input { width: 190px; font-size: 12.5px; padding: 6px 10px; }
.top nav { gap: 2px; }
.top nav a { padding: 7px 11px; font-size: 12.5px; }
.who { color: var(--muted); font-size: 13px; }
.out { color: var(--muted); font-size: 13px; text-decoration: none; }
/* --bad #F04438 is 3.76:1 on white — short of 4.5. Same hue darkened,
   as the status pills already are. */
.out:hover { color: #B42318; }

main { max-width: 1320px; margin: 0 auto; padding: 22px 26px 60px; }
.card { background: var(--surface); border: 1px solid var(--hairline);
        border-radius: var(--radius); padding: 18px 20px; margin-bottom: 16px; }

.kpi { display: flex; gap: 30px; flex-wrap: wrap; margin-bottom: 15px; }
.kpi span { display: block; font-size: 10.5px; letter-spacing: .7px;
            text-transform: uppercase; color: var(--muted); }
.kpi b { display: block; font-size: 23px; line-height: 1.25; }
.kpi b.bad { color: var(--bad); }
.eyebrow { font-size: 10.5px; letter-spacing: .7px; text-transform: uppercase;
           color: var(--muted); margin-bottom: 9px; }

.filters { display: flex; gap: 11px; align-items: center; flex-wrap: wrap; }
.filters label { font-size: 13px; display: flex; align-items: center; gap: 6px; }
input, select { font-family: var(--font); font-size: 13px; padding: 8px 10px;
                color: var(--ink); background: var(--surface);
                border: 1px solid var(--hairline); border-radius: var(--radius-sm); }
input:focus, select:focus { outline: 2px solid rgba(19,218,198,.45); }
.act { font-family: var(--font); font-size: 13px; padding: 8px 15px; border-radius: var(--radius-sm);
       border: 1px solid var(--hairline); background: var(--surface); color: var(--ink);
       cursor: pointer; text-decoration: none; display: inline-block; }
.act.submit { background: var(--ink); color: #fff; border-color: var(--ink); }
.act.ghost { color: var(--muted); }
.act.sm { padding: 5px 10px; font-size: 12px; }
.act:disabled { opacity: .45; cursor: not-allowed; }

.tbl { width: 100%; border-collapse: collapse; }
.tbl th { text-align: left; font-size: 10.5px; letter-spacing: .6px;
          text-transform: uppercase; color: var(--muted); font-weight: 600;
          padding: 9px 10px; border-bottom: 1px solid var(--hairline); }
.tbl td { padding: 10px; border-bottom: 1px solid var(--hairline);
          font-size: 13px; vertical-align: middle; }
.tbl .c { text-align: center; }
.tbl .r { text-align: right; }
.tbl tr.need td { background: rgba(198,40,40,.045); }
.tbl tr.silent td { background: rgba(198,40,40,.06); color: #9B1C1C; }
/* Was `.tbl tr.part td.nm`, but analysis.html only ever sets tr.silent,
   so the one cell that counts PODs already at Mandata with nobody's
   name on them was never highlighted. Both halves of the rule were
   orphaned — files.html sets tr.part and has no td.nm. */
.tbl td.nm { color: #8a5a00; font-weight: 600; }
.empty { text-align: center; color: var(--muted); padding: 24px; }
.g { font-size: 11px; color: var(--muted); }
.mono { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11.5px; }
input.name { width: 190px; font-size: 12px; padding: 5px 8px; }

.src { font-size: 10px; letter-spacing: .4px; text-transform: uppercase;
       padding: 3px 8px; border-radius: var(--pill); white-space: nowrap; }
.src-EventName    { background: rgba(19,218,198,.18); color: #0B7A6F; }
.src-EventComment { background: rgba(227,160,8,.2);  color: #8a5a00; }
.src-MANUAL       { background: rgba(120,110,255,.18); color: #4a3fd0; }
.src-stored       { background: rgba(120,130,145,.2); color: #4b5665; }
.src-sibling      { background: rgba(59,160,255,.16); color: #1A5FA8; }
.src-none         { background: rgba(198,40,40,.14); color: #9B1C1C; }

.warn { background: rgba(227,160,8,.12); border-left: 3px solid var(--warn);
        padding: 11px 15px; border-radius: var(--radius-sm); font-size: 13px; margin-bottom: 15px; }
.warn.danger { background: rgba(198,40,40,.1); border-left-color: var(--bad); }
.warn ul { margin: 8px 0 0; padding-left: 18px; }

.barrow { display: flex; align-items: center; gap: 9px; }
.bar { height: 7px; width: 110px; border-radius: 4px;
       background: rgba(120,120,120,.18); overflow: hidden; }
.bar i { display: block; height: 100%; background: var(--accent); }

.login { width: 340px; }
.login h1 { margin: 0 0 4px; font-size: 21px; }
.login h1 span { color: var(--accent); }
.login .sub { margin: 0 0 18px; color: var(--muted); font-size: 13px; }
.login label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 13px; }
.login input { width: 100%; margin-top: 5px; }
.login .act { width: 100%; }
.err { background: rgba(198,40,40,.1); border-left: 3px solid var(--bad);
       padding: 9px 12px; border-radius: 8px; font-size: 13px; margin-bottom: 14px; }
.sub { color: var(--muted); font-size: 12.5px; margin: 10px 0 0; }

/* The half-built dark mode that was here is removed rather than
   finished. It redefined four tokens and left error text at 2.25:1, and
   Flow 3 has no dark variant to align to — so completing it would have
   meant inventing a second palette nobody owns. A legible light theme
   beats an unreviewed dark one. */

.v-RUNNING     { background: rgba(80,140,255,.18);  color: #2a52b8; }
.v-PAUSED      { background: rgba(120,120,120,.18); color: #55606d; }
.v-OVERDUE     { background: rgba(227,160,8,.22);   color: #8a5a00; }
.v-LAST        { background: rgba(198,40,40,.14);   color: #9B1C1C; }
.src[class*="v-LAST"], .src[class*="v-NEVER"] {
  background: rgba(198,40,40,.14); color: #9B1C1C; }
pre.json { white-space: pre-wrap; word-break: break-all; max-height: 260px;
           overflow: auto; background: rgba(120,120,120,.08); padding: 9px 11px;
           border-radius: 8px; margin: 6px 0; font-size: 11px; }
details summary { cursor: pointer; padding: 3px 0; }
.tbl th[scope], .tbl tbody th { font-size: 11px; width: 120px; color: var(--muted); }

.top nav.row2 a { font-size: 12px; opacity: .85; }
.tbl tr.used td { background: rgba(19,218,198,.07); }

/* report emphasis */
.kpi b.bad { color: var(--bad); }

/* process-flow diagram */
svg.flow { width: 100%; height: auto; max-width: 980px; display: block; margin: 4px auto 14px; }
svg.flow .ext { fill: var(--navy); }
svg.flow .stp { fill: var(--surface); stroke: var(--hairline); stroke-width: 1.5; }
svg.flow .stp.alt { fill: var(--surface2); }
svg.flow .stp.off { stroke-dasharray: 5 4; }
svg.flow .stp.me  { fill: rgba(19,218,198,.10); stroke: var(--flow); stroke-width: 2; }
svg.flow .t  { font-family: var(--font); text-anchor: middle; fill: var(--ink); }
svg.flow .t.b { font-size: 14px; font-weight: 700; }
svg.flow .t.s { font-size: 10.5px; fill: var(--muted); }
svg.flow .t.f { fill: var(--flow-deep); font-weight: 600; }
svg.flow .ext + .t, svg.flow .ext ~ .t { }
svg.flow text[y="50"], svg.flow text[y="69"] { fill: #fff; }
svg.flow .ln { stroke: var(--muted); stroke-width: 1.6; }
svg.flow .ln.f { stroke: var(--flow); stroke-width: 2.2; }
svg.flow .ln.dash { stroke-dasharray: 4 4; }

/* ── Status Pill ───────────────────────────────────────────────────────
   Named component from the brand guide. One class, st-<STATUS>, so a
   status renders identically on every page and FAILED never scans the
   same as CONFIRMED.

   The colours are NOT the brand's. Flow 3 puts token-hex text on a 12-14%
   tint of itself and every one of its eight pills fails WCAG AA — p-flow
   at 2.32:1, p-good 2.45, p-bad 3.22. These are the same hues darkened to
   clear 4.5:1 on the same tints. An unreadable status pill is worse than
   plain text, because it looks like it is doing its job. */
.st { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px;
      font-weight: 700; letter-spacing: .3px; padding: 3px 9px;
      border-radius: var(--pill); white-space: nowrap; text-transform: uppercase; }
.st::before { content: ""; width: 6px; height: 6px; border-radius: 50%;
      background: currentColor; flex: none; }
.st-CONFIRMED, .st-SUCCESS, .st-OK, .st-LOADED, .st-LIVE
      { background: color-mix(in srgb, var(--good) 14%, transparent); color: #107D4A; }
.st-SENT, .st-SENDING, .st-RUNNING, .st-INGESTED
      { background: color-mix(in srgb, var(--sky) 15%, transparent); color: #1A5FA8; }
.st-PENDING, .st-PARTIAL, .st-WARN, .st-OVERDUE
      { background: color-mix(in srgb, var(--warn) 16%, transparent); color: #8A5A00; }
.st-FAILED, .st-EXHAUSTED, .st-ERROR, .st-TIMEOUT
      { background: color-mix(in srgb, var(--bad) 13%, transparent); color: #B42318; }
.st-SKIPPED, .st-ARCHIVED, .st-SUPERSEDED, .st-PAUSED, .st-UNMAPPED
      { background: color-mix(in srgb, var(--muted) 14%, transparent); color: #4B5A6B; }
.st-BLOCKED, .st-INFO { background: color-mix(in srgb, var(--info) 14%, transparent); color: #5B3FD4; }

/* ── bringing it to life ───────────────────────────────────────────────
   Restrained on purpose. This screen posts delivery records; movement
   that draws the eye away from the row being ticked is a cost, not a
   feature. Panels settle on load, rows respond to the cursor, and
   nothing animates on its own after that. */
.card { animation: settle .32s ease-out both; }
.card:nth-of-type(2) { animation-delay: .04s; }
.card:nth-of-type(3) { animation-delay: .08s; }
.card:nth-of-type(4) { animation-delay: .12s; }
@keyframes settle { from { opacity: 0; transform: translateY(8px); }
                    to   { opacity: 1; transform: none; } }

.tbl tbody tr { transition: background .14s ease; }
.tbl tbody tr:hover { background: var(--surface2); }
.tbl tr.need:hover { background: rgba(240,68,56,.07); }

.act { transition: transform .12s ease, box-shadow .16s ease, background .16s ease; }
.act:hover:not(:disabled) { transform: translateY(-1px); box-shadow: var(--shadow); }
.card:hover { box-shadow: var(--shadow); }
.act:active:not(:disabled) { transform: translateY(0); }
.act.submit { background: var(--fusion); border-color: var(--fusion); color: #fff; }
.act.submit:hover:not(:disabled) { background: var(--fusion2); border-color: var(--fusion2); }

/* The accent rail — a named Flow 3 component. One stripe down the left of
   a panel, carrying the brand gradient. Cheap, and it is what makes a
   stack of white cards read as Fusion rather than as Bootstrap. */
.card { position: relative; overflow: hidden; }
.card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; background: linear-gradient(180deg, var(--flow), var(--sky));
  opacity: .85; }
.card.plain::before { display: none; }

/* Focus, visibly. The keyboard path through tick-and-send has to be
   followable or the accessible route is theoretical. */
/* A focus indicator needs 3:1 against its surroundings. --flow-deep is
   2.54:1 on white, so the ring I added in the commit that argued the
   keyboard path must be followable was itself barely visible. Navy
   carries it at 14.48:1; the flow halo keeps it recognisably ours. */
:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--flow) 35%, transparent);
  border-radius: 4px; }

@media (prefers-reduced-motion: reduce) {
  .card { animation: none; opacity: 1; transform: none; }
  .act, .tbl tbody tr { transition: none; }
  .act:hover:not(:disabled) { transform: none; }
}
