:root {
  color-scheme: light;
  --bg: #f4f5fa;
  --ink: #1e293b;
  --muted: #475569;
  --card: #ffffff;
  --line: #e2e8f0;
  --accent: #4f46e5;
  --accent-2: #4338ca;
  --accent-soft: #eef2ff;
  --ok: #047857;
  --ok-soft: #ecfdf5;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  --bad: #be123c;
  --bad-soft: #fff1f2;
  --info: #475569;
  --info-soft: #f1f5f9;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(15, 23, 42, 0.06),
    0 4px 14px rgba(15, 23, 42, 0.06);
}

* {
  box-sizing: border-box;
}

/* `hidden` is how this app's one script takes something off the screen: a
   filtered row, the rows of another page, the panel of another tab, the submit
   button of a control that now sends itself.  A component that declares its
   own `display` -- `.btn` is `inline-flex` -- would otherwise ignore the
   attribute and stay on screen, so the rule is stated once, here, for all of
   them. */
[hidden] {
  display: none !important;
}

html {
  min-height: 100%;
}

body {
  min-height: 100vh;
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    sans-serif;
}

button,
input,
select,
textarea {
  font: inherit;
}

a {
  color: var(--accent);
}

/* Generated passwords, storage keys and SHA-256 digests have no break
   opportunity of their own, so they must be allowed to wrap anywhere rather
   than widen the page they sit in. */
code {
  overflow-wrap: anywhere;
}

h1,
h2,
h3,
h4,
p {
  margin-top: 0;
}

h1 {
  font-size: 24px;
  line-height: 1.3;
}

h2 {
  margin-bottom: 4px;
  font-size: 22px;
  line-height: 1.35;
}

h3 {
  font-size: 17px;
}

h4 {
  margin-bottom: 6px;
  font-size: 16px;
}

.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 200;
  transform: translateY(-150%);
  border-radius: 8px;
  background: var(--ink);
  color: #fff;
  padding: 8px 12px;
  text-decoration: none;
}

.skip-link:focus {
  transform: none;
}

:focus-visible {
  outline: 3px solid #a5b4fc;
  outline-offset: 2px;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 70;
  display: flex;
  min-height: 62px;
  align-items: center;
  gap: 14px;
  border-bottom: 1px solid var(--line);
  background: #fff;
  padding: 10px 20px;
}

.brand {
  display: inline-flex;
  align-items: center;
  color: var(--ink);
  font-weight: 800;
  letter-spacing: 0.2px;
  text-decoration: none;
  white-space: nowrap;
}

.brand-mark {
  width: 10px;
  height: 10px;
  margin-right: 8px;
  border-radius: 3px;
  background: var(--accent);
}

.topbar-spacer {
  flex: 1;
}

.signed-in {
  display: flex;
  align-items: center;
  gap: 9px;
}

.signed-in-copy {
  display: grid;
  line-height: 1.25;
  text-align: right;
}

.signed-in-copy strong {
  font-size: 13.5px;
}

.signed-in-copy span {
  color: var(--muted);
  font-size: 12px;
}

.avatar {
  display: inline-flex;
  width: 34px;
  height: 34px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-2);
  font-size: 13px;
  font-weight: 800;
}

.burger {
  display: none;
  min-width: 44px;
  min-height: 40px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
  font-size: 19px;
}

.layout {
  display: flex;
  align-items: stretch;
}

/* The sidebar takes the owner's PMS shape (owner, 2026-08-27): the same
   uppercase micro-heading per section, the same icon + label row anatomy, and
   the same quiet type scale.  Only presentation changed -- the links, their
   order and their hrefs are still exactly the approved v14 contract. */
.sidebar {
  position: sticky;
  top: 62px;
  display: flex;
  width: 236px;
  min-height: calc(100vh - 62px);
  /* The owner's menu is the longest one in the product: it reaches every
     section.  The rail is exactly one screen tall and scrolls inside itself,
     so "Sign out" and the last section are reachable on any screen -- on the
     phone especially, where the rail is a fixed off-canvas panel that the page
     behind it cannot scroll. */
  max-height: calc(100vh - 62px);
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: none;
  align-self: flex-start;
  flex-direction: column;
  gap: 0;
  border-right: 1px solid var(--line);
  background: #fff;
  padding: 12px 10px 14px;
}

/* The role chip. Deliberately a filled chip, not a bare micro-label, so it can
   never be read as one of the section headings below it. */
.side-label {
  align-self: flex-start;
  max-width: 100%;
  margin: 2px 2px 6px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-2);
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.side-section {
  margin: 12px 0 4px;
  padding: 0 10px;
  color: #64748b;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.side-group:first-of-type .side-section {
  margin-top: 4px;
}

.side-link {
  display: flex;
  min-height: 40px;
  align-items: center;
  gap: 10px;
  border-radius: 10px;
  color: #334155;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.3;
  margin-bottom: 1px;
  padding: 8px 10px;
  text-decoration: none;
}

.side-link-label {
  min-width: 0;
  margin-right: auto;
}

.side-icon {
  flex: none;
  color: #94a3b8;
}

.side-link:hover {
  background: var(--info-soft);
  color: var(--ink);
}

.side-link:hover .side-icon {
  color: #64748b;
}

.side-link[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent-2);
  font-weight: 700;
}

.side-link[aria-current="page"] .side-icon {
  color: var(--accent);
}

.side-count::before {
  content: attr(data-count);
}

.side-foot {
  margin-top: auto;
  border-top: 1px solid var(--line);
  padding: 12px 8px 0;
}

.logout-form {
  margin: 0;
}

.main-container {
  width: 100%;
  max-width: 1060px;
  min-width: 0;
  margin: 0 auto;
  padding: 24px 24px 80px;
}

.page-heading {
  margin: 2px 0 18px;
}

.spread {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* A page heading is two stacked lines, so its trailing chip or button belongs
   on the heading's own line rather than floating against the vertical middle
   of the whole block. */
.spread.align-top {
  align-items: flex-start;
}

.spread.align-top > :last-child {
  margin-top: 4px;
}

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
  margin: 14px 0;
}

.two-col {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  align-items: start;
  gap: 16px;
  margin: 12px 0;
}

/* A grid item's default `min-width: auto` refuses to shrink below its widest
   unbreakable child, so one long table, digest or code string used to push the
   whole page sideways instead of scrolling inside its own container. */
.two-col > * {
  min-width: 0;
}

.card,
.tile,
.stat,
.flow-step,
.error-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow);
}

/* Owner dashboard, 2026-08-27.  One equal-height responsive row of figures:
   the tiles are grid items, so they share a height, and the value row has a
   fixed height of its own, so every label starts on the same line no matter
   how many lines the label above it took.  The money tile is an ordinary
   member of this grid -- it must never be left orphaned on a row of its own. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 14px;
  margin: 12px 0 22px;
}

.stat {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 6px;
  padding: 16px;
}

.stat-top {
  display: flex;
  min-height: 24px;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.stat-value {
  margin: 0;
  overflow-wrap: anywhere;
  color: var(--ink);
  font-size: 25px;
  font-weight: 800;
  line-height: 1.2;
}

.stat-label {
  margin: 0;
  color: var(--muted);
  font-size: 13.5px;
  line-height: 1.4;
}

/* The payroll-group cards need more width than a figure tile: their status
   chip is a whole sentence.  The chip sits in a row of its own at the top, so
   it lands on the same line in every card however long the firm's legal name
   is, and the meta line is pushed to the bottom so those align too. */
.group-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 14px;
  margin: 12px 0 22px;
}

.group-tile {
  gap: 8px;
}

.tile-top {
  display: flex;
  min-height: 26px;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
}

.group-name {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  color: var(--ink);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.35;
}

.group-meta {
  margin-top: auto;
}

.section-heading {
  margin: 24px 0 4px;
  color: #64748b;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.section-caption {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.card {
  min-width: 0;
  margin: 12px 0;
  padding: 18px;
}

.tile {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 16px;
  color: var(--ink);
  text-decoration: none;
}

.tile:hover {
  border-color: #c7d2fe;
}

.tile h3,
.tile p {
  margin-bottom: 0;
}

.sub {
  color: var(--muted);
  font-size: 14px;
}

/* Every notice on every screen is this one banner: same geometry as a card,
   toned by what it says.  It is a statement the reader must not skim past, so
   it carries a little more weight than body copy. */
.banner {
  margin: 12px 0;
  border: 1px solid #c7d2fe;
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--accent-2);
  padding: 11px 14px;
  font-size: 14.5px;
  font-weight: 600;
}

.banner.ok {
  border-color: #a7f3d0;
  background: var(--ok-soft);
  color: var(--ok);
}

.banner.warn {
  border-color: #fcd34d;
  background: var(--warn-soft);
  color: var(--warn);
}

.tg {
  max-width: 100%;
  overflow-x: auto;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.flow {
  margin: 14px 0;
}

.flow-step {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin: 10px 0;
  padding: 18px;
}

.flow-step .n {
  display: flex;
  width: 36px;
  height: 36px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--info-soft);
  color: var(--info);
  font-size: 15px;
  font-weight: 800;
}

.flow-step .fs-body {
  min-width: 0;
  flex: 1;
}

.flow-step .fs-body > strong {
  display: block;
  margin-bottom: 2px;
  font-size: 16.5px;
}

.flow-step.done {
  background: #fbfdfc;
}

.flow-step.done .n {
  background: var(--ok-soft);
  color: var(--ok);
  font-size: 0;
}

.flow-step.done .n::before {
  content: "\2713";
  font-size: 15px;
}

.flow-step.now {
  border-color: #a5b4fc;
  outline: 3px solid #e0e7ff;
}

.flow-step.now .n {
  background: var(--accent);
  color: #fff;
}

.flow-step.wait {
  opacity: 0.62;
}

.actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 14px;
}

.btn {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
  font-size: 15px;
  font-weight: 600;
  padding: 9px 18px;
  text-decoration: none;
}

.btn:hover {
  border-color: #cbd5e1;
}

.btn-primary {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}

.btn-primary:hover {
  border-color: var(--accent-2);
  background: var(--accent-2);
}

.btn-ghost {
  background: transparent;
}

/* Binding prototype v14, design/prototype.html line 66. */
.btn-danger {
  border-color: #fecdd3;
  background: #fff;
  color: var(--bad);
}

.btn-block {
  width: 100%;
}

.pill,
.state,
.badge {
  display: inline-block;
  white-space: nowrap;
  font-weight: 700;
}

.pill,
.state {
  border-radius: 999px;
  font-size: 13px;
  line-height: 1.6;
  padding: 3px 11px;
}

.pill {
  background: var(--info-soft);
  color: var(--info);
}

.pill.ok {
  background: var(--ok-soft);
  color: var(--ok);
}

.pill.warn {
  background: var(--warn-soft);
  color: var(--warn);
}

.pill.bad {
  background: var(--bad-soft);
  color: var(--bad);
}

.pill.info {
  background: var(--accent-soft);
  color: var(--accent-2);
}

/* A status chip that states a whole sentence rather than a one-word state, so
   it keeps sentence case where `.badge` shouts in capitals.  (`.chip` is
   already taken -- that one is the clickable period filter.)  Its tone is its
   meaning: emerald = clear, amber = wants attention, slate = neutral.  The
   `.st-*` tones below are shared with `.state`, and every chip on a card sits
   in the card's own chip row so the chips align across a grid of cards. */
.status-chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--info-soft);
  color: var(--info);
  padding: 3px 10px;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.45;
  /* A sentence chip may wrap inside a narrow card; only the inline
     `.pill`/`.state`/`.badge` forms must stay on one line. */
  white-space: normal;
}

.status-chip.ok,
.status-chip.st-done {
  border-color: #a7f3d0;
  background: var(--ok-soft);
  color: var(--ok);
}

.st-done {
  background: var(--ok-soft);
  color: var(--ok);
}

.st-ready {
  border: 1px solid #c7d2fe;
  background: var(--accent-soft);
  color: var(--accent-2);
}

.st-attn {
  border: 1px solid #fcd34d;
  background: var(--warn-soft);
  color: var(--warn);
}

/* "No attendance read for this date" wants attention too, so it is amber --
   but outlined rather than filled, so it can never be mistaken for the filled
   amber chip that means days are actually waiting for an answer. */
.st-locked {
  border: 1px solid #fcd34d;
  background: var(--card);
  color: var(--warn);
}

.table-wrap {
  width: 100%;
  overflow-x: auto;
}

.table-wrap:focus-visible {
  border-radius: 8px;
  outline: 3px solid #a5b4fc;
  outline-offset: 2px;
}

.table {
  width: 100%;
  margin: 10px 0;
  border-collapse: collapse;
  background: #fff;
}

.table th {
  border-bottom: 2px solid var(--line);
  color: var(--muted);
  font-size: 12.5px;
  letter-spacing: 0.3px;
  padding: 9px 10px;
  text-align: left;
}

.table td {
  border-bottom: 1px solid var(--line);
  font-size: 14.5px;
  padding: 9px 10px;
}

.table tbody tr:nth-child(odd) {
  background: #fafbfe;
}

.kv {
  display: grid;
  grid-template-columns: minmax(140px, max-content) 1fr;
  gap: 6px 18px;
  margin: 8px 0;
}

.kv dt {
  color: var(--muted);
  font-size: 14px;
}

.kv dd {
  margin: 0;
  font-size: 14.5px;
  font-weight: 600;
}

.bar {
  height: 8px;
  min-width: 120px;
  margin: 12px 0;
  overflow: hidden;
  border-radius: 999px;
  background: #e2e8f0;
}

.bar > span {
  display: block;
  width: 90%;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}

.searchbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 12px 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: #fff;
  padding: 4px 6px 4px 14px;
}

.searchbar:focus-within {
  border-color: var(--accent);
  outline: 2px solid #c7d2fe;
}

.searchbar::before {
  content: "\1f50d";
  opacity: 0.55;
  font-size: 14px;
}

.searchbar input {
  min-width: 0;
  flex: 1;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--ink);
  padding: 10px 0;
}

.field {
  margin: 12px 0;
}

.field label {
  display: block;
  margin-bottom: 5px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  padding: 11px 12px;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--accent);
  outline: 2px solid #c7d2fe;
}

.form-error,
.errorlist {
  margin: 8px 0;
  border-radius: 10px;
  background: var(--bad-soft);
  color: #be123c;
  font-size: 14px;
  font-weight: 600;
  padding: 9px 11px;
}

.errorlist {
  list-style-position: inside;
}

.auth-page {
  display: flex;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  padding: 40px 16px;
}

.auth-card {
  width: 100%;
  max-width: 390px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: #fff;
  box-shadow: var(--shadow);
  padding: 28px;
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 26px;
}

.auth-brand-mark {
  display: flex;
  width: 48px;
  height: 48px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  background: var(--accent);
  color: #fff;
  font-size: 19px;
  font-weight: 800;
}

.auth-brand h1,
.auth-brand p {
  margin-bottom: 0;
}

.auth-brand h1 {
  font-size: 20px;
}

.auth-brand p {
  color: #94a3b8;
  font-size: 13px;
  font-weight: 600;
}

.auth-intro {
  margin-bottom: 18px;
}

.auth-intro h2 {
  font-size: 20px;
}

.auth-foot {
  margin: 20px 0 0;
  color: #94a3b8;
  font-size: 12px;
  text-align: center;
}

.error-page {
  display: flex;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.error-card {
  width: 100%;
  max-width: 520px;
  padding: 28px;
  text-align: center;
}

.error-code {
  margin-bottom: 6px;
  color: var(--accent);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

/* The prototype's 10px uppercase micro-label is not legible enough for the
   employee-directory duplicate-code warning, which is a real HR decision cue.
   Raised to the 13px the sibling `.pill`/`.state` badges already use. */
.badge {
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--accent-2);
  font-size: 13px;
  letter-spacing: 0.4px;
  padding: 2px 8px;
  text-transform: uppercase;
}

.badge.warn {
  background: #fef3c7;
  color: #92400e;
}

.badge.off {
  background: #e2e8f0;
  color: #475569;
}

/* Users & roles takes the owner's PMS shape (docs/09 item 51, 2026-08-26).
   `.role-row` is the full-width heading row that names a group of logins,
   counts it and states its boundary in one sentence.  Because that heading now
   carries the access boundary, the person rows below it carry no Role column
   at all -- so a filtered or paged view must never show a person without their
   heading (app.js `syncRowGroups`). */
.role-row td {
  border-bottom: 0;
  background: #fff;
  color: var(--ink);
  font-size: 16px;
  font-weight: 800;
  padding-top: 20px;
}

.group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}

.group-head h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 800;
}

.group-muted {
  color: var(--muted);
}

.group-count {
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}

.group-note {
  max-width: 72ch;
  margin: 3px 0 0;
  color: var(--muted);
  font-size: 13px;
  font-weight: 400;
}

/* Three columns only: who, what is true of them, what you can change. */
.people-table {
  min-width: 26rem;
}

.people-table .col-status,
.people-table .col-actions {
  text-align: right;
}

.people-table .col-actions {
  white-space: nowrap;
}

.person {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 11px;
}

.person-copy {
  display: grid;
  min-width: 0;
  line-height: 1.35;
}

.person-copy strong {
  font-weight: 800;
}

.person-copy .sub {
  font-size: 12.5px;
}

/* The one row belonging to the person reading the page. */
.avatar-me {
  background: var(--accent);
  color: #fff;
}

.status-cell {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 4px;
}

.status-none {
  color: #94a3b8;
}

.icon-actions {
  display: inline-flex;
  justify-content: flex-end;
  gap: 2px;
}

/* Hover target for a disabled control's own explanation. */
.icon-lock {
  display: inline-flex;
}

.icon-btn {
  display: inline-flex;
  width: 38px;
  height: 38px;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

/* The click target is the button, never one of the SVG's paths. */
.icon-btn .icon {
  display: block;
  pointer-events: none;
}

.icon-btn:enabled:hover {
  background: var(--info-soft);
  color: var(--ink);
}

.icon-btn:focus-visible {
  outline: 3px solid #a5b4fc;
  outline-offset: 1px;
}

/* Kept clickable-looking enough to carry its own `title`: a disabled control
   here is the app's existing self-protection, and it has to say why. */
.icon-btn:disabled {
  color: #cbd5e1;
  cursor: default;
}

.icon-btn.icon-danger:enabled:hover {
  background: var(--bad-soft);
  color: var(--bad);
}

.icon-btn.icon-good {
  color: var(--ok);
}

.icon-btn.icon-good:enabled:hover {
  background: var(--ok-soft);
  color: var(--ok);
}

/* Text a screen reader and the client-side search both read, but the eye does
   not: it keeps "search a name, username, role or department" literally true
   now that the visible role lives in the group heading. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.nav-scrim {
  display: none;
}

.date-range-form,
.pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.date-range-form label {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.date-range-form input {
  width: auto;
  min-height: 34px;
  padding: 5px 8px;
}

.pagination {
  justify-content: space-between;
  margin-top: 14px;
}

/* Prototype-v14 list and claims primitives shared by the M7 role screens. */
.xcard {
  margin: 12px 0;
  border: 1px solid var(--line);
  border-left-width: 5px;
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow);
  padding: 16px;
}

.x-miss {
  border-left-color: #f59e0b;
}

.x-abs {
  border-left-color: var(--bad);
}

.x-late {
  border-left-color: #94a3b8;
}

.who {
  font-weight: 700;
}

.who small,
.who span {
  color: var(--muted);
  font-weight: 500;
}

.punches {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0;
}

.punch {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--info-soft);
  padding: 3px 9px;
  font: 13.5px/1.55 ui-monospace, Consolas, monospace;
}

.punch.missing {
  border: 1.5px dashed #f59e0b;
  background: var(--warn-soft);
  color: var(--warn);
  font-weight: 700;
}

/* The PMS tab strip (app/src/components/ui/tab-strip.tsx): indigo pills, the
   chosen one filled.  It wraps rather than scrolls, exactly as the PMS strip
   does, so a narrow screen never hides a tab behind a swipe.  The strip ships
   hidden and the shared script reveals it, so a reader without JavaScript sees
   every section stacked instead of a control that does nothing. */
.tabstrip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 2px 0 16px;
}

.tab {
  min-height: 40px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent-2);
  padding: 8px 16px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.tab:hover {
  background: #e0e7ff;
}

.tab[aria-selected="true"] {
  background: var(--accent);
  color: #fff;
  box-shadow: var(--shadow);
}

.chipset {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0;
}

.chip {
  min-height: 38px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  color: var(--muted);
  padding: 7px 14px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
}

.chip.on {
  border-color: #a5b4fc;
  background: var(--accent-soft);
  color: var(--accent-2);
}

.preview {
  margin: 10px 0;
  border: 1px solid #c7d2fe;
  border-radius: 10px;
  background: var(--accent-soft);
  padding: 10px 14px;
  font-size: 14.5px;
}

.preview::before {
  content: "\2192\00a0\00a0";
  font-weight: 700;
}

.steps {
  margin: 10px 0;
  padding: 0;
  list-style: none;
  counter-reset: claim-step;
}

.step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  border-bottom: 1px dashed var(--line);
  padding: 11px 4px;
}

.step::before {
  display: inline-flex;
  width: 26px;
  height: 26px;
  flex: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--info-soft);
  color: var(--info);
  content: counter(claim-step);
  counter-increment: claim-step;
  font-size: 13px;
  font-weight: 800;
}

.step.done::before {
  background: var(--ok-soft);
  color: var(--ok);
  content: "\2713";
}

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 12px 0;
}

.pager .chip {
  min-width: 40px;
  text-align: center;
}

/* A page control is a compact strip, not a wall of buttons: the numbers are
   square-ish, and the pages a long list leaves out read as one quiet gap. */
.pagination .chip {
  display: inline-flex;
  min-width: 40px;
  align-items: center;
  justify-content: center;
  padding: 7px 10px;
  text-align: center;
}

.pager-gap {
  display: inline-flex;
  min-width: 20px;
  align-items: center;
  justify-content: center;
  color: #94a3b8;
  font-size: 14px;
  font-weight: 700;
  user-select: none;
}

[data-choose] {
  cursor: pointer;
}

.table tbody tr.is-selected,
.table tbody tr.is-selected:nth-child(odd) {
  background: var(--accent-soft);
}

.table tbody tr[data-choose]:hover {
  background: #f1f4ff;
}

.table tbody tr[data-choose]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
}

.btn[disabled] {
  pointer-events: none;
  opacity: 0.45;
}

.claim-dialog {
  width: min(680px, calc(100% - 32px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: 0 24px 70px rgba(15, 23, 42, 0.28);
  padding: 20px;
}

.claim-dialog::backdrop {
  background: rgba(15, 23, 42, 0.48);
}

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  overflow-y: auto;
  place-items: start center;
  padding: 40px 16px;
  background: rgba(15, 23, 42, 0.48);
}

.correction-modal {
  width: min(720px, 100%);
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  box-shadow: 0 24px 70px rgba(15, 23, 42, 0.28);
}

/* One punch of the day: what it is called, what it holds now, what it should
   become, and the way to say it never happened.  The four columns line up
   down the dialog so the whole timeline reads as a column of times rather
   than four separate forms. */
.punch-set-row {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) auto auto auto;
  align-items: center;
  gap: 10px 14px;
  padding: 10px 0;
}

/* A rule between punches, never one under the last of them: the timeline ends
   at the last punch, and a trailing line would read as a separator from the
   confirmation block below. */
.punch-set-row + .punch-set-row {
  border-top: 1px solid var(--line);
}

.punch-set-name {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.punch-set-name strong {
  color: var(--ink);
  font-size: 15px;
}

.punch-set-time {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-weight: 700;
}

.punch-set-time select {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  font-size: 15px;
  padding: 9px 8px;
}

.punch-set-time select:focus {
  border-color: var(--accent);
  outline: 2px solid #c7d2fe;
}

.punch-set-missing label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}

.punch-set-row-errors {
  grid-column: 1 / -1;
}

/* A note the row grows while it is being filled in, not a refusal: it sits
   under the whole row like the row's errors do, but stays in the quiet copy
   colour so it never reads as a rejected entry. */
.punch-set-hint {
  grid-column: 1 / -1;
  margin: 0;
}

@media (max-width: 640px) {
  /* Below this width the four columns stop being a row of one punch and
     become an unreadable squeeze, so each punch stacks into its own block. */
  .punch-set-row {
    grid-template-columns: 1fr;
    justify-items: start;
  }
}

/* Read-only oversight (owner decision, docs/09 item 51).  The MD may open
   every screen; the server still refuses every write outside his own modules,
   and that refusal -- not this rule -- is the security boundary.  This only
   stops a control that would always be refused from being offered on a screen
   he is merely observing.  Every filter and picker on these screens is a GET
   form, so navigation stays fully usable; dialog open/close controls are
   `type="button"` and stay usable too. */
[data-observer="read-only"] form[method="post" i] button[type="submit"],
[data-observer="read-only"] form[method="post" i] button:not([type]),
[data-observer="read-only"] form[method="post" i] input[type="submit"] {
  display: none;
}

[data-observer="read-only"] form[method="post" i] input,
[data-observer="read-only"] form[method="post" i] select,
[data-observer="read-only"] form[method="post" i] textarea {
  pointer-events: none;
}

@media (max-width: 900px) {
  .burger {
    display: inline-flex;
  }

  .sidebar {
    position: fixed;
    top: 62px;
    bottom: 0;
    left: 0;
    z-index: 75;
    min-height: 0;
    transform: translateX(-105%);
    transition: transform 0.2s ease;
    box-shadow: 0 10px 40px rgba(15, 23, 42, 0.25);
  }

  body.nav-open .sidebar {
    transform: none;
  }

  /* The off-canvas menu is a touch surface, so every row keeps a 44px target
     even though the desktop rail is denser. */
  .side-link {
    min-height: 44px;
  }

  /* Same reason: a page number and a tab are tapped, not clicked, below this
     width. */
  .pagination .chip {
    min-height: 44px;
    min-width: 44px;
  }

  .tab {
    min-height: 44px;
  }

  body.nav-open .nav-scrim {
    position: fixed;
    inset: 62px 0 0;
    z-index: 72;
    display: block;
    border: 0;
    background: rgba(15, 23, 42, 0.35);
  }

  .two-col {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 720px) {
  .topbar {
    padding: 10px 12px;
  }

  .main-container {
    padding: 18px 12px 60px;
  }

  .grid {
    grid-template-columns: 1fr;
  }

  .signed-in-copy span {
    display: none;
  }

  .auth-card {
    padding: 24px 20px;
  }
}

@media (max-width: 480px) {
  .signed-in-copy,
  .signed-in .avatar {
    display: none;
  }

  .brand {
    font-size: 14px;
  }

  .kv {
    grid-template-columns: 1fr;
    gap: 2px;
  }

  .kv dd {
    margin-bottom: 8px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }
}
