/* ==========================================================================
   FactoryHajri UI - 04. COMPONENTS
   --------------------------------------------------------------------------
   Everything starts with  fh-  . One class, one job. No nesting, no !important.
   Check here before inventing a new component.
   ========================================================================== */

/* ======================================================== 1. BUTTON ====== */
.fh-btn {
  --_bg: var(--fh-navy-800);
  --_fg: #fff;
  --_bd: var(--fh-navy-800);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--fh-s2);
  min-height: 40px; padding: 0 var(--fh-s4);
  background: var(--_bg); color: var(--_fg);
  border: 1px solid var(--_bd); border-radius: var(--fh-r-sm);
  font-family: inherit; font-size: var(--fh-text-base); font-weight: var(--fh-fw-semi);
  line-height: 1; white-space: nowrap; cursor: pointer;
  transition: filter var(--fh-dur-fast) var(--fh-ease), box-shadow var(--fh-dur-fast) var(--fh-ease);
}
.fh-btn:hover { filter: brightness(1.12); text-decoration: none; color: var(--_fg); }
.fh-btn:active { filter: brightness(.94); }
.fh-btn[disabled], .fh-btn.is-disabled { opacity: .5; pointer-events: none; }
.fh-btn svg { flex: 0 0 auto; }

.fh-btn--primary { --_bg: var(--fh-marigold-500); --_bd: var(--fh-marigold-500); --_fg: var(--fh-navy-900); }
.fh-btn--go      { --_bg: var(--fh-present);      --_bd: var(--fh-present);      --_fg: #fff; }
.fh-btn--danger  { --_bg: var(--fh-absent);       --_bd: var(--fh-absent);       --_fg: #fff; }
.fh-btn--ghost   { --_bg: transparent; --_bd: var(--fh-line-strong); --_fg: var(--fh-text); }
.fh-btn--ghost:hover { --_bg: var(--fh-surface-3); filter: none; }
.fh-btn--quiet   { --_bg: transparent; --_bd: transparent; --_fg: var(--fh-navy-600); }
.fh-btn--quiet:hover { --_bg: var(--fh-surface-3); filter: none; }

.fh-btn--sm { min-height: 32px; padding: 0 var(--fh-s3); font-size: var(--fh-text-sm); }
.fh-btn--lg { min-height: 48px; padding: 0 var(--fh-s6); font-size: var(--fh-text-md); }
.fh-btn--block { width: 100%; }
.fh-btn--icon { width: 40px; padding: 0; }
.fh-btn--icon.fh-btn--sm { width: 32px; }

/* button row - wraps by itself on mobile */
.fh-btnrow { display: flex; flex-wrap: wrap; gap: var(--fh-s2); align-items: center; }
.fh-btnrow--end { justify-content: flex-end; }
@media (max-width: 560px) {
  .fh-btnrow--split .fh-btn { flex: 1 1 calc(50% - var(--fh-s2)); }
  .fh-btnrow--stack .fh-btn { flex: 1 1 100%; }
}

/* ========================================================== 2. CARD ====== */
.fh-card {
  background: var(--fh-surface);
  border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md);
  box-shadow: var(--fh-sh-sm);
  overflow: hidden;
}
.fh-card__head {
  display: flex; align-items: center; gap: var(--fh-s3);
  padding: var(--fh-s4) var(--fh-s4) 0;
}
.fh-card__title { font-size: var(--fh-text-md); font-weight: var(--fh-fw-semi); margin: 0; }
.fh-card__sub { font-size: var(--fh-text-sm); color: var(--fh-text-muted); margin: 2px 0 0; }
.fh-card__link { margin-left: auto; font-size: var(--fh-text-sm); font-weight: var(--fh-fw-medium); }
.fh-card__body { padding: var(--fh-s4); }
.fh-card__foot {
  padding: var(--fh-s3) var(--fh-s4);
  border-top: 1px solid var(--fh-line);
  background: var(--fh-surface-2);
  font-size: var(--fh-text-sm); color: var(--fh-text-2);
}
.fh-card--flat { box-shadow: none; }
.fh-card--pad0 .fh-card__body { padding: 0; }

/* MODULE SPINE - a coloured bar on the left edge of a card.
   The app's own mark: one colour per module, like the tabs on a paper
   register. Use it only when a card belongs to a single module. */
.fh-card--spine { border-left: 3px solid var(--fh-mod, var(--fh-navy-600)); }
.fh-mod-attendance { --fh-mod: var(--fh-mod-attendance); }
.fh-mod-salary     { --fh-mod: var(--fh-mod-salary); }
.fh-mod-workers    { --fh-mod: var(--fh-mod-workers); }
.fh-mod-biometric  { --fh-mod: var(--fh-mod-biometric); }
.fh-mod-advance    { --fh-mod: var(--fh-mod-advance); }
.fh-mod-piecework  { --fh-mod: var(--fh-mod-piecework); }
.fh-mod-pettycash  { --fh-mod: var(--fh-mod-pettycash); }
.fh-mod-reports    { --fh-mod: var(--fh-mod-reports); }

/* ==================================================== 3. STAT TILE ====== */
.fh-stats { display: grid; gap: var(--fh-s3); grid-template-columns: repeat(2, minmax(0,1fr)); }
@media (min-width: 768px)  { .fh-stats { grid-template-columns: repeat(4, minmax(0,1fr)); } }
@media (min-width: 1200px) { .fh-stats--6 { grid-template-columns: repeat(6, minmax(0,1fr)); } }

.fh-stat {
  background: var(--fh-surface);
  border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md);
  padding: var(--fh-s4);
  box-shadow: var(--fh-sh-xs);
  display: block;
}
a.fh-stat:hover { border-color: var(--fh-line-strong); box-shadow: var(--fh-sh-md); text-decoration: none; }
.fh-stat__icon {
  width: 38px; height: 38px; border-radius: var(--fh-r-sm);
  display: grid; place-items: center; margin-bottom: var(--fh-s3);
  background: var(--fh-surface-3); color: var(--fh-navy-600);
}
/* The value must fit ITS OWN TILE, not just the page. A monthly salary total
   like Rs 1,17,333.33 is eleven characters, and on a 360px phone a half-width
   tile is only about 130px wide - at a fixed 30px it simply ran off the card.
   So the size follows the tile's width (container query), with a viewport-based
   fallback for browsers without it. Never put a fixed size back here. */
.fh-stat__value {
  font-family: var(--fh-font-data);
  font-size: clamp(15px, 5.2vw, var(--fh-text-2xl)); font-weight: var(--fh-fw-semi);
  min-width: 0; max-width: 100%;
  line-height: 1.05; letter-spacing: -.02em; color: var(--fh-text);
}
.fh-stat__label { font-size: var(--fh-text-sm); color: var(--fh-text-2); margin-top: 2px; }
@supports (container-type: inline-size) {
  .fh-stat { container-type: inline-size; }
  /* 14cqw = 14% of the TILE's width, so it shrinks in a half-width phone tile
     and grows back on a wide desktop one. */
  .fh-stat__value { font-size: clamp(15px, 14cqw, var(--fh-text-2xl)); }
}
.fh-stat__foot {
  margin-top: var(--fh-s3); font-size: var(--fh-text-xs);
  display: flex; align-items: center; gap: 4px; color: var(--fh-text-muted);
}
.fh-stat__delta { font-weight: var(--fh-fw-semi); font-family: var(--fh-font-data); }
.fh-stat__delta--up   { color: var(--fh-present); }
.fh-stat__delta--down { color: var(--fh-absent); }
/* tone: icon colour follows the status */
.fh-stat--present .fh-stat__icon { background: var(--fh-present-bg); color: var(--fh-present); }
.fh-stat--absent  .fh-stat__icon { background: var(--fh-absent-bg);  color: var(--fh-absent); }
.fh-stat--half    .fh-stat__icon { background: var(--fh-half-bg);    color: var(--fh-half); }
.fh-stat--money   .fh-stat__icon { background: var(--fh-info-bg);    color: var(--fh-info); }
.fh-stat--night   .fh-stat__icon { background: var(--fh-night-bg);   color: var(--fh-night); }

/* thin progress bar */
.fh-meter { height: 6px; border-radius: var(--fh-r-pill); background: var(--fh-surface-3); overflow: hidden; margin-top: var(--fh-s2); }
.fh-meter__fill { height: 100%; border-radius: inherit; background: var(--fh-navy-600); }
.fh-meter__fill--go { background: var(--fh-present); }
.fh-meter__fill--warn { background: var(--fh-half); }

/* ============================================ 4. HAJRI CODE (signature) ==
   The app's own vocabulary: D / N / HD / P / A / PL. One shape everywhere -
   grid cell, card, report, register. Monospaced so codes and counts line up. */
.fh-code {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 30px; padding: 2px 7px;
  font-family: var(--fh-font-data); font-size: var(--fh-text-sm);
  font-weight: var(--fh-fw-semi); line-height: 1.5;
  border-radius: 6px; border: 1px solid transparent;
  background: var(--fh-off-bg); color: var(--fh-off); border-color: var(--fh-off-line);
}
.fh-code--d, .fh-code--p { background: var(--fh-present-bg); color: var(--fh-present); border-color: var(--fh-present-line); }
.fh-code--n  { background: var(--fh-night-bg);  color: var(--fh-night);  border-color: var(--fh-night-line); }
.fh-code--hd { background: var(--fh-half-bg);   color: var(--fh-half);   border-color: var(--fh-half-line); }
.fh-code--a  { background: var(--fh-absent-bg); color: var(--fh-absent); border-color: var(--fh-absent-line); }
.fh-code--pl { background: var(--fh-leave-bg);  color: var(--fh-leave);  border-color: var(--fh-leave-line); }
.fh-code--empty { background: transparent; border-style: dashed; border-color: var(--fh-line-strong); color: var(--fh-text-muted); }
.fh-code--lg { min-width: 40px; font-size: var(--fh-text-base); padding: 4px 10px; }
.fh-code__bonus { font-size: var(--fh-text-xs); opacity: .8; margin-left: 3px; }

/* ========================================================== 5. CHIP ====== */
.fh-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: var(--fh-r-pill);
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  background: var(--fh-surface-3); color: var(--fh-text-2);
  border: 1px solid transparent;
}
.fh-chip--ok    { background: var(--fh-present-bg); color: var(--fh-present); }
.fh-chip--late  { background: var(--fh-absent-bg);  color: var(--fh-absent); }
.fh-chip--early { background: var(--fh-half-bg);    color: var(--fh-half); }
.fh-chip--info  { background: var(--fh-info-bg);    color: var(--fh-info); }
.fh-chip--night { background: var(--fh-night-bg);   color: var(--fh-night); }
.fh-chip--solid { background: var(--fh-absent); color: #fff; }
.fh-chip--ghost { background: transparent; border-color: var(--fh-line-strong); color: var(--fh-text-muted); }
.fh-chip__dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

/* selectable chips for filtering */
.fh-chipbar { display: flex; gap: var(--fh-s2); flex-wrap: wrap; }
.fh-chipbtn {
  padding: 6px var(--fh-s3); border-radius: var(--fh-r-pill);
  border: 1px solid var(--fh-line-strong); background: var(--fh-surface);
  font-size: var(--fh-text-sm); font-weight: var(--fh-fw-medium);
  color: var(--fh-text-2); cursor: pointer;
}
.fh-chipbtn:hover { border-color: var(--fh-navy-400); text-decoration: none; }
.fh-chipbtn.is-active { background: var(--fh-navy-800); border-color: var(--fh-navy-800); color: #fff; }
.fh-chipbtn__count { font-family: var(--fh-font-data); opacity: .75; margin-left: 4px; }

/* ========================================================= 6. TABLE ======
   TWO kinds, and the difference matters:

   .fh-table              a LIST of records (workers, advances, departments).
                          Under 760px every row becomes a card, each cell
                          labelled from its data-label. No sideways scroll.

   .fh-table--matrix      a GRID of numbers (worker x 31 days, worker x 12
                          months). It can never become cards, so it scrolls
                          inside its box: header sticks to the top, the first
                          column sticks to the left on desktop, the totals row
                          sticks to the bottom.

   Always wrap a table in .fh-tablewrap - that box owns the scrolling, not
   the page. A table must never make the whole page scroll sideways.
   ========================================================================== */
.fh-tablewrap {
  border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md);
  background: var(--fh-surface);
  overflow: auto;                 /* both directions, inside the box only */
  max-width: 100%;
  max-height: 72vh;
  overscroll-behavior: contain;   /* keep scrolling inside the box */
}
.fh-tablewrap--auto { max-height: none; }        /* short table, no cap */
.fh-tablewrap--tall { max-height: 84vh; }

/* A list table fills the box. A matrix keeps its natural column widths and
   only grows past the box when it has to - that is what keeps columns
   looking right instead of stretched. */
.fh-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fh-text-sm); }
.fh-table--matrix { width: auto; min-width: 100%; }

.fh-table th, .fh-table td {
  padding: 10px var(--fh-s3);
  border-bottom: 1px solid var(--fh-line);
  text-align: left; vertical-align: middle;
  background-clip: padding-box;   /* keeps borders clean on sticky cells */
}
.fh-table thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--fh-navy-800); color: #fff;
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  text-transform: uppercase; letter-spacing: var(--fh-track-label);
  white-space: nowrap; border-bottom: 0;
}
.fh-table tbody tr:last-child td { border-bottom: 0; }
/* The hajri grid is excluded: there a cell's colour IS the information, and a
   hover tint would wash out the D/N/HD/A/PL codes as the mouse moves. */
.fh-table:not(.fh-hgrid) tbody tr:hover td { background: var(--fh-surface-2); }
.fh-table__num, .fh-table td.fh-table__num, .fh-table th.fh-table__num {
  text-align: right; font-family: var(--fh-font-data);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.fh-table__name { font-weight: var(--fh-fw-semi); color: var(--fh-text); }
/* Several lines of value inside one cell, as a single flex child so it wraps
   instead of spilling out of the card on a phone. */
.fh-cellstack { display: block; line-height: 1.6; }
.fh-cellstack > * { display: inline; }

.fh-table__sub { display: block; font-size: var(--fh-text-xs); color: var(--fh-text-muted); font-weight: var(--fh-fw-normal); }
.fh-table__act { text-align: right; white-space: nowrap; }
.fh-table tbody tr.is-flagged td { background: var(--fh-flag-row); }
.fh-table tbody tr.is-muted { opacity: .62; }
.fh-table__group td {
  background: var(--fh-surface-3); font-weight: var(--fh-fw-semi);
  font-size: var(--fh-text-xs); text-transform: uppercase;
  letter-spacing: var(--fh-track-label); color: var(--fh-navy-700);
}
/* A total row written inline in the table body (a report that echoes rows as
   it loops cannot easily use <tfoot>). Same look, no sticky. */
.fh-table tr.is-total td {
  background: var(--fh-surface-3); font-weight: var(--fh-fw-bold);
  border-top: 2px solid var(--fh-line-strong);
}
.fh-table tr.is-subtotal td { background: var(--fh-surface-2); font-weight: var(--fh-fw-semi); }
@media (max-width: 760px) {
  .fh-table:not(.fh-table--matrix) tr.is-total td,
  .fh-table:not(.fh-table--matrix) tr.is-subtotal td { background: transparent; }
}

.fh-table tfoot td {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--fh-surface-3); font-weight: var(--fh-fw-bold);
  border-top: 2px solid var(--fh-line-strong); border-bottom: 0;
}

/* ---- desktop: matrix first column sticks ---- */
@media (min-width: 761px) {
  .fh-table--matrix tbody td:first-child,
  .fh-table--matrix tfoot td:first-child {
    position: sticky; left: 0; z-index: 2;
    background: var(--fh-surface);
    box-shadow: 1px 0 0 var(--fh-line);
  }
  .fh-table--matrix tfoot td:first-child { background: var(--fh-surface-3); }
  .fh-table--matrix tbody tr:hover td:first-child { background: var(--fh-surface-2); }
  .fh-table--matrix tbody tr.is-flagged td:first-child { background: var(--fh-flag-row); }
  /* A sticky first column paints its own background, so any row that has its
     own tint must repeat it here or the first cell stays white. */
  .fh-table--matrix tbody tr.is-total td:first-child    { background: var(--fh-surface-3); }
  .fh-table--matrix tbody tr.is-subtotal td:first-child { background: var(--fh-surface-2); }
  .fh-table--matrix thead th:first-child {
    position: sticky; left: 0; z-index: 4;      /* header and first column together */
    background: var(--fh-navy-800);
  }
}

/* ---- mobile ---- */
@media (max-width: 760px) {
  /* LIST table -> cards */
  .fh-table:not(.fh-table--matrix) { display: block; width: 100%; }
  .fh-table:not(.fh-table--matrix) thead { display: none; }
  .fh-table:not(.fh-table--matrix) tbody,
  .fh-table:not(.fh-table--matrix) tfoot { display: block; }
  .fh-table:not(.fh-table--matrix) tr {
    display: block; background: var(--fh-surface);
    border: 1px solid var(--fh-line); border-radius: var(--fh-r-sm);
    padding: var(--fh-s3) var(--fh-s3) var(--fh-s2);
    margin: 0 0 var(--fh-s2);
  }
  .fh-table:not(.fh-table--matrix) tr.is-flagged { border-color: var(--fh-half-line); }
  .fh-table:not(.fh-table--matrix) tr.is-flagged td { background: transparent; }
  .fh-table:not(.fh-table--matrix) td {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--fh-s3); padding: 3px 0; border: 0; text-align: right;
    background: transparent;
  }
  .fh-table:not(.fh-table--matrix) td::before {
    content: attr(data-label);
    flex: 0 0 40%; text-align: left;
    font-size: var(--fh-text-xs); text-transform: uppercase;
    letter-spacing: var(--fh-track-label); font-weight: var(--fh-fw-semi);
    color: var(--fh-text-muted);
  }
  .fh-table:not(.fh-table--matrix) td:empty { display: none; }
  /* IMPORTANT: in card mode a td is a flex row, so several inline pieces in
     one cell become SEPARATE flex items and will not wrap - they run off the
     card edge instead. A cell holding more than one line must put them inside
     ONE element: .fh-cellstack. */
  .fh-table:not(.fh-table--matrix) td > .fh-cellstack { flex: 1 1 auto; min-width: 0; }
  /* An empty state fills a whole colspan row - it is not a labelled cell.
     Left as a flex row it kept the data-label gutter and pushed itself off a
     360px screen. Give it the full width and drop the label. */
  .fh-table:not(.fh-table--matrix) td:has(> .fh-empty) { display: block; padding: 0; }
  .fh-table:not(.fh-table--matrix) td:has(> .fh-empty)::before { display: none; }
  .fh-table:not(.fh-table--matrix) td.fh-table__name {
    display: block; text-align: left; font-size: var(--fh-text-md);
    padding-bottom: var(--fh-s2);
  }
  .fh-table:not(.fh-table--matrix) td.fh-table__name::before { display: none; }
  .fh-table:not(.fh-table--matrix) td.fh-table__act {
    display: block; text-align: left; margin-top: var(--fh-s2);
  }
  .fh-table:not(.fh-table--matrix) td.fh-table__act::before { display: none; }
  .fh-table:not(.fh-table--matrix) tfoot td { position: static; }
  .fh-table:not(.fh-table--matrix) .fh-table__group { display: none; }
  /* Cards need no box - it is removed by default. */
  .fh-tablewrap { border: 0; background: transparent; max-height: none; overflow: visible; }
  /* ...but a matrix does need one. Two ways, both work:
     put  class="fh-tablewrap fh-tablewrap--matrix"  in the markup (reliable),
     or rely on :has() where the browser supports it. */
  .fh-tablewrap--matrix,
  .fh-tablewrap:has(.fh-table--matrix) {
    border: 1px solid var(--fh-line); background: var(--fh-surface);
    overflow: auto; max-height: 72vh;
  }

  /* MATRIX table: scrolls, but the first column does NOT stick - on a phone it
     would eat a third of the screen. The header still sticks to the top. */
  .fh-table--matrix tbody td:first-child,
  .fh-table--matrix tfoot td:first-child,
  .fh-table--matrix thead th:first-child {
    position: static;
    width: 132px; min-width: 132px; max-width: 132px;
    white-space: normal; overflow-wrap: break-word; word-break: normal;
  }
  .fh-table--matrix thead th:first-child { position: sticky; top: 0; }
  .fh-table--matrix th, .fh-table--matrix td { padding: 8px var(--fh-s2); }
}

/* ========================================================== 7. FORM ====== */
.fh-field { margin-bottom: var(--fh-s4); min-width: 0; }
.fh-label {
  display: block; margin-bottom: 5px;
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  text-transform: uppercase; letter-spacing: var(--fh-track-label);
  color: var(--fh-text-2);
}
.fh-label__opt { text-transform: none; letter-spacing: 0; font-weight: var(--fh-fw-normal); color: var(--fh-text-muted); }
.fh-input, .fh-select, .fh-textarea {
  width: 100%; min-height: 40px; padding: 8px var(--fh-s3);
  background: var(--fh-surface); color: var(--fh-text);
  border: 1px solid var(--fh-line-strong); border-radius: var(--fh-r-sm);
  font-family: inherit; font-size: var(--fh-text-base); line-height: 1.4;
  transition: border-color var(--fh-dur-fast) var(--fh-ease), box-shadow var(--fh-dur-fast) var(--fh-ease);
}
.fh-input:focus, .fh-select:focus, .fh-textarea:focus {
  outline: none; border-color: var(--fh-marigold-500); box-shadow: var(--fh-focus);
}
.fh-input[readonly], .fh-input[disabled] { background: var(--fh-surface-3); color: var(--fh-text-muted); }
.fh-textarea { min-height: 90px; resize: vertical; }
.fh-input--num { font-family: var(--fh-font-data); text-align: right; }
.fh-help { margin-top: 5px; font-size: var(--fh-text-xs); color: var(--fh-text-muted); line-height: 1.45; }
.fh-field--bad .fh-input, .fh-field--bad .fh-select { border-color: var(--fh-absent); }
.fh-field--bad .fh-help { color: var(--fh-absent); font-weight: var(--fh-fw-medium); }

.fh-check { display: flex; align-items: flex-start; gap: var(--fh-s2); font-size: var(--fh-text-base); cursor: pointer; }
.fh-check input { width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--fh-marigold-500); }

/* form layout */
.fh-grid { display: grid; gap: var(--fh-s4); grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.fh-grid--2 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.fh-grid--wide { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .fh-grid, .fh-grid--2 { grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--fh-s3); }
  .fh-grid .fh-field--wide { grid-column: 1 / -1; }
}
@media (max-width: 420px) { .fh-grid, .fh-grid--2 { grid-template-columns: 1fr; } }

/* form sections - a rule between them, air on both sides */
.fh-fieldset { padding: 0 0 var(--fh-s5); border: 0; margin: 0; }
.fh-fieldset + .fh-fieldset { border-top: 1px solid var(--fh-line); padding-top: var(--fh-s5); }
.fh-fieldset__legend {
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  text-transform: uppercase; letter-spacing: var(--fh-track-label);
  color: var(--fh-navy-700); margin-bottom: var(--fh-s3); padding: 0;
}
.fh-fieldset__legend span { text-transform: none; letter-spacing: 0; font-weight: var(--fh-fw-normal); color: var(--fh-text-muted); }
.fh-formfoot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--fh-s3);
  border-top: 1px solid var(--fh-line); padding-top: var(--fh-s4);
}

/* toolbar / filter bar */
.fh-toolbar {
  display: flex; flex-wrap: wrap; gap: var(--fh-s3); align-items: flex-end;
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md); padding: var(--fh-s3) var(--fh-s4);
  box-shadow: var(--fh-sh-xs); margin-bottom: var(--fh-s4);
}
.fh-toolbar .fh-field { margin: 0; flex: 1 1 160px; }
/* Browsers give a <input type="month"> a 41px box and a <select> 40px, so a
   filter bar mixing them sat one pixel out of line. Pin them in the toolbar,
   where things stand side by side and it shows. */
.fh-toolbar .fh-input, .fh-toolbar .fh-select { height: 40px; }
/* The action sits RIGHT AFTER the last field, not flung to the far edge.
   His words: "View button end me dikhata ha desktop per, usko department ke
   paas hi hona chahiye." He is right - on a 1920px screen a button parked at
   the opposite end of the bar reads as unrelated to the fields you just
   filled. A `margin-left:auto` used to push it there. Any page that DOES want
   the group at the right just lets its own field grow (flex:1 1 ...), which
   is what the search bars already do. */
.fh-toolbar__end { display: flex; gap: var(--fh-s2); align-items: center; }
@media (max-width: 700px) {
  .fh-toolbar .fh-field { flex: 1 1 46%; }
  .fh-toolbar__end { margin-left: 0; flex: 1 1 100%; }
  .fh-toolbar__end .fh-btn { flex: 1 1 calc(50% - var(--fh-s2)); }
}

/* ====================================================== 8. FEEDBACK ====== */
.fh-note {
  border: 1px solid var(--fh-info-line); background: var(--fh-info-bg);
  color: var(--fh-on-info-bg); border-radius: var(--fh-r-md);
  padding: var(--fh-s3) var(--fh-s4); font-size: var(--fh-text-sm);
  line-height: var(--fh-lh-snug); margin-bottom: var(--fh-s4);
}
.fh-note__title { font-weight: var(--fh-fw-semi); display: block; margin-bottom: 2px; }
.fh-note--ok   { background: var(--fh-present-bg); border-color: var(--fh-present-line); color: var(--fh-on-present-bg); }
.fh-note--warn { background: var(--fh-half-bg);    border-color: var(--fh-half-line);    color: var(--fh-on-half-bg); }
.fh-note--bad  { background: var(--fh-absent-bg);  border-color: var(--fh-absent-line);  color: var(--fh-on-absent-bg); }

.fh-empty { text-align: center; padding: var(--fh-s8) var(--fh-s4); }
.fh-empty__icon {
  width: 52px; height: 52px; margin: 0 auto var(--fh-s3);
  border-radius: var(--fh-r-md); display: grid; place-items: center;
  background: var(--fh-surface-3); color: var(--fh-navy-400);
}
.fh-empty__title { font-size: var(--fh-text-md); font-weight: var(--fh-fw-semi); margin-bottom: 4px; }
.fh-empty__text { color: var(--fh-text-2); font-size: var(--fh-text-sm); max-width: 46ch; margin: 0 auto var(--fh-s4); }

.fh-toastwrap {
  position: fixed; z-index: var(--fh-z-toast);
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--fh-tabbar-h) + var(--fh-s4));
  display: flex; flex-direction: column; gap: var(--fh-s2);
  width: min(460px, calc(100vw - var(--fh-s6)));
}
@media (min-width: 768px) { .fh-toastwrap { left: auto; right: var(--fh-s6); transform: none; bottom: var(--fh-s6); } }
.fh-toast {
  display: flex; align-items: flex-start; gap: var(--fh-s3);
  background: var(--fh-navy-800); color: #fff;
  border-radius: var(--fh-r-md); padding: var(--fh-s3) var(--fh-s4);
  box-shadow: var(--fh-sh-lg); font-size: var(--fh-text-sm);
}
.fh-toast--ok  { background: var(--fh-present); }
.fh-toast--bad { background: var(--fh-absent); }
.fh-toast__close { margin-left: auto; background: none; border: 0; color: inherit; opacity: .8; cursor: pointer; }

/* ========================================================= 9. MODAL ====== */
.fh-modal {
  position: fixed; inset: 0; z-index: var(--fh-z-modal);
  background: var(--fh-overlay);
  display: none; align-items: flex-end; justify-content: center;
  padding: 0;
}
.fh-modal.is-open { display: flex; }
.fh-modal__box {
  background: var(--fh-surface); width: 100%; max-height: 92vh; overflow: auto;
  border-radius: var(--fh-r-lg) var(--fh-r-lg) 0 0;
  box-shadow: var(--fh-sh-lg); animation: fh-rise var(--fh-dur) var(--fh-ease);
}
@keyframes fh-rise { from { transform: translateY(14px); opacity: .6; } to { transform: none; opacity: 1; } }
.fh-modal__head {
  display: flex; align-items: center; gap: var(--fh-s3);
  padding: var(--fh-s4); border-bottom: 1px solid var(--fh-line);
  position: sticky; top: 0; background: var(--fh-surface);
}
.fh-modal__title { font-size: var(--fh-text-md); font-weight: var(--fh-fw-semi); margin: 0; }
.fh-modal__body { padding: var(--fh-s4); }
.fh-modal__foot {
  display: flex; gap: var(--fh-s2); justify-content: flex-end;
  padding: var(--fh-s3) var(--fh-s4); border-top: 1px solid var(--fh-line);
  background: var(--fh-surface-2); position: sticky; bottom: 0;
}
@media (max-width: 560px) { .fh-modal__foot .fh-btn { flex: 1 1 calc(50% - var(--fh-s2)); } }
@media (min-width: 768px) {
  .fh-modal { align-items: center; padding: var(--fh-s6); }
  .fh-modal__box { max-width: 560px; border-radius: var(--fh-r-lg); }
  .fh-modal--wide .fh-modal__box { max-width: 860px; }
}

/* ======================================================== 10. LISTS ====== */
.fh-list { list-style: none; margin: 0; padding: 0; }
.fh-list__row {
  display: flex; align-items: center; gap: var(--fh-s3);
  padding: var(--fh-s3) var(--fh-s4);
  border-bottom: 1px solid var(--fh-line);
}
.fh-list__row:last-child { border-bottom: 0; }
.fh-list__icon {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: var(--fh-r-sm);
  display: grid; place-items: center;
  background: var(--fh-surface-3); color: var(--fh-navy-600);
}
.fh-list__main { min-width: 0; flex: 1 1 auto; }
.fh-list__title { font-size: var(--fh-text-base); font-weight: var(--fh-fw-medium); }
.fh-list__meta { font-size: var(--fh-text-xs); color: var(--fh-text-muted); }
.fh-list__end { margin-left: auto; text-align: right; white-space: nowrap; font-family: var(--fh-font-data); font-size: var(--fh-text-sm); color: var(--fh-text-2); }

/* legend, next to a chart */
.fh-legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.fh-legend li { display: flex; align-items: center; gap: var(--fh-s2); font-size: var(--fh-text-sm); }
.fh-legend__dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; }
.fh-legend__val { margin-left: auto; font-family: var(--fh-font-data); font-weight: var(--fh-fw-semi); }

/* ======================================================= 11. CHARTS ======
   No library - just SVG and CSS. Factory internet is slow, so a small chart
   is not worth a 200 KB dependency. */
.fh-donut { display: block; width: 100%; max-width: 190px; margin-inline: auto; }
.fh-donut circle { fill: none; stroke-width: 15; }
.fh-donut__track { stroke: var(--fh-surface-3); }
.fh-donut__seg { stroke-linecap: butt; transition: stroke-dasharray var(--fh-dur) var(--fh-ease); }
.fh-donut__center { text-anchor: middle; }
.fh-donut__num { font-family: var(--fh-font-data); font-size: 21px; font-weight: 600; fill: var(--fh-text); }
.fh-donut__cap { font-size: 8.5px; fill: var(--fh-text-muted); text-transform: uppercase; letter-spacing: .08em; }

.fh-bars { display: grid; gap: var(--fh-s3); }
.fh-bars__row { display: grid; grid-template-columns: 92px 1fr auto; align-items: center; gap: var(--fh-s3); font-size: var(--fh-text-sm); }
.fh-bars__track { height: 10px; background: var(--fh-surface-3); border-radius: var(--fh-r-pill); overflow: hidden; }
.fh-bars__fill { height: 100%; background: var(--fh-navy-600); border-radius: inherit; }
.fh-bars__val { font-family: var(--fh-font-data); font-weight: var(--fh-fw-semi); }

/* ===================================================== 12. QUICK GRID ====
   Large tap targets for the dashboard. */
.fh-quick { display: grid; gap: var(--fh-s3); grid-template-columns: repeat(3, minmax(0,1fr)); }
@media (min-width: 560px) { .fh-quick { grid-template-columns: repeat(5, minmax(0,1fr)); } }
.fh-quick__item {
  display: flex; flex-direction: column; align-items: center; gap: var(--fh-s2);
  padding: var(--fh-s3) var(--fh-s2); border-radius: var(--fh-r-md);
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  color: var(--fh-text); text-align: center;
}
.fh-quick__item:hover { border-color: var(--fh-navy-400); text-decoration: none; box-shadow: var(--fh-sh-sm); }
.fh-quick__icon { width: 42px; height: 42px; border-radius: var(--fh-r-md); display: grid; place-items: center; background: var(--fh-surface-3); color: var(--fh-navy-600); }

/* ==================================================== 13. HAJRI GRID =====
   The attendance entry grid - the densest screen in the app.

   IMPORTANT: the cell colour classes are cP / cN / cA / cPL / cHD and the
   Sunday tint is offd. Those exact names are ALSO returned by api_save.php
   after every keystroke, and set by cell_class() in PHP. All three have to
   agree, so do not rename them here on their own.
   ========================================================================== */
.fh-hgrid { border-collapse: separate; border-spacing: 0; font-size: var(--fh-text-sm); }
.fh-hgrid th, .fh-hgrid td { border: 1px solid var(--fh-line); padding: 0; }
/* TWO sticky header rows, and they must stack - not sit on top of each other.
   Both inherited `top: 0` from .fh-table, so the Day/Night row landed exactly
   over the date row and the dates vanished the moment you scrolled. The date
   row keeps top:0; the Day/Night row starts where the first one ends.
   The first row's height is FIXED here so that offset can be exact - if you
   ever change its padding or font, change --fh-hgrid-head1 to match. */
.fh-hgrid { --fh-hgrid-head1: 50px; }
.fh-hgrid thead th { background: var(--fh-navy-800); color: #fff; padding: 6px 4px;
  font-size: var(--fh-text-xs); text-align: center; }
.fh-hgrid thead tr:first-child th {
  height: var(--fh-hgrid-head1); top: 0; z-index: 5;
}
.fh-hgrid thead tr:nth-child(2) th {
  top: var(--fh-hgrid-head1); z-index: 4;
}
.fh-hgrid thead th.is-off { background: var(--fh-navy-700); }
.fh-hgrid__sub { font-size: 10px !important; font-weight: var(--fh-fw-normal); }
.fh-hgrid__cell { width: 46px; min-width: 46px; text-align: center; }
/* A unit that pays a daily bonus shows codes like "D + 100", which do not fit
   in 46px. The whole grid widens so every column stays the same size. */
.fh-hgrid--bonus .fh-hgrid__cell { width: 68px; min-width: 68px; }
.fh-hgrid--bonus .fh-hgrid__cell input { font-size: 12.5px; letter-spacing: -.02em; }
.fh-hgrid__cell input {
  width: 100%; height: 34px; border: 0; background: transparent; text-align: center;
  font-family: var(--fh-font-data); font-size: var(--fh-text-sm); font-weight: var(--fh-fw-semi);
  color: inherit; padding: 0;
}
.fh-hgrid__cell input:focus { outline: 2px solid var(--fh-marigold-500); outline-offset: -2px; background: #fff; }
.fh-hgrid__cell input:disabled { color: var(--fh-text-muted); }
/* The five code colours - class names shared with api_save.php.
   These use the STRONG --fh-cell-* fills, not the pale chip backgrounds:
   across 31 columns the fill has to be readable at a glance. */
.fh-hgrid__cell.cP  { background: var(--fh-cell-p);  color: var(--fh-present); }
.fh-hgrid__cell.cN  { background: var(--fh-cell-n);  color: var(--fh-night); }
.fh-hgrid__cell.cHD { background: var(--fh-cell-hd); color: var(--fh-half); }
.fh-hgrid__cell.cA  { background: var(--fh-cell-a);  color: var(--fh-absent); }
.fh-hgrid__cell.cPL { background: var(--fh-cell-pl); color: var(--fh-leave); }
.fh-hgrid__cell.offd { background: var(--fh-cell-off); }
/* A marked day on a Sunday keeps its code colour - the off-day tint must not
   swallow it, so the code wins and the off day is shown by the header instead. */
.fh-hgrid__cell.offd.cP  { background: var(--fh-cell-p); }
.fh-hgrid__cell.offd.cN  { background: var(--fh-cell-n); }
.fh-hgrid__cell.offd.cHD { background: var(--fh-cell-hd); }
.fh-hgrid__cell.offd.cA  { background: var(--fh-cell-a); }
.fh-hgrid__cell.offd.cPL { background: var(--fh-cell-pl); }
.fh-hgrid td.fh-hgrid__worker { padding: 6px var(--fh-s3); font-weight: var(--fh-fw-semi);
  background: var(--fh-surface); text-align: left; }
.fh-hgrid td.fh-hgrid__dept { padding: 6px var(--fh-s2); font-size: var(--fh-text-xs);
  color: var(--fh-text-muted); background: var(--fh-surface-2); text-align: left; }
/* JIS ROW ME CURSOR HA, US ROW KA NAAM UJAAGAR.
   31 columns tak scroll karne ke baad yaad nahi rehta kis worker ki hajri
   lag rahi ha - naam wala column sticky ha hi, use hi highlight kar dete
   hain. Marigold ACTION ka rang ha, aur cursor yahi to ha - isliye wahi.
   Cell ke apne code ke rang isse nahi badalte: sirf naam aur department
   wale do khaane rangte hain, grid ke cells nahi. */
.fh-hgrid tbody tr.is-cur td.fh-hgrid__worker {
  background: var(--fh-marigold-100);
  box-shadow: inset 3px 0 0 var(--fh-marigold-500);
  color: var(--fh-navy-900);
}
.fh-hgrid tbody tr.is-cur td.fh-hgrid__dept { background: var(--fh-marigold-100); }
@media (max-width: 760px) {
  .fh-hgrid td.fh-hgrid__worker { width: 124px; min-width: 124px; max-width: 124px;
    white-space: normal; overflow-wrap: break-word; }
  .fh-hgrid td.fh-hgrid__dept, .fh-hgrid th.deptcol { width: 74px; min-width: 74px; max-width: 74px;
    white-space: normal; font-size: 10.5px; }
}

/* ============================================= 14. INLINE PANEL =========
   A small form that opens under a table row - change a rate, mark a worker
   as left. Lives inside the row so it can never lose track of which record
   it belongs to. */
.fh-inlinepanel {
  background: var(--fh-surface-2);
  border: 1px solid var(--fh-line);
  border-left: 3px solid var(--fh-marigold-500);
  border-radius: var(--fh-r-sm);
  padding: var(--fh-s3);
  margin: var(--fh-s2) 0;
}
.fh-inlinepanel form { gap: var(--fh-s2) var(--fh-s3); }
@media (max-width: 560px) {
  .fh-inlinepanel .fh-input { flex: 1 1 100%; width: 100% !important; }
  .fh-inlinepanel .fh-btn { flex: 1 1 calc(50% - var(--fh-s2)); }
}

/* ======================================================= 15. MISC ======= */
.fh-kbd {
  display: inline-block; font-family: var(--fh-font-data); font-size: var(--fh-text-xs);
  background: var(--fh-surface-3); border: 1px solid var(--fh-line-strong);
  border-bottom-width: 2px; border-radius: 5px; padding: 1px 6px; color: var(--fh-text-2);
}
.fh-divider { height: 1px; background: var(--fh-line); margin: var(--fh-s5) 0; border: 0; }
.fh-skeleton {
  background: linear-gradient(90deg, var(--fh-surface-3) 25%, var(--fh-shimmer) 37%, var(--fh-surface-3) 63%);
  background-size: 400% 100%; animation: fh-shimmer 1.4s ease infinite; border-radius: var(--fh-r-sm);
}
@keyframes fh-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .fh-skeleton { animation: none; } }


/* ====================================================== 17. PANEL =======
   A card that is ONE element, not a head+body pair. Use it when the markup
   cannot easily carry two nested divs - converting an old page, for example.
   Same look as .fh-card, with tinted variants for the tone of the section. */
.fh-panel {
  background: var(--fh-surface);
  border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md);
  box-shadow: var(--fh-sh-sm);
  padding: var(--fh-s4);
  margin: 0 0 var(--fh-s4);
}
.fh-panel > h3 {
  margin: 0 0 var(--fh-s3);
  font-size: var(--fh-text-md); font-weight: var(--fh-fw-semi);
}
.fh-panel > h3 > span {
  font-weight: var(--fh-fw-normal); font-size: var(--fh-text-sm); color: var(--fh-text-muted);
}
.fh-panel > p:first-of-type { margin-top: 0; }
.fh-panel--info  { background: var(--fh-info-bg);    border-color: var(--fh-info-line);    box-shadow: none; }
.fh-panel--setup { background: var(--fh-marigold-100); border-color: #E8D7B6;              box-shadow: none; }
.fh-panel--ok    { background: var(--fh-present-bg); border-color: var(--fh-present-line); box-shadow: none; }
.fh-panel--warn  { background: var(--fh-half-bg);    border-color: var(--fh-half-line);    box-shadow: none; }
.fh-panel--bad   { background: var(--fh-absent-bg);  border-color: var(--fh-absent-line);  box-shadow: none; }

/* A field wrapper styles its own controls, so plain <label>, <input>, <select>
   and <small> inside .fh-field need no extra classes. Handy when converting an
   existing page - and it means a forgotten class can never look unstyled. */
.fh-field > label:not(.fh-label):not(.fh-check) {
  display: block; margin-bottom: 5px;
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  text-transform: uppercase; letter-spacing: var(--fh-track-label);
  color: var(--fh-text-2);
}
.fh-field > input:not([type=checkbox]):not([type=radio]):not(.fh-input),
.fh-field > select:not(.fh-select),
.fh-field > textarea:not(.fh-textarea) {
  width: 100%; min-height: 40px; padding: 8px var(--fh-s3);
  background: var(--fh-surface); color: var(--fh-text);
  border: 1px solid var(--fh-line-strong); border-radius: var(--fh-r-sm);
  font-family: inherit; font-size: var(--fh-text-base); line-height: 1.4;
}
.fh-field > input:focus, .fh-field > select:focus, .fh-field > textarea:focus {
  outline: none; border-color: var(--fh-marigold-500); box-shadow: var(--fh-focus);
}
.fh-field > small {
  display: block; margin-top: 5px;
  font-size: var(--fh-text-xs); color: var(--fh-text-muted); line-height: 1.45;
}

/* ================================================== 18. PICK CARD =======
   One small card per machine code on the import wizard: which worker is this?
   The orange edge marks a code the app has not seen before. */
.fh-pickgrid { display: grid; gap: var(--fh-s3); grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.fh-pickcard {
  border: 1px solid var(--fh-line); border-radius: var(--fh-r-sm);
  padding: var(--fh-s3); background: var(--fh-surface);
}
.fh-pickcard.is-new { border-left: 3px solid var(--fh-marigold-500); background: var(--fh-marigold-100); }
.fh-pickcard > b { font-size: var(--fh-text-base); }
.fh-pickcard > small { display: block; color: var(--fh-text-muted); font-size: var(--fh-text-xs);
  line-height: 1.45; margin: 2px 0 var(--fh-s2); }
.fh-pickcard select { width: 100%; box-sizing: border-box; min-height: 40px; padding: 8px var(--fh-s3);
  border: 1px solid var(--fh-line-strong); border-radius: var(--fh-r-sm);
  font-family: inherit; font-size: var(--fh-text-base); background: var(--fh-surface); }
@media (max-width: 520px) { .fh-pickgrid { grid-template-columns: 1fr; } }

/* ==================================================== 19. STEPPER =======
   For a three-step job like the import wizard. Numbers only when the order
   genuinely matters - otherwise they are decoration. */
.fh-steps { display: grid; gap: var(--fh-s2); grid-template-columns: 1fr; }
@media (min-width: 700px) { .fh-steps { grid-template-columns: repeat(3, 1fr); gap: var(--fh-s3); } }
.fh-step {
  display: grid; grid-template-columns: 30px 1fr; grid-template-rows: auto auto;
  gap: 1px var(--fh-s3); align-items: center;
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md); padding: var(--fh-s3) var(--fh-s4);
}
.fh-step__n {
  grid-row: 1 / 3; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--fh-surface-3); color: var(--fh-text-muted);
  font-family: var(--fh-font-data); font-weight: var(--fh-fw-semi); font-size: var(--fh-text-sm);
}
.fh-step__t { font-weight: var(--fh-fw-semi); font-size: var(--fh-text-base); }
.fh-step__s { font-size: var(--fh-text-xs); color: var(--fh-text-muted); }
.fh-step.is-done { border-color: var(--fh-present-line); background: var(--fh-present-bg); }
.fh-step.is-done .fh-step__n { background: var(--fh-present); color: #fff; }
.fh-step.is-now { border-color: var(--fh-marigold-500); box-shadow: var(--fh-sh-sm); }
.fh-step.is-now .fh-step__n { background: var(--fh-marigold-500); color: var(--fh-navy-900); }

/* ====================================================== 20. PLAN CARD ====
   The subscription picker on renew.php / subscribe.php. One card per plan;
   the customer's current plan is marked so switching is a deliberate act.
   ========================================================================== */
/* Term switch above the pricing grid. The saving sits ON the button, so the
   reason to press it does not need to be hunted for. */
.fh-termbar {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--fh-surface-3); border-radius: 999px;
  margin: 0 auto var(--fh-s3); position: relative; left: 50%; transform: translateX(-50%);
}
.fh-termbar__btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: var(--fh-text-sm); font-weight: var(--fh-fw-semi);
  color: var(--fh-text-2); padding: 8px var(--fh-s4); border-radius: 999px;
  transition: background var(--fh-dur-fast) var(--fh-ease), color var(--fh-dur-fast) var(--fh-ease);
  -webkit-appearance: none; appearance: none; white-space: nowrap;
}
.fh-termbar__btn:hover { color: var(--fh-text); }
.fh-termbar__btn.is-active { background: var(--fh-navy-800); color: #fff; box-shadow: var(--fh-sh-sm); }
.fh-termbar__off {
  font-size: 11px; font-weight: var(--fh-fw-bold);
  background: var(--fh-marigold-500); color: var(--fh-navy-900);
  padding: 1px 7px; border-radius: 999px; letter-spacing: .01em;
}
.fh-termbar__btn.is-active .fh-termbar__off { background: var(--fh-marigold-500); }
@media (max-width: 420px) {
  .fh-termbar { width: 100%; left: 0; transform: none; justify-content: space-between; }
  .fh-termbar__btn { padding: 8px var(--fh-s2); font-size: var(--fh-text-xs); }
  .fh-termbar__off { font-size: 10px; padding: 1px 5px; }
}
/* the "billed once for 3 years" line, with the list price and the saving */
.fh-plan__per s { color: var(--fh-text-muted); margin-left: 6px; }
.fh-plan__per b { color: var(--fh-present); }

.fh-plangrid {
  display: grid; gap: var(--fh-s4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin: var(--fh-s4) 0;
}
.fh-plan {
  position: relative; display: flex; flex-direction: column;
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md); padding: var(--fh-s5) var(--fh-s4) var(--fh-s4);
  box-shadow: var(--fh-sh-sm);
}
.fh-plan.is-current { border-color: var(--fh-marigold-500); box-shadow: var(--fh-sh-md); }
.fh-plan__badge {
  position: absolute; top: -11px; left: var(--fh-s4);
  background: var(--fh-marigold-500); color: var(--fh-navy-900);
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-bold);
  text-transform: uppercase; letter-spacing: var(--fh-track-label);
  padding: 3px var(--fh-s3); border-radius: 999px;
}
.fh-plan__name {
  font-size: var(--fh-text-md); font-weight: var(--fh-fw-semi);
  color: var(--fh-navy-800); margin-bottom: var(--fh-s2);
}
.fh-plan__price {
  font-family: var(--fh-font-data); font-size: var(--fh-text-2xl);
  font-weight: var(--fh-fw-bold); color: var(--fh-text); line-height: 1.1;
}
.fh-plan__price span { font-family: var(--fh-font-ui); font-size: var(--fh-text-sm);
  font-weight: var(--fh-fw-normal); color: var(--fh-text-muted); }
/* The rule above exists for the "/year" suffix. On the landing page the PRICE
   itself sits in a span too (so the term toggle can swap it), and was being
   shrunk to suffix size. Two classes beat one, so the number wins back. */
.fh-plan__price .js-year {
  font-family: var(--fh-font-data); font-size: inherit;
  font-weight: inherit; color: var(--fh-text);
}

/* ---- the three numbers he asked to see clearly: per-year, total, saving ---- */
/* Total: what actually leaves the bank. Second in weight only to the per-year
   figure, because that is what a customer compares across plans. */
.fh-plan__billing {
  margin-top: var(--fh-s2); font-size: var(--fh-text-sm);
  color: var(--fh-text-2); line-height: 1.5;
}
.fh-plan__billing b { color: var(--fh-text); font-weight: var(--fh-fw-bold); font-family: var(--fh-font-data); }
.fh-plan__billing s { color: var(--fh-text-muted); font-family: var(--fh-font-data); }
/* The saving is the reason to choose a longer term, so it gets a pill of its
   own rather than being one more grey number in a row. */
.fh-plan__save {
  display: inline-block; margin-top: 4px;
  background: var(--fh-present-bg); color: var(--fh-present);
  font-family: var(--fh-font-data); font-size: var(--fh-text-xs);
  font-weight: var(--fh-fw-bold); padding: 2px var(--fh-s3);
  border-radius: 999px; white-space: nowrap;
}
.fh-plan__per { font-size: var(--fh-text-xs); color: var(--fh-text-muted); min-height: 16px; }
/* The normal price, struck through beside the offer price. An offer with
   nothing to compare against is not an offer, it is just a number. */
.fh-plan__was {
  font-family: var(--fh-font-data); font-size: var(--fh-text-md);
  font-weight: var(--fh-fw-normal); color: var(--fh-text-muted);
  text-decoration: line-through; margin-left: 8px;
}
/* Line 1 of an offer: WHEN it ends. Meant to be noticed. */
.fh-plan__offer {
  margin: var(--fh-s2) 0 0; padding: 6px var(--fh-s3);
  background: var(--fh-marigold-100); border-radius: var(--fh-r-sm);
  font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  color: var(--fh-marigold-700); line-height: 1.4;
}
/* Line 2: what it costs AFTER. Small and quiet - it must be stated, but it is
   not the thing we are selling. Kept separate from line 1 on his instruction. */
.fh-plan__after {
  margin: 4px 0 0; font-size: 11.5px; color: var(--fh-text-muted);
  letter-spacing: .01em;
}
/* A multi-year plan says what actually leaves the bank, once. */
.fh-plan__term {
  margin: var(--fh-s2) 0 0; font-size: var(--fh-text-xs);
  font-weight: var(--fh-fw-semi); color: var(--fh-text-2);
}
.fh-plan__feat { list-style: none; margin: var(--fh-s4) 0; padding: 0; flex: 1 1 auto; }
.fh-plan__feat li {
  position: relative; padding-left: 22px; margin-bottom: 6px;
  font-size: var(--fh-text-sm); color: var(--fh-text-2);
}
.fh-plan__feat li::before {
  content: ""; position: absolute; left: 2px; top: 6px;
  width: 10px; height: 6px; border-left: 2px solid var(--fh-present);
  border-bottom: 2px solid var(--fh-present); transform: rotate(-45deg);
}

/* ======================================================= 21. AUTH PAGE ===
   Login, sign-up and the payment page - the only screens with no app shell
   around them. Navy field, one centred card, brand at the top. These are the
   first thing a new customer ever sees, so they carry the brand, not chrome.
   ========================================================================== */
.fh-auth {
  min-height: 100vh; min-height: 100dvh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: var(--fh-s5) var(--fh-s4);
  background: var(--fh-navy-900);
  background-image: radial-gradient(circle at 20% 0%, rgba(253,134,5,.10), transparent 55%);
}
.fh-auth__card {
  width: 100%; max-width: 420px;
  background: var(--fh-surface); border-radius: var(--fh-r-lg);
  box-shadow: 0 18px 50px rgba(0,0,0,.32);
  overflow: hidden;
}
.fh-auth__card--wide { max-width: 720px; }
.fh-auth__band { height: 5px; background: var(--fh-marigold-500); }
.fh-auth__body { padding: var(--fh-s6) var(--fh-s5) var(--fh-s5); }
.fh-auth__mark { display: block; height: 34px; width: auto; margin: 0 auto var(--fh-s5); }
.fh-auth__lead { text-align: center; color: var(--fh-text-2); font-size: var(--fh-text-sm);
  margin: calc(var(--fh-s5) * -1 + 4px) 0 var(--fh-s5); }
.fh-auth__foot {
  margin-top: var(--fh-s4); text-align: center;
  color: var(--fh-text-on-dark-soft); font-size: var(--fh-text-xs);
}
.fh-auth__note { text-align: center; font-size: var(--fh-text-sm);
  color: var(--fh-text-muted); margin: var(--fh-s3) 0 0; }
.fh-auth__note b { color: var(--fh-text-2); }
/* password box with a Show/Hide toggle inside it */
.fh-pw { position: relative; display: block; }
.fh-pw .fh-input { padding-right: 62px; }
.fh-pw button {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; cursor: pointer; padding: 6px var(--fh-s2);
  font: inherit; font-size: var(--fh-text-xs); font-weight: var(--fh-fw-semi);
  color: var(--fh-marigold-700);
}
@media (max-width: 420px) {
  .fh-auth { padding: var(--fh-s4) var(--fh-s3); }
  .fh-auth__body { padding: var(--fh-s5) var(--fh-s4) var(--fh-s4); }
}

/* A plan card that is CHOSEN rather than bought - the sign-up wizard wraps
   each one in a <label> around a hidden radio. */
.fh-plan--pick { cursor: pointer; }
.fh-plan--pick:hover { border-color: var(--fh-line-strong); }
.fh-plan--pick.is-selected {
  border-color: var(--fh-marigold-500); box-shadow: 0 0 0 2px var(--fh-marigold-200);
}
.fh-plan__badge--pop { background: var(--fh-navy-800); color: #fff; }

/* ==================================================== 22. UPI APP PICKER ==
   One button per UPI app on the payment page. A single upi:// link opens
   whichever app the PHONE decided owns that scheme, so the customer has to be
   able to choose by name instead.
   ========================================================================== */
.fh-payapps { display: grid; gap: var(--fh-s2); grid-template-columns: repeat(2, 1fr); }
.fh-payapp {
  display: flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 10px var(--fh-s3);
  border: 1px solid var(--fh-line-strong); border-radius: var(--fh-r-sm);
  background: var(--fh-surface); color: var(--fh-text);
  font-size: var(--fh-text-base); font-weight: var(--fh-fw-semi);
  text-align: center; text-decoration: none;
  transition: border-color var(--fh-dur-fast) var(--fh-ease),
              background var(--fh-dur-fast) var(--fh-ease);
}
.fh-payapp:hover { border-color: var(--fh-marigold-500); background: var(--fh-marigold-100); text-decoration: none; }
.fh-payapp:active { transform: translateY(1px); }
/* The generic one spans the row - it is the fallback, not a peer. */
.fh-payapp--other { grid-column: 1 / -1; color: var(--fh-text-2); font-weight: var(--fh-fw-medium); }
@media (min-width: 560px) { .fh-payapps { grid-template-columns: repeat(4, 1fr); } }

/* The plain dd/mm/yyyy reading, printed INSIDE the field's label so it costs
   no height - see dateHint() in fh-ui.js for why that matters. Quiet on
   purpose: it confirms what the browser's own box is showing, it is not a
   second label. */
.fh-label .fh-datehint {
  margin-left: 7px; text-transform: none; letter-spacing: normal;
  font-weight: var(--fh-fw-normal); color: var(--fh-text-muted);
  /* Deliberately NOT the mono font: mono has a taller line box, which made the
     label one pixel taller than its neighbours - and .fh-toolbar aligns to the
     BOTTOM, so that one pixel knocked every other label out of line. Same font
     as the label = identical metrics = nothing moves. */
  font-family: inherit; font-size: inherit; line-height: inherit;
}

/* ==================================================== 23. CHECK LIST =====
   A list of things to tick, each with a short consequence underneath. Used by
   the unit-reset box, where the person needs to read what each one takes with
   it before ticking - so the note is part of the row, not a tooltip.
   ========================================================================== */
.fh-checklist { display: grid; gap: var(--fh-s2); grid-template-columns: 1fr; }
@media (min-width: 760px) { .fh-checklist { grid-template-columns: 1fr 1fr; } }
.fh-check {
  display: flex; gap: var(--fh-s3); align-items: flex-start; cursor: pointer;
  border: 1px solid var(--fh-line); border-radius: var(--fh-r-sm);
  padding: 11px var(--fh-s3); background: var(--fh-surface);
  transition: border-color var(--fh-dur-fast) var(--fh-ease),
              background var(--fh-dur-fast) var(--fh-ease);
}
.fh-check:hover { border-color: var(--fh-line-strong); }
/* The RED state belongs to the reset list, where ticking a box destroys data.
   It was leaking into ordinary settings and making them look dangerous, so it
   is a modifier now: .fh-check--danger. Plain .fh-check stays neutral. */
.fh-check input { margin: 2px 0 0; flex: 0 0 auto; width: 17px; height: 17px; accent-color: var(--fh-navy-700); }
.fh-check:has(input:checked) { border-color: var(--fh-navy-500); background: var(--fh-navy-50); }
.fh-check--danger input { accent-color: var(--fh-absent); }
.fh-check--danger input:checked ~ span b { color: var(--fh-absent); }
.fh-check--danger:has(input:checked) { border-color: var(--fh-absent); background: var(--fh-absent-bg); }
.fh-check b { font-size: var(--fh-text-base); color: var(--fh-text); font-weight: var(--fh-fw-semi); }
.fh-check em {
  font-style: normal; font-family: var(--fh-font-data);
  font-size: var(--fh-text-xs); color: var(--fh-text-muted);
}
.fh-check__note {
  display: block; margin-top: 3px;
  font-size: var(--fh-text-xs); color: var(--fh-text-2); line-height: 1.45;
}
/* Nothing to clear - dimmed, but still tickable (harmless). */
.fh-check.is-empty { opacity: .58; }

/* ==================================================== 24. WAGE SLIP ======
   One worker, one month, one sheet of paper. Designed for the PRINTER first -
   on screen it is just a preview of what will come out. A worker keeps this,
   and sometimes shows it to an inspector, so it carries the legal entity's
   name, the unit, the attendance it is based on, and both signatures.
   ========================================================================== */
.fh-slip {
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md); padding: var(--fh-s5);
  margin-bottom: var(--fh-s5); max-width: 780px;
}
.fh-slip__head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: var(--fh-s4);
  border-bottom: 2px solid var(--fh-navy-800); padding-bottom: var(--fh-s3); margin-bottom: var(--fh-s4);
}
.fh-slip__head h2 { font-size: var(--fh-text-lg); color: var(--fh-navy-800); margin: 0; }
.fh-slip__addr { font-size: var(--fh-text-sm); color: var(--fh-text-2); margin: 2px 0 0; }
.fh-slip__title { text-align: right; flex: 0 0 auto; }
.fh-slip__title b {
  display: block; font-size: var(--fh-text-md); letter-spacing: var(--fh-track-label);
  color: var(--fh-navy-800);
}
.fh-slip__title span { font-size: var(--fh-text-sm); color: var(--fh-text-2); }

.fh-slip__who { width: 100%; border-collapse: collapse; margin-bottom: var(--fh-s4); }
.fh-slip__who td { padding: 6px 10px 6px 0; vertical-align: top; width: 33.33%; }
.fh-slip__who span {
  display: block; font-size: var(--fh-text-xs); text-transform: uppercase;
  letter-spacing: var(--fh-track-label); color: var(--fh-text-muted);
}
.fh-slip__who b { font-size: var(--fh-text-base); color: var(--fh-text); }

.fh-slip__att { width: 100%; border-collapse: collapse; margin-bottom: var(--fh-s4); }
.fh-slip__att th {
  background: var(--fh-surface-2); border: 1px solid var(--fh-line);
  padding: 5px 8px; font-size: var(--fh-text-xs); text-transform: uppercase;
  letter-spacing: var(--fh-track-label); color: var(--fh-text-2); text-align: center;
}
.fh-slip__att td {
  border: 1px solid var(--fh-line); padding: 7px 8px; text-align: center;
  font-family: var(--fh-font-data); font-weight: var(--fh-fw-semi);
}
/* On a narrow phone the row can carry seven or eight columns (month days,
   present, half day, paid leave, absent, weekly off, holiday), which will not
   fit side by side. Stack them into label/value pairs instead of letting the
   table push the page sideways. Print is unaffected - paper is wide. */
@media (max-width: 460px) {
  .fh-slip__att, .fh-slip__att tbody, .fh-slip__att tr { display: block; }
  .fh-slip__att tr:first-child { display: none; }
  .fh-slip__att td {
    display: flex; justify-content: space-between; align-items: baseline;
    text-align: right; border-top: 0; padding: 7px 10px;
  }
  .fh-slip__att tr:last-child td:first-child { border-top: 1px solid var(--fh-line); }
  .fh-slip__att td::before {
    content: attr(data-label); font-family: var(--fh-font-ui);
    font-weight: var(--fh-fw-normal); font-size: var(--fh-text-xs);
    text-transform: uppercase; letter-spacing: var(--fh-track-label);
    color: var(--fh-text-muted); text-align: left;
  }
}

.fh-slip__attnote {
  font-size: var(--fh-text-sm); color: var(--fh-text-2);
  margin: -6px 0 var(--fh-s4);
}
.fh-slip__money { display: grid; gap: var(--fh-s4); grid-template-columns: 1fr; }
@media (min-width: 620px) { .fh-slip__money { grid-template-columns: 1fr 1fr; } }
.fh-slip__money table { width: 100%; border-collapse: collapse; }
.fh-slip__money th {
  background: var(--fh-surface-2); border: 1px solid var(--fh-line);
  padding: 6px 9px; font-size: var(--fh-text-xs); text-transform: uppercase;
  letter-spacing: var(--fh-track-label); color: var(--fh-text-2); text-align: left;
}
.fh-slip__money th.fh-table__num, .fh-slip__money td.fh-table__num { text-align: right; }
.fh-slip__money td {
  border: 1px solid var(--fh-line); padding: 6px 9px; font-size: var(--fh-text-sm);
}
.fh-slip__money td.fh-table__num { font-family: var(--fh-font-data); }
.fh-slip__money tfoot td { background: var(--fh-surface-2); font-weight: var(--fh-fw-semi); }

.fh-slip__net {
  display: flex; justify-content: space-between; align-items: baseline;
  background: var(--fh-navy-800); color: #fff;
  padding: var(--fh-s3) var(--fh-s4); border-radius: var(--fh-r-sm);
  margin-top: var(--fh-s4);
}
.fh-slip__net span { font-size: var(--fh-text-sm); letter-spacing: var(--fh-track-label); text-transform: uppercase; }
.fh-slip__net b { font-family: var(--fh-font-data); font-size: var(--fh-text-xl); }
.fh-slip__net--neg { background: var(--fh-absent); }
.fh-slip__words { font-size: var(--fh-text-sm); color: var(--fh-text-2); margin: 6px 0 0; font-style: italic; }

.fh-slip__foot { display: flex; justify-content: space-between; gap: var(--fh-s5); margin-top: var(--fh-s7); }
.fh-slip__sign { flex: 0 1 44%; }
.fh-slip__sign span {
  display: block; border-top: 1px solid var(--fh-text-2); padding-top: 4px;
  font-size: var(--fh-text-sm); color: var(--fh-text-2);
}
.fh-slip__note { font-size: var(--fh-text-xs); color: var(--fh-text-muted); margin: var(--fh-s4) 0 0; }

/* ================================================== 25. WORKER PORTAL ====
   The one part of the product a WORKER sees, on his own phone. Different
   audience from the rest of the app: bigger type, fewer things per screen, one
   column always. Hindi by default, so the type has to breathe.
   ========================================================================== */
.fh-wapp { background: var(--fh-surface-2); }
.fh-wtop {
  position: sticky; top: 0; z-index: 20;
  background: var(--fh-navy-900); color: #fff;
  padding: 10px var(--fh-s4); padding-top: max(10px, env(safe-area-inset-top));
}
.fh-wtop__in { display: flex; align-items: center; justify-content: space-between; gap: var(--fh-s3);
  max-width: 640px; margin: 0 auto; }
.fh-wtop__brand { display: flex; align-items: center; gap: 8px; font-size: var(--fh-text-md); }
.fh-wtop__right { display: flex; align-items: center; gap: var(--fh-s3); }
.fh-wtop__lang, .fh-wtop__out {
  color: var(--fh-text-on-dark-soft); font-size: var(--fh-text-sm); text-decoration: none;
  border: 1px solid rgba(255,255,255,.22); border-radius: var(--fh-r-sm); padding: 4px 10px;
}
.fh-wtop__lang:hover, .fh-wtop__out:hover { color: #fff; border-color: rgba(255,255,255,.45); text-decoration: none; }

.fh-wmain { max-width: 640px; margin: 0 auto; padding: var(--fh-s4) var(--fh-s3) var(--fh-s7); }
.fh-wcard {
  background: var(--fh-surface); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-md); padding: var(--fh-s4); margin-bottom: var(--fh-s4);
  box-shadow: var(--fh-sh-xs);
}
.fh-wcard--narrow { max-width: 400px; margin-left: auto; margin-right: auto; margin-top: var(--fh-s6); }
.fh-wh1 { font-size: var(--fh-text-xl); color: var(--fh-navy-800); margin: 0; }
.fh-wh2 { font-size: var(--fh-text-md); color: var(--fh-navy-800); margin: 0 0 var(--fh-s3); }
.fh-wsub { font-size: var(--fh-text-sm); color: var(--fh-text-2); margin: 3px 0 0; }
.fh-whead { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--fh-s3); }

/* "You last signed in on ..." - quiet, but it is the thing that catches a
   shared PIN, so it sits right under the name. */
.fh-wlast {
  font-size: var(--fh-text-sm); color: var(--fh-text-2); line-height: 1.5;
  margin: var(--fh-s3) 0 0; padding-top: var(--fh-s3); border-top: 1px solid var(--fh-line);
}

.fh-wmonth { display: flex; align-items: center; justify-content: space-between; gap: var(--fh-s3);
  margin-bottom: var(--fh-s3); }
.fh-wmonth .fh-input { width: auto; max-width: 170px; }

.fh-wstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: var(--fh-s2); }
/* Money tiles need more room than a day count - "Rs 10,000" in a 76px tile
   clipped on a 320px phone. Two per row there, not four. */
.fh-wstats--money { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }
@media (max-width: 360px) { .fh-wstats--money { grid-template-columns: 1fr 1fr; } }
.fh-wstats > div {
  background: var(--fh-surface-2); border: 1px solid var(--fh-line);
  border-radius: var(--fh-r-sm); padding: 10px 8px; text-align: center;
}
.fh-wstats b { display: block; font-family: var(--fh-font-data); font-size: var(--fh-text-lg);
  font-weight: var(--fh-fw-bold); color: var(--fh-navy-800); }
.fh-wstats span { font-size: var(--fh-text-xs); color: var(--fh-text-2); }
.fh-wstats--money b { font-size: var(--fh-text-md); }
.fh-wstats .is-hi { background: var(--fh-marigold-100); border-color: var(--fh-marigold-500); }

.fh-wbig {
  display: flex; align-items: baseline; justify-content: space-between;
  background: var(--fh-navy-800); color: #fff; border-radius: var(--fh-r-sm);
  padding: var(--fh-s3) var(--fh-s4); margin-top: var(--fh-s3);
}
.fh-wbig span { font-size: var(--fh-text-sm); color: var(--fh-text-on-dark-soft); }
.fh-wbig b { font-family: var(--fh-font-data); font-size: var(--fh-text-xl); }
.fh-wnote { font-size: var(--fh-text-sm); color: var(--fh-text-2); line-height: 1.55; margin: var(--fh-s3) 0 0; }
/* Money actually cut this month. It is the one line on the worker's screen he
   must not skim past, so it carries the same red the hajri grid uses for an
   absent day - the colour already means "a day was lost" to him. */
.fh-wcut, .fh-wok, .fh-wpend {
  display: block; margin-bottom: 6px; padding: 8px 11px;
  border-radius: var(--fh-r-sm); font-size: var(--fh-text-base);
}
/* Month not closed yet. Deliberately NOT red - nothing is wrong, the figures
   are simply not final. Amber says "wait", red would say "problem". */
.fh-wpend {
  background: var(--fh-marigold-100); color: var(--fh-marigold-700);
  border-left: 3px solid var(--fh-marigold-500); font-weight: var(--fh-fw-semi);
}
.fh-wcut {
  background: var(--fh-absent-bg); color: var(--fh-absent);
  border-left: 3px solid var(--fh-absent); font-weight: var(--fh-fw-semi);
}
.fh-wcut b { font-family: var(--fh-font-data); font-weight: var(--fh-fw-bold); }
.fh-wok {
  background: var(--fh-present-bg); color: var(--fh-present);
  border-left: 3px solid var(--fh-present); font-weight: var(--fh-fw-medium);
}
.fh-wnote__body { display: block; }

.fh-wtable { width: 100%; border-collapse: collapse; }
.fh-wtable th {
  text-align: left; font-size: var(--fh-text-xs); text-transform: uppercase;
  letter-spacing: var(--fh-track-label); color: var(--fh-text-muted);
  padding: 4px 6px; border-bottom: 1px solid var(--fh-line);
}
.fh-wtable td { padding: 8px 6px; border-bottom: 1px solid var(--fh-line); font-size: var(--fh-text-base); }
.fh-wtable tr:last-child td { border-bottom: 0; }
.fh-wtable--kv td:first-child { color: var(--fh-text-2); width: 42%; font-size: var(--fh-text-sm); }
/* The break-up rows: a deduction in the absent red, the closing line in navy
   with a rule above it, like the last line of a bill. */
.fh-wtable .is-cut td { color: var(--fh-absent); }
.fh-wtable .is-net td {
  border-top: 2px solid var(--fh-line-strong); border-bottom: 0;
  color: var(--fh-navy-800); font-weight: var(--fh-fw-semi); padding-top: 10px;
}
.fh-wtable .is-net td:last-child { font-family: var(--fh-font-data); font-size: var(--fh-text-md); }
.fh-wtable td.fh-t-num { text-align: right; font-family: var(--fh-font-data); white-space: nowrap; }

.fh-wlinks { display: grid; gap: var(--fh-s2); }
.fh-wlink {
  display: flex; align-items: center; justify-content: space-between;
  border: 1px solid var(--fh-line); border-radius: var(--fh-r-sm);
  padding: 12px var(--fh-s3); text-decoration: none; color: var(--fh-text);
  background: var(--fh-surface);
}
.fh-wlink:hover { border-color: var(--fh-marigold-500); background: var(--fh-marigold-100); text-decoration: none; }
.fh-wlink b { color: var(--fh-marigold-700); font-size: var(--fh-text-sm); }

.fh-wslipbar { display: flex; justify-content: space-between; gap: var(--fh-s3); margin-bottom: var(--fh-s4); }

@media print {
  .fh-wtop, .fh-wslipbar { display: none !important; }
  .fh-wmain, .fh-wcard { max-width: none; margin: 0; padding: 0; border: 0; box-shadow: none; background: #fff; }
}

/* ================================================ 26. UNIT SWITCHES ======
   A row of on/off settings, each with its explanation folded away behind a
   small info button. These used to live inside a form field with the whole
   paragraph printed underneath - which stretched that column and pulled the
   grid out of shape. The person setting up a unit needs the explanation within
   reach, not open.
   ========================================================================== */
.fh-switches { display: grid; gap: var(--fh-s2); grid-template-columns: 1fr; margin-top: var(--fh-s2); }
@media (min-width: 720px) { .fh-switches { grid-template-columns: repeat(3, 1fr); } }

.fh-switch {
  display: flex; align-items: flex-start; gap: var(--fh-s2);
  border: 1px solid var(--fh-line); border-radius: var(--fh-r-sm);
  padding: 11px var(--fh-s3); background: var(--fh-surface);
  transition: border-color var(--fh-dur-fast) var(--fh-ease),
              background var(--fh-dur-fast) var(--fh-ease);
}
.fh-switch:has(input:checked) { border-color: var(--fh-navy-500); background: var(--fh-navy-50); }
.fh-switch__row { display: flex; align-items: center; gap: var(--fh-s2); cursor: pointer; flex: 1 1 auto; }
.fh-switch__row input { width: 17px; height: 17px; flex: 0 0 auto; accent-color: var(--fh-navy-700); margin: 0; }
.fh-switch__row b { font-size: var(--fh-text-base); font-weight: var(--fh-fw-semi); color: var(--fh-text); }

/* The info button. <details> so it needs no JS and still works if scripts
   never run - the explanation must not be the thing that breaks. */
.fh-info { position: relative; flex: 0 0 auto; }
.fh-info > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; color: var(--fh-text-muted);
}
.fh-info > summary::-webkit-details-marker { display: none; }
.fh-info > summary:hover { color: var(--fh-navy-700); background: var(--fh-surface-2); }
.fh-info[open] > summary { color: var(--fh-navy-700); background: var(--fh-navy-50); }
/* Absolute is the no-JS fallback; fh-ui.js switches it to fixed on open so a
   card's overflow:hidden cannot slice it. z-index has to clear the sticky
   topbar (20) and the rail. */
.fh-info__box {
  position: absolute; right: 0; top: 28px; z-index: 60; width: min(300px, 78vw);
  background: var(--fh-surface); border: 1px solid var(--fh-line-strong);
  border-radius: var(--fh-r-sm); box-shadow: var(--fh-sh-md);
  padding: var(--fh-s3); font-size: var(--fh-text-sm); line-height: 1.55; color: var(--fh-text-2);
  font-weight: var(--fh-fw-normal);
}
.fh-info__box b { color: var(--fh-text); }
/* A sheet across the bottom of the screen, because on a phone one of these
   paragraphs is taller than the room either above or below the button.

   The phone rule is a MEDIA QUERY with !important, not the .is-sheet class,
   and that is deliberate: fh-ui.js positions this box with inline styles, and
   an inline style beats a plain stylesheet rule. If a browser is still holding
   an older fh-ui.js - which is exactly what happened on his phone - those
   inline styles would drag the sheet off the bottom of the screen. CSS alone
   must be able to get this right. .is-sheet is still used above 520px, for a
   short desktop window where the box fits neither above nor below. */
.fh-info__box.is-sheet,
.fh-info[open] > .fh-info__box:where(.is-sheet) {
  position: fixed; left: 0; right: 0; bottom: 0; top: auto;
  width: auto; max-height: 62vh; overflow-y: auto;
  border-radius: var(--fh-r-md) var(--fh-r-md) 0 0;
  border-left: 0; border-right: 0; border-bottom: 0;
  padding: var(--fh-s4) var(--fh-s4) calc(var(--fh-s4) + env(safe-area-inset-bottom));
  box-shadow: 0 -8px 28px rgba(4,18,42,.22);
  font-size: var(--fh-text-base);
}
@media (max-width: 520px) {
  .fh-info__box {
    position: fixed !important;
    left: 0 !important; right: 0 !important; bottom: 0 !important; top: auto !important;
    width: auto !important; max-height: 62vh; overflow-y: auto;
    border-radius: var(--fh-r-md) var(--fh-r-md) 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    padding: var(--fh-s4) var(--fh-s4) calc(var(--fh-s4) + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 28px rgba(4,18,42,.22);
    font-size: var(--fh-text-base);
  }
}
