/* ==========================================================================
   Dooling Machine — Employee Portal
   One stylesheet, both pages. No external font, no CDN, no build step.

   Palette is locked and every value below was verified against computed
   contrast, not eyeballed. Two deviations from the token usage table are
   marked "Fix B" and "Fix C" where they appear, with the measured reason.
   ========================================================================== */

:root {
  --bg: #0e1116;
  --surface: #14181f;
  --surface-raised: #1b2029;
  --border: #2a3038;
  --border-interactive: #6b7480;
  --text: #e8eaed;
  --text-muted: #a8adb4;
  --text-dim: #8b9099;
  --accent: #ff6161;
  --brand-red: #fd0001;
  --btn-red: #b00d14;

  /* Phase 3 machine status and conflict marks. An approved exception to the locked palette
     (README, "deviations"): the palette has no green and open/in-use must read as green/red.
     Dots, badges and text only, never a field. Computed on --surface: 8.03, 6.05, 9.14. */
  --status-open: #4cc38a;
  /* Phase 5, part 9: the ground under a completed step's block on the Schedule (Owen asked for a
     green bubble). Dark enough to stay in the dark-first palette and to carry the text above AA:
     --text on it is 13.2, --text-muted 7.0, --status-open 6.5. */
  --done-fill: #14261c;
  --status-busy: #ff6161;
  --status-warn: #e3b341;

  --radius: 10px;
  --radius-sm: 6px;
  --tap: 44px; /* minimum touch target — gloves, not fingertips */
  --maxw: 1200px;

  /* Native controls (date and time pickers, scrollbars, <select> menus) render dark to match. */
  color-scheme: dark;
}

/* --------------------------------------------------------------- base */

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

/* Several containers below set display, which would beat the UA rule for the
   hidden attribute. The filter, the empty state and filtered-out cards all
   rely on it, so it wins here. */
[hidden] {
  display: none !important;
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  /* System stack only. Nothing loads from the network. */
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    sans-serif;
  font-size: 1rem; /* 16px floor for body text */
  line-height: 1.5;
}

/* Both pages carry this. The login page centres inside it; the portal turns it
   into a column so the footer sits at the bottom of a short page. */
.page {
  min-height: 100vh;
  min-height: 100dvh;
}

h1,
h2 {
  line-height: 1.3;
}

/* The focus ring is --accent: 6.42:1 on --bg, 6.05:1 on --surface, 5.55:1 on
   --surface-raised. --border-interactive stays for resting control outlines. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --------------------------------------------------------------- utilities */

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

.skip {
  position: absolute;
  top: -120px;
  left: 16px;
  z-index: 10;
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  padding: 10px 16px;
  background: var(--surface-raised);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-weight: 600;
}

.skip:focus {
  top: 16px;
}

/* A standing notice, never a toast: it stays for as long as the condition it describes. */
.banner {
  margin: 0 0 2px;
  padding: 4px 12px;
  border-left: 3px solid var(--accent);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1.5;
}

.banner strong {
  color: var(--text);
  font-weight: 600;
}

/* --------------------------------------------------------------- brand */

/* The supplied logo is a 156x140 raster with a fully opaque white ground, so it
   cannot sit on a dark surface without becoming a white block. Until a vector
   or transparent light-on-dark asset arrives, the header is type plus one
   accent bar: brand red as a non-text fill, nothing wider. */
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand__stripe {
  flex: none;
  width: 4px;
  height: 1.5em;
  background: var(--brand-red);
  border-radius: 2px;
}

.brand__wordmark {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}

.brand--stacked {
  justify-content: center;
  margin-bottom: 24px;
}

/* --------------------------------------------------------------- surfaces */

.card {
  background: var(--surface);
  /* Fix C: --border measures 1.34:1 on --surface, and --surface itself is only
     1.06:1 against --bg, so a card would have no findable edge on an older
     panel in a bright bay. Cards are real links, so their boundary is an
     interactive one. --border-interactive is 3.76:1. --border is kept below for
     dividers, where nothing is a target. */
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius);
}

/* --------------------------------------------------------------- login page */

.page--login {
  display: grid;
  place-items: center;
  padding: 24px 16px;
}

.login {
  width: 100%;
  max-width: 420px;
}

.login__card {
  padding: 28px 24px;
}

.login__title {
  margin: 0 0 6px;
  font-size: 1.25rem;
  font-weight: 600;
  text-align: center;
}

.login__lead {
  margin: 0 0 20px;
  font-size: 1rem;
  color: var(--text-muted);
  text-align: center;
}

/* The sign-in block: one status line and one button. No form, no fields — the password is
   typed on Microsoft's page, never here. */
.signin {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.hint {
  margin: 20px 0 0;
  font-size: 1rem;
  color: var(--text-muted);
  text-align: center;
}

.hint a {
  color: var(--accent);
}

.legal {
  margin: 20px 0 0;
  font-size: 0.875rem;
  color: var(--text-dim);
  text-align: center;
}

/* --------------------------------------------------------------- forms */

.form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field__label {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-muted);
}

.input {
  width: 100%;
  min-height: var(--tap);
  padding: 10px 12px;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 1rem;
}

.input::placeholder {
  color: var(--text-dim);
}

.input:hover {
  border-color: var(--text-dim);
}

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

.status {
  /* Height is reserved so an error appearing never shifts the form under a
     cursor or a gloved finger. */
  min-height: 1.5em;
  margin: 0;
  font-size: 1rem;
  color: var(--text-muted);
}

.status--error {
  color: var(--accent);
  font-weight: 600;
}

/* Validation copy sits under the field it belongs to, linked by aria-describedby. */
.field__error {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--accent);
}

.field__optional {
  font-weight: 400;
  color: var(--text-dim);
}

select.input {
  padding-right: 32px;
}

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}

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

/* A brand mark inside a button: Microsoft's four squares on the sign-in button. Small dose
   of colour in an icon, which the palette rules allow; never a surface. */
.btn__icon {
  flex: none;
  display: inline-flex;
}

.btn__icon svg {
  display: block;
}

.btn--primary {
  /* White on --brand-red #FD0001 is 4.06:1 and fails AA, so the fill is
     --btn-red with a white label at 7.21:1 — the trap CLAUDE.md flags.
     Fix B: --btn-red is only 2.62:1 against --bg, so the button's own boundary
     would miss the 3:1 non-text minimum even though its label passes. The 1px
     --border-interactive edge measures 3.99:1 on --bg and costs nothing. */
  background: var(--btn-red);
  color: #ffffff;
  border: 1px solid var(--border-interactive);
}

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

.btn--primary:disabled {
  /* No opacity dimming anywhere in this file: it would drag label contrast down
     with it. The disabled state swaps tokens instead. */
  background: var(--surface-raised);
  color: var(--text-dim);
  border-color: var(--border-interactive);
  cursor: default;
}

.btn--quiet {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-interactive);
}

.btn--quiet:hover {
  background: var(--surface-raised);
}

/* --------------------------------------------------------------- top bar */

.page--portal {
  display: flex;
  flex-direction: column;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
}

.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 12px 16px;
}

.topbar__lead {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Page actions live in the bar, between the brand and the session, so a page's own controls
   never push its content down. Empty on the portal. */
.topbar__actions {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}

.topbar__actions:empty {
  display: none;
}

.topbar__session {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.topbar__user {
  font-size: 1rem;
  color: var(--text-muted);
}

.topbar__user strong {
  color: var(--text);
  font-weight: 600;
}

/* --------------------------------------------------------------- menu */

.menu-wrap {
  position: relative;
  flex: none;
}

.menu-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.menu-button:hover {
  background: var(--surface);
}

.menu-button__icon {
  display: flex;
}

.menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 10;
  min-width: 280px;
  /* Never wider than a 360px viewport, and it scrolls rather than running off the bottom
     of a 768px-tall screen once there are a dozen destinations. The 6px list padding
     below leaves room for the 2px focus ring plus its 2px offset, which this scroll
     container would otherwise clip. */
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius);
}

.menu__list {
  margin: 0;
  padding: 6px;
  list-style: none;
}

.menu__item--divided {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.menu__link {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: var(--tap);
  padding: 8px 12px;
  background: transparent;
  color: var(--text);
  border: 0;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 1rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu__link:hover {
  background: var(--surface-raised);
}

.menu__icon {
  flex: none;
  display: flex;
  color: var(--text-muted);
}

.menu__link:hover .menu__icon,
.menu__link:focus-visible .menu__icon {
  color: var(--accent);
}

.menu__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.menu__state {
  font-size: 0.875rem;
  color: var(--text-dim);
}

/* Same honesty as the cards, and dimmed by swapping tokens rather than with opacity so
   the label stays above AA. */
.menu__link--unconfigured {
  color: var(--text-muted);
  cursor: default;
}

.menu__link--unconfigured:hover {
  background: transparent;
}

.menu__link--unconfigured .menu__icon,
.menu__link--unconfigured:hover .menu__icon,
.menu__link--unconfigured:focus-visible .menu__icon {
  color: var(--text-dim);
}

/* --------------------------------------------------------------- portal */

.portal {
  flex: 1 0 auto;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 24px 16px 40px;
}

.portal__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 16px;
}

.portal__title {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 600;
}

.filter {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 240px;
}

.empty {
  margin: 24px 0 0;
  font-size: 1rem;
  color: var(--text-muted);
}

/* --------------------------------------------------------------- grid */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.grid__item {
  display: flex;
}

.card--dest {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  width: 100%;
  min-height: 96px;
  padding: 18px;
  color: var(--text);
  text-decoration: none;
  /* Elevation is surface lightness and a 1px border. No coloured glow, no
     heavy drop shadow. */
  transition: background-color 120ms ease, border-color 120ms ease;
}

.card--dest:hover {
  background: var(--surface-raised);
  border-color: var(--text-dim);
}

.card__icon {
  flex: none;
  display: flex;
  /* Neutral at rest, red on the active state. Six red icons at once would push
     the grid past the colour budget. */
  color: var(--text-muted);
  transition: color 120ms ease;
}

.card--dest:hover .card__icon,
.card--dest:focus-visible .card__icon {
  color: var(--accent);
}

.card__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.card__label {
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--text);
}

.card__desc {
  font-size: 1rem;
  color: var(--text-muted);
}

.card__note {
  /* A revision or safety caveat is an alert, which is one of the three places
     red is allowed. Two pixels of it, not a field. */
  margin-top: 4px;
  padding-left: 10px;
  border-left: 2px solid var(--accent);
  font-size: 0.875rem;
  color: var(--text-muted);
}

.card__badge {
  align-self: flex-start;
  margin: 2px 0;
  padding: 2px 8px;
  border: 1px solid var(--border-interactive);
  border-radius: 999px;
  font-size: 0.875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* A card that looks live but goes nowhere is worse than one that admits it is
   not wired up. Dimming happens by swapping tokens, never with opacity — every
   value here still clears AA on --bg (--text-muted 8.38:1, --text-dim 5.90:1). */
.card--unconfigured {
  background: var(--bg);
  border-style: dashed;
  cursor: default;
}

.card--unconfigured:hover {
  background: var(--bg);
  border-color: var(--border-interactive);
}

.card--unconfigured .card__label {
  color: var(--text-muted);
}

.card--unconfigured .card__desc,
.card--unconfigured .card__icon,
.card--unconfigured:hover .card__icon,
.card--unconfigured:focus-visible .card__icon {
  color: var(--text-dim);
}

/* --------------------------------------------------------------- dialog */

.dialog {
  width: calc(100% - 32px);
  max-width: 26rem;
  padding: 24px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius);
}

.dialog::backdrop {
  background: rgba(0, 0, 0, 0.72);
}

.dialog__title {
  margin: 0 0 10px;
  font-size: 1.25rem;
  font-weight: 600;
}

.dialog__body {
  margin: 0 0 20px;
  font-size: 1rem;
  color: var(--text-muted);
}

.dialog__body strong {
  color: var(--text);
  font-weight: 600;
}

.dialog__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.dialog__actions .btn {
  flex: 1 1 auto;
}

/* --------------------------------------------------------------- footer */

.footer {
  padding: 20px 16px;
  border-top: 1px solid var(--border);
}

.footer p {
  max-width: var(--maxw);
  margin: 0 auto;
  font-size: 0.875rem;
  color: var(--text-dim);
  text-align: center;
}

/* --------------------------------------------------------------- narrow */

@media (max-width: 560px) {
  .topbar__inner {
    align-items: flex-start;
  }

  .topbar__session {
    width: 100%;
    justify-content: space-between;
  }

  .portal__head {
    align-items: stretch;
  }

  .filter {
    width: 100%;
    min-width: 0;
  }

  .grid {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
