/* ==========================================================================
   Pulse — desk screens

   An operations console, not a document. Three rules hold the system together:

   1. Panels are DRAWN, not FLOATED. A hairline separates content; a shadow
      says "this is above the page" and is therefore kept for things that
      genuinely are — dialogs, tooltips, sticky headers.
   2. Not every group needs a box. A metric strip, a status summary and a work
      queue are three different shapes. Making all three a white rounded card
      is what made this read as a spreadsheet with nice corners.
   3. Weight is a ranking. If two things look equally important, one of them
      is wrong.

   Colours are not chosen by eye: the three categorical series below were run
   through the palette validator (all-pairs, light surface #FFFFFF) and clear
   the lightness band, chroma floor, CVD separation, normal-vision floor and
   the 3:1 contrast gate. Status colours are reserved and never reused as a
   series — they always ship beside a label, never as colour alone.
   ========================================================================== */

:root{
  /* surfaces — a quiet canvas with panels barely lifted off it */
  --bg:          #F4F6F9;
  --surface:     #FFFFFF;
  --surface-2:   #F7F9FC;
  --surface-3:   #EDF1F7;
  --brand:       #101B33;
  --brand-2:     #1B2A4A;

  /* ink */
  --ink:         #0F1B32;
  --ink-2:       #485873;
  /* Darkened for contrast. --muted was #7B8BA4 at 3.3:1 on the surface it
     sits on, and it is the colour of every table heading, every stat label
     and every row subtitle — most of the words on most screens. --faint was
     #A7B3C6 at 2.1:1, and it is the colour this application uses to say "no
     data", which the architecture insists must be *stated* rather than faded
     until it disappears. */
  --muted:       #5E6E88;
  --faint:       #8492A8;
  --line:        #E1E7F0;
  --line-2:      #EDF1F7;

  /* categorical series — validated, fixed order, never cycled */
  --series-1:    #2D6BC4;
  --series-2:    #C9622A;
  --series-3:    #00A07A;

  /* The one accent, and it is the same blue the charts plot with — so a link,
     a focus ring and a data line are recognisably the same product. */
  --accent:      #2D6BC4;
  --accent-ink:  #22539B;

  /* status — reserved, always with a label beside them */
  --good:        #1F7A5C;
  --warn:        #B7791F;
  --warn-mark:   #E8A33D;
  --serious:     #D2691E;
  --bad:         #C0392F;

  /* tints */
  --good-wash:   #E8F5EF;
  --warn-wash:   #FDF4E3;
  --bad-wash:    #FCEEEC;
  --accent-wash: #EAF1FC;

  /* Radius, one step tighter than before: a large radius reads as "card",
     this reads as "panel". */
  --r:      10px;
  --r-sm:   8px;
  --r-xs:   6px;
  --pill:   999px;

  /* Spacing scale, used as a scale and not as a suggestion: tight inside a
     row, moderate between related things, wide between sections. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s7: 32px; --s8: 44px;

  /* Three shadows, two of them for things that genuinely float. */
  --sh-sticky: 0 4px 10px rgba(15,27,50,.05);
  --sh-pop:    0 4px 12px rgba(15,27,50,.09), 0 1px 3px rgba(15,27,50,.06);
  --sh-modal:  0 24px 60px rgba(15,27,50,.22), 0 2px 8px rgba(15,27,50,.1);

  --sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  --nav-w: 218px;
}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--bg);
  color:var(--ink);
  font-family:var(--sans);
  font-size:13.5px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
::selection{background:var(--accent-wash)}

/* Numbers should not look like prose. Everything reporting a figure gets the
   same treatment, so columns line up and a total is scannable. */
.num,.val,.pct,.when,.mono,.metric-val,.hc-score,.hs-num,.pc-num,.q-fig b,.tick,.ar-due{
  font-variant-numeric:tabular-nums;
}

/* ==========================================================================
   Shell — permanent navigation, grouped by the job being done
   ========================================================================== */
.shell{display:grid;grid-template-columns:var(--nav-w) minmax(0,1fr);min-height:100vh}

.rail{
  background:var(--brand);
  color:#fff;
  display:flex;
  flex-direction:column;
  position:sticky;
  top:0;
  height:100vh;
  padding:var(--s4) 10px 10px;
}
.rail .mark{display:flex;align-items:center;gap:9px;padding:0 6px var(--s4)}
.rail .mark .dot{
  width:28px;height:28px;border-radius:8px;flex:0 0 28px;
  background:linear-gradient(140deg,var(--warn-mark),#C9622A);
  display:grid;place-items:center;font-weight:700;font-size:14px;color:var(--brand);
}
.rail .mark b{font-size:15px;font-weight:620;letter-spacing:-.01em;display:block;line-height:1.15}
.rail .mark span{font-size:10.5px;color:#7787A4;letter-spacing:.02em}

/* Search belongs with the mark rather than in the list: it is a way into
   everything, not one more destination. */
.railsearch{padding:0 2px var(--s3)}
.railsearch input{
  width:100%;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);
  color:#fff;font-size:12.5px;padding:7px 10px 7px 29px;border-radius:var(--r-xs);
  background-repeat:no-repeat;background-position:9px center;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='7' cy='7' r='4.6' stroke='%236C7D99' stroke-width='1.5'/%3E%3Cpath d='M10.4 10.4 14 14' stroke='%236C7D99' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
}
.railsearch input::placeholder{color:#6C7D99}
.railsearch input:focus{
  outline:0;border-color:rgba(255,255,255,.26);box-shadow:none;background:rgba(255,255,255,.1);
}

.rail nav{display:flex;flex-direction:column;gap:1px;overflow-y:auto;flex:1;margin:0 -2px;padding:0 2px}
/* The gap above a group label is what separates the groups. A rule would be
   one more line in a place that already has enough of them. */
.rail .group{
  font-size:9.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:#5A6B89;padding:var(--s5) 8px var(--s1);
}
.rail nav > a:first-child{margin-top:2px}
.rail nav a{
  position:relative;
  display:flex;align-items:center;gap:9px;
  padding:7px 8px;border-radius:var(--r-xs);
  font-size:13px;font-weight:500;color:#A9B7CD;
  transition:background .13s ease,color .13s ease;
}
.rail nav a:hover{background:rgba(255,255,255,.055);color:#E6ECF5}
/* Active: a marker and a tint. Obvious without growing the row — a selected
   item that changes height makes the whole list twitch as you pass over it. */
.rail nav a.on{background:rgba(255,255,255,.09);color:#fff;font-weight:600}
.rail nav a.on::before{
  content:"";position:absolute;left:-2px;top:7px;bottom:7px;width:2.5px;
  border-radius:0 2px 2px 0;background:var(--warn-mark);
}
.rail nav a .ic{width:15px;height:15px;flex:0 0 15px;opacity:.72}
.rail nav a:hover .ic,.rail nav a.on .ic{opacity:1}

.rail .count{
  margin-left:auto;background:var(--warn-mark);color:var(--brand);
  font-size:10.5px;font-weight:700;border-radius:var(--pill);
  padding:0 6px;min-width:18px;height:17px;
  display:inline-flex;align-items:center;justify-content:center;
}
.rail .count.quiet{background:rgba(255,255,255,.13);color:#C6D2E4}

.rail .who{
  margin-top:auto;border-top:1px solid rgba(255,255,255,.08);
  padding:var(--s3) 8px 4px;display:flex;align-items:center;gap:9px;
}
.rail .who .av{
  width:28px;height:28px;border-radius:var(--pill);flex:0 0 28px;display:grid;place-items:center;
  background:rgba(255,255,255,.11);font-size:11px;font-weight:650;color:#DDE5F0;letter-spacing:.02em;
}
.rail .who>span{min-width:0;flex:1}
.rail .who b{
  display:block;font-size:12.5px;font-weight:600;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rail .who span{display:block;font-size:10.5px;color:#7787A4}
.rail .who button{
  background:none;border:0;color:#7787A4;cursor:pointer;font-family:inherit;
  font-size:11px;font-weight:600;padding:4px 6px;border-radius:var(--r-xs);
  transition:background .13s ease,color .13s ease;
}
.rail .who button:hover{color:#fff;background:rgba(255,255,255,.08)}

.sheet{padding:var(--s6) var(--s7) var(--s8);max-width:1520px;width:100%;min-width:0}

/* ==========================================================================
   Page head — where am I, what am I looking at, what can I do about it
   One line of identity, one line of context, controls on the right. It does
   not get to own a quarter of the screen before the first number.
   ========================================================================== */
.head{
  display:flex;align-items:flex-end;gap:var(--s5);flex-wrap:wrap;
  padding-bottom:var(--s3);margin-bottom:var(--s5);
  border-bottom:1px solid var(--line);
}
.head .titles{min-width:0;flex:1}
.crumb{
  font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);display:flex;align-items:center;gap:7px;margin-bottom:4px;
}
.crumb a{color:var(--accent)}
.crumb a:hover{text-decoration:underline}
.crumb .live{
  width:5px;height:5px;border-radius:50%;background:var(--good);
  box-shadow:0 0 0 3px var(--good-wash);flex:0 0 5px;
}
h1{font-size:21px;font-weight:650;letter-spacing:-.02em;line-height:1.2}
h1 .thin{color:var(--muted);font-weight:500}
/* Context, not copy. */
.head .place{color:var(--ink-2);font-size:12.5px;margin-top:3px;max-width:90ch;line-height:1.45}
.head .aside{margin-left:auto;display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}

/* The window the figures cover, stated rather than offered as a control. Every
   number on the screen is computed over a fixed period, and a selector that
   changed nothing would be a lie with a chevron on it. */
.periodnote{
  display:block;font-size:11.5px;color:var(--ink-2);text-align:right;line-height:1.35;
  padding-right:var(--s2);margin-right:var(--s1);border-right:1px solid var(--line);
}
.periodnote b{font-weight:650;color:var(--ink)}
.periodnote span{display:block;font-size:10.5px;color:var(--muted)}

/* ==========================================================================
   Metrics — a strip, not a row of cards

   One panel divided by hairlines. The figures then read as one set of related
   numbers, which is what they are, instead of six objects that happen to be
   side by side. Rank is carried by type size, not by a bigger box.
   ========================================================================== */
/* The floor is low enough that seven figures still make one row on a laptop.
   Wrapping to a second and third row is how a strip turns back into a wall of
   cards, which is the thing this replaced. */
.kpis{
  display:grid;gap:0;margin-bottom:var(--s5);
  grid-template-columns:repeat(auto-fit,minmax(136px,1fr));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;
}
/* A strip clips its children to its rounded corners, which also clipped the
   explanation bubble: the note is taller than the strip is, and two thirds of
   it was cut off. Only a strip that carries one lets its children out — the
   tiles have no background of their own, so nothing else escapes with it. */
.kpis:has(.whatis){overflow:visible}
.stat{
  position:relative;min-width:0;padding:var(--s3) var(--s4) var(--s4);
  border-left:1px solid var(--line-2);
}
.stat:first-child{border-left:0}
.stat .lbl{
  font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);display:flex;align-items:center;gap:6px;
  flex-wrap:wrap;min-width:0;line-height:1.35;
}
.stat .val{
  font-size:24px;font-weight:650;letter-spacing:-.028em;line-height:1.15;margin-top:6px;
  display:flex;align-items:baseline;gap:6px;
}
.stat .val small{font-size:13px;font-weight:500;color:var(--muted);letter-spacing:0}
.stat .val.good{color:var(--good)}
.stat .val.warn{color:var(--warn)}
.stat .val.bad{color:var(--bad)}
/* A metric with nothing behind it is a state, not a broken card. It says so
   in words at a readable size, rather than showing a greyed-out number. */
.stat .val.none{
  font-size:13.5px;font-weight:600;color:var(--ink-2);letter-spacing:-.005em;
  align-items:center;gap:7px;
}
.stat .val.none::before{
  content:"";width:7px;height:7px;border-radius:2px;flex:0 0 7px;
  background:repeating-linear-gradient(45deg,var(--faint) 0 2px,transparent 2px 4px);
}
.stat .sub{
  font-size:11.5px;color:var(--ink-2);margin-top:4px;
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;line-height:1.4;
}
.stat .spark{margin-top:var(--s2);height:26px;display:block;width:100%}

/* Primary metric. One per strip at most — the point of a lead figure is that
   there is only one. */
/* The lead figure takes two columns. At a seventh of the strip a formatted
   currency total ran past its cell, and shrinking the type to fit would have
   thrown away the one thing that marks it as the lead. */
.stat.hero{background:var(--surface-2);grid-column:span 2}
.stat.hero .val{font-size:32px}
.stat.hero .lbl{color:var(--ink-2)}

/* The severity tint runs along the top of a cell now that cells are not
   separate cards; on the left it fought the divider. */
.stat .rail-tint{position:absolute;left:0;right:0;top:0;height:2px}
.stat .rail-tint.good{background:var(--good)}
.stat .rail-tint.warn{background:var(--warn-mark)}
.stat .rail-tint.bad{background:var(--bad)}

/* Supporting figures: same strip, smaller type, for things that qualify the
   headline rather than compete with it. */
.kpis.support{margin-top:calc(var(--s5) * -1 + 8px)}
.kpis.support .stat{padding:var(--s3) var(--s4)}
.kpis.support .stat .val{font-size:18px;margin-top:3px}
.kpis.support .stat .lbl{font-size:10px}

/* An action offered from inside a data-state — "no target set" is only useful
   next to a way of setting one. */
.stat .fix{
  display:inline-flex;align-items:center;gap:5px;margin-top:7px;
  font-size:11.5px;font-weight:650;color:var(--accent);
}
.stat .fix:hover{text-decoration:underline}

/* delta — direction × whether up is good */
.delta{
  display:inline-flex;align-items:center;gap:3px;font-size:11.5px;font-weight:650;
  padding:1px 6px;border-radius:var(--r-xs);letter-spacing:-.005em;
  /* The arrow and the number are one reading, and in a narrow table column
     they were breaking across two lines. */
  white-space:nowrap;
}
.delta.up{background:var(--good-wash);color:var(--good)}
.delta.down{background:var(--bad-wash);color:var(--bad)}
.delta.flat{background:var(--surface-3);color:var(--ink-2)}

/* ==========================================================================
   Panels — drawn, not floated
   ========================================================================== */
.card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  margin-bottom:var(--s4);overflow:hidden;
}
.card-head{
  padding:10px var(--s4);display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  border-bottom:1px solid var(--line-2);background:var(--surface);min-height:42px;
}
.card-head h2{font-size:13px;font-weight:650;letter-spacing:-.005em;color:var(--ink)}
.card-head .tag{
  margin-left:auto;font-size:11.5px;font-weight:500;color:var(--muted);
  letter-spacing:.01em;display:flex;align-items:center;gap:6px;
}
.card-head .tag:is(a):hover{color:var(--accent)}

/* A control in the head, after the tag.

   The tag takes `margin-left:auto` to sit at the right end, and in a
   wrapping flex row an auto margin eats the whole line's free space — so
   anything after it goes to a second row even when it would have fitted
   beside it. Three items wanting 334px wrapped inside 431px of head, and
   that card's head stood 14px taller than every other one on the page.

   The push-right belongs to whatever is last, so the tag gives it up and
   the control takes it: title and count together on the left, the button
   on the right. */
.card-head > .tag:not(:last-child){margin-left:0}
.card-head > .tag:not(:last-child) + *{margin-left:auto}

/* And a head carrying a control takes its height from that control. Ten
   pixels of padding around a full-size button made that card head fourteen
   taller than the ones above and below it, which on a page of levelled
   cards reads as a mistake rather than as a control. Tighter padding and a
   small button put it back on the 42px every other head stands at.

   display:flex on the <details> as well: inline-block puts it on a line of
   its own, and the line box adds its leading to the button it holds. */
.card-head:has(> details.resolver){padding-top:6px;padding-bottom:6px}
.card-head > details.resolver{display:flex;align-items:center}
.card-body{padding:var(--s4)}
.cols{display:grid;grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);gap:var(--s4);align-items:start}
.cols .col{min-width:0}
/* A pair with only one half present. Keeping the two tracks would indent the
   survivor and leave an empty column beside it, which reads as something
   failing to load rather than as something that is not there. */
.cols.one{grid-template-columns:minmax(0,1fr)}

/* A pair that should read as one row.

   `align-items:start` is right for the queues, where two lists of unrelated
   lengths each stop where they stop. It is wrong for a pair that is one
   statement in two halves: a chart beside three lines of text leaves the
   shorter card looking like an offcut of the taller one. Levelled, the row has
   one top and one bottom, and the white space inside the short card reads as
   room rather than as something missing. */
/* Two halves of equal weight. The 1.55/1 split says one column leads and the
   other qualifies it, which is right for a chart beside a note and wrong for
   two tables asking the same question of different data. */
.cols.half{grid-template-columns:repeat(2,minmax(0,1fr))}
/* Three of equal weight, for three answers to the same question. */
.cols.third{grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:1100px){.cols.third{grid-template-columns:minmax(0,1fr)}}

/* The share bar inside a table row: a column of them is read down, so the bar
   carries the comparison and the number carries the value. */
.grid td .sub{display:block;font-size:11px;color:var(--muted);margin-top:1px}
/* A note that heads a table rather than closing one. */
.notefrom.tight{padding-top:8px;padding-bottom:8px}
.sharecell{min-width:110px}
.sharecell .meterline{gap:8px}
.sharecell .meter{min-width:52px}
.cols.level{align-items:stretch}
.cols.level > .col{display:flex;flex-direction:column}
/* The slack goes to the last card, not shared out among all of them. A column
   of four cards each stretched a quarter is four cards with a gap at the
   bottom; one card taking the difference is a column that ends where its
   neighbour does, which is the point. */
.cols.level > .col > .card:last-child{flex:1}

/* Where that stretched card holds a bounded table, the slack goes into the
   table rather than sitting under whatever closes the card. It was putting
   four hundred pixels of nothing between a card's explanation and its
   bottom edge, with the note floating in the middle of the white space.

   flex-basis:0, not auto, and that is the whole trick. With auto the item
   starts from its content — fifty-seven rows — and grows from there, which
   is how an earlier attempt at this took the cap off and put the page back
   to five thousand pixels. From zero it can only take the room the card
   actually has, which is the room the column next to it decided. The
   min-height is for the reverse case: this column being the taller one
   leaves no slack to take, and a region that shrank to nothing would be a
   table with a scrollbar and no rows. */
.cols.level > .col > .card:last-child:has(> .scroll.tall){display:flex;flex-direction:column}
.cols.level > .col > .card:last-child:has(> .scroll.tall) > .scroll.tall{
  flex:1 1 0;min-height:240px;max-height:none;
}

/* And where the stretched card has nothing in it, the empty state takes the
   room instead of hanging off the top of it. `.empty` already centres what
   it says, so given the height it centres in the card — which is what an
   empty card should look like, rather than a sentence at the top and four
   hundred pixels of nothing under it. No min-height here, deliberately: this
   card is often the short one, and a floor would push the column past the
   neighbour it is being levelled against. */
.cols.level > .col > .card:last-child:has(> .empty){display:flex;flex-direction:column}
.cols.level > .col > .card:last-child:has(> .empty) > .empty{flex:1 1 0}

/* The bar over a bounded list: somewhere to type, and what is being
   counted. Fifty-seven lines is past the point where a reader finds one by
   looking, and the board's list behaviour already searches a table — it
   only ever needed a box pointed at it. */
.stockfind{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.stockfind .select{flex:1 1 220px;min-width:0}
.stocksum-note{flex:0 0 auto;font-size:12px;color:var(--muted)}
.stocksum-note b.bad{color:var(--bad);font-weight:650}

/* ==========================================================================
   A visit check: elements, graded

   One element per block: the name and the grades on a line, the account and
   the photograph under them. The grades read left to right from good to
   unacceptable, which is the order somebody scans for the bad one.
   ========================================================================== */
.element{padding:var(--s4);border-bottom:1px solid var(--line-2)}
.element:last-child{border-bottom:0}
.el-head{display:flex;align-items:flex-start;gap:var(--s4);flex-wrap:wrap}
.el-name{flex:1 1 240px;min-width:0;font-size:13.5px;font-weight:650;color:var(--ink);line-height:1.4}
.el-name i{display:block;font-style:normal;font-size:12px;font-weight:400;color:var(--muted);margin-top:2px}
.el-grades{flex:0 0 auto;display:flex;gap:6px;flex-wrap:wrap}
.el-grade{position:relative;display:flex}
.el-grade input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.el-grade span{
  display:flex;align-items:center;justify-content:center;cursor:pointer;white-space:nowrap;
  min-width:58px;padding:7px 12px;border-radius:var(--r-xs);
  border:1px solid var(--line);background:var(--surface);
  font-size:12.5px;font-weight:650;color:var(--ink-2);
  transition:background .13s ease,border-color .13s ease,color .13s ease;
}
.el-grade span:hover{border-color:var(--accent);color:var(--ink)}
/* Each grade in its own colour, and only when chosen — an ungraded element
   is not a row of four verdicts going spare. */
.g-good input:checked + span{background:var(--good);border-color:var(--good);color:#fff}
.g-ok   input:checked + span{background:var(--accent);border-color:var(--accent);color:#fff}
.g-poor input:checked + span{background:var(--warn-mark);border-color:var(--warn-mark);color:#3A2A08}
.g-bad  input:checked + span{background:var(--bad);border-color:var(--bad);color:#fff}
.g-na   input:checked + span{background:var(--surface-3);border-color:var(--line);color:var(--ink-2)}
.el-grade input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}

/* The account and the picture, under the thing they are about. Quiet until
   the element is marked down, because most elements are fine and a form that
   shouts at every row is a form nobody reads. */
.el-more{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--s4);margin-top:var(--s3)}
@media (max-width:760px){.el-more{grid-template-columns:minmax(0,1fr)}}
.el-more textarea{min-height:0}
.element.marked{background:var(--warn-wash)}
.element.marked .el-more textarea{border-color:var(--warn-mark)}
.el-shot .fieldlabel{display:block;font-size:11.5px;font-weight:650;color:var(--ink-2);margin-bottom:5px}
.el-has{display:block;font-size:11.5px;color:var(--muted);margin-top:5px;line-height:1.4}

/* A number typed into a table of lines nobody is obliged to count. Narrow,
   because a wide empty box in every row reads as a form demanding to be
   filled in — and most of these rows are meant to stay blank. */
.countbox{width:84px;text-align:right}

[data-theme="dark"] .element.marked{background:rgba(229,180,92,.08)}
[data-theme="dark"] .g-poor input:checked + span{color:#241A05}

/* ==========================================================================
   A round: six judgements, answered three ways

   One row per point, the question on the left and the answer on the right,
   because that is the order somebody reads it in while standing up. Radios
   wearing chips — the input stays in the tab order and submits natively, so
   the screen works with nothing running.
   ========================================================================== */
.visitpoints{display:grid}
.visitpoint{
  display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  padding:12px var(--s4);border-bottom:1px solid var(--line-2);
}
.visitpoint:last-child{border-bottom:0}
.vp-name{flex:1 1 220px;min-width:0;font-size:13.5px;color:var(--ink);line-height:1.4}
.vp-answers{flex:0 0 auto;display:flex;gap:6px}
.vp-answer{position:relative;display:flex}
.vp-answer input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.vp-answer span{
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  min-width:62px;padding:7px 12px;border-radius:var(--r-xs);
  border:1px solid var(--line);background:var(--surface);
  font-size:12.5px;font-weight:650;color:var(--ink-2);
  transition:background .13s ease,border-color .13s ease,color .13s ease;
}
.vp-answer span:hover{border-color:var(--accent);color:var(--ink)}
/* Each answer in its own colour, because the three are not degrees of one
   thing: yes is well, no is a fault, and N/A is the absence of a question.
   Painted only when chosen — an unanswered row is not a row of three
   verdicts going spare. */
.vp-yes input:checked + span{background:var(--good);border-color:var(--good);color:#fff}
.vp-no  input:checked + span{background:var(--bad);border-color:var(--bad);color:#fff}
.vp-na  input:checked + span{background:var(--surface-3);border-color:var(--line);color:var(--ink-2)}
.vp-answer input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}

@media (max-width:640px){
  .visitpoint{align-items:flex-start}
  .vp-answers{width:100%}
  .vp-answer{flex:1}
}

/* A bare section title, for content that does not need a container around it. */
.sectionhead{
  display:flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap;
  margin:var(--s6) 0 var(--s3);
}
.sectionhead h2{font-size:13px;font-weight:650;letter-spacing:-.005em}
.sectionhead .tag{margin-left:auto;font-size:11.5px;color:var(--muted)}

/* ==========================================================================
   Charts — left as they were. They were already the strongest thing here;
   the rest of the interface has been brought up to them, not the reverse.
   ========================================================================== */
.chart{display:block;width:100%;overflow:visible}
.chart .grid-line{stroke:var(--line);stroke-width:1}
.chart .axis{fill:var(--muted);font-size:10.5px;font-weight:500}
.chart .line{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.chart .area{opacity:.1}
.chart .dot{stroke:var(--surface);stroke-width:2}
.chart .col-mark{rx:3}
.chart .target-line{stroke:var(--muted);stroke-width:1;stroke-dasharray:none;opacity:.55}
.chart .end-label{font-size:11.5px;font-weight:650;fill:var(--ink)}
.chart .hit{fill:transparent;cursor:pointer}
.chart .hit:hover ~ .hover-band,.chart .band-on{fill:var(--surface-3)}

.smalls{
  display:grid;gap:var(--s4);padding:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
}
.small{min-width:0}
.small .lbl{font-size:11.5px;font-weight:650;color:var(--ink-2);display:flex;align-items:baseline;gap:8px}
.small .lbl b{margin-left:auto;font-size:14px;font-weight:650;color:var(--ink);letter-spacing:-.01em}
.small .foot{font-size:10.5px;color:var(--muted);margin-top:4px;display:flex;justify-content:space-between}

.legend{
  display:flex;gap:var(--s4);flex-wrap:wrap;align-items:center;
  padding:0 var(--s4) var(--s3);font-size:12px;color:var(--ink-2);
}
.legend span{display:flex;align-items:center;gap:6px}
.legend i{width:9px;height:9px;border-radius:3px;display:inline-block;flex:0 0 9px}

/* tooltip */
.tip{
  position:fixed;z-index:60;pointer-events:none;opacity:0;transform:translateY(3px);
  transition:opacity .12s ease,transform .12s ease;
  background:var(--brand);color:#fff;border-radius:var(--r-xs);padding:8px 11px;
  font-size:12px;line-height:1.45;box-shadow:var(--sh-modal);max-width:260px;
}
.tip.on{opacity:1;transform:translateY(0)}
.tip b{display:block;font-size:11.5px;font-weight:600;color:#AFBCD1;margin-bottom:3px}
.tip .row{display:flex;align-items:center;gap:7px;white-space:nowrap}
.tip .row i{width:8px;height:8px;border-radius:2px;flex:0 0 8px}
.tip .row em{font-style:normal;margin-left:auto;font-weight:650;padding-left:14px}

/* ==========================================================================
   Meters — fill carries severity, track is the same hue lightened
   ========================================================================== */
.meter{display:block;height:6px;border-radius:var(--pill);background:var(--surface-3);overflow:hidden;min-width:60px}
.meter span{display:block;height:100%;border-radius:var(--pill);background:var(--accent);transition:width .3s ease}
.meter span.good{background:var(--good)}
.meter span.warn{background:var(--warn-mark)}
.meter span.bad{background:var(--bad)}
/* A share of a total is not a score: it gets the plain accent, not a verdict. */
.meter span.neutral{background:var(--accent)}
.meter.tall{height:8px}
.meterline{display:flex;align-items:center;gap:var(--s2)}
.meterline .num{
  font-weight:650;font-size:13px;letter-spacing:-.01em;min-width:44px;text-align:right;
}

/* ring gauge */
.ring{display:block}
.ring .track{fill:none;stroke:var(--surface-3);stroke-width:7}
.ring .fill{fill:none;stroke-width:7;stroke-linecap:round;transform:rotate(-90deg);transform-origin:center}
.ring .fill.good{stroke:var(--good)}
.ring .fill.warn{stroke:var(--warn-mark)}
.ring .fill.bad{stroke:var(--bad)}
.ringwrap{display:flex;align-items:center;gap:var(--s3)}
.ringwrap .mid{font-size:14px;font-weight:650;letter-spacing:-.02em}

/* ==========================================================================
   Tables — an application's table: dense rows, sticky head, a hover you can
   track along, and numbers that line up.
   ========================================================================== */
.scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;position:relative}

/* Long tables get a bounded region of their own.
   This is what makes the sticky header actually stick: `overflow-x:auto`
   forces `overflow-y` to compute to `auto` as well, so .scroll — not the page
   — is the scrolling box for anything inside it. Without a height it never
   scrolls vertically, and a `position:sticky` header has nothing to stick to.
   Capping the height gives it one, and turns a 200-row table from a page you
   scroll past into a region you work in. */
.scroll.tall{max-height:min(66vh,620px);overflow-y:auto}
/* Given tabindex="0" in the markup so the arrow keys can reach it: this is
   the scrolling box, and a region somebody can see but cannot scroll without
   a mouse is a region they cannot read. */
.scroll:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.grid{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.grid th{
  position:sticky;top:0;z-index:3;
  text-align:left;padding:8px var(--s4);white-space:nowrap;
  font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  background:var(--surface-2);border-bottom:1px solid var(--line);
}
.grid th.num{text-align:right}
.grid td{padding:9px var(--s4);border-bottom:1px solid var(--line-2);vertical-align:middle}
.grid tbody tr:last-child td{border-bottom:0}
.grid tbody tr{transition:background .1s ease}
.grid tbody tr:hover{background:var(--surface-2)}
.grid .num{text-align:right}
.grid .sub{display:block;font-size:11.5px;color:var(--muted);font-weight:400;letter-spacing:0;margin-top:1px}
.grid .sub.bad{color:var(--bad)}
.grid .sub.note{font-style:normal;max-width:230px;color:var(--muted)}
.grid .empty{text-align:center;color:var(--muted);padding:var(--s7) var(--s4);font-size:13px}
.grid.tight td{padding:7px var(--s4)}
.grid.tight th{padding:7px var(--s4)}
.grid tr.late td{background:var(--bad-wash)}
.grid tr.late:hover td{background:#FAE4E1}
.grid tr.off td{opacity:.55}

/* Sort affordance lives in the markup as .sortmark, so a header can carry
   a real <button> around it — see th.sortable below. There used to be a CSS
   pseudo-element as well, permanently stuck on "unsorted", so every sortable
   column rendered two arrows disagreeing with each other. */

/* Row-level actions stay quiet until the row is under the cursor, so a table
   of forty rows is not also a wall of forty buttons. */
.grid td.actions{width:1%;white-space:nowrap;text-align:right}
.grid tbody tr .rowact{opacity:.55;transition:opacity .12s ease}
.grid tbody tr:hover .rowact,.grid tbody tr:focus-within .rowact{opacity:1}

.shopname{font-weight:600;letter-spacing:-.01em;color:var(--ink);display:inline-flex;align-items:center;gap:7px}
.shopname:hover{color:var(--accent)}
.chip-code{
  background:var(--surface-3);border-radius:var(--r-xs);padding:1px 6px;
  font-size:11.5px;font-weight:700;letter-spacing:.02em;font-variant-numeric:tabular-nums;
  color:var(--ink-2);
}
.shopname:hover .chip-code{background:var(--accent-wash);color:var(--accent)}

.good{color:var(--good)}
.warn{color:var(--warn)}
.bad{color:var(--bad)}
.none{color:var(--faint)}
.pct{font-weight:650;font-size:13.5px;letter-spacing:-.01em}
.when{font-size:12px;white-space:nowrap}
.go a{font-size:12px;font-weight:650;color:var(--muted)}
.go a:hover{color:var(--accent)}

/* ==========================================================================
   Pills, chips, badges — a status, not a sticker collection
   ========================================================================== */
.pill{
  display:inline-flex;align-items:center;gap:5px;
  font-size:11px;font-weight:650;letter-spacing:.01em;
  padding:2px 8px;border-radius:var(--r-xs);
  background:var(--surface-3);color:var(--ink-2);white-space:nowrap;
}
.pill::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.8}
.pill.plain{background:none;padding:2px 0;color:var(--muted);font-weight:500}
.pill.plain::before{display:none}
.pill.good{background:var(--good-wash);color:var(--good)}
.pill.warn{background:var(--warn-wash);color:var(--warn)}
.pill.bad{background:var(--bad-wash);color:var(--bad)}
.pill.info{background:var(--accent-wash);color:var(--accent-ink)}

/* Severity label. Fixed width tone across the app so CRITICAL always looks
   like CRITICAL, wherever it turns up. */
.sev{
  font-size:9.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  padding:2px 6px;border-radius:var(--r-xs);flex:0 0 auto;
  background:var(--surface-3);color:var(--ink-2);
}
.sev.critical{background:var(--bad-wash);color:var(--bad)}
.sev.high{background:#FCEFE7;color:var(--serious)}
.sev.medium{background:var(--warn-wash);color:var(--warn)}
.sev.low{background:var(--surface-3);color:var(--muted)}
.sev.opp{background:#E6F6F1;color:var(--series-3)}

.tagdot{
  font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  background:var(--warn-wash);color:var(--warn);border-radius:var(--r-xs);padding:1px 5px;cursor:help;
}

/* ==========================================================================
   Alerts & notices
   ========================================================================== */
.flash{
  display:flex;align-items:center;gap:9px;
  background:var(--good-wash);color:#14503C;border-radius:var(--r-sm);
  border:1px solid #CDE7DC;
  padding:9px var(--s3);font-size:13px;margin-bottom:var(--s4);
}
.flash::before{content:"✓";font-weight:700;line-height:1.4}
.flash.bad{background:var(--bad-wash);color:#7E241C;border-color:#F0D2CE}
.flash.bad::before{content:"!"}
.alerts{display:grid;gap:var(--s2);margin-bottom:var(--s4)}
.alert{
  background:var(--warn-wash);border-radius:var(--r-sm);padding:10px var(--s3);font-size:13px;
  color:#7A5A22;border:1px solid #F2E2C2;border-left:3px solid var(--warn-mark);
}
.alert.bad{background:var(--bad-wash);color:#7E241C;border-color:#F0D2CE;border-left-color:var(--bad)}
.alert b{display:block;font-weight:650;margin-bottom:1px}
.notice{
  background:var(--warn-wash);padding:9px var(--s4);font-size:12.5px;color:#7A5A22;
  border-bottom:1px solid var(--line-2);
}
/* A note about the data, at the foot of the thing it is about. Quiet, because
   it is provenance rather than news. */
.notefrom{
  padding:10px var(--s4);font-size:12px;color:var(--muted);background:var(--surface-2);
  border-top:1px solid var(--line-2);line-height:1.5;
}
.notefrom b{color:var(--ink-2);font-weight:600}
.footnote{font-size:12px;color:var(--muted);max-width:82ch;margin:var(--s1) 2px 0;line-height:1.55}

/* ==========================================================================
   Empty, loading and error states

   Previously `.empty` was styled only inside a table, so a bare one rendered
   as unstyled text in a white void — which is exactly what a blank area is.
   ========================================================================== */
.empty{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  padding:var(--s7) var(--s5);text-align:center;color:var(--ink-2);font-size:13px;
  line-height:1.55;
}
.empty::before{
  content:"";width:22px;height:22px;border-radius:var(--r-xs);margin-bottom:2px;
  background:repeating-linear-gradient(45deg,var(--line) 0 3px,transparent 3px 6px);
  border:1px solid var(--line);
}
.empty b{display:block;font-size:13.5px;font-weight:650;color:var(--ink)}
.empty span{display:block;color:var(--muted);font-size:12.5px;max-width:52ch}
.empty .btn{margin-top:var(--s2)}
.empty.bare{padding:var(--s5)}
.empty.bare::before{display:none}

/* Skeletons, for anything fetched after paint. Deliberately colourless: a
   shimmering grey block is a wait, a coloured one looks like data. */
.skel{
  background:linear-gradient(90deg,var(--surface-3) 25%,var(--line-2) 37%,var(--surface-3) 63%);
  background-size:400% 100%;animation:skel 1.3s ease-in-out infinite;
  border-radius:var(--r-xs);height:12px;display:block;
}
.skel + .skel{margin-top:7px}
.skel.w60{width:60%}
.skel.w40{width:40%}
.skel.tall{height:26px}
@keyframes skel{0%{background-position:100% 50%}100%{background-position:0 50%}}


/* ==========================================================================
   Feeds & timelines
   ========================================================================== */
.feed{list-style:none}
.feed li{
  padding:9px var(--s4);border-bottom:1px solid var(--line-2);
  display:flex;gap:var(--s3);align-items:flex-start;transition:background .1s ease;
}
.feed li:last-child{border-bottom:0}
.feed li:hover{background:var(--surface-2)}
.feed li.empty{
  color:var(--muted);font-size:13px;justify-content:center;padding:var(--s6);
  display:flex;text-align:center;
}
.feed li.empty:hover{background:none}
.tick{font-size:11px;color:var(--muted);padding-top:2px;white-space:nowrap;min-width:50px}
.what{font-size:13px;line-height:1.45;min-width:0;flex:1}
.what b{font-weight:600}
.what span{display:block;color:var(--muted);font-size:12px;margin-top:1px}
.dot{width:7px;height:7px;border-radius:50%;margin-top:6px;flex:0 0 7px}
.dot.red{background:var(--bad);box-shadow:0 0 0 3px var(--bad-wash)}
.dot.amber{background:var(--warn-mark);box-shadow:0 0 0 3px var(--warn-wash)}
.dot.green{background:var(--good);box-shadow:0 0 0 3px var(--good-wash)}
.thumblink{margin-left:auto;font-size:11.5px;font-weight:650;color:var(--accent);align-self:center}

/* ==========================================================================
   Photos
   ========================================================================== */
.shots{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--s3);padding:var(--s4)}
.shots img{
  width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--r-sm);
  border:1px solid var(--line);transition:transform .16s ease,box-shadow .16s ease;
}
.shots a:hover img{transform:translateY(-2px);box-shadow:var(--sh-pop)}
.posmrow{display:flex;gap:7px;flex-wrap:wrap;padding:var(--s3) var(--s4);border-top:1px solid var(--line-2)}

/* ==========================================================================
   Buttons — a hierarchy, so the important action on a screen is obvious

   .btn          secondary, the everyday control (bordered, white)
   .btn.primary  the one main action
   .btn.ghost    tertiary, low priority
   .btn.danger   destructive
   ========================================================================== */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  font-family:inherit;font-size:12.5px;font-weight:650;letter-spacing:-.005em;
  padding:7px 13px;border-radius:var(--r-xs);
  background:var(--surface);color:var(--ink);border:1px solid var(--line);
  cursor:pointer;white-space:nowrap;
  transition:background .13s ease,border-color .13s ease,color .13s ease;
}
.btn:hover{background:var(--surface-2);border-color:#CFD9E7}
.btn:active{transform:translateY(.5px)}

.btn.primary,.btn.accent{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn.primary:hover,.btn.accent:hover{background:var(--accent-ink);border-color:var(--accent-ink)}

/* Kept because templates use it, but it is now the same weight as primary
   rather than a second competing dark button. */
.btn.dark{background:var(--brand);color:#fff;border-color:var(--brand)}
.btn.dark:hover{background:var(--brand-2);border-color:var(--brand-2)}

.btn.ghost{background:none;color:var(--ink-2);border-color:transparent}
.btn.ghost:hover{background:var(--surface-3);color:var(--ink);border-color:transparent}

.btn.danger{background:var(--surface);color:var(--bad);border-color:#F0D2CE}
.btn.danger:hover{background:var(--bad);color:#fff;border-color:var(--bad)}

.btn.small{padding:5px 10px;font-size:11.5px}
.btn[disabled],.btn.off{opacity:.5;pointer-events:none}

/* ==========================================================================
   Filters & controls
   ========================================================================== */
.filters{display:flex;gap:2px;background:var(--surface-3);padding:2px;border-radius:var(--r-xs)}
.filters a{
  font-size:12px;font-weight:650;padding:5px 11px;border-radius:4px;color:var(--ink-2);
  transition:background .13s ease,color .13s ease;display:inline-flex;align-items:center;gap:5px;
}
.filters a:hover{color:var(--ink)}
.filters a.on{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(15,27,50,.07)}
.filters a .n{color:var(--muted);font-weight:600;font-variant-numeric:tabular-nums}
.filters a.on .n{color:var(--accent)}

.range{display:flex;gap:2px;background:var(--surface-3);padding:2px;border-radius:var(--r-xs)}
.range a{font-size:11.5px;font-weight:650;padding:4px 10px;border-radius:4px;color:var(--ink-2)}
.range a.on{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(15,27,50,.07)}

select,input[type=text],input[type=email],input[type=date],input[type=number],
input[type=search],input[type=file],textarea{
  font-family:inherit;font-size:13px;color:var(--ink);background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-xs);padding:7px 10px;width:100%;
  transition:border-color .13s ease,box-shadow .13s ease;
}
select:hover,input:hover{border-color:#CFD9E7}
select:focus,input:focus,textarea:focus{
  outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash);
}
select{cursor:pointer;appearance:none;padding-right:28px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='%237B8BA4' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 9px center;
}
textarea{min-height:78px;resize:vertical}
.shopfilter{margin-left:auto;min-width:180px}
.filterbar{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center}
.filterbar select{width:auto;min-width:140px}
.daterange{display:flex;gap:var(--s2);align-items:center}
.daterange input{width:auto}

/* Active filters, stated plainly with a way out of each. A filter you cannot
   see is a filter you will forget you set. */
.chipbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:11.5px;color:var(--muted)}
.chipbar .fchip{
  display:inline-flex;align-items:center;gap:6px;background:var(--accent-wash);
  color:var(--accent-ink);border-radius:var(--r-xs);padding:2px 4px 2px 8px;font-weight:600;
}
.chipbar .fchip a{
  display:grid;place-items:center;width:15px;height:15px;border-radius:3px;
  color:var(--accent-ink);opacity:.65;font-size:13px;line-height:1;
}
.chipbar .fchip a:hover{background:rgba(45,107,196,.14);opacity:1}
.chipbar .reset{font-weight:650;color:var(--muted)}
.chipbar .reset:hover{color:var(--bad);text-decoration:underline}

/* forms */
.fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:var(--s4);padding:var(--s4)}
.fields.stacked{grid-template-columns:1fr}
.fields label{display:block;min-width:0}
.fields label>span{display:block;font-size:11.5px;font-weight:650;color:var(--ink-2);margin-bottom:5px}
.fields .checkline{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--ink-2);font-weight:500}
.fields .checkline input{width:auto;accent-color:var(--accent)}
.fields .btn{justify-self:start}
.formactions{display:flex;gap:var(--s2);align-items:center;padding:0 var(--s4) var(--s4);flex-wrap:wrap}
.formactions form{display:inline-block}
.inline{display:inline-block}
.linkish{
  background:none;border:0;color:var(--muted);cursor:pointer;font-family:inherit;
  font-size:11.5px;font-weight:500;text-decoration:underline;padding:0 3px;
}
.linkish:hover{color:var(--bad)}
.mono{font-size:12.5px;color:var(--ink-2)}
code{
  font-family:var(--mono);font-size:12px;
  background:var(--surface-3);padding:1px 5px;border-radius:4px;
}

/* approve / close boxes */
.steps{padding:0}
.approvebox{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4);padding:var(--s4)}
.approvebox form{display:flex;flex-direction:column;gap:var(--s2)}
.approvebox label,.closebox label{font-size:11.5px;font-weight:650;color:var(--ink-2)}
.approvebox .btn{align-self:flex-start}
.closebox{display:flex;align-items:flex-end;gap:var(--s3);padding:var(--s4);flex-wrap:wrap}
.closebox input{width:160px}
.closebox label{display:block;margin-bottom:5px}

/* ==========================================================================
   Dialog

   A centred dialog rather than a popover hung off the button. Both the panel
   and the horizontal table scroller are clipping contexts, so an absolutely
   positioned panel gets cut off at their edges. position:fixed escapes both,
   and the middle of the screen is where a form asking for a sentence belongs.
   ========================================================================== */
.resolver{display:inline-block}
.resolver summary{list-style:none;cursor:pointer;display:inline-flex}
.resolver summary::-webkit-details-marker{display:none}
.resolver[open]::before{
  content:"";position:fixed;inset:0;z-index:40;
  background:rgba(15,27,50,.45);
}
.resolver form{
  position:fixed;z-index:50;top:50%;left:50%;transform:translate(-50%,-50%);
  width:min(512px,calc(100vw - 32px));max-height:calc(100vh - 48px);overflow-y:auto;
  background:var(--surface);border-radius:var(--r);padding:var(--s5);
  box-shadow:var(--sh-modal);
  display:grid !important;gap:var(--s2);
  /* The dialog sits in the DOM inside a table cell but is not part of it, so
     it has to opt out of what that cell inherits down. td.actions sets nowrap
     and right-alignment to keep its little buttons in a row; inherited into
     here, nowrap stopped every chip label from wrapping and clipped it. */
  text-align:left;
  white-space:normal;
}
.resolver form h3{font-size:15px;font-weight:650;letter-spacing:-.01em;margin-bottom:2px}
.resolver form h3 span{display:block;font-size:12.5px;font-weight:400;color:var(--ink-2);margin-top:3px}
.resolver label{display:block;font-size:11.5px;font-weight:650;color:var(--ink-2)}
.resolver label.checkline{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:500}
.resolver label.checkline input{width:auto;accent-color:var(--accent)}
.resolver textarea{min-height:90px}
/* Why a submission came back, stated inside the panel that came back.
   The page's own flash line sits at the top of the document, behind this
   dialog's backdrop, and an error on a field the dialog is hiding is
   nowhere at all. */
.resolver .dialogerror{
  display:flex;align-items:center;gap:8px;
  background:var(--bad-wash);color:#7E241C;border:1px solid #F0D2CE;
  border-radius:var(--r-sm);padding:9px var(--s3);font-size:12.5px;line-height:1.45;
}
.resolver .dialogerror::before{content:"!";font-weight:700}
[data-theme="dark"] .resolver .dialogerror{color:var(--bad);border-color:#53241F}
[data-theme="dark"] .resolver .whenchip{background:var(--surface-2);border-color:var(--line);color:var(--ink-2)}
/* After the line above, never before it: the two carry the same specificity,
   and this is exactly how dark mode lost the chosen state on every chip in
   the field app. */
[data-theme="dark"] .resolver .whenchip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#0A101B}
/* Two short answers on one line — a date and an urgency are each four
   characters wide, and stacked they push the buttons off the fold.

   170px, not 150: at 150 three tracks fitted the dialog and the pair sat in
   two of them with an empty one beside, which is only invisible because
   auto-fit collapses it. Two is what this means.

   And the stacking margin comes off. `.fieldwrap + .fieldwrap` spaces fields
   that follow one another down a column; side by side it dropped the second
   one twelve pixels, so the two labels sat on different lines and the pair
   read as a mistake. The groupform has carried the same correction since it
   was written — this grid simply never got it. */
.resolver .duo{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:var(--s2)}
.resolver .duo > .fieldwrap + .fieldwrap{margin-top:0}

/* When it is wanted by, in the words somebody would say it in. A row of
   small pills rather than chips the width of the dialog: these are five
   short answers to one question, not five jobs to choose between. */
.resolver .whenchips{display:flex;flex-wrap:wrap;gap:6px}
.resolver .whenchip{
  font-family:inherit;font-size:11.5px;font-weight:650;letter-spacing:-.005em;
  padding:6px 11px;border-radius:999px;white-space:nowrap;cursor:pointer;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
  transition:background .13s ease,border-color .13s ease,color .13s ease;
}
.resolver .whenchip:hover{border-color:var(--accent);color:var(--ink)}
.resolver .whenchip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
/* The sentence a chip cannot say for you. Shown only by "Something else",
   and stated here rather than left to the user-agent sheet: the dialog sets
   `display:grid !important` on the form, and a grid item is exactly the kind
   of child that has had a display of its own forced on it before. */
.freetitle[hidden]{display:none}
.resolver .row{display:flex;gap:var(--s2);justify-content:flex-end;margin-top:var(--s1)}
.resolver .row .btn{flex:0 0 auto}

/* Quick choices. Radios wearing chips — the input itself is hidden from sight
   but not from the keyboard, so tab and arrow keys behave as they should. */
.picks{border:0;padding:0;min-width:0}
.picks legend{font-size:11.5px;font-weight:650;color:var(--ink-2);padding:0;margin-bottom:7px}
/* Two across, so seven choices are one glance rather than a scroll. Grid rows
   equalise, so a label that wraps does not leave its neighbour short. */
.pickgrid{display:grid;gap:6px;grid-template-columns:repeat(auto-fit,minmax(224px,1fr))}
/* Flex, so the chip fills the grid row: a two-line label beside a one-line one
   would otherwise leave its neighbour short and the row looking ragged.
   Scoped to .pickgrid so it outranks the plain `.resolver label` above — a
   chip is a <label>, and that rule would otherwise make it a block again. */
.pickgrid .pick{position:relative;display:flex;font-weight:500}
.pick input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.pick span{
  flex:1;display:flex;align-items:center;gap:8px;cursor:pointer;
  padding:9px 12px;border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface);font-size:13px;color:var(--ink);line-height:1.3;
  transition:border-color .13s ease,background .13s ease;
}
.pick span::before{
  content:"";width:14px;height:14px;flex:0 0 14px;border-radius:50%;
  border:1.5px solid #C3CDDD;transition:border-color .13s ease,border-width .13s ease;
}
.pick span:hover{border-color:#C3CDDD;background:var(--surface-2)}
.pick input:checked + span{border-color:var(--accent);background:var(--accent-wash);font-weight:650}
.pick input:checked + span::before{border:5px solid var(--accent)}
.pick input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}

/* ==========================================================================
   Operational queue — the core pattern of this application

   Severity, where, what, the number, how old, what to do. Six things, one
   line each where possible, so ten of them can be read in the time the old
   paragraph cards took for two. The reasoning is not deleted — it moves
   behind a disclosure, because it is needed when deciding, not when scanning.
   ========================================================================== */
.queue{display:block}
.qrow{
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto;
  align-items:center;gap:var(--s3);
  padding:9px var(--s4);border-bottom:1px solid var(--line-2);
  border-left:2px solid transparent;
  transition:background .1s ease;
}
.qrow:last-of-type{border-bottom:0}
.qrow:hover{background:var(--surface-2)}
.qrow.critical{border-left-color:var(--bad)}
.qrow.high{border-left-color:var(--serious)}
.qrow.medium{border-left-color:var(--warn-mark)}
.qrow.low{border-left-color:var(--line)}
.qrow.opp{border-left-color:var(--series-3)}

.q-sev{flex:0 0 auto}
/* Where and what, stacked. The place is the anchor — you look for the code
   first and read the sentence second. */
.q-what{min-width:0}
.q-where{
  display:flex;align-items:center;gap:7px;font-size:11px;color:var(--muted);
  margin-bottom:1px;
}
.q-title{
  font-size:13px;font-weight:600;color:var(--ink);letter-spacing:-.005em;
  line-height:1.35;display:block;
}
a.q-title:hover{color:var(--accent)}

/* The figure that makes the case. Dominant, because it is the argument. */
.q-fig{text-align:right;flex:0 0 auto;min-width:74px}
.q-fig b{
  display:block;font-size:16px;font-weight:650;letter-spacing:-.025em;
  line-height:1.15;color:var(--ink);
}
.q-fig span{display:block;font-size:10px;color:var(--muted);letter-spacing:.04em;text-transform:uppercase;margin-top:1px}
.qrow.critical .q-fig b{color:var(--bad)}
.qrow.high .q-fig b{color:var(--serious)}
.qrow.opp .q-fig b{color:var(--series-3)}

.q-act{display:flex;align-items:center;gap:6px;flex:0 0 auto}
/* Row controls fade up on hover, so a long queue is a list of problems rather
   than a grid of buttons — but they stay reachable by keyboard at all times. */
.q-act .btn{opacity:.62;transition:opacity .12s ease}
.qrow:hover .q-act .btn,.qrow:focus-within .q-act .btn{opacity:1}

/* Disclosure: the recommended action and the reasoning, on request. */
.qrow .q-more{
  background:none;border:0;cursor:pointer;color:var(--muted);font-family:inherit;
  font-size:15px;line-height:1;padding:4px 6px;border-radius:var(--r-xs);
}
.qrow .q-more:hover{background:var(--surface-3);color:var(--ink)}
.qdetail{
  padding:0 var(--s4) var(--s3) calc(var(--s4) + 2px);
  border-bottom:1px solid var(--line-2);background:var(--surface-2);
}
.qdetail .why{font-size:12.5px;color:var(--ink-2);line-height:1.55;padding-top:var(--s3);max-width:92ch}
.qdetail .detected{font-size:12.5px;color:var(--ink);line-height:1.55;padding-top:var(--s3);max-width:92ch}
.qdetail .rec{
  display:flex;align-items:flex-start;gap:7px;margin-top:var(--s2);
  font-size:12.5px;font-weight:650;color:var(--ink);
}
.qdetail .rec::before{content:"→";color:var(--accent);font-weight:700;flex:0 0 auto}
.qdetail .metrics{display:flex;gap:var(--s5);flex-wrap:wrap;margin-top:var(--s3)}
.qdetail .metrics span{display:flex;flex-direction:column;font-size:10.5px;color:var(--muted);letter-spacing:.03em;text-transform:uppercase}
.qdetail .metrics b{font-size:15px;font-weight:650;color:var(--ink);letter-spacing:-.02em;text-transform:none;letter-spacing:-.02em}

.qitem:last-child .qrow{border-bottom:0}
.qitem.open .qrow{background:var(--surface-2);border-bottom-color:transparent}
.qitem.open .q-more{transform:rotate(180deg);color:var(--ink)}
.q-more svg{display:block}

/* ---------- opportunity rows ---------- */
/* Same skeleton, different argument: the size of the prize leads, and the
   reason follows it. Visually distinct from the attention queue so the two
   are never confused at a glance. */
.qrow.opp .q-where{color:var(--series-3);font-weight:650}
.opps .qrow{grid-template-columns:minmax(0,1fr) auto auto}
.opps .q-fig b{font-size:17px}

/* ==========================================================================
   Status summary — where things stand
   Two columns, small indicators, no container per item.
   ========================================================================== */
.story .storycols{display:grid;grid-template-columns:1fr 1fr;gap:0}
.cols .col .story .storycols{grid-template-columns:1fr}
.cols .col .story .storycols>div+div{border-left:0;border-top:1px solid var(--line-2)}
.story .storycols>div{padding:var(--s3) var(--s4) var(--s4)}
.story .storycols>div+div{border-left:1px solid var(--line-2)}
.story h3{
  font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  margin-bottom:var(--s2);display:flex;align-items:center;gap:6px;color:var(--muted);
}
.story h3::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--line);flex:0 0 6px}
.story h3.good::before{background:var(--good)}
.story h3.bad::before{background:var(--warn-mark)}
.story ul{list-style:none;display:grid;gap:5px}
.story li{
  font-size:12.5px;color:var(--ink);padding-left:14px;position:relative;line-height:1.45;
}
.story li::before{
  content:"";position:absolute;left:0;top:6px;width:5px;height:5px;border-radius:50%;
  background:var(--line);
}
.story h3.good + ul li::before{background:var(--good)}
.story h3.bad + ul li::before{background:var(--warn-mark)}
.story li.none{color:var(--muted)}
.story li.none::before{background:var(--line)}

/* ==========================================================================
   Location health
   ========================================================================== */
.healthgrid{
  display:grid;gap:0;
  grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
}
/* Cells in a grid, divided by hairlines — not a scatter of little cards. */
.healthcard{
  display:block;padding:var(--s3) var(--s4) var(--s4);
  border-left:1px solid var(--line-2);border-top:1px solid var(--line-2);
  transition:background .13s ease;
}
.healthcard:hover{background:var(--surface-2)}
.hc-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s2)}
.hc-score{font-size:22px;font-weight:650;letter-spacing:-.03em;line-height:1}
.healthcard.good .hc-score{color:var(--good)}
.healthcard.watch .hc-score{color:var(--warn)}
.healthcard.poor .hc-score{color:var(--bad)}
.healthcard.unknown .hc-score{color:var(--faint)}
.hc-name{font-size:13px;font-weight:600;margin-top:7px;letter-spacing:-.01em}
.healthcard:hover .hc-name{color:var(--accent)}
.hc-place{font-size:11.5px;color:var(--muted);margin-top:1px}
.hc-bars{display:flex;align-items:flex-end;gap:3px;height:24px;margin-top:var(--s3)}
.hc-bar{
  flex:1;height:100%;background:var(--surface-3);border-radius:2px;
  display:flex;align-items:flex-end;overflow:hidden;cursor:help;
}
.hc-bar>span{width:100%;background:var(--accent);border-radius:2px;min-height:2px}
.healthcard.good .hc-bar>span{background:var(--good)}
.healthcard.watch .hc-bar>span{background:var(--warn-mark)}
.healthcard.poor .hc-bar>span{background:var(--bad)}
.hc-bar>span.blank{
  background:repeating-linear-gradient(45deg,var(--line) 0 3px,transparent 3px 6px);
  height:100%!important;
}
.hc-why{font-size:11px;color:var(--muted);margin-top:var(--s2);line-height:1.4}

.moreline{
  display:flex;align-items:center;gap:6px;padding:9px var(--s4);
  font-size:12px;font-weight:650;color:var(--accent);
  border-top:1px solid var(--line-2);background:var(--surface-2);
}
.moreline::after{content:"→"}
.moreline:hover{background:var(--accent-wash)}
.moreline.plain{color:var(--muted);font-weight:500;cursor:default}
.moreline.plain::after{display:none}
.moreline.plain:hover{background:var(--surface-2)}

/* ==========================================================================
   Location 360
   ========================================================================== */
.healthstrip{display:grid;grid-template-columns:168px minmax(0,1fr);align-items:stretch}
.hs-score{
  display:flex;flex-direction:column;justify-content:center;gap:2px;
  padding:var(--s4) var(--s5);background:var(--surface-2);border-right:1px solid var(--line-2);
}
.hs-num{font-size:42px;font-weight:650;letter-spacing:-.04em;line-height:1}
.healthstrip.good .hs-num{color:var(--good)}
.healthstrip.watch .hs-num{color:var(--warn)}
.healthstrip.poor .hs-num{color:var(--bad)}
.healthstrip.unknown .hs-num{color:var(--faint);font-size:24px}
.hs-label{
  font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
}
.hs-band{font-size:12px;font-weight:650;color:var(--ink-2);letter-spacing:-.005em}
.healthstrip.good .hs-band{color:var(--good)}
.healthstrip.watch .hs-band{color:var(--warn)}
.healthstrip.poor .hs-band{color:var(--bad)}
.hs-parts{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  gap:var(--s4);padding:var(--s4) var(--s5);
}
.hs-part.blank{opacity:.5}
.hs-part-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:5px}
.hs-part-top span{font-size:11.5px;font-weight:650;color:var(--ink-2)}
.hs-part-top b{font-size:14px;font-weight:650;letter-spacing:-.02em}
.hs-detail{display:block;font-size:10.5px;color:var(--muted);margin-top:4px;line-height:1.4}

/* ---------- inline figure groups ---------- */
/* A handful of related numbers reported side by side, without a container
   each. Used wherever a page needs to state three or four facts before it
   gets on with the argument. */
.figrow{
  display:flex;flex-wrap:wrap;gap:var(--s6);
  padding:var(--s4);border-bottom:1px solid var(--line-2);
}
.figrow.tight{gap:var(--s5);padding:var(--s3) var(--s4)}
.figrow>div{display:flex;flex-direction:column;min-width:0}
.fig-l{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
}
.fig-v{
  font-size:19px;font-weight:650;letter-spacing:-.025em;line-height:1.2;margin-top:3px;
  font-variant-numeric:tabular-nums;
}
.figrow.tight .fig-v{font-size:16px}
.fig-v.none{font-size:13px;font-weight:600;color:var(--faint)}
.fig-n{font-size:10.5px;color:var(--muted);margin-top:1px;letter-spacing:.03em}

/* ---------- labelled meter rows ---------- */
.barline{display:grid;grid-template-columns:minmax(88px,140px) minmax(0,1fr);align-items:center;gap:var(--s3)}
.barline+.barline{margin-top:7px}
.barline:last-child{margin-bottom:0}
.barline>span{font-size:12px;color:var(--ink-2);font-weight:500;min-width:0}
.barline .meterline{min-width:0}

/* ==========================================================================
   Performance drivers — what moved alongside sales

   The wording is load-bearing: "moved with", never "caused". The layout has
   to carry the same caution, so the reading sits level with the number rather
   than under a heading that would imply a conclusion.
   ========================================================================== */
.drivers{padding:var(--s4)}
.dr-head{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s2)}
.dr-change{
  font-size:15px;font-weight:650;letter-spacing:-.02em;
  padding:2px 9px;border-radius:var(--r-xs);background:var(--surface-3);color:var(--ink);
}
.dr-change.good{background:var(--good-wash);color:var(--good)}
.dr-change.bad{background:var(--bad-wash);color:var(--bad)}
.dr-note{font-size:11px;color:var(--muted);margin-left:auto;font-style:normal}
.dr-summary{font-size:12.5px;color:var(--ink-2);line-height:1.55;margin-bottom:var(--s3);max-width:88ch}
.dr-list{list-style:none;display:grid;gap:0;border-top:1px solid var(--line-2)}
.dr-list li{
  display:grid;grid-template-columns:minmax(0,1fr) auto auto;
  align-items:center;gap:var(--s2) var(--s3);
  padding:8px 0;border-bottom:1px solid var(--line-2);
}
.dr-list li:last-child{border-bottom:0}
.dr-name{font-size:12.5px;font-weight:600;color:var(--ink);min-width:0}
.dr-delta{font-size:13px;font-weight:650;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.dr-read{
  font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  padding:2px 7px;border-radius:var(--r-xs);background:var(--surface-3);color:var(--ink-2);
}
.dr-list li.with .dr-read{background:var(--accent-wash);color:var(--accent-ink)}
.dr-list li.against .dr-read{background:var(--warn-wash);color:var(--warn)}
.dr-detail{grid-column:1 / -1;font-size:11.5px;color:var(--muted);line-height:1.45;margin-top:-2px}

/* ==========================================================================
   Timeline — one chronological stream, a day at a time

   A fortnight at a busy location is sixty entries, and printed flat it is a
   page of scrolling to reach the day being asked about. So the day is the unit:
   one row each, carrying its date and what is in it, and the entries themselves
   stay folded until that row is opened. Fourteen lines instead of sixty, with
   nothing taken away.
   ========================================================================== */
/* Six chips in a half-width column: "Lost sales" was breaking across two
   lines and making its own chip twice the height of its neighbours. They stay
   on one line, and the row scrolls sideways before it wraps. */
#activity .listbar{overflow:hidden}
#activity .filters{overflow-x:auto;scrollbar-width:none}
#activity .filters::-webkit-scrollbar{display:none}
#activity .filters a{white-space:nowrap}
.dayrow{border-bottom:1px solid var(--line-2)}
.dayrow:last-of-type{border-bottom:0}
.dayrow > summary{
  display:flex;align-items:center;gap:var(--s3);
  padding:9px var(--s4);cursor:pointer;list-style:none;
  user-select:none;transition:background .12s ease;
}
/* Safari draws its own triangle from the list-style; this is the other half. */
.dayrow > summary::-webkit-details-marker{display:none}
.dayrow > summary:hover{background:var(--surface-2)}
.dayrow[open] > summary{background:var(--surface-2);border-bottom:1px solid var(--line-2)}

/* A chevron rather than a plus: it says the thing opens downwards. Shared with
   the foldable cards, so everything that folds on these screens folds the
   same way. */
.foldmark{
  flex:0 0 7px;width:7px;height:7px;margin-left:1px;
  border-right:1.6px solid var(--faint);border-bottom:1.6px solid var(--faint);
  transform:rotate(-45deg);transition:transform .15s ease;
}
details[open] > summary .foldmark{transform:rotate(45deg) translate(-1px,-1px)}

.dayrow-date{
  flex:0 0 96px;font-size:12px;font-weight:650;color:var(--ink);
  font-variant-numeric:tabular-nums;display:flex;align-items:center;gap:5px;
}
/* One mark for "read this day first", so a bad day is visible while folded. */
.dayrow-flag{
  width:6px;height:6px;border-radius:50%;background:var(--bad);flex:0 0 6px;
}
.dayrow-sum{
  font-size:11.5px;color:var(--muted);min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.dayrow > .tl{padding:var(--s2) var(--s4) var(--s3) calc(var(--s4) + var(--s3))}
.tl{list-style:none;position:relative;padding-left:var(--s3)}
/* One hairline down the day, so the entries read as a sequence rather than a
   list that happens to have times on it. */
.tl::before{
  content:"";position:absolute;left:3px;top:6px;bottom:6px;width:1px;background:var(--line);
}
.tl li{
  position:relative;display:flex;align-items:flex-start;gap:var(--s3);
  padding:5px 0 5px var(--s3);
}
.tl li::before{
  content:"";position:absolute;left:0;top:11px;width:7px;height:7px;border-radius:50%;
  background:var(--line);box-shadow:0 0 0 2px var(--surface);
}
.tl li.good::before{background:var(--good)}
.tl li.warn::before{background:var(--warn-mark)}
.tl li.bad::before{background:var(--bad)}
.tl-time{font-size:11px;color:var(--muted);flex:0 0 38px;padding-top:1px;font-variant-numeric:tabular-nums}
.tl-body{font-size:12.5px;line-height:1.45;min-width:0;flex:1}
.tl-body b{font-weight:600;display:block}
.tl-body span{display:block;color:var(--muted);font-size:11.5px;margin-top:1px}
.tl-who{color:var(--faint)!important}
.tl-go{
  margin-left:auto;align-self:center;color:var(--faint);font-size:14px;font-weight:600;
  padding:0 4px;
}
.tl li:hover .tl-go{color:var(--accent)}

/* ==========================================================================
   A card that folds

   For a section whose answer is worth having and whose question is not asked
   on most visits — a history behind a headline figure. Closed it costs one
   line; the head says enough to decide whether to open it.
   ========================================================================== */
.foldcard > summary{
  padding:10px var(--s4);display:flex;align-items:center;gap:var(--s3);
  min-height:42px;cursor:pointer;list-style:none;user-select:none;
  transition:background .12s ease;
}
.foldcard > summary::-webkit-details-marker{display:none}
.foldcard > summary:hover{background:var(--surface-2)}
.foldcard > summary h2{font-size:13px;font-weight:650;letter-spacing:-.005em;color:var(--ink)}
.foldcard > summary .tag{
  margin-left:auto;font-size:11.5px;font-weight:500;color:var(--muted);letter-spacing:.01em;
}
.foldcard[open] > summary{border-bottom:1px solid var(--line-2)}

/* ==========================================================================
   Action Center
   ========================================================================== */
.actionrow{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s4);align-items:center;
  padding:10px var(--s4);border-bottom:1px solid var(--line-2);
  border-left:2px solid transparent;transition:background .1s ease;
}
.actionrow:last-child{border-bottom:0}
.actionrow:hover{background:var(--surface-2)}
.actionrow.critical{border-left-color:var(--bad)}
.actionrow.high{border-left-color:var(--serious)}
.actionrow.medium{border-left-color:var(--warn-mark)}
.actionrow.late{background:var(--bad-wash)}
.actionrow.late:hover{background:#FAE4E1}
.ar-top{display:flex;align-items:center;gap:var(--s2);margin-bottom:2px}
.ar-title{font-size:13px;font-weight:600;letter-spacing:-.005em;line-height:1.35}
.ar-title:hover{color:var(--accent)}
.ar-meta{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;font-size:11.5px;color:var(--muted)}
/* The description is supporting detail, so it gets one line and an ellipsis
   rather than a paragraph in every row. */
.ar-desc{
  font-size:12px;color:var(--ink-2);line-height:1.45;margin-top:3px;max-width:86ch;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;
}
.ar-side{display:flex;align-items:center;gap:var(--s3);text-align:right;flex:0 0 auto}
.ar-owner{font-size:12.5px;font-weight:600}
.ar-due{font-size:11.5px;color:var(--muted)}
.ar-due.bad{color:var(--bad);font-weight:650}
.ar-due.none{color:var(--faint)}
.siginline{display:inline-block}

/* thread */
.thread{list-style:none}
.thread li{
  display:flex;gap:var(--s3);padding:10px var(--s4);border-bottom:1px solid var(--line-2);
  align-items:flex-start;
}
.thread li:last-child{border-bottom:0}
.thread li.system{background:var(--surface-2)}
.th-when{font-size:11px;color:var(--muted);flex:0 0 72px;padding-top:2px;font-variant-numeric:tabular-nums}
.th-body{font-size:13px;line-height:1.5;min-width:0}
.th-body b{font-weight:600;display:block;font-size:12px;color:var(--ink-2)}
.thread li.system .th-body span{color:var(--muted)}

/* ==========================================================================
   Team / shift intelligence
   ========================================================================== */
.personcard{border-left:2px solid var(--line)}
.personcard.good{border-left-color:var(--good)}
.personcard.watch{border-left-color:var(--warn-mark)}
.personcard.poor{border-left-color:var(--bad)}
.pc-head{display:flex;align-items:center;gap:var(--s4);padding:var(--s3) var(--s4);border-bottom:1px solid var(--line-2)}
.pc-who{display:flex;align-items:center;gap:var(--s3);flex:1;min-width:0}
.pc-av{
  width:34px;height:34px;border-radius:var(--pill);flex:0 0 34px;display:grid;place-items:center;
  background:var(--surface-3);font-size:12.5px;font-weight:650;color:var(--ink-2);
}
.pc-who b{display:block;font-size:14px;font-weight:650;letter-spacing:-.01em}
.pc-who>span>span{display:block;font-size:11.5px;color:var(--muted);margin-top:1px}
.pc-score{text-align:right;flex:0 0 auto}
.pc-num{display:block;font-size:26px;font-weight:650;letter-spacing:-.03em;line-height:1}
.personcard.good .pc-num{color:var(--good)}
.personcard.watch .pc-num{color:var(--warn)}
.personcard.poor .pc-num{color:var(--bad)}
.personcard.unknown .pc-num{color:var(--faint)}
.pc-band{font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
.pc-parts{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:var(--s4);padding:var(--s4)}
.pc-part.blank{opacity:.5}
.pc-part-top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:5px}
.pc-part-top span{font-size:11.5px;font-weight:650;color:var(--ink-2)}
.pc-part-top b{font-size:14px;font-weight:650;letter-spacing:-.02em}
.pc-detail{display:block;font-size:10.5px;color:var(--muted);margin-top:4px;line-height:1.4}
.pc-activity{
  display:flex;gap:var(--s5);flex-wrap:wrap;padding:10px var(--s4);
  border-top:1px solid var(--line-2);background:var(--surface-2);
  font-size:11.5px;color:var(--muted);
}
.pc-activity b{color:var(--ink);font-weight:650;font-variant-numeric:tabular-nums}
.pc-activity .bad b{color:var(--bad)}
.pc-weak{margin-left:auto;font-weight:650;color:var(--ink-2)}

/* ==========================================================================
   Roster grid
   ========================================================================== */
.roster th.shopcol,.roster td.shopcol{
  position:sticky;left:0;background:var(--surface);z-index:2;min-width:160px;
  box-shadow:1px 0 0 var(--line-2);
}
.roster th.shopcol{background:var(--surface-2);z-index:4}
.roster tbody tr:hover td.shopcol{background:var(--surface-2)}
.roster th{text-align:center}
.roster th.istoday{color:var(--accent)}
.roster th .sub{display:block;font-weight:500;text-transform:none;letter-spacing:0;font-size:10.5px;margin-top:2px}
.roster td.cellday{padding:5px 6px;min-width:140px}
.roster td.istoday{background:var(--accent-wash)}
.roster tbody tr:hover td.istoday{background:#E1EBFA}
.cell{
  display:flex;align-items:center;gap:6px;margin-bottom:4px;
  border-radius:var(--r-xs);background:var(--surface-2);padding:3px 4px;
  border:1px solid transparent;transition:border-color .13s ease,background .13s ease;
}
.cell:last-child{margin-bottom:0}
.cell:hover{border-color:var(--line)}
.cell .slot{
  font-size:9.5px;font-weight:700;color:var(--muted);background:var(--surface);
  border-radius:4px;padding:2px 5px;flex:0 0 auto;letter-spacing:.03em;
}
/* The name, where the select used to be: the grid is read now. */
.cell .who{
  font-size:12px;color:var(--ink);font-weight:500;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.cell .who.none{color:var(--muted);font-weight:400}
.cell.worked{background:var(--good-wash)}
.cell.worked .slot{color:var(--good)}
.cell.missed{background:var(--bad-wash)}
.cell.missed .slot{color:var(--bad)}
.cell.hole{background:var(--surface);border:1px dashed #E2A9A2}
.cell.hole .who{color:var(--bad)}
/* A week nobody has worked yet, read off the rota rather than off a row. The
   name is lighter and the slot marker is outlined: it is what will happen, not
   what did. */
.cell.projected{background:var(--surface-2);border:1px dashed var(--line)}
.cell.projected .who{color:var(--ink-2)}
.cell.projected.hole{background:var(--surface)}
.sw.projected{background:var(--surface-2);border:1px dashed var(--line)}
/* Nothing on this grid is a control any more, so nothing invites a click. */
.cell:hover{border-color:transparent}
.legendrow{
  display:flex;gap:var(--s4);flex-wrap:wrap;padding:10px var(--s4);border-top:1px solid var(--line-2);
  font-size:11.5px;color:var(--ink-2);
}
.legendrow span{display:flex;align-items:center;gap:6px}

/* A legend that explains rather than labels: four short sentences, two across
   on a wide screen and one on a narrow one. */
.legendgrid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:var(--s3) var(--s4);
  padding:12px var(--s4);border-top:1px solid var(--line-2);
  font-size:11.5px;color:var(--muted);line-height:1.45;
}
.legendgrid b{color:var(--ink-2);font-weight:650}
.legendkey{
  display:inline-flex;align-items:center;gap:6px;margin-right:5px;
  font-weight:700;color:var(--ink-2);text-transform:uppercase;letter-spacing:.06em;font-size:10px;
}
.sw{width:11px;height:11px;border-radius:3px;display:inline-block;background:var(--surface-2)}
.sw.worked{background:var(--good-wash)}
.sw.missed{background:var(--bad-wash)}
.sw.hole{background:var(--surface);border:1px dashed #E2A9A2}

/* issue queue */
.issues td.actions{width:1%;white-space:nowrap;text-align:right}
/* Direct children only — the form inside a .resolver dialog is not one of the
   little inline action buttons and must not be laid out as one. */
.issues td.actions > form{display:inline-block}
.issues td.actions>*{margin-left:5px}
.age{display:flex;flex-direction:column;gap:4px;min-width:72px}
/* The column the queue is sorted on, so it is set in lining figures: a column
   of durations is compared down its length, and proportional digits make the
   same number look like two different lengths on two rows. */
.issues .age .pct{font-variant-numeric:tabular-nums;letter-spacing:-.01em}

/* The subject of the issue: what it is about, then what kind of issue it is.
   The kind is one of three fixed values, so it is given a shape to be
   recognised by rather than another line of grey text to be read. */
.issues .subject{display:flex;align-items:baseline;gap:9px;min-width:0}
.issues .subject b{font-weight:600;letter-spacing:-.005em;min-width:0}
/* The kind leads, on a floor width, so the tags line up into a column rather
   than trailing off the end of twenty product names of twenty lengths. A floor
   and not a fixed width: "Display material" is nearly twice "Stock-out", and
   clipping a label to keep a column straight is the wrong thing to protect. */
.issues .subject .kind{
  flex:0 0 auto;min-width:92px;text-align:center;white-space:nowrap;
  font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted);background:var(--surface-3);border-radius:var(--r-xs);padding:2px 6px;
}
/* Narrow enough and the column it buys costs more than it is worth: six
   columns in a laptop width leaves the product name a third of what it needs,
   and a wall of four-line rows is harder to work than a ragged tag. It goes
   back above the name, which is where the width is. */
@media (max-width:1200px){
  .issues .subject{display:block}
  .issues .subject .kind{display:inline-block;min-width:0;margin-bottom:2px}
}

/* Who raised it reads as a person, not as a value in a cell. */
.issues .who{font-weight:550}

/* State, owner, reason, note — four things of three different weights. They
   were four identical grey lines before, which made the one that is countable
   (the reason) impossible to pick out from the one that is not (the note). */
.issues .state{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-width:0}
/* State and owner belong on one line — they are one fact about one row, and
   splitting them cost every closed row a third of its height. */
.issues .state .line{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
.issues .state .sub{margin-top:0}
.issues .state .why{
  font-size:11px;font-weight:600;color:var(--ink-2);
  border-left:2px solid var(--line);padding-left:7px;
}
.issues .state q{
  font-size:11px;color:var(--muted);font-style:italic;line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}

/* ---------- search results ---------- */
.resultrow{
  display:flex;align-items:center;gap:var(--s3);padding:10px var(--s4);
  border-bottom:1px solid var(--line-2);transition:background .1s ease;
}
.resultrow:last-child{border-bottom:0}
.resultrow:hover{background:var(--surface-2)}
.resultrow .what{flex:1;min-width:0}
.resultrow .job-go{font-size:15px;color:var(--faint);font-weight:600}
.resultrow:hover .job-go{color:var(--accent)}

/* ==========================================================================
   Responsive

   Not "stack everything". The rail becomes a bar, the metric strip becomes a
   two-up grid that keeps the lead figure first, tables keep scrolling inside
   their own panel rather than pushing the page sideways.
   ========================================================================== */
@media (max-width:1180px){
  .cols{grid-template-columns:minmax(0,1fr)}
  .approvebox{grid-template-columns:1fr}
  .sheet{padding:var(--s5) var(--s5) var(--s8)}
}

@media (max-width:900px){
  .shell{grid-template-columns:1fr}
  .rail{
    position:sticky;top:0;z-index:30;height:auto;flex-direction:row;align-items:center;
    gap:var(--s3);padding:8px var(--s3);overflow-x:auto;box-shadow:var(--sh-sticky);
  }
  .rail .mark{padding:0;flex:0 0 auto}
  .rail .mark span{display:none}
  .railsearch{display:none}
  .rail nav{flex-direction:row;overflow:visible;margin:0;flex:0 0 auto;gap:2px}
  .rail .group{display:none}
  .rail nav a{white-space:nowrap;padding:7px 10px}
  .rail nav a.on::before{left:0;right:0;top:auto;bottom:-8px;width:auto;height:2px;border-radius:2px 2px 0 0}
  .rail .who{margin:0 0 0 auto;border:0;padding:0;flex:0 0 auto}
  .rail .who b,.rail .who>span>span{display:none}
  .sheet{padding:var(--s4) var(--s4) var(--s7)}
  h1{font-size:19px}
  .head{align-items:flex-start;flex-direction:column;gap:var(--s3)}
  .head .aside{margin-left:0;width:100%}
  .story .storycols{grid-template-columns:1fr}
  .story .storycols>div+div{border-left:0;border-top:1px solid var(--line-2)}
}

@media (max-width:660px){
  /* Two figures across, lead figure still first and still larger. Six equal
     tiles in one column would bury the one that matters under five that
     do not. */
  .kpis{grid-template-columns:1fr 1fr}
  .stat{border-left:1px solid var(--line-2);border-top:1px solid var(--line-2)}
  .stat:nth-child(-n+2){border-top:0}
  .stat:nth-child(odd){border-left:0}
  .stat.hero{grid-column:1 / -1;border-left:0}
  .stat .val{font-size:22px}
  .stat.hero .val{font-size:28px}

  /* The queue keeps its shape: severity and place on top, figure beside the
     title, controls on their own line where a thumb can reach them. */
  .qrow{grid-template-columns:minmax(0,1fr) auto;gap:var(--s2) var(--s3);padding:10px var(--s3)}
  .q-sev{grid-column:1 / -1}
  .q-act{grid-column:1 / -1;justify-content:flex-start;margin-top:2px}
  .q-act .btn{opacity:1}
  .opps .qrow{grid-template-columns:minmax(0,1fr) auto}

  .actionrow{grid-template-columns:1fr;gap:var(--s2)}
  .ar-side{justify-content:flex-start;text-align:left;flex-wrap:wrap}

  /* The health strip is a score beside its parts. Below this width the parts
     column is narrower than the labels in it, so the score goes on top and
     the parts get the full width instead of a squeezed third of it. */
  .healthstrip{grid-template-columns:1fr}
  .hs-score{
    flex-direction:row;align-items:baseline;gap:var(--s3);
    border-right:0;border-bottom:1px solid var(--line-2);padding:var(--s3) var(--s4);
  }
  .hs-num{font-size:30px}
  .hs-parts{padding:var(--s4);grid-template-columns:1fr 1fr}
  .healthgrid{grid-template-columns:1fr 1fr}
  .grid th,.grid td{padding-left:var(--s3);padding-right:var(--s3)}
  .card-head{padding:9px var(--s3)}
  .filters{width:100%;overflow-x:auto}

  .dayrow > summary{flex-wrap:wrap;row-gap:2px}
  .dayrow-date{flex:0 0 auto}
  .dayrow-sum{flex:1 1 100%;padding-left:16px;white-space:normal}
  .dayrow > .tl{padding-left:var(--s4);padding-right:var(--s3)}
}

/* On a touch screen there is no hover, so a control that only appears on hover
   never appears at all. Anything that fades up for the mouse is simply present
   for a finger. */
@media (hover:none){
  .q-act .btn,.grid tbody tr .rowact{opacity:1}
}

@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* ==========================================================================
   Sign in

   This was the last screen still wearing the framework's default kit — a
   different typeface, a different palette and a drop-shadowed white box. It is
   the first thing anybody sees, so it is now the same product as the rest.
   ========================================================================== */
body.signin{background:var(--surface)}
.signwrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);min-height:100vh}

.signbrand{
  background:var(--brand);color:#fff;padding:var(--s7);
  display:flex;flex-direction:column;gap:var(--s6);
}
.signbrand .mark{display:flex;align-items:center;gap:10px}
.signbrand .mark .dot{
  width:30px;height:30px;border-radius:8px;flex:0 0 30px;
  background:linear-gradient(140deg,var(--warn-mark),#C9622A);
  display:grid;place-items:center;font-weight:700;font-size:15px;color:var(--brand);
}
.signbrand .mark b{font-size:16px;font-weight:620;letter-spacing:-.01em;display:block;line-height:1.15}
.signbrand .mark > span > span{font-size:11px;color:#7787A4;letter-spacing:.02em}
.signpitch{margin-top:auto;max-width:34ch}
.signpitch h2{font-size:26px;font-weight:650;letter-spacing:-.025em;line-height:1.25}
.signpitch p{margin-top:var(--s3);font-size:13.5px;color:#A9B7CD;line-height:1.6}
.signfoot{font-size:11.5px;color:#6C7D99}

.signpanel{display:grid;place-items:center;padding:var(--s7) var(--s6)}
.signform{width:100%;max-width:330px}
.signform h1{font-size:22px}
.signsub{font-size:13px;color:var(--ink-2);margin-top:4px}
.signfields{display:grid;gap:var(--s4);margin-top:var(--s6)}
.formlabel{display:block;font-size:11.5px;font-weight:650;color:var(--ink-2);margin-bottom:5px}
.input{width:100%}
.formerror{list-style:none;font-size:11.5px;color:var(--bad);margin-top:5px;display:grid;gap:2px}
.signremember{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink-2);cursor:pointer}
.signremember input{width:auto;accent-color:var(--accent)}
.signrow{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin-top:var(--s1)}
.signlink{font-size:12.5px;color:var(--muted)}
.signlink:hover{color:var(--accent);text-decoration:underline}
.signform .flash{margin-bottom:var(--s4)}

@media (max-width:820px){
  /* On a phone the pitch is not the job — signing in is. */
  .signwrap{grid-template-columns:1fr}
  .signbrand{padding:var(--s5);gap:var(--s4)}
  .signpitch{margin-top:0}
  .signpitch h2{font-size:19px}
  .signpitch p,.signfoot{display:none}
  .signpanel{padding:var(--s6) var(--s4);align-items:start}
}

/* A note under a field: quieter than body text, louder than a placeholder. */
.hint{font-size:12px;color:var(--ink-2);line-height:1.55;margin-top:6px}
.hint.good{color:var(--good)}

/* ==========================================================================
   LIST PAGES

   A dashboard answers "what is the situation". A list page answers "find,
   compare, inspect, act". Different jobs, different shapes — but the same
   type, colour, spacing and border treatment, so it stays one product.

   The rule underneath all of it: a record is a ROW, not a card. Twenty
   attractive cards is still a screen you have to read twenty times.
   ========================================================================== */

/* ---------- tools bar: search, filters, count ---------- */
.listtools{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;width:100%;min-width:0}
.listtools .right{margin-left:auto;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}

.searchbox{position:relative;flex:0 1 236px;min-width:148px}
.searchbox input{
  padding-left:29px;padding-right:26px;background-repeat:no-repeat;background-position:9px center;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='7' cy='7' r='4.6' stroke='%237B8BA4' stroke-width='1.5'/%3E%3Cpath d='M10.4 10.4 14 14' stroke='%237B8BA4' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
}
.searchbox .clear{
  position:absolute;right:5px;top:50%;transform:translateY(-50%);
  border:0;background:none;cursor:pointer;color:var(--muted);font-size:15px;line-height:1;
  padding:2px 4px;border-radius:4px;display:none;
}
.searchbox .clear:hover{background:var(--surface-3);color:var(--ink)}
.searchbox.filled .clear{display:block}

/* "How much am I looking at" belongs with the controls that change it, not at
   the bottom of the page where you find it after scrolling. */
.resultcount{font-size:11.5px;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}
.resultcount b{color:var(--ink-2);font-weight:650}

/* ---------- data table ---------- */
.grid.data td{padding:0 var(--s4);height:46px}
.grid.data tbody tr.clickable{cursor:pointer}
.grid.data tbody tr.clickable:hover{background:var(--surface-2)}
.grid.data .primary{
  font-size:13px;font-weight:600;color:var(--ink);letter-spacing:-.005em;
  display:inline-flex;align-items:center;gap:7px;
}
.grid.data a.primary:hover{color:var(--accent)}
.grid.data .secondary{display:block;font-size:11.5px;color:var(--muted);margin-top:1px}

/* Group headers inside the table: what turns sixty rows into six readable
   sets. Only ever on grouping the data already has. */
tr.grouprow th{
  position:static;background:var(--surface-2);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line-2);
  padding:0;text-transform:none;letter-spacing:0;
}
tr.grouprow:first-child th{border-top:0}
.grouphead{
  display:flex;align-items:center;gap:var(--s3);width:100%;
  padding:7px var(--s4);background:none;border:0;font-family:inherit;
  cursor:pointer;text-align:left;color:var(--ink);
}
.grouphead:hover{background:var(--surface-3)}
.grouphead .caret{color:var(--muted);font-size:9px;line-height:1;flex:0 0 auto;transition:transform .13s ease}
.grouphead[aria-expanded="false"] .caret{transform:rotate(-90deg)}
.grouphead .gname{font-size:12.5px;font-weight:650;letter-spacing:-.005em}
.grouphead .gcount{
  font-size:10.5px;font-weight:600;color:var(--muted);
  background:var(--surface-3);border-radius:var(--pill);padding:1px 7px;
}
/* The group's own figures on the group's own line, so a brand can be read and
   compared with the one above it without being opened. */
.gfigs{margin-left:auto;display:flex;gap:var(--s5);flex-wrap:wrap;align-items:baseline}
.gfig{display:flex;align-items:baseline;gap:5px;font-size:10.5px;color:var(--muted);white-space:nowrap}
.gfig b{font-size:12.5px;font-weight:650;color:var(--ink);letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.gfig b.good{color:var(--good)}
.gfig b.warn{color:var(--warn)}
.gfig b.bad{color:var(--bad)}
.gfig b.none{color:var(--faint);font-weight:500}

/* ---------- sortable columns ---------- */
/* The header is a <button> in the markup, so it is reachable by tab and
   answers to Enter and Space without a keydown handler. This styles it back
   into looking like a table heading. */
th.sortable{padding:0;white-space:nowrap}
th.sortable button{
  all:unset;box-sizing:border-box;display:flex;align-items:center;gap:4px;
  width:100%;padding:8px var(--s4);cursor:pointer;
  font:inherit;font-size:10.5px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted);
}
th.sortable.num button{justify-content:flex-end}
th.sortable button:hover{color:var(--ink-2)}
th.sortable button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
th.sortable .sortmark{opacity:.3;margin-left:4px;font-size:9px}
th.sortable:hover .sortmark{opacity:.65}
th.sortable[aria-sort="ascending"] .sortmark,
th.sortable[aria-sort="descending"] .sortmark{opacity:1;color:var(--accent)}

/* ---------- row actions ---------- */
/* One control, not a row of them. Native <details>: opens without a framework
   and closes on Escape like every other dialog here. */
.rowmenu{position:relative;display:inline-block}
.rowmenu > summary{
  list-style:none;cursor:pointer;display:inline-grid;place-items:center;
  width:24px;height:24px;border-radius:var(--r-xs);color:var(--muted);font-size:14px;line-height:1;
}
.rowmenu > summary::-webkit-details-marker{display:none}
.rowmenu > summary:hover,.rowmenu[open] > summary{background:var(--surface-3);color:var(--ink)}
.rowmenu[open]::before{content:"";position:fixed;inset:0;z-index:20}
.rowmenu .menu{
  position:absolute;right:0;top:calc(100% + 4px);z-index:25;
  min-width:170px;padding:4px;text-align:left;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  box-shadow:var(--sh-pop);
}
.rowmenu .menu a,.rowmenu .menu button{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:7px 9px;border:0;background:none;border-radius:var(--r-xs);
  font-family:inherit;font-size:12.5px;font-weight:500;color:var(--ink);
  cursor:pointer;text-align:left;white-space:nowrap;
}
.rowmenu .menu a:hover,.rowmenu .menu button:hover{background:var(--surface-2)}
.rowmenu .menu .sep{height:1px;background:var(--line-2);margin:4px 0}
.rowmenu .menu .danger{color:var(--bad)}
.rowmenu .menu .danger:hover{background:var(--bad-wash)}

td.actions .rowmenu > summary,td.actions .btn.small{opacity:.55;transition:opacity .12s ease}
tr:hover td.actions .rowmenu > summary,
tr:focus-within td.actions .rowmenu > summary,
tr:hover td.actions .btn.small,
tr:focus-within td.actions .btn.small,
.rowmenu[open] > summary{opacity:1}

/* ---------- expandable rows ---------- */
tr.detailrow > td{
  padding:0 var(--s4) var(--s3);background:var(--surface-2);
  border-bottom:1px solid var(--line-2);
}
.detailgrid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:var(--s3) var(--s5);padding-top:var(--s3);
}
.detailgrid > div{min-width:0}
.detailgrid dt{font-size:10px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
.detailgrid dd{font-size:12.5px;color:var(--ink);margin-top:3px;line-height:1.45}
.detailgrid dd.none{color:var(--faint)}
.detailnote{font-size:11.5px;color:var(--muted);margin-top:var(--s3);line-height:1.5;max-width:92ch}
.expander{
  border:0;background:none;cursor:pointer;color:var(--muted);padding:4px;border-radius:var(--r-xs);
  display:inline-grid;place-items:center;line-height:1;
}
.expander:hover{background:var(--surface-3);color:var(--ink)}
.expander svg{display:block;transition:transform .13s ease}
.expander[aria-expanded="true"] svg{transform:rotate(180deg)}

/* ---------- people list ---------- */
/* Six people used to be six tall cards, each with four meters and an activity
   strip — a page you scrolled rather than read. Same information, ranked: who,
   how they are doing, the four parts as bars that line up down the column so
   they can be compared, and the rest on request. */
.people{display:block}
.prow{
  display:grid;grid-template-columns:minmax(0,1.45fr) 58px minmax(0,2.3fr) auto;
  align-items:center;gap:var(--s4);
  padding:8px var(--s4);border-bottom:1px solid var(--line-2);
  border-left:2px solid transparent;transition:background .1s ease;
}
.prow:last-of-type{border-bottom:0}
.prow:hover{background:var(--surface-2)}
.prow.good{border-left-color:var(--good)}
.prow.watch{border-left-color:var(--warn-mark)}
.prow.poor{border-left-color:var(--bad)}
.prow.unknown{border-left-color:var(--line)}
.p-who{display:flex;align-items:center;gap:var(--s3);min-width:0}
.p-av{
  width:30px;height:30px;border-radius:var(--pill);flex:0 0 30px;display:grid;place-items:center;
  background:var(--surface-3);font-size:11.5px;font-weight:650;color:var(--ink-2);
}
.p-name{font-size:13px;font-weight:600;letter-spacing:-.005em;display:block}
.p-meta{
  font-size:11.5px;color:var(--muted);display:block;margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.p-score{text-align:right}
.p-num{font-size:20px;font-weight:650;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums}
.prow.good .p-num{color:var(--good)}
.prow.watch .p-num{color:var(--warn)}
.prow.poor .p-num{color:var(--bad)}
.prow.unknown .p-num{color:var(--faint);font-size:15px}
.p-band{
  display:block;font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin-top:3px;
}
.p-parts{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s3);min-width:0}
.p-part{min-width:0}
.p-part.blank{opacity:.45}
.p-part-top{display:flex;justify-content:space-between;align-items:baseline;gap:6px;margin-bottom:4px}
.p-part-top span{
  font-size:10px;color:var(--muted);letter-spacing:.03em;text-transform:uppercase;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.p-part-top b{font-size:11.5px;font-weight:650;font-variant-numeric:tabular-nums}
.p-act{display:flex;align-items:center;gap:6px}
.p-detail{
  padding:var(--s3) var(--s4) var(--s4) calc(var(--s4) + 2px);
  background:var(--surface-2);border-bottom:1px solid var(--line-2);
}



/* Rows hidden by search, or by a collapsed group. */
tr[hidden],.prow[hidden],.p-detail[hidden],tr.detailrow[hidden]{display:none}

@media (max-width:900px){
  /* Two rows: who they are, how they are doing and the control on the first;
     the four parts across the full width underneath. Placed explicitly,
     because leaving it to auto-placement pushed the control onto a third row
     of its own and squeezed its column to nothing. */
  .prow{grid-template-columns:minmax(0,1fr) auto auto;row-gap:var(--s3);gap:var(--s3)}
  .p-who{grid-column:1;grid-row:1}
  .p-score{grid-column:2;grid-row:1;text-align:right}
  .p-act{grid-column:3;grid-row:1}
  .p-parts{grid-column:1 / -1;grid-row:2}
}
@media (max-width:660px){
  .searchbox{flex:1 1 100%}
  .listtools .right{margin-left:0;width:100%;justify-content:space-between}
  .gfigs{display:none}
  .drawer .panel{width:100vw}
  .p-parts{grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* A label that exists for a screen reader and not for the eye — an actions
   column needs a name, but printing "ACTIONS" over a column of ⋮ is noise. */
.vh{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* A tools bar for a panel whose header is the table itself. Same height and
   the same rule beneath it as .card-head, so a panel with tabs and a panel
   with only a search box still line up with each other. */
.listbar{
  padding:8px var(--s4);border-bottom:1px solid var(--line-2);
  display:flex;align-items:center;gap:var(--s3);min-height:42px;
}
/* Enough controls that they will not fit a column at every width. They wrap
   onto a second line rather than squeezing the date fields down to something
   nobody can hit. */
.listbar.wrap{flex-wrap:wrap;row-gap:8px}
.listbar.wrap .period{margin-left:auto}
/* Says out loud that the list was capped before it reached the page. */
.capnote{
  font-size:10.5px;font-weight:600;color:var(--muted);
  background:var(--surface-3);border-radius:var(--r-xs);padding:1px 6px;white-space:nowrap;cursor:help;
}

/* ==========================================================================
   Photographs

   Evidence, not decoration: small, in a row, and the same size everywhere so
   a strip of them can be scanned rather than read one at a time.
   ========================================================================== */
.photostrip{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center}
.photostrip .photoview{display:inline-block}
.photostrip summary{
  list-style:none;cursor:zoom-in;display:block;position:relative;
  border-radius:var(--r-xs);overflow:hidden;line-height:0;
  border:1px solid var(--line);transition:border-color .13s ease,transform .13s ease;
}
.photostrip summary::-webkit-details-marker{display:none}
.photostrip summary:hover{border-color:var(--accent);transform:translateY(-1px)}
.photostrip summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Scoped to the summary: the full-size image in the dialog is a descendant of
   the strip too, and these rules were shrinking it to a thumbnail. */
.photostrip summary img{width:64px;height:48px;object-fit:cover;display:block;background:var(--surface-3)}
.photostrip.compact summary img{width:40px;height:30px}
.photostrip .kind{
  position:absolute;left:0;right:0;bottom:0;
  font-size:8.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:#fff;background:rgba(15,27,50,.62);padding:1px 3px;text-align:center;line-height:1.3;
}

/* The full view rides the existing .resolver dialog, so it opens without
   script and closes on Escape and on the backdrop like everything else. */
.resolver.photoview form{width:min(760px,calc(100vw - 32px))}
.photofull{
  width:100%;height:auto;max-height:70vh;object-fit:contain;
  border-radius:var(--r-sm);background:var(--surface-3);display:block;
}

/* In a table cell the strip must not stretch the row. */
td .photostrip{margin-top:5px}

/* ==========================================================================
   Notifications
   ========================================================================== */

/* The bell sits above the person in the rail: it is about you, not about a
   part of the application, so it does not belong in the nav list. */
.bell{
  position:relative;display:flex;align-items:center;gap:9px;
  margin:0 0 var(--s2);padding:7px 8px;border-radius:var(--r-xs);
  font-size:13px;font-weight:500;color:#A9B7CD;
  transition:background .13s ease,color .13s ease;
}
.bell::after{content:"Notifications"}
.bell:hover{background:rgba(255,255,255,.055);color:#E6ECF5}
.bell.on{background:rgba(255,255,255,.09);color:#fff;font-weight:600}
.bell svg{width:15px;height:15px;flex:0 0 15px;opacity:.72}
.bell:hover svg,.bell.on svg{opacity:1}
.bell .count{margin-left:auto}

.notifrow{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:var(--s3);
  padding:10px var(--s4);border-bottom:1px solid var(--line-2);
  transition:background .1s ease;
}
.notifrow:last-of-type{border-bottom:0}
.notifrow:hover{background:var(--surface-2)}
.notifrow.unread{background:var(--accent-wash)}
.notifrow.unread:hover{background:#E1EBFA}
.nf-dot{width:7px;height:7px;border-radius:50%;background:var(--line);flex:0 0 7px}
.notifrow.unread .nf-dot{background:var(--accent)}
.notifrow.high .nf-dot{background:var(--bad)}
.notifrow.medium .nf-dot{background:var(--warn-mark)}
.nf-what{min-width:0}
.nf-title{display:block;font-size:13px;font-weight:600;color:var(--ink);letter-spacing:-.005em}
.notifrow:hover .nf-title{color:var(--accent)}
.nf-body{
  display:block;font-size:11.5px;color:var(--muted);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.nf-when{
  font-size:11px;color:var(--muted);white-space:nowrap;text-align:right;
  display:flex;align-items:center;gap:7px;font-variant-numeric:tabular-nums;
}
.nf-new{
  font-size:9px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  background:var(--accent);color:#fff;border-radius:var(--r-xs);padding:1px 5px;
}

@media (max-width:900px){
  /* In the top bar the bell is an icon, and the word would not fit. */
  .bell{margin:0;padding:7px}
  .bell::after{display:none}
}

/* ==========================================================================
   Dark

   Both palettes were already custom properties on :root, so this is one media
   block redefining the tokens and nothing else — no component knows it exists.

   Not an inversion. The surfaces are lifted off black rather than dropped from
   white, because a pure-black panel under a bright shop ceiling has more
   contrast against the room than against its own content. Status colours are
   lightened until they clear the same 3:1 gate they clear in daylight; the
   accent shifts up rather than desaturating, so a plotted line stays the same
   colour as the link that describes it.
   ========================================================================== */
/* ---------- dark ----------
   Applied from a data-theme attribute rather than a media query, so the
   operating system's preference and the person's choice can disagree —
   which is the entire point of offering a choice. resources/js resolves
   "system" to one of these before the first paint; see the head script
   in the layouts.
   ------------------------------------------------------------------ */
:root[data-theme="dark"]{
  --bg:          #0D1420;
  --surface:     #141C2B;
  --surface-2:   #1A2434;
  --surface-3:   #232F42;
  --brand:       #0A101B;
  --brand-2:     #16202F;

  --ink:         #E8EDF5;
  --ink-2:       #AFBCCF;
  --muted:       #8496AE;
  --faint:       #64748B;
  --line:        #26334699;
  --line-2:      #1F2A3A;

/* The three series, lightened to clear the contrast gate on a dark
   surface. The hues are held so a chart read in one mode is recognisably
   the same chart in the other. */
  --series-1:    #6BA3F0;
  --series-2:    #E8935C;
  --series-3:    #34C9A3;

  --accent:      #6BA3F0;
  --accent-ink:  #93BEF7;

  --good:        #46C08D;
  --warn:        #E5B45C;
  --warn-mark:   #E5B45C;
  --serious:     #E8935C;
  --bad:         #F0736A;

/* Washes are a tint of the surface, not a tint of white. A pale wash on a
   dark panel glows; these sit just above it. */
  --good-wash:   #16302A;
  --warn-wash:   #322818;
  --bad-wash:    #351E1E;
  --accent-wash: #16263F;

/* Shadows do almost nothing on a dark ground. The borders already carry
   the separation, so these only mark the things that genuinely float. */
  --sh-sticky: 0 4px 10px rgba(0,0,0,.4);
  --sh-pop:    0 4px 14px rgba(0,0,0,.5);
  --sh-modal:  0 24px 60px rgba(0,0,0,.65);
}

/* The rail is the darkest thing in daylight and cannot also be the darkest
   thing at night, or it disappears into the page. */
[data-theme="dark"] .rail{background:var(--brand-2);border-right:1px solid var(--line-2)}

/* The two places that hard-code white on a tint. */
[data-theme="dark"] .flash{background:var(--good-wash);color:#9BE0C1;border-color:#1F4738}

[data-theme="dark"] .flash.bad{background:var(--bad-wash);color:#F7A9A2;border-color:#4A2A2A}

[data-theme="dark"] .alert{background:var(--warn-wash);color:#EBCB92;border-color:#4A3D22}

[data-theme="dark"] .alert.bad{background:var(--bad-wash);color:#F7A9A2;border-color:#4A2A2A}

[data-theme="dark"] .notice{background:var(--warn-wash);color:#EBCB92}

/* Photographs and charts keep their own light; the frame around them moves. */
[data-theme="dark"] .photostrip summary img,[data-theme="dark"] .photofull{background:var(--surface-3)}

[data-theme="dark"] .chart .dot{stroke:var(--surface)}

/* An input on a dark ground needs its own surface, or it reads as a gap. */
[data-theme="dark"] select,[data-theme="dark"] input[type=text],[data-theme="dark"] input[type=email],[data-theme="dark"] input[type=date],[data-theme="dark"] input[type=number],[data-theme="dark"] input[type=search],[data-theme="dark"] input[type=file],[data-theme="dark"] textarea{
  background:var(--surface-2);border-color:var(--line-2);color:var(--ink);
}

[data-theme="dark"] select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='%238496AE' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

[data-theme="dark"] .searchbox input{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 16 16' fill='none'%3E%3Ccircle cx='7' cy='7' r='4.6' stroke='%238496AE' stroke-width='1.5'/%3E%3Cpath d='M10.4 10.4 14 14' stroke='%238496AE' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
}

[data-theme="dark"] .btn{background:var(--surface-2);border-color:var(--line-2)}

[data-theme="dark"] .btn:hover{background:var(--surface-3);border-color:#33415A}

[data-theme="dark"] .btn.primary,[data-theme="dark"] .btn.accent{background:var(--accent);color:#0A101B;border-color:var(--accent)}

[data-theme="dark"] .btn.primary:hover,[data-theme="dark"] .btn.accent:hover{background:var(--accent-ink);border-color:var(--accent-ink)}

[data-theme="dark"] .btn.danger{background:var(--surface-2);color:var(--bad);border-color:#4A2A2A}

[data-theme="dark"] .btn.danger:hover{background:var(--bad);color:#0A101B}

[data-theme="dark"] .dayrow[open] > summary{background:var(--surface-2);border-bottom-color:var(--line)}
[data-theme="dark"] .filters,[data-theme="dark"] .range,[data-theme="dark"] .viewswitch{background:var(--surface-3)}

[data-theme="dark"] .filters a.on,[data-theme="dark"] .range a.on{background:var(--surface);box-shadow:none}

[data-theme="dark"] .resolver[open]::before{background:rgba(0,0,0,.6)}

[data-theme="dark"] .drawer .scrim{background:rgba(0,0,0,.6)}

[data-theme="dark"] .signin{background:var(--surface)}

/* ---------- field validation ---------- */
.fieldwrap{min-width:0}
.fieldwrap + .fieldwrap{margin-top:var(--s3)}
.fielderror{
  font-size:11.5px;font-weight:600;color:var(--bad);margin-top:5px;line-height:1.4;
  display:flex;align-items:flex-start;gap:5px;
}
.fielderror::before{content:"!";font-weight:700;flex:0 0 auto}
.fieldhint{font-size:11.5px;color:var(--muted);margin-top:5px;line-height:1.4}
.hasError > input,.hasError > select,.hasError > textarea{
  border-color:var(--bad);
}
.hasError > input:focus,.hasError > select:focus,.hasError > textarea:focus{
  box-shadow:0 0 0 3px var(--bad-wash);
}

/* A focused chart point has to be visible, and the default outline is drawn
   inside an SVG rect where it is easy to miss. */
.chart .hit:focus-visible{
  fill:var(--accent);fill-opacity:.12;
  outline:2px solid var(--accent);outline-offset:-1px;
}

/* The figures behind a chart, for anybody who cannot read a picture. Folded
   away, because the chart is the point for everybody who can. */
.figures{border-top:1px solid var(--line-2)}
.figures > summary{
  list-style:none;cursor:pointer;padding:8px var(--s4);
  font-size:11.5px;font-weight:650;color:var(--muted);
  display:flex;align-items:center;gap:6px;
}
.figures > summary::-webkit-details-marker{display:none}
.figures > summary::before{content:"▸";font-size:9px;transition:transform .13s ease}
.figures[open] > summary::before{transform:rotate(90deg)}
.figures > summary:hover{color:var(--ink)}
.figures .scroll{border-top:1px solid var(--line-2)}
/* The unit above the axis: quieter than a tick, because it is said once. */
.chart .axis.unit{font-size:9.5px;font-weight:600;letter-spacing:.04em;fill:var(--muted)}

/* ---------- colour scheme switch ---------- */
/* Three states in the width of one control. It lives above the person in the
   rail, beside the bell, because it is a preference about them rather than a
   place in the application. */
.themeswitch{
  display:flex;gap:2px;padding:2px;margin:0 0 var(--s2);
  background:rgba(255,255,255,.06);border-radius:var(--r-xs);
}
.themeswitch button{
  flex:1;display:grid;place-items:center;height:24px;
  border:0;background:none;border-radius:4px;cursor:pointer;
  color:#8B9AB4;transition:background .13s ease,color .13s ease;
}
.themeswitch button:hover{color:#E6ECF5}
.themeswitch button[aria-checked="true"]{background:rgba(255,255,255,.13);color:#fff}
.themeswitch button:focus-visible{outline:2px solid var(--warn-mark);outline-offset:-2px}
.themeswitch svg{width:14px;height:14px;display:block}

@media (max-width:900px){
  /* In the top bar it sits beside the bell rather than above the name. */
  .themeswitch{margin:0;width:auto}
  .themeswitch button{width:26px;flex:0 0 26px}
}
/* On the sign-in panel the switch sits under the pitch, on the dark side. */
.signfoot{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);flex-wrap:wrap}
.signfoot p{font-size:11.5px;color:#6C7D99;margin:0}
.signfoot .themeswitch{margin:0;flex:0 0 auto}

/* ==========================================================================
   First run — the setup wizard

   The only screens in the application that run with nobody signed in and
   nothing in the database, so they carry their own frame: no rail to navigate
   with, no shop switcher, nothing to go back to. One column, one question at a
   time, and a step list that says how far it goes.
   ========================================================================== */
body.setup{background:var(--bg)}

.wiz{min-height:100vh;display:flex;flex-direction:column}

.wizhead{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s5);flex-wrap:wrap;
  background:var(--brand);color:#fff;padding:var(--s4) var(--s6);
}
.wizhead .mark{display:flex;align-items:center;gap:10px}
.wizhead .mark .dot{
  width:30px;height:30px;border-radius:8px;flex:0 0 30px;
  background:linear-gradient(140deg,var(--warn-mark),#C9622A);
  display:grid;place-items:center;font-weight:700;font-size:15px;color:var(--brand);
}
.wizhead .mark b{font-size:16px;font-weight:620;letter-spacing:-.01em;display:block;line-height:1.15}
.wizhead .mark > span > span{font-size:11px;color:#7787A4;letter-spacing:.02em}

.wizsteps{display:flex;align-items:center;gap:var(--s5);list-style:none;flex-wrap:wrap}
.wizsteps li{display:flex;align-items:center;gap:7px;font-size:12px;font-weight:600;color:#7787A4}
.wizsteps li .n{
  width:20px;height:20px;border-radius:50%;display:grid;place-items:center;
  font-size:11px;font-weight:700;border:1px solid #3B4B6B;color:#93A2BD;
}
.wizsteps li.now{color:#fff}
.wizsteps li.now .n{background:var(--warn-mark);border-color:var(--warn-mark);color:var(--brand)}
.wizsteps li.done{color:#A9B7CD}
.wizsteps li.done .n{background:#2A3A58;border-color:#2A3A58;color:#C7D3E5}

.wizpanel{flex:1;padding:var(--s7) var(--s6)}
.wizbody{max-width:780px;margin:0 auto}
.wizbody h1{font-size:23px;letter-spacing:-.02em}
.wizlede{font-size:13.5px;color:var(--ink-2);line-height:1.6;margin-top:var(--s2);max-width:64ch}

/* ---------- the two ways to start ---------- */
.wizchoices{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:var(--s4);margin-top:var(--s6)}
.wizchoice{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:var(--s5);display:flex;flex-direction:column;gap:var(--s3);
}
.wizchoice h2{font-size:15px;font-weight:650;letter-spacing:-.01em}
.wizchoice p{font-size:13px;color:var(--ink-2);line-height:1.6}
.wizchoice .btn{align-self:flex-start;margin-top:auto}
.wizchoice form{margin-top:auto}
.wizchoice form .hint{margin-top:var(--s2)}

.wizlist{list-style:none;display:grid;gap:6px;font-size:12.5px;color:var(--ink-2)}
.wizlist li{padding-left:15px;position:relative;line-height:1.5}
.wizlist li::before{content:"";position:absolute;left:0;top:7px;width:5px;height:5px;border-radius:50%;background:var(--faint)}
.wizlist code{font-size:11.5px;background:var(--surface-3);padding:1px 5px;border-radius:4px}

.wiznote{
  font-size:12.5px;color:var(--ink-2);line-height:1.6;margin-top:var(--s5);
  border-left:2px solid var(--line);padding-left:var(--s3);max-width:68ch;
}
.wiznote a{color:var(--accent);font-weight:600}
.wiznote a:hover{text-decoration:underline}

/* ---------- forms ---------- */
.wizform{margin-top:var(--s6)}
.wizform-narrow{max-width:390px}
.wizform .hint{max-width:64ch}

.wizrows{width:100%;border-collapse:collapse}
.wizrows th{
  text-align:left;font-size:11px;font-weight:650;color:var(--muted);
  text-transform:uppercase;letter-spacing:.04em;padding:0 var(--s2) 6px 0;
}
.wizrows td{padding:0 var(--s2) var(--s2) 0;vertical-align:top}
.wizrows td:last-child,.wizrows th:last-child{padding-right:0}

.wizoptions{display:grid;gap:var(--s3)}
.wizoption{
  display:flex;gap:var(--s3);align-items:flex-start;cursor:pointer;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s4);
  transition:border-color .13s ease,background .13s ease;
}
.wizoption:hover{border-color:#CFD9E7}
.wizoption:has(input:checked){border-color:var(--accent);background:var(--accent-wash)}
.wizoption input{width:auto;margin-top:2px;accent-color:var(--accent);flex:0 0 auto}
.wizoption > span{display:grid;gap:4px}
.wizoption b{font-size:13.5px;font-weight:650}
.wizoption .hint{margin-top:0}

.wizactions{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin-top:var(--s6);flex-wrap:wrap}
.wizactions-end{display:flex;align-items:center;gap:var(--s2);margin-left:auto}

.wizerrors{list-style:none;display:grid;gap:3px;font-size:12.5px}

.wizdetails{margin-top:var(--s5);font-size:12.5px}
.wizdetails summary{cursor:pointer;color:var(--ink-2);font-weight:600}
.wizdetails summary:hover{color:var(--ink)}
.wizdetails .wizlist{margin-top:var(--s3)}

/* ---------- what was installed ---------- */
.wizsummary{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s4);margin-top:var(--s6)}
.wizsummary > div{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s4)}
.wizsummary dt{font-size:11px;font-weight:650;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.wizsummary dd{font-size:24px;font-weight:650;letter-spacing:-.02em;margin-top:4px;font-variant-numeric:tabular-nums}

.wizfoot{
  background:var(--brand);padding:var(--s3) var(--s6);
  display:flex;align-items:center;justify-content:flex-end;
}
.wizfoot .themeswitch{margin:0;width:auto}

@media (max-width:640px){
  .wizhead{padding:var(--s3) var(--s4)}
  .wizpanel{padding:var(--s5) var(--s4)}
  .wizsteps{gap:var(--s3);font-size:11px}
  /* Four inputs per row do not fit a phone; the table becomes stacked groups. */
  .wizrows,.wizrows tbody,.wizrows tr,.wizrows td{display:block;width:100%}
  .wizrows thead{display:none}
  .wizrows tr{border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s3);margin-bottom:var(--s3);background:var(--surface)}
  .wizrows td{padding:0 0 var(--s2) 0}
}

/* On a phone the column headings are gone, so each field carries its own —
   four boxes holding nothing but example text is a guessing game. */
@media (max-width:640px){
  .wizrows td::before{
    content:attr(data-label);
    display:block;font-size:10.5px;font-weight:650;color:var(--muted);
    text-transform:uppercase;letter-spacing:.04em;margin-bottom:3px;
  }
}

/* ---------- add staff, on the Team screen ----------
   Shut by default: adding somebody is occasional, and a form standing open
   above the list would push the ranking this screen exists for off the top. */
.addstaff{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);
  margin-bottom:var(--s4);
}
.addstaff > summary{
  list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;
  padding:11px var(--s4);font-size:12.5px;font-weight:650;color:var(--ink-2);
}
.addstaff > summary::-webkit-details-marker{display:none}
.addstaff > summary:hover{color:var(--ink)}
.addstaff > summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--r-sm)}
.addstaff[open] > summary{border-bottom:1px solid var(--line);color:var(--ink)}
.addstaff .plus{
  width:18px;height:18px;border-radius:5px;display:grid;place-items:center;
  background:var(--surface-3);color:var(--accent);font-weight:700;font-size:13px;line-height:1;
}
.addstaff .notefrom{padding:0 var(--s4) var(--s4)}

/* ---------- a form laid out in groups ----------
   For a form with enough fields that one flowing grid turns ragged: the
   labels sit at different heights, a hint pushes its neighbour down a line,
   and the submit button lands wherever the flow left room.

   Each group is titled down the left — what the fields in it are for — and the
   fields themselves sit on an even grid, so a hint grows its own cell and
   moves nothing else. The actions get a line of their own at the end rather
   than a place in the queue. */
/* The form answers to the box it is in, not to the window. The same form sits
   full width under the team list and in a 230px column on a person's page,
   where a three-column row squeezed every input down to three characters. */
.groupform{display:grid;container-type:inline-size}
.groupform .group{
  display:grid;grid-template-columns:184px minmax(0,1fr);gap:var(--s4);
  align-items:start;padding:var(--s4);border-bottom:1px solid var(--line-2);
}
@container (max-width:620px){
  .groupform .group{grid-template-columns:minmax(0,1fr)}
}
.groupform .group h3{font-size:12.5px;font-weight:650;color:var(--ink);padding-top:1px}
.groupform .group h3 span{
  display:block;font-size:11.5px;font-weight:500;color:var(--muted);
  margin-top:4px;line-height:1.45;
}
.groupform .row{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(176px,1fr));
  gap:var(--s4);align-items:start;
}
.groupform .row.two{grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}

/* The component's own label, which had no type of its own and rendered at body
   size — the one thing on the form that was not styled like the rest of it. */
.fieldwrap > label{
  display:block;font-size:11.5px;font-weight:650;color:var(--ink-2);margin-bottom:5px;
}
/* These share a row, so the stacking margin would drop every second field. */
.groupform .fieldwrap + .fieldwrap{margin-top:0}
/* A select renders a couple of pixels shorter than a text input; side by side
   that reads as a mistake. */
.groupform select,.groupform input[type=text],.groupform input[type=email]{height:34px}

/* Four tick boxes in a row where a select would have been: covering two
   teams is the normal case, so this is a set rather than a choice. */
.checkrow{display:flex;flex-wrap:wrap;gap:6px 14px;padding-top:3px}
.checkrow .checkline{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--ink-2);font-weight:500}
.checkrow .checkline input{width:auto;accent-color:var(--accent);margin:0}

.groupform .formfoot{display:flex;justify-content:flex-end;padding:var(--s4)}

@media (max-width:900px){
  .groupform .group{grid-template-columns:minmax(0,1fr)}
}

/* ---------- "what does this column mean" ----------
   A heading has room for one word; the word is rarely the whole story. Shown
   on hover and on focus, so it is reachable without a mouse. */
.whatis{position:relative;display:inline-flex;align-items:center;vertical-align:middle;margin-left:4px;cursor:help}
.whatis-mark{
  width:13px;height:13px;border-radius:50%;display:grid;place-items:center;
  font-size:9px;font-weight:700;font-style:italic;line-height:1;
  color:var(--muted);border:1px solid var(--line);background:var(--surface);
  transition:color .12s ease,border-color .12s ease;
}
.whatis:hover .whatis-mark,.whatis:focus .whatis-mark{color:var(--accent);border-color:var(--accent)}
.whatis:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:50%}

.whatis-note{
  position:absolute;top:calc(100% + 7px);left:50%;transform:translateX(-50%) translateY(-3px);
  z-index:40;width:max-content;max-width:260px;
  background:var(--brand);color:#EEF2F8;border-radius:var(--r-xs);
  padding:7px 9px;font-size:11.5px;font-weight:500;line-height:1.45;letter-spacing:0;
  text-transform:none;text-align:left;white-space:normal;
  box-shadow:0 6px 18px rgba(15,27,50,.22);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .12s ease,transform .12s ease,visibility .12s;
}
.whatis:hover .whatis-note,.whatis:focus .whatis-note{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0)}

/* Near the right edge the bubble would hang off the table; anchor it to the
   right instead of the centre. */
.grid th:last-of-type .whatis-note,.grid th:nth-last-of-type(2) .whatis-note{left:auto;right:0;transform:translateY(-3px)}
.grid th:last-of-type .whatis:hover .whatis-note,
.grid th:last-of-type .whatis:focus .whatis-note,
.grid th:nth-last-of-type(2) .whatis:hover .whatis-note,
.grid th:nth-last-of-type(2) .whatis:focus .whatis-note{transform:translateY(0)}

/* And the same at the end of a metric strip, where the last tile sits
   against the right edge of the page rather than of a table: the strip lets
   the bubble out on purpose, so without this it leaves the document and the
   whole page gains a horizontal scrollbar. */
.kpis .stat:last-child .whatis-note{left:auto;right:0;transform:translateY(-3px)}
.kpis .stat:last-child .whatis:hover .whatis-note,
.kpis .stat:last-child .whatis:focus .whatis-note{transform:translateY(0)}

/* ---------- period picker ---------- */
.period{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.period form{display:flex;align-items:center;gap:6px}
.period input[type=date]{width:auto;font-size:12px;padding:5px 8px}
.period .sep{font-size:11.5px;color:var(--muted)}

/* A sortable heading that also carries a note.
   th.sortable button is display:flex and width:100% so the whole cell is the
   sort target — which pushed the note marker onto a second line underneath the
   title. Where a note is present the button is only as wide as its label, and
   the marker sits beside the title on the same line. */
th.sortable.noted{padding-right:var(--s4);text-align:left}
th.sortable.noted.num{text-align:right}
th.sortable.noted button{display:inline-flex;width:auto;padding-right:2px;vertical-align:middle}
th.sortable.noted .whatis{vertical-align:middle;margin-left:1px}

/* The note has to outrank its neighbours, not just its own cell.
   Every th in a sticky head is position:sticky with the same z-index, so each
   is its own stacking context and the one to its right paints last — taking a
   bite out of the top right corner of any bubble that reaches under it. The
   note's own z-index cannot help: it only orders things inside its own cell.
   Lifting the cell while its note is open is what puts the whole bubble on
   top, and it drops back the moment the pointer leaves. */
.grid thead th:has(.whatis:hover),
.grid thead th:has(.whatis:focus){z-index:50}

/* ---------- person page ----------
   The team ranking links to one person in full, so the name is now a link. */
a.p-name{color:var(--ink);transition:color .12s ease}
a.p-name:hover{color:var(--accent)}

/* A list of records: a title, and a quiet line under it saying where and when.
   Used where a table would be four columns of one-word values. */
.plainlist{list-style:none;display:grid}
.plainlist li{padding:9px var(--s4);border-top:1px solid var(--line-2);display:grid;gap:1px;font-size:12.5px}
.plainlist li:first-child{border-top:0}
.plainlist .secondary{display:block;font-size:11.5px;color:var(--muted)}
.plainlist li.none{color:var(--faint)}
.plainlist li.none .secondary{color:var(--faint)}

/* A list long enough to set the height of the card it is in.

   The tables have had .scroll.tall for this since they were written; a card
   holding a plain list had nothing, so twelve issues ran to seven hundred
   pixels and left the column beside it half the height. Bounded, the card
   says what it holds and the rows are a region you work in.

   No overscroll containment. A panel in a page that also scrolls has to
   hand the wheel back when it reaches its end, or the page stops moving
   wherever the pointer happens to be resting. */
/* And the pager under it, the same control the field app puts under a
   bounded list: the arrows move a page less one row, so the line somebody
   was reading at the fold is still there afterwards, and the readout
   answers the question an arrow leaves open — how much of this is there.
   Script hides it when the list fits, so a card never grows a control
   that cannot do anything. */
.cardnav{
  display:flex;align-items:center;justify-content:flex-end;gap:4px;
  padding:7px 10px;border-top:1px solid var(--line-2);background:var(--surface-2);
}
.cardnav-read{
  margin-right:auto;padding-left:6px;
  font-size:11.5px;font-weight:600;color:var(--muted);font-variant-numeric:tabular-nums;
}
.cardnav-step{
  display:grid;place-items:center;width:28px;height:28px;
  border:1px solid var(--line);border-radius:var(--r-xs);
  background:var(--surface);color:var(--ink-2);cursor:pointer;
}
.cardnav-step svg{width:15px;height:15px}
.cardnav-step:hover:not(:disabled){background:var(--surface-3);color:var(--ink)}
.cardnav-step:disabled{opacity:.38;cursor:default}

/* A list short enough to fit is not a scroll container at all, so it never
   answers for a wheel it cannot act on. Set by the same measurement that
   hides the pager. */
.plainlist.bounded.fits{max-height:none;overflow-y:visible}

.plainlist.bounded{
  max-height:min(46vh,420px);overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent;
}
.plainlist.bounded::-webkit-scrollbar{width:9px}
.plainlist.bounded::-webkit-scrollbar-thumb{background:var(--line);border-radius:9px;border:3px solid var(--surface)}
.plainlist.bounded::-webkit-scrollbar-track{background:transparent}

/* The sell-out shape on a person's page: a sparkline with its span under it. */
.personspark{padding:0 var(--s4) var(--s3);display:grid;gap:4px}
.personspark .spark{display:block;width:100%;height:52px}
.personspark .none{font-size:11px;letter-spacing:.02em}

/* ---------- where somebody is stationed now ----------
   The current location leads the card; the history sits under it. */
.stationnow{display:flex;align-items:center;gap:var(--s3);padding:var(--s4);border-bottom:1px solid var(--line-2)}
.stationnow b{display:block;font-size:13.5px;font-weight:650;letter-spacing:-.01em}
.stationnow .secondary{display:block;font-size:11.5px;color:var(--muted);margin-top:1px}
.stationnow .chip-code{flex:0 0 auto}

/* The same disclosure as "add staff", sitting inside a card rather than above
   a list, so it carries no border of its own. */
.addstaff.flush{border:0;border-radius:0;margin-bottom:0;background:none}
.addstaff.flush > summary{padding:11px var(--s4)}
.addstaff.flush[open] > summary{border-bottom:1px solid var(--line-2)}

/* Location and date sit on one row: they are one decision — where, and from
   when — and the shared .fields grid wraps them apart in a narrow column.
   Aligned at the top, so the two inputs line up whatever hint sits under them. */
.stationfields{grid-template-columns:minmax(0,1fr) minmax(0,150px);align-items:start}
.stationfields .btn{grid-column:1 / -1}
/* .fieldwrap + .fieldwrap carries a top margin for forms that stack. Here the
   two fields share a row, and that margin dropped the second one 12px below
   the first. The grid gap already spaces them. */
.stationfields .fieldwrap + .fieldwrap{margin-top:0}
/* A date input renders a couple of pixels taller than a select; side by side
   that reads as a mistake. */
.stationfields select,.stationfields input[type=date]{height:34px}

@media (max-width:560px){
  .stationfields{grid-template-columns:1fr}
}

/* ---------- picking the lines a promotion runs on ----------
   Fifty-seven lines is too many for a multi-select and too few to need a
   search: grouped by brand in a scroll box, it is a list somebody can read. */
.picker{max-height:340px;overflow-y:auto;padding:var(--s3) var(--s4);display:grid;gap:var(--s4)}
.pickgroup{display:grid;gap:2px}
.pickbrand{
  font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);padding-bottom:4px;border-bottom:1px solid var(--line-2);margin-bottom:4px;
}
.pickline{display:flex;align-items:center;gap:9px;padding:5px 2px;font-size:12.5px;cursor:pointer;border-radius:4px}
.pickline:hover{background:var(--surface-2)}
.pickline input{width:auto;accent-color:var(--accent);flex:0 0 auto}
.pickline .secondary{color:var(--muted);font-size:11.5px;margin-left:5px}

/* ---------- was the promotion actually on shelf ---------- */
.shelfcheck{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:var(--s4);padding:var(--s4)}
.shelfcheck > div{display:flex;flex-direction:column;min-width:0}

/* ---------- up one level ----------
   Sits above the page title in the same place on every screen, quiet enough
   not to compete with it and large enough to hit on a laptop trackpad. */
/* A pill in the rail's own colour, so the control that takes you back to the
   navigation is visibly part of the navigation. */
.upto{
  display:inline-flex;align-items:center;gap:6px;
  font-size:12px;font-weight:600;letter-spacing:-.005em;
  color:#E6ECF5;background:var(--brand);
  padding:6px 13px 6px 10px;margin:0 0 var(--s3);border-radius:999px;
  transition:background .13s ease,transform .1s ease;
}
.upto:hover{background:var(--brand-2);color:#fff}
.upto:active{transform:translateY(.5px)}
.upto:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.upto svg{width:13px;height:13px;display:block;flex:0 0 13px}

/* The rail is --brand-2 in the dark theme; the pill follows it there too. */
[data-theme="dark"] .upto{background:var(--brand-2)}
[data-theme="dark"] .upto:hover{background:var(--surface-3)}

/* The page head follows it, so its own top margin would double the gap. */
.upto + .flash{margin-top:0}

/* ---------- units held at a location ---------- */
/* Two figures: what is out front is what a customer can buy, what is in the
   back is what the person standing there can fix it with. A single total
   hides the difference, which is the thing worth seeing. */
.units{flex:0 0 auto;text-align:right;min-width:112px}
.unitpair{display:flex;align-items:baseline;gap:3px;justify-content:flex-end}
.unitpair b{font-size:16px;font-weight:650;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.unitpair small{font-size:10.5px;color:var(--muted);font-weight:600}
.unitpair i{font-style:normal;color:var(--line);margin:0 1px}
.unitpair.none b{color:var(--muted)}
.unitpair.stuck b:first-child{color:var(--bad)}
.units .sub{display:block;font-size:10.5px;color:var(--muted);margin-top:2px}

/* ---------- hidden means hidden ---------- */
/* The browser enforces this only from its own stylesheet, which any author
   rule setting display outranks — so a row marked hidden was still drawn.
   See the same note in field.css. */
[hidden]{display:none !important}

/* ---------- a resolution that moves stock ---------- */
/* Shown by the reasons that do. Set apart from the note below it because
   it is the one field on this form that changes a figure everybody else
   reads, and it should not look like another optional box. */
/* The panel is the lighter step, not the field.

   It was --surface-2, which is exactly what the theme's own rule for a
   number input gives the input — so the box sat on a panel of its own
   colour and read as empty space with a label above it. The field keeps
   whatever the application dresses form controls in; the panel moves out
   of its way. */
.movesstock{
  margin:var(--s4) 0 0;padding:var(--s4);
  background:var(--surface-3);border:1px solid var(--line);border-radius:var(--r-xs);
}
.movesstock label{display:block;margin:0 0 6px;font-weight:600}
.movesstock input{
  width:140px;font-size:15px;font-weight:650;font-variant-numeric:tabular-nums;
}
.movesnote{margin:8px 0 0;font-size:12.5px;color:var(--muted);line-height:1.5}
