/* ReSouris — Base layout (BPP-10 WP-10.1)
 *
 * The application shell: header + nav + main + footer. Designed for the
 * 8-screen v1.0 spec in `delivery-v1.1.md` §3, accommodating the admin
 * + auto-scheduling feature set.
 *
 * Layout posture:
 *   * Centred shell up to 1400px wide
 *   * Sticky header with site brand + (eventually) user menu
 *   * Horizontal navigation pill-tabs (added in WP-10.2)
 *   * Main content area with consistent padding
 *   * Footer with minimal metadata (version, env)
 *
 * a11y rules baked in here at WP-10.1:
 *   * Skip-link target visible on focus
 *   * Visible focus ring on every interactive element
 *   * Reduced-motion-respected transitions (--motion-* tokens)
 *   * Body text contrast ≥ 4.5:1 against --bg (slate-900)
 */

* {
  box-sizing: border-box;
}

/* ⚠️ THE `hidden` ATTRIBUTE MUST BEAT A COMPONENT'S OWN `display`, AND WITHOUT
 * THIS IT LOSES EVERY TIME.
 *
 * `[hidden] { display: none }` lives in the USER-AGENT stylesheet, so any
 * author rule setting `display` outranks it regardless of specificity or
 * order. `.form-field { display: flex }` in screens.css therefore rendered
 * every hidden form field — measured in the live DOM 2026-09-26 on the
 * confirm-attendance modal: `hidden` true, computed display `flex`. The
 * JavaScript was correct; the stylesheet defeated it, and a worker was shown
 * an activity question with nothing in it on a session that has no question
 * to ask.
 *
 * ⚠️ TWO TEMPLATES CARRY `form-field` TOGETHER WITH `hidden` — `my_roster.html`
 * and `_fixed_commitments_worker_tab.html`. Only the first was reported; the
 * second is the same defect in a screen nobody had looked at. That is why this
 * is a reset rather than a rule on `.attendance-activity`, which would have
 * fixed the symptom and left the other one broken.
 *
 * ⚠️ `!important` IS LOAD-BEARING, NOT A SHORTCUT. Without it this loses to
 * `.form-field { display: flex }` on ordinary cascade order, which is the
 * whole problem. The declaration it overrides is always a mistake: an element
 * carrying `hidden` is asking not to be displayed.
 *
 * The two per-component rules this supersedes — `.wps-results[hidden]` and
 * `.rtl-pop[hidden]` in screens.css — are LEFT IN PLACE deliberately. They are
 * now redundant, and removing them in the same change as introducing this
 * would mean two untested claims at once; they are noted in the v1.22 plan. */
[hidden] {
  display: none !important;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  font-family: var(--font-stack);
  font-size: var(--text-base);
  line-height: var(--line-normal);
  color: var(--text-primary);
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ── Links ──────────────────────────────────────────────────────────────────
 *
 * ⚠️ **THE PRODUCT HAD NO ANCHOR COLOUR RULE AT ALL.** Not globally, not per
 * component. Every link in every screen fell back to the browser's built-in
 * `#0000EE` / `#551A8B`, which nobody had noticed because the light theme
 * happens to survive it: browser blue on white measures 9.40:1.
 *
 * ⚠️ **ON THE DARK SURFACE IT IS A FAILURE, AND NOT A MARGINAL ONE.** Measured
 * against `--surface` #1E293B on 2026-09-16:
 *
 *     browser link     #0000EE   1.56:1   FAIL   (WCAG AA needs 4.5:1)
 *     browser visited  #551A8B   1.33:1   FAIL
 *     --accent         #38BDF8   6.83:1   AA
 *
 * At 1.33:1 a visited link is very nearly invisible. Reported as "blue on dark
 * blue, and a selected link is purple on grey" — both exactly right.
 *
 * ⚠️ **`:visited` IS DELIBERATELY IDENTICAL TO `:link`.** Marcus, 2026-09-16:
 * *"I don't need to register that you have followed the link before."* In a
 * console whose lists are mostly rows you have already opened, visited styling
 * is noise that marks almost everything — and it was the worse of the two
 * contrast failures. The trade is real and is being made knowingly: some users
 * do navigate by visited state, so the distinction is dropped only because
 * hover, focus and `aria-current` all still carry position.
 *
 * ⚠️ **AND THIS COMMENT ONCE CLOSED ITSELF HERE, WHICH DELETED THE RULE BELOW.**
 * A stray comment terminator sat on the next line (deliberately not written out
 * again, for the obvious reason), so the remaining prose parsed as a selector —
 * and CSS error recovery discards an invalid selector *together with the next
 * declaration block*, which was `a:where(:link, :visited)` itself. The rule was
 * present in the file, matched by `element.matches()`, and absent from the
 * stylesheet. Every unclassed link on the console fell back to the browser
 * default `#0000EE`: 9.4:1 on light and **1.55:1 on dark**, which is what
 * Marcus reported as "the Customer link ... does not work on Dark theme".
 *
 * ⚠️ *Only computed style can see this class of fault.* The file greps clean,
 * the selector is right, the token resolves — and the rule does not exist.
 *
 * ⚠️ **`:where()` IS LOAD-BEARING, AND TWO COMPONENTS WERE BROKEN BEFORE IT WAS
 * USED.** The first version was a bare `a:visited { color: var(--accent) }` —
 * specificity **0,1,1**, which BEATS any single-class component rule at
 * **0,1,0**. So every anchor styled as something else rendered accent-on-accent,
 * i.e. invisible, and only AFTER being clicked once:
 *
 *   * `.btn--primary` ("+ Add tenant") lost its `color: var(--accent-on)`;
 *   * `.theme-toggle__option--active` ("Light") lost its `color: var(--surface)`.
 *
 * The second one arrived while patching the first with `:not(.btn)` — which is
 * the tell that per-component exclusions are the wrong shape. `:where()`
 * contributes **zero** specificity, so this whole block weighs 0,0,1: the
 * weakest possible element rule, which ANY component class outranks by default.
 *
 * ⚠️ *A `:visited` or `:hover` looks like a state but counts as a class-level
 * weight. Base rules that style a bare element should be wrapped in `:where()`
 * so components never have to fight them.*
 */
a:where(:link, :visited) {
  color: var(--accent);
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  text-underline-offset: 2px;
}

a:where(:hover) {
  text-decoration-color: currentColor;
}

/* ---- Skip link (keyboard a11y; visible only on focus) ---- */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--accent-on);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-100%);
  transition: transform var(--motion-fast) var(--ease-standard);
  z-index: 100;
}
.skip-link:focus {
  transform: translateY(0);
  outline: 3px solid var(--text-primary);
  outline-offset: 2px;
}

/* ---- Application shell ---- */
.app-shell {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-6);
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}

/* ---- Header ---- */
.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border-subtle);
}

/* ⚠️ THE MARK IS A SIBLING OF THE WORDMARK, NOT INSIDE THE LINK. Putting it in
   the anchor would fold the icon into the click target's accessible name and
   grow the focus ring around both; as a sibling the link stays exactly the text
   it was, and the guest topbar -- which has a <span>, not an <a> -- gets the
   same treatment with no second rule.

   A fixed box so the topbar cannot reflow when the file arrives, and
   `object-fit: contain` so a square source is never stretched. */
.app-brand__mark {
  width: 28px;
  height: 28px;
  object-fit: contain;
  flex: 0 0 auto;
  display: block;
}
.app-brand {
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  text-decoration: none;
}
.app-brand:focus {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

.app-header-meta {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* ---- Primary navigation (pill tabs) ---- */
.app-nav {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border-subtle);
}

.app-nav__link {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: var(--radius-md);
  transition: background-color var(--motion-fast) var(--ease-standard),
              color var(--motion-fast) var(--ease-standard);
}
.app-nav__link:hover {
  background: var(--surface);
  color: var(--text-primary);
}
.app-nav__link[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
}
.app-nav__link:focus {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Empty-state placeholder — used by WP-10.1 base layout until WP-10.2
   wires real nav items. */
.app-nav--empty::after {
  content: "—";
  color: var(--text-muted);
  font-size: var(--text-sm);
  padding: var(--space-2);
}

/* ---- Main content ---- */
.app-main {
  flex: 1 0 auto;
  padding: var(--space-6) 0;
}

/* ---- Footer ---- */
.app-footer {
  padding: var(--space-4) 0;
  margin-top: var(--space-8);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* ---- Focus rings on common interactives (global a11y baseline) ---- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* ── Theme control (shared by the tenant UI and the owner console) ───────────
 *
 * ⚠️ MOVED HERE FROM `owner.css`, WHICH THE TENANT UI DOES NOT LOAD. The tenant
 * shell loads tokens/layout/sidebar/screens; `owner.css` is the owner console's
 * alone. So porting the control to the tenant UI without moving these rules
 * would have rendered three unstyled links in the top bar — the markup correct,
 * the component invisible. Both apps load THIS sheet.
 *
 * A segmented three-way control: System / Light / Dark, rendered as links so it
 * works without scripting. The active option is marked server-side from the
 * cookie, which is also why there is no JavaScript file for any of this.
 */
/* ── Theme control ──────────────────────────────────────────────────────────
 *
 * A segmented three-way control: System / Light / Dark. Rendered as links so it
 * works without scripting; the active option is marked server-side from the
 * cookie.
 */
.theme-toggle {
  display: inline-flex;
  border: 1px solid var(--border-subtle);
  border-radius: 7px;
  overflow: hidden;
}

.theme-toggle__option {
  padding: 5px 10px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-secondary);
  text-decoration: none;
  background: transparent;
  border-right: 1px solid var(--border-subtle);
}

.theme-toggle__option:last-child {
  border-right: 0;
}

.theme-toggle__option:hover {
  background: var(--surface-raised);
  color: var(--text-primary);
}

.theme-toggle__option--active {
  background: var(--accent);
  color: var(--surface);
  font-weight: 600;
}

/* ⚠️ Icon-only segments in the top bar. The labels are carried by `title` and
 * `aria-label`, so the control still announces as three named choices — the
 * saving is visual, not semantic. The full labelled version is on Profile. */
.theme-toggle--compact .theme-toggle__option {
  padding: 5px 9px;
  font-size: 13px;
  line-height: 1.2;
  min-width: 28px;
  text-align: center;
}
