/* The building blocks. Rendered from apps/saas/src/design/components.ts.
   No colour value appears in this file: every rule names a token instead.
   Served at /design/components.css. */

/* The page itself
   The reset, the body, and the focus ring. The focus ring is `--ink` rather than the accent so that it cannot move when the accent does, and it is drawn with `:focus-visible`, so a mouse click does not leave a ring behind. */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  margin: 0;
  line-height: var(--leading-tight);
  font-weight: var(--weight-strong);
}
h1 { font-size: var(--text-2xl); letter-spacing: -0.02em; }
h2 { font-size: var(--text-xl); letter-spacing: -0.01em; }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-base); }

p { margin: 0 0 var(--space-4); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
a:hover { color: var(--accent-strong); }

:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

::selection { background: var(--accent-tint); }

.skip-link {
  position: absolute;
  left: var(--space-3);
  top: calc(-1 * var(--space-8));
  z-index: 10;
  padding: var(--space-2) var(--space-3);
  background: var(--card);
  border: var(--border);
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
}
.skip-link:focus { top: var(--space-3); }

.muted { color: var(--ink-muted); }
.mono {
  font-family: var(--font-mono);
  font-size: 0.9em;
  font-variant-numeric: tabular-nums;
}
.code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  padding: 0.1em 0.35em;
  background: var(--surface);
  border: var(--border);
  border-radius: var(--radius-sm);
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The skill's accessibility line asks for reduced-motion, and it is right to:
   a hover transition is small, but somebody who has asked the system to stop
   moving things has asked for all of them. The durations are shortened to
   nothing rather than the rules being deleted, so a transition added later
   inherits this rather than escaping it. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* Wordmark
   A placeholder until the name is settled, so it is type and nothing else — no picture to redraw when it changes. Tightened letter spacing, and it is the only thing in the app that is pure ink rather than a token shade of it. */

.wordmark {
  font-size: var(--text-lg);
  font-weight: var(--weight-strong);
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
}
.wordmark__dot { color: var(--accent); }

/* Buttons
   Three weights of button and one disabled state, all the same height so a row of them lines up. Primary is the accent; secondary is a bordered white button for the second-commonest action on a screen; quiet is a link that needed a hit area. The disabled state dims rather than hides — a person has to be able to see that the action exists and is not available yet. (WCAG exempts inactive controls from the contrast minimum; that is why dimming is allowed here and nowhere else.) */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  transition: background-color 200ms ease, border-color 200ms ease, color 200ms ease;
}

.btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}
.btn--primary:hover { background: var(--accent-strong); }

.btn--secondary {
  background: var(--card);
  border-color: var(--line-strong);
  color: var(--ink);
}
.btn--secondary:hover { background: var(--surface); }

.btn--quiet {
  background: transparent;
  color: var(--accent);
  padding: 0 var(--space-2);
}
.btn--quiet:hover { background: var(--accent-tint); }

.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink-muted);
  cursor: not-allowed;
  opacity: 0.6;
}

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

/* Fields
   A label above the box, a hint under it when there is one, and one message slot that is either a hint or an error, never both. The label is a real `<label>` tied to the input, so clicking the words puts the cursor in the box. The error state changes the border and adds the message *and* sets `aria-invalid`, so the state is not carried by colour alone. */

.field { margin-bottom: var(--space-4); }
.field__label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ink);
}
.field__hint {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--ink-muted);
}
.field__error {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--status-broken);
}

.input,
.select,
.textarea {
  display: block;
  width: 100%;
  min-height: var(--control-height);
  padding: var(--space-2) var(--space-3);
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
}
.textarea { min-height: calc(var(--control-height) * 3); resize: vertical; }
.input::placeholder,
.textarea::placeholder { color: var(--ink-muted); }
.input:disabled,
.select:disabled,
.textarea:disabled { background: var(--surface); color: var(--ink-muted); cursor: not-allowed; }

.input[aria-invalid="true"] { border-color: var(--status-broken); }

/* Cards
   A hairline and a heading, nothing else — no shadow, no second shade of white. The header and footer are separated from the body by the same hairline, so a card is one shape with three parts rather than three boxes. */

.card {
  background: var(--card);
  border: var(--border);
  border-radius: var(--radius-lg);
}
.card__header,
.card__footer { padding: var(--space-4) var(--space-5); }
.card__header { border-bottom: var(--border); }
.card__footer { border-top: var(--border); }
.card__body { padding: var(--space-5); }
.card__title { font-size: var(--text-base); font-weight: var(--weight-strong); }
.card__meta {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

/* Tables
   For lists of records. The header is the one place the panel shade is used, numbers are right-aligned and set in tabular figures so they line up down the column, and the whole thing scrolls sideways rather than squeezing when the screen is narrow. A table is never a way to lay out a page. */

.table-wrap { overflow-x: auto; border: var(--border); border-radius: var(--radius-lg); }
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.table caption {
  caption-side: top;
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.table th {
  background: var(--surface);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
  white-space: nowrap;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table__number { text-align: right; font-variant-numeric: tabular-nums; }
.table__primary { font-weight: var(--weight-medium); color: var(--ink); }

/* Status badges
   The four states of a connection, and the word is always there beside the colour — never a dot on its own, and never colour as the only carrier of meaning. The dot is the same token as the text, so a badge cannot end up with a green word and a red dot. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.15rem var(--space-2) 0.15rem var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.6;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-pill);
  flex: none;
}

.badge--working { background: var(--status-working-tint); border-color: var(--status-working-line); color: var(--status-working); }
.badge--working .badge__dot { background: var(--status-working); }
.badge--warning { background: var(--status-warning-tint); border-color: var(--status-warning-line); color: var(--status-warning); }
.badge--warning .badge__dot { background: var(--status-warning); }
.badge--broken { background: var(--status-broken-tint); border-color: var(--status-broken-line); color: var(--status-broken); }
.badge--broken .badge__dot { background: var(--status-broken); }
.badge--undetermined { background: var(--status-undetermined-tint); border-color: var(--status-undetermined-line); color: var(--status-undetermined); }
.badge--undetermined .badge__dot { background: var(--status-undetermined); }

/* A dot on its own, for a table cell that already has a column heading. */
.dot {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-pill);
}
.dot--working { background: var(--status-working); }
.dot--warning { background: var(--status-warning); }
.dot--broken { background: var(--status-broken); }
.dot--undetermined { background: var(--status-undetermined); }

/* Empty states
   What a screen shows before there is anything to show. It says what would be here, and it offers the one action that fills it — a screen with nothing on it and no way forward reads as broken. */

.empty {
  padding: var(--space-7) var(--space-5);
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
}
.empty__title { font-size: var(--text-base); font-weight: var(--weight-strong); }
.empty__body {
  max-width: 32rem;
  margin: var(--space-2) auto var(--space-5);
  color: var(--ink-muted);
  font-size: var(--text-sm);
}
.empty__action { display: inline-flex; }

/* The app frame
   Sidebar, top bar, content. The sidebar is fixed width and the content takes what is left, up to a maximum — past that the eye has to travel too far from the end of one line to the start of the next. On a narrow screen the sidebar becomes a row above the content rather than a drawer, because nothing here needs a script to be usable. */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}
.app__sidebar {
  background: var(--card);
  border-right: var(--border);
  padding: var(--space-5) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.app__nav { display: flex; flex-direction: column; gap: var(--space-1); }
.app__nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  text-decoration: none;
}
.app__nav-link:hover { background: var(--surface); color: var(--ink); }
.app__nav-link--active { background: var(--accent-tint); color: var(--accent); font-weight: var(--weight-medium); }
.app__nav-group {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 0 var(--space-3);
}
.app__main { display: flex; flex-direction: column; min-width: 0; }
.app__topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--topbar-height);
  padding: 0 var(--space-5);
  background: var(--card);
  border-bottom: var(--border);
}
.app__topbar-title { font-size: var(--text-base); font-weight: var(--weight-medium); }
.app__user {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
.app__avatar {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: var(--border);
  display: grid;
  place-items: center;
  font-size: var(--text-xs);
  color: var(--ink);
}
.app__content {
  flex: 1;
  width: 100%;
  max-width: var(--content-max);
  padding: var(--space-6) var(--space-5) var(--space-8);
}
.app__page-title { margin-bottom: var(--space-2); }
.app__page-note { color: var(--ink-muted); margin-bottom: var(--space-6); }

@media (max-width: 48rem) {
  .app { grid-template-columns: 1fr; }
  .app__sidebar { border-right: 0; border-bottom: var(--border); }
  .app__nav { flex-direction: row; flex-wrap: wrap; }
}

/* The sign-in card
   The app's one screen that is a single small card in the middle of the page rather than the frame. The card is 30rem wide — kept from S06's own stylesheet, where it was chosen by eye, and the one measurement on it that is not a token. The failure notice uses the broken status colour and its tint, a pair the token file already measures. */

.auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6) var(--space-5);
  background: var(--surface);
}
.auth__card {
  width: 100%;
  max-width: 30rem;
  padding: var(--space-6);
}
.auth__card h1 { font-size: var(--text-xl); margin-bottom: var(--space-5); }
.auth__brand { margin: 0 0 var(--space-6); font-size: var(--text-lg); }
.auth__form { display: grid; gap: var(--space-4); }
.auth__message {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--status-broken-tint);
  border: 1px solid var(--status-broken-line);
  border-radius: var(--radius-md);
  color: var(--status-broken);
  font-size: var(--text-sm);
}

/* Facts
   A short list of labelled values — who you are, which company, when the system was last checked. The label sits above its value and not beside it: a label beside a value has to be read in two directions, and a person scanning the screen reads it in one. */

.facts {
  margin: 0 0 var(--space-5);
  display: grid;
  gap: var(--space-4);
}
.facts dt {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.facts dd { margin: var(--space-1) 0 0; }
