/* Jericho Platform — shared styles. Mobile-first: base rules target small
   screens; wider layouts are added via min-width media queries. */

:root {
  /* "Sky Tint", chosen by the owner on 2026-10-13 from eight options on
     white. The six values he picked are in docs/LOOK-DECISION.md and are
     reproduced here exactly; anything else in this block is marked as
     derived, because a colour nobody chose should not look like one that was.

     THE STANDING RULE: white behind everything. Not cream, not off-white, not
     a tinted page. He rejected a cream version of this very look. Colour goes
     in the blocks, badges and buttons - never behind the page. */
  --bg: #ffffff;              /* chosen: the page */
  --surface: #ffffff;         /* chosen: same white, so cards do not float */
  --border: #e2ebf3;          /* chosen: hairline */
  --text: #15202b;            /* chosen */
  --text-muted: #52677a;      /* chosen: quiet text; darkened from #5f7488 on 2026-10-28 to pass the 4.5:1 contrast check (axe) on grey and tinted fills */
  --blue: #27618f;            /* chosen: accent - buttons, active tab */
  --blue-tint: #f0f6fb;       /* chosen: the listing block fill */

  /* Derived. A blue badge used to sit on a white row and show up against it.
     The row is now #f0f6fb, so a badge filled with #f0f6fb would be the same
     colour as the thing behind it - an invisible status, on screens whose
     whole job is to say what state something is in. One step deeper. */
  --blue-tint-deep: #dcebf7;

  /* Derived, not chosen. The header and the pressed state of a button need a
     darker tone than the accent, and the decision lists no value for either.
     This is the accent itself darkened; it is not a seventh colour. */
  --blue-dark: #1d4a6d;

  /* The status colours are untouched. They carry MEANING - approved, waiting,
     refused - and restyling them is a change to what a screen says, not to
     how it looks. docs/LOOK-DECISION.md draws exactly that line. */
  --green: #22744a;           /* darkened from #2e8b57 (2026-10-28): 5.1:1 on --green-tint */
  --green-tint: #e7f5ec;
  --amber: #876208;           /* darkened from #b8860b (2026-10-28): 5.0:1 on --amber-tint */
  --amber-tint: #fbf3de;
  --red: #b3423a;
  --red-tint: #fbeae8;

  /* chosen: 12px on listing blocks, 10px on buttons. Cards and inputs take
     the button radius, which the decision does not name - the alternative is
     three radii on one screen. */
  --radius: 10px;
  --radius-block: 12px;

  /* Lighter than before, because the page and the cards are now the same
     white: at the old opacity every card looked like it was peeling off. */
  --shadow: 0 1px 2px rgba(21, 32, 43, 0.06);

  /* chosen: a serif for headings and titles, a sans for everything that is
     read as information. Georgia and the system sans are the fallbacks, so a
     blocked or slow Google Fonts changes the face and nothing else. */
  --font-head: "Spectral", Georgia, "Times New Roman", serif;
  --font-body: "Source Sans 3", -apple-system, BlinkMacSystemFont, "Segoe UI",
               Roboto, Arial, sans-serif;
}
/* ---------------------------------------------------------------------------
   TYPE SCALE
   Raised one step on 2026-10-21, at the owner's request. He has keratoconus
   and reads this on a 13-inch screen; the old scale bottomed out at 11px,
   which is below what he can read comfortably, and nothing in it was a
   deliberate choice - the sizes accumulated one card at a time.
   Every size now comes from here, so the next change is one edit rather
   than thirty. The relationships are preserved: nothing overtook anything
   it previously sat below.
   The small end moved two steps and the headings only one, deliberately:
   the body text, labels, badges and table cells are what he has to read,
   and the headings are what overflow a 390px screen. --text-md reaching
   16px has a second effect worth knowing - iOS stops zooming the page when
   a text input is focused at 16px or above, so the form inputs at that size
   no longer jump the layout on a phone.
   The constraint this has to respect is tests/e2e/on-a-phone.spec.js, which
   fails the build if any screen scrolls sideways by more than 2px at 390px
   wide, and if any control is under 44px tall. Larger type only helps the
   second. The first is the risk, and it is CI that decides it, not a local
   look - the browser suites need the live project and cannot run here.
   --------------------------------------------------------------------------- */
:root {
  --text-xs: 13px;
  --text-sm: 14px;
  --text-base: 15px;
  --text-md: 16px;
  --text-lg: 17px;
  --text-xl: 18px;
  --text-2xl: 21px;
  --text-3xl: 24px;
  --text-4xl: 30px;
}


* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.45;
}

a { color: var(--blue); }

/* ---- The serif, where the decision puts it ---- */
h1, h2, h3, h4 { font-family: var(--font-head); font-weight: 600; }
.list-row-title { font-family: var(--font-head); }

.container {
  max-width: 960px;
  margin: 0 auto;
  padding: 0 16px;
}

/* ---- Header / nav ---- */
header.site-header {
  background: var(--blue-dark);   /* the accent, darkened - see :root */
  color: #fff;
  padding: 14px 16px;
}
header.site-header .container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
header.site-header h1 {
  font-family: var(--font-head);
  font-size: var(--text-2xl);
  margin: 0;
  letter-spacing: 0.3px;
}
header.site-header .user-info {
  font-size: var(--text-base);
  opacity: 0.9;
}

nav.tabs {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
}
nav.tabs .container { display: flex; gap: 4px; padding: 0 8px; }
nav.tabs button {
  background: none;
  border: none;
  padding: 12px 14px;
  font-size: var(--text-md);
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
nav.tabs button.active {
  color: var(--blue-dark);
  border-bottom-color: var(--blue);
  font-weight: 600;
}

main { padding: 16px 0 48px; }

.screen { display: none; }
.screen.active { display: block; }

/* ---- Cards ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  margin-bottom: 14px;
}
.card h2 { margin-top: 0; font-size: var(--text-xl); }
.card h3 { font-size: var(--text-md); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.4px; }

/* ---- Forms ---- */
label { display: block; font-size: var(--text-base); font-weight: 600; margin: 12px 0 4px; }
input, select, textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-lg);
  font-family: inherit;
  background: #fff;
  color: var(--text);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-tint);
}
textarea { min-height: 80px; resize: vertical; }
.field-hint { font-size: var(--text-sm); color: var(--text-muted); margin-top: 3px; }
/* Checkbox/radio rows are the main touch target on the listing form (15
   document checkboxes). Native controls render ~13px tall, well under a
   comfortable tap size, so the whole label is made the target: the label
   wraps the input, so tapping anywhere on the row toggles it. */
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0;
  min-height: 40px;
  padding: 4px 2px;
  cursor: pointer;
  font-weight: normal;
}
.checkbox-row input[type="checkbox"],
.checkbox-row input[type="radio"] {
  width: 20px;
  height: 20px;
  flex: none;
  margin: 0;
  accent-color: var(--blue);
}
fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; margin: 12px 0 0; }
fieldset legend { font-size: var(--text-base); font-weight: 600; padding: 0 6px; }

/* ---- Buttons ---- */
button, .btn {
  display: inline-block;
  border: none;
  border-radius: var(--radius);
  padding: 11px 18px;
  font-size: var(--text-md);
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
}
.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover { background: var(--blue-dark); }
.btn-secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
/* Was: hover to var(--bg). That used to be grey; it is white now, and the
   button is also white, so hovering showed nothing - a control that does not
   answer the pointer reads as dead. The accent's own tint instead. */
.btn-secondary:hover { background: var(--blue-tint); }
.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover { opacity: 0.9; }
.btn-small { padding: 8px 14px; font-size: var(--text-sm); min-height: 34px; }
.btn-block { display: block; width: 100%; }
button:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---- Badges ---- */
.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.badge-blue { background: var(--blue-tint-deep); color: var(--blue-dark); }
.badge-green { background: var(--green-tint); color: var(--green); }
.badge-amber { background: var(--amber-tint); color: var(--amber); }
.badge-red { background: var(--red-tint); color: var(--red); }
/* --bg is white now, which would make this badge vanish on a white card
   as well as on a tinted row. A neutral of its own. */
.badge-grey { background: #eceff2; color: var(--text-muted); }

/* ---- Lists / rows ---- */
.list-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Vertical padding unchanged at 12px. The phone suite fails anything a
     thumb cannot hit and it has caught four targets already; a restyle that
     tightens this would redden it, and the suite would be right. */
  padding: 12px;
  margin-bottom: 8px;
  background: var(--blue-tint);
  border-radius: var(--radius-block);
}
.list-row:last-child { margin-bottom: 0; }
/* min-width: 0 and overflow-wrap are not tidying - they are the fix for a
   real overflow the type change exposed on 2026-10-21.
   The Operator's activity log prints a row's details as raw JSON on one line
   (js/operator.js:1281), e.g.
     {"invitation_id":"8f3c91a8-2b7e-4d1a-9c55-ee0718b2d4a1","email":"x@y.com"}
   which is a single unbroken string. A flex item will not shrink below its
   content's intrinsic width unless min-width is cleared, so the row pushed
   the page sideways: 42px over on a 390px screen, caught by
   tests/e2e/on-a-phone.spec.js:194. At 13px it happened to fit; it would
   have broken at any size given a long enough email address or UUID, so the
   larger type revealed this rather than caused it.
   anywhere, not break-word: break-word will not split a long unspaced token
   when the line is otherwise empty, which is exactly this case. */
.list-row-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.list-row-title { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.list-row-meta { font-size: var(--text-base); color: var(--text-muted); min-width: 0; overflow-wrap: anywhere; }
.empty-state { text-align: center; color: var(--text-muted); padding: 24px 8px; }

/* ---- Tables (operator views) — stack to cards below 700px ---- */
table.data-table { width: 100%; border-collapse: collapse; font-size: var(--text-md); }
table.data-table th, table.data-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
table.data-table th { color: var(--text-muted); font-size: var(--text-sm); text-transform: uppercase; }

@media (max-width: 700px) {
  table.data-table thead { display: none; }
  table.data-table, table.data-table tbody, table.data-table tr, table.data-table td { display: block; width: 100%; }
  table.data-table tr { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 10px; padding: 6px 10px; }
  table.data-table td { border: none; padding: 6px 0; display: flex; justify-content: space-between; gap: 10px; }
  table.data-table td::before { content: attr(data-label); font-weight: 600; color: var(--text-muted); font-size: var(--text-sm); }
}

/* ---- Toasts ---- */
#toast-container { position: fixed; top: 12px; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 1000; pointer-events: none; }
.toast {
  background: var(--text);
  color: #fff;
  padding: 10px 18px;
  border-radius: var(--radius);
  font-size: var(--text-md);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.2s, transform 0.2s;
  max-width: 90vw;
}
.toast-visible { opacity: 1; transform: translateY(0); }
.toast-error { background: var(--red); }
.toast-success { background: var(--green); }

/* ---- Misc ---- */
.grid-2 { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 640px) { .grid-2 { grid-template-columns: 1fr 1fr; } }
.text-muted { color: var(--text-muted); }
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.flex-between { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 10px; }
.row { display: flex; gap: 10px; flex-wrap: wrap; }
.hidden { display: none !important; }
.auth-shell { max-width: 420px; margin: 40px auto; padding: 0 16px; }
.auth-shell .card { padding: 24px 20px; }
.logo-row { text-align: center; margin-bottom: 20px; }
.logo-row h1 { color: var(--blue-dark); font-size: var(--text-3xl); margin: 0; }
.logo-row p, .logo-row .logo-sub { color: var(--text-muted); font-size: var(--text-base); margin: 4px 0 0; }
/* The Terms page subtitle is an h2 (so its h3 sections follow in order) that
   looks like the plain subtitle on every other page. */
.logo-row .logo-sub { font-family: var(--font-body); font-weight: 400; line-height: inherit; }
.notification-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--red); margin-left: 4px; }

/* ---- Workload overview (operator) ----
   Each tile is a button, not a card with a link inside it: the whole tile is
   the target, which is what an operator glancing at a count expects to be able
   to hit. auto-fit keeps them on one row on a desktop and stacks them on a
   phone without a breakpoint of their own. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
  text-align: left;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: inherit;
  min-height: 84px;
}
.stat-tile:hover { background: var(--blue-tint); border-color: var(--blue); }
.stat-value { font-size: var(--text-4xl); font-weight: 700; color: var(--blue-dark); line-height: 1.1; }
.stat-label { font-size: var(--text-base); color: var(--text-muted); }
/* Nothing waiting is the good case and should not read as an alert. */
.stat-tile.stat-zero .stat-value { color: var(--text-muted); }

/* The "is this still available?" prompt on a listing nobody has touched in a
   month. Amber rather than red: nothing is wrong, something is merely old. */
.stale-notice {
  background: var(--amber-tint);
  border: 1px solid var(--amber);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin-top: 8px;
  font-size: var(--text-base);
  color: var(--amber);
}

/* A period in which nothing happened. Still shown - a missing row would read
   as "no data" rather than "no activity" - but not competing for attention. */
tr.row-quiet td { color: var(--text-muted); }

/* Role and status on the Profile page: shown, never editable. */
.locked-row { display: flex; gap: 20px; flex-wrap: wrap; font-size: var(--text-base); align-items: center; }
.locked-row .badge { margin-left: 6px; }

.filter-note {
  background: var(--blue-tint);
  border-radius: var(--radius);
  padding: 8px 12px;
  margin: 0 0 12px;
  font-size: var(--text-base);
}

/* Touch screens: keep every interactive control at a comfortable tap size.
 *
 * This block existed and still missed two whole kinds of control, which
 * tests/e2e/on-a-phone.spec.js found the first time it ran on a 390px screen:
 *
 *   - .lang-btn sets `min-height: 0`, and being more specific than the plain
 *     `button` selector below it WON. The EN / ES toggle measured 19px.
 *   - the list is buttons and form fields. A LINK is neither, so #logout-link
 *     and the footer's Terms link measured 15px - and signing out is not an
 *     optional control.
 *
 * Both are now named explicitly rather than relying on a general rule that
 * demonstrably did not reach them. Sizes come from padding and line-height as
 * well as min-height, because min-height alone leaves the clickable box the
 * right height and the text crammed against the top of it.
 */
@media (max-width: 700px) {
  button, .btn, input, select, textarea { min-height: 40px; }
  .btn-small { min-height: 36px; }
  input[type="checkbox"], input[type="radio"] { min-height: 20px; }
  nav.tabs button { min-height: 44px; }

  /* Links that act as controls. Inline-block because a plain inline <a> has
     no height of its own to set - it is as tall as its text and nothing the
     stylesheet says about height applies. */
  a#logout-link,
  footer a,
  .list-row a {
    display: inline-block;
    min-height: 36px;
    line-height: 36px;
    padding: 0 4px;
  }

  /* Specific enough to beat .lang-btn's own min-height: 0, which is right on
     a desktop header and wrong under a thumb. */
  .lang-toggle .lang-btn {
    min-height: 34px;
    padding: 6px 10px;
    font-size: var(--text-sm);
  }
}

/* ---------------------------------------------------------------------------
   Language toggle (participant pages only — the Operator dashboard is
   English-only and never renders one). Two small EN / ES buttons that inherit
   the surrounding colour, so the same markup works both on the dark header
   and on the light sign-in card.
   --------------------------------------------------------------------------- */
.lang-toggle {
  display: inline-flex;
  gap: 2px;
  vertical-align: middle;
}
.lang-btn {
  background: transparent;
  border: 1px solid currentColor;
  color: inherit;
  /* Full strength: at 0.65 the label failed the contrast check. The active
     language is still marked by its grey wash. */
  opacity: 1;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.5px;
  line-height: 1;
  padding: 3px 6px;
  min-height: 0;
  border-radius: 3px;
  cursor: pointer;
}
.lang-btn:hover { opacity: 0.9; }
/* A neutral grey wash rather than currentColor: the toggle sits on the dark
   header and on the light auth card, and filling with the text colour would
   make the active label invisible against its own background. */
.lang-btn-active {
  opacity: 1;
  background: rgba(128, 128, 128, 0.3);
}

/* On the sign-in and registration cards the toggle sits above the logo,
   right-aligned, so it never competes with the form. */
.lang-toggle-auth {
  /* The page's top-right corner, not above the centred card (owner,
     2026-10-28). */
  position: absolute;
  top: 16px;
  right: 16px;
  display: flex;
  justify-content: flex-end;
  /* The logo's dark blue: the muted grey failed contrast on the active
     button's grey wash once the class stopped being stripped. */
  color: var(--blue-dark);
}
.help-item { border-bottom: 1px solid var(--border, #dde5ee); padding: 10px 0; }
.help-item summary { cursor: pointer; font-weight: 600; }
.help-item p { margin: 8px 0 0; }
.help-link { text-align: center; margin-top: 16px; }

/* ---- Terms & Conditions ------------------------------------------------ */

/* The acceptance row sits above the register button and its label wraps on a
   narrow screen, so it aligns to the top rather than centring the checkbox
   against two lines of text. */
.terms-accept { margin-top: 18px; }
.terms-accept .checkbox-row { align-items: flex-start; }
.terms-accept .checkbox-row input[type="checkbox"] { margin-top: 10px; }
.terms-accept a { text-decoration: underline; }

/* The terms page is prose, not a form - wider than the auth shell and with a
   measure that stays readable. */
.terms-shell { max-width: 760px; }
.terms-section { margin-top: 20px; }
.terms-section h3 {
  font-size: var(--text-md);
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
  margin: 0 0 6px;
}
.terms-section p { margin: 0; line-height: 1.6; }
/* Sections that run to several paragraphs (the Privacy Notice, the company
   details) need the paragraphs to read as separate ones. */
.terms-section p + p { margin-top: 10px; }

/* The unfinished-document warning. Deliberately loud: these terms carry
   placeholders where the operating company's identity belongs, and nobody
   should reach the end of the page without having been told so. */
.terms-placeholder-notice {
  background: var(--amber-tint);
  color: var(--amber);
  border: 1px solid var(--amber);
  border-radius: 6px;
  padding: 10px 12px;
  margin: 0 0 14px;
  font-size: var(--text-base);
  line-height: 1.5;
  font-weight: 600;
}

.app-footer {
  text-align: center;
  padding: 20px 16px 28px;
  font-size: var(--text-base);
  color: var(--text-muted);
}
.app-footer a { color: var(--text-muted); }

/* Read by screen readers, not shown: names a table column whose cells are
   buttons, so the header is not empty. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
