/* Consultant Hours — scoped styles for the weekly hours entry page.
 *
 * Every selector is prefixed .hub-ch-* (or scoped under .hub-ch) so this file
 * cannot reach anything the rest of the Hub owns. Design tokens come from
 * styles.css; nothing here re-declares a brand colour or a spacing value.
 *
 * DELIBERATELY SMALL, like hub-grant-metrics.css. The page is built from shared
 * furniture that already exists and is already reviewed: .hub-section,
 * .hub-section-head, .hub-card, .hub-metric, .hub-grid-*, .hub-status-banner,
 * .hub-empty, .hub-state-card, .hub-hint, .hub-muted, .hub-btn, .hub-select.
 * What follows is only what has no equivalent: the term list and the entry grid.
 *
 * THE GRID IS THE WHOLE FILE. It is up to 31 rows by 18 columns of number
 * inputs, which is more data-entry surface than anywhere else in this
 * application, and three things about it are load-bearing rather than cosmetic:
 *
 *   1. THE NAME COLUMN IS STICKY. Scrolled to week 14 with the names gone, every
 *      row looks the same and hours land on the wrong person.
 *   2. A FILLED CELL LOOKS DIFFERENT FROM AN EMPTY ONE, because "0" and "" are
 *      different answers and the page's whole point is that they never blur
 *      together. A recorded zero gets the filled treatment: it IS an answer.
 *   3. A FAILED SAVE IS RED AND STAYS. The typed value is kept in the box, so
 *      the only thing telling somebody it did not save is this styling.
 *
 * No @import and no url() to a remote host: the live CSP is style-src 'self'.
 * For the same reason no value here is ever set from an inline style attribute —
 * an inline style is dropped silently, which on this page would mean a cell that
 * failed to save looking exactly like one that succeeded.
 */

.hub-ch { color: var(--text); }

/* --- the term list -------------------------------------------------------- */
.hub-ch-terms { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.hub-ch-term {
  display: grid;
  gap: 2px;
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line-2);
}
.hub-ch-term:last-child { border-bottom: 0; }
.hub-ch-term-btn {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  width: 100%; padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.hub-ch-term-btn:hover .hub-ch-term-name { text-decoration: underline; }
.hub-ch-term-name { font-weight: 600; }
.hub-ch-term-hours { font-variant-numeric: tabular-nums; font-weight: 600; }
.hub-ch-term-note { font-size: 12.5px; line-height: 1.5; color: var(--text2); }
.hub-ch-term.is-selected .hub-ch-term-name { color: var(--accent, var(--text)); }

/* --- the entry grid ------------------------------------------------------- */
/* Eighteen week columns will not fit a laptop, so the grid scrolls INSIDE this
   box and the page itself never scrolls sideways. */
.hub-ch-scroll {
  overflow: auto;
  max-height: 70vh;
  border: 1px solid var(--line-2);
  border-radius: 6px;
}
.hub-ch-grid { border-collapse: separate; border-spacing: 0; font-size: 13px; }
.hub-ch-grid th,
.hub-ch-grid td {
  border-bottom: 1px solid var(--line-2);
  border-right: 1px solid var(--line-2);
  padding: 0;
  text-align: left;
  white-space: nowrap;
}
.hub-ch-grid thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface2);
  padding: 4px 6px;
  font-weight: 600;
  color: var(--text2);
  vertical-align: bottom;
}
/* THE STICKY NAME COLUMN. Without it, scrolling to week 14 leaves 31
   indistinguishable rows and hours land on the wrong person. z-index 3 so the
   corner cell stays above both sticky axes. */
.hub-ch-name-col {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface);
  min-width: 12rem; max-width: 16rem;
  padding: 4px 8px !important;
  white-space: normal;
}
.hub-ch-grid thead .hub-ch-name-col { z-index: 3; background: var(--surface2); }
.hub-ch-name { display: block; font-weight: 500; }
.hub-ch-off { display: block; font-size: 11.5px; color: var(--warn, var(--text3)); }
.hub-ch-grid tr.is-retired .hub-ch-name { color: var(--text3); }

.hub-ch-week-col { min-width: 3.4rem; text-align: center !important; }
.hub-ch-week-n { display: block; font-size: 11px; color: var(--text3); }
.hub-ch-week-d { display: block; font-size: 11.5px; }

.hub-ch-total-col {
  min-width: 4rem;
  padding: 4px 8px !important;
  text-align: right !important;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  background: var(--surface2);
}

/* --- one cell ------------------------------------------------------------- */
.hub-ch-cell { position: relative; }
.hub-ch-input {
  width: 100%;
  min-width: 3.4rem;
  box-sizing: border-box;
  border: 0;
  background: transparent;
  padding: 6px 4px;
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--text);
}
.hub-ch-input:focus {
  outline: 2px solid var(--accent, var(--info));
  outline-offset: -2px;
}
/* A RECORDED ANSWER LOOKS LIKE ONE — including a recorded 0, which is an answer
   and must not read as an empty box. */
.hub-ch-cell.is-filled { background: var(--ok-bg, var(--surface2)); }
.hub-ch-cell.is-saving { background: var(--info-bg); }
.hub-ch-cell.is-error  { background: var(--danger-bg); }
.hub-ch-cell.is-error .hub-ch-input { color: var(--danger); font-weight: 600; }

/* The message sits over the grid rather than inside the cell: widening a column
   to fit a sentence would reflow all eighteen of them. */
.hub-ch-cell-msg {
  position: absolute; z-index: 4; top: 100%; left: 0;
  min-width: 15rem; max-width: 20rem;
  padding: 4px 8px;
  border: 1px solid var(--danger-br);
  border-radius: 4px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 12px;
  white-space: normal;
}

/* --- roster controls ------------------------------------------------------ */
.hub-ch-add-row { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.hub-ch-add-row .hub-select { min-width: 15rem; }
.hub-ch-remove { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.hub-ch-remove li {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); padding: 4px 0;
  border-bottom: 1px solid var(--line-2);
}
.hub-ch-remove li:last-child { border-bottom: 0; }

.hub-ch-method { margin-top: var(--s5); }

@media (max-width: 640px) {
  .hub-ch-add-row .hub-select { min-width: 0; width: 100%; }
  .hub-ch-name-col { min-width: 9rem; }
}

/* --- folded reference ------------------------------------------------------
   The counting rules, closed until wanted. <details> carries the state and the
   keyboard behaviour, so there is no JS here. Matches the fold on Grant Metrics;
   the two pages sit beside each other and should not disagree about what a
   reference block looks like. */
.hub-ch-fold > .hub-ch-fold-head { cursor: pointer; list-style: none; }
.hub-ch-fold > .hub-ch-fold-head::-webkit-details-marker { display: none; }
.hub-ch-fold-mark { flex: none; width: 7px; height: 7px; align-self: center;
  border-right: 2px solid var(--text3); border-bottom: 2px solid var(--text3);
  transform: rotate(45deg) translate(-2px, -2px); transition: transform .15s ease; }
.hub-ch-fold[open] > .hub-ch-fold-head .hub-ch-fold-mark {
  transform: rotate(225deg) translate(-2px, -2px); }
.hub-ch-fold:not([open]) > .hub-ch-fold-head {
  margin-bottom: calc(var(--s5) * -1); border-bottom: 0;
  border-radius: var(--r-panel); }

/* --- print ---------------------------------------------------------------- */
/* The grid gets printed and checked against the paper tracker, so it must not be
   cut off at the page edge and the sticky headers must not float over rows. */
@media print {
  /* A closed <details> prints nothing, and on paper these rules are the reason
     a total is what it is. Every fold prints open.

     The pseudo-element is targeted as well as the child: current Chromium hides
     a closed fold's contents through ::details-content, which the child's own
     display does not override. Measured under print media on 2026-09-21 -- see
     the matching note in hub-grant-metrics.css. */
  .hub-ch-fold::details-content {
    content-visibility: visible !important;
    block-size: auto !important;
    opacity: 1 !important;
  }
  .hub-ch-fold-body { display: block !important; }
  .hub-ch-fold-mark { display: none; }
  .hub-ch-fold:not([open]) > .hub-ch-fold-head {
    margin-bottom: var(--s5); border-bottom: 1px solid #ccc; }
  .hub-ch-scroll { overflow: visible; max-height: none; }
  .hub-ch-grid thead th,
  .hub-ch-name-col { position: static; }
  .hub-ch-input { border: 0; }
  /* Colour is the only thing distinguishing a recorded zero from a blank on
     screen, and it is the first thing a printer drops. */
  .hub-ch-cell.is-filled { outline: 1px solid #666; outline-offset: -1px; }
}
