/*
  Application shell.

  Every value here resolves to a token from tokens.css, which is transcribed from
  design-system/MASTER.md. There are no literals in this file by design.

  The visual language is hairlines and density: 1px borders do the structural work that
  shadows would do elsewhere, and a teacher scanning thirty students should see all thirty
  where the viewport allows. Whitespace is spent on document surfaces, not working ones.
*/

*,
*::before,
*::after {
  box-sizing: border-box;
}

/*
  The hidden attribute has to win.

  A class that sets `display` beats the user-agent rule behind `hidden`, so any element carrying
  both renders anyway — which showed up as an empty error box with an icon and no words on the
  guardian page, the exact opposite of what the four-states rule asks for. Declared once here
  rather than defended at every call site.
*/
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  margin: 0;
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
  line-height: 1.25;
}

h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }

a {
  color: var(--accent-text);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/*
  Focus. A visible focus ring on every interactive element is a WCAG 2.1 AA obligation,
  not a preference — :focus-visible so it appears for keyboard users without following
  every mouse click.
*/
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius);
}

/*
  A heading that takes focus programmatically — FocusOnNavigate's h1 after every navigation, the
  roster's group heading — is moved to so a screen reader announces it, not so it can be operated.
  WCAG 2.4.7 asks for a visible indicator on operable components; a heading is not one, and the
  ring there reads as a selected control. Headings only: a bare [tabindex="-1"] would also match
  roving-focus items, which are operable and keep the ring above (spec 016 F045).
*/
:is(h1, h2, h3, h4, h5, h6)[tabindex="-1"]:focus {
  outline: none;
}

/* Skip link — the first thing in the tab order, invisible until focused. */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: calc(var(--space-2) * -10);
  z-index: 100;
  padding: var(--space-2) var(--space-3);
  background-color: var(--surface);
  border: var(--border-width) solid var(--accent);
  border-radius: var(--radius);
  color: var(--text);
  transition: top var(--duration) var(--ease);
}

.skip-link:focus {
  top: var(--space-2);
}

/* --- Shell ---------------------------------------------------------------------- */

.shell {
  display: grid;
  grid-template-columns: 224px 1fr;
  min-height: 100vh;
}

.shell__main {
  min-width: 0; /* lets grid children shrink instead of forcing horizontal scroll */
  padding: var(--space-6);
  max-width: var(--width-dashboard);
}

/* --- Navigation rail ------------------------------------------------------------ */

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-3);
  background-color: var(--surface);
  border-right: var(--border-width) solid var(--border);
}

.rail__brand {
  display: flex;
  align-items: center;
  padding: 0 var(--space-2);
  font-size: var(--text-h3);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
  color: var(--text);
}

/*
  The wordmark: "Teach" with the final h in the accent. Interim identity per MASTER.md —
  typographic only, no symbol, because a placeholder mark is harder to kill than no mark.
*/
.rail__brand-accent {
  color: var(--accent-text);
}

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

.rail__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius);
  color: var(--text-muted);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  /* Colour only — never transform. A scale on hover shifts layout and hurts users with
     motor impairments (MASTER.md anti-pattern). */
  transition: background-color var(--duration) var(--ease), color var(--duration) var(--ease);
}

.rail__link:hover {
  background-color: var(--surface-sunken);
  color: var(--text);
  text-decoration: none;
}

.rail__link--active {
  background-color: var(--accent-wash);
  color: var(--accent-text);
}

.rail__link svg {
  flex: none;
  width: 16px;
  height: 16px;
}

.rail__footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--border);
}

/* --- Controls ------------------------------------------------------------------- */

.control {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  background-color: transparent;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background-color var(--duration) var(--ease), color var(--duration) var(--ease),
    border-color var(--duration) var(--ease);
}

.control:hover {
  background-color: var(--surface-sunken);
  color: var(--text);
  border-color: var(--text-muted);
}

.control svg {
  flex: none;
  width: 16px;
  height: 16px;
}

/* --- Status table --------------------------------------------------------------- */
/*
  Dense, hairline-ruled, one row per dependency. This is the first working surface in the
  product and it sets the pattern every later table follows: rules instead of cards, no
  shadow, status carried by icon AND text rather than by colour.
*/

.panel {
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.panel__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
}

.panel__title {
  font-size: var(--text-h3);
}

.panel__meta {
  font-family: var(--font-data);
  font-size: var(--text-caption);
  color: var(--text-muted);
}

.status-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
}

.status-row:last-child {
  border-bottom: none;
}

.status-row__name {
  font-family: var(--font-data);
  font-size: var(--text-small);
  color: var(--text);
}

.status-row__duration {
  font-family: var(--font-data);
  font-size: var(--text-caption);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/*
  Status chips. Colour is never the only signal — every chip carries an icon and a word,
  per WCAG 1.4.1 and MASTER.md.
*/
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-pill);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.chip svg {
  width: 12px;
  height: 12px;
}

.chip--healthy { color: var(--success); background-color: var(--surface-sunken); }
.chip--unhealthy { color: var(--error); background-color: var(--surface-sunken); }
.chip--unknown { color: var(--warning); background-color: var(--surface-sunken); }
.chip--neutral { color: var(--text-muted); background-color: var(--surface-sunken); }

/* --- The four (five) observable states ------------------------------------------ */

.state {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-small);
}

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

/* An error state must name what failed. A generic message here is the bug this whole
   project keeps guarding against. */
.state--failed .state__title { color: var(--error); }
.state--indeterminate .state__title { color: var(--warning); }

.state__detail {
  max-width: var(--measure-document);
  margin: 0 auto;
  /* Long dependency messages must wrap at 375px, not push the page sideways. */
  overflow-wrap: anywhere;
}

/* Loading: a hairline shimmer rather than a spinner. It reads as "content arriving"
   instead of "something is stuck", and it resolves — never indefinite. */
.skeleton {
  height: 14px;
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--surface-sunken), var(--border), var(--surface-sunken));
  background-size: 200% 100%;
  animation: skeleton-sweep 1.2s var(--ease) infinite;
}

@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
    background: var(--surface-sunken);
  }
}

/* --- Page furniture -------------------------------------------------------------- */

.page__header {
  margin-bottom: var(--space-5);
}

.page__lede {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
  max-width: var(--measure-document);
}

/* --- Empty state ----------------------------------------------------------------- */
/*
  A genuine state, not an error: one hairline panel, the only colour the icon. It says what will
  appear here and what to do if something is missing — directions, not mood.
*/

.empty-state {
  max-width: var(--measure-document);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}

.empty-state__icon {
  display: block;
  width: 28px;
  height: 28px;
  color: var(--accent-text);
}

.empty-state__icon svg {
  width: 100%;
  height: 100%;
}

.empty-state__title {
  margin: var(--space-4) 0 0;
}

.empty-state__body {
  margin: var(--space-2) 0 0;
}

.empty-state__help {
  margin: var(--space-3) 0 0;
  color: var(--text-muted);
}

/* --- Responsive ------------------------------------------------------------------ */
/*
  Below 768px the rail becomes a bottom bar. Breakpoints are 375 / 768 / 1024 / 1440 and
  no width may introduce horizontal scroll.
*/
@media (max-width: 767px) {
  .shell {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;
  }

  .shell__main {
    padding: var(--space-4);
    /* Room for the fixed bottom bar so the last row is never trapped underneath it. */
    padding-bottom: calc(var(--space-7) + var(--space-6));
  }

  .rail {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 10;
    flex-direction: row;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-right: none;
    border-top: var(--border-width) solid var(--border);
  }

  .rail__brand {
    padding: 0;
  }

  .rail__nav {
    flex-direction: row;
    flex: 1;
  }

  .rail__footer {
    flex-direction: row;
    margin-top: 0;
    padding-top: 0;
    border-top: none;
  }

  .control {
    width: auto;
  }

  /*
    Icon-only below 768px. With the account menu (spec 003) the bar held six labelled items in
    705px of content inside a 375px viewport, and "Logga ut" sat entirely off-screen — a guardian
    on a phone could not sign out. The labels stay in the accessibility tree (visually hidden, not
    removed), so every control keeps its name; each item is a 44px target. The wordmark is the one
    thing here with no job to do, so it is the one thing that goes.
  */
  .rail {
    justify-content: space-between;
  }

  .rail__brand {
    display: none;
  }

  .rail__nav,
  .rail__footer {
    flex: 0 1 auto;
    gap: var(--space-1);
  }

  .rail__link,
  .rail__account,
  .rail__footer .control {
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: var(--space-2);
  }

  .rail__link span,
  .rail__account span,
  .rail__footer .control span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .status-row {
    grid-template-columns: 1fr auto;
    row-gap: var(--space-1);
  }

  .status-row__duration {
    grid-column: 1 / -1;
  }
}

/* ============================================================================
   Sign-in surfaces (spec 003)

   A single centred column on --bg, holding one --surface panel with a hairline
   border. No shadow: MASTER.md reserves elevation for modals, popovers and
   dropdowns, and a sign-in panel is none of those — it is the page.

   These are the only pages in the product with no nav rail. Somebody who is not
   signed in has nowhere to navigate to, and offering a rail full of links that
   all bounce back here would be worse than offering none.
   ============================================================================ */

.auth {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--space-5) var(--space-4);
  gap: var(--space-5);
}

.auth__brand {
  font-size: var(--text-h2);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
}

/* The interim wordmark's one accent moment. MASTER.md says not to invent a mark,
   so the accented terminal is the whole of the brand expression here. */
.auth__brand-accent {
  color: var(--accent-text);
}

.auth__panel {
  width: 100%;
  max-width: 420px;
  padding: var(--space-6);
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-card);
}

.auth__lede {
  margin: var(--space-2) 0 var(--space-5);
  color: var(--text-muted);
}

.auth__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.auth__label {
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
}

.auth__input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  background-color: var(--bg);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  transition: border-color var(--duration) var(--ease);
}

.auth__input:hover {
  border-color: var(--text-muted);
}

.auth__button {
  width: 100%;
  padding: var(--space-2) var(--space-4);
  color: var(--surface);
  background-color: var(--accent);
  border: var(--border-width) solid var(--accent);
  border-radius: var(--radius);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background-color var(--duration) var(--ease);
}

.auth__button:hover {
  background-color: var(--accent-text);
  border-color: var(--accent-text);
}

.auth__button--quiet {
  color: var(--text);
  background-color: var(--surface);
  border-color: var(--border);
}

.auth__button--quiet:hover {
  background-color: var(--surface-sunken);
  border-color: var(--text-muted);
}

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

/*
  Every message region below pairs its colour with an icon and with words. Colour is
  never the only signal — MASTER.md, and WCAG 1.4.1.
*/
.auth__message {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-small);
}

.auth__message svg {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}

.auth__message--error {
  color: var(--error);
  border-color: var(--error);
}

.auth__message--busy {
  color: var(--text-muted);
}

.auth__message--empty {
  color: var(--text-muted);
  background-color: var(--surface-sunken);
}

/* One kommun on the chooser. A button rather than a link: it posts a choice. */
.auth__choice {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  color: var(--text);
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-ui);
  font-size: var(--text-body);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--duration) var(--ease), background-color var(--duration) var(--ease);
}

.auth__choice:hover {
  background-color: var(--surface-sunken);
  border-color: var(--accent);
}

.auth__choice svg {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  color: var(--text-muted);
}

.auth__choice-name {
  font-weight: var(--weight-medium);
}

/* Roles read as data, so they are set in the data face. */
.auth__choice-roles {
  display: block;
  margin-top: 2px;
  color: var(--text-muted);
  font-family: var(--font-data);
  font-size: var(--text-caption);
}

.auth__qr {
  display: block;
  width: 200px;
  height: 200px;
  margin: 0 auto var(--space-4);
  padding: var(--space-2);
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
}

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

.auth__hint {
  margin: var(--space-4) 0 0;
  color: var(--text-muted);
  font-size: var(--text-small);
  text-align: center;
}

/*
  The demo panel (spec 019). Below the real form, behind a hairline, so the page still reads as the sign-in page
  it is. The persona buttons are the kommun chooser's buttons: one way to show "pick one of these" in the product.
*/
.auth__demo {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--border);
}

.auth__demo-heading {
  margin: 0;
  font-size: var(--text-h3);
  font-weight: var(--weight-semibold);
}

.auth__demo-lede {
  margin: var(--space-1) 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-small);
}

.auth__demo-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
  The demo banner (spec 019) on every signed-in page. Sunken surface and a hairline like every other structure; the
  icon carries the one colour, and the words carry the meaning. Not the accent wash: that belongs to the approval seam.
*/
.demo-banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  background-color: var(--surface-sunken);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-small);
}

.demo-banner svg {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  color: var(--warning);
}

/*
  The busy icon is the one place these surfaces move. It is information rather than
  decoration — it says the eID order is still open — so it survives reduced motion as a
  static icon, and the sentence beside it carries the meaning either way.
*/
@keyframes auth-spin {
  to { transform: rotate(360deg); }
}

.auth__message--busy svg {
  animation: auth-spin 1s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .auth__message--busy svg {
    animation: none;
  }
}

/*
  Account block in the rail. The kommun name is the load-bearing part — a teacher who works
  in two kommuner has to see which one they are in without opening anything.
*/
.rail__account {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-small);
}

.rail__account svg {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}

.rail__account-kommun {
  overflow: hidden;
  color: var(--text);
  font-weight: var(--weight-medium);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sign-out posts a form, so it is a button that has to read as the rail's other links do. */
.rail__link--button {
  width: 100%;
  background: none;
  border: none;
  /* Inherit, so "Logga ut" is set like every other rail item rather than two sizes larger. */
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/*
  The circuit-failure bar.

  Lives here rather than in MainLayout's scoped stylesheet, because it is a shell-wide element
  and the sign-in surfaces use a different layout — scoped to one layout, it rendered unstyled
  and therefore permanently visible on every sign-in page.

  Retyped from the Blazor template's default while it moved: that shipped `lightyellow`, an
  ad-hoc rgba shadow and rem padding, none of which exist in MASTER.md. It is an error, so it
  reads as one, and it pairs its colour with words.
*/
#blazor-error-ui {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 1000;
  box-sizing: border-box;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  color: var(--error);
  background-color: var(--surface);
  border-top: var(--border-width) solid var(--error);
  font-size: var(--text-small);
}

#blazor-error-ui .reload {
  margin-left: var(--space-2);
}

#blazor-error-ui .dismiss {
  position: absolute;
  top: var(--space-2);
  right: var(--space-3);
  cursor: pointer;
}

/* ============================================================================
   Granskningslogg — the kommun's audit trail (spec 006)

   A working surface, so density over whitespace: one hairline table, grouped by the Stockholm calendar day the
   entries happened on. The day divider is the one structural device here, because a trail is read by date — "what
   happened on the 14th?" — and a date repeated on every row is noise the eye has to skip. Times, detail codes and
   references are data and take the data face; everything a person reads takes the UI face. No approval seam: nothing
   here was drafted by AI.
   ============================================================================ */

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

.audit-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

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

.audit-filters__label {
  font-size: var(--text-small);
  color: var(--text-muted);
}

.audit-filters__input {
  min-height: 36px;
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-small);
  color: var(--text);
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  color-scheme: light;
  transition: border-color var(--duration) var(--ease);
}

/* The native date picker follows the app's theme, not the operating system's. */
:root[data-theme="dark"] .audit-filters__input {
  color-scheme: dark;
}

.audit-filters__input:hover {
  border-color: var(--text-muted);
}

.audit-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  font: inherit;
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  color: var(--text);
  background-color: var(--surface);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}

.audit-button:hover:not(:disabled) {
  background-color: var(--surface-sunken);
}

.audit-button:disabled {
  cursor: default;
  color: var(--text-muted);
}

.audit-button svg {
  width: 14px;
  height: 14px;
}

.audit-button--primary {
  color: var(--surface);
  background-color: var(--accent);
  border-color: var(--accent);
}

.audit-button--primary:hover:not(:disabled) {
  background-color: var(--accent-text);
}

:root[data-theme="dark"] .audit-button--primary {
  color: var(--bg);
}

.audit-filters__input:focus-visible,
.audit-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.audit__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-small);
}

.audit__table thead th {
  padding: var(--space-2) var(--space-3);
  font-weight: var(--weight-medium);
  text-align: left;
  color: var(--text-muted);
  border-bottom: var(--border-width) solid var(--border);
}

.audit__day th {
  padding: var(--space-4) var(--space-3) var(--space-2);
  font-weight: var(--weight-semibold);
  text-align: left;
  color: var(--text);
  background-color: var(--surface-sunken);
  border-bottom: var(--border-width) solid var(--border);
}

.audit__day th::first-letter {
  text-transform: uppercase;
}

.audit__row td {
  padding: var(--space-2) var(--space-3);
  vertical-align: top;
  border-bottom: var(--border-width) solid var(--border);
  overflow-wrap: anywhere;
}

.audit__time {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  white-space: nowrap;
}

.audit__data {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-caption);
  color: var(--text-muted);
}

.audit__justification {
  max-width: 40ch;
  color: var(--text-muted);
}

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

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

.audit__more-error {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-small);
  color: var(--error);
}

.audit__more-error svg {
  width: 14px;
  height: 14px;
}

.audit .state__title svg {
  width: 16px;
  height: 16px;
  vertical-align: -2px;
}

.audit__skeleton {
  padding: var(--space-2) 0;
}

.audit__skeleton-row {
  display: grid;
  grid-template-columns: 1fr 2fr 4fr;
  gap: var(--space-4);
  padding: var(--space-3);
  border-bottom: var(--border-width) solid var(--border);
}

.audit__skeleton-cell--short { width: 60%; }
.audit__skeleton-cell--wide { width: 80%; }

/*
  Below 768px each entry becomes a block with its own labels: six columns at 375px would force the page sideways,
  which no width may do. The day divider stays, so the grouping survives the reflow.
*/
@media (max-width: 767px) {
  .audit__table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .audit__table,
  .audit__table tbody,
  .audit__row,
  .audit__row td,
  .audit__day,
  .audit__day th {
    display: block;
  }

  .audit__row {
    padding: var(--space-2) 0;
    border-bottom: var(--border-width) solid var(--border);
  }

  .audit__row td {
    display: grid;
    grid-template-columns: 9ch 1fr;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    border-bottom: none;
  }

  .audit__row td::before {
    content: attr(data-label);
    color: var(--text-muted);
  }

  /* One grid item per cell, so a secondary data line stays under its value rather than under the label. */
  .audit__cell {
    min-width: 0;
  }

  /* A label with nothing after it is noise when the label is repeated on every entry. */
  .audit__row td:has(> .audit__cell:empty) {
    display: none;
  }

  .audit__justification {
    max-width: none;
  }

  .audit-filters__field {
    flex: 1 1 140px;
  }
}

/* --- Kursplaner (spec 007) --------------------------------------------------------
   A subject rail and a reading surface. Skolverket's text sits at the document measure;
   the chrome stays hairline-quiet so the one strong device — the grade ladder — reads. */

.kursplaner {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.kursplaner__layout {
  display: grid;
  grid-template-columns: minmax(200px, 240px) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}

.kursplaner__subjects {
  position: sticky;
  top: var(--space-4);
  max-height: calc(100vh - var(--space-7));
  overflow-y: auto;
  border-right: var(--border-width) solid var(--border);
  padding-right: var(--space-3);
}

.kursplaner__subjects-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
}

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

.kursplaner__subject {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  border-left: 2px solid transparent;
  color: var(--text);
  text-decoration: none;
  transition: background-color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}

.kursplaner__subject:hover {
  background-color: var(--surface-sunken);
}

.kursplaner__subject--current {
  border-left-color: var(--accent);
  background-color: var(--surface-sunken);
  font-weight: var(--weight-medium);
}

.kursplaner__subject-name {
  overflow-wrap: anywhere;
}

.kursplaner__subject-code,
.kursplaner__data,
.ingest__data {
  font-family: var(--font-data);
  font-size: var(--text-caption);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.kursplaner__subject:focus-visible,
.kursplaner__band:focus-visible,
.kursplaner__link:focus-visible,
.kursplaner__purpose summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.kursplaner__reader {
  max-width: var(--measure-document);
  min-width: 0;
}

.kursplaner__loading,
.kursplaner__reader-loading,
.ingest__loading {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
}

.kursplaner__skeleton--title {
  height: 24px;
  width: 40%;
}

.kursplaner__header {
  padding-bottom: var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
}

.kursplaner__name {
  margin: 0;
  font-size: var(--text-h1);
  font-weight: var(--weight-semibold);
}

.kursplaner__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: var(--space-3) 0 0;
}

.kursplaner__meta dt {
  font-size: var(--text-caption);
  color: var(--text-muted);
}

.kursplaner__meta dd {
  margin: 0;
  font-size: var(--text-small);
}

.kursplaner__note {
  margin: var(--space-3) 0 0;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.kursplaner__purpose {
  margin: var(--space-4) 0;
  border-bottom: var(--border-width) solid var(--border);
  padding-bottom: var(--space-3);
}

.kursplaner__purpose summary {
  cursor: pointer;
  font-weight: var(--weight-medium);
  border-radius: var(--radius);
}

.kursplaner__prose {
  line-height: 1.6;
}

.kursplaner__prose h3,
.kursplaner__prose h4 {
  font-size: var(--text-body);
}

.kursplaner__bands {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0;
}

.kursplaner__bands-label {
  font-size: var(--text-small);
  color: var(--text-muted);
  margin-right: var(--space-1);
}

.kursplaner__band {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 var(--space-3);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-size: var(--text-small);
  text-decoration: none;
  transition: background-color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}

.kursplaner__band:hover {
  background-color: var(--surface-sunken);
}

.kursplaner__band--current {
  border-color: var(--accent);
  color: var(--accent-text);
  font-weight: var(--weight-medium);
}

.kursplaner__section {
  margin-top: var(--space-6);
}

.kursplaner__section-title {
  margin: 0 0 var(--space-3);
  font-size: var(--text-h2);
  font-weight: var(--weight-semibold);
}

.kursplaner__block + .kursplaner__block {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--border);
}

.kursplaner__track,
.ladder__track {
  margin: 0 0 var(--space-2);
  font-size: var(--text-small);
  color: var(--text-muted);
}

.kursplaner__rubrik {
  margin: var(--space-4) 0 var(--space-2);
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
}

.kursplaner__points {
  margin: 0;
  padding-left: var(--space-5);
  line-height: 1.6;
}

.kursplaner__points li + li {
  margin-top: var(--space-1);
}

.kursplaner__none,
.kursplaner__source {
  color: var(--text-muted);
  font-size: var(--text-small);
}

.kursplaner__source {
  margin-top: var(--space-6);
}

.kursplaner__link {
  color: var(--accent-text);
}

/* The grade ladder: E to A as one vertical sequence, the step letter set large in the data
   face on a hairline spine. It is a real sequence — each step builds on the one before — so
   the numbering-like device carries information rather than decoration. The value words
   Skolverket sets in bold are what separate the steps; they take the accent text colour. */

.ladder + .ladder {
  margin-top: var(--space-5);
}

.ladder__title {
  margin: 0 0 var(--space-3);
  font-weight: var(--weight-medium);
}

.ladder__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: var(--border-width) solid var(--border);
  margin-left: 18px;
}

.ladder__step {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  gap: var(--space-3);
  margin-left: -18px;
  padding: var(--space-2) 0;
}

.ladder__grade {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius);
  background-color: var(--surface);
  font-family: var(--font-data);
  font-size: var(--text-h3);
  font-weight: var(--weight-medium);
  color: var(--text);
}

.ladder__grade svg {
  width: 18px;
  height: 18px;
  color: var(--success);
}

.ladder__step--between .ladder__grade {
  width: 28px;
  height: 28px;
  margin-left: 4px;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.ladder__text {
  line-height: 1.6;
  padding-top: 6px;
}

.ladder__text p {
  margin: 0 0 var(--space-2);
}

.ladder__text strong {
  color: var(--accent-text);
  font-weight: var(--weight-semibold);
}

.ladder__step--between .ladder__text {
  color: var(--text-muted);
  padding-top: 3px;
}

/* --- Ingest panel ------------------------------------------------------------------ */

.ingest .panel__header .audit-button {
  flex: none;
  white-space: nowrap;
}

.ingest__lede {
  margin: var(--space-1) 0 0;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.ingest__message {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
  font-size: var(--text-small);
  color: var(--success);
}

.ingest__message--error {
  color: var(--error);
}

.ingest__message svg {
  width: 14px;
  height: 14px;
  flex: none;
}

.ingest__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-small);
}

.ingest__table th,
.ingest__table td {
  padding: var(--space-2) var(--space-4);
  text-align: left;
  border-bottom: var(--border-width) solid var(--border);
}

.ingest__table thead th {
  font-weight: var(--weight-medium);
  color: var(--text-muted);
}

.ingest__table tbody tr:last-child td {
  border-bottom: none;
}

.ingest__number {
  text-align: right;
}

.ingest__table th.ingest__number,
.ingest__table td.ingest__number {
  text-align: right;
}


@media (max-width: 1023px) {
  .kursplaner__layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .kursplaner__subjects {
    position: static;
    max-height: 240px;
    border-right: none;
    border-bottom: var(--border-width) solid var(--border);
    padding: 0 0 var(--space-3);
  }
}

@media (max-width: 767px) {
  .ingest__table thead {
    display: none;
  }

  .ingest__table tr {
    display: block;
    padding: var(--space-2) var(--space-4);
    border-bottom: var(--border-width) solid var(--border);
  }

  .ingest__table td {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-1) 0;
    border: none;
  }

  .ingest__table td::before {
    content: attr(data-label);
    color: var(--text-muted);
  }

  .ingest__table td.ingest__number {
    text-align: left;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kursplaner__subject,
  .kursplaner__band {
    transition: none;
  }
}

/* ── SIS import (spec 008 FR-051) ─────────────────────────────────────────
   The connection form and the run history, in the ingest panel's idiom: hairline structure, the admin's form on the
   dashboard grid, and a run's issues opened beneath its row. */

.sis {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.sis__form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4) var(--space-5);
  padding: var(--space-4);
}

.sis__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.sis__hint,
.sis__empty {
  margin: 0;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.sis__empty {
  padding: var(--space-3) var(--space-4);
}

.sis__check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-small);
  color: var(--text);
}

.sis__check input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sis__actions,
.sis__run-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.sis__actions {
  grid-column: 1 / -1;
}

.sis__remove {
  margin-left: auto;
  color: var(--error);
}

.sis__row-link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent-text);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.sis__row-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius);
}

.sis__row--selected td {
  background-color: var(--accent-wash);
}

.sis__detail {
  border-top: var(--border-width) solid var(--border);
}

.sis__detail-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-4) var(--space-4) var(--space-2);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  color: var(--text);
}

.sis__detail-title svg {
  width: 14px;
  height: 14px;
}

.sis__conflicts {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3) var(--space-6);
}

.sis__audit-link {
  margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-4);
  font-size: var(--text-small);
}

@media (max-width: 767px) {
  .sis__form {
    grid-template-columns: minmax(0, 1fr);
  }

  .sis__remove {
    margin-left: 0;
  }
}

/* ── Klasser (spec 008 FR-052) ────────────────────────────────────────────
   The Kursplaner list/detail layout. The one deliberate device is the masked member: a lock, a hatched name cell
   and a sentence saying why, so a teacher reads skyddad identitet as a rule, never as missing data. */

.kursplaner__meta-line {
  margin: var(--space-1) 0 0;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.roster__members,
.roster__lessons {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3);
  list-style: none;
}

.roster__member,
.roster__lesson {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: var(--border-width) solid var(--border);
  font-size: var(--text-body);
  color: var(--text);
}

.roster__member:last-child,
.roster__lesson:last-child {
  border-bottom: 0;
}

.roster__member--masked {
  padding-left: var(--space-2);
  border-left: 2px solid var(--text-muted);
  background-image: repeating-linear-gradient(135deg, transparent 0 6px, var(--surface-sunken) 6px 12px);
}

.roster__lock {
  display: inline-flex;
  color: var(--text-muted);
}

.roster__lock svg {
  width: 14px;
  height: 14px;
}

.roster__masked-name {
  font-weight: var(--weight-medium);
}

.roster__masked-why {
  flex-basis: 100%;
  font-size: var(--text-small);
  color: var(--text-muted);
}

.roster__lesson--cancelled > span:not(.chip) {
  text-decoration: line-through;
  color: var(--text-muted);
}

/* ── Guardian children (spec 008 FR-053) ── */

.children {
  margin: 0;
  padding: var(--space-1) var(--space-4);
  list-style: none;
}

.children__child {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--border);
}

.children__child:last-child {
  border-bottom: 0;
}

.children__name {
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--text);
}

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

/* The Kursplaner rail is links; the Klasser rail is buttons (they open a group in place), so they shed the UA look. */
.roster .kursplaner__subject {
  width: 100%;
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.roster .kursplaner__subjects-title + .kursplaner__subject-list,
.roster .kursplaner__subject-list + .kursplaner__subjects-title {
  margin-top: var(--space-2);
}

.roster .state__title svg,
.sis .state__title svg,
[data-testid='children-error'] .state__title svg {
  width: 16px;
  height: 16px;
  vertical-align: -2px;
}
