/* ==========================================================================
   Smart Queue Manager -- University of Antique
   Design system + application styles
   ========================================================================== */

:root {
  /* --- University of Antique seal -------------------------------------- */
  /* The five colours lifted straight off the seal. The system runs on the
     red; the rest stay available as accents and as the seal's own ink. */
  --seal-indigo: #2e2c7e;  /* ring and waves   */
  --seal-red:    #d42127;  /* lettering, lamp  */
  --seal-gold:   #f5b81c;  /* triangle         */
  --seal-green:  #00a551;  /* tree             */
  --seal-gray:   #9aa4af;  /* swallows         */

  /* Brand: the seal's red, deepened until white text sits on it at 7.7:1
     and opened out into a ramp. -700 is the hover step, darker than -600. */
  --brand-900: #3d0a0d;
  --brand-800: #6b0f13;
  --brand-700: #861217;
  --brand-600: #a4161a;
  --brand-500: #d4464c;
  --brand-100: #fadcdd;
  --brand-50:  #fdf1f1;

  /* Gold: the triangle. -600 is darkened until it carries text on white. */
  --gold-600: #8f6408;
  --gold-500: #f5b81c;
  --gold-100: #fdf1cf;

  /* Semantic. Danger is deliberately a brighter, more orange red than the
     brand crimson, so a destructive button never reads as the primary one. */
  --ok-600: #0b7a3c;   --ok-100: #d5f2e0;
  --warn-600: #8f6408; --warn-100: #fdefcd;
  --err-600: #c22a16;  --err-100: #fbe1dc;
  --info-600: #2a2873; --info-100: #e0dff6;

  /* Surfaces: neutrals tinted a few degrees towards the brand red */
  --bg:        #faf6f6;
  --surface:   #ffffff;
  --surface-2: #fdf9f9;
  --surface-3: #f5eded;
  --border:    #dbcbcb;
  /* Carries the edge of a form control, so it has to clear 3:1 against the
     white those sit on (WCAG 1.4.11) -- the old #cdb8b8 was 1.9:1 and fields
     read as edgeless boxes on the warm background. */
  --border-strong: #a68789;

  /* Text */
  --text:      #241618;
  --text-2:    #695659;
  /* Small meta text at .68-.85rem, so it needs 4.5:1 on every surface it
     lands on, the tinted --surface-3 included. */
  --text-3:    #796468;
  --on-brand:  #ffffff;

  /* The sidebar is deep maroon in both themes, so its ink is themed apart. */
  --side-text:  #f0dcdd;
  --side-label: #dda9ac;
  --side-dim:   #c99196;
  --side-active: #a3181f;

  /* Ink that sits on gold. Dark enough to read at badge sizes. */
  --on-gold: #2e2100;

  /* Geometry */
  --r-sm: 6px; --r: 10px; --r-lg: 14px; --r-xl: 20px;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  --shadow-1: 0 1px 2px rgba(61, 10, 13, .06), 0 1px 3px rgba(61, 10, 13, .08);
  --shadow-2: 0 4px 12px rgba(61, 10, 13, .10), 0 2px 4px rgba(61, 10, 13, .06);
  --shadow-3: 0 12px 32px rgba(61, 10, 13, .16);

  --sidebar-w: 248px;
  --appbar-h: 60px;

  --font: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: "SF Mono", "Cascadia Mono", "Consolas", ui-monospace, monospace;

  /* Tells the browser which palette its own controls are drawn in -- the list
     a dropdown opens, the clock in a time field, scrollbars. Without it they
     stay light in the dark theme, and light option text lands on a white list. */
  color-scheme: light;
}

/* The dark palette keeps the same warm cast, dropped to night levels. The
   brand ramp lightens so links stay legible, which flips --on-brand to dark
   ink for anything sitting ON brand. The seal colours are left alone. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg:        #170d0e;
    --surface:   #211517;
    --surface-2: #2a1b1d;
    --surface-3: #372426;
    --border:    #452e30;
    --border-strong: #5f4144;

    --text:      #f6eaea;
    --text-2:    #cfb3b5;
    --text-3:    #a68c8f;

    --brand-100: #4d1418;
    --brand-50:  #3a0f13;
    --brand-500: #f4989c;
    --brand-600: #f0777c;
    --brand-700: #d95a60;
    --on-brand:  #3d0a0d;

    --ok-100: #0d3524;   --ok-600: #4fd894;
    --warn-100: #3a2c0c; --warn-600: #eab94f;
    --err-100: #4a2018;  --err-600: #ff9576;
    --info-100: #221f4e; --info-600: #9d99e6;

    --gold-100: #3a2c0c;
    --shadow-1: 0 1px 2px rgba(0,0,0,.3);
    --shadow-2: 0 4px 14px rgba(0,0,0,.4);
    --shadow-3: 0 14px 36px rgba(0,0,0,.55);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:        #170d0e;
  --surface:   #211517;
  --surface-2: #2a1b1d;
  --surface-3: #372426;
  --border:    #452e30;
  --border-strong: #5f4144;
  --text:      #f6eaea;
  --text-2:    #cfb3b5;
  --text-3:    #a68c8f;
  --brand-100: #4d1418;
  --brand-50:  #3a0f13;
  --brand-500: #f4989c;
  --brand-600: #f0777c;
  --brand-700: #d95a60;
  --on-brand:  #3d0a0d;
  --ok-100: #0d3524;   --ok-600: #4fd894;
  --warn-100: #3a2c0c; --warn-600: #eab94f;
  --err-100: #4a2018;  --err-600: #ff9576;
  --info-100: #221f4e; --info-600: #9d99e6;
  --gold-100: #3a2c0c;
  --shadow-1: 0 1px 2px rgba(0,0,0,.3);
  --shadow-2: 0 4px 14px rgba(0,0,0,.4);
  --shadow-3: 0 14px 36px rgba(0,0,0,.55);
}

/* ----------------------------------------------------------------- icons -- */
/* Icons are drawn in currentColor, so they inherit the ink of whatever they
   sit in and need no per-context colour rules. */
.icon { display: block; flex: none; }
.nav-link .icon, .tab .icon, .btn .icon { pointer-events: none; }

/* ---------------------------------------------------------------- reset -- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

/* The browser's own [hidden] rule is a UA-stylesheet declaration, so ANY
   author rule that sets `display` (e.g. .modal { display: grid }) silently
   overrides it and the element stays on screen. Restate it at author level so
   the hidden attribute is always honoured. */
[hidden] { display: none !important; }

body {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 var(--sp-2); line-height: 1.25; font-weight: 650; letter-spacing: -.01em; }
h1 { font-size: 1.6rem; } h2 { font-size: 1.25rem; } h3 { font-size: 1.05rem; } h4 { font-size: .95rem; }
p { margin: 0 0 var(--sp-3); }
a { color: var(--brand-600); text-decoration: none; }
a:hover { text-decoration: underline; }
img, svg { max-width: 100%; }

:focus-visible {
  outline: 3px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--brand-800); color: #fff; padding: var(--sp-3) var(--sp-4);
  border-radius: 0 0 var(--r) 0;
}
.skip-link:focus { left: 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ----------------------------------------------------------------- boot -- */
.boot {
  position: fixed; inset: 0; display: grid; place-content: center; gap: var(--sp-4);
  justify-items: center; background: var(--bg); z-index: 50;
}
.boot__mark {
  position: relative; width: 96px; height: 96px;
  display: grid; place-items: center;
}
.boot__mark img { width: 72px; height: 72px; display: block; }
.boot__mark::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 4px solid var(--brand-100); border-top-color: var(--brand-600);
  animation: spin .9s linear infinite;
}
.boot__text { color: var(--text-2); margin: 0; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .boot__mark::after { animation-duration: 3s; }
  * { scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------- layout --- */
#app {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a plain 1fr column grows to the widest thing in it,
     and the app bar's title (one line, no wrapping) made every page with a long
     title scroll sideways on a small phone. The title truncates instead. */
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: var(--appbar-h) 1fr;
  grid-template-areas: "brand appbar" "sidebar main";
  min-height: 100vh; min-height: 100dvh;
}

.appbar {
  grid-area: appbar;
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 0 var(--sp-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}

.sidebar {
  grid-area: sidebar / brand;
  grid-row: 1 / -1;
  background: var(--brand-900);
  color: var(--side-text);
  display: flex; flex-direction: column;
  padding: var(--sp-4) var(--sp-3);
  gap: var(--sp-1);
  overflow-y: auto;
  position: sticky; top: 0; height: 100vh; height: 100dvh;
}

.main {
  grid-area: main;
  padding: var(--sp-5);
  max-width: 1280px;
  width: 100%;
  min-width: 0;   /* a grid item defaults to min-content: without this the
                     widest cell in any table scrolls the whole page sideways */
  outline: none;
}

.tabbar { display: none; }

/* Backdrop for the mobile navigation drawer. Without it the drawer could only
   be closed by picking one of its own links. */
.scrim { display: none; }

@media (max-width: 900px) {
  #app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: var(--appbar-h) 1fr auto;
    grid-template-areas: "appbar" "main" "tabbar";
  }
  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: 84vw; max-width: 300px;
    transform: translateX(-102%); transition: transform .22s ease;
    z-index: 40; height: 100dvh; box-shadow: var(--shadow-3);
  }
  .sidebar.is-open { transform: none; }
  .scrim.is-open {
    display: block; position: fixed; inset: 0; z-index: 35;
    background: rgba(23, 13, 14, .55);
  }
  .main { padding: var(--sp-4) var(--sp-3) 92px; }
  .tabbar {
    grid-area: tabbar; display: flex; position: fixed; bottom: 0; left: 0; right: 0;
    background: var(--surface); border-top: 1px solid var(--border);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); z-index: 30;
  }
}
@media (min-width: 901px) { .appbar__menu { display: none; } }

/* ------------------------------------------------------------ sidebar ---- */
.brand {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3) var(--sp-5);
  color: #fff;
}
.brand__logo {
  width: 42px; height: 42px; flex: none; display: block; border-radius: 50%;
  background: #fff; box-shadow: 0 0 0 1px rgba(255, 255, 255, .3);
}
.brand__name { display: block; font-weight: 700; font-size: .98rem; line-height: 1.2; }
.brand__sub {
  display: block; font-size: .72rem; color: var(--side-label);
  letter-spacing: .04em; text-transform: uppercase; line-height: 1.3;
}

.nav-group { margin-top: var(--sp-4); }
.nav-group__label {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--side-dim); padding: 0 var(--sp-3) var(--sp-2); font-weight: 700;
}
.nav-link {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px var(--sp-3); border-radius: var(--r);
  color: var(--side-text); font-size: .9rem; font-weight: 500;
  border: none; background: none; width: 100%; text-align: left;
  cursor: pointer; font-family: inherit;
}
.nav-link:hover { background: rgba(255,255,255,.08); color: #fff; text-decoration: none; }
.nav-link.is-active { background: var(--side-active); color: #fff; font-weight: 600; }
.nav-link__icon { display: grid; place-items: center; width: 20px; flex: none; opacity: .9; }
.nav-link__badge {
  margin-left: auto; background: var(--gold-500); color: var(--on-gold);
  border-radius: 999px; padding: 1px 7px; font-size: .72rem; font-weight: 700;
}
.sidebar__footer { margin-top: auto; padding-top: var(--sp-4); font-size: .75rem; color: var(--side-dim); }

/* ------------------------------------------------------------- appbar ---- */
.appbar__menu {
  background: none; border: 1px solid var(--border); border-radius: var(--r-sm);
  width: 38px; height: 38px; font-size: 1.1rem; cursor: pointer; color: var(--text);
}
.appbar__title {
  font-weight: 650; font-size: 1.02rem;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.appbar__spacer { flex: 1; }
.appbar__actions { display: flex; align-items: center; gap: var(--sp-2); }

.chip-clock {
  font-variant-numeric: tabular-nums; font-size: .85rem; color: var(--text-2);
  padding: 4px 10px; background: var(--surface-3); border-radius: 999px;
}
@media (max-width: 700px) { .chip-clock { display: none; } }

.icon-btn {
  position: relative; width: 38px; height: 38px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface);
  cursor: pointer; color: var(--text);
  display: grid; place-content: center;
}
.icon-btn:hover { background: var(--surface-3); }
.icon-btn__dot {
  position: absolute; top: 5px; right: 5px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 999px; background: var(--seal-red); color: #fff;
  font-size: .66rem; font-weight: 700; display: grid; place-content: center;
}

.avatar {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  background: var(--brand-600); color: var(--on-brand);
  display: grid; place-content: center; font-weight: 700; font-size: .82rem;
}

/* -------------------------------------------------------------- tabbar --- */
.tab {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 6px 2px; border: none; background: none; cursor: pointer;
  color: var(--text-3); font-size: .68rem; font-family: inherit; border-radius: var(--r-sm);
}
.tab.is-active { color: var(--brand-600); font-weight: 650; }
.tab__icon { display: grid; place-items: center; }

/* --------------------------------------------------------------- cards --- */
.card {
  --card-pad: var(--sp-5);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  padding: var(--card-pad); margin-bottom: var(--sp-4);
}
.card--flat { box-shadow: none; }
.card--pad-sm { --card-pad: var(--sp-4); }
/* Something the client has to act on, lifted out of the ordinary run of cards. */
.card--attention { border-left: 4px solid var(--warn-600); }
.attention__row { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
.attention__text { flex: 1; min-width: 220px; }
.attention__row .btn-row { margin-left: auto; }
.card__head {
  display: flex; align-items: flex-start; gap: var(--sp-3); flex-wrap: wrap;
  /* Falls back when a head is used outside a card, where the token is unset. */
  margin: calc(var(--card-pad, var(--sp-5)) * -1) calc(var(--card-pad, var(--sp-5)) * -1) var(--sp-4);
  padding: var(--sp-4) var(--card-pad, var(--sp-5));
  border-bottom: 1px solid var(--border); background: var(--surface-2);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.card__head h2, .card__head h3 { margin: 0; }
.card__head-actions { margin-left: auto; display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.card__sub { color: var(--text-2); font-size: .86rem; margin: 2px 0 0; }

.grid { display: grid; gap: var(--sp-4); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

/* A grid item is min-content wide by default, and a card's min-content includes
   the widest table inside it. One six-column table was therefore setting the
   column width for the whole grid, and on a phone every card in the row -- even
   a bar chart with nothing wide in it -- was pushed past the screen edge with
   it. Letting the item shrink hands the problem back to .table-wrap, which is
   the element that knows how to scroll. */
.grid > .card, .grid > .stat { min-width: 0; }
.table-wrap { min-width: 0; }

/* Cards sized to what is in them.
   Stretching every card in a row to the tallest left a chart with two bars in
   it followed by most of a screen of empty panel. The row still ends level;
   the space is simply below the short card rather than inside it. */
.grid--cards { align-items: start; }

/* A card that takes the whole row, whatever the column count.
   A five-column table squeezed into a third of the width clips its last column
   behind a scrollbar and hyphenates every heading; given the full row it simply
   fits. It also keeps the tall tables out of the same row as the short charts,
   which were being stretched to match and left with a screen of empty space. */
.grid > .card--full { grid-column: 1 / -1; }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

.page-head {
  display: flex; align-items: flex-end; gap: var(--sp-4);
  flex-wrap: wrap; margin-bottom: var(--sp-5);
}
.page-head__text { flex: 1; min-width: 240px; }
.page-head h1 { margin: 0; }
.page-head p { margin: 4px 0 0; color: var(--text-2); }

/* --------------------------------------------------------------- stats --- */
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-4); box-shadow: var(--shadow-1);
}
.stat__label {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-3); font-weight: 700; margin-bottom: 6px;
}
.stat__value { font-size: 1.9rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat__meta { font-size: .8rem; color: var(--text-2); margin-top: 4px; }
.stat--brand { background: var(--brand-800); border-color: var(--brand-800); color: #fff; }
.stat--brand .stat__label { color: #eab9bb; }
.stat--brand .stat__meta { color: #f3dcdd; }

/* ------------------------------------------------------------- buttons --- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  padding: 9px 16px; border-radius: var(--r); border: 1px solid transparent;
  font: inherit; font-size: .9rem; font-weight: 600; cursor: pointer;
  background: var(--brand-600); color: var(--on-brand); white-space: nowrap;
  transition: background .14s ease, transform .06s ease;
}
.btn:hover { background: var(--brand-700); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
.btn--secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn--secondary:hover { background: var(--surface-3); }
.btn--ghost { background: transparent; color: var(--brand-600); }
.btn--ghost:hover { background: var(--brand-50); }
.btn--danger { background: var(--err-600); }
.btn--danger:hover { background: #a52514; }
.btn--ok { background: var(--ok-600); }
.btn--ok:hover { background: #075c2c; }
.btn--warn { background: var(--warn-600); }
.btn--lg { padding: 13px 24px; font-size: 1rem; }
.btn--sm { padding: 5px 11px; font-size: .8rem; }
.btn--block { width: 100%; }
.btn-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* The controls beside a page title are one group: on the queue console, the
   date being worked, encoding a walk-in and calling one forward all act on the
   same thing. Letting the group shrink to whatever the heading left over split
   it across two lines with the last button stranded underneath.
   Sizing it by its content is not enough on its own: Chrome works out the
   max-content width of a *wrapping* flex container from its children alone and
   leaves the `gap` between them out of the total, so the group is handed a few
   pixels less than it needs and splits anyway. Telling the group not to wrap
   removes the question -- its width is then simply what it needs. If the
   heading and the group together do not fit, `.page-head` wraps and the group
   takes its own line, where there is room. Below 900px the rule further down
   takes over and gives the group the full width with the buttons sharing it. */
@media (min-width: 901px) {
  .page-head > .btn-row { flex: 0 0 auto; flex-wrap: nowrap; max-width: 100%; }
}
/* A row of filters with its action at the end. Given the room, the button
   holds the line with the controls it acts beside; below that the row wraps
   and the button keeps to the right rather than sitting under the search
   box. Forcing one line at every width would push the row wider than the
   page instead, which is the one outcome worse than a second line. */
.filter-row .btn { margin-left: auto; }
@media (min-width: 1150px) {
  .filter-row { flex-wrap: nowrap; align-items: center; }
  .filter-row .input, .filter-row .select { min-width: 0; }
  .filter-row .btn { flex: none; margin-left: 0; }
}
/* Filters that each carry a label. Two bare date boxes side by side left the
   reader to work out which end of the range each one was. */
.filter-bar { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }
.filter-bar > .field { margin: 0; flex: 1 1 150px; min-width: 0; }
.filter-bar > .filter-bar__grow { flex: 3 1 240px; }
.filter-bar > .btn { flex: none; }
/* One height for every box in the bar. Chrome draws a date box a few pixels
   taller than a text box, and with the fields aligned at the bottom that set
   its label higher than the labels beside it. */
.filter-bar .input, .filter-bar .select { height: 42px; }

/* --------------------------------------------------------------- forms --- */
.field { margin-bottom: var(--sp-4); }
.field__label { display: block; font-weight: 600; font-size: .86rem; margin-bottom: 5px; }
.field__hint { font-size: .79rem; color: var(--text-2); margin-top: 4px; }
.field__error { font-size: .8rem; color: var(--err-600); margin-top: 4px; font-weight: 600; }
.field__req { color: var(--err-600); margin-left: 2px; }

.input, .select, .textarea {
  width: 100%; padding: 9px 12px; font: inherit; font-size: .92rem;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: var(--r);
}
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--brand-500); outline: 3px solid var(--brand-100); outline-offset: 0;
}
.input[aria-invalid="true"], .select[aria-invalid="true"] { border-color: var(--err-600); }
/* The options a dropdown opens, in the theme's own colours for the browsers
   that draw them from the page rather than from color-scheme alone. */
.select option { color: var(--text); background: var(--surface); }
.input:disabled, .select:disabled, .textarea:disabled { background: var(--surface-3); color: var(--text-3); }
.textarea { min-height: 84px; resize: vertical; }
.input--code {
  font-family: var(--mono); font-size: 1.6rem; letter-spacing: .55em;
  text-align: center; padding: 12px; text-indent: .55em;
}

/* A password box with a show/hide button inside its right-hand end. */
.password { position: relative; }
.password .input { padding-right: 48px; }
/* Edge draws its own reveal button in password boxes; one eye is enough. */
.password .input::-ms-reveal, .password .input::-ms-clear { display: none; }
.password__toggle {
  position: absolute; top: 1px; right: 1px; bottom: 1px; width: 44px;
  display: grid; place-content: center;
  border: 0; border-radius: 0 calc(var(--r) - 1px) calc(var(--r) - 1px) 0;
  background: none; color: var(--text-2); cursor: pointer;
}
.password__toggle:hover { color: var(--text); background: var(--surface-3); }
.password__toggle:focus-visible { outline: 3px solid var(--brand-100); outline-offset: -3px; color: var(--text); }

.check { display: flex; gap: var(--sp-3); align-items: flex-start; cursor: pointer; }
.check input { margin-top: 3px; width: 17px; height: 17px; flex: none; accent-color: var(--brand-600); }
.check__text { font-size: .89rem; }
/* The title on a line of its own, so a description after it reads as a
   description rather than running on as part of the same sentence. */
.check__title { font-weight: 600; display: block; }
.check__desc { display: block; color: var(--text-2); font-size: .84rem; margin-top: 1px; }

/* A label that says a field may be left empty. Most fields on the long forms
   are optional, so the few that are not keep the asterisk and the rest say so
   in words rather than leaving the client to guess. */
.field__opt { font-weight: 400; font-size: .8rem; color: var(--text-3); margin-left: 5px; }

/* A choice drawn as a card: the whole box is the thing to tap, and the one
   picked is marked by more than a small dot. Used where the choice decides what
   the rest of a form asks. */
.choice-list { display: grid; gap: var(--sp-2); }
.choice-list--2 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.choice {
  display: flex; gap: var(--sp-3); align-items: flex-start; cursor: pointer;
  padding: 12px 14px; border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface);
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.choice:hover { border-color: var(--brand-500); }
.choice:has(input:checked) {
  border-color: var(--brand-600); background: var(--brand-50);
  box-shadow: inset 0 0 0 1px var(--brand-600);
}
.choice:has(input:focus-visible) { outline: 3px solid var(--brand-100); outline-offset: 1px; }
.choice:has(input:disabled) { opacity: .55; cursor: not-allowed; }
.choice input { margin-top: 3px; width: 17px; height: 17px; flex: none; accent-color: var(--brand-600); }
.choice__body { min-width: 0; }
.choice__title { display: block; font-weight: 650; font-size: .92rem; color: var(--text); }
.choice__desc { display: block; font-size: .83rem; color: var(--text-2); margin-top: 2px; line-height: 1.45; }
.choice__aside { margin-left: auto; padding-left: var(--sp-2); font-weight: 650; font-size: .88rem; white-space: nowrap; }

fieldset { border: 1px solid var(--border); border-radius: var(--r); padding: var(--sp-4); margin: 0 0 var(--sp-4); }
legend { font-weight: 650; font-size: .9rem; padding: 0 6px; }
/* A group of choices already drawn as cards needs no box around it: the
   legend reads as the question, like any field label. */
.fieldset--plain { border: 0; padding: 0; margin: 0 0 var(--sp-4); min-width: 0; }
.fieldset--plain > legend { padding: 0; margin-bottom: 6px; font-weight: 600; font-size: .86rem; }

.form-row { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* -------------------------------------------------------------- tables --- */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r); background: var(--surface); }
.table { width: 100%; border-collapse: collapse; font-size: .87rem; }
.table th, .table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table th {
  background: var(--surface-2); font-weight: 650; font-size: .76rem;
  text-transform: uppercase; letter-spacing: .05em; color: var(--text-2);
  position: sticky; top: 0; z-index: 1;
}
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--surface-2); }
.table--compact th, .table--compact td { padding: 7px 10px; }
.table--compact th { letter-spacing: .03em; }

/* A table inside a half-width card has to fit that card.
   `width: 1%` on a cell asks for its content width and nothing more; the first
   column then takes everything left over and wraps. Without this the numeric
   columns claimed an equal share each, the name column pushed the rest past the
   edge, and the last column sat permanently behind a horizontal scrollbar. */
.table--compact th.num, .table--compact td.num { width: 1%; white-space: nowrap; }
.table--compact th:first-child, .table--compact td:first-child { width: 100%; }
/* A dropdown in a table cell. `.select` is width: 100%, and in a table whose
   first column takes all the room that resolves to nothing: the box shrank to
   its padding and arrow, with no space left for the choice it was showing.
   Sized to its options instead, so the column keeps room for the words. */
.table td .select { width: auto; min-width: 11rem; }
/* break-word, not anywhere: `anywhere` split "Certification" across lines as
   "Certificatio / n". This breaks a word only when it genuinely cannot fit. */
.table--compact td:first-child { overflow-wrap: break-word; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- badges -- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 999px; font-size: .75rem; font-weight: 650;
  background: var(--surface-3); color: var(--text-2); white-space: nowrap;
}
.badge--ok { background: var(--ok-100); color: var(--ok-600); }
.badge--warn { background: var(--warn-100); color: var(--warn-600); }
.badge--err { background: var(--err-100); color: var(--err-600); }
.badge--info { background: var(--info-100); color: var(--info-600); }
.badge--brand { background: var(--brand-100); color: var(--brand-700); }
.badge--gold { background: var(--gold-100); color: var(--gold-600); }
.badge--priority { background: var(--gold-500); color: var(--on-gold); }

.dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: inline-block; }
.dot--live { background: var(--ok-600); box-shadow: 0 0 0 0 rgba(11,122,60,.5); animation: pulse 2s infinite; }
@keyframes pulse {
  70% { box-shadow: 0 0 0 8px rgba(11,122,60,0); }
  100% { box-shadow: 0 0 0 0 rgba(11,122,60,0); }
}

/* -------------------------------------------------------------- tickets -- */
.ticket-card {
  background: linear-gradient(150deg, var(--brand-900), var(--brand-700));
  color: #fff; border-radius: var(--r-xl); padding: var(--sp-5);
  box-shadow: var(--shadow-2); position: relative; overflow: hidden;
  margin-bottom: var(--sp-4);
}
.ticket-card::after {
  content: ""; position: absolute; right: -50px; top: -50px;
  width: 190px; height: 190px; border-radius: 50%;
  background: rgba(255,255,255,.07);
}
.ticket-card--priority { background: linear-gradient(150deg, #a3760c, var(--gold-500)); color: var(--on-gold); }
/* A number the day went by on. It keeps the card, because it is still the
   only number this client is holding -- but drained of the colour a live
   number earns, and struck through, because it is good at no counter now.
   Told apart from a live number by more than hue: the weight of the surface
   and the line through the digits both survive a mono screen. */
.ticket-card--inactive {
  background: linear-gradient(150deg, var(--surface-3), var(--surface-2));
  color: var(--text); border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-1);
}
.ticket-card--inactive::after { background: rgba(128,128,128,.10); }
.ticket-card--inactive .ticket-card__label { color: var(--text-2); opacity: 1; }
.ticket-card--inactive .ticket-card__no {
  color: var(--text-2);
  text-decoration: line-through; text-decoration-thickness: 3px;
}
.ticket-card--inactive .ticket-card__meta div span { color: var(--text-3); opacity: 1; }
.ticket-card__label { font-size: .74rem; text-transform: uppercase; letter-spacing: .1em; opacity: .8; font-weight: 700; }
.ticket-card__no {
  /* Sized so the longest number -- office, date, lane and sequence -- still
     lands on one line on a narrow phone. */
  font-size: clamp(1.5rem, 7vw, 2.6rem); font-weight: 800; line-height: 1;
  letter-spacing: -.02em; margin: 6px 0; font-variant-numeric: tabular-nums;
  position: relative; overflow-wrap: anywhere;
}
.ticket-card__meta { display: flex; gap: var(--sp-5); flex-wrap: wrap; margin-top: var(--sp-4); position: relative; }
.ticket-card__meta div span { display: block; font-size: .74rem; opacity: .8; text-transform: uppercase; letter-spacing: .06em; }
.ticket-card__meta div strong { font-size: 1.15rem; font-weight: 700; }

/* ------------------------------------------------- the live wait estimate -- */
/* "How much longer?" is the only question most clients open the app to ask,
   so it gets one figure and nothing else of that size. It sits on the card
   with the number -- an answer belongs next to the thing it is about -- and
   it counts itself down rather than waiting to be told, so a quiet queue
   reads as a quiet queue instead of as a broken page.

   Drawn in the card's own ink: the panel is a translucent plate and every
   colour below is currentColor at some opacity, so the same markup lands
   correctly on the maroon of a regular number, the gold of a priority one and
   the flat surface of one that has closed. */
.ticket-card {
  --tc-plate: rgba(255, 255, 255, .13);
  --tc-edge:  rgba(255, 255, 255, .22);
}
.ticket-card--priority { --tc-plate: rgba(0, 0, 0, .09); --tc-edge: rgba(0, 0, 0, .18); }
.ticket-card--inactive { --tc-plate: var(--surface); --tc-edge: var(--border); }

/* The one thing to do, said once, above the figure. */
.ticket-card__do {
  position: relative; margin: var(--sp-4) 0 0;
  font-size: 1.02rem; font-weight: 650; line-height: 1.35;
}

.eta {
  position: relative; margin-top: var(--sp-4);
  background: var(--tc-plate); border: 1px solid var(--tc-edge);
  border-radius: var(--r-lg); padding: var(--sp-4);
}
.eta__head {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
}
.eta__label { opacity: .8; }
.eta__live { margin-left: auto; display: flex; align-items: center; gap: 5px; opacity: .9; }
/* The stream is down, so the figure is only as fresh as the last thing the
   page heard. Said here as well as in the banner, because this is the number
   the staleness is actually about. */
.eta__live--off { opacity: .7; }

.eta__value {
  font-size: clamp(2.4rem, 13vw, 3.6rem); font-weight: 800; line-height: 1;
  letter-spacing: -.02em; margin: 6px 0 2px;
}
/* A running clock, so the digits must not reflow as they change -- tabular
   figures and a fixed slot for the colon. Only the clock form gets them: the
   rounded "~25 min" reads better in the proportional face. */
.eta__value--clock { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }

.eta__band { font-size: .82rem; opacity: .8; }

/* A fresh estimate has just landed. Brief and quiet: it is there to show the
   figure is alive, not to interrupt. */
@keyframes eta-reckon {
  from { background: var(--tc-edge); }
  to   { background: transparent; }
}
.eta__value.is-reckoned, .eta__band.is-reckoned {
  animation: eta-reckon .9s ease-out;
  border-radius: var(--r-sm);
}
@media (prefers-reduced-motion: reduce) {
  .eta__value.is-reckoned, .eta__band.is-reckoned { animation: none; }
}

/* The people in front, drawn rather than counted. Four marks is something a
   client takes in without reading; "4" is something they have to compare
   against the last number they remember. It is also what makes the queue
   moving visible -- a withdrawal ahead of them takes a mark away. */
.eta__strip {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--tc-edge);
}
.eta__strip-text { font-size: .82rem; opacity: .85; }
.pips { display: flex; align-items: center; gap: 5px; }
.pip {
  width: 10px; height: 10px; border-radius: 50%;
  background: currentColor; opacity: .38;
}
/* Where the client is in that row. Told apart by size and by the ring around
   it, not by colour alone, so it survives a mono screen. */
.pip--mine {
  width: 12px; height: 12px; opacity: 1;
  outline: 2px solid currentColor; outline-offset: 2px;
  margin-left: 5px;
}

/* ------------------------------------------------------ dashboard layout -- */
/* The greeting carries no information, so it takes as little room as a line
   of text can while still reading as a heading. */
.page-head--tight { margin-bottom: var(--sp-4); }
.page-head--tight h1 { font-size: clamp(1.15rem, 4vw, 1.5rem); }

.section-head {
  margin: var(--sp-5) 0 var(--sp-3);
  font-size: .76rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .1em; color: var(--text-2);
}
.board-set > .section-head:first-child { margin-top: 0; }

/* The step a request is on, in a line. The reference numbers, the schedule
   and the receipt all live on the request's own page, which the heading links
   to -- printed here as well, they turned a progress report into a form. */
.req-now { margin: 0; font-size: .92rem; color: var(--text-2); }
.req-now strong { color: var(--text); }

/* ----------------------------------------------------- office queues -- */
/* The queues lead the dashboard: how long each line is, is the one thing a
   client checks before deciding whether to come in.
   One office is drawn at a time, picked from a row of tabs. Every board used
   to be drawn at once, side by side at every width -- on a phone, three
   columns a third of the screen wide, a counter's name broken one word to a
   line, and the client's own number pushed most of the way down the page.
   Each tab still carries the length of its office's line, so the comparison
   is made along the row, and the board beneath has the full width. */
.board-set { margin-bottom: var(--sp-5); }
.board-set__sub { margin: calc(var(--sp-3) * -1) 0 var(--sp-3); font-size: .82rem; color: var(--text-2); }

/* Three offices sit in one row even on a phone; a longer list wraps rather
   than scrolling sideways, where the offices past the edge would be missed. */
.board-tabs {
  display: grid; gap: var(--sp-2); margin-bottom: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
}
.board-tab {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 0; padding: 10px 12px;
  font: inherit; text-align: left; color: var(--text); cursor: pointer;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r);
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.board-tab:hover { border-color: var(--brand-500); }
.board-tab:focus-visible { outline: 3px solid var(--brand-100); outline-offset: 1px; }
/* The office on show, marked the way a chosen option is elsewhere in the app:
   the brand edge and tint, with a second ring so it survives a mono screen. */
.board-tab[aria-selected="true"] {
  border-color: var(--brand-600); background: var(--brand-50);
  box-shadow: inset 0 0 0 1px var(--brand-600);
}
.board-tab__name { font-weight: 700; font-size: .92rem; line-height: 1.25; overflow-wrap: anywhere; }
.board-tab__meta { font-size: .78rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.board-tab > .badge { margin-top: 4px; }

.queue-row {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); margin-bottom: 8px;
}
.queue-row--priority { border-left: 4px solid var(--gold-500); background: var(--gold-100); }
.queue-row--active { border-left: 4px solid var(--ok-600); background: var(--ok-100); }
/* Parked at the Cashier: still the client's number, just not callable. */
.queue-row--held { border-left: 4px solid var(--warn-600); background: var(--warn-100); opacity: .92; }
.queue-row__no { font-family: var(--mono); font-weight: 700; font-size: 1rem; min-width: 148px; }
.queue-row__body { flex: 1; min-width: 0; }
.queue-row__name { font-weight: 600; }
.queue-row__meta { font-size: .8rem; color: var(--text-2); }
.queue-row__actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* The slim row. Ten clients waiting used to be a page nobody could take
   in: two badge stacks beside the number, seven grey lines under the name,
   the request flow drawn in full, and eight buttons. The row now answers
   the two questions a counter has -- who is next, what did they come for --
   and folds the rest behind the line under the name. */
.queue-row--slim { align-items: flex-start; padding: 9px var(--sp-3); }
.queue-row--slim .queue-row__no { min-width: 108px; padding-top: 1px; }
.queue-row--slim .queue-row__actions { padding-top: 1px; }

/* Who the number belongs to, and the one thing about them the counter
   cannot afford to miss: a priority claim nobody has verified. */
.qrow__who { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.qrow__id { font-size: .8rem; color: var(--text-2); }

/* The fold. One line closed; everything the row used to print, open. */
.qrow__fold { margin-top: 1px; }
.qrow__gist {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  padding: 1px 0; border-radius: var(--r-sm);
  font-size: .8rem; color: var(--text-2); cursor: pointer;
  list-style: none; user-select: none;
}
.qrow__gist::-webkit-details-marker { display: none; }
.qrow__gist::marker { content: ""; }
.qrow__gist:hover { color: var(--text); }
.qrow__gist:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }
.qrow__fold[open] .req-brief__chev { transform: rotate(45deg); }
/* One line, whatever it holds: the fold below is where the rest lives, so
   a long purpose must not be allowed to set the height of every row. */
.qrow__gist-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qrow__gist-text--plain { font-size: .8rem; color: var(--text-2); padding: 1px 0; }
.qrow__detail { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--border); }
.qrow__detail .req-brief { max-width: none; }

@media (max-width: 760px) {
  /* The number keeps the top line to itself and the buttons take the
     bottom one, rather than three columns fighting over a phone. */
  .queue-row--slim { flex-wrap: wrap; }
  .queue-row--slim .queue-row__no { min-width: 0; flex: 1 0 auto; }
  .queue-row--slim .queue-row__body { flex: 1 1 100%; order: 2; }
  .queue-row--slim .queue-row__actions { order: 1; margin-left: auto; }
}
/* What the client filed for, folded shut. Closed it is one line -- how much
   there is and what it costs -- so a queue of ten numbers still fits on the
   screen; open it is the folder list the clerk pulls from. */
.req-brief {
  margin-top: 6px; max-width: 440px;
  border: 1px solid var(--border); border-left: 3px solid var(--brand-500);
  border-radius: var(--r-sm); background: var(--surface-2); overflow: hidden;
}
.req-brief__sum {
  display: flex; align-items: center; gap: 8px; padding: 5px 10px;
  font-size: .8rem; color: var(--text-2); cursor: pointer;
  list-style: none; user-select: none;
}
/* The default disclosure triangle sits badly against a flex row, so the row
   draws its own and the browser's is taken out on both engines. */
.req-brief__sum::-webkit-details-marker { display: none; }
.req-brief__sum::marker { content: ""; }
.req-brief__sum:hover { background: var(--surface-3); }
.req-brief__sum:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }
.req-brief__chev {
  flex: none; width: 6px; height: 6px; margin-left: 1px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform .15s ease;
}
.req-brief[open] .req-brief__chev { transform: rotate(45deg); }
.req-brief__count { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Whatever else rides on the line -- a waiver, say -- keeps its full width;
   the count is the part that gives way when the row is narrow. */
.req-brief__sum > .badge { flex: none; }
/* The amount is the one number the counter quotes out loud, so it holds the
   end of the line where the eye can find it without reading the rest. */
.req-brief__total {
  margin-left: auto; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.req-brief__body { padding: 7px 10px 8px; border-top: 1px solid var(--border); }
/* The same fold inside a table cell, where the cell sets the width and a
   440px cap would leave the folder list wrapping in a column with room to
   spare. */
.req-brief--cell { margin-top: 0; max-width: none; min-width: 190px; }
.req-brief__purpose { margin-top: 4px; font-size: .78rem; color: var(--text-2); }

/* The documents on a request: one line each, never a wall of text. */
.doc-list { list-style: none; margin: 0; padding: 0; font-size: .82rem; }
.doc-list li { padding: 1px 0; overflow-wrap: anywhere; }

/* What kind of document each line is -- credential, certification, record.
   Set in a chip rather than run into the name, so "Copy of Grades Record"
   cannot be misread as the name of the document. */
.doc-list__type {
  display: inline-block; padding: 0 7px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
  font-size: .72rem; font-weight: 650; white-space: nowrap;
}

/* A number whose request can be opened by tapping it. The whole row is the
   target, so it needs to look like one and answer to the keyboard. */
.queue-row--tappable { cursor: pointer; }
/* An outline rather than a border or a background: priority and now-serving
   rows carry their own left accent and tint, and hovering must not repaint
   either of them. */
.queue-row--tappable:hover { outline: 1px solid var(--brand-500); outline-offset: -1px; }
.queue-row--tappable:focus-visible {
  outline: 2px solid var(--brand-600); outline-offset: 2px;
}
.queue-row__reveal {
  margin-top: 3px; font-size: .74rem; font-weight: 650; color: var(--brand-700);
}
.queue-row--tappable:hover .queue-row__reveal { text-decoration: underline; }

/* ------------------------------------------------------ credential flow -- */
/* The three counters a request walks -- file it, pay for it, collect it --
   drawn as three steps so a clerk reads the client's position instead of
   deducing it from a stage badge, a request status and a referral line.
   One strip, one row high, so it can sit on every number without pushing the
   rest of the line off the screen. */
.flow {
  display: flex; flex-wrap: wrap; list-style: none;
  margin: 6px 0 0; padding: 0;
  font-size: .74rem; font-weight: 650;
}
.flow__step {
  display: flex; align-items: center; gap: 5px;
  padding: 2px 10px; border: 1px solid var(--border); border-right: 0;
  color: var(--text-2); background: var(--surface-2);
}
.flow__step:first-child { border-radius: 999px 0 0 999px; }
.flow__step:last-child { border-radius: 0 999px 999px 0; border-right: 1px solid var(--border); }
.flow__mark {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--surface-3); color: var(--text-2);
  font-size: .64rem; line-height: 1;
}
/* Behind you, where you are, and ahead of you -- told apart by weight of
   colour rather than by hue alone, so the strip survives a mono display and
   a red-green eye. */
.flow__step--done { color: var(--ok-600); background: var(--ok-100); border-color: var(--ok-600); }
.flow__step--done .flow__mark { background: var(--ok-600); color: var(--surface); }
.flow__step--now { color: var(--on-brand); background: var(--brand-700); border-color: var(--brand-700); }
.flow__step--now .flow__mark { background: var(--on-brand); color: var(--brand-700); }
/* Which counter the step happens at. Quieter than the step itself: it answers
   "where", and the step has already answered "what". */
.flow__at { font-weight: 500; opacity: .85; }

/* The one sentence that says what has to happen for this number to move on,
   and who has to do it. */
.flow__next { margin-top: 4px; font-size: .78rem; color: var(--text-2); }
.flow__next strong { color: var(--text); }

/* The same strip in a card head, where it describes the counter rather than
   one number on it. */
.flow--station { margin-top: 8px; }

/* The rest of the floor, as a count and nothing more. An operator's console
   shows their own counter's line; this one line says the other counters are
   not idle, without putting numbers they cannot call in front of them. */
.floor {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  font-size: .82rem; color: var(--text-2);
}
.floor__label { font-weight: 700; color: var(--text); }
.floor__item { white-space: nowrap; }
.floor__item + .floor__item { padding-left: var(--sp-3); border-left: 1px solid var(--border); }
.floor__item strong { font-family: var(--mono); color: var(--text); }
.floor__note { margin-left: auto; font-size: .76rem; font-style: italic; }

/* ------------------------------------------------------- office boards -- */
/* The board of the office the client picked, drawn in full. */
.board {
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); box-shadow: var(--shadow-1);
  overflow: hidden;
}
/* The office this client has a number at, marked by a rule down its edge as
   well as by colour, so it survives a mono screen. A border rather than an
   inset shadow: the card clips its corners, and the heading's own background
   painted straight over the shadow for the height of the heading. */
.board--mine { border-color: var(--brand-600); border-left-width: 4px; }

.board__head {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.board__name { margin: 0; font-size: .98rem; font-weight: 700; }
.board__head > .badge { flex: none; }
/* The whole board in a line -- the number at the counter and the length of
   the line. Beside the office name where there is room and under it where
   there is not; it wraps rather than truncates, because "6 wai..." is not
   worth the tidier edge. */
.board__gist {
  flex: 1 1 16rem; min-width: 0;
  font-size: .82rem; color: var(--text-2);
}
.board__body {
  flex: 1; padding: var(--sp-3) var(--sp-4) var(--sp-4);
}
.board__body > .card__sub { margin: 0 0 var(--sp-3); font-size: .8rem; }

/* ------------------------------------------------------- counter lines -- */
/* An office board on the client’s own dashboard, one row per counter line.
   A document request is served by three different queues -- the counter that
   checks it, the Cashier, the counter that hands it over -- and a single
   "now serving" for the whole office answered none of them. Each line says
   which counters call it, what is at them, and how long the line is. */
/* Side by side when the page is wide enough, stacked on a phone. */
.board-lines {
  list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}
.board-line {
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-2); padding: var(--sp-3) var(--sp-4);
}
/* The line the client is actually standing in. Marked by a rule down its
   edge as well as by colour, so it survives a mono screen. */
.board-line--mine {
  border-color: var(--brand-600);
  box-shadow: inset 3px 0 0 var(--brand-600);
}
.board-line__head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.board-line__at { font-size: .82rem; font-weight: 650; color: var(--text); }
.board-line__open { font-size: .74rem; color: var(--text-2); }

/* The cells share the line's width out between them rather than sitting in a
   row that wraps into a ragged edge.
   The floor is what a ticket number needs on one line. Below it the grid
   drops to two cells a row rather than breaking REG-1005-004 across two --
   a number read off a board and then looked for on a screen is the one
   string here that must not be hyphenated by the layout. */
.board-line__stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: var(--sp-3) var(--sp-4); margin-top: var(--sp-3);
}
.board-line__stats > div { min-width: 0; }
.board-line__stats span {
  display: block; font-size: .7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-3);
}
.board-line__stats strong {
  display: block; font-size: 1.05rem; font-weight: 700;
  font-variant-numeric: tabular-nums; line-height: 1.25;
}
/* Which window, under the number it is calling -- quieter than the number,
   because the number is what the client is listening for. */
.board-line__stats em { display: block; font-size: .74rem; font-style: normal; color: var(--text-2); }

.board-line__next { margin-top: var(--sp-2); font-size: .79rem; color: var(--text-2); }
.board-line__next strong { color: var(--brand-600); }
/* A ticket number, held together. The browser breaks at a hyphen by default,
   and REG-1005-004 split across two lines reads as two numbers. */
.board .tno { white-space: nowrap; }

/* On a phone the board keeps its type size -- it has the width of the page
   to itself now -- and gives up only some of its padding. */
@media (max-width: 560px) {
  .board__head { padding: var(--sp-2) var(--sp-3); }
  .board__body { padding: var(--sp-2) var(--sp-3) var(--sp-3); }
  .board-line { padding: var(--sp-3); }
}

/* -------------------------------------------------------------- display -- */
.display {
  min-height: 100vh; min-height: 100dvh; background: var(--brand-900); color: #fff;
  padding: 2vh 2vw; display: flex; flex-direction: column; gap: 2vh;
}
.display__head { display: flex; align-items: center; gap: var(--sp-4); }
.display__seal { width: clamp(52px, 6vw, 104px); height: clamp(52px, 6vw, 104px); flex: none; display: block; }
.display__title { font-size: clamp(1.1rem, 2.4vw, 2rem); font-weight: 700; margin: 0; }
.display__clock { margin-left: auto; font-size: clamp(1rem, 2.2vw, 1.8rem); font-variant-numeric: tabular-nums; }
.display__grid { flex: 1; display: grid; grid-template-columns: 2fr 1fr; gap: 2vw; min-height: 0; }
.display__now { display: grid; gap: 1.6vh; align-content: start; }
.now-card {
  background: rgba(255,255,255,.09); border-radius: 18px; padding: 2.2vh 2vw;
  display: flex; align-items: center; gap: 2vw; border: 2px solid rgba(255,255,255,.14);
}
.now-card--priority { background: rgba(245,184,28,.22); border-color: var(--gold-500); }
/* The lobby board's headline. It shrinks with the viewport rather than
   overflowing the card, and may give way to the window label beside it. */
.now-card__no {
  font-size: clamp(1.6rem, 4.4vw, 4rem); font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; min-width: 0; overflow-wrap: anywhere;
}
.now-card__win { margin-left: auto; text-align: right; }
.now-card__win span { display: block; font-size: clamp(.7rem, 1.1vw, 1rem); opacity: .75; text-transform: uppercase; letter-spacing: .08em; }
.now-card__win strong { font-size: clamp(1.2rem, 3vw, 2.6rem); }
.display__side { background: rgba(255,255,255,.07); border-radius: 18px; padding: 1.8vh 1.4vw; overflow: hidden; }
.display__side h2 { font-size: clamp(.85rem, 1.4vw, 1.3rem); text-transform: uppercase; letter-spacing: .08em; opacity: .8; }
.display__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .9vh; }
.display__list li {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: clamp(.85rem, 1.5vw, 1.35rem); font-weight: 700; font-variant-numeric: tabular-nums;
  padding: .7vh .8vw; background: rgba(255,255,255,.07); border-radius: 10px;
}
.display__list li.is-priority { background: rgba(245,184,28,.2); }
.display__foot { display: flex; gap: var(--sp-5); font-size: clamp(.75rem, 1.2vw, 1.05rem); opacity: .8; flex-wrap: wrap; }
@media (max-width: 800px) { .display__grid { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------- misc --- */
.toasts {
  position: fixed; bottom: var(--sp-4); right: var(--sp-4); z-index: 200;
  display: flex; flex-direction: column; gap: var(--sp-2); max-width: min(400px, calc(100vw - 32px));
}
.toast {
  background: var(--surface); border: 1px solid var(--border); border-left: 4px solid var(--brand-600);
  border-radius: var(--r); padding: var(--sp-3) var(--sp-4); box-shadow: var(--shadow-3);
  font-size: .89rem; animation: slide-in .2s ease;
}
.toast--ok { border-left-color: var(--ok-600); }
.toast--err { border-left-color: var(--err-600); }
.toast--warn { border-left-color: var(--warn-600); }
.toast__title { font-weight: 650; margin-bottom: 2px; }
@keyframes slide-in { from { transform: translateX(24px); opacity: 0; } }
@media (max-width: 900px) { .toasts { bottom: 84px; left: var(--sp-3); right: var(--sp-3); max-width: none; } }

.modal {
  position: fixed; inset: 0; z-index: 150; display: grid; place-items: center;
  background: rgba(23,13,14,.62); padding: var(--sp-4); backdrop-filter: blur(2px);
}
.modal__box {
  background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow-3);
  width: min(560px, 100%); max-height: 90dvh; overflow-y: auto;
}
.modal__box--wide { width: min(880px, 100%); }
.modal__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border);
}
.modal__head h2 { margin: 0; font-size: 1.1rem; }
.modal__body { padding: var(--sp-5); }
.modal__foot {
  display: flex; gap: var(--sp-2); justify-content: flex-end;
  padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--border); background: var(--surface-2);
  flex-wrap: wrap;
}

.offline-banner {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 120;
  background: var(--seal-gold); color: var(--on-gold); text-align: center;
  padding: 8px var(--sp-4); font-size: .85rem; font-weight: 600;
}
@media (max-width: 900px) { .offline-banner { bottom: 68px; } }

.empty {
  text-align: center; padding: var(--sp-7) var(--sp-4); color: var(--text-2);
}
.empty__icon {
  display: grid; place-items: center; width: 64px; height: 64px;
  margin: 0 auto var(--sp-3); border-radius: 50%;
  background: var(--surface-3); color: var(--text-3);
}
.empty h3 { color: var(--text); }

.note {
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r);
  background: var(--info-100); color: var(--info-600);
  font-size: .87rem; border-left: 4px solid currentColor; margin-bottom: var(--sp-4);
}
.note--warn { background: var(--warn-100); color: var(--warn-600); }
.note--err { background: var(--err-100); color: var(--err-600); }
.note--ok { background: var(--ok-100); color: var(--ok-600); }
.note strong { display: block; margin-bottom: 2px; }
/* Why a first sign-in is not offered SMS, under the line saying where its code went. */
.note .otp-why { display: block; margin-top: 6px; }

.progress { height: 8px; background: var(--surface-3); border-radius: 999px; overflow: hidden; }
.progress__bar { height: 100%; background: var(--brand-600); border-radius: 999px; transition: width .4s ease; }

.steps { display: flex; gap: 0; margin-bottom: var(--sp-5); overflow-x: auto; padding-bottom: 4px; }
.step {
  flex: 1; min-width: 92px; text-align: center; font-size: .76rem; color: var(--text-3);
  position: relative; padding-top: 26px;
}
.step::before {
  content: ""; position: absolute; top: 8px; left: 50%; width: 100%; height: 2px;
  background: var(--border); z-index: 0;
}
.step:last-child::before { display: none; }
.step::after {
  content: ""; position: absolute; top: 2px; left: 50%; transform: translateX(-50%);
  width: 14px; height: 14px; border-radius: 50%; background: var(--surface);
  border: 2px solid var(--border-strong); z-index: 1;
}
.step.is-done::after { background: var(--ok-600); border-color: var(--ok-600); }
.step.is-done::before { background: var(--ok-600); }
.step.is-current::after { background: var(--brand-600); border-color: var(--brand-600); box-shadow: 0 0 0 4px var(--brand-100); }
.step.is-current { color: var(--brand-600); font-weight: 700; }

/* ------------------------------------------------------- request wizard -- */
/* The new-request form asks one card at a time. Three cards stacked on one
   screen left the client deciding which to read first, and the checklist at
   the bottom could be answered before the office at the top had settled what
   the checklist should contain. In order -- what you need, who you are, what
   you want copies of -- each answer is taken before the next question is put,
   while the assessment stays alongside, because what the request will cost is
   the one thing worth seeing at every step. */
.wiz-step { display: none; }
.wiz-step.is-active { display: block; }

/* The strip along the top: the same three-pill vocabulary as the credential
   flow, one size up, because here it is the page's own navigation. A step you
   have not reached is not a button, so the strip cannot be used to skip a
   question. */
.flow--wizard { margin: 0 0 var(--sp-4); font-size: .82rem; }
.flow--wizard .flow__step { padding: 0; min-width: 0; }
.flow--wizard .flow__go {
  display: flex; align-items: center; gap: 7px; width: 100%;
  padding: 8px 14px; border: 0; background: none; color: inherit;
  font: inherit; font-weight: inherit; cursor: pointer; text-align: left;
}
.flow--wizard .flow__go:disabled { cursor: default; opacity: .8; }
.flow--wizard .flow__go:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }
.flow--wizard .flow__mark { width: 18px; height: 18px; font-size: .7rem; font-weight: 700; }

/* Back, where you are, and forward -- under both columns, so the step's card
   and the assessment it drives are both above the button that leaves them.
   It stays on screen while the step scrolls: the document checklist is long,
   and on a phone the assessment stacks under the form, so the way forward used
   to be a long scroll away. */
.wiz-bar {
  position: sticky; bottom: 0; z-index: 15;
  margin-top: var(--sp-4); padding: var(--sp-3) 0;
  border-top: 1px solid var(--border); background: var(--bg);
}
.wiz-foot { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.wiz-foot__pos { margin-inline: auto; font-size: .82rem; color: var(--text-2); }
.wiz-foot .btn { min-width: 136px; }
/* Why the forward button is not available yet, said beside it rather than
   found out by pressing it. Absent when there is nothing to say. */
.wiz-note { margin: 0 0 var(--sp-3); font-size: .82rem; color: var(--text-2); }
.wiz-note:empty { display: none; }

/* The running total, carried on the bar that is already fixed to the bottom of
   the screen. On a wide screen the assessment sits beside the form and this is
   not needed; on a phone the assessment stacks a whole checklist below the
   boxes being ticked, and the one number that decides whether to tick another
   was the furthest thing from the thumb. The working stays in the panel and
   opens from here. */
.wiz-total { display: none; }
@media (max-width: 900px) {
  /* Above the tab bar, not underneath it. */
  .wiz-bar { bottom: calc(62px + env(safe-area-inset-bottom)); }

  .wiz-total {
    display: flex; align-items: center; gap: var(--sp-3); width: 100%;
    margin-bottom: var(--sp-2); padding: 8px 12px;
    border: 1px solid var(--brand-100); border-radius: var(--r);
    background: var(--brand-50); color: inherit;
    font: inherit; text-align: left; cursor: pointer;
  }
  .wiz-total[hidden] { display: none; }
  .wiz-total:focus-visible { outline: 3px solid var(--brand-100); outline-offset: 1px; }
  .wiz-total__text {
    display: flex; align-items: baseline; gap: var(--sp-2) var(--sp-3);
    flex: 1; min-width: 0; flex-wrap: wrap;
  }
  .wiz-total__label { font-size: .8rem; color: var(--text-2); }
  .wiz-total__value { font-weight: 700; font-size: 1.05rem; color: var(--brand-600); }
  /* Before anything is ticked there is no number to carry, and the label says
     so on its own rather than standing a dash in for one. */
  .wiz-total__value:empty { display: none; }
  .wiz-total__more {
    flex: none; font-size: .8rem; font-weight: 650; color: var(--brand-600);
    text-decoration: underline;
  }

  /* The panel the sheet is lifted from. Everything it holds is one tap away on
     the bar above, so leaving it in the scroll as well would be the same
     assessment twice -- and the second copy is a screen and a half long. */
  #assessment { display: none; }

  /* Inside the sheet the panel's own card head is the dialog's title over
     again, and its sticky offset belongs to a column that is not there. */
  .assess-sheet > .card__head { display: none; }
  .assess-sheet .table { font-size: .86rem; }
}

@media (max-width: 620px) {
  /* Room is scarce: the step you are on keeps its name, the rest are numbers. */
  .flow--wizard .flow__step:not(.flow__step--now) .flow__label { display: none; }
  .flow--wizard .flow__step--now { flex: 1 1 auto; }
  .flow--wizard .flow__go { padding: 7px 11px; }
  /* "2/3" rides on the step you are on, because the count under the buttons is
     the first thing to go when the bar has to fit one row. */
  .flow--wizard .flow__step--now .flow__of { display: inline; margin-left: auto; opacity: .8; }
}
.flow__of { display: none; font-size: .78rem; font-weight: 600; }

@media (max-width: 680px) {
  /* One row that stays small enough to leave the step readable above it:
     Back to one side, the way forward taking the rest, where the thumb is.
     The strip at the top already says which step this is. "Submit request
     and get my number" is too long a promise for one line on a phone, so the
     button is the one place a label may wrap. */
  .wiz-foot { flex-wrap: nowrap; align-items: stretch; gap: var(--sp-2); }
  .wiz-foot__pos { display: none; }
  .wiz-foot .btn { min-width: 0; white-space: normal; }
  .wiz-foot [data-wiz-next], .wiz-foot [data-wiz-submit] { flex: 1 1 auto; }
}

/* --- the form, made to fit a phone --------------------------------------- */
/* Everything below is the same form at a smaller grain. The page used to open
   on a title, a paragraph of reassurance, a step strip and a card heading --
   four blocks and the better part of a screen before the first question. */
@media (max-width: 620px) {
  .page-head--tight { margin-bottom: var(--sp-3); }
  .page-head--tight h1 { font-size: 1.3rem; }
  .page-head--tight p { font-size: .84rem; }
  .flow--wizard { margin-bottom: var(--sp-3); font-size: .8rem; }

  /* A card's heading band is a full step of padding above and below a line of
     text; at this width the line is enough. */
  .wiz-step { --card-pad: var(--sp-4); }
  .wiz-step .card__head { padding: var(--sp-3) var(--card-pad); }
  .wiz-step .card__head h2 { font-size: 1.05rem; }
  .wiz-step .card__sub { font-size: .8rem; }
  .wiz-step .field { margin-bottom: var(--sp-3); }

  /* Three offices took most of a screen as full-height cards. The short name is
     what the client is choosing between; the official name stays, on one line. */
  #office-cards .choice { padding: 9px 11px; }
  /* Clamped rather than `nowrap`: an unbreakable line sets the card's own
     minimum width, and one office's official name is wider than the screen --
     which pushed the whole column, and the page with it, sideways. */
  #office-cards .choice__desc {
    margin-top: 0; font-size: .78rem; overflow: hidden;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;
  }

  .choice { min-width: 0; padding: 10px 11px; }
  .choice__title { font-size: .88rem; }
  .choice__desc { font-size: .8rem; }

  /* The times are a grid of buttons; at four to a row they stop being a list to
     scroll past and start being a thing to glance at. */
  .slots { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 6px; }
  .slot__face { padding: 7px 4px; font-size: .82rem; }
  .slot__face small { font-size: .68rem; }

  /* The counts sat indented under the document's name, which left each of them
     about a hundred pixels to lay out a label and three controls in. */
  .doc-pick { padding: 9px 11px; }
  .doc-pick__qty { margin-left: 0; gap: var(--sp-2) var(--sp-3); }
  .doc-pick__more { margin-left: 0; }
  .doc-pick__name { font-size: .92rem; }
  .doc-pick__price { font-size: .78rem; }
}

/* The search above the checklist. Eighteen documents is four screens to scroll
   and two letters to type. */
.doc-find { margin-bottom: var(--sp-3); }
.doc-find__count { margin: 5px 0 0; font-size: .79rem; color: var(--text-2); }
.doc-find__count:empty { display: none; }

/* --- the request form's own pieces --------------------------------------- */

/* Who the request is for, read back from the profile. Most clients change
   nothing here, so it is a summary with an Edit button rather than seven
   filled-in boxes to read through. */
.who {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: 14px; margin-bottom: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2);
}
.who__avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: grid; place-content: center; font-weight: 700; font-size: .85rem;
  background: var(--brand-600); color: var(--on-brand);
}
.who__body { min-width: 0; flex: 1; }
.who__name { font-weight: 650; }
.who__meta { font-size: .84rem; color: var(--text-2); overflow-wrap: anywhere; }
.who__edit { flex: none; }

/* The document checklist: a document is a card, a ticked one is marked, and
   only a ticked one asks how many. */
.doc-group + .doc-group { margin-top: var(--sp-4); }
.doc-group__title {
  margin: 0 0 var(--sp-2); font-size: .74rem; font-weight: 700; color: var(--text-2);
  text-transform: uppercase; letter-spacing: .06em;
}
.doc-pick {
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface);
  padding: 10px 12px; margin-bottom: var(--sp-2);
  transition: border-color .15s ease, background-color .15s ease;
}
.doc-pick:hover { border-color: var(--border-strong); }
.doc-pick:has([data-toggle]:checked) { border-color: var(--brand-600); background: var(--brand-50); }
.doc-pick__head { display: flex; gap: var(--sp-3); align-items: flex-start; cursor: pointer; }
.doc-pick__head input { margin-top: 3px; width: 17px; height: 17px; flex: none; accent-color: var(--brand-600); }
.doc-pick__name { display: block; font-weight: 600; }
.doc-pick__price { display: block; font-size: .82rem; color: var(--text-2); }
.doc-pick__more { margin: 6px 0 0 30px; font-size: .8rem; color: var(--text-2); }
.doc-pick__more summary { cursor: pointer; color: var(--brand-600); font-weight: 600; width: max-content; }
.doc-pick__more p { margin: 4px 0 0; }
.doc-pick__qty {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: var(--sp-3) var(--sp-4); margin: 10px 0 2px 30px;
}

/* A count with a minus and a plus beside it, so a thumb can change it without
   the keyboard opening. The box still takes a typed number. */
.qty { display: grid; align-content: start; gap: 4px; font-size: .78rem; color: var(--text-2); }
.qty__label { font-weight: 600; color: var(--text); }
.qty__ctl {
  display: inline-flex; width: max-content;
  border: 1px solid var(--border-strong); border-radius: var(--r); overflow: hidden; background: var(--surface);
}
.qty__ctl button {
  width: 34px; border: 0; background: var(--surface-2); color: var(--text);
  font: inherit; font-size: 1.05rem; line-height: 1; cursor: pointer;
}
.qty__ctl button:hover { background: var(--surface-3); }
.qty__ctl button:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }
.qty__ctl input {
  width: 46px; padding: 7px 2px; border: 0; text-align: center;
  font: inherit; font-size: .92rem; color: var(--text); background: var(--surface);
  -moz-appearance: textfield; appearance: textfield;
}
.qty__ctl input::-webkit-inner-spin-button,
.qty__ctl input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.qty__ctl input:focus { outline: 2px solid var(--brand-500); outline-offset: -2px; }
.qty__hint { font-size: .74rem; }

/* Reservation times as buttons to tap rather than a list to scroll. */
.slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
.slot { position: relative; display: block; }
.slot input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.slot__face {
  display: block; padding: 8px 6px; text-align: center; cursor: pointer;
  border: 1px solid var(--border-strong); border-radius: var(--r); background: var(--surface);
  font-weight: 600; font-size: .86rem;
}
.slot__face small { display: block; font-weight: 400; font-size: .72rem; color: var(--text-2); }
.slot:hover .slot__face { border-color: var(--brand-500); }
.slot input:checked + .slot__face { border-color: var(--brand-600); background: var(--brand-600); color: var(--on-brand); }
.slot input:checked + .slot__face small { color: inherit; opacity: .85; }
.slot input:focus-visible + .slot__face { outline: 3px solid var(--brand-100); outline-offset: 1px; }
.slots__empty {
  padding: 10px 12px; font-size: .86rem; color: var(--text-2);
  border: 1px dashed var(--border-strong); border-radius: var(--r);
}

/* What happens after submitting, drawn as the numbered steps it is. */
.next-steps__title { margin: var(--sp-5) 0 var(--sp-3); font-size: .86rem; font-weight: 700; }
.next-steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.next-steps li {
  position: relative; padding: 0 0 14px 34px; font-size: .86rem; color: var(--text-2);
  counter-increment: step;
}
.next-steps li:last-child { padding-bottom: 0; }
.next-steps li::before {
  content: counter(step); position: absolute; left: 0; top: 0;
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-content: center;
  font-size: .72rem; font-weight: 700; color: var(--brand-600);
  background: var(--brand-50); border: 1px solid var(--brand-100);
}
.next-steps li:not(:last-child)::after {
  content: ""; position: absolute; left: 11px; top: 24px; bottom: 3px; width: 1px; background: var(--border);
}
.next-steps strong { color: var(--text); }
.assess-note { margin-top: var(--sp-2); font-size: .8rem; color: var(--text-2); }

.auth-shell {
  min-height: 100dvh; display: grid; place-items: center; padding: var(--sp-4);
  position: relative; isolation: isolate;
  background: var(--brand-900);
}
/* The university gate behind the sign-in screens, tinted maroon so the card
   stays the thing to read. Fixed to the screen rather than the page, so a long
   form scrolls over the photo instead of stretching it. */
.auth-shell::before {
  content: ""; position: fixed; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(38, 6, 9, .55) 0%, rgba(38, 6, 9, .68) 55%, rgba(24, 4, 6, .85) 100%),
    url("/bg.jpg") 60% 35% / cover no-repeat,
    var(--brand-900);
}
/* Sized so the whole sign-in card fits on one screen without scrolling -- a
   laptop at 150% display scaling shows about 630px of page, a small phone
   about 640px -- which is what the seal, the gaps and the links below are cut
   to. The sign-up form is longer than any screen and is kept short instead. */
.auth-box {
  width: min(460px, 100%); background: var(--surface);
  border-radius: var(--r-xl); box-shadow: var(--shadow-3); padding: var(--sp-5);
  position: relative; overflow: hidden;
}
.auth-box::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 5px;
  background: linear-gradient(90deg,
    var(--seal-indigo) 0 25%, var(--seal-red) 25% 50%,
    var(--seal-gold) 50% 75%, var(--seal-green) 75% 100%);
}
.auth-box--wide { width: min(700px, 100%); }
.auth-box__brand { text-align: center; margin-bottom: var(--sp-4); }
.auth-box__logo {
  width: 64px; height: 64px; display: block; margin: 0 auto var(--sp-2);
}
.auth-box h1 { font-size: 1.3rem; margin-bottom: 2px; }
.auth-box__sub { color: var(--text-2); font-size: .87rem; margin: 0; }
.auth-alt { text-align: center; margin: var(--sp-3) 0 0; font-size: .87rem; color: var(--text-2); }

/* A link drawn at the far end of a field's label line, such as "Forgot
   password?" beside Password, so it costs no height of its own. It comes after
   the box in the markup: Tab goes from the box above straight into this one. */
.field--aside { position: relative; }
.field__aside {
  position: absolute; top: 0; right: 0;
  font-size: .82rem; line-height: 20px; white-space: nowrap;
}

/* The sign-up fields, two to a row where the card is wide enough for two,
   so the form is not twice the height of the window. A field whose hint is a
   long sentence keeps the whole width. */
@media (min-width: 720px) {
  .auth-fields {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--sp-4); align-items: start;
  }
  .auth-fields .field--full { grid-column: 1 / -1; }
}

/* Phones: a little less frame around the card, so more of the screen is form. */
@media (max-width: 480px) {
  .auth-shell { padding: var(--sp-3); }
  .auth-box { padding: var(--sp-5) var(--sp-4); }
}

/* Short screens -- a laptop at 150% display scaling, or a small phone -- get a
   tighter frame and a smaller seal, so the sign-in code screen fits with its
   longest note and the "don't ask again on this device" box. */
@media (max-height: 700px) {
  .auth-box { padding: var(--sp-4); }
  .auth-box__logo { width: 48px; height: 48px; }
}

/* "or", with a rule either side of it. */
.auth-or {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-4) 0; color: var(--text-2); font-size: .82rem;
}
.auth-or::before, .auth-or::after {
  content: ''; flex: 1; height: 1px; background: var(--border);
}

/* Google asks that its button keep its own look rather than take on the
   host application's colours, so this one does not use the brand. It stays a
   light button in dark mode for the same reason -- the mark is drawn for a
   light background, and recolouring it is the one thing the guidelines forbid. */
.btn--google {
  background: #fff; color: #1f1f1f; border: 1px solid #747775;
  gap: var(--sp-3); font-weight: 600;
}
.btn--google:hover { background: #f2f2f2; }
.btn--google:active { background: #e8e8e8; }
.btn--google svg { flex: none; }

/* Student / Alumni tabs on the client sign-in screen. One panel shows at a
   time; the other is hidden and its inputs disabled, so the tabs are the whole
   of the difference between the two doors. */
.auth-tabs {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: var(--sp-1); margin-bottom: var(--sp-4);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 4px;
}
.auth-tab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--text-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r);
  transition: background .15s ease, color .15s ease;
}
.auth-tab:hover { color: var(--text); }
.auth-tab.is-active {
  background: var(--surface); color: var(--brand-600);
  box-shadow: var(--shadow-1);
}
.auth-tab:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }
.auth-panel[hidden] { display: none; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 6px var(--sp-4); font-size: .89rem; }
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; font-weight: 550; }

.list-reset { list-style: none; margin: 0; padding: 0; }

.bar-chart { display: grid; gap: 8px; }

/* The label column has a ceiling but no floor, so a card that is narrow gives
   its width to the bar rather than to a caption nobody can read. A grid item
   is min-content wide by default, which is what let a long service name run
   straight across the bar and the figure beside it -- hence the min-width:0 and
   the clamp on the label itself. */
.bar-chart__row {
  display: grid;
  grid-template-columns: minmax(0, 11.5em) minmax(0, 1fr) auto;
  gap: var(--sp-3);
  align-items: center;
  font-size: .84rem;
  min-height: 22px;
}

/* Three lines, then an ellipsis. One line was not enough to tell "Government
   Employment (Permanent, Promotion, Re-classification)" from "Government
   Employment (Casual, Contractual)" -- both came out as "Government
   Employment...". The full text is on the title attribute either way. */
.bar-chart__label {
  min-width: 0;
  line-height: 1.3;
  overflow: hidden;
  overflow-wrap: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
}

/* Nothing in a chart row may paint outside its own column, whatever markup it
   was built with. A grid item defaults to min-content width and visible
   overflow, so a caption that did not fit used to render straight across the
   bar and the figure next to it. This is deliberately written against the row's
   children rather than one class name: a browser holding an older cached copy
   of the script renders the caption with a different class, and it must not be
   able to bring the overlap back. */
.bar-chart__row > * { min-width: 0; overflow: hidden; }
.bar-chart__row > :first-child { white-space: normal; }

.bar-chart__track { height: 20px; background: var(--surface-3); border-radius: var(--r-sm); overflow: hidden; }
.bar-chart__fill { height: 100%; background: var(--brand-500); border-radius: var(--r-sm); min-width: 2px; }
.bar-chart__val {
  min-width: 2.5em; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 600;
}
@media (max-width: 600px) {
  .bar-chart__row { grid-template-columns: minmax(0, 7.5em) minmax(0, 1fr) auto; font-size: .78rem; }
  .bar-chart__val { min-width: 2em; }
}

/* ---------------------------------------------------------- audit trail -- */
/* Time, who, what. The time column is as narrow as a clock reading; the day
   it belongs to is the band above it rather than repeated on every row. */
.audit-table td { vertical-align: top; }
.audit-table td:first-child { width: 1%; white-space: nowrap; }
.audit-table td:nth-child(2) { width: 26%; min-width: 9.5rem; }
.audit-table__day td {
  padding-top: 6px; padding-bottom: 6px;
  background: var(--surface-3); color: var(--text-2);
  font-size: .76rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
}
.audit-what { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--sp-2); }
.audit-summary { margin-top: 2px; color: var(--text-2); overflow-wrap: anywhere; }
.audit-more { margin-top: 4px; }
.audit-more > summary {
  width: max-content; cursor: pointer;
  font-size: .8rem; font-weight: 600; color: var(--brand-600);
}
.audit-more > summary:focus-visible { outline: 3px solid var(--brand-100); outline-offset: 2px; border-radius: 4px; }
.audit-more[open] > summary { margin-bottom: var(--sp-2); }
.audit-more .kv {
  font-size: .82rem; padding: var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.audit-more .kv dd { font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.audit-changes { margin: 0; padding-left: 1.1em; }
.audit-stack {
  margin: var(--sp-2) 0 0; padding: var(--sp-2); max-height: 220px; overflow: auto;
  white-space: pre-wrap; overflow-wrap: anywhere; font-size: .74rem;
  background: var(--surface-3); border-radius: var(--r-sm);
}
/* On a phone three columns left "what happened" a word wide, stacked down the
   screen. Each entry becomes a block instead: the time and the person on one
   line, and what they did underneath at the full width. */
@media (max-width: 600px) {
  .audit-table, .audit-table tbody { display: block; }
  .audit-table thead { display: none; }
  .audit-table tbody tr {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--sp-2); align-items: baseline;
    border-bottom: 1px solid var(--border);
  }
  .audit-table tbody tr:last-child { border-bottom: 0; }
  .audit-table tbody tr > td { border-bottom: 0; }
  .audit-table tbody tr > td:first-child { width: auto; padding: 10px 0 0 12px; }
  .audit-table tbody tr > td:nth-child(2) { width: auto; min-width: 0; padding: 10px 12px 0 0; }
  .audit-table tbody tr > td:nth-child(2) > .small { display: inline; }
  .audit-table tbody tr > td:nth-child(2) > .small::before { content: " · "; }
  .audit-table tbody tr > td:nth-child(3) { grid-column: 1 / -1; padding: 4px 12px 10px; }
  .audit-table tbody tr.audit-table__day { display: block; }
  .audit-table tbody tr.audit-table__day > td { display: block; padding: 6px 12px; }
}

/* The notification log: the message is the column with something to say, so
   it takes the spare width and the rest are as wide as what is in them. Left
   to .table--compact, the timestamp column claimed the whole row and every
   message was stacked one word to a line. */
.table--compact.log-table th, .table--compact.log-table td { white-space: nowrap; }
.table--compact.log-table th:first-child, .table--compact.log-table td:first-child { width: auto; }
.table--compact.log-table td:nth-child(4) { width: 100%; white-space: normal; }

.mono { font-family: var(--mono); }
.muted { color: var(--text-2); }
.small { font-size: .82rem; }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.flex { display: flex; gap: var(--sp-3); align-items: center; }
.flex-between { display: flex; gap: var(--sp-3); align-items: center; justify-content: space-between; flex-wrap: wrap; }
.wrap { flex-wrap: wrap; }
.mt-0 { margin-top: 0; } .mt-2 { margin-top: var(--sp-2); } .mt-4 { margin-top: var(--sp-4); }
.mb-0 { margin-bottom: 0; } .mb-2 { margin-bottom: var(--sp-2); } .mb-4 { margin-bottom: var(--sp-4); }
.hidden { display: none !important; }

.spinner {
  width: 18px; height: 18px; border-radius: 50%; display: inline-block;
  border: 2px solid currentColor; border-top-color: transparent; opacity: .75;
  animation: spin .7s linear infinite;
}
.btn--secondary .spinner { border-color: var(--border-strong); border-top-color: var(--brand-600); }

/* Shown by the router while a screen fetches what it is about to draw. */
.page-loading {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 40vh; color: var(--text-2); font-size: .95rem;
}

.skeleton {
  background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-2) 50%, var(--surface-3) 75%);
  background-size: 200% 100%; animation: shimmer 1.4s infinite;
  border-radius: var(--r-sm); height: 1em;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* --------------------------------------------------------------- phones -- */
/* The app is used on a phone in a queue far more often than at a desk, so the
   narrow layout is corrected here rather than left to wrap as best it can. */

@media (max-width: 700px) {
  /* Every control in the bar is reachable elsewhere except the title, which
     truncates; the bar itself must never be what makes the page scroll. */
  .appbar { padding: 0 var(--sp-3); gap: var(--sp-2); }
  .appbar__actions { gap: 6px; }
  /* Signing out lives on the profile page, reached from the avatar. */
  #logout-btn { display: none; }
  #install-btn { display: none; }
}

@media (max-width: 560px) {
  .card { --card-pad: var(--sp-4); }
  .main { padding: var(--sp-3) var(--sp-3) 92px; }

  h1 { font-size: 1.35rem; }
  h2 { font-size: 1.12rem; }

  /* A heading and its actions stop competing for one line. */
  .page-head { gap: var(--sp-3); }
  .page-head__text { min-width: 0; }
  .page-head .btn-row { width: 100%; }
  /* Sized by their labels, so a pair that will not fit one line wraps
     instead of pushing the page wider than the phone; still sharing the row. */
  .page-head .btn-row .btn { flex: 1 1 auto; }

  .card__head-actions { margin-left: 0; width: 100%; }

  .ticket-card { padding: var(--sp-4); }
  .ticket-card__meta { gap: var(--sp-4); }
  .ticket-card__meta div strong { font-size: 1.05rem; }

  /* A row of number + detail + buttons does not fit one line on a phone:
     let it become three stacked bands rather than three crushed columns. */
  .queue-row { flex-wrap: wrap; }
  .queue-row__no { min-width: 0; }
  .queue-row__body { flex-basis: 100%; order: 2; }
  .queue-row__actions { order: 3; width: 100%; }
  .queue-row__actions .btn { flex: 1; }

  /* Three pills and three counter names do not fit a phone. The steps are the
     point and the counter names are the gloss, so the gloss goes first rather
     than the strip wrapping into broken-looking half-pills. */
  .flow { font-size: .7rem; }
  .flow__step { padding: 2px 8px; }
  .flow__at { display: none; }

  .attention__row .btn-row { margin-left: 0; width: 100%; }
  .attention__row .btn-row .btn { flex: 1; }

  .modal { padding: var(--sp-3); }
  .modal__head, .modal__foot { padding: var(--sp-3) var(--sp-4); }
  .modal__body { padding: var(--sp-4); }
  .modal__foot .btn { flex: 1; }

  .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }
}

/* Fingers are not mouse pointers: give the small buttons a real target on any
   touch device, whatever the viewport happens to be. */
@media (pointer: coarse) {
  .btn--sm { padding: 8px 14px; }
  .icon-btn, .appbar__menu { width: 40px; height: 40px; }
}

/* ------------------------------------------------- public home page -- */
/* The screen a visitor sees before signing in. Everything is scoped under
   .home so it cannot reach into the signed-in shell, and it draws only on
   the shared tokens, so it follows light and dark with the rest. */
.home {
  min-height: 100dvh;
  background: var(--brand-900);
  color: var(--text);
  display: flex;
  flex-direction: column;
  position: relative;
  isolation: isolate;
}
/* The university gate behind the whole page, fixed to the screen. Kept dark
   across the first 720px, where the writing sits, and lighter to the right so
   the gate itself shows through on a wide screen. */
.home::before {
  content: ""; position: fixed; inset: 0; z-index: -1;
  background:
    linear-gradient(90deg, rgba(30, 6, 9, .88) 0, rgba(30, 6, 9, .74) 720px, rgba(30, 6, 9, .45) 100%),
    linear-gradient(180deg, rgba(20, 4, 6, 0) 50%, rgba(20, 4, 6, .55) 100%),
    url("/bg.jpg") 60% 35% / cover no-repeat,
    var(--brand-900);
}
/* A phone is narrower than the dark band, so the tint is even all the way across. */
@media (max-width: 720px) {
  .home::before {
    background:
      linear-gradient(180deg, rgba(30, 6, 9, .74) 0%, rgba(30, 6, 9, .84) 60%, rgba(20, 4, 6, .92) 100%),
      url("/bg.jpg") 60% 35% / cover no-repeat,
      var(--brand-900);
  }
}

.home__bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-3) clamp(var(--sp-4), 4vw, var(--sp-6));
  background: var(--surface);   /* fallback where color-mix is unsupported */
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
.home__brand { display: flex; align-items: center; gap: var(--sp-3); text-decoration: none; color: inherit; }
.home__brand img { border-radius: 50%; }
.home__brand-name { display: block; font-weight: 750; letter-spacing: -.01em; }
.home__brand-sub { display: block; font-size: .78rem; color: var(--text-2); }
.home__bar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); }

.home__hero {
  padding: clamp(var(--sp-6), 7vw, 84px) clamp(var(--sp-4), 4vw, var(--sp-6)) var(--sp-6);
  max-width: 1180px; width: 100%; margin: 0 auto;
  color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
.home__cta .btn { text-shadow: none; }
.home__eyebrow {
  margin: 0 0 var(--sp-2); font-size: .78rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--seal-gold);
}
.home__title {
  margin: 0 0 var(--sp-3); font-size: clamp(1.9rem, 5vw, 3.1rem);
  line-height: 1.08; letter-spacing: -.025em; max-width: 16ch;
}
.home__lead { margin: 0; max-width: 62ch; color: rgba(255, 255, 255, .88); font-size: 1.03rem; line-height: 1.6; }
.home__cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-5); }

.home__section {
  padding: var(--sp-6) clamp(var(--sp-4), 4vw, var(--sp-6));
  max-width: 1180px; width: 100%; margin: 0 auto;
}
.home__section-head { margin-bottom: var(--sp-4); color: #fff; }
.home__section-head h2 { margin: 0; font-size: 1.35rem; letter-spacing: -.015em; }
.home__muted { color: var(--text-2); font-size: .88rem; margin: var(--sp-1) 0 0; }
/* Section headings and notes sit straight on the photo; the cards bring their own ground. */
.home__section .home__muted { color: rgba(255, 255, 255, .8); }

.home__cards { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); }

/* The three front doors: client portal, public display, staff console. Each
   card is the whole link, so the target is the card and not the words on it. */
.home__portals { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.home__portal {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-5); border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); color: inherit; text-decoration: none;
  box-shadow: var(--shadow-1);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.home__portal:hover, .home__portal:focus-visible {
  transform: translateY(-2px); box-shadow: var(--shadow-2); border-color: var(--brand-600);
}
.home__portal-icon { display: inline-flex; color: var(--brand-600); }
.home__portal h3 { margin: 0; font-size: 1.05rem; }
.home__portal p { margin: 0; font-size: .87rem; color: var(--text-2); line-height: 1.55; }
.home__portal-go { margin-top: auto; padding-top: var(--sp-2); font-size: .85rem; font-weight: 650; color: var(--brand-600); }

.home__notice {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: baseline;
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4);
  border: 1px solid var(--border); border-left: 4px solid var(--warn-600);
  border-radius: var(--r); background: var(--surface-2); font-size: .9rem;
}
.home__notice span { color: var(--text-2); }
.home__stamp { margin: var(--sp-4) 0 0; font-size: .8rem; color: rgba(255, 255, 255, .75); }

/* --- one office --------------------------------------------------------- */
.qcard {
  display: flex; flex-direction: column; gap: var(--sp-3);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-1);
  padding: var(--sp-4);
}
.qcard__head { display: flex; align-items: flex-start; gap: var(--sp-3); }
.qcard__head .badge { margin-left: auto; }
.qcard__name { margin: 0; font-size: 1.02rem; letter-spacing: -.01em; }
.qcard__loc { margin: 2px 0 0; font-size: .8rem; color: var(--text-3); }

.qcard__label {
  display: block; font-size: .7rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}
.qcard__now {
  border-radius: var(--r); padding: var(--sp-3);
  background: linear-gradient(150deg, var(--brand-900), var(--brand-700)); color: #fff;
}
.qcard__now .qcard__label { color: rgba(255, 255, 255, .72); }
.qcard__nums { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-2); }
.qnum { display: flex; flex-direction: column; gap: 1px; }
.qnum__no { font-family: var(--mono); font-size: 1.55rem; font-weight: 750; line-height: 1.1; letter-spacing: -.02em; }
.qnum__at { font-size: .74rem; opacity: .8; }
.qnum--priority .qnum__no { color: var(--gold-500); }
.qnum--idle .qnum__no { opacity: .55; }

.qcard__stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); margin: 0; }
.qcard__stats dt { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-3); }
.qcard__stats dd { margin: 2px 0 0; font-size: .95rem; font-weight: 700; }

.qcard__next { display: flex; flex-direction: column; gap: var(--sp-2); }
.qcard__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.qcard__none { font-size: .85rem; color: var(--text-3); }
.home .chip {
  font-family: var(--mono); font-size: .76rem; font-weight: 650;
  padding: 3px 8px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
}
.home .chip--priority { background: var(--gold-100); color: var(--gold-600); }

.qcard__foot {
  display: flex; align-items: center; gap: var(--sp-3);
  padding-top: var(--sp-3); border-top: 1px solid var(--border);
  font-size: .82rem; color: var(--text-3);
}
.qcard__link { margin-left: auto; color: var(--brand-600); font-weight: 650; text-decoration: none; }
.qcard__link:hover { text-decoration: underline; }

/* --- how it works ------------------------------------------------------- */
.home__steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  counter-reset: home-step;
}
.home__steps li {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-4); position: relative;
}
.home__steps li::after {
  counter-increment: home-step; content: counter(home-step);
  position: absolute; top: var(--sp-4); right: var(--sp-4);
  font-size: 1.6rem; font-weight: 750; color: var(--surface-3);
}
.home__step-icon { display: inline-flex; color: var(--brand-600); }
.home__steps h3 { margin: var(--sp-2) 0 var(--sp-1); font-size: 1rem; }
.home__steps p { margin: 0; font-size: .87rem; color: var(--text-2); line-height: 1.55; }

/* --- footer ------------------------------------------------------------- */
.home__foot {
  margin-top: auto;
  display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); align-items: center;
  padding: var(--sp-5) clamp(var(--sp-4), 4vw, var(--sp-6));
  border-top: 1px solid var(--border); background: var(--surface-2);
  font-size: .85rem;
}
.home__foot > div:first-child { display: flex; flex-direction: column; gap: 2px; }
.home__foot-links { margin-left: auto; display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.home__foot-links a { color: var(--brand-600); text-decoration: none; }
.home__foot-links a:hover { text-decoration: underline; }

@media (max-width: 560px) {
  .home__bar-actions .btn--secondary { display: none; }
  .qcard__stats { grid-template-columns: repeat(2, 1fr); }
  .home__foot-links { margin-left: 0; }
}

@media print {
  .sidebar, .appbar, .tabbar, .toasts, .btn, .offline-banner { display: none !important; }
  #app { grid-template-columns: 1fr; grid-template-areas: "main"; }
  .main { padding: 0; max-width: none; }
  .card { box-shadow: none; border: 1px solid #999; break-inside: avoid; }
  body { background: #fff; font-size: 12px; }
}

/* ==========================================================================
   The accomplished request form -- public/js/views/formprint.js

   An A4 sheet, not a card: the offices file this in a folder, so it is laid
   out in millimetres against paper rather than in the app's own spacing, and
   it is deliberately exempt from dark mode. A form printed white-on-black
   wastes a cartridge and is unreadable once it is photocopied, which is what
   happens to a sheet in a folder.
   ========================================================================== */
.sheet-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.sheet-bar__text { margin: 0; flex: 1 1 240px; font-size: .85rem; color: var(--muted); }

.sheet {
  /* A4 less a 15mm margin on each side. `auto` centres it on a wide screen
     and the max-width keeps it to the paper on a narrow one. */
  width: 180mm;
  max-width: 100%;
  margin: 0 auto;
  padding: 12mm 14mm;
  box-sizing: border-box;
  background: #fff;
  color: #111;
  border: 1px solid #c9c9c9;
  box-shadow: 0 2px 10px rgb(0 0 0 / .08);
  font-family: "Times New Roman", Times, Georgia, serif;
  font-size: 11.5pt;
  line-height: 1.45;
}

.sheet__head {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  align-items: start;
  gap: 10px;
}
.sheet__seal { width: 64px; height: 64px; object-fit: contain; }
.sheet__head-text { text-align: center; }
.sheet__head-text p { margin: 0; }
.sheet__republic { font-size: 10pt; }
.sheet__uni { font-size: 13pt; font-weight: 700; letter-spacing: .02em; }
.sheet__campus { font-size: 10pt; font-style: italic; }
.sheet__office { font-size: 11pt; font-weight: 600; margin-top: 2px !important; }
.sheet__control {
  text-align: right;
  font-size: 8.5pt;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sheet__rev { white-space: nowrap; }

.sheet__title {
  margin: 10px 0 12px;
  text-align: center;
  font-size: 14pt;
  font-weight: 700;
  letter-spacing: .08em;
  border-top: 1.5px solid #111;
  border-bottom: 1.5px solid #111;
  padding: 4px 0;
}

.sheet__section { margin-bottom: 10px; break-inside: avoid; }
.sheet__section-title {
  margin: 0 0 4px;
  font-weight: 700;
  font-size: 11pt;
}

/* --- the tick lists ------------------------------------------------------ */
.sheet__boxes { display: grid; gap: 2px; }
.sheet__boxes--row { grid-template-columns: repeat(2, 1fr); gap: 2px 16px; }
.sheet__box { display: flex; align-items: flex-start; gap: 7px; }
.sheet__tick {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin-top: 3px;
  border: 1.2px solid #111;
  font-size: 11px;
  line-height: 11px;
  text-align: center;
  font-family: Arial, Helvetica, sans-serif;
}
.sheet__tick.is-on { font-weight: 700; }
.sheet__box-label { flex: 1 1 auto; }
.sheet__note { font-size: 8.5pt; font-style: italic; color: #555; }

/* --- the ruled lines ----------------------------------------------------- */
.sheet__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 18px;
  margin: 4px 0;
}
.sheet__line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-height: 20px;
}
.sheet__line--wide { grid-column: 1 / -1; }
.sheet__label { flex: 0 0 auto; font-size: 9.5pt; color: #333; white-space: nowrap; }
/* The answer sits on the rule, so the rule runs to the end of the line
   whether or not there is an answer on it. */
.sheet__value {
  flex: 1 1 auto;
  min-width: 0;
  border-bottom: 1px solid #111;
  padding: 0 2px 1px;
  overflow-wrap: anywhere;
}

/* Where the paper prints blank lines to write on rather than a caption. */
.sheet__written {
  border-bottom: 1px solid #111;
  min-height: 42px;
  padding: 2px;
  overflow-wrap: anywhere;
}

/* --- the credentials checklist ------------------------------------------- */
/* On a phone the checklist scrolls inside the sheet rather than widening it.
   Printing has the full width of the paper, so the wrapper lets go there. */
.sheet__scroll { overflow-x: auto; }
.sheet__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 10pt;
}
.sheet__table th,
.sheet__table td {
  border: 1px solid #111;
  padding: 3px 5px;
  text-align: left;
  vertical-align: top;
}
.sheet__table th { font-weight: 700; background: #f0f0f0; }
.sheet__table .num { text-align: right; white-space: nowrap; }
.sheet__table .right { text-align: right; }
.sheet__table tfoot td { border-top: 1px solid #111; }

/* --- signature and the office foot --------------------------------------- */
.sheet__sign {
  margin: 16px 0 10px;
  width: 70mm;
  margin-left: auto;
  text-align: center;
  break-inside: avoid;
}
.sheet__sign-rule { border-bottom: 1px solid #111; height: 26px; }
.sheet__sign-label { margin: 2px 0 0; font-size: 9.5pt; }
.sheet__sign .sheet__note { display: block; margin-top: 2px; }

.sheet__office-use {
  margin-top: 12px;
  border: 1px solid #111;
  padding: 6px 8px 8px;
  break-inside: avoid;
}
.sheet__office-use-title {
  margin: 0 0 4px;
  font-weight: 700;
  font-size: 10pt;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.sheet__foot {
  margin: 10px 0 0;
  font-size: 8pt;
  color: #555;
  text-align: center;
  border-top: 1px solid #ccc;
  padding-top: 4px;
}

@media (max-width: 640px) {
  /* The sheet still has to be readable on the phone that is looking at it
     before it is printed from somewhere else. */
  .sheet { width: 100%; padding: 8mm; font-size: 11pt; }
  .sheet__head { grid-template-columns: 44px 1fr; }
  .sheet__seal { width: 44px; height: 44px; }
  .sheet__control { grid-column: 1 / -1; text-align: left; flex-direction: row; gap: 8px; }
  .sheet__grid,
  .sheet__boxes--row { grid-template-columns: minmax(0, 1fr); }
}

@media print {
  /* One sheet, on the paper, with the app's chrome and the toolbar gone. */
  @page { size: A4 portrait; margin: 12mm; }
  .sheet-bar { display: none !important; }
  .sheet {
    width: auto;
    max-width: none;
    margin: 0;
    padding: 0;
    border: 0;
    box-shadow: none;
    font-size: 11pt;
  }
  .sheet__table th { background: transparent; }
  .sheet__scroll { overflow-x: visible; }
}
