/* ─────────────────────────────────────────────────────────────────────────────
   ሓበሬታ (Notice) — habereta.org · your daily notice board
   One stylesheet for the public pages, the admin panel and the account area.

   The look is meant to read as a real notice board rather than a tech product:
   warm cream stock, hairline sand rules, and the badge's own colours — the
   orange-red of the ring, the gold of the megaphone, the silver of the disc and
   the navy of the wordmark under it.

   Colours come from the database (`design` and `theme` settings) and are written
   onto :root at runtime, so an administrator can restyle the site without a
   deploy. The values below are the fallbacks used before that request lands, and
   they are sampled from the logo itself so the two never disagree.
   ──────────────────────────────────────────────────────────────────────────── */

:root {
  --primary: #c04524; /* the orange-red of the ring */
  --accent: #d4a02a; /* the gold of the megaphone */
  --surface: #f6efe1; /* the cream the badge is mounted on */
  --ink: #1c2b47;
  --radius: 18px;

  /* The extended palette the raised shell needs. Every one of these is
     settable by the administrator (see `applyDesign` in core.js); the values
     here are what the site looks like before that request lands. */
  --deep: #8a2c11; /* the underside of a raised surface */
  --glow: #f2c469; /* the lit edge along its top */
  --gold: #d9a52c; /* anything that means money */
  --danger: #b42318;
  --navbar: #9c3117; /* the two bars, which are darker than the page */
  --btn-face: #c9502a; /* the face of a raised button */

  /* The wordmark address under the badge is navy blue, and nothing else on the
     page is. It is kept for the places that need a third colour — a link inside
     a warm panel, a partner button — without reaching for the accent, which on
     this site means "money". */
  --royal: #1f4d90;
  --royal-soft: #e5edf8;

  --paper: #fffbf2;
  --sand: #e9dcc3;
  --sand-deep: #d5c3a1;
  --muted: #6b6255;
  --primary-soft: #fbe8e0;
  --accent-soft: #fdf1da;
  --good: #2f7a2a;
  --warn: #9a6410;
  --bad: #b42318;

  --display: 'Fraunces', 'Noto Serif Ethiopic', 'Nyala', Georgia, serif;
  --body: 'Karla', 'Noto Sans Ethiopic', 'Abyssinica SIL', system-ui, sans-serif;
  --ethiopic: 'Noto Sans Ethiopic', 'Abyssinica SIL', 'Nyala', system-ui, sans-serif;
  --ethiopic-display: 'Noto Serif Ethiopic', 'Nyala', 'Abyssinica SIL', serif;

  --shadow-low: 0 1px 2px rgba(28, 43, 71, 0.05), 0 8px 24px -18px rgba(28, 43, 71, 0.35);
  --shadow-high: 0 2px 4px rgba(28, 43, 71, 0.06), 0 28px 60px -32px rgba(28, 43, 71, 0.42);
  --band: 1200px;

  /* How far the two fixed bars stand off the page, and how much relief the
     raised surfaces have. `data-depth` on <html> scales the second one. */
  --bar-top: 64px;
  --bar-bottom: 66px;
  --lift: 1;
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

/* The administrator's depth setting. `flat` exists for anybody who finds the
   relief distracting, and for a slow device. */
html[data-depth='flat'] {
  --lift: 0;
}
html[data-depth='soft'] {
  --lift: 0.6;
}
html[data-depth='deep'] {
  --lift: 1.35;
}

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

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

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  * {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--body);
  font-size: 1.0125rem;
  line-height: 1.62;
  /* A faint warm wash at the top so the page does not read as flat white. */
  background-image: radial-gradient(120% 60% at 50% -10%, rgba(192, 69, 36, 0.09), transparent 60%);
  background-repeat: no-repeat;
}

h1,
h2,
h3,
h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: var(--primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

img {
  max-width: 100%;
  display: block;
}

/* ── nothing may be wider than the telephone it is read on ─────────────────
   Three rules, in the order they matter.

   The first is the backstop: horizontal scrolling of the page itself is never
   correct here, so it is turned off at the root. It is set on <html> and not on
   <body>, deliberately — the value propagates to the viewport either way, but
   putting it on <body> would make <body> a scroll container and every sticky
   header on the site (the top bar, the table headings, the step navigation)
   would quietly stop sticking. `clip` is the same thing without even a
   scrollport; browsers that do not know it keep the `hidden` above it.

   The second stops embedded media doing the pushing. An <img> was already
   capped; a video, a map, a Jitsi frame or an inline SVG was not.

   The third is for the strings that have no spaces in them and therefore cannot
   wrap on their own: a wallet address, a transaction hash, a long reference, a
   generated password. `anywhere` breaks them mid-string rather than letting one
   of them set the width of the page.

   A backstop is not a licence to leave overflow in the layout: everything that
   actually overflowed under 480px is fixed where it happens (the grids collapse
   with `minmax(min(…, 100%), 1fr)`, wide tables scroll inside `.tablewrap`).
   This is here so that the next component added cannot break the page. */
html {
  max-width: 100%;
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  max-width: 100%;
}

video,
canvas,
svg,
iframe,
embed,
object {
  max-width: 100%;
}

pre,
code,
kbd,
samp,
.mono {
  overflow-wrap: anywhere;
}

pre {
  white-space: pre-wrap;
  max-width: 100%;
}

:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.wrap {
  width: 100%;
  max-width: var(--band);
  margin-inline: auto;
  padding-inline: clamp(1.1rem, 4vw, 2.5rem);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--primary);
  color: #fff;
  padding: 0.7rem 1rem;
  z-index: 90;
  border-radius: 0 0 10px 0;
}
.skip:focus {
  left: 0;
}

/* ── trilingual text ───────────────────────────────────────────────────────
   All three languages are always in the document — families read one or
   another, and hiding a translation behind a toggle only splits the audience.
   The toggle changes which one is the headline and which is the whisper, which
   is why a language change costs one class on <html> and never a re-render.

   The chosen language leads (`order: 1`). Of the other two, one follows as a
   quiet second line and the third is hidden: three stacked renditions of every
   heading turns a page into a glossary. Tigrinya and Amharic keep each other
   company; English keeps company with Tigrinya, the language of the service. */

.ti,
.am {
  font-family: var(--ethiopic);
  font-feature-settings: normal;
}
h1 .ti,
h2 .ti,
h3 .ti,
h1 .am,
h2 .am,
h3 .am{
  font-family: var(--ethiopic-display);
  line-height: 1.35;
}
.en {
  font-family: var(--body);
}

.bi {
  display: flex;
  flex-direction: column;
}
.bi > .ti,
.bi > .am,
.bi > .en {
  display: block;
}

/* The third rendition, out of the way. */
html.lang-ti .bi > .am,
html.lang-am .bi > .en,
html.lang-en .bi > .am {
  display: none;
}

/* The lead. */
html.lang-ti .bi > .ti,
html.lang-am .bi > .am,
html.lang-en .bi > .en {
  order: 1;
}

/* The whisper underneath it. */
html.lang-ti .bi > .en,
html.lang-am .bi > .ti,
html.lang-en .bi > .ti {
  order: 2;
  font-size: 0.86em;
  color: var(--muted);
  font-weight: 400;
  letter-spacing: 0;
  margin-top: 0.28em;
  line-height: 1.5;
}

/* A block with no Amharic rendition — `bi(ti, en)` called with two arguments, or
   markup written before Amharic was added. The rules above would hide the English
   and whisper the Tigrinya, leaving an Amharic reader nothing legible to lead
   with, so English takes the lead instead and Tigrinya stays underneath it. */
html.lang-am .bi:not(:has(> .am)) > .en {
  display: block;
  order: 1;
  font-size: inherit;
  color: inherit;
  font-weight: inherit;
  margin-top: 0;
}

/* Interface strings the shell writes in one language only: `bi()` is for
   content, but a navbar label is a single word and gets `.only` instead. */
.only {
  display: block;
}

/* ── buttons ─────────────────────────────────────────────────────────────── */

.btn {
  --btn-bg: var(--primary);
  --btn-ink: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.82rem 1.35rem;
  min-height: 46px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--btn-bg);
  color: var(--btn-ink);
  font: inherit;
  font-weight: 700;
  font-size: 0.97rem;
  letter-spacing: 0.005em;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.16s ease, box-shadow 0.16s ease, background 0.16s ease;
  box-shadow: var(--shadow-low);
}
.btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-high);
}
.btn:active {
  transform: translateY(0);
}
.btn[disabled],
.btn[aria-busy='true'] {
  opacity: 0.62;
  cursor: progress;
  transform: none;
}
.btn.accent {
  --btn-bg: var(--accent);
}
.btn.ghost {
  --btn-bg: transparent;
  --btn-ink: var(--primary);
  border-color: var(--sand-deep);
  box-shadow: none;
}
.btn.ghost:hover {
  background: var(--paper);
}
.btn.quiet {
  --btn-bg: transparent;
  --btn-ink: var(--muted);
  border-color: transparent;
  box-shadow: none;
  padding-inline: 0.6rem;
  font-weight: 600;
}
.btn.quiet:hover {
  color: var(--ink);
  transform: none;
}
.btn.danger {
  --btn-bg: transparent;
  --btn-ink: var(--bad);
  border-color: rgba(163, 58, 42, 0.35);
  box-shadow: none;
}
.btn.small {
  padding: 0.45rem 0.85rem;
  min-height: 34px;
  font-size: 0.85rem;
}
.btn svg {
  width: 1.05em;
  height: 1.05em;
  flex: none;
}

/* ── header ──────────────────────────────────────────────────────────────── */

.top {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(12px) saturate(140%);
  border-bottom: 1px solid var(--sand);
}


.brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  color: inherit;
  flex: none;
}
.brand .mark {
  width: 42px;
  height: 42px;
  flex: none;
  border-radius: 12px;
  object-fit: cover;
  background: var(--primary);
}
.brand .words {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}
.brand .name {
  font-family: var(--ethiopic-display);
  font-size: 1.32rem;
  font-weight: 700;
  color: var(--primary);
}
.brand .name-en {
  font-family: var(--display);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}

.nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-inline-start: auto;
}
.nav a {
  padding: 0.5rem 0.75rem;
  border-radius: 999px;
  text-decoration: none;
  color: var(--muted);
  font-weight: 600;
  font-size: 0.94rem;
}
.nav a:hover {
  color: var(--ink);
  background: var(--paper);
}


@media (max-width: 940px) {
  .nav {
    position: fixed;
    inset: 74px 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    background: var(--paper);
    border-bottom: 1px solid var(--sand);
    padding: 0.6rem clamp(1.1rem, 4vw, 2.5rem) 1.2rem;
    box-shadow: var(--shadow-high);
    display: none;
  }
  .nav.open {
    display: flex;
  }
  .nav a {
    padding: 0.75rem 0.4rem;
    border-bottom: 1px solid var(--surface);
  }
  
  
  
}

/* ── hero ────────────────────────────────────────────────────────────────── */

.hero {
  padding-block: clamp(2.4rem, 6vw, 4.6rem) clamp(2rem, 5vw, 3.4rem);
}


.hero h1 {
  font-size: clamp(2.15rem, 5.4vw, 3.5rem);
  margin-bottom: 1rem;
}
.hero h1 .ti {
  color: var(--primary);
}
.hero .lede {
  font-size: 1.06rem;
  color: var(--muted);
  max-width: 46ch;
  margin-bottom: 1.6rem;
}
.hero .lede .ti {
  color: #3f4a47;
}

.motto {
  border-left: 3px solid var(--accent);
  padding: 0.35rem 0 0.35rem 1rem;
  margin-bottom: 1.7rem;
  font-family: var(--ethiopic-display);
  font-size: 1.06rem;
  line-height: 1.6;
  color: var(--primary);
  font-weight: 600;
}


/* The card on the right: a "who to call" panel, which is what a worried
   family actually needs first. */

.phones {
  display: grid;
  gap: 0.6rem;
  margin-bottom: 1.2rem;
}
.phone {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.85rem 1rem;
  border-radius: 14px;
  background: rgba(255, 253, 249, 0.1);
  border: 1px solid rgba(255, 253, 249, 0.2);
  color: #fff;
  text-decoration: none;
  font-size: 1.28rem;
  font-weight: 700;
  font-family: var(--display);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  transition: background 0.16s ease;
}
.phone:hover {
  background: rgba(255, 253, 249, 0.18);
}
.phone svg {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--accent);
}


/* ── capability strip ────────────────────────────────────────────────────── */

.strip {
  border-block: 1px solid var(--sand);
  background: var(--paper);
}
.strip-in {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: clamp(1rem, 3vw, 2rem);
  padding-block: 1.9rem;
}
.strip .item {
  display: flex;
  gap: 0.85rem;
}
.strip .item svg {
  width: 22px;
  height: 22px;
  flex: none;
  color: var(--accent);
  margin-top: 0.2rem;
}
.strip .item h3 {
  font-size: 1rem;
  font-family: var(--body);
  font-weight: 700;
  margin-bottom: 0.15rem;
}
.strip .item p {
  font-size: 0.88rem;
  color: var(--muted);
}

/* ── sections ────────────────────────────────────────────────────────────── */

section[id] {
  scroll-margin-top: 92px;
}

.sec {
  padding-block: clamp(3rem, 7vw, 5rem);
}
.sec.tint {
  background: var(--paper);
  border-block: 1px solid var(--sand);
}

.sec-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  margin-bottom: clamp(1.6rem, 4vw, 2.6rem);
}
.sec-head h2 {
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
}
.sec-head h2 .ti {
  color: var(--primary);
}
.sec-head p {
  color: var(--muted);
  max-width: 42ch;
  font-size: 0.96rem;
}
@media (max-width: 760px) {
  .sec-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.8rem;
  }
}

/* ── the icon plate ─────────────────────────────────────────────────────── */

/* The icon plate. The service catalogue no longer uses one — its cards are
   colour-coded keys — but the admin services table still previews the glyph an
   administrator has chosen, and the details sheet still prints it beside the
   heading, so the plate itself stays. */
.ico {
  width: 42px;
  height: 42px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: var(--primary-soft);
  color: var(--primary);
  flex: none;
}
.ico svg {
  width: 22px;
  height: 22px;
}

/* ── the catalogue pager ─────────────────────────────────────────────────── */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 1.2rem;
}

.chips.categories {
  margin-block: 0.6rem 0.2rem;
}
.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.22rem 0.55rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid rgba(238, 84, 6, 0.25);
}
.tag.plain {
  background: var(--primary-soft);
  color: var(--primary);
  border-color: rgba(192, 69, 36, 0.18);
  text-transform: none;
  letter-spacing: 0.02em;
  font-family: var(--ethiopic);
}
/* The "ሓዲሽ / New" flag. Solid rather than tinted so it reads first among the
   chips, and not uppercased because Ethiopic has no case. */
.tag.new {
  background: var(--accent);
  color: #fff;
  border-color: transparent;
  text-transform: none;
  letter-spacing: 0.02em;
}

/* ── a face ──────────────────────────────────────────────────────────────── */

/* The face on any card about a person. `--face` is written onto the element by
   `faceOf`, so a caller's size is the starting point rather than the last word:
   the contexts below set their own width and height and win cleanly, which is
   what lets a photograph be a thumbnail in a feed and a portrait on a profile
   without either being an inline style nobody can override. */
.avatar {
  width: var(--face, 56px);
  height: var(--face, 56px);
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
  background: var(--primary-soft);
}
.initials.avatar {
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: calc(var(--face, 56px) * 0.36);
  line-height: 1;
  color: var(--primary);
}

/* ── about ───────────────────────────────────────────────────────────────── */

.about-in {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: clamp(1.6rem, 5vw, 3.6rem);
  align-items: start;
}
@media (max-width: 900px) {
  .about-in {
    grid-template-columns: minmax(0, 1fr);
  }
}
.about-in .body {
  font-size: 1.02rem;
  color: #3f4a47;
  display: grid;
  gap: 1rem;
}
.promises {
  display: grid;
  gap: 0.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.promises li {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: 14px;
  padding: 0.95rem 1.1rem;
  font-size: 0.95rem;
}
.sec.tint .promises li {
  background: var(--surface);
}
.promises svg {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--good);
  margin-top: 0.18rem;
}

/* ── the seven services, listed on the about page ─────────────────────────
   Not the choosing grid the services page draws — this is the document's own
   inventory, read once by somebody working out what the board is for. So it is
   a list of rows rather than a wall of tiles: a coloured ring, the name in all
   three languages, one sentence, and what it costs.

   The tone is set on each row by the script, and everything here reads
   `--tone`. Each row keeps its own colour rather than taking the section's,
   because this is the one place on the page where the seven colours are the
   information: it is where a reader learns that gold means vacancies before
   they meet a gold button anywhere else.
   ───────────────────────────────────────────────────────────────────────── */
.aboutseven {
  display: grid;
  gap: 0.7rem;
  list-style: none;
  margin: 0.2rem 0 0;
  padding: 0;
}
.aboutseven li {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  background: var(--paper);
  border: 1px solid color-mix(in srgb, var(--tone, var(--primary)) 26%, var(--sand));
  /* The colour as a stripe down the leading edge rather than a tint behind the
     text: these rows carry a sentence, and a sentence on a coloured face is
     the first thing a reader skips. */
  border-inline-start: 5px solid var(--tone, var(--primary));
  border-radius: 14px;
  padding: 0.9rem 1rem;
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.sec.tint .aboutseven li {
  background: var(--surface);
}
.aboutseven li:hover {
  transform: translateY(-2px);
  box-shadow: 0 calc(10px * var(--lift)) calc(22px * var(--lift)) -14px
    color-mix(in srgb, var(--tone, var(--primary)) 70%, transparent);
}

/* The plate around the trade's icon. The same raised face the buttons wear, at
   the size of a bullet, so the list reads as part of this site rather than as
   a table dropped into it. */
.aboutseven-ring {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tone, var(--primary)) 86%, #fff),
    var(--tone, var(--primary))
  );
  color: var(--tone-ink, #fff);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42),
    0 calc(4px * var(--lift)) calc(10px * var(--lift)) -5px color-mix(in srgb, var(--tone, var(--primary)) 62%, transparent);
}
.aboutseven-ring svg {
  width: 20px;
  height: 20px;
}

.aboutseven-body {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 0.2rem;
}
.aboutseven-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.25;
  color: var(--ink);
  text-decoration: none;
}
.aboutseven-name:hover,
.aboutseven-name:focus-visible {
  color: color-mix(in srgb, var(--tone, var(--primary)) 76%, #000);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.aboutseven-body p {
  margin: 0;
  font-size: 0.9rem;
  color: var(--muted);
}
/* The fee sits at the end of the row on a wide screen and under the sentence on
   a telephone, which is what `flex-wrap` on the row would not give us — the
   pill is a sibling of the body, so it drops below only when the row runs out
   of width for both. */
.aboutseven > li > .pill {
  flex: 0 0 auto;
  align-self: center;
}
@media (max-width: 520px) {
  .aboutseven li {
    flex-wrap: wrap;
  }
  .aboutseven > li > .pill {
    margin-inline-start: calc(38px + 0.85rem);
  }
}

/* ── the about page's own colour coding ────────────────────────────────────
   Six sections, six tones. The tone is set on the section by the script, so
   everything below only has to read `--tone` — which the attribute rules near
   the foot of this file have already resolved into a real colour. */
/* ── the contents, and the two columns it stands in ───────────────────────
   This page is a document — eight numbered parts — and a document of this
   length wants a contents list rather than a row of shortcuts.

   One list, two shapes. Below 1000px the contents and the writing stack, the
   contents first, drawn as numbered chips that scroll sideways: the only shape
   that fits eight entries on a telephone without eating the first screen. From
   1000px they separate into two columns and the contents become what they
   actually are — a numbered list standing in its own margin, staying put while
   the page moves, marking the part the reader is in. The writing gets a real
   measure at the same time, instead of running the whole width of a desktop
   window at eleven words a line.
   ─────────────────────────────────────────────────────────────────────────── */
.about-shell {
  display: grid;
  gap: clamp(1.1rem, 3vw, 1.8rem);
  align-items: start;
  margin-top: clamp(0.8rem, 2.5vw, 1.4rem);
}

/* The eight parts, and the gap between them they never had. `#page` is a plain
   block with no rhythm of its own, so until now one part's last card sat hard
   against the next part's heading and the whole page read as a single slab. */
.about-doc {
  display: grid;
  gap: clamp(2rem, 4.5vw, 3.4rem);
  min-width: 0;
}

/* A grid item's default `min-width: auto` would refuse to shrink below the
   width of eight chips, and the sideways scroll would become the whole page's.
   ─────────────────────────────────────────────────────────────────────────── */
.about-outline {
  min-width: 0;
}
.about-outline-head {
  display: none;
}
.about-outline-list {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0 0 0.35rem;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.about-outline-list::-webkit-scrollbar {
  display: none;
}
.about-outline-list li {
  flex: none;
  scroll-snap-align: start;
  min-width: 0;
}
.about-outline-list a {
  display: flex;
  align-items: center;
  gap: 0.42rem;
  padding: 0.36rem 0.78rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--tone) 38%, var(--sand));
  background: color-mix(in srgb, var(--tone) 8%, var(--paper));
  color: color-mix(in srgb, var(--tone) 78%, var(--ink));
  text-decoration: none;
  font-weight: 600;
  font-size: 0.86rem;
  white-space: nowrap;
  transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;
}
.about-outline-list a:hover {
  background: color-mix(in srgb, var(--tone) 16%, var(--paper));
}
.about-outline-list a svg {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--tone);
}
/* Tabular figures so eight numbers keep one width, which is what lets them
   share a left edge in the desktop rail below. */
.about-outline-n {
  font-variant-numeric: tabular-nums;
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  opacity: 0.72;
}

@media (min-width: 1000px) {
  .about-shell {
    grid-template-columns: minmax(196px, 236px) minmax(0, 1fr);
    gap: clamp(1.8rem, 3.5vw, 3rem);
  }
  .about-outline {
    position: sticky;
    top: calc(var(--bar-top) + 1.1rem);
    align-self: start;
    max-height: calc(100dvh - var(--bar-top) - 2rem);
    overflow-y: auto;
    scrollbar-width: none;
  }
  .about-outline::-webkit-scrollbar {
    display: none;
  }
  .about-outline-head {
    display: block;
    margin: 0 0 0.7rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
  }
  /* A hairline down the left of the list, and the current part marked against
     it. The chips' pills, borders and glyphs all go: eight bordered pills in a
     narrow column read as eight buttons, and a contents list is not a toolbar. */
  .about-outline-list {
    display: grid;
    gap: 0.1rem;
    overflow: visible;
    padding: 0;
    border-inline-start: 1px solid var(--sand);
  }
  .about-outline-list a {
    border: 0;
    border-radius: 0 10px 10px 0;
    background: none;
    padding: 0.5rem 0.7rem;
    font-size: 0.9rem;
    font-weight: 600;
    white-space: normal;
    color: var(--muted);
    box-shadow: inset 2px 0 0 transparent;
  }
  .about-outline-list a svg {
    display: none;
  }
  .about-outline-list a:hover {
    background: color-mix(in srgb, var(--tone) 9%, transparent);
    color: color-mix(in srgb, var(--tone) 72%, var(--ink));
  }
  /* The part the reader is standing in. The band the script watches is the
     middle of the window only, so exactly one line is ever lit — three lit at
     once would tell a reader nothing about where they are. */
  .about-outline-list a.on {
    background: color-mix(in srgb, var(--tone) 12%, transparent);
    color: color-mix(in srgb, var(--tone) 78%, var(--ink));
    box-shadow: inset 2px 0 0 var(--tone);
    font-weight: 700;
  }
  .about-outline-list a.on .about-outline-n {
    opacity: 1;
  }
}

/* ── the eyebrow above each part's heading ────────────────────────────────
   The number is what turns eight headings into an outline: a reader who has
   been sent here for one answer can see there are eight parts and which one
   they want. It is also what a coordinator can say down a telephone — "part
   five, paying" — which is the practical reason for numbering a page at all. */
.about-step {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--tone) 58%, var(--muted));
}
.about-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding-inline: 0.3rem;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--tone) 32%, var(--sand));
  background: color-mix(in srgb, var(--tone) 13%, var(--paper));
  color: color-mix(in srgb, var(--tone) 78%, var(--ink));
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
.about-step-w {
  min-width: 0;
}

/* A rule of the section's own colour under its heading, and the same colour
   along the top of the card beneath it. Two small marks rather than a coloured
   panel: the writing on this page is long, and a tinted background behind a
   thousand words is harder to read, not easier. */
.about-sec {
  /* So a jump from the contents does not park the heading under the top bar. */
  scroll-margin-top: calc(var(--bar-top) + 1.4rem);
}
.about-sec .sec-head h2 {
  color: color-mix(in srgb, var(--tone) 70%, var(--ink));
}
.about-sec .sec-head::after {
  content: '';
  display: block;
  width: 62px;
  height: 4px;
  margin-top: 0.55rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--tone), color-mix(in srgb, var(--tone) 35%, transparent));
}
.about-sec > .card3 {
  border-top: 3px solid color-mix(in srgb, var(--tone) 62%, var(--sand));
}

/* The base `.sec-head` is a row with the heading at one end and its note at the
   other. That shape cannot carry a third thing, and with the eyebrow prepended
   it put the number, the heading and the note side by side across the top of
   every part. Here the head stacks instead, and `order` puts the four pieces in
   reading order — number, heading, company name or note, then the coloured
   rule closing the block. */
.about-sec .sec-head {
  display: grid;
  justify-items: start;
  gap: 0;
  margin-bottom: clamp(1rem, 2.6vw, 1.5rem);
}
.about-sec .sec-head > .about-step {
  order: 0;
}
.about-sec .sec-head h2 {
  order: 1;
  font-size: clamp(1.45rem, 2.6vw, 1.95rem);
}
.about-sec .sec-head .legal-name {
  order: 2;
}
.about-sec .sec-head p {
  order: 3;
  margin-top: 0.7rem;
  max-width: 68ch;
}
.about-sec .sec-head::after {
  order: 4;
  margin-top: 0.9rem;
}
/* The page-tone rules underline every section head on a toned page. On this one
   the coloured rule already closes the block, and a hairline half a centimetre
   under it is one line too many. */
body[data-page-tone] .about-sec .sec-head {
  border-bottom: 0;
  padding-bottom: 0;
}

/* The registered company name, under the heading it belongs to. Small, quiet,
   and set apart from the sentence beneath it: it is the sort of fact a reader
   wants to be able to find rather than one they want to read. */
.about-sec .legal-name {
  margin: 0.35rem 0 0;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--tone) 55%, var(--muted));
}

/* A note under a section heading, before the cards start. */
.about-sec .sec-note {
  margin: -0.2rem 0 0.1rem;
  color: var(--muted);
  font-size: 0.92rem;
  max-width: 62ch;
}

/* The company's plain facts — what the platform is, and where it reaches.
   A label above its answer on a telephone, and a proper two-column list from
   the width where the labels stop crowding the text. The labels are aligned to
   each other rather than to the paragraph, which is what makes a pair of them
   read as a table rather than as two more sentences. */
.profile-facts {
  display: grid;
  gap: 0.3rem 1rem;
  margin: 0;
  padding-top: 0.2rem;
  border-top: 1px solid var(--sand);
}
.profile-facts dt {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding-top: 0.7rem;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--tone) 60%, var(--muted));
}
.profile-facts dt svg {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--tone);
}
.profile-facts dd {
  margin: 0;
  padding-bottom: 0.55rem;
  font-size: 0.95rem;
  line-height: 1.55;
}
.profile-facts dd:not(:last-child) {
  border-bottom: 1px solid color-mix(in srgb, var(--sand) 60%, transparent);
}
@media (min-width: 720px) {
  .profile-facts {
    grid-template-columns: minmax(9rem, 15ch) 1fr;
    align-items: baseline;
  }
  .profile-facts dt {
    padding-top: 0.62rem;
  }
  .profile-facts dd {
    padding-top: 0.55rem;
  }
  /* With two columns the rule has to run under both halves of the row, and a
     border on the value alone would stop short of the label. */
  .profile-facts dt:not(:first-of-type),
  .profile-facts dd:not(:first-of-type) {
    border-top: 1px solid color-mix(in srgb, var(--sand) 60%, transparent);
  }
  .profile-facts dd:not(:last-child) {
    border-bottom: 0;
  }
}

/* A bullet whose first line is its own heading. The title and the sentence are
   two blocks in one cell rather than two list items, so the icon stays beside
   the title and the text underneath lines up with it instead of with the
   bullet — which is the difference between a list that reads as a leaflet and
   one that reads as a wall. */
.promise-body {
  display: grid;
  gap: 0.18rem;
  min-width: 0;
}
.promise-body b {
  font-size: 0.98rem;
  line-height: 1.35;
}
/* The sentence under the term. `.bi` is the translated kind, a plain `span` the
   kind written straight into the code — both are the quiet half of the bullet. */
.promise-body > .bi:not(b),
.promise-body > span {
  color: var(--muted);
  line-height: 1.6;
}

/* An outline list: bullets that open with the term they are about rather than
   with a sentence the reader has to finish before knowing whether it was for
   them. Roomier than a plain promise list, because each one is two lines of
   writing rather than half of one. */
.promises.outline li {
  align-items: flex-start;
  padding: 1rem 1.1rem;
  border-color: color-mix(in srgb, var(--tone, var(--good)) 20%, var(--sand));
}
.promises.outline svg {
  margin-top: 0.24rem;
}
.promises.pillars li {
  align-items: flex-start;
  padding: 1rem 1.1rem;
}
.promises.pillars svg {
  color: var(--tone, var(--good));
  margin-top: 0.28rem;
}
.about-sec .promises svg {
  color: var(--tone);
}
/* ── booking form ────────────────────────────────────────────────────────── */


.form {
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) + 6px);
  padding: clamp(1.3rem, 3vw, 2rem);
  box-shadow: var(--shadow-low);
}
.sec.tint .form {
  background: var(--surface);
}
.rows {
  display: grid;
  gap: 1rem;
}
.row2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1rem;
}

/* A subject heading inside a long form. Some panels hold several subjects at
   once — the platform, the three pillars, the service list — and without a rule
   between them the fields run together into one undifferentiated wall. */
.rows > .form-head {
  margin: 0.5rem 0 -0.25rem;
  padding-top: 0.95rem;
  border-top: 1px solid var(--sand);
  font-size: 1rem;
  color: var(--ink);
}
.rows > .form-head:first-child {
  padding-top: 0;
  border-top: 0;
}
@media (max-width: 620px) {
  .row2 {
    grid-template-columns: minmax(0, 1fr);
  }
}

.field {
  display: grid;
  gap: 0.35rem;
}
.field > span,
.field > label {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink);
}
.field .note {
  font-size: 0.78rem;
  color: var(--muted);
  font-weight: 400;
}
/* The same quiet aside used outside a field — beside a file picker, under a
   colour swatch. Kept separate from `.noterow` so a hint can never inherit the
   notification row's grid. */
.note {
  font-size: 0.78rem;
  color: var(--muted);
  font-weight: 400;
}
.field .req {
  color: var(--accent);
}

input[type='text'],
input[type='tel'],
input[type='email'],
input[type='password'],
input[type='date'],
input[type='time'],
input[type='number'],
input[type='url'],
select,
textarea {
  width: 100%;
  font: inherit;
  font-size: 0.98rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--sand-deep);
  border-radius: 12px;
  padding: 0.7rem 0.85rem;
  min-height: 46px;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.sec.tint input,
.sec.tint select,
.sec.tint textarea {
  background: var(--paper);
}
textarea {
  min-height: 108px;
  resize: vertical;
  line-height: 1.55;
}
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(192, 69, 36, 0.12);
}
input::placeholder,
textarea::placeholder {
  color: #9aa5a1;
}
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%235f6b67' stroke-width='2'%3E%3Cpath d='M5 8l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 18px;
  padding-inline-end: 2.2rem;
}


.form-foot {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--sand);
}
.form-foot .small {
  font-size: 0.82rem;
  color: var(--muted);
  max-width: 34ch;
}

.notice {
  border-radius: 14px;
  padding: 0.9rem 1.1rem;
  font-size: 0.92rem;
  border: 1px solid;
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
}
.notice svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 0.22rem;
}
.notice.info {
  background: var(--primary-soft);
  border-color: rgba(192, 69, 36, 0.2);
  color: #17423d;
}
.notice.good {
  background: #e8f3ec;
  border-color: rgba(47, 109, 79, 0.28);
  color: #1f5138;
}
.notice.bad {
  background: #fbeae6;
  border-color: rgba(163, 58, 42, 0.28);
  color: #7d2a1d;
}
.notice.warn {
  background: #fbf1de;
  border-color: rgba(154, 100, 16, 0.28);
  color: #6d4708;
}
.notice + .notice,
.rows .notice {
  margin: 0;
}

.done {
  text-align: center;
  padding: clamp(1.5rem, 4vw, 2.5rem) 1rem;
}
.done .ref {
  font-family: var(--display);
  font-size: 2rem;
  font-weight: 700;
  color: var(--primary);
  letter-spacing: 0.06em;
  margin-block: 0.6rem 0.3rem;
}
.done p {
  color: var(--muted);
  max-width: 44ch;
  margin-inline: auto;
}

/* ── contact & footer ────────────────────────────────────────────────────── */

/* ── "Reach us" ───────────────────────────────────────────────────────────
   The same block appears twice: on the dark strip at the foot of the front page
   and on a pale card on the about page. The colours below are the ones for the
   card; `.dark` overrides them where the ground is dark. Written this way round
   because a value inherited wrongly on a pale card is invisible text, which is
   worse than a slightly grey number on a dark strip.

   Three columns, each one labelled and each one a list of named facts rather
   than a heading over a stack of anonymous paragraphs. The address used to sit
   under the opening hours with nothing to say which was which; both now carry
   their own label and their own icon.
   ───────────────────────────────────────────────────────────────────────── */
.contact-in {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: clamp(1.2rem, 3vw, 2.4rem);
}
.contact-col {
  display: grid;
  align-content: start;
  gap: 0.5rem;
  min-width: 0;
}
.contact-in h3 {
  font-family: var(--body);
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.2rem;
}

/* The telephone number: the one thing on the page somebody may be reading in a
   hurry, so it is the largest thing in its column and a whole row is the tap
   target rather than the digits alone. */
.contact-in .big {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
  padding: 0.55rem 0.7rem;
  border-radius: 14px;
  border: 1px solid var(--sand);
  background: color-mix(in srgb, var(--primary) 4%, var(--paper));
  text-decoration: none;
  color: var(--ink);
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.contact-in .big:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--primary) 45%, transparent);
}
.big-ring {
  display: grid;
  place-items: center;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 12px;
  background: var(--primary-soft);
  color: var(--primary);
}
.big-ring svg {
  width: 19px;
  height: 19px;
}
.big-value {
  font-family: var(--display);
  font-size: clamp(1.15rem, 4.6vw, 1.42rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  min-width: 0;
  overflow-wrap: anywhere;
}
.contact-note {
  font-size: 0.84rem;
  color: var(--muted);
  margin-top: 0.15rem;
}

/* Every other way of reaching the office: email, website, the messengers, the
   street, the hours. One shape for all of them, so the column reads as a list
   rather than as a paragraph that happens to contain links. */
.reachrow,
.reachfact {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
  padding: 0.5rem 0.65rem;
  border-radius: 12px;
  border: 1px solid transparent;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.14s ease, background 0.14s ease;
}
.reachrow:hover {
  border-color: var(--sand);
  background: color-mix(in srgb, var(--primary) 5%, transparent);
}

/* An IMO number is the one way of reaching the office that has no link to
   follow, so its row is a `span` and answering the pointer would promise
   something pressing it cannot do. The class was already written on it and had
   never been given a rule. */
.reachrow.flat:hover {
  border-color: transparent;
  background: none;
}
.reachrow-ring {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: var(--primary-soft);
  color: var(--primary);
}
.reachrow-ring svg {
  width: 17px;
  height: 17px;
}
.reachrow-body {
  display: grid;
  min-width: 0;
}
.reachrow-net {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}
.reachrow-value {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.reachrow:hover .reachrow-value {
  color: var(--primary);
}
.contact-in p {
  color: var(--muted);
  font-size: 0.94rem;
}

/* The dark strip at the foot of the front page. Same markup, inverted ground. */
.dark .contact-in .big {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.06);
}
.dark .contact-in .big:hover {
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}
.dark .big-ring,
.dark .reachrow-ring {
  background: rgba(255, 255, 255, 0.1);
  color: var(--accent);
}
.dark .reachrow:hover {
  border-color: rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
}
.dark .reachrow-value {
  color: #fff;
}
.dark .reachrow:hover .reachrow-value {
  color: var(--accent);
}
.dark .reachrow-net,
.dark .contact-note {
  color: rgba(244, 241, 234, 0.72);
}
.dark .contact-in p {
  color: rgba(244, 241, 234, 0.82);
}

.dark {
  background: var(--primary);
  color: #f4f1ea;
}
.dark h2 {
  color: #fff;
}
.dark .sec-head p {
  color: rgba(244, 241, 234, 0.8);
}

.closing {
  font-family: var(--ethiopic-display);
  font-size: clamp(1.3rem, 3.4vw, 2rem);
  font-weight: 600;
  color: #fff;
  text-align: center;
  padding-block: clamp(2rem, 5vw, 3rem) 0;
}
.closing span {
  display: block;
  font-family: var(--display);
  font-size: 0.95rem;
  font-weight: 400;
  color: rgba(244, 241, 234, 0.75);
  margin-top: 0.6rem;
}

.foot {
  background: #0b3833;
  color: rgba(244, 241, 234, 0.72);
  font-size: 0.88rem;
}
.foot-in {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: center;
  justify-content: space-between;
  padding-block: 1.6rem;
  border-top: 1px solid rgba(244, 241, 234, 0.12);
}
.foot a {
  color: rgba(244, 241, 234, 0.9);
}
.foot .links {
  display: flex;
  gap: 1.2rem;
  flex-wrap: wrap;
}

/* ── states ──────────────────────────────────────────────────────────────── */

.empty {
  border: 1px dashed var(--sand-deep);
  border-radius: var(--radius);
  padding: clamp(1.6rem, 4vw, 2.6rem);
  text-align: center;
  color: var(--muted);
  background: var(--paper);
}
.empty h3 {
  font-size: 1.1rem;
  color: var(--ink);
  margin-bottom: 0.4rem;
}
.empty p {
  font-size: 0.92rem;
  max-width: 46ch;
  margin-inline: auto;
}
.dark .empty {
  background: rgba(255, 253, 249, 0.06);
  border-color: rgba(255, 253, 249, 0.25);
  color: rgba(244, 241, 234, 0.8);
}
.dark .empty h3 {
  color: #fff;
}

/* The heading and paragraph written into each page's own file, shown while the
   module that draws the real page is still on its way. It is what a crawler
   reads, what a link preview quotes and what somebody on a 2G connection has to
   look at for the first second — so it is styled properly rather than hidden.
   The page's script clears `#page` before it draws, which removes it. */
.prerender {
  margin-block-end: 1.25rem;
}
.prerender h1 {
  font-family: var(--display);
  font-size: clamp(1.5rem, 4.5vw, 2.15rem);
  line-height: 1.15;
  margin: 0 0 0.6rem;
  color: var(--ink);
}
.prerender p {
  margin: 0 0 0.55rem;
  max-width: 62ch;
  color: var(--muted);
  line-height: 1.6;
}
.prerender a {
  color: var(--primary);
  font-weight: 600;
}
.dark .prerender h1 {
  color: #fff;
}

.skeleton {
  border-radius: var(--radius);
  background: linear-gradient(100deg, var(--sand) 20%, #f0eae0 40%, var(--sand) 60%);
  background-size: 240% 100%;
  animation: sweep 1.4s ease-in-out infinite;
  min-height: 168px;
  border: 1px solid var(--sand);
}
@keyframes sweep {
  from {
    background-position: 130% 0;
  }
  to {
    background-position: -30% 0;
  }
}

.toast-host {
  position: fixed;
  inset: auto 1rem 1rem auto;
  z-index: 80;
  display: grid;
  gap: 0.6rem;
  max-width: min(380px, calc(100vw - 2rem));
}
.toast {
  border-radius: 14px;
  padding: 0.85rem 1.05rem;
  font-size: 0.92rem;
  font-weight: 600;
  box-shadow: var(--shadow-high);
  background: var(--ink);
  color: #fff;
  animation: rise 0.22s ease;
}
.toast.good {
  background: #1f5138;
}
.toast.bad {
  background: #7d2a1d;
}
/* Saved, but with something the person needs to know — "your profile stays
   private until an administrator approves it". Neither a success nor a failure,
   and it should not be dressed as either. */
.toast.warn {
  background: #7a4a10;
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* ── the arrival card ──────────────────────────────────────────────────────
   A notification that landed while somebody was looking at the page. It is a
   link, so it must look pressable and must not be mistaken for a toast, which
   is only ever a statement about something they just did. Sits above the bottom
   bar rather than over it: the bar is how they leave, and covering it would make
   the card feel like a trap. */
.notecall-host {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: calc(var(--bar-bottom) + var(--safe-bottom) + 0.85rem);
  z-index: 85;
  display: flex;
  align-items: stretch;
  gap: 0.4rem;
  max-width: min(460px, calc(100vw - 2rem));
  margin-inline: auto;
  animation: rise 0.24s var(--spring, ease);
}
.notecall {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.8rem 0.95rem;
  border-radius: 16px;
  background: var(--ink);
  color: #fff;
  text-decoration: none;
  box-shadow: var(--shadow-high);
}
.notecall-icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 12px;
  background: color-mix(in srgb, var(--primary) 78%, #fff);
}
.notecall-icon svg {
  width: 20px;
  height: 20px;
}
.notecall-text {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
}
.notecall-text b {
  font-size: 0.95rem;
}
.notecall-text small {
  opacity: 0.82;
  font-size: 0.82rem;
  /* Two lines at most. A message longer than that belongs in the bell, not in
     something that is covering the page. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.notecall-go {
  margin-top: 0.15rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--glow, #7fdccb);
}
.notecall-close {
  flex: none;
  width: 40px;
  border: 0;
  border-radius: 14px;
  background: var(--ink);
  color: #fff;
  opacity: 0.85;
  box-shadow: var(--shadow-high);
  cursor: pointer;
}
.notecall-close svg {
  width: 16px;
  height: 16px;
}
@media (min-width: 900px) {
  .notecall-host {
    left: auto;
    right: 1.2rem;
    bottom: 1.2rem;
  }
}

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Admin panel and clinician portal
   ──────────────────────────────────────────────────────────────────────────── */

.gate {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
}
.gate-card {
  width: min(430px, 100%);
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) + 6px);
  padding: clamp(1.5rem, 4vw, 2.3rem);
  box-shadow: var(--shadow-high);
}
.gate-card .brand {
  margin-bottom: 1.4rem;
}
.gate-card h1 {
  font-size: 1.5rem;
  margin-bottom: 0.3rem;
}
.gate-card .sub {
  color: var(--muted);
  font-size: 0.92rem;
  margin-bottom: 1.4rem;
}


.shell {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  min-height: 100dvh;
}
@media (max-width: 900px) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }
}

.side {
  background: var(--primary);
  color: rgba(244, 241, 234, 0.82);
  padding: 1.4rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100dvh;
  overflow-y: auto;
}
@media (max-width: 900px) {
  .side {
    position: static;
    max-height: none;
  }
}
.side .brand .name {
  color: #fff;
}
.side .brand .name-en {
  color: var(--accent);
}
.side nav {
  display: grid;
  gap: 0.15rem;
}
.side nav button {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  font-size: 0.94rem;
  color: rgba(244, 241, 234, 0.8);
  text-align: start;
  padding: 0.62rem 0.8rem;
  border-radius: 10px;
  cursor: pointer;
}
.side nav button svg {
  width: 17px;
  height: 17px;
  flex: none;
  opacity: 0.8;
}
.side nav button:hover {
  background: rgba(255, 253, 249, 0.09);
  color: #fff;
}
.side nav button[aria-current='true'] {
  background: var(--paper);
  color: var(--primary);
  font-weight: 700;
}
.side nav button[aria-current='true'] svg {
  opacity: 1;
}
.side nav button .count {
  margin-inline-start: auto;
  font-size: 0.76rem;
  font-weight: 700;
  background: var(--accent);
  color: #fff;
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
}
/* The portal's link across to the admin panel, for someone who holds both. */

.side .who {
  margin-top: auto;
  font-size: 0.82rem;
  border-top: 1px solid rgba(255, 253, 249, 0.16);
  padding-top: 1rem;
  display: grid;
  gap: 0.5rem;
}
.side .who b {
  color: #fff;
  display: block;
  font-weight: 700;
  word-break: break-word;
}

.main {
  padding: clamp(1.2rem, 3vw, 2.2rem);
  min-width: 0;
}
.main > header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
.main > header h1 {
  font-size: clamp(1.5rem, 3vw, 2.05rem);
}
.main > header p {
  color: var(--muted);
  font-size: 0.93rem;
  max-width: 58ch;
}
.head-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.panel {
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: var(--radius);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
  margin-bottom: 1.2rem;
}
.panel > h2 {
  font-size: 1.15rem;
  margin-bottom: 0.35rem;
}
.panel > .hint {
  color: var(--muted);
  font-size: 0.88rem;
  margin-bottom: 1.1rem;
}
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(158px, 100%), 1fr));
  gap: 0.8rem;
  margin-bottom: 1.2rem;
}
.stat {
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: 14px;
  padding: 1rem 1.1rem;
}
.stat b {
  display: block;
  font-family: var(--display);
  font-size: 1.8rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--primary);
}
.stat span {
  font-size: 0.82rem;
  color: var(--muted);
}
.stat.flag b {
  color: var(--accent);
}

/* A number alone does not say how close to a ceiling it is. The mint allowance
   is the one figure on the panel where that matters — an administrator wants to
   see at a glance that today is nearly spent — so it gets a bar. It turns warm
   past three quarters, which is the point at which the next mint might not fit. */
.meter {
  height: 10px;
  border-radius: 6px;
  background: rgba(26, 36, 22, 0.09);
  overflow: hidden;
}
.meter i {
  display: block;
  height: 100%;
  width: var(--done, 0%);
  border-radius: 6px;
  background: linear-gradient(90deg, var(--primary), var(--glow));
  transition: width 0.5s ease;
}
.meter.hot i {
  background: linear-gradient(90deg, var(--warn), var(--danger));
}

.tablewrap {
  /* A table that will not fit scrolls inside its own box. `min-width: 0` is the
     half of that which is easy to forget: as a grid or flex child this box
     defaults to the width of its content, so without it the table pushes the
     page wide instead of scrolling within it. */
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--sand);
  border-radius: 14px;
  background: var(--paper);
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  min-width: 620px;
}
thead th {
  text-align: start;
  font-size: 0.74rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 700;
  padding: 0.7rem 0.9rem;
  background: var(--surface);
  border-bottom: 1px solid var(--sand);
  white-space: nowrap;
  position: sticky;
  top: 0;
}
tbody td {
  padding: 0.72rem 0.9rem;
  border-bottom: 1px solid var(--surface);
  vertical-align: top;
}
tbody tr:last-child td {
  border-bottom: 0;
}
tbody tr:hover td {
  background: var(--surface);
}
td .who-name {
  font-weight: 700;
  display: block;
}
td .sub,
td small {
  color: var(--muted);
  font-size: 0.82rem;
  display: block;
}
td.num {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
td.acts {
  white-space: nowrap;
  text-align: end;
}
.mono {
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 0.86em;
  letter-spacing: 0.02em;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--sand-deep);
  color: var(--muted);
  white-space: nowrap;
}
.pill.pending {
  background: #fbf1de;
  border-color: rgba(154, 100, 16, 0.3);
  color: var(--warn);
}
.pill.live,
.pill.confirmed,
.pill.completed,
.pill.paid {
  background: #e8f3ec;
  border-color: rgba(47, 109, 79, 0.3);
  color: var(--good);
}
.pill.off,
.pill.cancelled,
.pill.urgent {
  background: #fbeae6;
  border-color: rgba(163, 58, 42, 0.3);
  color: var(--bad);
}
.pill.assigned,
.pill.progress {
  background: var(--primary-soft);
  border-color: rgba(192, 69, 36, 0.22);
  color: var(--primary);
}

.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 600;
}

/* A column of switches — the welcome card's buttons, and anywhere else a panel
   offers a list of things to show or hide. Two columns where there is room. */
.switchgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0.6rem 1.2rem;
  padding: 0.4rem 0 0.2rem;
}
.switch input {
  appearance: none;
  width: 42px;
  height: 24px;
  border-radius: 999px;
  background: var(--sand-deep);
  position: relative;
  cursor: pointer;
  transition: background 0.16s ease;
  flex: none;
  margin: 0;
}
.switch input::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.16s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.switch input:checked {
  background: var(--good);
}
.switch input:checked::after {
  transform: translateX(18px);
}

/* Editing happens in a side drawer so the list stays visible behind it. */
.drawer {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: none;
}
.drawer[open] {
  display: block;
}
.drawer .veil {
  position: absolute;
  inset: 0;
  background: rgba(11, 56, 51, 0.42);
  backdrop-filter: blur(2px);
  border: 0;
  padding: 0;
  width: 100%;
  cursor: pointer;
}
.drawer .sheet {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  width: min(560px, 100%);
  background: var(--surface);
  border-inline-start: 1px solid var(--sand);
  box-shadow: var(--shadow-high);
  overflow-y: auto;
  padding: clamp(1.2rem, 3vw, 1.9rem);
  animation: slide 0.22s ease;
}
@keyframes slide {
  from {
    transform: translateX(24px);
    opacity: 0.6;
  }
}
.drawer .sheet > header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.3rem;
}
.drawer .sheet h2 {
  font-size: 1.3rem;
}
.drawer .sheet .sub {
  color: var(--muted);
  font-size: 0.88rem;
}
.iconbtn {
  border: 1px solid var(--sand-deep);
  background: var(--paper);
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--muted);
  flex: none;
}
.iconbtn:hover {
  color: var(--ink);
  border-color: var(--muted);
}
.iconbtn svg {
  width: 17px;
  height: 17px;
}

.photo-row {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.photo-row .avatar {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--primary-soft);
  border: 1px solid var(--sand-deep);
  flex: none;
}
.photo-row .stack {
  display: grid;
  gap: 0.35rem;
}
input[type='file'] {
  font: inherit;
  font-size: 0.86rem;
  color: var(--muted);
}
input[type='file']::file-selector-button {
  font: inherit;
  font-weight: 700;
  font-size: 0.86rem;
  border: 1px solid var(--sand-deep);
  background: var(--paper);
  color: var(--primary);
  border-radius: 999px;
  padding: 0.42rem 0.9rem;
  margin-inline-end: 0.7rem;
  cursor: pointer;
}

.filters {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  align-items: center;
}
.filters input,
.filters select {
  min-height: 40px;
  padding-block: 0.45rem;
  font-size: 0.9rem;
  width: auto;
  min-width: 180px;
}

.grid2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 1.2rem;
  align-items: start;
}

.trail {
  display: grid;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}
.trail li {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  gap: 1rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--surface);
  font-size: 0.89rem;
}
.trail li:last-child {
  border-bottom: 0;
}
.trail time {
  color: var(--muted);
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}
.trail .act {
  font-weight: 700;
  color: var(--primary);
}
.trail .by {
  color: var(--muted);
  font-size: 0.83rem;
}
@media (max-width: 620px) {
  .trail li {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
  }
}

.token {
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  background: var(--primary-soft);
  border: 1px dashed rgba(192, 69, 36, 0.35);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  word-break: break-all;
  user-select: all;
}

/* The ten recovery codes, and the box that asks for a six-digit one. Both are
   monospaced and widely spaced because both are read off one screen and typed
   into another, often by somebody reading them aloud to somebody else. */
.codegrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.45rem;
  margin: 0.8rem 0;
}
.codegrid code {
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  text-align: center;
  padding: 0.5rem 0.4rem;
  border: 1px solid var(--sand);
  border-radius: 10px;
  background: var(--surface);
  user-select: all;
  overflow-wrap: anywhere;
}
.twofa {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.twofa input {
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 1.25rem;
  letter-spacing: 0.3em;
  text-align: center;
}
.twofa-host {
  max-width: 100%;
}
@media (max-width: 480px) {
  .codegrid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.swatch-row {
  display: flex;
  gap: 0.6rem;
  align-items: center;
}
input[type='color'] {
  width: 46px;
  height: 46px;
  padding: 3px;
  border: 1px solid var(--sand-deep);
  border-radius: 12px;
  background: var(--paper);
  cursor: pointer;
}

.divider {
  height: 1px;
  background: var(--sand);
  border: 0;
  margin-block: 1.3rem;
}

.count-note {
  font-size: 0.84rem;
  color: var(--muted);
}

/* Patient details in the portal drawer: a plain definition list reads faster
   than a form for information nobody is meant to edit. */
.facts {
  display: grid;
  grid-template-columns: minmax(7rem, auto) 1fr;
  gap: 0.45rem 1rem;
  font-size: 0.92rem;
  margin: 0;
}
.facts dt {
  color: var(--muted);
  font-weight: 600;
}
.facts dd {
  margin: 0;
  font-weight: 600;
}
.facts dd a {
  color: var(--primary);
}


/* ═══════════════════════════════════════════════════════════════════════════
   THE APPLICATION SHELL
   ───────────────────────────────────────────────────────────────────────────
   Every page except the public front page carries two fixed bars: a top bar
   with the brand, the language switch, the notification bell and the sign-in
   or sign-out icon, and a bottom bar of large tap targets. That is the shape
   of a telephone application, and it is deliberate — most of the people this
   site is for arrive on a telephone, and a bar that is always there means the
   way out of any page is always in the same place.

   Both bars are raised: a lit hairline along the top edge, a dark underside,
   and a shadow that falls away from the page. `--lift` scales all of it, so
   the administrator's `flat` setting collapses the relief without changing a
   single rule below.
   ══════════════════════════════════════════════════════════════════════════ */

body.app {
  /* Room for the two bars, plus the home-indicator strip on a modern phone. */
  padding-top: var(--bar-top);
  padding-bottom: calc(var(--bar-bottom) + var(--safe-bottom));
}

.appbar {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 60;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--navbar) 88%, #fff) 0%,
    var(--navbar) 46%,
    color-mix(in srgb, var(--navbar) 78%, #000) 100%
  );
  color: #eef5f2;
  /* The lit edge and the dark underside are what make it read as a raised
     object rather than a coloured stripe. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, calc(0.28 * var(--lift))),
    inset 0 -1px 0 rgba(0, 0, 0, calc(0.4 * var(--lift))),
    0 calc(10px * var(--lift)) calc(30px * var(--lift)) -14px rgba(8, 48, 44, 0.75);
}
.appbar-in {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  max-width: var(--band);
  margin-inline: auto;
  padding-inline: clamp(0.6rem, 3vw, 2rem);
}

.appbar.top {
  top: 0;
  border-bottom: 0;
  backdrop-filter: none;
}
.appbar.top .appbar-in {
  min-height: var(--bar-top);
  /* One line, always. The brand shrinks and the buttons shrink; the row itself
     never breaks, so the bar is one bar at every width and `--bar-top` is the
     whole of its height. */
  flex-wrap: nowrap;
}
.appbar.bottom {
  bottom: 0;
  padding-bottom: var(--safe-bottom);
}
.appbar.bottom .appbar-in {
  min-height: var(--bar-bottom);
  justify-content: space-between;
  gap: 0.15rem;
}

/* The brand inside the dark bar, which needs its own colours. */
.appbar .brand {
  gap: 0.55rem;
  margin-inline-end: 0.35rem;
}
.appbar .brand .mark {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: #fff;
  padding: 2px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}
.appbar .brand .name {
  color: #fff;
  font-size: 1.08rem;
}
.appbar .brand .name-en {
  color: var(--glow);
  font-size: 0.62rem;
  letter-spacing: 0.2em;
}

/* ── the animated wordmark ───────────────────────────────────────────────────
   The name in the top bar is set by the administrator, and so is the effect it
   plays: `navbar_animation` in the brand settings picks one of the classes
   below. Every one of them animates only colour, opacity or transform, so none
   of them moves the bar around or costs a layout, and all of them stop dead
   under `prefers-reduced-motion`. */

.wordmark {
  position: relative;
}
.wordmark .name {
  display: inline-block;
  will-change: auto;
}
.wordmark-sub {
  margin-top: 0.1rem;
  font-family: var(--display);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

/* A light travelling across the letters — the default, and the quietest. */
.wordmark.anim-shimmer .name {
  background: linear-gradient(100deg, #fff 20%, var(--gold) 42%, var(--glow) 58%, #fff 80%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: mark-shimmer 5.5s ease-in-out infinite;
}
@keyframes mark-shimmer {
  0%,
  100% {
    background-position: 180% 0;
  }
  50% {
    background-position: -40% 0;
  }
}

/* A slow heartbeat, in keeping with the mark. */
.wordmark.anim-pulse .name {
  animation: mark-pulse 3.2s ease-in-out infinite;
  transform-origin: left center;
}
@keyframes mark-pulse {
  0%,
  100% {
    transform: scale(1);
  }
  45% {
    transform: scale(1.06);
  }
  60% {
    transform: scale(0.99);
  }
}

/* A tilt that rocks back, like a hand waving. */
.wordmark.anim-wave .name {
  animation: mark-wave 4s ease-in-out infinite;
  transform-origin: left bottom;
}
@keyframes mark-wave {
  0%,
  70%,
  100% {
    transform: rotate(0deg);
  }
  78% {
    transform: rotate(-3.5deg);
  }
  86% {
    transform: rotate(2.5deg);
  }
  94% {
    transform: rotate(-1deg);
  }
}

/* A halo that swells and fades. */
.wordmark.anim-glow .name {
  animation: mark-glow 3.6s ease-in-out infinite;
}
@keyframes mark-glow {
  0%,
  100% {
    text-shadow: 0 0 0 rgba(95, 179, 38, 0);
  }
  50% {
    text-shadow: 0 0 14px rgba(95, 179, 38, 0.85), 0 0 26px rgba(242, 160, 58, 0.35);
  }
}

/* The name arriving from the left, once per page load rather than forever. */
.wordmark.anim-slide .name,
.wordmark.anim-slide .name-en {
  animation: mark-slide 0.75s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
.wordmark.anim-slide .name-en {
  animation-delay: 0.12s;
}
@keyframes mark-slide {
  from {
    opacity: 0;
    transform: translateX(-14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* A cursor blinking beside the name, for the typewriter effect. */
.wordmark.anim-type .name::after {
  content: '';
  display: inline-block;
  width: 2px;
  height: 0.95em;
  margin-inline-start: 3px;
  vertical-align: -0.1em;
  background: var(--gold);
  animation: mark-caret 1.1s steps(1) infinite;
}
@keyframes mark-caret {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wordmark .name,
  .wordmark .name-en,
  .wordmark .name::after {
    animation: none !important;
  }
  .wordmark.anim-shimmer .name {
    background: none;
    -webkit-text-fill-color: currentColor;
    color: #fff;
  }
}

/* ── the buttons on the bars ─────────────────────────────────────────────── */

.barbtn {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.16rem;
  min-width: 46px;
  min-height: 46px;
  padding: 0.34rem 0.5rem;
  border: 0;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.06);
  color: #dfeae6;
  font: inherit;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.14s ease, background 0.14s ease, color 0.14s ease;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, calc(0.16 * var(--lift))),
    0 calc(2px * var(--lift)) calc(4px * var(--lift)) rgba(0, 0, 0, calc(0.32 * var(--lift)));
}
.barbtn svg {
  width: 21px;
  height: 21px;
  flex: none;
}
.barbtn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  transform: translateY(-1px);
}
.barbtn:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.4);
}
.barbtn[aria-current='page'],
.barbtn.on {
  background: linear-gradient(180deg, color-mix(in srgb, var(--glow) 34%, transparent), rgba(255, 255, 255, 0.05));
  color: #fff;
}
.barbtn[aria-current='page']::after {
  content: '';
  position: absolute;
  bottom: 3px;
  width: 16px;
  height: 3px;
  border-radius: 3px;
  background: var(--glow);
}
.appbar.bottom .barbtn {
  flex: 1 1 0;
  background: transparent;
  box-shadow: none;
}
.appbar.bottom .barbtn:hover {
  background: rgba(255, 255, 255, 0.1);
}
/* The bottom bar's middle button — booking, the thing the site is for. */
.appbar.bottom .barbtn.hot {
  flex: 0 0 auto;
  min-width: 58px;
  margin-top: -18px;
  border-radius: 50%;
  width: 58px;
  height: 58px;
  gap: 0;
  font-size: 0;
  background: radial-gradient(120% 120% at 50% 0%, color-mix(in srgb, var(--accent) 82%, #fff), var(--accent));
  color: #fff;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 calc(6px * var(--lift)) calc(16px * var(--lift)) -4px rgba(0, 0, 0, 0.55);
}
.appbar.bottom .barbtn.hot svg {
  width: 26px;
  height: 26px;
}
.appbar.bottom .barbtn.hot span {
  display: none;
}

/* ── colour-coded navigation ──────────────────────────────────────────────
   Each button wears the tone the administrator gave it, so the two bars read
   as a row of distinct destinations rather than a row of identical grey
   shapes. On a small screen colour is recognised before a word is read, which
   is the whole argument for doing this to a navigation bar.

   The tone is a lit rim and a wash rather than a solid fill: a bar of eight
   saturated blocks would fight the page above it, whereas this leaves the bar
   one dark surface with eight coloured lamps set into it. The open page is the
   exception and wears its colour outright, so there is never a question which
   of the eight you are looking at.

   These sit after the `.appbar.bottom` rules and are scoped to `.appbar`
   deliberately: those rules flatten every button in the bottom bar, and a
   colour that arrives first is a colour that gets erased. `--lift` scales the
   relief, so the depth control in the panel moves these with everything else.
   ───────────────────────────────────────────────────────────────────────── */
.appbar .barbtn[data-tone] {
  background: linear-gradient(180deg, color-mix(in srgb, var(--tone) 52%, transparent), rgba(0, 0, 0, 0.2));
  color: #f7fbf9;
  /* The cap along the top edge is the first of the four shadows: a 3px line of
     the button's own colour at full strength. It is what makes the row read as
     colour-coded rather than as five dark buttons that happen to be tinted — a
     line of pure tone is recognised before any wash behind an icon is. An inset
     shadow rather than a border so that it costs no height: these buttons are
     already down to 36px on a phone held sideways, and 3px taken out of the
     padding there is 3px taken off the icon. */
  box-shadow:
    inset 0 3px 0 color-mix(in srgb, var(--tone) 82%, #fff),
    inset 0 calc(-1px * var(--lift)) 0 rgba(0, 0, 0, calc(0.42 * var(--lift))),
    0 calc(2px * var(--lift)) calc(5px * var(--lift)) rgba(0, 0, 0, calc(0.34 * var(--lift)));
}
/* The colour is carried three times over: in the cap, in the face, and in the
   glyph. The glyph is the part of a bar button the eye lands on first, so it
   takes the most tone the dark bar behind it will still let it be read
   against — half the tone and half white, up from a wash that was nearly all
   white and read as grey on every colour but the gold. */
.appbar .barbtn[data-tone] svg {
  color: color-mix(in srgb, var(--tone) 52%, #fff);
  filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.3));
}

.appbar .barbtn[data-tone]:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--tone) 56%, transparent), rgba(0, 0, 0, 0.1));
  color: #fff;
}
.appbar .barbtn[data-tone]:hover svg {
  color: #fff;
}
.appbar .barbtn[data-tone][aria-current='page'],
.appbar .barbtn[data-tone].on {
  background: linear-gradient(180deg, color-mix(in srgb, var(--tone) 94%, #fff), var(--tone));
  color: var(--tone-ink);
}
.appbar .barbtn[data-tone][aria-current='page'] svg,
.appbar .barbtn[data-tone].on svg {
  color: var(--tone-ink);
}
.appbar .barbtn[data-tone][aria-current='page']::after {
  background: var(--tone-ink);
}
/* The raised middle button keeps its lift and takes the tone as a full face:
   it is the one button in the bar that is meant to be reached for. */
.appbar.bottom .barbtn.hot[data-tone] {
  background: radial-gradient(
    120% 120% at 50% 0%,
    color-mix(in srgb, var(--tone) 82%, #fff),
    color-mix(in srgb, var(--tone) 88%, #000)
  );
  color: var(--tone-ink);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 calc(6px * var(--lift)) calc(16px * var(--lift)) -4px rgba(0, 0, 0, 0.55);
}
.appbar.bottom .barbtn.hot[data-tone] svg {
  color: var(--tone-ink);
}
/* The menu sheet's rows take the same colours, so a destination is the same
   colour wherever it is reached from. */
.choice[data-tone] .ring {
  background: linear-gradient(180deg, color-mix(in srgb, var(--tone) 22%, transparent), transparent);
  border-color: color-mix(in srgb, var(--tone) 46%, transparent);
  color: var(--tone);
}

/* The label under a bar icon. The shell writes one language into it.
   `max-width: 100%` is the important half: without it an eight-character label
   is eight characters wide whatever the button is, and on a 320px screen five
   of those together are wider than the bar — which is how a button ends up
   half off the edge instead of merely having a shortened label. */
.barbtn span {
  display: block;
  max-width: min(8ch, 100%);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The bottom bar shares its width between its buttons and never scrolls: every
   destination on it is reachable without a gesture, at every width. */
.appbar.bottom .appbar-in {
  flex-wrap: nowrap;
  overflow: visible;
}

/* Five buttons, all of them on the screen, on the narrowest telephone still in
   use.

   `min-width: 0` is the load-bearing line and it is overriding the 46px every
   bar button otherwise carries. A flex child refuses by default to shrink below
   its own content, so five buttons whose combined minimum is wider than the
   screen do not compress — the row grows past the viewport and the last button
   or two end up beyond the right-hand edge, present in the markup and invisible
   to the reader. Allowing them to shrink is what guarantees the opposite: the
   width is divided between them, whatever the width is.

   The tap target does not go with it. `min-height` is untouched, so a button
   that has narrowed is still 46px tall and still comfortably hit; and the words
   ellipsize rather than wrap, so a narrowed button is a shorter label and never
   a taller bar. */
.appbar.bottom .barbtn {
  min-width: 0;
}
.appbar.bottom .barbtn span {
  max-width: 100%;
}

/* The wide-screen shape of both bars is set further down, under "the two bars
   on a desktop". It is one block rather than a rule here and a rule there
   because the two bars have to be fixed together: the lower one becomes a
   floating dock, and the room the page has to leave for it is decided by the
   same numbers. */

/* The two bars are the way out of every page, so neither is ever allowed to
   leave the screen. Written once, here, and deliberately blunt: a page that
   sets a transform, an opacity or a visibility on its own chrome — a leftover
   scroll-away, a half-finished animation, a stylesheet cached from an older
   build — would take the navigation with it, and a reader on a telephone with
   no navigation has no way back to the front page but the browser's own
   button. */
.appbar.top,
.appbar.bottom {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

/* The upper controls: Quick Actions, language, notifications when signed in,
   and account. The launcher opens the full dock instead of duplicating every
   destination in this fixed-width row. The brand is the only flexible item, so
   all controls retain a complete tap target at every width. */
.quickset {
  display: flex;
  align-items: center;
  gap: 0.28rem;
  min-width: 0;
  flex-wrap: nowrap;
}
.quicktools {
  /* Pushed to the right so the brand keeps the left edge and the three controls
     sit together against it, reading as one block. */
  margin-inline-start: auto;
  justify-content: flex-end;
  flex: none;
}
.quickset .barbtn {
  flex: none;
}
.quickset .barbtn span {
  max-width: min(9ch, 100%);
}
/* The divider between the top bar's destinations and its controls. It is drawn
   only on a wide bar — at a telephone's width the five buttons are already
   shoulder to shoulder and a sixth mark between them is one thing too many. */
.bar-split {
  display: none;
}

/* ── the top bar on a narrow screen ───────────────────────────────────────
   One line, at every width, with nothing dropped from it. The brand keeps the
   left edge and is the only part allowed to truncate; every upper control keeps
   its full tap area throughout.
   ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 780px) {
  .appbar.top .appbar-in {
    flex-wrap: nowrap;
    gap: 0.3rem;
    padding-inline: 0.6rem;
  }
  /* Whatever the shell marks `hide-sm` — today the marquee under the wordmark —
     goes, because this is the width at which the bar has to fit a brand and
     three controls on one line. The rule that used to do this was written
     inside the old top bar's own container, so it stopped matching the moment
     that bar was replaced and the class quietly did nothing. */
  .hide-sm {
    display: none;
  }
  .appbar.top .brand {
    flex: 0 1 auto;
    min-width: 0;
    margin-inline-end: 0;
  }
  .quickset {
    gap: 0.22rem;
  }
  /* Still `auto`. Anything else here would win over the base rule and pull the
     upper controls back against the brand, leaving the right-hand third of the
     bar empty and the buttons nowhere near the thumb that reaches for them. */
  .quicktools {
    margin-inline-start: auto;
  }
  .appbar.top .barbtn {
    min-width: 44px;
    min-height: 44px;
    padding: 0.28rem 0.34rem;
    gap: 0.1rem;
    font-size: 0.58rem;
  }
  .appbar.top .barbtn svg {
    width: 20px;
    height: 20px;
  }
  .appbar.top .barbtn span {
    max-width: min(6ch, 100%);
  }
}

/* Below this the words under the icons are what will not fit, so the words go
   and the buttons stay. Every one of them carries `aria-label` written by the
   shell, so a button with no visible word is still announced by name and still
   shows one on a long press — which is the difference between a label that has
   been shortened and a button that has been hidden. Nothing is hidden. */
@media (max-width: 420px) {
  /* The label, and only the label. `:not(.tally)` keeps the unread count on the
     bell, which is a `span` in the same button and is the one thing on this bar
     that must never disappear. */
  .appbar.top .barbtn > span:not(.tally) {
    display: none;
  }
  .appbar.top .barbtn {
    width: 42px;
    min-width: 42px;
    padding-inline: 0;
  }
  .appbar.top .barbtn svg {
    width: 21px;
    height: 21px;
  }
  /* The underline that marks the open page sits where the word used to be. */
  .appbar.top .barbtn[aria-current='page']::after {
    bottom: 4px;
    width: 14px;
  }
}

/* The narrowest screens still in use — a 320px telephone, or a window dragged
   down to a column. The upper controls are five now for a signed-in reader —
   the feed, the about page, the language, the bell and the account — so they
   come in a little from 40px to keep the site's name a readable strip of the
   bar rather than a logo and one letter. 38px is still comfortably over the
   24px minimum a pointer target has to be, and nothing is dropped. */
@media (max-width: 380px) {
  .appbar.top .appbar-in {
    padding-inline: 0.4rem;
    gap: 0.18rem;
  }
  .quickset {
    gap: 0.1rem;
  }
  .quicktools {
    margin-inline-start: auto;
  }
  .appbar.top .barbtn {
    width: 38px;
    min-width: 38px;
    min-height: 38px;
  }
  .appbar.top .barbtn svg {
    width: 20px;
    height: 20px;
  }
}

/* A very short screen — a phone held sideways — has the same single line, only
   shorter. Nothing is removed here either. */
@media (max-height: 480px) and (max-width: 900px) {
  html:root {
    --bar-top: 56px;
  }
  .appbar.top .barbtn {
    min-height: 36px;
  }
}

/* ── the two bars on a desktop ────────────────────────────────────────────
   Both bars were drawn for a telephone and then simply left at that width, and
   a telephone's chrome stretched across 1400px is the wrong object twice over.

   The upper bar was five 46px squares with a five-point label stacked under
   each glyph, pinned to the right of an otherwise empty 1200px band, every
   word cut off at eight characters by a rule that exists for a 320px screen.
   The lower bar was worse: a full-width dark band across the foot of the
   window with a 620px huddle of buttons marooned in the middle of it and a
   circular booking button poking out of its top edge — a shape that means
   something under a thumb and nothing at all under a mouse.

   What they become here is what each of them is actually for at this width:

     · The upper bar becomes a proper header. The brand keeps the left edge at
       a size worth reading, the two destinations become worded links laid out
       icon-beside-word, and the language, the bell and the account become
       round icon controls behind a hairline on the right — which is the shape
       of every desktop header a reader has ever used. The controls keep their
       names in `aria-label` and `title`, written by the shell, so nothing is
       lost by not printing them.

     · The lower bar stops being a band and becomes a floating dock: a centred
       pill standing clear of the bottom edge, the width of its own contents,
       with the booking button inside the row as a worded pill rather than a
       circle cut into the top of a stripe. The strip either side of it is made
       transparent to the pointer, so the page underneath is still clickable
       right down to the last line — which a full-width invisible bar was not.

   Nothing is dropped and nothing moves: every destination that is on the bars
   at 360px is on them here, in the same order.
   ───────────────────────────────────────────────────────────────────────── */
@media (min-width: 781px) {
  html:root {
    --bar-top: 72px;
    /* The dock's own height plus the air above and below it. This is what the
       page leaves clear at the foot, and what the arrival card and the
       quick-action dock measure themselves against, so all three move
       together. */
    --bar-bottom: 84px;
  }

  /* ── the header ──────────────────────────────────────────────────────── */

  .appbar.top .appbar-in {
    gap: 1rem;
  }
  .appbar.top .brand .mark {
    width: 40px;
    height: 40px;
    border-radius: 12px;
  }
  .appbar.top .brand .name {
    font-size: 1.24rem;
  }
  .appbar.top .brand .name-en {
    font-size: 0.68rem;
  }
  /* The destinations sit beside the brand, where a desktop header puts its
     navigation, and the controls are sprung away to the far right by the
     hairline between them. Right-aligning the whole group — which is what a
     telephone's bar does, because a thumb reaches that edge — left the middle
     two thirds of a 1200px header empty and every button as far from the
     reader's eye as the layout could put it. */
  .quicktools {
    flex: 1 1 auto;
    margin-inline-start: 0;
    justify-content: flex-start;
    gap: 0.4rem;
  }

  /* A worded destination: icon beside the word, not stacked over it, and the
     word set at a size a desktop reader is not asked to squint at. The 8ch
     cap that keeps five buttons on a 320px bar is lifted — there is room for
     the whole word here, and a header that says "Notificati…" reads as
     broken rather than as compact. */
  .appbar.top .barbtn.barlink {
    flex-direction: row;
    gap: 0.5rem;
    min-height: 42px;
    padding: 0.4rem 0.95rem;
    border-radius: 12px;
    font-size: 0.86rem;
    letter-spacing: 0;
  }
  .appbar.top .barbtn.barlink span {
    max-width: none;
  }
  .appbar.top .barbtn.barlink[aria-current='page']::after {
    bottom: 5px;
    width: 22px;
  }

  /* A control: round, glyph only, its name spoken rather than printed. */
  .appbar.top .barbtn.barctl {
    width: 42px;
    min-width: 42px;
    min-height: 42px;
    padding: 0;
    border-radius: 50%;
  }
  .appbar.top .barbtn.barctl > span:not(.tally) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .appbar.top .barbtn.barctl svg {
    width: 20px;
    height: 20px;
  }

  /* The hairline that separates the places to go from the things to press. */
  .bar-split {
    display: block;
    flex: none;
    width: 1px;
    height: 24px;
    /* Also the spring: everything after it is pushed to the right-hand edge. */
    margin-inline: auto 0.6rem;
    background: rgba(255, 255, 255, 0.22);
  }

  /* ── the dock ────────────────────────────────────────────────────────── */

  /* The bar itself is now only a positioning frame: no background, no shadow,
     and — the part that matters — no claim on the pointer. Everything visible
     moves onto the row inside it. */
  .appbar.bottom {
    background: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    padding-bottom: calc(var(--safe-bottom) + 0.9rem);
    pointer-events: none;
  }
  .appbar.bottom .appbar-in {
    pointer-events: auto;
    width: fit-content;
    max-width: min(760px, calc(100vw - 2rem));
    min-height: 0;
    gap: 0.3rem;
    padding: 0.35rem;
    border-radius: 999px;
    background: linear-gradient(
      180deg,
      color-mix(in srgb, var(--navbar) 88%, #fff) 0%,
      var(--navbar) 46%,
      color-mix(in srgb, var(--navbar) 78%, #000) 100%
    );
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, calc(0.3 * var(--lift))),
      inset 0 -1px 0 rgba(0, 0, 0, calc(0.42 * var(--lift))),
      0 calc(18px * var(--lift)) calc(44px * var(--lift)) -16px rgba(8, 48, 44, 0.8);
  }
  /* Inside a pill the buttons are worded rows, sized to their own words rather
     than each taking a fifth of the window. */
  .appbar.bottom .barbtn {
    flex: 0 0 auto;
    max-width: none;
    flex-direction: row;
    gap: 0.5rem;
    min-height: 46px;
    padding: 0.5rem 1rem;
    border-radius: 999px;
    font-size: 0.86rem;
    letter-spacing: 0;
  }
  .appbar.bottom .barbtn span {
    max-width: none;
  }
  .appbar.bottom .barbtn[aria-current='page']::after {
    display: none;
  }
  /* Booking keeps its lift and its colour, and gets its word back: a circle
     with no label is a telephone's affordance, and this is not one. */
  .appbar.bottom .barbtn.hot {
    width: auto;
    height: auto;
    min-width: 0;
    margin-top: 0;
    gap: 0.5rem;
    font-size: 0.88rem;
    padding-inline: 1.25rem;
  }
  .appbar.bottom .barbtn.hot svg {
    width: 21px;
    height: 21px;
  }
  .appbar.bottom .barbtn.hot span {
    display: block;
  }
}

/* A wide but very short window — a telephone held sideways, or a browser
   dragged down to a strip — keeps the shorter header the rule further up asked
   for. Restated rather than reordered: the desktop block above it sets
   `--bar-top` on the same selector, and the last one written is the one that
   counts. */
@media (min-width: 781px) and (max-width: 900px) and (max-height: 480px) {
  html:root {
    --bar-top: 56px;
  }
}

/* The dock is a pill, so the page behind it shows at its corners; on the
   widest screens it is given a little more air and the header a little more
   presence. */
@media (min-width: 1200px) {
  .appbar.top .appbar-in {
    gap: 1.4rem;
  }
  .appbar.bottom {
    padding-bottom: calc(var(--safe-bottom) + 1.2rem);
  }
}

/* The unread count on the bell. */
.barbtn .tally {
  position: absolute;
  top: 3px;
  right: 3px;
  min-width: 17px;
  height: 17px;
  padding-inline: 4px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 800;
  line-height: 17px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--navbar);
}
.barbtn .tally[hidden] {
  display: none;
}

/* ── the panels that drop out of the top bar ─────────────────────────────── */

.barpop {
  position: fixed;
  top: calc(var(--bar-top) + 6px);
  right: clamp(0.5rem, 3vw, 2rem);
  z-index: 70;
  width: min(360px, calc(100vw - 1.4rem));
  max-height: min(70vh, 560px);
  overflow-y: auto;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--sand);
  border-radius: 16px;
  box-shadow: var(--shadow-high);
  padding: 0.5rem;
  animation: pop 0.16s ease-out;
}
.barpop[hidden] {
  display: none;
}
@keyframes pop {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
}
.barpop h4 {
  font-family: var(--body);
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0.45rem 0.6rem;
}
.barpop .langrow {
  display: grid;
  gap: 0.3rem;
}
.barpop .langrow button {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.62rem 0.7rem;
  border: 1px solid transparent;
  border-radius: 12px;
  background: transparent;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  text-align: start;
  cursor: pointer;
}
.barpop .langrow button:hover {
  background: var(--primary-soft);
}
.barpop .langrow button[aria-pressed='true'] {
  border-color: var(--primary);
  background: var(--primary-soft);
  color: var(--primary);
}
.barpop .langrow button .tick {
  margin-inline-start: auto;
  width: 18px;
  height: 18px;
  color: var(--primary);
}
.barpop .langrow button[aria-pressed='false'] .tick {
  visibility: hidden;
}

/* One notification. The whole row is the link, because the point of the bell
   is that tapping it takes you to the thing that happened. */
.noterow {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  gap: 0.1rem 0.6rem;
  padding: 0.6rem;
  border-radius: 12px;
  text-decoration: none;
  color: inherit;
  border: 1px solid transparent;
}
.noterow:hover {
  background: var(--surface);
}
.noterow.unread {
  background: color-mix(in srgb, var(--primary-soft) 70%, transparent);
  border-color: color-mix(in srgb, var(--primary) 18%, transparent);
}
.noterow svg {
  width: 20px;
  height: 20px;
  grid-row: span 2;
  color: var(--primary);
  margin-top: 2px;
}
.noterow b {
  font-size: 0.93rem;
  font-weight: 700;
  line-height: 1.35;
}
.noterow small {
  color: var(--muted);
  font-size: 0.8rem;
}
.noterow .body {
  font-size: 0.87rem;
  color: var(--muted);
  grid-column: 2;
}
/* The letter book's rows open a drawer rather than a page, so they are buttons.
   Undo the browser's button look and put the writing back on the left, where a
   list of letters reads. */
button.noterow {
  width: 100%;
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
button.noterow .pill {
  grid-column: 2;
  justify-self: start;
  margin-top: 0.25rem;
}
button.noterow .tiny {
  grid-column: 2;
}
.barpop .allnotes {
  display: block;
  text-align: center;
  padding: 0.6rem;
  font-size: 0.86rem;
  font-weight: 700;
}

/* ── the quick action button and its panel ───────────────────────────────── */

/* The one coloured button in a row of grey ones. Prominence here is not a
   larger size — the bar is already tight on a phone — but being the only thing
   in it that is not the same as everything else. */
.barbtn.accent {
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 74%, #000));
  color: #fff;
}
.barbtn.accent:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 88%, #fff), var(--accent));
  color: #fff;
}
.barbtn.accent svg {
  color: #fff;
}

/* ═══════════════════════════════════════════════════════════════════════════
   RAISED SURFACES AND COLOURED BUTTONS
   ══════════════════════════════════════════════════════════════════════════ */

/* The default `.btn` stays as it was — flat and quiet — and anything that
   wants relief asks for it. That way the existing panels are untouched. */
.btn.raised {
  border: 0;
  background: linear-gradient(180deg, color-mix(in srgb, var(--btn-bg) 82%, #fff), var(--btn-bg) 58%,
      color-mix(in srgb, var(--btn-bg) 86%, #000));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, calc(0.42 * var(--lift))),
    inset 0 -2px 0 rgba(0, 0, 0, calc(0.22 * var(--lift))),
    0 calc(3px * var(--lift)) 0 color-mix(in srgb, var(--btn-bg) 62%, #000),
    0 calc(8px * var(--lift)) calc(20px * var(--lift)) -8px rgba(8, 48, 44, 0.6);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.18);
}
.btn.raised:hover {
  transform: translateY(-2px);
}
.btn.raised:active {
  transform: translateY(calc(2px * var(--lift)));
  box-shadow:
    inset 0 2px 6px rgba(0, 0, 0, 0.3),
    0 1px 0 color-mix(in srgb, var(--btn-bg) 62%, #000);
}

/* The colours a button can wear. The administrator sets the two that matter
   (`--primary`, `--accent`); these are the fixed meanings. */
.btn.tone-gold {
  --btn-bg: var(--gold);
  --btn-ink: #2b1d05;
}
.btn.tone-teal {
  --btn-bg: var(--glow);
  --btn-ink: #04241f;
}
.btn.tone-deep {
  --btn-bg: var(--deep);
}
.btn.tone-red {
  --btn-bg: var(--danger);
}

.btn.wide {
  width: 100%;
}
.btn.big {
  padding: 1rem 1.6rem;
  min-height: 54px;
  font-size: 1.03rem;
}

/* A raised card: the same trick, in paper. */
.card3 {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) + 4px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    0 1px 0 var(--sand-deep),
    0 calc(14px * var(--lift)) calc(34px * var(--lift)) -20px rgba(26, 36, 22, 0.55);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.card3.lift:hover {
  transform: translateY(-3px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    0 2px 0 var(--sand-deep),
    0 calc(24px * var(--lift)) calc(50px * var(--lift)) -22px rgba(26, 36, 22, 0.6);
}

/* An interior page's heading strip. */
.pagehead {
  padding-block: clamp(1.4rem, 4vw, 2.6rem) 1.2rem;
}
.pagehead h1 {
  font-size: clamp(1.7rem, 4.4vw, 2.6rem);
}
.pagehead h1 .ti,
.pagehead h1 .am {
  color: var(--primary);
}
.pagehead .lede {
  color: var(--muted);
  max-width: 60ch;
  margin-top: 0.7rem;
}
.pagehead .crumbs {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 0.6rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE HERO STAGE — slideshow, subtitles, sound and the advert
   ══════════════════════════════════════════════════════════════════════════ */

.stage {
  position: relative;
  border-radius: calc(var(--radius) + 8px);
  overflow: hidden;
  background: var(--deep);
  aspect-ratio: 4 / 3;
  box-shadow: var(--shadow-high);
  isolation: isolate;
}
@media (max-width: 900px) {
  .stage {
    aspect-ratio: 16 / 11;
  }
}

.slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.9s ease;
  pointer-events: none;
}
.slide.on {
  opacity: 1;
  pointer-events: auto;
}
.slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* A slow drift, so a still photograph does not look like a stalled page. */
.slide.on img {
  animation: drift 9s ease-out both;
}
@keyframes drift {
  from {
    transform: scale(1.02);
  }
  to {
    transform: scale(1.11) translate3d(-1.5%, -1%, 0);
  }
}

.slide-cap {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: clamp(1rem, 3vw, 2rem) clamp(1rem, 3vw, 2rem) clamp(2.6rem, 5vw, 3.2rem);
  color: #fff;
  background: linear-gradient(0deg, rgba(6, 30, 27, 0.92) 0%, rgba(6, 30, 27, 0.62) 45%, transparent 100%);
}
.slide.on .slide-cap {
  animation: rise 0.7s 0.15s ease-out both;
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
}
.slide-cap .sub {
  font-family: var(--ethiopic-display);
  font-size: clamp(1.15rem, 2.6vw, 1.65rem);
  font-weight: 700;
  line-height: 1.3;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
.slide-cap .cap {
  font-size: 0.95rem;
  color: #d8e6e1;
  margin-top: 0.4rem;
  max-width: 52ch;
}
.slide-cap .bi > .ti,
.slide-cap .bi > .am,
.slide-cap .bi > .en {
  color: inherit;
}
.slide-cap a.btn {
  margin-top: 0.9rem;
}

/* The controls sit on the glass: dots, a pause and a mute. */
.stagebar {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
}
.stagedots {
  display: flex;
  gap: 0.35rem;
  margin-inline-end: auto;
}
.stagedots button {
  width: 26px;
  height: 6px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.38);
  cursor: pointer;
  transition: background 0.2s ease, width 0.2s ease;
}
.stagedots button[aria-pressed='true'] {
  background: #fff;
  width: 34px;
}
.glassbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  background: rgba(8, 48, 44, 0.45);
  backdrop-filter: blur(6px);
  color: #fff;
  cursor: pointer;
}
.glassbtn svg {
  width: 18px;
  height: 18px;
}
.glassbtn:hover {
  background: rgba(8, 48, 44, 0.7);
}

/* The advert. It sits in the same stage, above the slides, and is muted until
   somebody asks for sound — an unmuted video that starts by itself is the
   fastest way to lose a visitor. */
.stage video.advert {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
  background: #000;
}
.stage video.advert[hidden] {
  display: none;
}
.advert-tag {
  position: absolute;
  top: 0.7rem;
  inset-inline-start: 0.7rem;
  z-index: 3;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  background: rgba(8, 48, 44, 0.6);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  backdrop-filter: blur(6px);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE WELCOME FLOW — language first, then what they came for
   ══════════════════════════════════════════════════════════════════════════ */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgba(6, 30, 27, 0.62);
  backdrop-filter: blur(4px);
  animation: fade 0.2s ease-out;
}
.sheet[hidden] {
  display: none;
}
@keyframes fade {
  from {
    opacity: 0;
  }
}
.sheet-card {
  width: min(620px, 100%);
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  background: var(--paper);
  border-radius: calc(var(--radius) + 8px);
  padding: clamp(1.2rem, 4vw, 2rem);
  box-shadow: 0 40px 80px -30px rgba(0, 0, 0, 0.6);
  animation: rise 0.28s ease-out both;
}
.sheet-card h2 {
  font-size: clamp(1.35rem, 3.4vw, 1.9rem);
}
.sheet-card h2 .ti,
.sheet-card h2 .am {
  color: var(--primary);
}
.sheet-card .lede {
  color: var(--muted);
  margin-top: 0.5rem;
  font-size: 0.95rem;
}
.sheet-card .sheet-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  justify-content: space-between;
  margin-top: 1.2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--sand);
  font-size: 0.86rem;
  color: var(--muted);
}

/* The language choice: three big buttons, each written in its own script. */
.langpick {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.7rem;
  margin-top: 1.2rem;
}
@media (max-width: 520px) {
  .langpick {
    grid-template-columns: minmax(0, 1fr);
  }
}
.langpick button {
  padding: 1.1rem 0.6rem;
  border: 1.5px solid var(--sand-deep);
  border-radius: var(--radius);
  background: var(--surface);
  font-family: var(--ethiopic-display);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--primary);
  cursor: pointer;
  transition: transform 0.14s ease, border-color 0.14s ease, background 0.14s ease;
  box-shadow: inset 0 1px 0 #fff, 0 calc(3px * var(--lift)) 0 var(--sand-deep);
}
.langpick button:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
  background: var(--primary-soft);
}
.langpick button small {
  display: block;
  font-family: var(--body);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 0.3rem;
}

/* What did you come here for? */
.choices {
  display: grid;
  gap: 0.7rem;
  margin-top: 1.2rem;
}
.choice {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) 20px;
  align-items: center;
  gap: 0.9rem;
  padding: 0.9rem 1rem;
  border: 1.5px solid var(--sand);
  border-radius: var(--radius);
  background: var(--surface);
  text-align: start;
  font: inherit;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}
.choice:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
  box-shadow: 0 calc(10px * var(--lift)) calc(24px * var(--lift)) -14px rgba(192, 69, 36, 0.6);
}
.choice .ring {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 14px;
  background: var(--primary-soft);
  color: var(--primary);
}
.choice .ring svg {
  width: 24px;
  height: 24px;
}
.choice.accent .ring {
  background: var(--accent-soft);
  color: var(--accent);
}
.choice b {
  display: block;
  font-size: 1rem;
  font-weight: 700;
}
.choice small {
  display: block;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.45;
}
.choice > svg:last-child {
  width: 20px;
  height: 20px;
  color: var(--muted);
}

/* ── the menu window ──────────────────────────────────────────────────────
   The bottom bar's menu button used to open the same full-width sheet the
   welcome flow uses: a 620px card holding sixteen full-width rows, which on a
   telephone ran past the bottom of the screen and had to be scrolled before
   the reader could see what the site contained. A menu is not a document. It
   is a small window onto the rest of the site, and it should sit over the page
   it was opened from rather than replace it.

   So it is now a narrow card holding a board of tiles, each cell an icon over
   a word in that destination's own colour. Two columns on a 320px telephone
   and three as soon as there is room for them — a tile is a 40px plate and a
   label of about eight characters, so two fit across the narrowest screen in
   use. Sixteen destinations become six short rows instead of sixteen long
   ones, which is a window a third the height that needs no scrolling.
   ──────────────────────────────────────────────────────────────────────── */
.sheet-card.compact {
  width: min(380px, 100%);
  padding: clamp(0.9rem, 3vw, 1.15rem);
  border-radius: calc(var(--radius) + 4px);
}
.sheet-card.compact h2 {
  font-size: 1.1rem;
  letter-spacing: 0.01em;
}
.sheet-card.compact .sheet-foot {
  margin-top: 0.85rem;
  padding-top: 0.7rem;
  font-size: 0.8rem;
}

.menuchoices.tiles {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.45rem;
  margin-top: 0.85rem;
}
/* A tile, not a row: the plate on top, the word under it, nothing else. The
   arrow that used to close each row is gone — in a grid of two columns it was
   twenty pixels spent saying "this is a link" about something that is plainly
   a link, and those twenty pixels are what the word needs. */
.menuchoices.tiles .choice {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: 0.35rem;
  padding: 0.6rem 0.35rem 0.55rem;
  text-align: center;
  /* The tone is worn as a tinted face and a coloured edge rather than as a
     solid block: sixteen saturated tiles at once would be a colour chart, and
     the reader is meant to pick one out of it, not look at all of them. */
  border-color: color-mix(in srgb, var(--tone, var(--primary)) 34%, var(--sand));
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tone, var(--primary)) 12%, var(--surface)),
    var(--surface)
  );
}
.menuchoices.tiles .choice:hover {
  border-color: var(--tone, var(--primary));
  box-shadow: 0 calc(10px * var(--lift)) calc(20px * var(--lift)) -12px
    color-mix(in srgb, var(--tone, var(--primary)) 70%, transparent);
}
.menuchoices.tiles .choice .ring {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tone, var(--primary)) 88%, #fff),
    var(--tone, var(--primary))
  );
  color: var(--tone-ink, #fff);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.menuchoices.tiles .choice .ring svg {
  width: 21px;
  height: 21px;
}
.menuchoices.tiles .choice b {
  font-size: 0.8rem;
  line-height: 1.25;
  /* Two lines at most: "Clinician portal" wraps, and every tile in a row stays
     the same height whatever its neighbour is called. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Three columns once there is room for them — a tablet or a desktop opens the
   same window, and at 420px wide a third column costs nothing. */
@media (min-width: 420px) {
  .sheet-card.compact {
    width: min(440px, 100%);
  }
  .menuchoices.tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE NUMBERED STEPS, AND DECLARING A PAYMENT
   ══════════════════════════════════════════════════════════════════════════ */

/* The numbered list of what a booking actually involves, on the about page.
   Not a progress bar: nothing here is `on` or `done`, because the reader is not
   part-way through anything — they are reading about the order before they
   start. So each number sits against its own step and the sentence explaining
   it, rather than in a row of pills that has to be read alongside a second list
   further down the page.
   The number is the list's own counter rather than the browser's marker, so it
   can be a filled circle in the section's colour and stay put beside a step
   whose explanation runs to three lines. */
.stepnotes {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
  display: grid;
  gap: 0.9rem;
}
.stepnotes li {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  gap: 0.15rem 0.7rem;
  counter-increment: step;
}
.stepnotes li::before {
  content: counter(step);
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--tone, var(--primary));
  color: var(--tone-ink, #fff);
  font-size: 0.78rem;
  font-weight: 800;
  /* Optically level with the first line of the heading beside it. */
  margin-top: 0.05rem;
}
.stepnotes h3 {
  font-size: 0.98rem;
  font-weight: 700;
}
.stepnotes p {
  color: var(--muted);
  font-size: 0.92rem;
  max-width: 62ch;
}
/* Below the step number's own width there is nothing left for a hanging indent
   to hang from, so the explanation moves under the heading rather than beside
   it and the number keeps its place at the top. */
@media (max-width: 420px) {
  .stepnotes p {
    grid-column: 1 / -1;
  }
}

/* A grid of things to pick one of: a service, a clinician, an area. */
.picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
  gap: 0.8rem;
}
.pick {
  position: relative;
  display: grid;
  gap: 0.3rem;
  padding: 1rem 1.1rem;
  border: 1.5px solid var(--sand);
  border-radius: var(--radius);
  background: var(--paper);
  text-align: start;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}
.pick:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
}
.pick[aria-pressed='true'] {
  border-color: var(--primary);
  background: var(--primary-soft);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.pick[aria-pressed='true']::after {
  content: '✓';
  position: absolute;
  top: 0.7rem;
  inset-inline-end: 0.8rem;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 800;
}
.pick[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}
.pick .who {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
.pick .face {
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  background: var(--primary-soft);
}
.pick .cost {
  font-family: var(--display);
  font-size: 1.12rem;
  font-weight: 700;
  color: var(--primary);
}
.pick .meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  font-size: 0.8rem;
  color: var(--muted);
}

/* Morning, afternoon, evening. Thirty-two half-hour buttons at once is a wall;
   three named parts with eight hours behind each is a choice. */

.part {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.7rem;
  border: 1.5px solid var(--sand);
  border-radius: 14px;
  background: var(--paper);
  font: inherit;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.part svg {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--primary);
}
.part b {
  display: block;
  font-size: 0.92rem;
}
.part small {
  display: block;
  color: var(--muted);
  font-size: 0.76rem;
}
.part[aria-pressed='true'] {
  border-color: var(--primary);
  background: var(--primary-soft);
}
.part[aria-pressed='true'] svg {
  color: var(--primary);
}


/* The summary rail that follows a booking down the page. */
.tally-card {
  display: grid;
  gap: 0.5rem;
}
.tally-card .line {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.92rem;
}
.tally-card .line span {
  color: var(--muted);
}
.tally-card .line b {
  font-weight: 700;
  text-align: end;
}
.tally-card .total {
  border-top: 1px solid var(--sand);
  padding-top: 0.6rem;
  font-family: var(--display);
  font-size: 1.3rem;
}
.tally-card .total b {
  color: var(--primary);
}

/* ── ways to pay ─────────────────────────────────────────────────────────── */

.methods {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
  gap: 0.6rem;
}
.method {
  display: grid;
  gap: 0.35rem;
  place-items: center;
  padding: 0.9rem 0.6rem;
  border: 1.5px solid var(--sand);
  border-radius: var(--radius);
  background: var(--paper);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--ink);
  text-align: center;
  cursor: pointer;
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.method:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
}
.method[aria-pressed='true'] {
  border-color: var(--primary);
  background: var(--primary-soft);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.method .glyph {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: var(--surface);
  color: var(--primary);
}
.method .glyph svg {
  width: 22px;
  height: 22px;
}
.method img {
  width: 42px;
  height: 42px;
  object-fit: contain;
}
.method small {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.76rem;
}
.method[data-kind='crypto'] .glyph,
.method[data-kind='medcoin'] .glyph {
  background: color-mix(in srgb, var(--gold) 22%, transparent);
  color: color-mix(in srgb, var(--gold) 72%, #000);
}

/* ── the three steps of a declared payment ───────────────────────────────── */

/* Send the money, show the receipt, add anything else. Numbering them is what
   turned a wall of boxes into an errand with an end: a reader who has done the
   first two is finished, and the third says so by admitting it is optional. */
.paystep {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin-top: 1.1rem;
}
.paystep-num {
  display: grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--paper);
  font-family: var(--display);
  font-size: 0.95rem;
  font-weight: 700;
}
.paystep b {
  display: block;
  font-size: 1rem;
}
.paystep small {
  display: block;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

/* The receipt box. It is the largest thing on the payment screen on purpose:
   the screenshot is what confirms a payment here, and everything else is a
   fallback for the reader whose telephone cannot produce one. */
.dropzone {
  display: grid;
  justify-items: center;
  gap: 0.35rem;
  margin-top: 0.7rem;
  padding: 1.15rem 1rem;
  border: 2px dashed var(--primary);
  border-radius: var(--radius);
  background: var(--primary-soft);
  text-align: center;
  cursor: pointer;
}
.dropzone svg {
  width: 30px;
  height: 30px;
  color: var(--primary);
}
.dropzone b {
  font-size: 1rem;
}
.dropzone small {
  color: var(--muted);
  font-size: 0.82rem;
}
.dropzone input[type='file'] {
  width: 100%;
  max-width: 320px;
  font-size: 0.85rem;
}
.dropzone.has-proof {
  border-style: solid;
  border-color: var(--good);
}

/* Everything a patient does not have to fill in, folded away until they ask. */
.more-fields {
  margin-top: 1rem;
  border: 1px solid var(--sand);
  border-radius: var(--radius);
  background: var(--surface);
}
.more-fields > summary {
  padding: 0.75rem 0.9rem;
  font-weight: 700;
  font-size: 0.9rem;
  cursor: pointer;
}
.more-fields > .rows {
  padding: 0 0.9rem 0.9rem;
}

/* Where to send the money, once a method is chosen. */
.paylines {
  display: grid;
  gap: 0.6rem;
  margin-top: 1rem;
}
.copyrow {
  display: flex;
  align-items: center;
  /* A wallet address is longer than a narrow screen. The row wraps rather than
     squeezing the copy button off the edge, and the note below it takes a line
     of its own. */
  flex-wrap: wrap;
  gap: 0.6rem;
  max-width: 100%;
  padding: 0.7rem 0.85rem;
  border: 1px dashed var(--sand-deep);
  border-radius: 12px;
  background: var(--surface);
}
.copyrow .k {
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.copyrow .v {
  flex: 1 1 12ch;
  min-width: 0;
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  word-break: break-all;
  user-select: all;
}
.copyrow .btn {
  margin-inline-start: auto;
  flex: none;
}
.copyrow small {
  flex: 1 0 100%;
}

/* ═══════════════════════════════════════════════════════════════════════════
   APPOINTMENTS — marks, the settlement timer, contact buttons
   ══════════════════════════════════════════════════════════════════════════ */

.countdown {
  display: inline-block;
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 0.88rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--warn) 16%, transparent);
  color: color-mix(in srgb, var(--warn) 85%, #000);
}
.countdown.over {
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  color: var(--danger);
  animation: throb 1.6s ease-in-out infinite;
}

/* ═══════════════════════════════════════════════════════════════════════════
   REGISTERING A PATIENT BY HAND, AND THE MULTI-DAY PROGRAMMES
   The credentials block is read aloud down a telephone as often as it is
   copied, so the values are monospaced and set apart from their labels rather
   than run together; and the dispatch message is shown as the plain text it
   will actually be sent as, wrapping rather than scrolling sideways.
   ══════════════════════════════════════════════════════════════════════════ */

.dispatch {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: 100%;
  margin: 0.4rem 0 0;
  padding: 0.9rem 1rem;
  border: 1px solid var(--sand);
  border-radius: 12px;
  background: var(--surface);
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 0.84rem;
  line-height: 1.6;
}


@keyframes throb {
  50% {
    opacity: 0.55;
  }
}

.marks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
}


/* "Something is wrong — tell the office." Kept quiet until it is opened: a
   complaint button that shouts sits on every card of a page most people open
   because everything is fine. Once opened it is unmistakably its own thing,
   marked in the danger colour down the side. */
.appeal {
  display: grid;
  justify-items: start;
  gap: 0.6rem;
}


/* ── the ways to reach one person directly ────────────────────────────────
   Each one is a real link: `tel:`, `mailto:`, and the deep links the three
   messengers publish.

   These used to be pills — a round-cornered chip with an icon and a value in
   it, the same shape whether it held a telephone number, a Telegram handle or
   an email address. Two things were wrong with that. A reader could not tell
   at a glance which was which without decoding the icon, and on a narrow
   screen a long email address made a pill wider than the card it sat in.

   They are now a responsive grid of labelled cards: the network is named above
   the value, the value is set in a tabular face so digits line up down the
   column, and a long one wraps inside its own card instead of pushing the
   layout sideways. Each card is a comfortable target for a thumb.
   ───────────────────────────────────────────────────────────────────────── */

.contacts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: 0.5rem;
}
.contacts .contact {
  --net: var(--primary);
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
  padding: 0.6rem 0.75rem;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--net) 26%, var(--sand));
  background: linear-gradient(180deg, color-mix(in srgb, var(--net) 6%, var(--paper)), var(--paper));
  text-decoration: none;
  color: inherit;
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}
.contacts .contact:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--net) 55%, transparent);
  box-shadow: 0 calc(8px * var(--lift)) calc(20px * var(--lift)) -12px rgba(8, 48, 44, 0.55);
}

.contacts .contact[data-net='whatsapp'] {
  --net: #0b7a58;
}
.contacts .contact[data-net='telegram'] {
  --net: #1668a4;
}
.contacts .contact[data-net='imo'] {
  --net: #1b5fbe;
}
.contacts .contact[data-net='email'] {
  --net: var(--royal);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE PUBLIC FEED

   A social wall, drawn in the shape every reader here already knows: a column
   of cards, each one a face and a name, then the writing, then the picture edge
   to edge, then the counts over a hairline, then Like · Comment · Share.

   Written for a telephone held in one hand and widened from there. On a narrow
   screen the cards run edge to edge, because a 1.1rem gutter down each side of
   a 360px screen is six per cent of the reading width spent on nothing, and a
   photograph that reaches the edges reads as a photograph rather than as a
   thumbnail. Everything that can be pressed is at least 44px tall.
   ══════════════════════════════════════════════════════════════════════════ */

.feed {
  display: grid;
  gap: 0.6rem;
  max-width: 680px;
  margin-inline: auto;
}
@media (min-width: 621px) {
  .feed {
    gap: 1.1rem;
  }
}
.post {
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) + 2px);
  box-shadow: var(--shadow-low);
  overflow: hidden;
}

/* ── who wrote it ────────────────────────────────────────────────────────
   Face, then name over byline, then whatever badge the post carries. The
   middle column is the only one allowed to grow, so a long name shortens
   rather than pushing the badge off the right-hand edge. */
.post-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.8rem 0.9rem 0.5rem;
}
.post-face {
  flex: none;
  display: block;
}
.post-head .avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--primary-soft);
}
.post-ident {
  display: grid;
  gap: 0.05rem;
  min-width: 0;
  flex: 1 1 auto;
}
.post-author {
  display: flex;
  align-items: center;
  gap: 0.32rem;
  min-width: 0;
  font-size: 0.97rem;
  line-height: 1.3;
}
.post-author svg {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--primary);
}
/* The byline: role, when, and the globe that says the post is public. */
.post-meta {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
  color: var(--muted);
  font-size: 0.79rem;
}
.post-when {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.post-reach {
  display: inline-flex;
  flex: none;
}
.post-reach svg {
  width: 13px;
  height: 13px;
}
.post-pin {
  flex: none;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--gold) 20%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  color: var(--warn);
  font-size: 0.72rem;
  font-weight: 700;
}
.post-pin svg {
  width: 13px;
  height: 13px;
}

/* ── what they wrote ─────────────────────────────────────────────────────── */
.post-body {
  padding: 0.15rem 0.9rem 0.75rem;
}
.post-body h3 {
  font-size: 1.08rem;
  line-height: 1.3;
  margin-bottom: 0.35rem;
}
.post-text p {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.6;
}
.post-text p + p {
  margin-top: 0.55rem;
}
/* Folded to eight lines until asked otherwise, so one long post cannot push
   the next three off the screen. `postCard` removes the class on the next
   frame when the writing fitted anyway. */
.post-text.clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 8;
  line-clamp: 8;
  overflow: hidden;
}
.post-more {
  margin-top: 0.3rem;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--muted);
  cursor: pointer;
}
.post-more:hover {
  color: var(--ink);
  text-decoration: underline;
}
/* A link is its own row rather than a line of blue in the middle of a
   paragraph: on a wall, a link is a destination, not a footnote. */
.post-link {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.6rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--sand);
  border-radius: 12px;
  background: var(--surface);
  color: var(--royal);
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: none;
  overflow-wrap: anywhere;
}
.post-link svg {
  width: 16px;
  height: 16px;
  flex: none;
}
.post-link:hover {
  background: var(--royal-soft);
}
.post-media {
  background: #0a0a0a;
}
.post-media.plain {
  background: var(--surface);
}
.post-media img,
.post-media video {
  display: block;
  width: 100%;
  /* Small viewport units, so the browser's collapsing address bar does not
     change the height of a photograph mid-scroll. */
  max-height: 70svh;
  object-fit: contain;
}
.post-media audio {
  width: 100%;
  padding: 0.8rem 0.9rem;
}
.post-file {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0.9rem 0.8rem;
  padding: 0.8rem 0.9rem;
  min-height: 48px;
  border: 1px solid var(--sand);
  border-radius: 12px;
  background: var(--surface);
  text-decoration: none;
  color: var(--primary);
  font-weight: 700;
  font-size: 0.9rem;
}
.post-file span {
  display: grid;
  min-width: 0;
}
.post-file b,
.post-file small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.post-file svg {
  width: 20px;
  height: 20px;
  flex: none;
}

/* ── the counts ──────────────────────────────────────────────────────────
   Likes behind a small filled bubble on the left, comments and shares on the
   right. Both halves are pressable: on every wall a reader has used, the count
   is the way into the thing it counts. A post nobody has touched hides the row
   rather than printing three zeroes. */
.post-tally {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  padding: 0.5rem 0.9rem;
  font-size: 0.82rem;
  color: var(--muted);
}
.post-tally.none {
  display: none;
}
.post-tally button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.15rem 0.1rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.82rem;
  color: var(--muted);
  cursor: pointer;
}
.post-tally button:hover {
  color: var(--ink);
  text-decoration: underline;
}
.tally-bubble {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  flex: none;
  box-shadow: 0 1px 3px rgba(26, 36, 22, 0.28);
}
.tally-bubble svg {
  width: 11px;
  height: 11px;
}
.post-tally .tally-rest {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* ── the three actions ───────────────────────────────────────────────────
   A hairline, then three equal buttons across the foot. They fill on hover
   rather than changing colour, which is what makes a row of three read as
   three buttons and not as three words. */
.post-acts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.2rem;
  padding: 0.25rem 0.5rem 0.4rem;
  border-top: 1px solid var(--sand);
  margin-top: 0.1rem;
}
.post-act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  /* A thumb, not a mouse pointer. */
  min-height: 44px;
  padding: 0.6rem 0.3rem;
  border: 0;
  border-radius: 10px;
  background: transparent;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.14s ease, color 0.14s ease;
}
.post-act:hover,
.post-act:focus-visible {
  background: var(--surface);
  color: var(--ink);
}
.post-act:active {
  background: var(--sand);
}
.post-act svg {
  width: 20px;
  height: 20px;
  flex: none;
}
.post-act.liked {
  color: var(--accent);
}

/* ── the thread ──────────────────────────────────────────────────────────
   A face, a rounded bubble with the name inside it, and the time beneath —
   the shape that makes six comments read as six voices rather than as six
   paragraphs. */
.comments {
  display: grid;
  gap: 0.7rem;
  padding: 0.8rem 0.9rem 0.95rem;
  border-top: 1px solid var(--sand);
  background: color-mix(in srgb, var(--surface) 60%, var(--paper));
}
.comments[hidden] {
  display: none;
}
.comments-empty {
  color: var(--muted);
  font-size: 0.86rem;
}
.comment {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}
.comment .avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
  background: var(--primary-soft);
  font-size: 0.72rem;
}
.comment-in {
  min-width: 0;
  display: grid;
  gap: 0.15rem;
}
.comment-bubble {
  padding: 0.5rem 0.8rem;
  border-radius: 16px;
  background: var(--paper);
  border: 1px solid var(--sand);
  font-size: 0.9rem;
  width: fit-content;
  max-width: 100%;
}
.comment-bubble b {
  display: block;
  font-size: 0.84rem;
  line-height: 1.35;
}
.comment-bubble p {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.5;
}
.comment-foot {
  display: flex;
  gap: 0.8rem;
  padding-inline-start: 0.8rem;
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 600;
}

/* Writing a comment: a pill with the box in it and the send button on the
   inside edge, which is what a comment field looks like everywhere else. */
.composer {
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
}
.composer-pill {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: flex-end;
  gap: 0.3rem;
  padding: 0.25rem 0.3rem 0.25rem 0.9rem;
  border: 1px solid var(--sand);
  border-radius: 22px;
  background: var(--paper);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
.composer-pill:focus-within {
  border-color: color-mix(in srgb, var(--primary) 45%, var(--sand));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);
}
.composer-pill textarea {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 34px;
  max-height: 180px;
  padding: 0.4rem 0;
  border: 0;
  background: none;
  outline: none;
  resize: none;
  font: inherit;
  font-size: 0.9rem;
  line-height: 1.45;
}
.composer-send {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: var(--primary-soft);
  color: var(--primary);
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.composer-send:hover {
  background: var(--primary);
  color: #fff;
}
.composer-send svg {
  width: 18px;
  height: 18px;
}
.composer-send:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Writing a post. A stack, always — these are labelled fields rather than a
   single line, and laying them out in a row is what made this unusable on a
   telephone. */
.post-composer {
  display: grid;
  gap: 0.7rem;
  padding: 1rem;
  margin-bottom: 0.9rem;
}
.post-composer .field {
  display: grid;
  gap: 0.3rem;
}
.post-composer textarea {
  min-height: 96px;
  resize: vertical;
}
.post-composer .rowbetween {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  justify-content: space-between;
}
.post-composer .rowbetween .btn {
  min-height: 46px;
}

@media (max-width: 620px) {
  /* Edge to edge. The gutter is given back to the reading column, and the
     rounded corners go with it — a card that touches both edges should not
     pretend to be a card. */
  .feed-page {
    margin-inline: calc(-1 * clamp(1.1rem, 4vw, 2.5rem));
    gap: 0.5rem;
  }
  .feed-page .post {
    border-radius: 0;
    border-inline: 0;
  }
  .post-body h3 {
    font-size: 1.02rem;
  }
  .post-media img,
  .post-media video {
    max-height: 78svh;
  }
  .post-composer {
    padding: 0.9rem;
  }
}

@media (max-width: 380px) {
  /* At this width three labelled buttons crowd; the icons carry the meaning and
     the label goes to the screen reader instead. */
  .post-act span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .post-tally {
    font-size: 0.78rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MONEY — balances, coins, the withdrawal request
   ══════════════════════════════════════════════════════════════════════════ */

.moneyrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 0.8rem;
}
.moneybox {
  padding: 1rem 1.1rem;
  border-radius: var(--radius);
  background: var(--paper);
  border: 1px solid var(--sand);
  box-shadow: var(--shadow-low);
}
.moneybox .k {
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.moneybox .v {
  font-family: var(--display);
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--primary);
  line-height: 1.15;
  margin-top: 0.2rem;
}
.moneybox small {
  display: block;
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 0.25rem;
}
.moneybox.gold {
  background: linear-gradient(150deg, color-mix(in srgb, var(--gold) 20%, var(--paper)), var(--paper));
  border-color: color-mix(in srgb, var(--gold) 40%, transparent);
}
.moneybox.gold .v {
  color: color-mix(in srgb, var(--gold) 80%, #000);
}
.moneybox.confirmed .v {
  color: var(--good);
}


/* ── odds and ends the new pages need ───────────────────────────────────── */

.rowbetween {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  align-items: center;
  justify-content: space-between;
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  background: var(--primary-soft);
  color: var(--primary);
  font-size: 0.78rem;
  font-weight: 700;
}
.chip svg {
  width: 14px;
  height: 14px;
}
.chip.warm {
  background: var(--accent-soft);
  color: var(--accent);
}
.chip.gold {
  background: color-mix(in srgb, var(--gold) 22%, transparent);
  color: color-mix(in srgb, var(--gold) 80%, #000);
}
.chip.good {
  background: color-mix(in srgb, var(--good) 14%, transparent);
  color: var(--good);
}
.chip.bad {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  color: var(--danger);
}

/* A chip used as a switch between views — the letter book's four boxes. It is a
   real button, so the browser's own button appearance has to be undone first. */
button.chip {
  border: 1px solid transparent;
  cursor: pointer;
  font-family: inherit;
}
.chip.on {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
}

/* A letter, as it was written. `pre` because the paragraphs and the line breaks
   somebody typed are part of what they wrote, but wrapped, because a letter is
   not code and should never need scrolling sideways to read. */
.letter-body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: 14px;
  padding: 0.9rem 1rem;
  max-height: 52vh;
  overflow-y: auto;
}

.stack {
  display: grid;
  gap: 1rem;
}
.center-narrow {
  width: min(520px, 100%);
  margin-inline: auto;
}
.mute {
  color: var(--muted);
}
.tiny {
  font-size: 0.82rem;
}
[hidden] {
  display: none !important;
}

/* A page that is waiting for its first request. */
.skeleton {
  border-radius: 12px;
  background: linear-gradient(90deg, var(--sand) 25%, var(--surface) 50%, var(--sand) 75%);
  background-size: 240% 100%;
  animation: sweep 1.3s linear infinite;
  min-height: 14px;
}
@keyframes sweep {
  to {
    background-position: -240% 0;
  }
}

/* On a phone the top bar keeps only the essentials; the bottom bar carries
   navigation, which is the whole point of having one. */
@media (max-width: 620px) {
  :root {
    /* One line again. It was 58px when the shortcuts were hidden at this width,
       then 96px when they were given a second line of their own; it is back to
       a single line now that they fit on the first one as icons. */
    --bar-top: 60px;
  }
  /* The name stays. It used to be hidden here, which left a phone showing a
     logo and no site title at all — the one place a first-time visitor looks to
     find out where they are. Instead it shrinks and, if the name is a long one,
     truncates: `min-width: 0` is what lets a flex child shrink below its own
     content at all, and without it the words push the bar wider than the screen
     and reintroduce the horizontal scroll the reset exists to prevent. */
  .appbar.top .brand {
    min-width: 0;
    flex: 0 1 auto;
    gap: 0.5rem;
  }
  .appbar.top .brand .words {
    min-width: 0;
  }
  .appbar.top .brand .name,
  .appbar.top .brand .name-en {
    display: block;
    max-width: 42vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .appbar.top .brand .name {
    font-size: 1.05rem;
  }
  .appbar.top .brand .name-en {
    font-size: 0.6rem;
    letter-spacing: 0.1em;
  }
  /* The bar divides its width between the five; they do not each demand a
     share of it. See the `min-width: 0` note where the bottom bar's buttons are
     defined — a minimum that cannot be met is what pushes a button off the
     right-hand edge instead of making it narrower. */
  .appbar.bottom .barbtn {
    min-width: 0;
    padding-inline: 0.25rem;
  }
  .appbar.bottom .barbtn span {
    font-size: 0.56rem;
  }
}

/* The two bars are chrome. They should never print. */
@media print {
  .appbar,
  .barpop,
  .sheet,
  .appbar.bottom {
    display: none !important;
  }
  body.app {
    padding: 0;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CHOSEN STATES AND SMALL PARTS THE PAGES ASKED FOR

   A card that can be picked has to look picked. Without this a reader could
   choose a department, a town or a receiving account and be shown exactly what
   they saw before, which reads as the press having failed.
   ══════════════════════════════════════════════════════════════════════════ */

/* The foot of the feed: the "load more" button, or the note that says there is
   no more. */
.feed-foot {
  display: grid;
  gap: 0.6rem;
  margin-top: 1rem;
}
.center {
  text-align: center;
}

/* `.pick .face` was drawn for a photograph of a clinician, but the same row is
   used for choices that have no photograph — a service area, a way back into a
   forgotten account. Given a <span> holding an icon instead of an <img>, centre
   the icon in the same circle rather than letting it fill it. */
.pick .face:not(img) {
  display: grid;
  place-items: center;
  color: var(--primary);
}
.pick .face svg {
  width: 22px;
  height: 22px;
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE APP LAYER
   Everything below turns the pages above into something that behaves like an
   application on a telephone: buttons with a real press, a logo that covers a
   navigation, glass over the fixed bars, and the small animations that tell a
   patient what a service is before they have read its name.

   It is appended rather than woven in so that it can be read as one piece, and
   so that removing it would leave the site working and merely flat.
   Its companion is /js/engine.js.
   ════════════════════════════════════════════════════════════════════════════ */

:root {
  /* One easing for everything that moves under a thumb. Slightly overshooting
     on the way out is what makes a button feel like an object. */
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --glass: color-mix(in srgb, var(--paper) 72%, transparent);
  --glass-line: color-mix(in srgb, var(--sand-deep) 60%, transparent);
}

/* ── 1. Tactile buttons ──────────────────────────────────────────────────────
   A button is a solid object with a thickness. The thickness is a second shadow
   in the button's own dark colour, directly beneath it; pressing shortens it and
   moves the button down by exactly the amount it lost, so the top face travels
   and the base stays put. That is the whole trick, and it is why the press reads
   as physical rather than as a shrink. */

.btn {
  position: relative;
  isolation: isolate;
  --sink: calc(4px * var(--lift));
  --face: var(--btn-bg);
  transform: translateY(0);
  box-shadow:
    0 calc(var(--sink)) 0 0 color-mix(in srgb, var(--btn-bg) 55%, var(--deep)),
    0 calc(var(--sink) + 6px) 18px -8px rgba(8, 48, 44, calc(0.45 * var(--lift)));
  transition:
    transform 0.18s var(--spring),
    box-shadow 0.18s var(--ease),
    background 0.2s var(--ease),
    filter 0.2s var(--ease);
}

/* The lit top edge — a one-pixel highlight that says which way is up. */
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 45%);
  opacity: calc(0.9 * var(--lift));
  z-index: -1;
}

/* The light that follows the thumb. --px/--py are written by engine.js on
   pointerdown; without them it sits in the middle and looks like a plain sheen. */
.btn::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(circle at var(--px, 50%) var(--py, 50%), rgba(255, 255, 255, 0.3), transparent 62%);
  opacity: 0;
  transition: opacity 0.2s var(--ease);
  z-index: -1;
}

.btn:hover {
  transform: translateY(calc(-1px * var(--lift)));
  box-shadow:
    0 calc(var(--sink) + 1px) 0 0 color-mix(in srgb, var(--btn-bg) 55%, var(--deep)),
    0 calc(var(--sink) + 10px) 26px -10px rgba(8, 48, 44, calc(0.5 * var(--lift)));
}

.btn:active,
.btn.pressing {
  transform: translateY(var(--sink));
  box-shadow:
    0 0 0 0 color-mix(in srgb, var(--btn-bg) 55%, var(--deep)),
    0 2px 6px -2px rgba(8, 48, 44, 0.4);
}

.btn.pressing::after {
  opacity: 1;
}

/* The flat variants keep the press but not the plinth — a ghost button with a
   thick dark base underneath it would look like a mistake. */
.btn.ghost,
.btn.quiet,
.btn.danger {
  --sink: calc(2px * var(--lift));
  box-shadow: 0 var(--sink) 0 0 color-mix(in srgb, var(--sand-deep) 70%, transparent);
}
.btn.ghost::before,
.btn.quiet::before,
.btn.danger::before {
  opacity: 0;
}

.btn[disabled],
.btn[aria-busy='true'] {
  box-shadow: none;
  transform: none;
  filter: saturate(0.7);
}

/* The colour-coded set. Each carries its own meaning, and each one's plinth is
   mixed from its own face colour so the object is one material. */
.btn.gold {
  --btn-bg: var(--gold);
  --btn-ink: #2a1c05;
}
.btn.good {
  --btn-bg: var(--good);
}
.btn.warn {
  --btn-bg: var(--warn);
}
.btn.solid-danger {
  --btn-bg: var(--danger);
  --btn-ink: #fff;
  border-color: transparent;
}
.btn.glass {
  --btn-bg: var(--glass);
  --btn-ink: var(--ink);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border-color: var(--glass-line);
}

/* A button that fills its row, which on a telephone is most of them. */
.btn.block {
  width: 100%;
}

/* Anything else the engine marks as pressed sinks a little, without a plinth. */
.pressing:not(.btn) {
  transform: scale(0.97);
  transition: transform 0.12s var(--ease);
}
.chip.pressing,
.pick.pressing {
  transform: scale(0.965);
}

/* ── 2. Glass over the fixed bars ────────────────────────────────────────── */

.appbar.top {
  transition:
    transform 0.28s var(--ease),
    background 0.28s var(--ease),
    box-shadow 0.28s var(--ease),
    height 0.28s var(--ease);
}
.appbar.top.shrunk {
  background: color-mix(in srgb, var(--navbar) 86%, transparent);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  box-shadow: 0 10px 30px -18px rgba(8, 48, 44, 0.7);
}
/* The top bar used to slide out of the way on a downward scroll. It does not
   any more — both bars are fixed and every button on them stays reachable on
   every page, at every width. The class is kept and neutralised rather than
   deleted so that a browser holding a cached copy of the older script cannot
   push the bar off the screen with it. */
.appbar.top.tucked {
  transform: none;
}
/* The glass belongs to the full-width band, which is what the bottom bar is on
   a telephone. On a desktop it is a floating dock with an opaque face and the
   page visible all round it, and blurring a strip that is no longer there would
   only smear the foot of the window. */
@media (max-width: 780px) {
  .appbar.bottom {
    backdrop-filter: blur(20px) saturate(1.35);
    -webkit-backdrop-filter: blur(20px) saturate(1.35);
  }
}

/* A bar button presses like everything else, but sinks rather than tilting —
   there is no room under it for a plinth. */
.barbtn {
  transition: transform 0.18s var(--spring), color 0.18s var(--ease), opacity 0.18s var(--ease);
}
.barbtn.pressing {
  transform: scale(0.9);
}
.barbtn.hot.pressing {
  transform: translateY(2px) scale(0.94);
}

/* ── 3. The logo loader ──────────────────────────────────────────────────────
   Raised over the page on any navigation. The logo breathes and a ring sweeps
   around it; both stop the moment the next page paints. */

.appload {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(10px) saturate(1.2);
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s var(--ease), visibility 0.18s;
}
.appload.on {
  opacity: 1;
  visibility: visible;
}
.appload-mark {
  position: relative;
  display: grid;
  place-items: center;
  width: 108px;
  height: 108px;
}
.appload-mark img {
  width: 62px;
  height: 62px;
  border-radius: 16px;
  animation: logo-breathe 1.1s var(--ease) infinite;
}
.appload-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-top-color: var(--glow);
  animation: logo-spin 0.9s linear infinite;
}

@keyframes logo-breathe {
  0%,
  100% {
    transform: scale(1) rotate(0deg);
  }
  45% {
    transform: scale(1.12) rotate(-7deg);
  }
  70% {
    transform: scale(1.04) rotate(4deg);
  }
}
@keyframes logo-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ── 4. Pull to refresh ──────────────────────────────────────────────────── */

.apppull {
  position: fixed;
  top: calc(var(--bar-top) - 6px);
  left: 50%;
  z-index: 700;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: var(--shadow-high);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, calc(var(--pull, 0px) - 46px));
  transition: opacity 0.15s var(--ease), transform 0.05s linear;
}
.apppull.on {
  opacity: 1;
}
.apppull-ring {
  width: 17px;
  height: 17px;
  border-radius: 50%;
  border: 2.5px solid color-mix(in srgb, var(--primary) 22%, transparent);
  border-top-color: var(--primary);
  transition: transform 0.2s var(--ease);
}
.apppull.ready .apppull-ring {
  border-top-color: var(--glow);
  transform: scale(1.18);
}
.apppull.spinning {
  opacity: 1;
  transform: translate(-50%, 12px);
}
.apppull.spinning .apppull-ring {
  animation: logo-spin 0.7s linear infinite;
}

/* ── 5. Entering and swapping views ──────────────────────────────────────── */

.view-out {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.11s var(--ease), transform 0.11s var(--ease);
}
.view-in {
  animation: view-in 0.24s var(--ease) both;
}
@keyframes view-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

.enter-up {
  animation: enter-up 0.5s var(--ease) both;
  animation-delay: var(--enter-delay, 0ms);
}
@keyframes enter-up {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.985);
  }
}

.flash-good,
.flash-warn {
  animation: flash-ring 1.1s var(--ease) 1;
}
.flash-good {
  --flash: var(--good);
}
.flash-warn {
  --flash: var(--warn);
}
@keyframes flash-ring {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--flash) 55%, transparent);
  }
  60% {
    box-shadow: 0 0 0 12px color-mix(in srgb, var(--flash) 0%, transparent);
  }
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--flash) 0%, transparent);
  }
}

/* The default view transition, for browsers that have one. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) {
    animation: view-fade-out 0.16s var(--ease) both;
  }
  ::view-transition-new(root) {
    animation: view-in 0.24s var(--ease) both;
  }
}
@keyframes view-fade-out {
  to {
    opacity: 0;
    transform: translateY(-6px);
  }
}

/* ── 6. Glass panels ─────────────────────────────────────────────────────── */

.glasspanel {
  position: relative;
  border-radius: var(--radius);
  border: 1px solid var(--glass-line);
  background: linear-gradient(
    155deg,
    color-mix(in srgb, var(--paper) 88%, transparent),
    color-mix(in srgb, var(--primary-soft) 60%, transparent)
  );
  backdrop-filter: blur(16px) saturate(1.25);
  -webkit-backdrop-filter: blur(16px) saturate(1.25);
  box-shadow: var(--shadow-high);
  padding: 1rem 1.05rem;
}
.glasspanel::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
}

/* The heartbeat: the icon itself beating at a resting rate, roughly 72 a
   minute, because a pulse animation on a care site should be a plausible one. */

@keyframes care-beat {
  0%,
  55%,
  100% {
    transform: scale(1);
  }
  12% {
    transform: scale(1.18);
  }
  26% {
    transform: scale(1.02);
  }
  38% {
    transform: scale(1.1);
  }
}

/* ── 10. The contact channel that opens after payment ─────────────────────── */

.channel {
  display: grid;
  gap: 0.55rem;
  animation: view-in 0.4s var(--ease) both;
}


/* ── 11. The P2P board ───────────────────────────────────────────────────────
   Two books side by side on a desk, stacked on a telephone. Each row is one
   offer: who, at what price, for how much, and what they will take. */

.p2p-tabs {
  display: flex;
  gap: 0.35rem;
  padding: 0.28rem;
  border-radius: 999px;
  background: var(--primary-soft);
}
.p2p-tabs button {
  flex: 1;
  border: 0;
  border-radius: 999px;
  padding: 0.55rem 0.8rem;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 700;
  font-size: 0.92rem;
  cursor: pointer;
  transition: background 0.22s var(--ease), color 0.22s var(--ease), transform 0.18s var(--spring);
}
.p2p-tabs button.on {
  background: var(--paper);
  color: var(--primary);
  box-shadow: var(--shadow-low);
}
.p2p-tabs button.on.sell {
  color: var(--danger);
}
.p2p-tabs button.pressing {
  transform: scale(0.96);
}

.offer {
  display: grid;
  gap: 0.6rem;
  padding: 0.85rem 0.9rem;
  border-radius: var(--radius);
  border: 1px solid var(--sand);
  background: var(--paper);
  box-shadow: var(--shadow-low);
  transition: transform 0.18s var(--spring), box-shadow 0.18s var(--ease), border-color 0.18s var(--ease);
}
.offer:hover {
  border-color: color-mix(in srgb, var(--primary) 35%, var(--sand));
  box-shadow: var(--shadow-high);
}
.offer-top {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.offer-agent {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
  flex: 1;
}
.offer-agent b {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.98rem;
}
.offer-agent small {
  color: var(--muted);
  font-size: 0.78rem;
}
.offer-price {
  text-align: end;
  font-family: var(--display);
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--good);
}
.offer.sell .offer-price {
  color: var(--danger);
}
.offer-price small {
  display: block;
  font-family: var(--body);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.offer-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  font-size: 0.83rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.offer-meta b {
  color: var(--ink);
  font-weight: 600;
}
.offer-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.offer-foot .btn {
  margin-inline-start: auto;
}

/* The verification badge. Gold, silver or new — earned by completed trades. */
.vbadge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid transparent;
}
.vbadge svg {
  width: 12px;
  height: 12px;
}
.vbadge.gold {
  background: color-mix(in srgb, var(--gold) 18%, var(--paper));
  border-color: color-mix(in srgb, var(--gold) 50%, transparent);
  color: #7a5410;
}
.vbadge.silver {
  background: #eef1f2;
  border-color: #cfd7d9;
  color: #4b5a5e;
}
.vbadge.new {
  background: var(--primary-soft);
  border-color: color-mix(in srgb, var(--primary) 25%, transparent);
  color: var(--primary);
}

/* The completion figure — the number people actually read before trading. */
.rate-ring {
  --pct: 100;
  position: relative;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(var(--good) calc(var(--pct) * 1%), color-mix(in srgb, var(--sand) 70%, transparent) 0);
  font-size: 0.68rem;
  font-weight: 800;
  color: var(--primary);
}
.rate-ring::before {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--paper);
}
.rate-ring span {
  position: relative;
  z-index: 2;
  font-variant-numeric: tabular-nums;
}
.rate-ring.low {
  background: conic-gradient(var(--warn) calc(var(--pct) * 1%), color-mix(in srgb, var(--sand) 70%, transparent) 0);
}

/* The price ticker at the top of the board. */
.tickers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: 0.6rem;
}
.ticker {
  display: grid;
  gap: 0.15rem;
  padding: 0.75rem 0.85rem;
  border-radius: 16px;
  background: linear-gradient(150deg, var(--primary), var(--deep));
  color: #fff;
  box-shadow: var(--shadow-low);
}
.ticker.gold {
  background: linear-gradient(150deg, #b4832a, #7a5410);
}
.ticker .sym {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.75;
}
.ticker .val {
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.ticker .sub {
  font-size: 0.76rem;
  opacity: 0.8;
}

/* An order in flight, with the pay window running down. */
.orderline {
  display: grid;
  gap: 0.5rem;
  padding: 0.85rem 0.9rem;
  border-radius: var(--radius);
  border: 1px solid var(--sand);
  background: var(--paper);
  border-inline-start: 4px solid var(--primary);
}
.orderline.paid {
  border-inline-start-color: var(--warn);
}
.orderline.disputed {
  border-inline-start-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 4%, var(--paper));
}
.orderline.released {
  border-inline-start-color: var(--good);
}
.escrow-clock {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.escrow-clock.urgent {
  background: color-mix(in srgb, var(--danger) 12%, var(--paper));
  color: var(--danger);
  animation: care-beat 1.2s var(--ease) infinite;
}

/* ── 12. Asset cards in the wallet ───────────────────────────────────────── */

.assetcard {
  position: relative;
  display: grid;
  gap: 0.45rem;
  padding: 1rem;
  border-radius: 20px;
  color: #fff;
  overflow: hidden;
  background: linear-gradient(145deg, var(--primary), var(--deep));
  box-shadow: var(--shadow-high);
}
.assetcard.medcoin {
  background: linear-gradient(145deg, #c99a30, #6f4a08);
}
.assetcard.usdt {
  background: linear-gradient(145deg, #1a8f74, #06382c);
}
.assetcard::after {
  content: '';
  position: absolute;
  right: -30px;
  top: -30px;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.09);
}
.assetcard .amount {
  font-family: var(--display);
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.assetcard .sym {
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.8;
}
.assetcard .worth {
  font-size: 0.85rem;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}
.assetcard .held {
  font-size: 0.78rem;
  opacity: 0.75;
}

/* A saved external address or card. */
.addrline {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.8rem;
  border-radius: 14px;
  border: 1px solid var(--sand);
  background: var(--paper);
}
.addrline code {
  font-size: 0.78rem;
  overflow-wrap: anywhere;
  color: var(--muted);
}
.addrline .who {
  display: grid;
  gap: 0.15rem;
  min-width: 0;
  flex: 1;
}
.addrline .who b {
  font-size: 0.92rem;
}
.addrline .marks {
  margin-left: auto;
  flex: none;
}
.netpill {
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  background: var(--primary-soft);
  color: var(--primary);
}
.netpill.bep20 {
  background: #fdf4dd;
  color: #8a6a08;
}
.netpill.binance {
  background: #fcf3d6;
  color: #7a5c00;
}

/* The two sides of a swap, with the button that turns them round between. */
.swaprow {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.6rem;
}
.swapside {
  display: grid;
  gap: 0.15rem;
  padding: 0.7rem 0.8rem;
  border-radius: 14px;
  border: 1px solid var(--sand);
  background: var(--paper);
  text-align: center;
  min-width: 0;
}
.swapside b {
  font-size: 1.05rem;
}
.swapturn {
  flex: none;
  padding: 0.5rem;
  border-radius: 999px;
}
.swapturn svg {
  transition: transform 0.4s var(--ease);
}
.swapturn:hover svg,
.swapturn:focus-visible svg {
  transform: rotate(180deg);
}

/* ── 13. Small mercies ───────────────────────────────────────────────────── */

/* A number that has just changed, counted up rather than replaced. */
.tnum {
  font-variant-numeric: tabular-nums;
  transition: color 0.3s var(--ease);
}

/* Anything the engine is waiting on. */
.busy {
  position: relative;
  pointer-events: none;
}
.busy::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: color-mix(in srgb, var(--paper) 55%, transparent);
  backdrop-filter: blur(1px);
}

/* On a telephone the two fixed bars own the top and bottom of the screen; give
   a sheet and a drawer room to sit above the bottom one. Held to that width on
   purpose: the desktop dock stands clear of the bottom edge on its own padding,
   and an unqualified rule here would flatten it back against the frame. */
@supports (padding: max(0px)) {
  @media (max-width: 780px) {
    .appbar.bottom {
      padding-bottom: max(var(--safe-bottom), 0px);
    }
  }
}

/* Somebody who has asked for less motion gets the layout and the colour and
   none of the loops. The single-shot transitions above are already neutralised
   by the global rule near the top of this file. */
@media (prefers-reduced-motion: reduce) {
  
  .appload-mark img,
  .appload-ring,
  .escrow-clock.urgent {
    animation: none;
  }
  .appload-ring {
    border-top-color: var(--glow);
  }
}

/* ── 14. Chips that are buttons ──────────────────────────────────────────────
   `.chip` was a label. On the market board and the booking rail it is a filter
   you press, so it needs a resting state, a chosen state and a cursor. */

button.chip {
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  padding: 0.34rem 0.75rem;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.2s var(--ease), color 0.2s var(--ease), transform 0.16s var(--spring),
    border-color 0.2s var(--ease);
}
button.chip:hover {
  border-color: color-mix(in srgb, var(--primary) 35%, transparent);
}
button.chip.on {
  background: var(--primary);
  color: var(--paper);
  box-shadow: 0 2px 0 0 var(--deep);
}
button.chip.on svg {
  color: inherit;
}

/* A row of chips wider than the screen scrolls sideways rather than wrapping to
   four lines and pushing the content below the fold. */
.scroll-x {
  /* The display is declared here rather than assumed: this class is used on its
     own in the markup, and without it the `gap` below does nothing and the row
     wraps instead of scrolling — which is exactly the overflow it exists to
     prevent. `max-width: 100%` keeps the scroller inside its grid track. */
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 100%;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 0.2rem;
  scroll-snap-type: x proximity;
}
.scroll-x::-webkit-scrollbar {
  display: none;
}
.scroll-x > * {
  /* A flex child shrinks by default, which in a nowrap row turns "scrolls
     sideways" into "every chip squashed". They keep their width and the row
     scrolls, which is the whole point. */
  flex: none;
  scroll-snap-align: start;
}

.wrap-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-width: 0;
}

/* The receipt a buyer just attached, shown back to them before they commit. */
.receipt-preview {
  display: block;
  max-height: 220px;
  width: auto;
  max-width: 100%;
  border-radius: 12px;
  border: 1px solid var(--sand);
  object-fit: contain;
  background: var(--sand-deep);
}
/* The same class also wraps a preview, for the panels that show a filename when
   the receipt was a PDF rather than a photograph. */
.receipt-preview > img {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: 218px;
  border-radius: 11px;
  object-fit: contain;
}
.receipt-preview > p {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0.6rem 0.75rem;
}
.receipt-preview > p svg {
  width: 16px;
  height: 16px;
  flex: none;
}

/* A one-line verdict under an amount box: neutral, encouraging, or wrong. */
.tiny.good {
  color: var(--good);
}
.tiny.bad {
  color: var(--danger);
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE COLOUR-CODED LAYER
   Every button says what it does in colour, every page says where you are in
   colour, and the two never fight: a button's colour comes from its errand
   (paying is gold, ringing is red, confirming is green) and the page's colour
   comes from its address. Anything the code did not name wears the page's own
   colour, which is never wrong.

   The tone tokens below are the whole vocabulary. /js/paint.js writes
   `data-tone` onto buttons and `data-page-tone` onto <body>; nothing here needs
   to know which page it is on.
   ════════════════════════════════════════════════════════════════════════════ */

:root {
  /* `teal` is the house tone — the one everything unnamed falls back to — so it
     is the badge's own orange-red rather than the teal the site used to wear.
     The rest of the vocabulary codes meaning, not brand, so it is only nudged
     warmer where a hue would have fought the badge. */
  --tone-teal: #c04524; /* the ring: the house tone */
  --tone-green: #2f8a3c;
  --tone-cyan: #1583b4;
  --tone-blue: #1f4d90; /* the navy of the wordmark under the badge */
  --tone-indigo: #4a51bd;
  --tone-violet: #7448c0;
  --tone-rose: #b8477e;
  --tone-orange: #e2601a; /* the lit edge of the ring */
  --tone-slate: #6a6f78; /* the silver of the disc */
}

/* One rule per colour, matched on either attribute, so a button and the page
   it sits on are painted from the same line. */
[data-tone='teal'],
[data-page-tone='teal'] {
  --tone: var(--tone-teal);
  --tone-ink: #fff;
}
[data-tone='green'],
[data-page-tone='green'] {
  --tone: var(--tone-green);
  --tone-ink: #fff;
}
[data-tone='cyan'],
[data-page-tone='cyan'] {
  --tone: var(--tone-cyan);
  --tone-ink: #fff;
}
[data-tone='blue'],
[data-page-tone='blue'] {
  --tone: var(--tone-blue);
  --tone-ink: #fff;
}
[data-tone='indigo'],
[data-page-tone='indigo'] {
  --tone: var(--tone-indigo);
  --tone-ink: #fff;
}
[data-tone='violet'],
[data-page-tone='violet'] {
  --tone: var(--tone-violet);
  --tone-ink: #fff;
}
[data-tone='rose'],
[data-page-tone='rose'] {
  --tone: var(--tone-rose);
  --tone-ink: #fff;
}
[data-tone='red'],
[data-page-tone='red'] {
  --tone: var(--danger);
  --tone-ink: #fff;
}
[data-tone='orange'],
[data-page-tone='orange'] {
  --tone: var(--tone-orange);
  --tone-ink: #fff;
}
[data-tone='gold'],
[data-page-tone='gold'] {
  --tone: var(--gold);
  --tone-ink: #2b1d05;
}
[data-tone='slate'],
[data-page-tone='slate'] {
  --tone: var(--tone-slate);
  --tone-ink: #fff;
}
[data-tone='deep'],
[data-page-tone='deep'] {
  --tone: var(--deep);
  --tone-ink: #fff;
}
/* Two that had a written-out class but no attribute rule. The navigation is
   coloured from stored names, and a name the panel offers has to paint. */
[data-tone='ink'],
[data-page-tone='ink'] {
  --tone: var(--ink);
  --tone-ink: #fff;
}
[data-tone='face'],
[data-page-tone='face'] {
  --tone: var(--btn-face);
  --tone-ink: #fff;
}
/* The fallback: wear whatever colour this page is. */
[data-tone='page'] {
  --tone: var(--page, var(--primary));
  --tone-ink: var(--page-ink, #fff);
}

/* The written-out form. A handful of buttons name their colour in the markup —
   `class="btn tone-green"` — and the painter deliberately leaves those alone.
   The class carries the same two tokens as the attribute so both spellings end
   up in the same place. `.tone-gold`, `.tone-teal`, `.tone-deep`, `.tone-red`
   and `.tone-face` are older and are set on `.btn` further up. */
.tone-green {
  --tone: var(--tone-green);
  --tone-ink: #fff;
}
.tone-cyan {
  --tone: var(--tone-cyan);
  --tone-ink: #fff;
}
.tone-blue {
  --tone: var(--tone-blue);
  --tone-ink: #fff;
}
.tone-indigo {
  --tone: var(--tone-indigo);
  --tone-ink: #fff;
}
.tone-violet {
  --tone: var(--tone-violet);
  --tone-ink: #fff;
}
.tone-rose {
  --tone: var(--tone-rose);
  --tone-ink: #fff;
}
.tone-orange {
  --tone: var(--tone-orange);
  --tone-ink: #fff;
}
.tone-slate {
  --tone: var(--tone-slate);
  --tone-ink: #fff;
}
.btn.tone-green,
.btn.tone-cyan,
.btn.tone-blue,
.btn.tone-indigo,
.btn.tone-violet,
.btn.tone-rose,
.btn.tone-orange,
.btn.tone-slate {
  --btn-bg: var(--tone);
  --btn-ink: var(--tone-ink);
}

/* The page's own colour, derived once on <body> and inherited by everything
   that wants a tint of it. */
body[data-page-tone] {
  --page: var(--tone);
  --page-ink: var(--tone-ink, #fff);
  --page-soft: color-mix(in srgb, var(--tone) 12%, var(--paper));
  --page-line: color-mix(in srgb, var(--tone) 42%, var(--sand));
  --page-deep: color-mix(in srgb, var(--tone) 72%, #06211d);
}

/* A painted button. `--btn-bg` is the one variable the whole button system is
   built on, so a tone is one line and every existing effect — the plinth, the
   press, the thumb light — follows for free. */
.btn[data-tone] {
  --btn-bg: var(--tone);
  --btn-ink: var(--tone-ink, #fff);
}
/* The flat variants keep their colour as ink rather than as a face, otherwise a
   quiet button stops being quiet. */
.btn.quiet[data-tone],
.btn.ghost[data-tone] {
  --btn-bg: transparent;
  --btn-ink: color-mix(in srgb, var(--tone) 78%, var(--ink));
}
.btn.ghost[data-tone] {
  border-color: color-mix(in srgb, var(--tone) 45%, var(--sand));
}
.btn.quiet[data-tone]:hover,
.btn.ghost[data-tone]:hover {
  background: color-mix(in srgb, var(--tone) 10%, transparent);
  color: var(--tone);
}

/* ── hyperresponsive ─────────────────────────────────────────────────────────
   Three things make a control feel instant: no 300ms tap delay, no blue flash
   from the browser's own highlight, and a reaction that starts on touch rather
   than on release. The first two are these declarations; the third is
   engine.js, which adds `.pressing` on pointerdown. The transitions are short
   on purpose — a button that takes a quarter of a second to acknowledge a press
   feels broken on a slow telephone even when the page is fast. */

button,
a,
.btn,
.pick,
.choice,
.chip,
.quick-item,
.quick-trigger,
.langpick button {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.btn {
  transition:
    transform 0.11s var(--spring),
    box-shadow 0.11s var(--ease),
    background 0.14s var(--ease),
    filter 0.14s var(--ease);
}

/* One focus ring for the whole site, in the colour of the thing focused. It is
   drawn outside the control so it never changes a layout. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid color-mix(in srgb, var(--tone, var(--page, var(--primary))) 70%, transparent);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ── the page wears its colour ───────────────────────────────────────────── */

/* A hairline of the page's colour under the top bar, so the colour is visible
   even on a page made mostly of white cards. */
body[data-page-tone] .appbar.top,
body[data-page-tone] .top {
  border-bottom: 2px solid var(--page);
}
/* The wash behind the page takes the same colour, faintly. */
body[data-page-tone] {
  background-image: radial-gradient(120% 60% at 50% -10%, color-mix(in srgb, var(--page) 16%, transparent), transparent 62%);
}
/* Section and page headings are ruled and inked in it. */
body[data-page-tone] .sec-head {
  border-bottom: 1px solid var(--page-line, var(--sand));
  padding-bottom: 0.55rem;
}
body[data-page-tone] .sec-head h2 .ti,
body[data-page-tone] .sec-head h2 .am,
body[data-page-tone] .pagehead h1 .ti,
body[data-page-tone] .pagehead h1 .am {
  color: var(--page-deep, var(--primary));
}
body[data-page-tone] .pagehead .crumbs a {
  color: var(--page-deep, var(--primary));
}
/* And the current entry in the bottom bar is lit by it. */
body[data-page-tone] .barbtn.on,
body[data-page-tone] .barbtn[aria-current='page'] {
  color: color-mix(in srgb, var(--page) 60%, #fff);
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE QUICK DOCK
   One glowing button halfway down the right-hand edge, on every page, opening
   into the handful of things a reader most often wants next. It breathes so it
   can be found without being hunted for, and it is placed where a thumb already
   is on a telephone held in one hand.
   ════════════════════════════════════════════════════════════════════════════ */

.quickdock {
  position: fixed;
  inset-inline-end: max(0.7rem, env(safe-area-inset-right, 0px));
  top: 50%;
  transform: translateY(-50%);
  z-index: 70;
  display: grid;
  justify-items: end;
  gap: 0.5rem;
  pointer-events: none;
}
.quickdock > * {
  pointer-events: auto;
}

.quick-items {
  display: grid;
  justify-items: end;
  gap: 0.42rem;
  /* The dock holds a dozen rows for somebody signed in on a browser that can
     install the site. On a short screen that is taller than the window, so it
     scrolls inside itself rather than running off the top — with room left for
     the two bars and the trigger below it. The scrollbar is hidden because the
     rows themselves make it obvious there is more. */
  max-height: calc(100svh - var(--bar-top, 60px) - var(--bar-bottom, 68px) - 7rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  padding-block: 0.2rem;
}
.quick-items::-webkit-scrollbar {
  display: none;
}
.quickdock[data-open='false'] .quick-items {
  visibility: hidden;
  pointer-events: none;
}

.quick-item {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.42rem 0.5rem 0.42rem 0.9rem;
  border: 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(10px) saturate(1.3);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  color: var(--ink);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.7) inset,
    0 10px 26px -14px rgba(8, 48, 44, 0.8);
  opacity: 0;
  transform: translateX(14px) scale(0.92);
  transition: transform 0.16s var(--spring), opacity 0.16s var(--ease);
}
.quickdock[data-open='true'] .quick-item {
  opacity: 1;
  transform: none;
  transition-delay: var(--stagger, 0ms);
}
.quick-item:hover {
  background: var(--paper);
  transform: translateX(-2px);
}
.quick-word {
  white-space: nowrap;
}
.quick-glyph {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 50%;
  background: var(--tone, var(--primary));
  color: var(--tone-ink, #fff);
}
.quick-glyph svg {
  width: 16px;
  height: 16px;
}

/* The trigger. Two rings leave it every few seconds; the core holds the icon. */
.quick-trigger {
  position: relative;
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  /* When the dock holds a single action the trigger is that action, and an
     anchor rather than a button. It should look identical either way. */
  text-decoration: none;
  color: inherit;
}
.quick-core {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: linear-gradient(160deg, color-mix(in srgb, var(--page, var(--primary)) 78%, #fff), var(--page, var(--primary)));
  color: #fff;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 calc(4px * var(--lift)) 0 color-mix(in srgb, var(--page, var(--primary)) 55%, #000),
    0 18px 34px -14px rgba(8, 48, 44, 0.9);
  transition: transform 0.16s var(--spring);
}
.quick-core svg {
  width: 24px;
  height: 24px;
}
.quick-trigger:hover .quick-core {
  transform: scale(1.05);
}
.quick-trigger.pressing .quick-core {
  transform: scale(0.94);
}
.quick-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--page, var(--primary)) 45%, transparent);
  animation: quick-bubble 3.2s var(--ease) infinite;
}
.quick-ring.two {
  animation-delay: 1.6s;
}
.quickdock[data-open='true'] .quick-ring {
  animation-play-state: paused;
  opacity: 0;
}
@keyframes quick-bubble {
  0% {
    transform: scale(0.86);
    opacity: 0.55;
  }
  70% {
    transform: scale(1.75);
    opacity: 0;
  }
  100% {
    transform: scale(1.75);
    opacity: 0;
  }
}

/* On a short screen the dock sits a little above centre so it never lands on
   the thing the reader is reading. */
@media (max-height: 560px) {
  .quickdock {
    top: 42%;
  }
}
@media print {
  .quickdock {
    display: none;
  }
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE SMALL SHEETS — the first visit, and the offer to install
   ════════════════════════════════════════════════════════════════════════════ */

/* A mini sheet is a question, not a page: it takes the height of its answers
   and sits low on a telephone, near the thumb that has to answer it. */
.sheet.mini {
  place-items: end center;
  padding: 0.8rem;
}
@media (min-width: 720px) {
  .sheet.mini {
    place-items: center;
  }
}
.sheet-card.mini {
  width: min(440px, 100%);
  padding: 1.05rem 1.1rem 1.15rem;
  border-radius: calc(var(--radius) + 6px);
  border-top: 4px solid var(--page, var(--primary));
  animation: sheet-pop 0.24s var(--spring) both;
}
.sheet-card.mini h2 {
  font-size: 1.05rem;
  line-height: 1.35;
  margin: 0;
}
.sheet-card.mini .lede {
  font-size: 0.88rem;
  margin-top: 0.35rem;
}
.sheet-card.mini .sheet-foot {
  margin-top: 0.85rem;
  padding-top: 0.7rem;
}
.sheet-pair {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: center;
}
@keyframes sheet-pop {
  from {
    opacity: 0;
    transform: translateY(18px) scale(0.97);
  }
}

/* Three languages, three small buttons, one row even on a narrow telephone —
   the reader cannot read the explanation yet, so there is nothing to explain. */
.langpick.mini {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.45rem;
  margin-top: 0.75rem;
}
@media (max-width: 520px) {
  .langpick.mini {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.langpick.mini button {
  padding: 0.65rem 0.35rem;
  font-size: 1.02rem;
  border-color: color-mix(in srgb, var(--tone, var(--primary)) 35%, var(--sand-deep));
}
.langpick.mini button small {
  font-size: 0.62rem;
  margin-top: 0.15rem;
}
.langpick button[aria-pressed='true'] {
  border-color: var(--tone, var(--primary));
  background: color-mix(in srgb, var(--tone, var(--primary)) 12%, var(--surface));
}

/* The second question: the services themselves, as small coloured buttons. */
.pickgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.45rem;
  margin-top: 0.75rem;
}
@media (min-width: 560px) {
  .pickgrid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.pickgrid .pick {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.6rem;
  border-radius: 14px;
  border-color: color-mix(in srgb, var(--tone, var(--primary)) 30%, var(--sand));
  text-decoration: none;
  font-size: 0.86rem;
  font-weight: 700;
  line-height: 1.25;
}
.pickgrid .pick:hover {
  border-color: var(--tone, var(--primary));
  background: color-mix(in srgb, var(--tone, var(--primary)) 8%, var(--paper));
}
.pick-glyph {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--tone, var(--primary)) 16%, var(--paper));
  color: var(--tone, var(--primary));
}
.pick-glyph svg {
  width: 17px;
  height: 17px;
}
.pick-word {
  min-width: 0;
  overflow-wrap: anywhere;
}

.installcard .lede {
  margin: 0.4rem 0 0;
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE FRONT PAGE — one screenful, then everything else
   ════════════════════════════════════════════════════════════════════════════ */

.heroblock {
  display: grid;
  gap: 1rem;
  align-content: center;
  /* As tall as the visible window and no taller. `svh` rather than `vh` because
     on a telephone `vh` measures the window with the browser's own bars hidden,
     which is a screenful the reader cannot see. The two fixed bars and the home
     indicator are already padding on <body>, so they come off here. */
  min-height: calc(100svh - var(--bar-top) - var(--bar-bottom) - var(--safe-bottom) - 0.5rem);
  padding-block: 0.9rem 2.2rem;
  position: relative;
}
/* The fixed bars already reserve their own space on the body. Keeping the home
   page inside that remaining viewport prevents either navbar from covering its
   first-screen controls, while still allowing unusually tall translated copy
   to continue naturally below the fold. */
body[data-page='home'] main {
  min-height: calc(100svh - var(--bar-top) - var(--bar-bottom) - var(--safe-bottom));
  overflow-x: clip;
}
@media (min-width: 940px) {
  .heroblock {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    align-items: center;
    gap: 2rem;
    padding-block: 1.4rem 2.6rem;
  }
  /* The words first in the reading order on a wide screen, the picture beside
     them; on a telephone the picture leads, because it is what a photograph is
     for. */
  .hero-words {
    order: -1;
  }
}
.hero-stage-wrap {
  min-width: 0;
}
.hero-stage-wrap .stage {
  aspect-ratio: 16 / 10;
  max-height: 42svh;
}
@media (min-width: 940px) {
  .hero-stage-wrap .stage {
    aspect-ratio: 4 / 3;
    max-height: none;
  }
}

.hero-words {
  display: grid;
  gap: 0.55rem;
  align-content: center;
  min-width: 0;
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0.28rem 0.7rem;
  justify-self: start;
  border-radius: 999px;
  background: color-mix(in srgb, var(--page, var(--primary)) 12%, var(--paper));
  color: var(--page-deep, var(--primary));
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.hero-eyebrow svg {
  width: 14px;
  height: 14px;
}
.hero-title {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.85rem, 6.2vw, 3.2rem);
  line-height: 1.08;
  letter-spacing: -0.015em;
}
.hero-lede {
  margin: 0;
  color: var(--muted);
  font-size: clamp(0.95rem, 2.4vw, 1.08rem);
  max-width: 46ch;
}
.hero-acts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
  margin-top: 0.35rem;
}
@media (min-width: 560px) {
  .hero-acts {
    display: flex;
    flex-wrap: wrap;
  }
}
.hero-acts .btn {
  min-width: 0;
}
.hero-acts .btn span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* On a small screen the big buttons stop being big — four of them at 54px each
   is more than a first screen can spare. */
@media (max-width: 559px) {
  .hero-acts .btn.big {
    padding: 0.7rem 0.8rem;
    min-height: 46px;
    font-size: 0.9rem;
  }
}
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1rem;
  margin: 0.35rem 0 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.hero-trust li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.hero-trust svg {
  width: 15px;
  height: 15px;
  color: var(--page, var(--primary));
}

/* The arrow at the bottom of the first screen: the only thing on the page whose
   job is to admit that there is more of it. */
.hero-more {
  position: absolute;
  bottom: 0.5rem;
  inset-inline: 0;
  justify-self: center;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  margin-inline: auto;
  border-radius: 50%;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  color: var(--page, var(--primary));
  box-shadow: 0 8px 20px -12px rgba(8, 48, 44, 0.8);
  animation: hero-nudge 2.4s var(--ease) infinite;
}
.hero-more svg {
  width: 18px;
  height: 18px;
  transform: rotate(90deg);
}
@keyframes hero-nudge {
  0%,
  100% {
    transform: translateY(0);
  }
  55% {
    transform: translateY(5px);
  }
}
@media (max-height: 620px) {
  .hero-more {
    display: none;
  }
}

/* ═════════════════════════════════════════════════════════════════════════════
   THE PAGE TRANSITION
   Pressing a link raises the logo over the page, in the middle, with the name of
   where the reader is going written under it. It covers the wait; it does not
   cause it — the navigation was never held back for the animation.
   ════════════════════════════════════════════════════════════════════════════ */

.appload-stack {
  display: grid;
  justify-items: center;
  gap: 0.85rem;
  padding: 1.2rem 1.6rem;
}
.appload-pulse {
  position: absolute;
  inset: -10px;
  z-index: 0;
  border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--page, var(--primary)) 30%, transparent), transparent 68%);
  animation: appload-pulse 1.5s var(--ease) infinite;
}
.appload-mark img {
  position: relative;
  z-index: 1;
}
@keyframes appload-pulse {
  0% {
    transform: scale(0.82);
    opacity: 0.75;
  }
  70%,
  100% {
    transform: scale(1.4);
    opacity: 0;
  }
}
.appload-ring {
  border-top-color: var(--page, var(--glow));
}
.appload-words {
  display: grid;
  justify-items: center;
  gap: 0.15rem;
  text-align: center;
  animation: appload-words 0.3s var(--ease) both;
}
.appload-lede {
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.appload-where {
  font-family: var(--display);
  font-size: clamp(1.15rem, 4.5vw, 1.6rem);
  font-weight: 700;
  color: var(--page-deep, var(--primary));
  line-height: 1.15;
}
@keyframes appload-words {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}
/* A thin bar that fills while the next page is fetched. It is honest about
   being indeterminate: it sweeps rather than claiming a percentage. */
.appload-track {
  width: min(220px, 60vw);
  height: 3px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--page, var(--primary)) 16%, transparent);
  overflow: hidden;
}
.appload-track > i {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: inherit;
  background: var(--page, var(--primary));
  animation: appload-sweep 1.1s var(--ease) infinite;
}
@keyframes appload-sweep {
  from {
    transform: translateX(-105%);
  }
  to {
    transform: translateX(255%);
  }
}

/* Everything decorative above stops for a reader who has asked the operating
   system for less movement. The global damping already shortens the durations;
   these two would otherwise sit mid-animation and look broken. */
@media (prefers-reduced-motion: reduce) {
  .quick-ring,
  .hero-more,
  .appload-pulse {
    animation: none;
    opacity: 0;
  }
  .appload-track > i {
    width: 100%;
    animation: none;
  }
}

/* ── The opening screen ──
   The same logo, over the whole page, from the very first frame the browser
   paints. It is written into every page's markup rather than drawn by script,
   which is the only way it can be on screen before the modules have loaded —
   and what it stands in front of is the half-built page: the bare headline with
   grey boxes under it, which is the one view of this site nobody should see.

   Opaque, not the frosted panel the navigation overlay uses, because there is
   nothing behind it worth showing through. The engine takes it away the moment
   the page has drawn itself. */
.appload.boot {
  z-index: 950;
  background: var(--surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: opacity 0.34s var(--ease), visibility 0.34s;
}
.appload.boot .appload-stack {
  gap: 1rem;
  max-width: min(30rem, 88vw);
}
.appload.boot .appload-mark {
  width: 132px;
  height: 132px;
}
.appload.boot .appload-mark img {
  width: 86px;
  height: 86px;
  border-radius: 24px;
  box-shadow: var(--shadow-high);
}
.appload.boot .appload-lede {
  font-family: var(--display), serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: none;
  color: color-mix(in srgb, var(--primary) 78%, var(--muted));
}
/* The subtitles. One line in the reader's language, the English underneath it
   when they are not reading English — the way the rest of the site speaks. */
.appload-sub {
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--muted);
  max-width: 32ch;
}
.appload-sub[hidden] {
  display: none;
}
/* Replayed by the engine on every change of line, so a subtitle that follows
   another arrives rather than simply being different. */
.appload-words.turn {
  animation: appload-words 0.34s var(--ease) both;
}

/* The four doors on the front page, and any other `.choice` row the painter has
   given a colour to: the ring takes the colour, the border takes a wash of it,
   and the arrow at the end stops being grey when the row is under the pointer. */
.choice[data-tone] {
  border-inline-start: 4px solid var(--tone);
}
.choice[data-tone]:hover {
  border-color: var(--tone);
}
.choice[data-tone] .ring {
  background: color-mix(in srgb, var(--tone) 14%, var(--paper));
  color: var(--tone);
}
.choice[data-tone]:hover > svg:last-child {
  color: var(--tone);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE NARROWEST SCREEN
   Everything above is written to fit any width. This block is the last stop
   for the one that is actually hardest — a 320-360px telephone held in one
   hand, which is what most of this site is read on.

   The rule the whole block serves: the page scrolls down and never sideways.
   A single element wider than the viewport shifts the entire layout, so what
   follows is the short list of things that are naturally wide — a brand name,
   a row of buttons, a drawer, a table, a wallet address — each given a way to
   get narrower rather than a way to push.
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 480px) {
  /* The gutter takes back nearly a centimetre on each side. `clamp` already
     scales it, but its floor was set for a 390px screen. */
  .wrap {
    padding-inline: 0.85rem;
  }

  /* The brand is the widest fixed thing on both bars. The mark shrinks, the
     name is allowed to shrink with it, and the transliteration under it goes:
     it is decoration, and it is the part that was setting the width. */
  
  .brand {
    flex: 0 1 auto;
    min-width: 0;
  }
  .brand .mark,
  .appbar .brand .mark {
    width: 32px;
    height: 32px;
  }
  .brand .words {
    min-width: 0;
  }
  .brand .name,
  .appbar .brand .name {
    font-size: 1.02rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .brand .name-en,
  .appbar .brand .name-en {
    display: none;
  }

  /* Buttons in a footer row go full width rather than wrapping into a ragged
     stack of different lengths — and a full-width button cannot overflow. */
  .form-foot,
  .sheet-foot {
    flex-wrap: wrap;
  }
  .form-foot > .btn,
  .sheet-foot > .btn {
    flex: 1 1 100%;
    justify-content: center;
  }

  /* Panels that hang off an edge take the width they are given, minus a thumb's
     width so the page behind is still visible and tappable. The full-screen
     veils (`.sheet`, `.drawer`) are left alone — they are meant to cover the
     screen; it is the card inside each of them that is sized here. */
  .barpop {
    width: min(360px, calc(100vw - 1rem));
    right: 0.5rem;
  }
  .drawer .sheet,
  .sheet-card {
    width: 100%;
    max-width: 100%;
  }

  /* A table never shrinks below the width of its own columns; it scrolls inside
     .tablewrap instead. Here it is allowed to start narrower, so that on most
     rows there is nothing to scroll at all. */
  table {
    min-width: 520px;
    font-size: 0.86rem;
  }
  thead th,
  tbody td {
    padding: 0.6rem 0.65rem;
  }

  /* Two-up grids that were still two-up at this width. */
  
  .row2,
  .grid2 {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The strings with no spaces in them: addresses, hashes, references,
     generated passwords. They break mid-string rather than setting the width
     of the page. */
  
  .copyrow .v,
  .token,
  .codegrid code,
  .dispatch {
    font-size: 0.9rem;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* Headings are the other long word — a service title or an Ethiopic name with
     no break opportunity in it. */
  h1,
  h2,
  h3 {
    overflow-wrap: break-word;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE TWO APPS, AS OBJECTS

   Haliot and Hakimm both install from the browser rather than from a store, and
   that is a hard thing to ask for in a sentence and an easy thing to show. So
   the download sheet holds two phones, built out of elements rather than
   screenshots: a body, a screen standing forward of it on the Z axis, an icon
   forward of that again, and a sheet of glare across the front. The scene has
   perspective, so those three depths part company as the phone turns — which is
   what makes it read as an object rather than as a drawing of one.

   Nothing here is an image, which is the point: the whole showcase costs a few
   hundred bytes over a stylesheet the reader already has.
   ══════════════════════════════════════════════════════════════════════════ */

.appstage {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.8rem, 3vw, 1.6rem);
  margin-block: 0.4rem 0.2rem;
}
@media (max-width: 620px) {
  .appstage {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.4rem;
  }
}
.appstage-note {
  margin-top: 0.9rem;
  text-align: center;
}

.app3d {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  text-align: center;
  padding: 1rem 0.8rem 0.9rem;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--tone, var(--primary)) 26%, var(--sand));
  background:
    radial-gradient(120% 80% at 50% 0%, color-mix(in srgb, var(--tone, var(--primary)) 16%, transparent), transparent 70%),
    var(--paper);
}

/* The scene. `perspective` lives here and not on the phone, so the vanishing
   point is the middle of this box and the two phones do not each invent their
   own. */
.app3d-scene {
  perspective: 900px;
  perspective-origin: 50% 45%;
  width: 100%;
  padding-block: 0.6rem 1.4rem;
  position: relative;
  display: grid;
  place-items: center;
}

/* The drift, kept on its own element: the pointer owns the rotation below, and
   two transforms on one element would mean one of them winning. */
.app3d-float {
  animation: appFloat 7s ease-in-out infinite;
  transform-style: preserve-3d;
}

.app3d-phone {
  position: relative;
  width: 128px;
  height: 226px;
  border-radius: 26px;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 6deg)) rotateY(var(--ry, -17deg));
  transition: transform 0.28s cubic-bezier(0.22, 0.61, 0.36, 1);
  background: linear-gradient(
    155deg,
    color-mix(in srgb, var(--tone, var(--primary)) 40%, #0d1512) 0%,
    #0d1512 46%,
    #05100c 100%
  );
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -2px 0 rgba(0, 0, 0, 0.5),
    0 calc(18px * var(--lift)) calc(34px * var(--lift)) -16px rgba(6, 24, 18, 0.75);
}

/* The thickness. A second slab pushed back along Z, wider than the face by a
   hair, so the phone has a visible side when it turns rather than being a card. */
.app3d-back {
  position: absolute;
  inset: -1px;
  border-radius: 27px;
  background: linear-gradient(160deg, #16211c, #05100c);
  transform: translateZ(-11px);
}

.app3d-screen {
  position: absolute;
  inset: 7px;
  border-radius: 20px;
  transform: translateZ(3px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.28rem;
  padding: 1.15rem 0.55rem 0.7rem;
  overflow: hidden;
  color: #fff;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tone, var(--primary)) 88%, #fff) 0%,
    var(--tone, var(--primary)) 42%,
    color-mix(in srgb, var(--tone, var(--primary)) 66%, #04120d) 100%
  );
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

/* The bar at the top of every telephone made since 2017. */
.app3d-notch {
  position: absolute;
  top: 6px;
  left: 50%;
  width: 42px;
  height: 5px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.42);
  transform: translateX(-50%);
}

/* The icon, further forward than the screen it sits on: this is the layer that
   parts company with the rest as the phone turns, and the reason the thing
   reads as having depth at all. */
.app3d-mark-wrap {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.94);
  transform: translateZ(16px);
  box-shadow:
    0 2px 0 rgba(0, 0, 0, 0.24),
    0 10px 16px -8px rgba(4, 18, 13, 0.8);
}
.app3d-mark {
  width: 34px;
  height: 34px;
  object-fit: contain;
}
.app3d-mark.glyph {
  display: grid;
  place-items: center;
  color: var(--tone, var(--primary));
}
.app3d-mark.glyph svg {
  width: 26px;
  height: 26px;
}

.app3d-word {
  transform: translateZ(10px);
  font-family: var(--display, inherit);
  font-weight: 800;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
}
.app3d-domain {
  transform: translateZ(8px);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  opacity: 0.85;
}

/* Three bars where a list of services would be. Deliberately unreadable: a
   legible fake screenshot is wrong the day the real screen changes. */
.app3d-rows {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
  margin-top: 0.4rem;
  transform: translateZ(6px);
}
.app3d-rows i {
  height: 9px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.34);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.app3d-rows i:nth-child(2) {
  width: 82%;
  background: rgba(255, 255, 255, 0.26);
}
.app3d-rows i:nth-child(3) {
  width: 64%;
  background: rgba(255, 255, 255, 0.2);
}

/* The glass. One diagonal sheet across the whole front, in front of everything,
   and no pointer events so it never eats a press. */
.app3d-glare {
  position: absolute;
  inset: 0;
  border-radius: 26px;
  transform: translateZ(9px);
  pointer-events: none;
  background: linear-gradient(
    118deg,
    rgba(255, 255, 255, 0.42) 0%,
    rgba(255, 255, 255, 0.12) 26%,
    transparent 47%,
    transparent 100%
  );
}

/* What the phone is standing over. It is not the phone's own shadow — that is on
   the box-shadow above — but the pool underneath, which is what tells the reader
   the object is off the page rather than printed on it. */
.app3d-shadow {
  position: absolute;
  bottom: 0.55rem;
  width: 92px;
  height: 14px;
  border-radius: 50%;
  background: rgba(6, 24, 18, calc(0.32 * var(--lift)));
  filter: blur(7px);
  animation: appShadow 7s ease-in-out infinite;
}

.app3d-name {
  font-size: 1.02rem;
  color: color-mix(in srgb, var(--tone, var(--primary)) 76%, var(--ink));
}
.app3d-tag {
  font-size: 0.86rem;
  color: var(--muted);
  margin: 0;
}
.app3d-hint {
  font-size: 0.76rem;
  color: var(--muted);
  margin: 0.15rem 0 0.2rem;
}
.app3d-act {
  margin-top: 0.35rem;
  display: flex;
  justify-content: center;
  width: 100%;
}
.app3d-act .btn {
  min-width: 10rem;
  justify-content: center;
}

@keyframes appFloat {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-9px);
  }
}
@keyframes appShadow {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(0.86);
    opacity: 0.7;
  }
}

/* Asked for stillness: the phone keeps its pose and stops moving. The pose is
   the whole illustration, so it stays. */
@media (prefers-reduced-motion: reduce) {
  .app3d-float,
  .app3d-shadow {
    animation: none;
  }
  .app3d-phone {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE NOTICE BOARD
   The seven services, and the furniture they share.

   Everything below is written against tokens that already exist — `--tone`,
   `--lift`, `--paper`, `--sand`, `--radius` — so the administrator's colour
   and depth settings reach these pages without a second set of controls, and
   `data-tone` on a card paints it from the same line as a button.
   ═══════════════════════════════════════════════════════════════════════════ */

/* A row of pills. `.chips` already exists for the interactive ones; this is its
   quiet sibling, for the pills that only state a fact. */
.pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}

/* ── the raised coloured button ────────────────────────────────────────────
   The doorways were asked for in these words: coloured, three-dimensional,
   animated. The relief is four things at once — a lit top edge, a shaded
   bottom one, a solid skirt beneath and a soft shadow under that — and all
   four are scaled by `--lift`, so turning depth down to flat turns this off
   with everything else on the site.

   The colour comes from `--face` when the row carries an accent of its own and
   from `--tone` otherwise, which means a hospital can wear its own blue
   without any of this knowing what a hospital is.
   ─────────────────────────────────────────────────────────────────────────── */
.btn3d {
  --btn3d-face: var(--face, var(--tone, var(--primary)));
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0.62rem 1.15rem;
  border: 0;
  border-radius: calc(var(--radius) - 4px);
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  color: var(--tone-ink, #fff);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--btn3d-face) 78%, #fff) 0%,
    var(--btn3d-face) 52%,
    color-mix(in srgb, var(--btn3d-face) 90%, #000) 100%
  );
  box-shadow:
    0 calc(4px * var(--lift)) 0 color-mix(in srgb, var(--btn3d-face) 56%, #000),
    0 calc(11px * var(--lift)) calc(22px * var(--lift)) -10px rgba(8, 30, 24, 0.55);
  transition: transform 0.16s ease, box-shadow 0.16s ease, filter 0.16s ease;
}
.btn3d.big {
  min-height: 62px;
  padding: 0.8rem 1.5rem;
  font-size: 1.05rem;
  width: 100%;
}
/* The lit and shaded edges, as an overlay rather than more shadows on the
   element itself: the press below only has to move the element, and the relief
   inside it stays put instead of jumping. */
.btn3d-edge {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, calc(0.52 * var(--lift))),
    inset 0 -2px 0 rgba(0, 0, 0, calc(0.24 * var(--lift)));
}
.btn3d-face {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-width: 0;
}
.btn3d-face svg {
  width: 20px;
  height: 20px;
  flex: none;
}
.btn3d-face span {
  min-width: 0;
}
.btn3d:hover {
  transform: translateY(-2px);
  filter: saturate(1.08);
}
.btn3d:active {
  transform: translateY(calc(3px * var(--lift)));
  box-shadow:
    inset 0 2px 8px rgba(0, 0, 0, 0.34),
    0 1px 0 color-mix(in srgb, var(--btn3d-face) 56%, #000);
}
.btn3d:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
}

/* Two or three of them side by side, wrapping to full width on a telephone
   rather than shrinking until the words break. */
.row3d {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.9rem;
}
.row3d > * {
  flex: 1 1 14rem;
}

/* ── the filter strip the three boards share ───────────────────────────────
   One shape for the vacancy board, the marketplace and the talent wall: a
   search field, then one labelled row of chips per facet. The label sits above
   its row rather than beside it, because a Tigrigna word and its English
   translation are rarely the same width and a beside-label makes the rows
   ragged. */
.notefilters {
  display: grid;
  gap: 0.7rem;
  margin: clamp(0.8rem, 2.5vw, 1.2rem) 0;
}
.facetrow {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
}
.facetrow-label {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ── the facts under a notice ──────────────────────────────────────────────
   Profession, number of places, sex, experience, education, closing date. A
   table would be the honest markup, but a table of six two-word rows reads as
   a spreadsheet on a telephone; this is the same information as cards that
   reflow. */
.notefacts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 0.55rem;
}
.notefact {
  display: grid;
  gap: 0.1rem;
  padding: 0.55rem 0.7rem;
  border-radius: calc(var(--radius) - 8px);
  background: color-mix(in srgb, var(--tone, var(--primary)) 6%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--tone, var(--primary)) 16%, var(--sand));
}
.notefact small {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.notefact b {
  font-size: 0.98rem;
  line-height: 1.3;
}

/* ── the vacancy board ─────────────────────────────────────────────────────
   One column on a telephone, two from 760px. Not three: a vacancy card holds
   an institution's name and a town as well as a profession, and at three
   columns on a laptop every one of those wraps. */
.notegrid {
  display: grid;
  gap: 0.8rem;
}
@media (min-width: 760px) {
  .notegrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.notecard {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.9rem 1rem;
  text-decoration: none;
  color: inherit;
}
.notecard:hover {
  transform: translateY(-2px);
}
.notecard-mark {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  flex: none;
  border-radius: 14px;
  background: color-mix(in srgb, var(--tone, var(--primary)) 14%, var(--paper));
  color: color-mix(in srgb, var(--tone, var(--primary)) 82%, #000);
}
.notecard-mark svg {
  width: 22px;
  height: 22px;
}
.notecard-body {
  display: grid;
  gap: 0.15rem;
  min-width: 0;
}
.notecard-body h3 {
  font-size: 1.06rem;
}
.notecard-who {
  margin: 0;
  font-weight: 600;
  font-size: 0.9rem;
  color: color-mix(in srgb, var(--tone, var(--primary)) 68%, var(--ink));
}
.notecard-body p {
  margin: 0;
}
.notecard-go {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  color: var(--muted);
  background: var(--surface);
  transition: transform 0.16s ease, background 0.16s ease;
}
.notecard:hover .notecard-go {
  transform: translateX(3px);
  background: color-mix(in srgb, var(--tone, var(--primary)) 16%, var(--surface));
}
.notecard-go svg {
  width: 16px;
  height: 16px;
}

/* The institution above the notice, and the list of documents it asks for. */
.employer {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.9rem;
  align-items: start;
}
.employer-mark {
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  border-radius: 16px;
  overflow: hidden;
  background: color-mix(in srgb, var(--tone, var(--primary)) 12%, var(--surface));
  color: color-mix(in srgb, var(--tone, var(--primary)) 80%, #000);
}
.employer-mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.employer-mark svg {
  width: 26px;
  height: 26px;
}
.ticklist {
  display: grid;
  gap: 0.4rem;
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
}
.ticklist li {
  display: flex;
  align-items: start;
  gap: 0.5rem;
}
.ticklist svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 0.22rem;
  color: var(--good);
}

/* The application itself, which is the point of the page. */
.apply {
  scroll-margin-top: calc(var(--bar-top) + 1rem);
}

/* ── the drawn notice ─────────────────────────────────────────────────────
   The canvas is drawn at 1080×1350 and shown scaled, so what a reader presses
   download on is the bitmap they were already looking at. The frame keeps that
   ratio at every width, which stops the picture from jumping a hundred pixels
   taller the moment the fonts land. */
.posterpanel {
  display: grid;
  gap: 0.9rem;
}
.postershot-wrap {
  max-width: 26rem;
  margin-inline: auto;
  width: 100%;
  border-radius: calc(var(--radius) - 4px);
  overflow: hidden;
  background: var(--surface);
  box-shadow:
    0 1px 0 var(--sand-deep),
    0 calc(18px * var(--lift)) calc(40px * var(--lift)) -24px rgba(26, 36, 22, 0.6);
}
.postershot {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1080 / 1350;
}
.themepicker {
  display: grid;
  gap: 0.35rem;
}
/* The message that travels with the picture. Open by default it would push the
   buttons off the first screen; inside a `<details>` it is one press away for
   the administrator who wants to change the wording. */
.sharewrap {
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) - 6px);
  background: var(--paper);
  padding: 0.5rem 0.75rem;
}
.sharewrap > summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 0.9rem;
}
.sharetext {
  width: 100%;
  margin-top: 0.6rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) - 8px);
  background: var(--surface);
  color: inherit;
  font: inherit;
  font-size: 0.92rem;
  line-height: 1.55;
  resize: vertical;
}

/* ── the marketplace ──────────────────────────────────────────────────────
   Goods are looked at before they are read, so this board is photographs
   first: three across on a laptop, two on a telephone, and the price in the
   card rather than on the detail page. */
.shopgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 0.8rem;
}
@media (min-width: 700px) {
  .shopgrid {
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  }
}
.shopcard {
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
}
.shopcard:hover {
  transform: translateY(-2px);
}
.shopcard-shot {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--surface);
  display: grid;
  place-items: center;
  color: var(--sand-deep);
}
.shopcard-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.shopcard-shot svg {
  width: 30px;
  height: 30px;
}
/* Sold is said on the picture, not under it. A buyer scrolling a wall of
   photographs never reaches the caption. */
.shopcard-sold {
  position: absolute;
  inset-inline-start: 0.5rem;
  inset-block-start: 0.5rem;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}
.shopcard-body {
  display: grid;
  gap: 0.22rem;
  align-content: start;
  padding: 0.7rem 0.8rem 0.85rem;
  min-width: 0;
}
.shopcard-body h3 {
  font-size: 0.98rem;
  line-height: 1.25;
}
.shopcard-body p {
  margin: 0;
}
.shopcard-price {
  font-family: var(--display);
  font-size: 1.08rem;
  color: color-mix(in srgb, var(--gold) 82%, #000);
}

/* One item, at length. A large picture with a strip under it that swaps what
   the large one shows — deliberately not a timed carousel, because a buyer
   comparing two photographs of the same sofa should not have them move. */
.shopgallery {
  display: grid;
  gap: 0.6rem;
}
.shopbig {
  width: 100%;
  max-height: 62vh;
  object-fit: contain;
  border-radius: calc(var(--radius) - 4px);
  background: var(--surface);
}
.shopthumbs {
  display: flex;
  gap: 0.45rem;
  overflow-x: auto;
  padding-bottom: 0.2rem;
  scrollbar-width: thin;
}
.shopthumb {
  flex: none;
  width: 68px;
  height: 68px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
  cursor: pointer;
}
.shopthumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.shopthumb.on {
  border-color: var(--primary);
}
.shopthumb:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}
.shopfacts {
  display: grid;
  gap: 0.7rem;
}
.shopprice {
  font-family: var(--display);
  font-size: clamp(1.4rem, 4vw, 1.9rem);
  color: color-mix(in srgb, var(--gold) 82%, #000);
}
/* The specification, as the pairs it actually is. `<dl>` in two columns, so
   "Screen — 6.1 inch" reads as one fact rather than two lines. */
.speclist {
  display: grid;
  grid-template-columns: minmax(7rem, auto) minmax(0, 1fr);
  gap: 0.3rem 0.9rem;
  margin: 0;
}
.speclist dt {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--muted);
}
.speclist dd {
  margin: 0;
  min-width: 0;
}
/* And the seller's side of the same thing: rows of name and value they can add
   to, because nobody knows in advance how many facts a generator has. */
.specedit {
  display: grid;
  gap: 0.4rem;
}
.specedit-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto;
  gap: 0.4rem;
  align-items: center;
}

/* ── the talent wall ──────────────────────────────────────────────────────
   Three rows of twelve, as asked. The geometry comes from the API as
   `--gallery-rows` and `--gallery-columns`, so the slide the server fills and
   the slide the reader sees cannot disagree.

   Twelve columns only exist where twelve columns fit. Below 1000px the wall
   packs as many portraits per row as the screen allows and lets the rows run
   on: the same thirty-six people per slide, in the shape the screen has.
   ─────────────────────────────────────────────────────────────────────────── */
.gallerypanel {
  display: grid;
  gap: 0.8rem;
}
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 0.5rem;
  min-height: calc(var(--gallery-rows, 3) * 118px);
}
@media (min-width: 1000px) {
  .gallery {
    grid-template-columns: repeat(var(--gallery-columns, 12), minmax(0, 1fr));
  }
}
/* One person. A framed portrait, which is the whole point of the thing: a wall
   of faces an employer reads before they read a single word. */
.frame {
  display: grid;
  grid-template-rows: auto auto auto auto;
  gap: 0.15rem;
  padding: 0.35rem 0.35rem 0.5rem;
  border: 1px solid var(--sand);
  border-radius: 14px;
  background: var(--paper);
  text-align: center;
  font: inherit;
  color: inherit;
  cursor: pointer;
  position: relative;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
  box-shadow: 0 1px 0 var(--sand-deep);
  animation: frameIn 0.34s ease both;
  animation-delay: var(--frame-in, 0ms);
}
.frame:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--tone, var(--primary)) 44%, var(--sand));
  box-shadow:
    0 2px 0 var(--sand-deep),
    0 calc(16px * var(--lift)) calc(30px * var(--lift)) -18px rgba(26, 36, 22, 0.6);
}
.frame:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}
.frame.featured {
  border-color: color-mix(in srgb, var(--gold) 62%, var(--sand));
  background: linear-gradient(180deg, color-mix(in srgb, var(--gold) 9%, var(--paper)), var(--paper));
}
.frame-shot {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface);
  color: var(--sand-deep);
}
.frame-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.frame-shot svg {
  width: 26px;
  height: 26px;
}
/* Three lines that must never become four: a wall whose cells are different
   heights stops reading as a wall. */
.frame-name,
.frame-what,
.frame-where {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.3;
}
.frame-name {
  font-weight: 700;
  font-size: 0.78rem;
}
.frame-what {
  font-size: 0.7rem;
  color: color-mix(in srgb, var(--tone, var(--primary)) 70%, var(--ink));
}
.frame-where {
  font-size: 0.66rem;
  color: var(--muted);
}
/* Available for work: a green tick in the corner of the portrait. */
.frame-open {
  position: absolute;
  inset-block-start: 0.45rem;
  inset-inline-end: 0.45rem;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--good);
  color: #fff;
}
.frame-open svg {
  width: 12px;
  height: 12px;
}

/* Moving between slides. The dots say how many there are, the words say which
   one this is, and the pause is visible — a wall that slides while somebody is
   reading a name is worse than one that never moved. */
.gallerybar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.gallerydots {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
.gallerydot {
  width: 10px;
  height: 10px;
  padding: 0;
  border: 1px solid var(--sand-deep);
  border-radius: 999px;
  background: var(--surface);
  cursor: pointer;
  transition: background 0.16s ease, transform 0.16s ease;
}
.gallerydot.on {
  background: var(--primary);
  border-color: var(--primary);
  transform: scale(1.25);
}
.gallerydot:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* One person, opened. The portrait large enough to be a face rather than a
   thumbnail, and everything else beside it. */
.personcard {
  display: grid;
  gap: 0.9rem;
}
@media (min-width: 620px) {
  .personcard {
    grid-template-columns: 150px minmax(0, 1fr);
    align-items: start;
  }
}
.personcard-shot {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border-radius: calc(var(--radius) - 4px);
  overflow: hidden;
  background: var(--surface);
  color: var(--sand-deep);
}
.personcard-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.personcard-shot svg {
  width: 40px;
  height: 40px;
}

/* ── choosing photographs and documents ───────────────────────────────────
   The same two pickers serve an applicant's certificates, a seller's
   photographs and an institution's logo. The file input itself is
   screen-reader-only; the button in front of it is what anybody sees. */
.picker-shot {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
}
.pickshot {
  display: grid;
  place-items: center;
  width: 84px;
  height: 84px;
  flex: none;
  border-radius: 14px;
  overflow: hidden;
  background: var(--surface);
  border: 1px dashed var(--sand-deep);
}
.pickshot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pickshot-empty {
  display: grid;
  place-items: center;
  color: var(--sand-deep);
}
.pickshot-empty svg {
  width: 26px;
  height: 26px;
}
.picker-files {
  display: grid;
  gap: 0.5rem;
  justify-items: start;
}
.picker-files > .btn {
  justify-self: start;
}
/* Chosen documents, as a list with their sizes — so somebody who attached the
   wrong four-megabyte photograph can see which one it was. */
.filelist {
  display: grid;
  gap: 0.3rem;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
}
.filelist li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--sand);
  border-radius: calc(var(--radius) - 10px);
  background: var(--paper);
}
.filelist svg {
  width: 17px;
  height: 17px;
  color: var(--muted);
}
.filelist-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.88rem;
  font-weight: 600;
}
/* Photographs, as thumbnails in the order they will be shown. The first is the
   cover and says so, because "the first one is the cover" is a rule nobody
   guesses. */
.photostrip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 0.45rem;
  width: 100%;
}
.photostrip-cell {
  position: relative;
  margin: 0;
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--sand);
}
.photostrip-cell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.photostrip-cell.cover {
  border-color: color-mix(in srgb, var(--primary) 52%, var(--sand));
}
.photostrip-cell figcaption {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  padding: 0.12rem 0.3rem;
  background: rgba(28, 43, 71, 0.72);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  text-align: center;
}
.photostrip-tools {
  position: absolute;
  inset-block-start: 0.25rem;
  inset-inline-end: 0.25rem;
  display: flex;
  gap: 0.2rem;
}
.photostrip-tools .iconbtn {
  background: rgba(255, 251, 242, 0.9);
}
/* The reorder button points backwards: it moves this photograph earlier. */
.photostrip-tools .iconbtn:first-child svg {
  transform: rotate(180deg);
}

/* ── reaching somebody ────────────────────────────────────────────────────
   Telephone, WhatsApp, Telegram, email — each in its own colour, which is the
   one place on this site where colour is not decoration: somebody looking for
   the green button is looking for WhatsApp. */
.reach {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin-top: 0.6rem;
}

/* ── the three doorways ───────────────────────────────────────────────────
   Home care, online consultation, the hospitals list. One card each, with the
   coloured raised button that takes somebody through and the application file
   beside it. */
.doorintro {
  margin-bottom: 0.9rem;
}
.doorgrid {
  display: grid;
  gap: 0.9rem;
}
@media (min-width: 900px) {
  .doorgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.doorcard {
  position: relative;
  display: grid;
  gap: 0.8rem;
  align-content: start;
  /* The entrance: each card a beat behind the one above it, which is animation
     in service of reading order. The delay is set on the element by the module
     and capped there, so a list of forty hospitals does not take four seconds
     to arrive. */
  animation: doorIn 0.42s ease both;
  animation-delay: var(--reveal-delay, 0ms);
  border-block-start: 3px solid var(--tone, var(--primary));
}
.doorcard-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.8rem;
  align-items: center;
}
.doorcard-head h2 {
  font-size: 1.2rem;
}
.doorcard-head p {
  margin: 0.1rem 0 0;
}
.doorcard-mark {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  overflow: hidden;
  background: color-mix(in srgb, var(--tone, var(--primary)) 14%, var(--surface));
  color: color-mix(in srgb, var(--tone, var(--primary)) 80%, #000);
}
.doorcard-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 0.3rem;
}
.doorcard-mark svg {
  width: 26px;
  height: 26px;
}
.doorcard-body {
  display: grid;
  gap: 0.6rem;
  min-width: 0;
}
/* Photographs of the place, as a strip that scrolls sideways: a hospital with
   nine pictures should not push its telephone number off the screen. */
.doorshots {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding-bottom: 0.25rem;
  scrollbar-width: thin;
}
.doorshots figure {
  flex: none;
  width: 11rem;
  margin: 0;
}
.doorshots img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
  background: var(--surface);
}
.doorshots figcaption {
  margin-top: 0.2rem;
  font-size: 0.74rem;
  color: var(--muted);
}
/* "Tell them you came through habereta.org." On the hospitals page only. */
.doorcard-via {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0.45rem 0.6rem;
  border-radius: calc(var(--radius) - 10px);
  background: color-mix(in srgb, var(--accent) 13%, var(--paper));
  border: 1px dashed color-mix(in srgb, var(--accent) 42%, var(--sand));
  font-size: 0.85rem;
  font-weight: 600;
}
.doorcard-via svg {
  width: 17px;
  height: 17px;
  flex: none;
  color: color-mix(in srgb, var(--accent) 80%, #000);
}
/* The office's own edit, on the card rather than only in the panel: somebody
   who notices a wrong link should be able to fix it where they noticed it. */
.doorcard-edit {
  justify-self: end;
}

@keyframes doorIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes frameIn {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Asked for stillness. The blanket rule near the top of this file collapses
   every duration but leaves the delays alone, which would hold a staggered
   card at opacity zero for half a second — so these two are switched off
   outright rather than merely hurried. */
@media (prefers-reduced-motion: reduce) {
  .doorcard,
  .frame {
    animation: none;
  }
  .btn3d:hover,
  .notecard:hover,
  .shopcard:hover,
  .frame:hover {
    transform: none;
  }
}

/* ── the website-building page ─────────────────────────────────────────────
   The portfolio, the agreement and the brief. */

/* What has already been built. Two columns from 820px; the screenshots inside
   a card scroll sideways rather than making the card taller than the screen. */
.worksgrid {
  display: grid;
  gap: 0.9rem;
  margin-top: 0.8rem;
}
@media (min-width: 820px) {
  .worksgrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.workcard {
  display: grid;
  grid-template-rows: auto 1fr;
  overflow: hidden;
}
.workcard-shot {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  background: var(--surface);
  color: var(--sand-deep);
}
.workcard-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.workcard-shot svg {
  width: 34px;
  height: 34px;
}
.workcard-body {
  display: grid;
  gap: 0.45rem;
  justify-items: start;
  align-content: start;
  padding: 0.85rem 1rem 1rem;
  min-width: 0;
}
.workcard-body p {
  margin: 0;
}
.workcard-body > * {
  max-width: 100%;
}
/* Screenshots of the inside pages, which is what makes a sample persuasive
   rather than decorative. */
.workshots {
  display: flex;
  gap: 0.45rem;
  overflow-x: auto;
  padding-bottom: 0.25rem;
  width: 100%;
  scrollbar-width: thin;
}
.workshots figure {
  flex: none;
  width: 9rem;
  margin: 0;
}
.workshots img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: 1px solid var(--sand);
  border-radius: 10px;
  background: var(--surface);
}
.workshots figcaption {
  margin-top: 0.15rem;
  font-size: 0.7rem;
  color: var(--muted);
}

/* The agreement, said before the form rather than after it. */
.deal {
  border-inline-start: 3px solid var(--accent);
}

/* ── the brief ────────────────────────────────────────────────────────────
   One section at a time with a count above it. The count is the only reason a
   form of this length gets finished, and it is recomputed from the sections
   that actually apply — saying "nine of twelve" when three of them no longer
   exist is the kind of small lie that makes somebody stop trusting a form. */
.brief {
  scroll-margin-top: calc(var(--bar-top) + 1rem);
}
.briefbody {
  display: grid;
  gap: 0.9rem;
}
.briefrail {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.7rem;
}
.briefrail-count {
  font-family: var(--display);
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
  color: var(--muted);
}
.briefrail-track {
  height: 8px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--sand);
  overflow: hidden;
}
.briefrail-track i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--primary), var(--accent));
  transition: width 0.28s ease;
}
/* Back on the left, forward on the right, and forward stays reachable with one
   thumb on a telephone. */
.brieffoot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: space-between;
}
.brieffoot .btn {
  flex: 1 1 9rem;
  justify-content: center;
}
/* A yes/no or a short list of choices, answered by pressing rather than by
   opening a menu — which is the difference between a twenty-question form that
   gets finished and one that does not. */
.askpick {
  display: grid;
  gap: 0.3rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE HUB — the seven services, one key each
   ═══════════════════════════════════════════════════════════════════════════
   Three across on a desk, two on a tablet, one on a telephone. Not a fixed
   three-column grid: the seventh key would sit alone in a row of three and read
   as though an eighth were missing, whereas `auto-fit` lets the last row fill
   the width it has.

   Each key takes its colour from the navigation's, through `[data-tone]`, so the
   gold of vacancies here is the same gold as the menu row that leads to it. The
   card sets nothing but the tone; every shade in it is derived. */
.hubgrid {
  display: grid;
  gap: 0.9rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}
.hubcard {
  position: relative;
  display: grid;
  /* Marks, words, button — with the words taking the slack, so the seven
     buttons line up along the bottom however unequal the descriptions above
     them are. */
  grid-template-rows: auto 1fr auto;
  gap: 0.75rem;
  overflow: hidden;
  border-block-start: 3px solid var(--tone, var(--primary));
  animation: hubIn 0.4s ease both;
  animation-delay: var(--reveal-delay, 0ms);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
/* A wash of the key's own colour behind the top of the card, so the seven are
   told apart by colour before a word of any of them has been read. */
.hubcard::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 6.5rem;
  background: linear-gradient(180deg, color-mix(in srgb, var(--tone, var(--primary)) 13%, transparent), transparent);
  pointer-events: none;
}
.hubcard:hover {
  transform: translateY(calc(-3px * var(--lift)));
  box-shadow: 0 calc(16px * var(--lift)) calc(34px * var(--lift)) -18px rgba(8, 30, 24, 0.45);
}
.hubcard > * {
  position: relative;
}
/* The icon and whatever the card can say in a word: how many are listed, and
   what it costs today. */
.hubcard-marks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: center;
}
.hubcard-mark {
  display: inline-grid;
  place-items: center;
  inline-size: 3rem;
  block-size: 3rem;
  border-radius: calc(var(--radius) * 0.7);
  color: var(--tone-ink, #fff);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tone, var(--primary)) 76%, #fff) 0%,
    var(--tone, var(--primary)) 58%,
    color-mix(in srgb, var(--tone, var(--primary)) 88%, #000) 100%
  );
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    inset 0 -1px 0 rgba(0, 0, 0, 0.22),
    0 calc(4px * var(--lift)) calc(12px * var(--lift)) -6px rgba(8, 30, 24, 0.5);
}
.hubcard-mark svg {
  inline-size: 1.6rem;
  block-size: 1.6rem;
}
.hubcard-body {
  display: grid;
  gap: 0.35rem;
}
.hubcard-body .sec-head {
  margin: 0;
}
.hubcard-body .sec-head h2 {
  font-size: 1.15rem;
  line-height: 1.25;
}
.hubcard-lede {
  margin: 0;
  color: var(--muted);
  font-size: 0.93rem;
  line-height: 1.55;
}
/* The key's own button, pushed to the bottom so the seven line up however
   different the lengths of their descriptions are. */
.hubcard-foot {
  display: grid;
}
.hubnote {
  margin-block-start: 1rem;
}

@keyframes hubIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The blanket reduced-motion rule collapses durations but leaves delays alone,
   which would hold a staggered key invisible for the better part of a second.
   Named here so it does not. */
@media (prefers-reduced-motion: reduce) {
  .hubcard {
    animation: none;
  }
  .hubcard:hover {
    transform: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE FRONT PAGE — the eight doors and the two shortcuts
   ═══════════════════════════════════════════════════════════════════════════
   The doors used to be four, in one column, which was the right shape for four.
   They are now the seven services plus the reader's own account, and eight rows
   in one column is a front page somebody has to scroll twice to see. Two across
   as soon as there is room, and each row keeps its full wording — a door whose
   description has been cut to fit is a door nobody presses. */
@media (min-width: 820px) {
  .choices {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* What the service costs today, under its own description. Pushed down a little
   so it reads as a fact about the door rather than as the end of the sentence
   above it. */
.choice .pill {
  margin-block-start: 0.4rem;
}

/* The two platforms this board hands a reader on to — haliot.net and
   hakimm.com. Quieter than the buttons above them on purpose: each has a whole
   page of its own further down, and this is the shortcut, not the service. */
.hero-away {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-block-start: 0.2rem;
}
.hero-away .btn svg:last-child {
  inline-size: 0.85rem;
  block-size: 0.85rem;
  opacity: 0.7;
}
