/* ============================================================================
   The Design Hub - application shell and components
   Requires tokens.css to be loaded first.

   Structure: a fixed three-column grid that never scrolls as a whole. The rail,
   the content column and the detail panel each own their own scroll region, so
   the nav and the shift header stay put while a 200-row queue scrolls.
   ========================================================================== */

:root {
  --rail-w: 232px;
  --panel-w: 380px;
  /* Tall enough for a 28px h1 with its date sub-line under it. */
  --topbar-h: 68px;
}

html, body { height: 100%; }
body { overflow: hidden; display: flex; flex-direction: column; height: 100vh; }

/* Anything stacked above .app (e.g. the demo banner) must be flex:none, so the
   app still gets exactly the remaining height and never overflows the viewport.
   Using height:100vh here instead would push the detail panel's footer off
   screen by the banner's height. */
.app {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr) 0;
  transition: grid-template-columns var(--t-base) var(--ease);
}
.app.panel-open { grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--panel-w); }
.app:not(.panel-open) .detail { display: none; }

/* ==========================================================================
   Rail
   ========================================================================== */

/* Flat navy, no gradient. A gradient on a full-height column reads as a
   lighting effect on a surface that is supposed to be the one constant in the
   product; the colour tokens live in tokens.css so the slab can share them. */
.rail {
  background: var(--rail-bg);
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--rail-border);
}

.rail-head {
  display: flex; align-items: center; gap: 10px;
  padding: 0 var(--sp-4);
  height: var(--topbar-h);
  flex: none;
  border-bottom: 1px solid var(--rail-border);
}
/* The RRD mark is a single near-black colour and must not be recoloured, so on
   navy it sits on its own small white tile - the way it appears on printed
   work. The tile is the licence-safe way to keep the artwork untouched. */
.mark {
  width: 32px; height: 32px; border-radius: 8px; flex: none;
  background: #FFFFFF;
  display: grid; place-items: center;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .30);
}
.mark img { display: block; width: 24px; height: 24px; }
.rail-head .wm { min-width: 0; }
.rail-head .wm b {
  display: block; font-size: var(--fs-base); font-weight: 600;
  color: var(--rail-text-strong); letter-spacing: -.01em; line-height: 1.2;
}
.rail-head .wm span {
  display: block; font-size: var(--fs-xs); letter-spacing: .04em;
  color: var(--rail-muted); line-height: 1.3;
}

.rail-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: var(--sp-4) 0; }

.navgroup { padding: 0 var(--sp-3); margin-bottom: var(--sp-5); }
.navgroup:last-child { margin-bottom: 0; }
/* 12px is the floor for anything a person is meant to read. The old 10px
   headings were decoration pretending to be structure. */
.navgroup > h3 {
  margin: 0 0 6px; padding: 0 12px;
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--rail-muted);
}
.navitem {
  display: flex; align-items: center; gap: 10px;
  min-height: 34px;
  padding: 7px 12px; border-radius: var(--r-md);
  color: var(--rail-text); text-decoration: none;
  font-size: var(--fs-base); font-weight: 500;
  position: relative;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.navitem:hover { background: var(--rail-bg-2); color: var(--rail-text-strong); }
.navitem svg { width: 18px; height: 18px; flex: none; }
.navitem.on {
  background: var(--rail-bg-2); color: var(--rail-text-strong); font-weight: 600;
}
/* The marker bar is the only place --rail-accent appears: azure on navy is not
   a text colour, but as a 3px edge it is exactly the right amount of signal. */
.navitem.on::before {
  content: ""; position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 20px; border-radius: 0 2px 2px 0;
  background: var(--rail-accent);
}
.navitem .count {
  margin-left: auto; font-size: var(--fs-xs); font-weight: 600;
  font-variant-numeric: tabular-nums;
  /* Solid, not a white wash: a 13% white tint on navy left the digits at
     about 2:1 and the count is a number people act on. */
  background: var(--rail-accent); color: #002449;
  padding: 1px 7px; border-radius: var(--r-pill);
}
.navitem .count.hot { background: #FF9A8F; color: #3A0F0A; }

.rail-foot { flex: none; border-top: 1px solid var(--rail-border); padding: var(--sp-3); }
/* Named for where it lives, not for who it is: a generic `.me` also matched the
   current member's <tr> on People and turned that table row into a flex box. */
.rail-user { display: flex; align-items: center; gap: 9px; }
.rail-user .avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: grid; place-items: center; text-decoration: none;
  font-size: var(--fs-xs); font-weight: 700; color: var(--avatar-fg);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .16);
}
.rail-user .who { min-width: 0; flex: 1; }
/* The name is a link to that member's own page now, so it has to look like
   one on hover without looking like a link everywhere else in the rail. */
.rail-user .who a.mine { display: block; text-decoration: none; color: inherit; }
.rail-user .who b {
  display: block; font-size: var(--fs-sm); font-weight: 600;
  color: var(--rail-text-strong); line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-user .who a.mine:hover b { text-decoration: underline; }
.rail-user .who span { display: block; font-size: var(--fs-xs); color: var(--rail-muted); }
.rail-user .who span a:hover { text-decoration: underline; }
/* the theme toggle is a form, which must not add a line box of its own */
.rail-user .themeform { display: flex; flex: none; margin: 0; }
.iconbtn {
  width: 32px; height: 32px; flex: none; cursor: pointer;
  display: grid; place-items: center;
  border: 1px solid transparent; border-radius: var(--r-md);
  background: transparent; color: var(--rail-text);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.iconbtn:hover { background: var(--rail-bg-2); color: var(--rail-text-strong); }
.iconbtn svg { width: 17px; height: 17px; }

/* The narrow-screen menu toggle. It is a <details> with nothing inside it,
   used purely as a no-JavaScript switch for the nav list that follows it in
   the DOM (see the narrow-screen block at the foot of this file). The revealed
   list is the very next thing in reading order, so the disclosure still makes
   sense to a screen reader. Hidden entirely on a wide screen. */
.railmenu { display: none; }
.railmenu > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  list-style: none; min-height: 40px; padding: 0 12px;
  border-radius: var(--r-md);
  color: var(--rail-text); font-size: var(--fs-base); font-weight: 600;
}
.railmenu > summary::-webkit-details-marker { display: none; }
.railmenu > summary:hover { background: var(--rail-bg-2); color: var(--rail-text-strong); }
.railmenu > summary:focus-visible { outline: 2px solid var(--rail-accent); outline-offset: 2px; }
.railmenu[open] > summary { background: var(--rail-bg-2); color: var(--rail-text-strong); }

/* ==========================================================================
   Main column
   ========================================================================== */

.main { display: flex; flex-direction: column; min-width: 0; min-height: 0;
        background: var(--bg);
        container-type: inline-size; container-name: main; }

.topbar {
  height: var(--topbar-h); flex: none;
  display: flex; align-items: center; gap: var(--sp-4);
  padding: 0 var(--sp-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
/* The heading block is the one that yields space: it truncates so the search,
   shift chip and primary action keep their full size. */
.topbar .title { min-width: 0; flex: 0 1 auto; }
.topbar h1 {
  margin: 0; font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -.02em;
  line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 13px, not 12: this line carries the shift dates, which people read. */
.topbar .sub {
  font-size: var(--fs-sm); color: var(--text-2); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.search {
  margin-left: auto; position: relative; flex: 0 1 236px; min-width: 132px;
  display: flex; align-items: center;
}
.topbar .sub .short { display: none; }
@container main (max-width: 940px) {
  .topbar .sub .full  { display: none; }
  .topbar .sub .short { display: inline; }
  .search { flex-basis: 178px; }
}
.search svg {
  position: absolute; left: 9px; width: 14px; height: 14px;
  color: var(--text-3); pointer-events: none;
}
.search input {
  font: inherit; font-size: var(--fs-sm); width: 100%;
  padding: 6px 46px 6px 29px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--surface-2); color: var(--text);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.search input::placeholder { color: var(--text-3); }
.search input:hover { background: var(--surface); }
.search input:focus { background: var(--surface); border-color: var(--accent); outline: none; }
.search kbd {
  position: absolute; right: 7px;
  font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-3); background: var(--surface-3);
  border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: var(--r-sm); padding: 1px 5px;
}

/* live shift indicator */
.shiftchip {
  display: flex; align-items: center; gap: 8px; flex: none;
  padding: 5px 11px 5px 9px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface-2); white-space: nowrap;
}
.shiftchip .beat {
  width: 7px; height: 7px; border-radius: 50%; background: var(--st-completed-fg);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--st-completed-fg) 22%, transparent);
  animation: beat 2.6s var(--ease) infinite;
}
@keyframes beat { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.shiftchip b { font-size: var(--fs-xs); font-weight: 650; }
.shiftchip span { font-size: var(--fs-xs); color: var(--text-3);
                  font-variant-numeric: tabular-nums; }

.scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden;
           container-type: inline-size; container-name: content; }
.page { padding: var(--sp-5); max-width: 1180px; }

/* ==========================================================================
   Buttons
   ========================================================================== */

/* Hover changes colour and nothing else. A button that lifts or sinks on
   hover moves its own hit target out from under a shaky hand, which on a
   dense queue of small actions is the difference between claiming a job and
   claiming the one below it. */
.btn {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; text-decoration: none;
  min-height: 32px; padding: 6px 12px; border-radius: var(--r-md);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); white-space: nowrap;
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.btn:hover { background: var(--surface-2); border-color: var(--text-3); }
.btn svg { width: 16px; height: 16px; }
/* Still 24px tall and 24px wide at its smallest, per WCAG 2.5.8. */
.btn.sm { font-size: var(--fs-xs); min-height: 26px; padding: 3px 9px; }
.btn.sm svg { width: 14px; height: 14px; }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--text-inverse);
}
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* The quiet secondary: present, outlined, but plainly not the main move.
   One filled azure button per screen is the whole emphasis budget. */
.btn.quiet { background: transparent; color: var(--text-2); }
.btn.quiet:hover { background: var(--surface-2); color: var(--text); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text); }
.btn.danger { color: var(--err-fg); border-color: var(--ur-urgent-bd); background: var(--err-bg); }
.btn.block { width: 100%; justify-content: center; }
.btn.lg { font-size: var(--fs-md); font-weight: 600; min-height: 44px; padding: 10px 20px; }
.btn.lg svg { width: 18px; height: 18px; }

/* ==========================================================================
   Pills - status, urgency, owner
   ========================================================================== */

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px 2px 7px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 600; border: 1px solid;
  white-space: nowrap; line-height: 1.5;
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%;
             background: currentColor; flex: none; }
.pill.bare { border-color: transparent; background: transparent; padding-left: 0; }
.st-queued    { color: var(--st-queued-fg);    background: var(--st-queued-bg);    border-color: var(--st-queued-bd); }
.st-progress  { color: var(--st-progress-fg);  background: var(--st-progress-bg);  border-color: var(--st-progress-bd); }
.st-paused    { color: var(--st-paused-fg);    background: var(--st-paused-bg);    border-color: var(--st-paused-bd); }
.st-hold      { color: var(--st-hold-fg);      background: var(--st-hold-bg);      border-color: var(--st-hold-bd); }
.st-proofing  { color: var(--st-proofing-fg);  background: var(--st-proofing-bg);  border-color: var(--st-proofing-bd); }
.st-completed { color: var(--st-completed-fg); background: var(--st-completed-bg); border-color: var(--st-completed-bd); }
.st-cancelled { color: var(--st-cancelled-fg); background: var(--st-cancelled-bg); border-color: var(--st-cancelled-bd); }
.ur-urgent    { color: var(--ur-urgent-fg);    background: var(--ur-urgent-bg);    border-color: var(--ur-urgent-bd); }
.ur-today     { color: var(--ur-today-fg);     background: var(--ur-today-bg);     border-color: var(--ur-today-bd); }
.ur-tomorrow  { color: var(--ur-tomorrow-fg);  background: var(--ur-tomorrow-bg);  border-color: var(--ur-tomorrow-bd); }
.ur-nextweek  { color: var(--ur-nextweek-fg);  background: var(--ur-nextweek-bg);  border-color: var(--ur-nextweek-bd); }
.ur-unknown   { color: var(--ur-unknown-fg);   background: var(--ur-unknown-bg);   border-color: var(--ur-unknown-bd); }

.owner {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px 2px 3px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 600; white-space: nowrap;
}
.owner .av {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font-size: 9px; font-weight: 700; color: var(--avatar-fg);
}
.owner.none {
  color: var(--text-3); background: transparent;
  border: 1px dashed var(--border-strong); padding: 2px 9px;
}

/* ==========================================================================
   Stat strip
   ========================================================================== */

.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3);
         margin-bottom: var(--sp-4); }
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--shadow-sm);
}
.stat .lbl {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-3);
}
.stat .val {
  font-size: var(--fs-2xl); font-weight: 700; letter-spacing: -.03em;
  line-height: 1.15; margin-top: 3px; font-variant-numeric: tabular-nums;
}
.stat .val.mono { font-family: var(--font-mono); font-size: var(--fs-xl); }
.stat .foot { font-size: var(--fs-xs); color: var(--text-3); margin-top: 1px; }
.stat .foot em { font-style: normal; color: var(--ok-fg); font-weight: 650; }

/* ==========================================================================
   Panels and the job table
   ========================================================================== */

.panelcard {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-4); overflow: hidden;
}
.panelcard > header {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border); background: var(--surface);
}
.panelcard > header h2 {
  margin: 0; font-size: var(--fs-base); font-weight: 650; letter-spacing: -.01em;
}
.panelcard > header .n {
  font-size: var(--fs-xs); font-weight: 650; color: var(--text-3);
  background: var(--surface-3); border-radius: var(--r-pill); padding: 1px 8px;
  font-variant-numeric: tabular-nums;
}
.panelcard > header .spacer { margin-left: auto; }
.panelcard.attention { border-color: var(--ur-urgent-bd); }
.panelcard.attention > header { background: var(--ur-urgent-bg); }
.panelcard.attention > header h2 { color: var(--ur-urgent-fg); }

/* A segmented filter. The <a> form is the one that matters: each segment is a
   real ?view= link, so it works with scripting off, is bookmarkable, and can
   carry its own count. The <button> form stays for the places still using it. */
.seg { display: inline-flex; flex-wrap: wrap; border: 1px solid var(--border-strong);
       border-radius: var(--r-md); overflow: hidden; }
.seg button,
.seg a {
  font: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 30px; padding: 4px 11px; border: 0; text-decoration: none;
  background: var(--surface); color: var(--text-2);
  border-right: 1px solid var(--border);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.seg button:last-child, .seg a:last-child { border-right: 0; }
.seg button:hover, .seg a:hover { background: var(--surface-2); color: var(--text); }
.seg button[aria-pressed="true"],
.seg a[aria-current="page"] { background: var(--accent); color: var(--text-inverse); }
.seg a[aria-current="page"]:hover { background: var(--accent); color: var(--text-inverse); }
.seg .n { font-variant-numeric: tabular-nums; opacity: .8; }

table.jobs { width: 100%; border-collapse: collapse; table-layout: fixed; }
col.c-ref    { width: 120px; }
col.c-subj   { width: auto;  }   /* absorbs all remaining width */
col.c-status { width: 118px; }
col.c-due    { width: 150px; }
col.c-time   { width: 78px;  }
col.c-act    { width: 128px; }
/* A docket, not a card: the header is a line of small caps over a hairline,
   with no fill behind it. The grey header band was card chrome on a table
   that is already inside one, and it made every list look like a widget. */
table.jobs thead th {
  text-align: left; padding: 7px var(--sp-3);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-3);
  background: transparent; border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}
table.jobs tbody td {
  padding: 9px var(--sp-3); border-bottom: 1px solid var(--divider);
  vertical-align: middle; font-size: var(--fs-sm);
}
table.jobs tbody tr:last-child td { border-bottom: 0; }
table.jobs th:first-child, table.jobs td:first-child { padding-left: var(--sp-4); }
table.jobs th:last-child,  table.jobs td:last-child  { padding-right: var(--sp-4); }
table.jobs tbody tr { transition: background var(--t-fast) var(--ease); }
table.jobs tbody tr:hover { background: var(--surface-2); cursor: pointer; }
table.jobs tbody tr.sel { background: var(--accent-quiet); }
table.jobs tbody tr.sel td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

td.ref { white-space: nowrap; }
td.ref b {
  font-family: var(--font-mono); font-weight: 600; font-size: var(--fs-sm);
  letter-spacing: -.01em;
}
td.ref .rev {
  display: inline-block; margin-left: 5px;
  font-size: var(--fs-xs); font-weight: 650; color: var(--text-3);
  background: var(--surface-3); border-radius: var(--r-sm); padding: 0 4px;
}
td.subj { min-width: 0; }
td.subj .nm, td.subj .ds {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
td.subj .nm { font-weight: 550; }
td.subj .ds { font-size: var(--fs-xs); color: var(--text-3); margin-top: 1px; }
td.time { font-family: var(--font-mono); font-variant-numeric: tabular-nums;
          color: var(--text-2); white-space: nowrap; }
td.time.live { color: var(--st-progress-fg); font-weight: 650; }
td.time.none { color: var(--text-3); }
td.acts { text-align: right; white-space: nowrap; width: 1%; }
td.acts .row { display: flex; gap: 5px; justify-content: flex-end;
               opacity: 0; transition: opacity var(--t-fast) var(--ease); }
tr:hover td.acts .row, tr:focus-within td.acts .row { opacity: 1; }
tr.sel td.acts .row { opacity: 1; }

@container content (max-width: 820px) {
  col.c-status { width: 116px; }
  col.c-due    { width: 136px; }
  col.c-time   { width: 0; }
  col.c-act    { width: 126px; }
  /* Collapse, do NOT display:none - the cell must stay in the row so every
     remaining cell keeps mapping to its own <col>. */
  table.jobs th.h-time, table.jobs td.time {
    padding-left: 0; padding-right: 0;
    max-width: 0; overflow: hidden; visibility: hidden;
  }
}

/* One quiet line. An empty queue is good news on this team, not an occasion
   for a centred illustration and a paragraph of encouragement. */
.empty { padding: var(--sp-4); text-align: left; }
.empty p { margin: 0; font-size: var(--fs-sm); color: var(--text-3); }
.empty p + p { margin-top: var(--sp-2); }

/* ==========================================================================
   Detail panel
   ========================================================================== */

.detail {
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-panel);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
  /* Short enough to read as "this arrived from the right" and not as a wait.
     Purely decorative: the panel is laid out and clickable from the first
     frame, so nothing depends on the animation finishing. */
  animation: detail-in 200ms cubic-bezier(.2, .7, .3, 1) both;
}
@keyframes detail-in {
  from { opacity: 0; transform: translateX(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .detail { animation: none; }
}
.detail > header {
  flex: none; padding: var(--sp-4); border-bottom: 1px solid var(--border);
}
.detail .dtop { display: flex; align-items: flex-start; gap: var(--sp-3); }
.detail .dref {
  margin: 0;                                   /* it is the panel's <h2> now */
  font-family: var(--font-mono); font-size: var(--fs-lg); font-weight: 650;
  letter-spacing: -.01em;
}
/* It takes focus on open so keyboard users land in the panel. The ring hugs the
   reference rather than the full header width, which otherwise reads as an
   input box around the title. */
.detail .dref:focus-visible {
  display: inline-block; outline: 2px solid var(--focus-ring); outline-offset: 3px;
  border-radius: var(--r-sm);
}
.detail .dref:focus:not(:focus-visible) { outline: none; }
.detail .dwho { font-size: var(--fs-sm); color: var(--text-2); margin-top: 2px; }
.detail .dmeta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--sp-3); }
.detail .dscroll { flex: 1; min-height: 0; overflow-y: auto; }
.detail .dfoot {
  flex: none; padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border); background: var(--surface-2);
  display: flex; gap: 7px;
}

.tabs { display: flex; gap: 2px; padding: 0 var(--sp-4);
        border-bottom: 1px solid var(--border); background: var(--surface); }
.tabs button {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  padding: 9px 11px; border: 0; background: none; color: var(--text-3);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs button:hover { color: var(--text); }
.tabs button[aria-selected="true"] { color: var(--accent-text);
                                     border-bottom-color: var(--accent); }

.kv { padding: var(--sp-4); display: grid; grid-template-columns: 96px 1fr;
      gap: 9px var(--sp-3); align-items: baseline; }
/* Inside a titled section the section already supplies the padding. */
.section > .kv { padding: 0; }
.kv dt { font-size: var(--fs-xs); color: var(--text-3); font-weight: 600; }
.kv dd { margin: 0; font-size: var(--fs-sm); }
.kv dd.unknown { color: var(--text-3); font-style: italic; }

.section { padding: var(--sp-4); border-top: 1px solid var(--divider); }
/* The section nav scrolls to these, so they must clear the sticky action bar
   instead of landing underneath it. */
.section { scroll-margin-top: 92px; }
.section > h3,
.section > h4 {
  margin: 0 0 var(--sp-3); font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--text-2);
}

.feed { position: relative; padding-left: 21px; }
.feed::before {
  content: ""; position: absolute; left: 5px; top: 4px; bottom: 6px;
  width: 1px; background: var(--border);
}
.feed .ev { position: relative; padding-bottom: var(--sp-4); }
.feed .ev:last-child { padding-bottom: 0; }
.feed .ev::before {
  content: ""; position: absolute; left: -20px; top: 4px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--border-strong);
}
.feed .ev.now::before { border-color: var(--accent); background: var(--accent); }
.feed .ev .w { font-size: var(--fs-sm); }
.feed .ev .w b { font-weight: 650; }
.feed .ev .t { font-size: var(--fs-xs); color: var(--text-3); margin-top: 1px;
               font-variant-numeric: tabular-nums; }

.sess { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.sess td { padding: 5px 0; border-bottom: 1px solid var(--divider); }
.sess tr:last-child td { border-bottom: 0; }
.sess td.d { font-family: var(--font-mono); font-variant-numeric: tabular-nums;
             text-align: right; color: var(--text-2); }
.sess tfoot td { font-weight: 700; border-top: 1px solid var(--border-strong);
                 border-bottom: 0; padding-top: 7px; }

/* ==========================================================================
   Utility
   ========================================================================== */

/* Shared form-control look, written with :where() so it carries ZERO
   specificity. Every page style, and in particular grid.css's in-cell editors,
   still wins without needing an override - this is a default, not a rule. */
.page :where(input[type="text"], input[type="search"], input[type="email"],
             input[type="number"], input[type="date"], input[type="time"],
             input[type="password"], select, textarea) {
  font: inherit; font-size: var(--fs-sm); color: var(--text);
  padding: 7px 10px; min-height: 32px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color var(--t-fast) var(--ease);
}
.page :where(input, select, textarea)::placeholder { color: var(--text-3); }
.page :where(input, select, textarea):focus { border-color: var(--accent); }
.page :where(textarea) { line-height: var(--lh-normal); resize: vertical; }
.page :where(label.fld) {
  display: block; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-2); margin-bottom: 5px;
}

/* Flash messages. One line, tinted, no icon: the words say what happened.
   Some pages still declare their own copy of this in a page <style>; those
   come later in the cascade and win, which is harmless - they agree. */
.flash {
  margin: var(--sp-4) var(--sp-5) 0;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md);
  font-size: var(--fs-sm); font-weight: 600;
}
.flash.ok  { background: var(--ok-bg);  color: var(--ok-fg);  border: 1px solid var(--st-completed-bd); }
.flash.err { background: var(--err-bg); color: var(--err-fg); border: 1px solid var(--ur-urgent-bd); }
.flash form { display: inline; margin-left: var(--sp-3); }

.row { display: flex; align-items: center; gap: var(--sp-2); }
.mt4 { margin-top: var(--sp-4); }
.muted { color: var(--text-3); }
.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.demobar {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-xs); font-weight: 600;
  color: var(--warn-fg); background: var(--warn-bg);
  border-bottom: 1px solid var(--ur-tomorrow-bd);
  padding: 6px var(--sp-5);
}

/* Under ~1400px a third grid column starves the job table: the requestor name
   was truncating to "Sample Requesto...". So the detail panel stops taking
   layout space and overlays the content instead, which keeps the table at full
   width. Standard behaviour for a detail pane, and it removes the need to shave
   pixels off every column. */
@media (max-width: 1400px) {
  .app.panel-open { grid-template-columns: var(--rail-w) minmax(0, 1fr) 0; }
  .detail {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: min(var(--panel-w), 92vw); z-index: 30;
  }
}
@media (max-width: 1040px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
}

/* ==========================================================================
   Detail panel actions
   ========================================================================== */

/* The action area is the first thing in the scroll area and stays pinned to the
   top of it, so the decision you opened the job to make is still one click away
   while you are reading the history further down. */
.detail .dacts {
  position: sticky; top: 0; z-index: 2;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface); border-bottom: 1px solid var(--border);
  /* A sticky box taller than the scroll area would pin its own bottom out of
     reach, so with "More" open on a short screen it scrolls itself instead. */
  max-height: 72vh; overflow-y: auto;
}
.detail .actrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.detail .actrow form { margin: 0; }

/* Complete keeps its credit choice beside it rather than hidden behind the
   button: who gets the credit is a real decision, not a default to bury. */
.detail .cact {
  display: flex; align-items: center; gap: 5px;
  margin-left: auto;                     /* the end of the row, away from Start */
}
.detail .cact label {
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-3);
}
.detail .cact select {
  font: inherit; font-size: var(--fs-xs); padding: 4px 6px; max-width: 110px;
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  background: var(--surface); color: var(--text);
}

.detail .dmore { margin-top: var(--sp-3); }
.detail .dmore > summary {
  cursor: pointer; display: inline-block; list-style: none;
  font-size: var(--fs-xs); font-weight: 650; color: var(--text-2);
  padding: 4px 9px; border: 1px solid var(--border-strong);
  border-radius: var(--r-pill); background: var(--surface);
}
.detail .dmore > summary::-webkit-details-marker { display: none; }
.detail .dmore > summary::after { content: " ▾"; }
.detail .dmore[open] > summary::after { content: " ▴"; }
.detail .dmore > summary:hover { color: var(--text); }
.detail .dmore > summary:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
}
.detail .dmorebody { padding-top: var(--sp-3); }

.detail .dsecnav {
  display: flex; gap: var(--sp-3); padding: 9px var(--sp-4);
  border-bottom: 1px solid var(--divider); background: var(--surface-2);
}
.detail .dsecnav a {
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-2);
  text-decoration: none;
}
.detail .dsecnav a:hover { color: var(--accent-text); }

.detail .actform {
  margin-bottom: var(--sp-4); padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--divider);
}
.detail .actform:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.detail .actform label {
  display: block; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-2); margin-bottom: 5px;
}
.detail .actform input[type="text"],
.detail .actform select {
  width: 100%; font: inherit; font-size: var(--fs-sm); padding: 7px 10px;
  margin-bottom: 7px; border: 1px solid var(--border-strong);
  border-radius: var(--r-md); background: var(--surface); color: var(--text);
}
.detail .actform input[type="text"]:focus,
.detail .actform select:focus { border-color: var(--accent); outline: none; }

/* ==========================================================================
   Clickable rows
   The whole row opens the detail panel, but the link inside keeps it reachable
   by keyboard and working without JavaScript.
   ========================================================================== */

tr[data-href] { cursor: pointer; }
a.rowlink { display: block; color: inherit; text-decoration: none; }
a.rowlink:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ==========================================================================
   Narrow screens (<= 860px)
   ----------------------------------------------------------------------------
   The rail becomes a top bar with a "Menu" disclosure. What it replaced was a
   horizontally-scrolling strip of nav items, which hid whichever destinations
   did not fit behind a scrollbar most phones do not draw - so on a 390px
   screen, Reports and Settings were effectively gone.

   The disclosure is a <details>, so it opens and closes with no JavaScript.
   The list it reveals is its next sibling rather than its content, because a
   closed <details> hides its own content in the user-agent shadow tree and
   cannot reliably be forced open again with CSS on a wide screen.
   ========================================================================== */

@media (max-width: 860px) {
  .app,
  .app.panel-open { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }

  .rail {
    flex-direction: row; flex-wrap: wrap; align-items: center;
    padding: 0 var(--sp-3);
    border-right: 0; border-bottom: 1px solid var(--rail-border);
  }
  .rail-head { border-bottom: 0; flex: 1; min-width: 0; padding: 0; height: 52px; }
  .rail-head .wm b { font-size: var(--fs-sm); }
  .rail-head .wm span { display: none; }

  .railmenu { display: block; flex: none; order: 2; }

  /* Closed by default; the nav list below it is revealed by the open state. */
  .rail-scroll {
    display: none; order: 4; flex-basis: 100%; width: 100%;
    max-height: 60vh; padding: var(--sp-2) 0 var(--sp-3);
    border-top: 1px solid var(--rail-border);
  }
  .railmenu[open] ~ .rail-scroll { display: block; }

  .navgroup { padding: 0; margin-bottom: var(--sp-3); }
  .navgroup > h3 { padding: 0 var(--sp-2); }
  .navitem { min-height: 44px; padding: 10px var(--sp-2); }

  .rail-foot {
    border-top: 0; flex: none; order: 3; padding: 0 0 0 var(--sp-2);
  }
  .rail-user .who { display: none; }

  /* The detail panel covers the content instead of splitting it. */
  .detail { position: fixed; inset: 0; width: 100%; z-index: 50; }

  .stats { grid-template-columns: repeat(2, 1fr); }
  .topbar { height: auto; padding: var(--sp-3) var(--sp-4); flex-wrap: wrap; }
  .topbar h1 { font-size: var(--fs-xl); white-space: normal; }
  .search { flex-basis: 100%; margin-left: 0; order: 3; }
  .page { padding: var(--sp-4); }
}

/* Very narrow: the due column goes too, leaving Metrics ID, requestor,
   status and the action. Everything dropped is still in the detail panel. */
@container content (max-width: 560px) {
  col.c-due { width: 0; }
  table.jobs th.h-due, table.jobs td.due {
    padding-left: 0; padding-right: 0;
    max-width: 0; overflow: hidden; visibility: hidden;
  }
}

/* ==========================================================================
   Quick search and command entry (Ctrl+K)
   --------------------------------------------------------------------------
   The <dialog> is only ever opened by a script, so none of this is reachable
   with JavaScript off - and it does not need to be: the topbar search form it
   sits on top of is the feature, and this is a faster way to reach it.
   ========================================================================== */

.cmdk {
  width: min(560px, calc(100vw - 32px));
  padding: 0; border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); background: var(--surface);
  color: var(--text); box-shadow: var(--shadow-lg);
  margin-top: 12vh;                       /* near the top, where the eye is */
}
.cmdk::backdrop { background: rgba(0, 20, 40, .42); }
.cmdk-in {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4); border-bottom: 1px solid var(--border);
}
.cmdk-in svg { color: var(--text-3); flex: none; }
.cmdk-in input {
  font: inherit; font-size: var(--fs-md); flex: 1; min-width: 0;
  border: 0; background: none; color: var(--text); outline: none;
}
.cmdk-in input::placeholder { color: var(--text-3); }

.cmdk-list {
  list-style: none; margin: 0; padding: var(--sp-2) 0;
  max-height: min(52vh, 420px); overflow-y: auto;
}
.cmdk-list li {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 8px var(--sp-4); min-height: 34px;
  font-size: var(--fs-sm); cursor: pointer;
}
/* Colour only. The active row must never move under a pointer that is on its
   way to click it. */
.cmdk-list li.on { background: var(--surface-3); }
.cmdk-list li.on .cmdk-lbl { font-weight: 650; }
.cmdk-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
            white-space: nowrap; }
.cmdk-hint { flex: none; font-size: var(--fs-xs); color: var(--text-3); }
.cmdk-foot {
  margin: 0; padding: 7px var(--sp-4); border-top: 1px solid var(--border);
  font-size: var(--fs-xs); color: var(--text-3);
}
.cmdk-foot kbd {
  font-family: var(--font-sans); font-size: var(--fs-xs); font-weight: 600;
  background: var(--surface-3); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 0 4px; margin-right: 2px;
}

/* ==========================================================================
   Completion feedback
   --------------------------------------------------------------------------
   The check is drawn, once, and ONLY in a flash the server produced after it
   committed the completion - the markup does not exist otherwise. Nothing
   waits for it: the sentence beside it is the actual confirmation.
   ========================================================================== */

.flash.fl-done { display: flex; align-items: center; gap: 10px; }
.fl-check { flex: none; }
.fl-check path {
  stroke-dasharray: 30; stroke-dashoffset: 30;
  animation: fl-draw 300ms var(--ease) forwards;
}
@keyframes fl-draw { to { stroke-dashoffset: 0; } }

/* ==========================================================================
   Density: comfortable (default) / compact
   --------------------------------------------------------------------------
   Set server-side on <html> from the dh_density cookie, so the rows are the
   right height in the first paint. Compact tightens SPACE only: no text drops
   below 12px and no target below 24x24.
   ========================================================================== */

html[data-density="compact"] { --topbar-h: 56px; }
html[data-density="compact"] .page { padding: var(--sp-4); }
html[data-density="compact"] .topbar { gap: var(--sp-3); }

@media (prefers-reduced-motion: reduce) {
  /* The check is simply drawn already. Nothing waits for the stroke. */
  .fl-check path { animation: none; stroke-dashoffset: 0; }
}
