/* =============================================================================
   MyNumbr design system
   See docs/specs/2026-08-13-ui-design-system.md

   Dark for the app, light for the pages a message *recipient* lands on. Someone
   who got an unexpected message and is hunting for how to stop it is already
   uneasy; a dark technical interface reads as more opaque to them, not less.

   No downloaded fonts. The CSP is default-src 'self', so anything external is
   blocked outright — the technical character comes from the palette and from
   monospace doing real work, not from a bespoke typeface.
   ============================================================================= */

/* ---------------------------------------------------------------- tokens ---- */

:root {
  /* Cold near-black rather than pure black, which is harsh at full-screen size. */
  --bg:           #0b0f11;
  --surface:      #151b1e;
  --surface-2:    #1c2427;
  --surface-3:    #232c30;
  --border:       #222a2e;
  --border-str:   #33403f;

  --text:         #e6edf0;
  --muted:        #8b979d;
  /* #7d8990, not something dimmer: --faint carries empty states, the withheld
     marker and the footer, all of which are body-sized text and so need 4.5:1.
     The obvious choice one shade darker measured 3.03:1 and failed. */
  --faint:        #7d8990;

  /* Signal green: reads as "verified" against dark, where the old forest green
     read as "corporate". */
  --accent:       #3ddc97;
  --accent-dim:   #2bb87b;
  --accent-text:  #06231a;
  --accent-soft:  rgba(61, 220, 151, .12);

  --danger:       #ff7b72;
  --danger-soft:  rgba(255, 123, 114, .12);
  --warn:         #e3b341;
  --warn-soft:    rgba(227, 179, 65, .12);
  --info:         #6cb6ff;
  --info-soft:    rgba(108, 182, 255, .12);
  --ok:           var(--accent);
  --ok-soft:      var(--accent-soft);

  --radius:       10px;
  --radius-lg:    14px;
  --radius-pill:  999px;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --shadow-2: 0 8px 28px rgba(0,0,0,.45);

  --step: 4px;
}

/*
   Public pages go light. These are read by recipients, not operators: the abuse
   form, the policies, the certificate verifier. Set via a body class rather than
   a media query, because it is a decision about *audience*, not about the
   viewer's system preference.
*/
body.surface-public {
  --bg:         #f7f8f9;
  --surface:    #ffffff;
  --surface-2:  #f2f4f5;
  --surface-3:  #e9edee;
  --border:     #e0e5e7;
  --border-str: #c9d2d5;

  --text:       #14191b;
  --muted:      #5d686d;
  --faint:      #8a959a;

  --accent:      #0d7a54;
  --accent-dim:  #0a6244;
  --accent-text: #ffffff;
  --accent-soft: rgba(13, 122, 84, .10);

  --danger:      #b3261e;
  --danger-soft: rgba(179, 38, 30, .09);
  --warn:        #8a6100;
  --warn-soft:   rgba(138, 97, 0, .10);
  --info:        #10559a;
  --info-soft:   rgba(16, 85, 154, .10);

  --shadow-1: 0 1px 2px rgba(16,24,40,.06);
  --shadow-2: 0 10px 30px rgba(16,24,40,.10);
}

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

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

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

body {
  margin: 0;
  padding: 28px 20px 96px;
  background: var(--bg);
  color: var(--text);
  font: 15.5px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.wrap      { max-width: 680px;  margin: 0 auto; }
.wrap-wide { max-width: 1060px; margin: 0 auto; }

h1, h2, h3 { line-height: 1.25; letter-spacing: -0.011em; }

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

/* One visible focus treatment for everything, so keyboard users are never lost. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

::selection { background: var(--accent-soft); color: var(--text); }

/* -------------------------------------------------------------- identity ---- */

header.brand { margin: 0 0 26px; }

header.brand h1 {
  margin: 0 0 6px;
  font-size: 27px;
  font-weight: 680;
  letter-spacing: -0.022em;
}

header.brand p { margin: 0; color: var(--muted); font-size: 15px; }

/*
   The mark is the product's own handle prefix. Every MyNumbr address starts with
   a '+', so the logo is the thing users already type.
*/
.topnav-brand::before {
  content: "+";
  color: var(--accent);
  font-family: var(--mono);
  font-weight: 700;
  margin-right: 1px;
}

/* ------------------------------------------------------------------- nav ---- */

.topnav {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  max-width: 1060px;
  margin: -8px auto 26px;
  padding: 0 0 14px;
  border-bottom: 1px solid var(--border);
}

.topnav-brand {
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--text);
  font-family: var(--mono);
}
.topnav-brand:hover { color: var(--text); }

.topnav-links {
  display: flex;
  gap: 4px;
  margin-left: auto;
  flex-wrap: wrap;
  font-size: 13.5px;
}

.topnav-links a {
  color: var(--muted);
  text-decoration: none;
  padding: 5px 10px;
  border-radius: var(--radius-pill);
  transition: background .12s ease, color .12s ease;
}
.topnav-links a:hover { color: var(--text); background: var(--surface-2); }
.topnav-links a.muted-link { opacity: .72; }

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

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 22px;
  margin-bottom: 18px;
  box-shadow: var(--shadow-1);
}

h2.section {
  margin: 0 0 14px;
  font-size: 13px;
  font-weight: 640;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
}

.empty { color: var(--faint); font-size: 14px; margin: 0; }

/* ------------------------------------------------------------------ text ---- */

.hint { color: var(--muted); font-size: 13.5px; font-weight: 400; margin: 0 0 8px; }

/*
   Identifiers are monospace everywhere: handles, references, hashes, codes,
   tokens, numbers. These are strings people copy off a screen and type
   somewhere else, and proportional digits are measurably easier to get wrong.
*/
code, .mono, .ident {
  font-family: var(--mono);
  font-size: .92em;
  background: var(--surface-2);
  border: 1px solid var(--border);
  padding: 1px 6px;
  border-radius: 5px;
  word-break: break-all;
}

.ident-plain {
  font-family: var(--mono);
  font-size: .95em;
  letter-spacing: -0.01em;
  background: none;
  border: 0;
  padding: 0;
}

/*
   The organising idea, made concrete. Wherever a phone number would appear and
   deliberately does not, this marks the absence — so privacy reads as something
   the product is doing rather than something missing.
*/
.withheld {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--faint);
  letter-spacing: .02em;
}
.withheld::before {
  content: "";
  width: 9px; height: 9px;
  border: 1.5px solid currentColor;
  border-radius: 2px 2px 3px 3px;
  border-top-width: 3px;
  opacity: .8;
}

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

button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-text);
  font: 600 14.5px/1 var(--sans);
  cursor: pointer;
  text-decoration: none;
  transition: background .12s ease, border-color .12s ease, transform .06s ease;
}
button:hover, .btn:hover { background: var(--accent-dim); color: var(--accent-text); }
button:active, .btn:active { transform: translateY(1px); }
button:disabled { opacity: .5; cursor: not-allowed; transform: none; }

button.secondary, .btn.secondary {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--border-str);
}
button.secondary:hover, .btn.secondary:hover { background: var(--surface-3); color: var(--text); }

button.danger, .btn.danger {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}
button.danger:hover { background: var(--danger-soft); color: var(--danger); }

button.link {
  background: none;
  border: 0;
  color: var(--accent);
  padding: 0;
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
button.link:hover { background: none; color: var(--accent-dim); }

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

label {
  display: block;
  font-weight: 600;
  font-size: 13.5px;
  margin: 20px 0 6px;
  color: var(--text);
}
label:first-child { margin-top: 0; }

input[type=text], input[type=email], input[type=number], input[type=tel],
input[type=datetime-local], input[type=password], textarea, select, input[type=file] {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border-str);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 14.5px;
  transition: border-color .12s ease, background .12s ease;
}

input:hover, textarea:hover, select:hover { border-color: var(--faint); }
input:focus, textarea:focus, select:focus { border-color: var(--accent); background: var(--surface); }
::placeholder { color: var(--faint); }

textarea { resize: vertical; min-height: 118px; line-height: 1.55; }

/* Fields holding identifiers get the monospace treatment automatically. */
input[name="code"], input[name="ref"], input[name="phone"], input[name="their_number"],
input[name="recipients"], input[readonly], textarea[name="recipients"] {
  font-family: var(--mono);
  font-size: 13.5px;
}

input[type=checkbox] { width: auto; accent-color: var(--accent); }
input[type=file] { padding: 8px; font-size: 13.5px; }

/* ---------------------------------------------------------------- notices --- */

.notice {
  padding: 11px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
  font-size: 14px;
  border: 1px solid var(--border-str);
  background: var(--surface-2);
  color: var(--text);
}
.notice.error { background: var(--danger-soft); border-color: var(--danger); color: var(--text); }
.notice.ok    { background: var(--ok-soft);     border-color: var(--accent); color: var(--text); }

.testbar {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  color: var(--text);
  border-radius: var(--radius);
  padding: 9px 14px;
  margin-bottom: 18px;
  font-size: 13px;
  font-weight: 500;
}
.testbar code { background: rgba(0,0,0,.2); border-color: transparent; }

/* ------------------------------------------------------------------ pills --- */

/*
   Delivery state carries real meaning, so it gets real colour. The old single
   grey pill made "read" and "failed" look like the same kind of fact.
*/
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  font: 600 11.5px/1.6 var(--sans);
  letter-spacing: .02em;
  background: var(--surface-3);
  color: var(--muted);
  border: 1px solid var(--border-str);
  vertical-align: middle;
}
.pill.queued                          { background: var(--surface-3); color: var(--muted); }
.pill.sent, .pill.paid                { background: var(--info-soft);   color: var(--info);   border-color: var(--info); }
.pill.delivered, .pill.read,
.pill.active, .pill.approved          { background: var(--ok-soft);     color: var(--accent); border-color: var(--accent); }
.pill.failed, .pill.rejected,
.pill.blocked, .pill.disabled         { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); }
.pill.unreachable, .pill.held,
.pill.pending, .pill.unverified,
.pill.cancelled                       { background: var(--warn-soft);   color: var(--warn);   border-color: var(--warn); }

/* ----------------------------------------------------------------- tables --- */

table { width: 100%; border-collapse: collapse; font-size: 14px; }

th {
  text-align: left;
  padding: 8px 10px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 640;
  text-transform: uppercase;
  letter-spacing: .06em;
  border-bottom: 1px solid var(--border-str);
}

td { text-align: left; padding: 11px 10px; border-bottom: 1px solid var(--border); }
tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }

/* --------------------------------------------------------------- fragments -- */

.row { display: flex; gap: 12px; }
.row > * { flex: 1; }

.stack  { display: flex; flex-direction: column; gap: 10px; }
.inline { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.inline > form { margin: 0; }
.spread { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--border-str);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.chip:hover { border-color: var(--accent); background: var(--surface-3); }

.price {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
  border-bottom: 1px dashed var(--border);
  font-size: 14px;
  color: var(--muted);
}
.price:last-child {
  border-bottom: 0;
  font-weight: 700;
  font-size: 17px;
  padding-top: 13px;
  color: var(--text);
}
.price span:last-child { font-family: var(--mono); }

/* ---------------------------------------------------------------- threads --- */

.msg {
  padding: 11px 14px;
  border-radius: var(--radius);
  margin-bottom: 9px;
  max-width: 82%;
  font-size: 14.5px;
  line-height: 1.5;
}
.msg.out { background: var(--accent-soft); border: 1px solid var(--accent); margin-left: auto; }
.msg.in  { background: var(--surface-2);   border: 1px solid var(--border); }
.msg .meta { display: block; font-size: 11px; color: var(--muted); margin-top: 6px; }

/* The WhatsApp preview keeps WhatsApp's own colours in both themes — its job is
   to look like the recipient's screen, not like this site. */
.wa-preview {
  background: #0b141a;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 60px;
}
.wa-bubble {
  background: #202c33;
  color: #e9edef;
  border-radius: 8px;
  padding: 9px 12px;
  max-width: 88%;
  font-size: 14.5px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}
.wa-att { font-size: 13.5px; opacity: .95; }
.wa-att span { opacity: .6; font-size: 12px; }

/* --------------------------------------------------------------- feedback --- */

.fb-wrap { position: fixed; right: 18px; bottom: 18px; z-index: 60; }

.fb-open {
  padding: 8px 15px;
  border-radius: var(--radius-pill);
  font-size: 13px;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border-str);
  box-shadow: var(--shadow-2);
}
.fb-open:hover { background: var(--surface-3); color: var(--text); border-color: var(--accent); }

.fb-panel {
  display: none;
  position: absolute;
  right: 0;
  bottom: 46px;
  width: 330px;
  max-width: calc(100vw - 36px);
  background: var(--surface);
  border: 1px solid var(--border-str);
  border-radius: var(--radius-lg);
  padding: 18px;
  box-shadow: var(--shadow-2);
}
.fb-panel.fb-show { display: block; }
.fb-panel label { font-size: 12.5px; }
.fb-panel textarea { min-height: 78px; }

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

footer.fine {
  color: var(--faint);
  font-size: 12.5px;
  text-align: center;
  margin-top: 26px;
}

.legalbar {
  margin-top: 38px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 12.5px;
}
.legalbar a { color: var(--faint); text-decoration: none; }
.legalbar a:hover { color: var(--text); text-decoration: underline; }

/* ------------------------------------------------------------ responsive --- */

@media (max-width: 700px) {
  body { padding: 20px 14px 90px; }
  .grid-2 { grid-template-columns: 1fr; }
  .row { flex-direction: column; gap: 0; }
  .card { padding: 18px; }
  header.brand h1 { font-size: 23px; }
  .topnav { gap: 10px; }
  .topnav-links { gap: 2px; font-size: 13px; }
  table { font-size: 13.5px; }
  th, td { padding: 9px 6px; }
}

/* Honour the system setting rather than animating regardless. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* A form mid-flight. The button keeps its name and value — see the note in
   layout.php on why it is not disabled — so this is a visual state only. */
button.is-busy { opacity: .72; cursor: progress; }
form[aria-busy="true"] { opacity: .94; }

/* ------------------------------------------------------------ field groups --- */

/* Ten fields in one column asks the sender to hold the whole form in their head.
   Naming the groups lets them answer one decision at a time, and skip the parts
   that do not apply. */
.fieldgroup {
  border-top: 1px solid var(--border);
  padding-top: 18px;
  margin-top: 22px;
}
.fieldgroup:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }

.fieldgroup-label {
  margin: 0 0 16px;
  font-size: 11.5px;
  font-weight: 640;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--faint);
}
