/*
 * attend. -- the components layer: shared components first (icon, buttons,
 * page header, lists and rows, cards, navigation, dialogs, status chips,
 * states, banners, forms, segmented control, tables), then the screens.
 *
 * static/css/app.css declares the layer order and holds the tokens; this file
 * only adds rules to `@layer components`, with colours as var(--attend-*)
 * tokens, no gradients, blur or filters, and shadows only as the light theme's
 * elevation tokens (tests/test_css_rules.py). Look: docs/design/visual-design.md
 * (tests/ui/test_components_look.py, tests/ui/test_status_families.py).
 *
 * Linked after app.css (templates/base.html), so screen stylesheets linked
 * after this one override a shared component at equal specificity.
 */
@layer components {
  /* Icon (Lucide sprite via {% icon %}) */
  .icon {
    inline-size: 16px;
    block-size: 16px;
    flex-shrink: 0;
  }

  /* A plain link with a leading icon ("All groups" back links): the icon on
     the link's line (svg is a block in the reset), the link a 44px target. */
  p > a:not([class]):has(> .icon:first-child) {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--touch);
  }

  .icon--lg {
    inline-size: 20px;
    block-size: 20px;
  }

  .icon--xl {
    inline-size: 28px;
    block-size: 28px;
  }

  /* Wordmark */
  .brand {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0 var(--space-3);
    color: var(--attend-text-strong);
    text-decoration: none;
  }

  .brand__mark {
    font-size: 23px;
    font-weight: var(--weight-bold);
    letter-spacing: -1.2px;
    line-height: 1.2;
    color: var(--attend-text-strong);
  }

  .brand__org {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-meta);
    color: var(--attend-text-secondary);
  }

  /* Buttons (visual-design.md §7): pills in Figtree 650, icon 16-20px, gap 8.
     .btn is the secondary button (brand tint), .btn--primary the one primary
     of a view, .btn--plain the ghost, .btn--danger the rose outline of a
     destructive confirmation (never a bright fill). Heights: phone 56 for the
     primary and 48 for the others; from 768px 44 / 40, ghost 36. */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 48px;
    min-width: var(--touch);
    padding: var(--space-2) var(--space-6);
    border: 1px solid transparent;
    border-radius: var(--r-pill);
    background: var(--attend-brand-tint);
    color: var(--attend-brand-text);
    font-size: var(--text-button);
    font-weight: var(--weight-button);
    line-height: var(--leading-button);
    text-decoration: none;
    /* Labels stay on one line while they fit; with enlarged text a button
       wraps its label instead of pushing the page sideways. */
    max-width: 100%;
    text-align: center;
    overflow-wrap: anywhere;
    transition: transform 120ms ease-out;
  }

  .btn:hover {
    border-color: currentColor;
    color: var(--attend-link-hover);
  }

  .btn:active {
    transform: scale(0.97);
  }

  .btn--primary {
    min-height: 56px;
    background: var(--attend-brand);
    color: var(--attend-on-brand);
    box-shadow: var(--attend-shadow-primary);
  }

  /* brand-hover is a darker fill than brand in both themes, so the fill shows
     the hover (white on it 7.20 / 6.11). */
  .btn--primary:hover {
    border-color: transparent;
    background: var(--attend-brand-hover);
    color: var(--attend-on-brand);
  }

  .btn--plain {
    background: transparent;
    color: var(--attend-text);
  }

  .btn--plain:hover {
    border-color: transparent;
    background: var(--attend-row-hover);
    color: var(--attend-text-strong);
  }

  .btn--danger {
    border-color: var(--attend-danger-text);
    background: transparent;
    color: var(--attend-danger-text);
  }

  .btn--danger:hover {
    border-color: var(--attend-danger-text);
    background: var(--attend-danger-bg);
    color: var(--attend-danger-text);
  }

  .btn--icon {
    padding: 0;
    inline-size: 48px;
  }

  .btn--block {
    width: 100%;
  }

  .btn:disabled,
  .btn[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: 0.4;
    box-shadow: none;
    transform: none;
  }

  .btn[aria-busy="true"] {
    cursor: progress;
  }

  /* Compact for a mouse only: a touched tablet keeps 44px targets
     (tests/test_css_touch.py). */
  @media (min-width: 768px) and (pointer: fine) {
    .btn {
      min-height: 40px;
      padding: var(--space-1) var(--space-5);
    }

    .btn--primary {
      min-height: 44px;
    }

    .btn--plain {
      min-height: 36px;
    }

    /* Square and round: .btn's 44px min-width would stretch it. */
    .btn--icon {
      inline-size: 40px;
      min-width: 0;
      min-height: 40px;
      padding: 0;
    }
  }

  .actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }

  /* Page header: breadcrumbs or an eyebrow label, the title, a muted line;
     the page's actions on the right. */
  .page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4) var(--space-6);
    margin-bottom: var(--space-6);
  }

  /* A one-word title ("Administration") still fits a 360px screen with
     enlarged text: it breaks inside the word rather than scrolling sideways. */
  .page-header > div {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .page-header__eyebrow {
    margin-bottom: var(--space-2);
    color: var(--attend-text-muted);
    font-size: var(--text-label);
    font-weight: var(--weight-label);
    letter-spacing: var(--tracking-label);
    line-height: var(--leading-label);
    text-transform: uppercase;
  }

  .page-header__sub {
    max-width: var(--measure);
    margin-top: var(--space-2);
    font-size: var(--text-small);
    color: var(--attend-text-muted);
  }

  /* Breadcrumbs: small muted links with "/" between them; the current page
     in body text. */
  .breadcrumbs {
    margin-bottom: var(--space-2);
    color: var(--attend-text-muted);
    font-size: var(--text-small);
    line-height: var(--leading-small);
  }

  .breadcrumbs ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .breadcrumbs li + li::before {
    content: "/";
    content: "/" / "";
    margin-inline: var(--space-2);
  }

  .breadcrumbs a {
    color: var(--attend-text-muted);
    text-decoration: none;
  }

  .breadcrumbs a:hover,
  .breadcrumbs a:focus-visible {
    color: var(--attend-text-strong);
    text-decoration: underline;
  }

  .breadcrumbs [aria-current="page"] {
    color: var(--attend-text);
    font-weight: var(--weight-bold);
  }

  /* A breadcrumb is the whole list item, so on phones it is a 44px target. */
  @media (max-width: 767px) {
    .breadcrumbs a {
      display: inline-flex;
      align-items: center;
      min-height: var(--touch);
      min-inline-size: var(--touch);
    }
  }

  /* Sections and lists */
  .section {
    margin-top: var(--space-8);
  }

  .section__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--attend-border);
  }

  .section__head h2 {
    font-size: var(--text-body);
  }

  .section > h3:not(:first-child) {
    margin-block-start: var(--space-6);
  }

  /* The eyebrow label above a section ("Later today", "Needs attention"). */
  .section-label {
    margin: var(--space-6) 0 var(--space-2);
    color: var(--attend-text-muted);
    font-size: var(--text-label);
    font-weight: var(--weight-label);
    letter-spacing: var(--tracking-label);
    line-height: var(--leading-label);
    text-transform: uppercase;
  }

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

  /* List rows (§7): a card of 64px rows, 12x16 padding, dividers inset 16px. */
  .row-list:has(> .row) {
    border: 1px solid var(--attend-border);
    border-radius: var(--r-lg);
    background: var(--attend-surface);
    box-shadow: var(--attend-shadow-card);
  }

  .row {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: 64px;
    padding: var(--space-3) var(--space-4);
  }

  .row + .row::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline: var(--space-4);
    border-top: 1px solid var(--attend-border);
  }

  /* Inside a card or a dialog's sheet the list keeps only its dividers (no
     card in a card, at most two levels at once: §6). */
  .panel .row-list,
  .card .row-list,
  .dialog .row-list {
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  .panel .row,
  .card .row,
  .dialog .row {
    padding-inline: 0;
  }

  .panel .row + .row::before,
  .card .row + .row::before,
  .dialog .row + .row::before {
    inset-inline: 0;
  }

  .row__title {
    color: var(--attend-text-strong);
    font-weight: var(--weight-strong);
  }

  /* Names that open a record (row titles, a table's first cell) read as plain
     bold names, as in the reference; the underline comes back on hover and
     focus. Action links in other cells keep the link colour. */
  .row__title a,
  .table td:first-child a:not(.btn),
  .table th[scope="row"] a {
    color: inherit;
    text-decoration: none;
  }

  a.row__title {
    text-decoration: none;
  }

  a.row__title:hover,
  a.row__title:focus-visible,
  .row__title a:hover,
  .row__title a:focus-visible,
  .table td:first-child a:not(.btn):hover,
  .table td:first-child a:not(.btn):focus-visible,
  .table th[scope="row"] a:hover,
  .table th[scope="row"] a:focus-visible {
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  /* Administration overview: "Needs attention" rows keep "Review" beside the
     text on phones too (the reference's review-row); "Quick access" sits a
     section further down. */
  .attention-list .row {
    flex-wrap: nowrap;
  }

  .attention-list .row > div {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .attention-list .btn {
    flex-shrink: 0;
  }

  .overview-section + .overview-section {
    margin-top: var(--space-8);
  }

  .overview-section > .actions {
    margin-top: var(--space-4);
  }

  .row__meta {
    margin-top: 2px;
    font-size: var(--text-small);
    color: var(--attend-text-muted);
  }

  /* Leading items of a row: a time tile or a person's initials (no photos). */
  .time-tile {
    display: inline-grid;
    place-items: center;
    flex: none;
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--r-sm);
    background: var(--attend-brand-tint);
    color: var(--attend-brand-text);
    font-size: 13px;
    font-weight: var(--weight-label);
    font-variant-numeric: tabular-nums;
  }

  .initials {
    display: inline-grid;
    place-items: center;
    flex: none;
    inline-size: 40px;
    block-size: 40px;
    border: 1px solid var(--attend-border);
    border-radius: 50%;
    background: var(--attend-ground);
    color: var(--attend-text-strong);
    font-size: var(--text-small);
    font-weight: var(--weight-strong);
  }

  .initials--sm {
    inline-size: 28px;
    block-size: 28px;
    font-size: var(--text-label);
  }

  /* Cards (§7): surface, 22px corners, the light theme's card shadow. */
  .card,
  .panel {
    padding: var(--space-5);
    border: 1px solid var(--attend-border);
    border-radius: var(--r-lg);
    background: var(--attend-surface);
    box-shadow: var(--attend-shadow-card);
  }

  @media (min-width: 768px) {
    .card,
    .panel {
      padding: var(--space-6);
    }
  }

  /* A disclosure card (details.panel) is a compact row until it is opened;
     its chevron sits at the end. */
  details.panel:not([open]) {
    padding-block: var(--space-2);
  }

  details.panel > summary::after {
    margin-inline-start: auto;
  }

  /* Hero card base (the current class): the soft tinted card, dark text, no
     glow. Screens place its content; blobs, if any, are clipped by it. */
  .hero {
    position: relative;
    overflow: hidden;
    padding: var(--space-6);
    border: 1px solid var(--attend-hero-border);
    border-radius: var(--r-xl);
    background: var(--attend-hero);
    color: var(--attend-text);
  }

  .definition-list {
    display: grid;
    grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
    gap: var(--space-3) var(--space-6);
    margin: 0;
  }

  .definition-list dt {
    font-size: var(--text-small);
    color: var(--attend-text-muted);
  }

  .definition-list dd {
    margin: 0;
    overflow-wrap: anywhere;
  }

  @media (max-width: 479px) {
    .definition-list {
      grid-template-columns: minmax(0, 1fr);
      gap: var(--space-1);
    }

    .definition-list dd + dt {
      margin-top: var(--space-3);
    }
  }

  /* Navigation: sidebar / rail */
  .nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .nav__section {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .nav__title {
    padding: 0 var(--space-3) var(--space-1);
    font-size: var(--text-meta);
    font-weight: var(--weight-medium);
    color: var(--attend-text-secondary);
  }

  .nav__link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch);
    padding: 0 var(--space-3);
    border-radius: var(--radius-control);
    color: var(--attend-text-secondary);
    font-size: var(--text-small);
    font-weight: var(--weight-book);
    text-decoration: none;
  }

  /* One line; the link's title shows a label cut short (enlarged text). */
  .nav__label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .nav__link:hover {
    background: var(--attend-surface-hover);
    color: var(--attend-text-strong);
  }

  .nav__link[aria-current="page"] {
    background: var(--attend-surface-hover);
    color: var(--attend-text-strong);
    font-weight: var(--weight-semi);
  }

  /* Notices (apps.ui.notices): the shell's link with its unread count */
  .notices-link {
    position: relative;
  }

  .count-badge {
    display: inline-grid;
    place-items: center;
    min-inline-size: 18px;
    block-size: 18px;
    padding: 0 5px;
    border-radius: var(--r-pill);
    background: var(--attend-action);
    color: var(--attend-action-text);
    font-size: var(--text-meta);
    font-weight: var(--weight-semi);
    line-height: 1;
  }

  .btn--icon.notices-link .count-badge {
    position: absolute;
    inset-block-start: 2px;
    inset-inline-end: 0;
  }

  .notice--unread .row__title {
    font-weight: var(--weight-semi);
    color: var(--attend-text-strong);
  }

  .notice--unread {
    border-inline-start: 3px solid var(--attend-action);
    padding-inline-start: var(--space-3);
  }

  .notice__body {
    white-space: normal;
  }

  /* The foot is quieter than the navigation (reference: a small, muted profile). */
  .sidebar__foot {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }

  .sidebar__credit {
    padding: 0 var(--space-3);
  }

  .nav-toggle {
    display: none;
  }

  /* Rail: 768-1023px always, and >=1024px when collapsed. Labels stay in the
     accessibility tree (visually hidden), so links keep their names. */
  @media (min-width: 768px) and (max-width: 1023px) {
    .sidebar .brand__org,
    .sidebar .brand__full,
    .sidebar .nav__title,
    .sidebar .sidebar__credit,
    .sidebar .user-menu__text {
      display: none;
    }

    .sidebar .nav__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    .sidebar .nav__link,
    .sidebar .brand {
      justify-content: center;
      align-items: center;
      padding: 0;
    }
  }

  @media (min-width: 1024px) {
    .nav-toggle {
      display: inline-flex;
    }

    .shell[data-nav="collapsed"] .sidebar .brand__org,
    .shell[data-nav="collapsed"] .sidebar .brand__full,
    .shell[data-nav="collapsed"] .sidebar .nav__title,
    .shell[data-nav="collapsed"] .sidebar .sidebar__credit,
    .shell[data-nav="collapsed"] .sidebar .user-menu__text {
      display: none;
    }

    .shell[data-nav="collapsed"] .sidebar .nav__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    .shell[data-nav="collapsed"] .sidebar .nav__link,
    .shell[data-nav="collapsed"] .sidebar .brand {
      justify-content: center;
      align-items: center;
      padding: 0;
    }

    .shell[data-nav="collapsed"] .nav-toggle .icon {
      transform: scaleX(-1);
    }
  }

  /* Mobile top bar */
  .topbar__brand {
    font-size: 19px;
    font-weight: var(--weight-bold);
    letter-spacing: -0.05em;
    color: var(--attend-text-strong);
    text-decoration: none;
  }

  .topbar__actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
  }

  /* Mobile bottom navigation (students and lecturers) */
  .bottom-nav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 20;
    border-top: 1px solid var(--attend-edge);
    background: var(--attend-surface);
    padding-bottom: env(safe-area-inset-bottom);
  }

  .bottom-nav__list {
    display: grid;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    list-style: none;
    margin: 0;
    padding: 0 env(safe-area-inset-right) 0 env(safe-area-inset-left);
  }

  .bottom-nav__link .icon {
    inline-size: 20px;
    block-size: 20px;
  }

  .bottom-nav__link[aria-current="page"] {
    color: var(--attend-text-strong);
  }

  /* Dialogs (§6-7): a bordered, raised sheet; on phones it rises from the
     bottom with 28px top corners. Destructive confirmations put the rose
     outlined .btn--danger in it. */
  .dialog {
    width: min(480px, calc(100vw - 2 * var(--space-4)));
    max-height: calc(100dvh - 2 * var(--space-4));
    padding: 0;
    border: 1px solid var(--attend-border);
    border-radius: var(--r-lg);
    background: var(--attend-raised);
    color: var(--attend-text);
    box-shadow: var(--attend-shadow-raised);
  }

  .dialog::backdrop,
  .drawer::backdrop {
    background: color-mix(in srgb, var(--attend-ground) 72%, transparent);
  }

  .dialog__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-3) var(--space-2) var(--space-6);
  }

  .dialog__head h2 {
    font-size: var(--text-h2);
  }

  .dialog__body {
    padding: var(--space-2) var(--space-6) var(--space-6);
  }

  .dialog__foot {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: 0 var(--space-6) var(--space-6);
  }

  @media (max-width: 767px) {
    .dialog {
      width: 100%;
      max-width: 100%;
      margin: auto 0 0;
      border-radius: var(--r-xl) var(--r-xl) 0 0;
      padding-bottom: env(safe-area-inset-bottom);
    }
  }

  .drawer {
    width: min(320px, 86vw);
    max-width: none;
    height: 100dvh;
    max-height: none;
    margin: 0;
    padding: var(--space-3);
    padding-top: max(var(--space-3), env(safe-area-inset-top));
    padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
    border: 0;
    border-right: 1px solid var(--attend-edge);
    background: var(--attend-surface-subtle);
    color: var(--attend-text-strong);
  }

  .drawer[open] {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .drawer__foot {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  /* User menu and avatar */
  .avatar {
    display: inline-grid;
    place-items: center;
    inline-size: 30px;
    block-size: 30px;
    flex-shrink: 0;
    border: 1px solid var(--attend-edge);
    border-radius: 50%;
    background: var(--attend-avatar-bg);
    color: var(--attend-text-strong);
    font-size: var(--text-meta);
    font-weight: var(--weight-bold);
    text-decoration: none;
  }

  .user-menu {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }

  .user-menu__who {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch);
    padding: 0 var(--space-3);
    border-radius: var(--radius-control);
    color: var(--attend-text-strong);
    text-decoration: none;
  }

  .user-menu__who:hover {
    background: var(--attend-surface-hover);
  }

  .user-menu__text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    color: var(--attend-text-secondary);
    font-size: var(--text-meta);
    line-height: 1.35;
  }

  .user-menu__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: var(--weight-medium);
  }

  .user-menu__role {
    color: var(--attend-text-secondary);
    font-size: var(--text-meta);
  }

  .user-menu form {
    display: contents;
  }

  .user-menu .btn {
    justify-content: flex-start;
    padding: 0 var(--space-3);
    font-size: var(--text-small);
    font-weight: var(--weight-book);
  }

  @media (min-width: 768px) and (max-width: 1023px) {
    .sidebar .user-menu__who,
    .sidebar .user-menu .btn {
      justify-content: center;
      padding: 0;
    }

    .sidebar .user-menu .btn .nav__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
  }

  @media (min-width: 1024px) {
    .shell[data-nav="collapsed"] .sidebar .user-menu__who,
    .shell[data-nav="collapsed"] .sidebar .user-menu .btn {
      justify-content: center;
      padding: 0;
    }
  }

  /* Credit line */
  .credit {
    font-size: var(--text-meta);
    color: var(--attend-text-secondary);
  }

  /* Status chips ("Status colours", §7): always icon + word, never colour
     alone. A pill, softly filled with the family's tint on phones and in
     cards; outlined (the tint cleared, the edge in the text colour) in dense
     desktop tables. The text colour is also the icon's and the outline's.
     A tone class alone (.status--danger on a notice line, policies/confirm.html)
     only colours the text: the tint belongs to the chip. */
  .status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 28px;
    padding: 0 10px 0 var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--r-pill);
    font-size: 13px;
    font-weight: var(--weight-strong);
    line-height: 18px;
    vertical-align: middle;
    /* One line where it fits; wraps rather than widening the page (200% text). */
    white-space: nowrap;
    max-width: 100%;
  }

  .status .icon {
    inline-size: 14px;
    block-size: 14px;
  }

  /* The word wraps between words only: break-word splits a word only when
     it is longer than the whole line, without shrinking a table column to a
     letter or two ("anywhere" did: "Prese / nt"). */
  .status > span {
    min-width: 0;
    white-space: normal;
    overflow-wrap: break-word;
  }

  .status--positive {
    color: var(--attend-positive-text);
  }

  .status.status--positive {
    background: var(--attend-positive-bg);
  }

  .status--warning {
    color: var(--attend-warning-text);
  }

  .status.status--warning {
    background: var(--attend-warning-bg);
  }

  .status--info {
    color: var(--attend-info-text);
  }

  .status.status--info {
    background: var(--attend-info-bg);
  }

  .status--inactive {
    color: var(--attend-inactive-text);
  }

  .status.status--inactive {
    background: var(--attend-inactive-bg);
  }

  .status--danger {
    color: var(--attend-danger-text);
  }

  .status.status--danger {
    background: var(--attend-danger-bg);
  }

  /* Live (an open check-in only): a 6px dot before the icon, pulsing. */
  .status--live .status__dot {
    flex: none;
    inline-size: 6px;
    block-size: 6px;
    border-radius: 50%;
    background: currentColor;
    animation: attend-pulse 1.6s ease-in-out infinite alternate;
  }

  @keyframes attend-pulse {
    from {
      opacity: 1;
    }

    to {
      opacity: 0.35;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .status--live .status__dot {
      animation: none;
    }
  }

  /* A detail beside a chip ("code mismatch", "10:09"), outside the pill. */
  .status-detail {
    margin-inline-start: var(--space-2);
    color: var(--attend-text-muted);
    font-size: var(--text-small);
    vertical-align: middle;
  }

  @media (min-width: 768px) {
    .status {
      min-height: 24px;
    }

    .table .status {
      min-height: 24px;
      border-color: currentColor;
      background: transparent;
    }

    /* A chip, an ID or a time stays on one line in a desktop table; a table
       too wide for the page scrolls in its frame instead. */
    .table .status > span {
      white-space: nowrap;
    }

    .table .tabular {
      white-space: nowrap;
    }
  }

  /* Empty, error and loading states: the icon in a 48px circle, a title and
     what to do next. */
  .state {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-8) 0;
    color: var(--attend-text-muted);
  }

  .state__icon {
    display: inline-grid;
    place-items: center;
    inline-size: 48px;
    block-size: 48px;
    margin-bottom: var(--space-1);
    border-radius: 50%;
    background: var(--attend-inactive-bg);
    color: var(--attend-inactive-text);
  }

  .state__icon .icon {
    inline-size: 24px;
    block-size: 24px;
  }

  .state__title {
    font-size: var(--text-body);
    font-weight: var(--weight-strong);
    color: var(--attend-text-strong);
  }

  .state__body {
    max-width: 52ch;
    font-size: var(--text-small);
  }

  .state--error .state__icon {
    background: var(--attend-warning-bg);
    color: var(--attend-warning-text);
  }

  .state .actions {
    margin-top: var(--space-2);
  }

  .loading {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--attend-text-secondary);
    font-size: var(--text-support);
  }

  .spin {
    animation: attend-spin 0.9s linear infinite;
  }

  @keyframes attend-spin {
    to {
      transform: rotate(1turn);
    }
  }

  /* htmx request indicator (htmx's own indicator styles are disabled). */
  .htmx-indicator {
    visibility: hidden;
  }

  .htmx-request .htmx-indicator,
  .htmx-request.htmx-indicator {
    visibility: visible;
  }

  .app-loading {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-6) var(--space-4);
    text-align: center;
  }

  /* Banners: a soft tint of the status family with its icon; the message in
     body text. */
  .banner {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--r-md);
    background: var(--attend-warning-bg);
    color: var(--attend-text);
    font-size: var(--text-small);
  }

  .banner .icon {
    margin-top: 2px;
    color: var(--attend-warning-text);
  }

  .banner--info {
    background: var(--attend-info-bg);
  }

  .banner--info .icon {
    color: var(--attend-info-text);
  }

  .banner--danger {
    background: var(--attend-danger-bg);
  }

  .banner--danger .icon {
    color: var(--attend-danger-text);
  }

  .banner__title {
    font-weight: var(--weight-strong);
    color: var(--attend-text-strong);
  }

  /* App update notice (static/js/pwa/update.js): above the mobile bottom
     navigation, bottom right from 768px. Never shown while scanning or presenting. */
  .pwa-update {
    position: fixed;
    inset: auto var(--space-4) calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + var(--space-3));
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--attend-edge);
    border-left: 2px solid var(--attend-accent-line);
    border-radius: var(--radius-panel);
    background: var(--attend-raised);
    color: var(--attend-text-strong);
    font-size: var(--text-support);
  }

  .pwa-update__text {
    font-weight: var(--weight-medium);
  }

  @media (min-width: 768px) {
    .pwa-update {
      inset: auto var(--space-6) var(--space-6) auto;
      max-width: 360px;
    }
  }

  /* Forms: labels above fields; help in muted small text; errors are a
     circle-alert icon plus danger text, never colour alone. */
  .form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
  }

  .field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .field__label {
    font-size: var(--text-small);
    font-weight: var(--weight-bold);
    color: var(--attend-text-strong);
  }

  .field__help {
    font-size: var(--text-small);
    color: var(--attend-text-muted);
  }

  .field__error,
  .form-error {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    font-size: var(--text-small);
    color: var(--attend-danger-text);
  }

  .form-error {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--r-md);
    background: var(--attend-danger-bg);
  }

  .form-error .icon,
  .field__error .icon {
    margin-top: 2px;
  }

  /* A file input's button is the secondary pill (brand tint, brand text). */
  input[type="file"]::file-selector-button {
    margin-inline-end: var(--space-3);
    padding: var(--space-1) var(--space-4);
    border: 0;
    border-radius: var(--r-pill);
    background: var(--attend-brand-tint);
    color: var(--attend-brand-text);
    font: inherit;
    font-size: var(--text-small);
    font-weight: var(--weight-button);
    cursor: pointer;
  }

  /* "Install the app" (components/install_card.html): a card with the phone
     icon in a soft circle, a line of text and, where the browser offers it,
     the Install button. Shown by static/js/pwa/install-card.js only. */
  .install-card {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3) var(--space-4);
    margin-block: var(--space-4);
    padding: var(--space-4);
    border: 1px solid var(--attend-border);
    border-radius: var(--r-lg);
    background: var(--attend-surface);
    box-shadow: var(--attend-shadow-card);
  }

  .install-card[hidden] {
    display: none;
  }

  .install-card .install-card__icon {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    inline-size: 44px;
    block-size: 44px;
    border-radius: 50%;
    background: var(--attend-brand-tint);
    color: var(--attend-accent);
  }

  .install-card .install-card__body {
    flex: 1 1 200px;
    min-width: 0;
  }

  .install-card .install-card__title {
    margin: 0;
    color: var(--attend-text-strong);
    font-size: var(--text-body);
    font-weight: var(--weight-bold);
  }

  .install-card .install-card__text {
    margin: 0;
    color: var(--attend-text-muted);
    font-size: var(--text-small);
  }

  /* Search (ScopedSearch and screens): the pill box with a leading icon. */
  .search-field {
    position: relative;
    display: block;
  }

  .search-field > .icon {
    position: absolute;
    inset-inline-start: var(--space-4);
    inset-block-start: 50%;
    inline-size: 18px;
    block-size: 18px;
    translate: 0 -50%;
    color: var(--attend-text-muted);
    pointer-events: none;
  }

  .search-field > input[type="search"] {
    padding-inline-start: 40px;
  }

  /* Theme switcher (a plain form; app.js saves on change) */
  .theme-switcher {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  .theme-switcher legend {
    margin-bottom: var(--space-2);
    font-size: var(--text-support);
    font-weight: var(--weight-medium);
  }

  /* Segmented control (§7 "Tabs / segmented"): a pill track on the ground,
     4px inset; the selected segment is a surface pill with the card shadow
     and a 3:1 edge (also in the dark theme, which has no shadows). */
  .segmented {
    display: inline-grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 2px;
    max-width: 360px;
    padding: var(--space-1);
    border: 1px solid var(--attend-border);
    border-radius: var(--r-pill);
    background: var(--attend-ground);
  }

  .segmented__option {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--touch);
    padding: 0 var(--space-4);
    border-radius: var(--r-pill);
    color: var(--attend-text-muted);
    font-size: var(--text-small);
    font-weight: var(--weight-bold);
    cursor: pointer;
  }

  .segmented__option:has(input:checked) {
    background: var(--attend-surface);
    color: var(--attend-text-strong);
    font-weight: var(--weight-strong);
    box-shadow: var(--attend-shadow-card);
    outline: 1px solid var(--attend-border-strong);
  }

  .segmented__option:has(input:focus-visible) {
    outline: 2px solid var(--attend-focus);
    outline-offset: 1px;
  }

  /* Dark (§7): the track one step up on the surface, the selected segment
     on raised, so it stands apart without a shadow. */
  :root[data-theme="dark"] .segmented {
    background: var(--attend-surface);
  }

  :root[data-theme="dark"] .segmented__option:has(input:checked) {
    background: var(--attend-raised);
  }

  @media (prefers-color-scheme: dark) {
    :root[data-theme="system"] .segmented {
      background: var(--attend-surface);
    }

    :root[data-theme="system"] .segmented__option:has(input:checked) {
      background: var(--attend-raised);
    }
  }

  /* The radio stays in the accessibility tree; the label is the target. */
  .segmented__option input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  @media (min-width: 768px) and (pointer: fine) {
    .segmented__option {
      min-height: 36px;
    }
  }

  /* Count pills: the number on a count tab ("All 57 · Present 41 · Review 3"),
     in the tint of what it counts. */
  .count-pill {
    display: inline-grid;
    place-items: center;
    min-inline-size: 20px;
    block-size: 20px;
    padding: 0 6px;
    border-radius: var(--r-pill);
    background: var(--attend-inactive-bg);
    color: var(--attend-inactive-text);
    font-size: var(--text-label);
    font-weight: var(--weight-label);
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .count-pill--positive {
    background: var(--attend-positive-bg);
    color: var(--attend-positive-text);
  }

  .count-pill--warning {
    background: var(--attend-warning-bg);
    color: var(--attend-warning-text);
  }

  .count-pill--info {
    background: var(--attend-info-bg);
    color: var(--attend-info-text);
  }

  .count-pill--inactive {
    background: var(--attend-inactive-bg);
    color: var(--attend-inactive-text);
  }

  .count-pill--danger {
    background: var(--attend-danger-bg);
    color: var(--attend-danger-text);
  }

  /* Pagination */
  .pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-5);
    font-size: var(--text-small);
    color: var(--attend-text-muted);
  }

  /* Tables (§7 "Table rows"): a bordered surface container with 16px
     corners, never elevated; the header band on the ground in the label
     style; 44px rows from 768px, 1px dividers, row actions as ghost buttons. */
  /* position: relative makes the frame the containing block of absolutely
     placed children (visually-hidden text in row actions), so its overflow
     clips them; otherwise they escape it and widen the page. */
  .table-wrap {
    position: relative;
    overflow-x: auto;
    border: 1px solid var(--attend-border);
    border-radius: var(--r-md);
    background: var(--attend-surface);
  }

  .table {
    width: 100%;
    text-align: left;
    font-size: var(--text-small);
  }

  .table th,
  .table td {
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--attend-border);
    vertical-align: middle;
  }

  .table thead th {
    block-size: 36px;
    border-top: 0;
    background: var(--attend-ground);
    color: var(--attend-text-muted);
    font-size: var(--text-label);
    font-weight: var(--weight-label);
    letter-spacing: 0.06em;
    line-height: var(--leading-label);
    text-transform: uppercase;
  }

  .table tbody tr:hover {
    background: var(--attend-row-hover);
  }

  /* A row that needs review: a thin plum marker and the review icon, no tint. */
  .table tr.is-review > :first-child {
    border-inline-start: 3px solid var(--attend-warning-text);
  }

  @media (min-width: 768px) {
    .table td,
    .table tbody th {
      block-size: 44px;
    }
  }

  @media (min-width: 768px) and (pointer: fine) {
    .table .btn {
      min-height: 36px;
    }

    /* Square: .btn's 44px min-width would make it an oval. */
    .table .btn--icon {
      inline-size: 36px;
      min-width: 0;
    }
  }

  /* Filter links that act as tabs: the selected one is a surface pill with a
     3:1 edge (the segmented style without the track). */
  .btn--plain[aria-current="true"] {
    border-color: var(--attend-border-strong);
    background: var(--attend-surface);
    color: var(--attend-text-strong);
    font-weight: var(--weight-strong);
    text-decoration: none;
  }

  /* Import preview: per-row errors, changes and values */
  .import-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-wrap: anywhere;
  }

  /* Tables that become one card per row on narrow screens (UI_UX §2:
     "responsive cards when columns cannot fit"). Each cell carries
     data-label; the first cell is the card's title. */
  @media (max-width: 767px) {
    /* Block boxes, so each card's padding sits inside the container's width
       and a long value (an id, a UUID) wraps instead of overflowing. */
    .table--cards,
    .table--cards tbody {
      display: block;
    }

    .table--cards thead {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }

    .table--cards tr {
      display: block;
      padding: var(--space-3) var(--space-4);
      border-top: 1px solid var(--attend-border);
    }

    .table--cards tbody tr:first-child {
      border-top: 0;
    }

    /* A shown caption is a line above the cards, not a narrow column. */
    .table--cards caption:not(.visually-hidden) {
      display: block;
      padding: var(--space-3) var(--space-4) 0;
      text-align: start;
    }

    /* The label keeps its width at the start; the values sit at the end,
       on the next line when they do not fit beside it. */
    .table--cards td {
      display: flex;
      flex-wrap: wrap;
      justify-content: flex-end;
      gap: var(--space-1) var(--space-3);
      min-width: 0;
      overflow-wrap: anywhere;
      padding: var(--space-1) 0;
      border-top: 0;
      text-align: right;
    }

    .table--cards td:first-child {
      display: block;
      text-align: left;
      font-weight: var(--weight-strong);
    }

    .table--cards td[data-label]:not(:first-child)::before {
      content: attr(data-label);
      flex: none;
      margin-inline-end: auto;
      color: var(--attend-text-muted);
      font-size: var(--text-small);
      text-align: left;
    }
  }

  /* ScopedSearch: search box plus scope filters above a list */
  .filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    margin-bottom: var(--space-5);
  }

  .filters .field {
    flex: 1 1 180px;
    min-width: 0;
  }

  .filters .field--search {
    flex: 2 1 240px;
  }

  @media (min-width: 768px) {
    .filters .field:not(.field--search) {
      flex: 0 1 200px;
    }
  }

  /* A data region loaded by htmx: results plus its error states */
  .region {
    min-height: var(--touch);
  }

  /* Checkbox and radio lists (fieldset fields) */
  .field--choices > div {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
  }

  .field--choices label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch);
    font-size: var(--text-support);
  }

  /* Two columns of fields on wide screens */
  .form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-5);
  }

  /* Conflicts, impacts and other short warnings inside a section */
  .notice-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: var(--space-3) 0;
    padding: 0;
    list-style: none;
  }

  .notice-list li {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--text-support);
  }

  /* A single confirmation checkbox with its sentence beside it */
  .field--check label {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-height: var(--touch);
    padding-top: var(--space-2);
  }

  .field--check input {
    flex: none;
    margin-top: 2px;
  }

  /* Numbered instructions (install guidance) */
  .steps {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding-left: var(--space-6);
  }

  /* Official identity: photo beside the record (device approval, manual attendance) */
  .identity {
    display: flex;
    align-items: flex-start;
    gap: var(--space-5);
  }

  .identity__photo {
    flex: none;
    width: 120px;
    height: 150px;
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-panel);
    background: var(--attend-surface-subtle);
    object-fit: cover;
  }

  .identity__photo--none {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--attend-text-secondary);
  }

  @media (max-width: 479px) {
    .identity {
      flex-direction: column;
    }
  }

  /* Row headers (a table whose first cell names the row) */
  .table tbody th[scope="row"] {
    color: var(--attend-text-strong);
    font-size: var(--text-small);
    font-weight: var(--weight-bold);
  }

  @media (max-width: 767px) {
    .table--cards th[scope="row"] {
      display: block;
      padding: var(--space-1) 0;
      border-top: 0;
    }
  }

  /* Settings change review: changed rows stand out by weight and the
     "Changed" status text, never by colour alone */
  .diff-table__changed td {
    font-weight: var(--weight-medium);
  }

  /* Policy preview timeline: one bar per code on a shared time scale. The
     table holds the data; the bars (aria-hidden SVG) repeat it visually. */
  .timeline-table__bar {
    inline-size: 40%;
    min-inline-size: 120px;
  }

  .timeline-bar {
    display: block;
    inline-size: 100%;
    block-size: 12px;
  }

  .timeline-bar__track {
    fill: var(--attend-brand-tint);
  }

  .timeline-bar__shown {
    fill: var(--attend-accent);
  }

  .timeline-bar__grace {
    fill: var(--attend-accent-soft);
    stroke: var(--attend-accent-line);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
  }

  .timeline-scale {
    display: flex;
    justify-content: space-between;
    gap: var(--space-2);
  }

  .timeline-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--attend-text-secondary);
    font-size: var(--text-small);
  }

  .timeline-legend li {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
  }

  .timeline-swatch {
    inline-size: 12px;
    block-size: 12px;
  }

  @media (max-width: 767px) {
    .table--cards td.timeline-table__bar {
      display: block;
      inline-size: auto;
    }
  }

  /* Backup codes and other monospace lists */
  .code-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2) var(--space-4);
    list-style: none;
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-panel);
    background: var(--attend-raised);
    font-family: var(--font-mono);
  }

  .otp-qr {
    inline-size: 200px;
    padding: var(--space-3);
    border-radius: var(--radius-panel);
    /* Always dark-on-light so authenticator apps can read it in dark mode. */
    background: var(--attend-projection-bg);
    color: var(--attend-projection-fg);
  }

  .otp-qr svg {
    inline-size: 100%;
    block-size: auto;
  }

  .secret {
    overflow-wrap: anywhere;
    font-family: var(--font-mono);
    font-size: var(--text-support);
  }

  /* An activation code, shown once to the Super Admin who hands it over */
  .activation-code {
    font-size: var(--text-body);
    font-weight: var(--weight-medium);
    letter-spacing: 0.04em;
    color: var(--attend-text-strong);
  }

  /* New attendance round: the derived timing shown before submitting */
  .round-preview {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--attend-edge);
    border-left: 2px solid var(--attend-accent-line);
    border-radius: var(--radius-control);
    font-size: var(--text-support);
  }

  .round-preview__line {
    color: var(--attend-text-strong);
  }

  .field--group {
    margin: 0;
    padding: 0;
    border: 0;
  }

  .field--group[hidden] {
    display: none;
  }

  .round-setup__policy {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  /* ScannerView, ZoomControl, VerifiedProgress, ReceiptView (UI_UX §3 Scanner) */
  .scanner {
    /* The digital zoom factor; static/js/scanner/index.js sets it on the video. */
    --scanner-zoom: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .scanner__title {
    font-size: var(--text-section);
    font-weight: var(--weight-semi);
    color: var(--attend-text-strong);
  }

  .scanner__body {
    max-width: 60ch;
    margin-top: var(--space-1);
    color: var(--attend-text-secondary);
  }

  .scanner__body[hidden] {
    display: none;
  }

  .scanner .banner {
    margin-bottom: 0;
  }

  .scanner__camera {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  .scanner__camera[hidden] {
    display: none;
  }

  .scanner__viewport {
    position: relative;
    overflow: hidden;
    width: 100%;
    aspect-ratio: 3 / 4;
    max-height: 52vh;
    max-height: 52dvh;
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-panel);
    background: var(--attend-surface-subtle);
  }

  @media (min-width: 600px) {
    .scanner__viewport {
      aspect-ratio: 4 / 3;
    }
  }

  /* Digital zoom scales the preview by exactly the factor the decoder crops by
     (static/js/scanner/zoom.js); hardware zoom leaves it at 1. */
  .scanner__video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: scale(var(--scanner-zoom));
    transform-origin: center;
  }

  .scanner__frame {
    position: absolute;
    inset: 18%;
    border: 2px solid var(--attend-accent-line);
    border-radius: var(--radius-panel);
    pointer-events: none;
  }

  .zoom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
  }

  .zoom__label {
    font-size: var(--text-support);
    font-weight: var(--weight-medium);
    color: var(--attend-text-strong);
  }

  .zoom__value {
    min-width: 4ch;
    text-align: center;
    font-weight: var(--weight-semi);
  }

  .zoom__note {
    flex-basis: 100%;
    font-size: var(--text-small);
    color: var(--attend-text-secondary);
  }

  .zoom__note[hidden] {
    display: none;
  }

  .verified-progress {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .verified-progress[hidden] {
    display: none;
  }

  .verified-progress__text {
    font-size: var(--text-body);
    font-weight: var(--weight-semi);
    color: var(--attend-text-strong);
    font-variant-numeric: tabular-nums;
  }

  .verified-progress__bar {
    width: 100%;
    height: 8px;
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-control);
    background: var(--attend-surface-subtle);
    appearance: none;
  }

  .verified-progress__bar::-webkit-progress-bar {
    border-radius: var(--radius-control);
    background: var(--attend-brand-tint);
  }

  .verified-progress__bar::-webkit-progress-value {
    border-radius: var(--radius-control);
    background: var(--attend-accent);
  }

  .verified-progress__bar::-moz-progress-bar {
    border-radius: var(--radius-control);
    background: var(--attend-accent);
  }

  .scanner__guidance {
    font-size: var(--text-body);
    color: var(--attend-text-strong);
  }

  .scanner__guidance[hidden],
  .scanner__notice[hidden],
  .scanner__head[hidden],
  .scanner__actions[hidden] {
    display: none;
  }

  .scanner__notice {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-support);
    color: var(--attend-warning);
  }

  .receipt {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-5);
    border: 1px solid var(--attend-edge);
    border-left: 2px solid var(--attend-positive);
    border-radius: var(--radius-panel);
    background: var(--attend-raised);
  }

  .receipt[hidden] {
    display: none;
  }

  .receipt:focus-visible {
    outline: 2px solid var(--attend-accent-line);
    outline-offset: 2px;
  }

  .receipt__title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-section);
    font-weight: var(--weight-semi);
    color: var(--attend-text-strong);
  }

  .receipt__title .icon {
    color: var(--attend-positive);
  }

  /* Round history on the student's attendance page (UI_UX §3 History) */
  .round-history {
    margin-top: var(--space-2);
  }

  .round-history summary {
    min-height: var(--touch);
    display: flex;
    align-items: center;
    cursor: pointer;
    font-size: var(--text-support);
    color: var(--attend-text-secondary);
  }

  .round-history__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-2) 0 var(--space-1);
  }

  .round-history__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-3);
    font-size: var(--text-support);
  }

  /* A list row whose details sit below its headline (student history) */
  .row--stacked {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
  }

  .row__main {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
  }

  .row__status {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-1);
  }

  /* The student's requests about one lecture record */
  .request-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: 0;
    list-style: none;
  }

  .request-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-3);
    min-height: var(--touch);
    font-size: var(--text-support);
  }

  /* Evidence files: name, type, size, actions; image previews for reviewers */
  .evidence-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: 0;
    list-style: none;
  }

  .evidence-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-panel);
  }

  .evidence-list__name {
    overflow-wrap: anywhere;
    font-weight: var(--weight-medium);
  }

  .evidence-preview {
    flex-basis: 100%;
  }

  .evidence-preview img {
    display: block;
    max-width: 100%;
    max-height: 480px;
    height: auto;
    border: 1px solid var(--attend-edge);
    border-radius: var(--radius-control);
  }

  /* AuditTimeline (UI_UX §7): who did what, when and why, oldest first */
  .audit-timeline {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: 0 0 0 var(--space-4);
    list-style: none;
    border-left: 2px solid var(--attend-edge);
  }

  .audit-timeline__item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: var(--text-support);
  }

  .audit-timeline__what {
    font-weight: var(--weight-medium);
    color: var(--attend-text-strong);
  }

  .audit-timeline__meta {
    font-size: var(--text-small);
    color: var(--attend-text-secondary);
  }

  /* Audit screen: an event's recorded before/after/context, on request */
  .audit-details {
    margin-top: var(--space-1);
    font-size: var(--text-small);
  }

  .audit-details summary {
    cursor: pointer;
    color: var(--attend-text-secondary);
  }

  .audit-details pre {
    max-width: 100%;
    margin: 0 0 var(--space-2);
    padding: var(--space-2);
    overflow-x: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background: var(--attend-surface-subtle);
    border-radius: var(--radius-control);
  }

  /* AttendanceTable (reports): counts right-aligned, the row's explanation
     under its title, "Not applicable" in words (never 0%) */
  .attendance-table .num {
    text-align: right;
    white-space: nowrap;
  }

  .attendance-table__title {
    display: block;
    font-weight: var(--weight-medium);
    color: var(--attend-text-strong);
  }

  .attendance-table__why {
    display: block;
    max-width: 48ch;
    overflow-wrap: anywhere;
  }

  .na {
    color: var(--attend-text-secondary);
    font-style: italic;
  }

  @media (max-width: 767px) {
    .attendance-table .num {
      white-space: normal;
    }
  }

  /* A wide report table scrolls in its frame rather than squeezing a column
     to a word per line: the headers may wrap, the name column and the chips
     keep their width. */
  @media (min-width: 768px) {
    .attendance-table thead th {
      white-space: normal;
    }

    /* .attendance-table .num keeps counts on one line; their headers wrap. */
    .attendance-table thead th.num {
      white-space: normal;
    }

    .attendance-table td:first-child {
      min-width: 24ch;
    }

    .attendance-table .status,
    .attendance-table .status > span {
      white-space: nowrap;
    }
  }

  /* A report export's status (polled while it is being prepared) */
  .export-job {
    margin-top: var(--space-3);
  }

  .export-job__line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-1);
  }
}
