/* LimeAdmissions — direction C2 + C3.
   Dark header and hero, heavy Archivo display, bright accent, proof above the
   fold, anchored pricing. Google Fonts is the only external request. */

:root {
  --ink: #111311;
  --body: #40483F;
  --muted: #5A645D;
  --paper: #ffffff;
  --wash: #f2f6e8;        /* sage-50  — the main band tint */
  --wash-deep: #e6efd2;   /* sage-100 — table headers, widget head, selected tiles */
  --line: #dbe4c9;

  --accent: #b8e62e;
  --on-accent: #111311;
  --accent-hover: #a6d227;

  /* Link olive. The brief specifies #5f7d10, which clears AA on white (4.74)
     but NOT on the sage bands (4.32 on sage-50, 3.98 on sage-100). Darkened one
     step so body links pass 4.5 on every surface they actually sit on. */
  --link: #55700f;
  --link-hover: #48600b;

  /* White text, for the only things still painted ink: solid buttons and the
     one small tag. Everything larger went light in the 28 Aug restyle. */
  --on-ink: #ffffff;

  /* The ground a scrollable table sits on, used by .table-scroll's edge fades.
     Declared here and not on .table-scroll itself: a custom property set on the
     element wins over an inherited one, which would make the per-band overrides
     dead code. */
  --scroll-fade: var(--paper);

  /* These three used to mean "readable text on a dark band". Every band is
     light now, so they are REPOINTED rather than deleted: 53 call sites across
     this file and build.py follow the change at once instead of being edited
     one by one. The names are legacy — read them as "text on a band". */
  --on-dark: #111311;
  --on-dark-body: #40483F;
  --on-dark-muted: #5A645D;

  /* rem, not ch: a ch measure scales with font-size, so headings and body
     text would land on different left edges. */
  --measure: 42rem;
  --wide: 1040px;
  --pad: 44px;
}

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

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

body {
  margin: 0;
  font-family: Archivo, "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
}

/* ---------- header (white, with a hard ink rule under it) ------------------ */

.site-header {
  background: var(--paper);
  color: var(--ink);
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  padding: 20px var(--pad);
}

.brand {
  font-weight: 900;
  font-size: 1.2rem;
  letter-spacing: -0.03em;
  color: var(--on-dark);
  text-decoration: none;
}

.site-header nav { display: flex; flex-wrap: wrap; gap: 1.4rem; }

.site-header nav a {
  color: var(--on-dark-muted);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
}

.site-header nav a:hover { color: var(--link); }

/* ---------- layout ---------------------------------------------------------
   Block children are held to the prose measure by default; anything that
   should span the full width opts out with class="full". */

.page, .guide { margin: 0 0 4rem; }

.page > *, .guide > * {
  max-width: var(--measure);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

.page > .full, .guide > .full {
  max-width: none;
  padding-left: 0;
  padding-right: 0;
}

.wrap { max-width: var(--wide); margin: 0 auto; padding: 0 var(--pad); }

.page > h1, .guide > h1 { margin-top: 2.75rem; }

/* ---------- hero (C2: proof sits above the fold) --------------------------- */

.hero {
  background: var(--wash);
  color: var(--ink);
  padding: 34px 0 52px;
}

.hero h1 {
  font-size: clamp(2.4rem, 6.6vw, 4.6rem);
  font-weight: 900;
  line-height: 0.96;
  letter-spacing: -0.04em;
  margin: 0 0 1.4rem;
  max-width: 15ch;
  text-wrap: pretty;
}

/* A lime highlight mark on the key phrase, not lime type: #b8e62e as text is
   1.8:1 on any light ground. As a plate behind ink it is 12.8:1. */
.hero h1 em {
  font-style: normal;
  color: var(--ink);
  background: var(--accent);
  /* Asymmetric on purpose: a right pad as wide as the left one pushes any
     following comma off the plate and reads as a stray space. */
  padding: 0.02em 0.06em 0.02em 0.1em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.hero .lede {
  font-size: clamp(1.05rem, 2.4vw, 1.25rem);
  line-height: 1.45;
  color: var(--on-dark-body);
  margin: 0 0 1.4rem;
  max-width: 36em;
}

.hero .proof {
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px 16px;
  margin: 0 0 1.75rem;
  max-width: 40em;
}

.hero .proof p { margin: 0; font-size: 1rem; line-height: 1.5; color: var(--body); }
.hero .proof strong { color: var(--on-dark); font-weight: 700; }

.hero .actions { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: center; }
.hero .note { color: var(--on-dark-muted); font-size: 0.95rem; }

.hero .tick { display: flex; align-items: center; gap: 10px; margin: 0 0 1.6rem; }
.hero .tick .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.hero .tick span { font-size: 0.85rem; font-weight: 600; color: var(--on-dark-muted); }

/* ---------- typography ----------------------------------------------------- */

h1 {
  font-size: clamp(1.9rem, 5vw, 2.7rem);
  font-weight: 900;
  line-height: 1.06;
  letter-spacing: -0.035em;
  margin: 0 0 1rem;
  text-wrap: pretty;
}

h2 {
  font-size: clamp(1.3rem, 3.4vw, 1.6rem);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin: 2.75rem 0 0.75rem;
  text-wrap: pretty;
}

h3 { font-size: 1.15rem; font-weight: 700; margin: 1.9rem 0 0.5rem; }

p, ul, ol { margin: 0 0 1.15rem; color: var(--body); }

blockquote {
  margin: 0 0 1.15rem;
  padding: 0.9rem 1.2rem;
  background: var(--wash);
  border-left: 3px solid var(--accent);
  color: var(--body);
}

blockquote p:last-child { margin-bottom: 0; }

strong { color: var(--ink); font-weight: 700; }

a { color: var(--link); font-weight: 500; }
a:hover { color: var(--link-hover); }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em;
  background: var(--wash);
  padding: 0.1em 0.35em;
}

hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }

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

table { width: 100%; border-collapse: collapse; margin: 0 0 1.5rem; font-size: 0.97rem; }

th, td {
  text-align: left;
  padding: 0.65rem 0.7rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  color: var(--body);
}

th { background: var(--wash-deep); color: var(--ink); font-weight: 700; }

/* First column holds dates — keep it on one line, let the rest wrap. */
th:first-child, td:first-child { white-space: nowrap; color: var(--ink); font-weight: 600; }

/* ---------- pricing (C3: tags, delta framing, explicit gap) ---------------- */

.pricing { background: var(--paper); padding: 2.5rem 0 0.5rem; }

.pricing .wrap {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.1rem;
  align-items: stretch;
}

@media (min-width: 760px) {
  .pricing .wrap { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.tier { padding: 28px 26px; display: flex; flex-direction: column; }

.tier-basic { border: 2px solid var(--ink); background: var(--paper); }
.tier-featured { background: var(--accent); color: var(--on-accent); }

.tag {
  align-self: flex-start;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  padding: 5px 10px;
  margin-bottom: 14px;
}

.tier-basic .tag { border: 1px solid #c8cfc7; color: var(--muted); }
.tier-featured .tag { background: var(--ink); color: var(--on-ink); }

.tier h3 {
  font-size: 0.94rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 0.6rem;
}

.tier-basic h3 { color: var(--muted); }
.tier-featured h3 { opacity: 0.72; }

.price-row { display: flex; align-items: baseline; gap: 10px; margin-bottom: 1.35rem; }

.price {
  font-size: clamp(2.6rem, 8vw, 3.9rem);
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
}

.delta { font-size: 0.88rem; font-weight: 600; opacity: 0.7; }

.tier ul { list-style: none; padding: 0; margin: 0 0 1.6rem; flex: 1; }

.tier li {
  margin-bottom: 0.65rem;
  font-size: 0.95rem;
  line-height: 1.45;
  color: inherit;
}

.tier-basic li { color: var(--body); }

.tier li.plus {
  font-weight: 700;
  padding-bottom: 0.4rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid rgba(17, 19, 17, 0.22);
}

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

.button, button.button {
  display: inline-block;
  min-height: 48px;
  padding: 0.85rem 1.6rem;
  font: inherit;
  font-weight: 800;
  text-align: center;
  text-decoration: none;
  border: 2px solid var(--ink);
  background: var(--ink);
  color: var(--on-ink);
  cursor: pointer;
}

.button:hover { background: #000; border-color: #000; color: var(--on-ink); }

.button-accent { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.button-accent:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }

.button-outline { background: transparent; color: var(--ink); }
.button-outline:hover { background: var(--ink); color: var(--on-ink); }

.tier .button { width: 100%; }

/* ---------- free-edit CTA (C2: demoted to a plain ask) --------------------- */

.cta {
  margin: 3rem 0 0;
  padding: 1.4rem 1.6rem;
  background: var(--wash);
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
  align-items: center;
  justify-content: space-between;
}

.cta h2 { margin: 0; font-size: 1.15rem; font-weight: 800; letter-spacing: -0.01em; }
.cta p { margin: 0.3rem 0 0; font-size: 0.97rem; color: var(--body); }
.cta .button { white-space: nowrap; }

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

label { display: block; font-weight: 700; margin: 1.3rem 0 0.4rem; color: var(--ink); }

input[type="email"], textarea {
  width: 100%;
  padding: 0.8rem 0.9rem;
  font: inherit;
  color: var(--ink);
  border: 2px solid var(--line);
  background: var(--paper);
}

textarea { min-height: 9.5rem; resize: vertical; }

input[type="email"]:focus, textarea:focus { outline: none; border-color: var(--ink); }

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

.site-footer {
  background: var(--paper);
  color: var(--muted);
  border-top: 1px solid var(--line);
  padding: 2rem var(--pad) 3rem;
  font-size: 0.92rem;
}

.site-footer p { margin: 0 0 0.45rem; color: var(--on-dark-muted); }
.site-footer a { color: var(--on-dark-muted); }
.site-footer a:hover { color: var(--link-hover); }
.deadline strong { color: var(--on-dark); font-weight: 700; }

/* ---------- forms: choices ------------------------------------------------- */

.choices { border: 0; padding: 0; margin: 1.3rem 0 0; }

.choices legend { font-weight: 700; color: var(--ink); padding: 0; margin-bottom: 0.5rem; }

.choice {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 400;
  margin: 0 0 0.2rem;
  min-height: 44px;
  cursor: pointer;
  color: var(--body);
}

.choice input { width: 18px; height: 18px; accent-color: var(--ink); margin: 0; }

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-note { font-size: 0.9rem; color: var(--muted); margin: 0.8rem 0 0; }

.form-error {
  margin: 1rem 0 0;
  padding: 0.7rem 0.9rem;
  background: #fff2f2;
  border-left: 3px solid #c0392b;
  color: #7d2018;
  font-size: 0.95rem;
}

.contact-form button[type="submit"] { margin-top: 1.4rem; }

/* ---------- contact widget -------------------------------------------------
   The launcher only exists when JS does; without it the /contact/ page is the
   whole feature and nothing dangles. */

.widget-launcher { display: none; }

.js .widget-launcher {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 40;
  min-height: 48px;
  padding: 0.8rem 1.3rem;
  font: inherit;
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--on-accent);
  background: var(--accent);
  border: 2px solid var(--ink);
  cursor: pointer;
  box-shadow: 0 4px 0 var(--ink);
}

.js .widget-launcher:hover { background: #a6d227; }
.js .widget-launcher:active { transform: translateY(2px); box-shadow: 0 2px 0 var(--ink); }

.widget-backdrop {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: rgba(17, 19, 17, 0.45);
}

.widget-panel {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 50;
  width: min(400px, calc(100vw - 40px));
  max-height: min(640px, calc(100vh - 40px));
  overflow-y: auto;
  background: var(--paper);
  border: 2px solid var(--ink);
  box-shadow: 0 6px 0 var(--ink);
}

.widget-panel[hidden], .widget-backdrop[hidden] { display: none; }

.widget-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem 1.2rem;
  background: var(--wash-deep);
  color: var(--ink);
}

.widget-head h2 { margin: 0; font-size: 1.05rem; font-weight: 800; letter-spacing: -0.01em; }
.widget-head p { margin: 0.25rem 0 0; font-size: 0.85rem; color: var(--on-dark-muted); }

.widget-close {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--on-dark);
  background: transparent;
  border: 1px solid var(--line);
  cursor: pointer;
}

.widget-close:hover { background: var(--wash); }

.widget-body, .widget-done { padding: 1.2rem; }

.widget-body label:first-of-type { margin-top: 0; }
.widget-body .choices { margin-top: 1.1rem; }
.widget-body textarea { min-height: 7rem; }
.widget-body .button { width: 100%; }

.widget-done h3 { margin: 0 0 0.5rem; font-size: 1.2rem; font-weight: 800; }
.widget-done p { font-size: 0.95rem; }
.widget-meanwhile { color: var(--muted); }
.widget-done .button { width: 100%; margin-top: 0.5rem; }

body.widget-locked { overflow: hidden; }

/* Phone: a full-height sheet, not a cramped floating box. */
@media (max-width: 560px) {
  .js .widget-launcher { right: 14px; bottom: 14px; padding: 0.75rem 1.1rem; }

  .widget-panel {
    right: 0;
    bottom: 0;
    left: 0;
    top: 0;
    width: 100%;
    max-height: none;
    height: 100%;
    border-width: 0;
    box-shadow: none;
  }

  .widget-head { position: sticky; top: 0; }
}

/* ---------- inline "ask before you buy" ------------------------------------ */

.pricing-aside {
  max-width: var(--wide);
  margin: 1.1rem auto 0;
  padding: 0 var(--pad);
  font-size: 0.95rem;
  color: var(--muted);
}

.pricing-aside button {
  font: inherit;
  font-weight: 700;
  color: #5f7d10;
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
}

.pricing-aside button:hover { color: #48600b; }


/* ==========================================================================
   Added 23 Aug 2026 — three-tier pricing, comparison tables, bio, consents.
   ========================================================================== */

/* Phone: 44px of padding each side left a ~287px measure on a 375px screen. */
@media (max-width: 560px) {
  :root { --pad: 20px; }

  /* Two more nav items pushed this onto a second line on a phone. */
  .site-header { padding: 14px var(--pad); gap: 0.5rem 0.9rem; }
  .site-header nav { gap: 0.55rem 0.95rem; }
  .site-header nav a { font-size: 0.85rem; }
  .brand { font-size: 1.05rem; }
}

/* ---------- three tiers ---------------------------------------------------- */

@media (min-width: 760px) and (max-width: 1059px) {
  .pricing.three .wrap { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1060px) {
  .pricing.three .wrap { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.tier-anchor { border: 2px solid var(--ink); background: var(--wash); color: var(--ink); }
.tier-anchor h3 { color: var(--on-dark-muted); }
.tier-anchor .tag { background: var(--accent); color: var(--on-accent); }
.tier-anchor li { color: var(--on-dark-body); }
.tier-anchor li.plus { color: var(--ink); border-bottom-color: var(--line); }
.tier-anchor .button { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.tier-anchor .button:hover { background: #a6d227; border-color: #a6d227; }

.tier li.muted-note {
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(17, 19, 17, 0.16);
  font-size: 0.9rem;
  opacity: 0.85;
}
.tier-anchor li.muted-note { border-top-color: var(--line); }

.anchor-note {
  margin: 0 0 1.2rem;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--on-dark-body);
}
.anchor-note a { color: var(--link); font-weight: 600; }

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

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* margin-block, never the shorthand: this is a `.page > *` child and the
     shorthand resets the auto side margins that centre it. */
  margin-block: 0 1.5rem;
  /* Fades the right edge while there is more table to scroll to, so it is
     obvious the column continues rather than being cut off. The fade has to be
     the colour the table actually sits on; --scroll-fade inherits, so a tinted
     band declares it once and every table inside follows. `transparent` for the
     far stop rather than rgba(255,255,255,0) — browsers interpolate
     premultiplied, so it fades to nothing instead of through white. The
     default lives in :root — see the note there. */
  background:
    linear-gradient(to right, var(--scroll-fade) 30%, transparent) left / 40px 100% no-repeat,
    linear-gradient(to left, var(--scroll-fade) 30%, transparent) right / 40px 100% no-repeat,
    linear-gradient(to right, rgba(17,19,17,.14), transparent) left / 14px 100% no-repeat,
    linear-gradient(to left, rgba(17,19,17,.14), transparent) right / 14px 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}

/* The bands that put a table on the sage tint rather than on paper. */
.compare-strip, .hero { --scroll-fade: var(--wash); }
.table-scroll table { margin: 0; min-width: 30rem; }
table.compare { min-width: 34rem; }

/* Four columns and nine agencies. At the 34rem floor the two prose columns
   squeeze to about 90px each and every row becomes a tower, so this variant
   gets its own floor — and its wrapper is allowed past the 42rem measure that
   `.page > *` gives everything else, or the table would scroll on a desktop
   screen with room to spare. max-width only: the auto side margins that centre
   it are set at the end of the sheet and must not be touched here. */
table.compare.wide { min-width: 52rem; }
table.compare.wide td { vertical-align: top; }
.table-scroll.wide { max-width: 58rem; }   /* 52rem table + the wrapper's 44px side padding */
table.compare td:first-child { white-space: normal; }
table.compare .us td { background: var(--accent); color: var(--on-accent); }
table.compare .us td strong { color: var(--on-accent); }
table.compare .sum td { background: var(--wash); }
table.compare .working { display: block; margin-top: 0.25rem; font-size: 0.85rem; color: var(--muted); }
table.compare td.honest { font-size: 0.92rem; }

.sources { font-size: 0.88rem; color: var(--muted); }
.sources a { font-weight: 500; }

.lede-sub {
  font-size: 1.12rem;
  line-height: 1.45;
  color: var(--muted);
  margin: -0.35rem 0 1.6rem;
}


/* ---------- comparison shown at the point of choosing ---------------------- */

.compare-strip { background: var(--wash); padding: 2.4rem 0 2rem; margin-top: 2.5rem; }
.compare-strip h2 { margin: 0 0 1.1rem; font-size: clamp(1.15rem, 3vw, 1.4rem); }
.compare-strip .table-scroll { margin-bottom: 0.9rem; background-color: var(--wash); }
.compare-strip table { background: var(--wash); min-width: 26rem; }
.strip-note { margin: 0; font-size: 0.88rem; color: var(--muted); }

table.compare.mini td { font-size: 0.93rem; }
table.compare.mini td:last-child,
table.compare.mini th:last-child { white-space: nowrap; text-align: right; }

.anchor-inline {
  padding: 0.9rem 1.1rem;
  background: var(--wash);
  border-left: 3px solid var(--accent);
  font-size: 0.94rem;
}

/* ---------- about: the offers list ----------------------------------------- */

.offers { list-style: none; padding: 0; margin: 0 0 0.5rem; }
.offers li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.6rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line);
}
.offers li:last-child { border-bottom: 0; }
.offers strong { font-size: 1.02rem; }
.rank { font-size: 0.87rem; color: var(--muted); }
.rank-note { font-size: 0.83rem; color: var(--muted); margin-top: 0.5rem; }

/* ---------- about: portrait ------------------------------------------------ */

.bio { display: flex; flex-wrap: wrap; gap: 1.6rem; align-items: flex-start; margin: 0 0 1.15rem; }
.bio-text { flex: 1 1 18rem; }
.bio-text p:last-child { margin-bottom: 0; }

.portrait {
  width: 190px;
  height: 190px;
  object-fit: cover;
  border: 3px solid var(--ink);
  flex-shrink: 0;
}

@media (max-width: 560px) {
  .portrait { width: 140px; height: 140px; }
}

/* ---------- forms: dates, checkboxes, consents ----------------------------- */

input[type="date"] {
  width: 100%;
  padding: 0.8rem 0.9rem;
  font: inherit;
  color: var(--ink);
  border: 2px solid var(--line);
  background: var(--paper);
}
input[type="date"]:focus { outline: none; border-color: var(--ink); }

.consents { margin-top: 2rem; border-top: 2px solid var(--line); padding-top: 1.2rem; }
.consents legend { font-weight: 800; }

.choice.consent {
  align-items: flex-start;
  gap: 0.7rem;
  font-size: 0.92rem;
  line-height: 1.45;
  margin-bottom: 0.9rem;
  min-height: 0;
}
.choice.consent input { margin-top: 0.2rem; flex-shrink: 0; }

.limit-note {
  padding: 0.9rem 1.1rem;
  background: var(--wash);
  border-left: 3px solid var(--accent);
}

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

.site-footer .trader { font-size: 0.84rem; color: var(--muted); margin-top: 0.7rem; }

.trader-block {
  padding: 1rem 1.2rem;
  background: var(--wash);
  border-left: 3px solid var(--ink);
  line-height: 1.6;
}

.model-form {
  font-size: 0.9rem;
  color: var(--muted);
  padding: 0.9rem 1.1rem;
  background: var(--wash);
}

.nowrap { white-space: nowrap; }

/* ---------- header mark --------------------------------------------------- */

/* The base sheet only styles input[type="email"]; the review form needs text. */
input[type="text"] {
  width: 100%;
  padding: 0.8rem 0.9rem;
  font: inherit;
  color: var(--ink);
  border: 2px solid var(--line);
  background: var(--paper);
}
input[type="text"]:focus { outline: none; border-color: var(--ink); }
.hp input[type="text"] { width: 1px; }

.bio-name { font-size: 1.15rem; margin-bottom: 0.6rem; }
.review-ask {
  padding: 0.9rem 1.1rem;
  background: var(--wash);
  border-left: 3px solid var(--accent);
  font-size: 0.94rem;
}

.brand { display: inline-flex; align-items: center; gap: 0.55rem; }
.brand .mark { display: block; flex: none; }

/* ==========================================================================
   Centring fix. `.page > *` centres content with auto side margins, but any
   later rule using the `margin` SHORTHAND resets margin-left/right to 0 at the
   same specificity and silently wins. On a 1280px screen that left every
   table, .cta and .lede-sub pinned at x=0 while the prose sat at x=304.
   Restoring it last fixes every offender at once, including pre-existing ones.
   ========================================================================== */


/* ==========================================================================
   Added 24 Aug 2026 — German version: language switcher and suggestion bar.
   ========================================================================== */

/* The switcher sits at the end of the nav and reads as a mode, not a page.
   It keeps its own colour so it does not compete with the section links. */
.site-header nav a.lang-switch {
  border: 1px solid var(--ink);
  padding: 0.14rem 0.5rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.site-header nav a.lang-switch:hover { border-color: var(--ink); color: var(--link); }

/* A bar above the header, in normal flow — not fixed and not a modal. A
   floating overlay here would be an intrusive interstitial in Google's sense
   and would cover the first thing a visitor came to read. */
.lang-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.9rem;
  padding: 0.7rem var(--pad);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.92rem;
}
.lang-bar[hidden] { display: none; }
/* The November 2026 offer strip (9 Oct 2026): black, the brand lime, one link.
   Colours are literal on purpose: the strip must look the same on the classic
   pages and the lm pages, whose token sets differ. */
.offer-bar {
  display: block;
  padding: 0.6rem var(--pad, 24px);
  background: #111311;
  color: #b8e62e;
  text-align: center;
  text-decoration: none;
  font-size: 0.95rem;
  line-height: 1.4;
}
.offer-bar strong { color: #b8e62e; font-weight: 800; letter-spacing: -0.01em; }
.offer-bar span { color: #fff4ef; }
.offer-bar:hover strong, .offer-bar:focus-visible strong { text-decoration: underline; }
@media (max-width: 560px) { .offer-bar { font-size: 0.85rem; } .offer-bar span { display: block; } }
.lang-bar p { margin: 0; }
/* Downloads on /ib/past-papers/ (10 Oct 2026): a button, then its one-line description. */
.download { margin: 0 0 1.1rem; }
.download .button { display: inline-block; }
.download span { display: block; margin-top: 0.45rem; color: #40483F; font-size: 0.95rem; }
.lang-bar a {
  color: var(--on-accent);
  font-weight: 700;
  text-underline-offset: 2px;
}
.lang-bar button {
  margin-left: auto;
  border: 1px solid rgba(17, 19, 17, 0.4);
  background: none;
  color: var(--on-accent);
  font: inherit;
  font-size: 0.82rem;
  padding: 0.1rem 0.5rem;
  cursor: pointer;
}
.lang-bar button:hover { border-color: var(--on-accent); }
.lang-bar .lang-note {
  flex-basis: 100%;
  font-size: 0.78rem;
  opacity: 0.72;
}

/* On a phone the bar sits above everything the visitor came for, so it has to
   stay one or two lines. Left to itself the auto margin pushes the dismiss
   button onto its own row and the note onto a third, which cost ~250px of the
   first screen. */
@media (max-width: 560px) {
  .lang-bar { padding: 0.55rem var(--pad); font-size: 0.86rem; row-gap: 0.15rem; }
  .lang-bar p { flex: 1 1 auto; }
  .lang-bar button { margin-left: 0.5rem; align-self: center; }
  .lang-bar .lang-note { font-size: 0.72rem; }
}

/* s.312j(2) BGB: directly before the order button, the service and the total
   price must appear "klar und verständlich in hervorgehobener Weise". This is
   the hervorgehobene Weise — it must stay visually distinct from body copy and
   must stay immediately above the button. */
.order-summary {
  margin: 1.6rem 0 1.1rem;
  padding: 0.9rem 1.1rem;
  background: var(--wash);
  border: 2px solid var(--ink);
}
.order-summary p { margin: 0; }
.order-summary p + p { margin-top: 0.35rem; }
/* The capacity statement is a contract term, not a footnote — it must stay
   legible inside the highlighted block rather than fading into small print. */
.order-summary .capacity {
  margin-top: 0.6rem;
  padding-top: 0.55rem;
  border-top: 1px solid var(--line);
  font-size: 0.9rem;
}
/* The English forms carry the same statement as a plain sentence directly
   above the send button, in ink rather than the muted .form-note grey. */
.contact-form > .capacity { margin: 1.1rem 0 0.9rem; font-size: 0.9rem; }

/* ---------- the countdown strip above the prices --------------------------
   Sits between the hero and the pricing, so the dates are read immediately
   before the decision. Dark ground so it reads as a fact panel rather than a
   promotional banner — the urgency comes from the dates themselves. */

/* Flush against the hero, which is also dark: a white band between two dark
   sections read as a rendering fault rather than a division. A hairline does
   the separating instead. */
.deadline-strip {
  background: var(--paper);
  color: var(--ink);
  padding: 1.6rem 0 1.7rem;
  margin-block: 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.deadline-strip[hidden] { display: none; }
.deadline-strip h2 {
  margin: 0 0 1.05rem;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--link);
}

/* The base stylesheet sets `p, ul, ol { color: var(--body) }`, and --body is a
   dark grey meant for the light pages. Any list dropped into a dark section
   therefore inherits near-invisible text — the same trap the .tier-anchor
   rules already work around. Set the colour explicitly here; the dimmer
   sub-elements below re-override it deliberately. */
.ds-list {
  color: var(--ink);
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--line);
  border-block: 1px solid var(--line);
}
@media (min-width: 720px) {
  .ds-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.ds-list li {
  background: var(--paper);
  padding: 0.85rem 0 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
}
@media (min-width: 720px) {
  .ds-list li { padding: 0.9rem 1rem 1rem; }
  .ds-list li:first-child { padding-left: 0; }
}
.ds-list li[hidden] { display: none; }

.ds-what { font-weight: 700; font-size: 1rem; line-height: 1.25; }
.ds-sub { display: block; font-weight: 400; font-size: 0.86rem; color: var(--on-dark-muted); }
.ds-when { font-size: 0.9rem; color: var(--on-dark-body); font-variant-numeric: tabular-nums; }
/* Empty until the script fills it, so it must not reserve space when JS is off. */
.ds-left:not(:empty) {
  margin-top: 0.35rem;
  align-self: start;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  padding: 0.12rem 0.45rem;
}
.ds-note { margin: 0; font-size: 0.9rem; color: var(--on-dark-body); }
.ds-note a { color: var(--link); font-weight: 600; white-space: nowrap; }

/* A warning the reader must not skim past — heavier than .anchor-inline,
   which is used for supporting context rather than for a real hazard. */
.warn-box {
  background: #fff4ef;
  border: 2px solid #a8420c;
  padding: 1rem 1.2rem;
  margin: 0 0 1.6rem;
}
.warn-box p { margin: 0 0 0.7rem; color: #6d2c08; }
.warn-box p:last-child { margin-bottom: 0; }
.warn-box strong { color: #6d2c08; }
/* The lime accent sits at 4.4:1 on this pale rust ground — just under AA. Links
   here take the box's own ink instead, which also stops the one hyperlink in a
   warning competing with it for attention. */
.warn-box a { color: #8f3a0a; }

/* The base link green is #5f7d10, which clears AA on white at 4.74 but drops to
   4.29 on the --wash tint these three components use. A slightly darker green
   restores it without changing the site's link colour everywhere. */
.compare-strip a:not(.button), .ics-cta a:not(.button),
.anchor-inline a:not(.button) { color: #57720f; }

/* The calendar-subscription offer on the deadline hubs. */
.ics-cta {
  background: var(--wash);
  border-left: 3px solid var(--accent);
  padding: 1.1rem 1.3rem;
  margin: 0 0 1.8rem;
}
.ics-cta p { margin: 0 0 0.8rem; }
.ics-cta p:last-child { margin-bottom: 0; }
.ics-alt { margin-left: 0.9rem; font-size: 0.92rem; }
.ics-note { font-size: 0.88rem; color: var(--muted); }

/* Deadline tables carry a lot of dates; line the digits up and let a long
   table scroll inside itself rather than squeezing the columns. */
table.deadlines td.when { white-space: nowrap; font-variant-numeric: tabular-nums; font-weight: 600; }
table.deadlines td .sub { display: block; font-size: 0.85rem; color: var(--muted); margin-top: 0.15rem; }
table.deadlines tr.past td { opacity: 0.55; }
/* ==========================================================================
   KEEP THIS LAST. `.page > *` centres content with auto side margins, and any
   later rule using the `margin` SHORTHAND resets margin-left/right to 0 at the
   same specificity and wins. Every block added below the old position of this
   rule on 24 Aug 2026 — .warn-box, .ics-cta, .order-summary — did exactly that
   and rendered flush to the left of the prose. Anything appended after this
   rule must use margin-block / margin-bottom, never the shorthand.
   ========================================================================== */

.page > *, .guide > * { margin-left: auto; margin-right: auto; }
.page > .full, .guide > .full { margin-left: 0; margin-right: 0; }

/* ==========================================================================
   Added 25 Aug 2026 — the cross-section switch.

   /ib/ and the admissions site are two products sold to the same person, and
   the link between them rendered as an ordinary nav item: .nav-cross carried a
   class but had no rule at all, and the admissions side pointed back with a
   bare two-letter "IB". It is a mode switch, so it is filled rather than
   outlined — a reader has to notice the other half exists before crossing to
   it. No `margin` shorthand here; see the centring note above.
   ========================================================================== */

.site-header nav a.nav-cross {
  background: var(--accent);
  color: var(--on-accent);
  padding: 0.16rem 0.6rem;
  border-radius: 3px;
  font-weight: 800;
  font-size: 0.86rem;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.site-header nav a.nav-cross:hover,
.site-header nav a.nav-cross:focus-visible {
  background: var(--ink);
  color: var(--on-ink);
}

/* Part of the wordmark, not a badge on it: same size and weight as
   "LimeAdmissions", separated only by colour. */
.brand-tag {
  color: var(--link);
  margin-inline-start: 0.3em;
}

/* ==========================================================================
   Added 25 Aug 2026 — optional detail.

   Checkout pages carried several hundred words of reasoning above the form.
   All of it is worth reading and none of it is worth reading *first*, so it
   moves behind a disclosure: still on the page, still crawlable, one click.

   What never goes in here: the price and service summary, and any consent the
   customer has to give. Burying either defeats the thing they are there for —
   and on the German forms s.312j(2) requires the summary to sit visibly
   immediately above the order button.
   ========================================================================== */

details.more {
  border-top: 1px solid var(--line);
  padding-block: 0.55rem 0.2rem;
}
details.more > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding-block: 0.5rem;
  font-weight: 600;
  color: var(--ink);
}
details.more > summary::-webkit-details-marker { display: none; }
details.more > summary::before {
  content: "+";
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  line-height: 1rem;
  text-align: center;
  font-weight: 700;
  color: var(--muted);
}
/* `\2212` and not `2`: this is a plain Python string, so Python reads
   `` as an octal escape and CSS receives U+0091 followed by a literal
   "2" — which is the stray 2 that showed in the toggle when a disclosure was
   open. Doubling the backslash hands CSS the escape it expects, U+2212. */
details.more[open] > summary::before { content: "\2212"; }
details.more > summary:hover::before { border-color: var(--ink); color: var(--ink); }
details.more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.more-body { padding-block: 0.2rem 0.8rem; }
.more-body > * { margin-bottom: 0.9rem; }
.more-body > *:last-child { margin-bottom: 0; }

/* The tutoring upsell stays out of the disclosure and reads as an offer. */
label.choice.addon {
  /* Always three lines of text, so the control belongs at the top of them
     rather than at their vertical centre — the same reason .choice.consent
     sets it. Only visible once the text became a single flex item. */
  align-items: flex-start;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 0 4px 4px 0;
  padding: 0.7rem 0.9rem;
  background: var(--wash);
}
label.choice.addon input { margin-top: 0.2rem; flex-shrink: 0; }

/* The file field read as one more line in the stack, so buyers missed it
   (owner, 20 Sep 2026). Its own box, in the brand lime. The hex is literal:
   --lm-* lives in the redesign stylesheet, which order pages do not load. */
.upload-box {
  border: 2px solid #c5f135;
  border-radius: 4px;
  padding: 0.85rem 0.9rem;
  background: #fbfee9;
}
.upload-box label { margin-bottom: 0.15rem; font-weight: 700; }
.upload-box__note { margin-block: 0 0.6rem; font-size: 0.85rem; color: var(--muted); }
.upload-box input[type="file"] { display: block; width: 100%; max-width: 100%; }
.field-note { margin-block: 0.15rem 0.4rem; font-size: 0.85rem; color: var(--muted); }

/* ==========================================================================
   Added 26 Aug 2026 — audit fixes. No `margin` shorthand here; see the
   centring note above.
   ========================================================================== */

/* `strong { color: var(--ink) }` inside the anchor tier's dark card rendered
   the German "Einführungspreis für den Zyklus 2027" line black on black —
   1.00:1, literally invisible. Same trap as the th:first-child fix above. */
.tier-anchor strong { color: var(--on-dark); }

/* "Allgemeine Geschäftsbedingungen" is a single unbreakable word wider than a
   375px phone minus padding, and it dragged the whole /de/agb/ page into
   sideways scroll. Hyphenation only fires when a word cannot fit its line, so
   English headings are unaffected in practice. */
h1 { hyphens: auto; }
h1, h2, h3 { overflow-wrap: break-word; }

/* ==========================================================================
   Added 28 Aug 2026 — the consent bar.

   A bar, not an overlay, for the same reason the language bar is one: it shows
   on every landing page an advert pays for, and a floating panel across the
   first screen is an intrusive interstitial in Google's sense. Dark rather than
   lime so the two bars cannot be confused when both appear.

   Accept and reject are the same size, side by side, one click each. A reject
   button that is smaller, paler, or a layer deeper is the precise pattern the
   German supervisory authorities enforce against, and hiding it would put the
   ad accounts at risk to gain nothing.

   No `margin` shorthand below — see the centring note above.
   ========================================================================== */
/* Revised 28 Aug 2026. The bar was --ink on a page whose header and hero are
   also --ink, with --on-dark-body text: nothing marked it out as a thing to
   answer, so it read as page furniture. It is still dark — going lime would
   confuse it with the language bar, which is the one thing this must not do —
   but it is now a lighter dark with a lime rule under it, and the text is
   full white rather than grey. Type went up a step without the bar getting
   taller, because the padding came down to compensate: this sits above the
   fold on every ad landing page and height here is paid for in clicks. */
.consent-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.6rem var(--pad);
  background: var(--wash-deep);
  color: var(--ink);
  border-bottom: 2px solid var(--ink);
  font-size: 0.95rem;
}
.consent-bar[hidden] { display: none; }
/* `p, ul, ol { color: var(--body) }` is an element rule, so it beats the white
   this bar inherits from its own container — the banner text has been
   rendering at #40483F on near-black, about 1.4:1, since the bar shipped.
   That is the same trap as the invisible German order text and the th
   first-child fix above: an element-level colour quietly winning over an
   inherited one. `inherit` puts it back under the bar's control. */
.consent-bar p { margin-block: 0; flex: 1 1 22rem; color: inherit; }
.consent-bar a { color: var(--link); font-weight: 700; text-underline-offset: 2px; }
.consent-actions { display: flex; gap: 0.5rem; margin-left: auto; }
.consent-bar button {
  border: 2px solid var(--ink);
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  padding: 0.35rem 1.1rem;
  cursor: pointer;
}
.consent-bar button:hover { background: var(--ink); color: var(--on-ink); }
.consent-bar button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.consent-bar button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

/* On a phone the two buttons go full width rather than stacking under an auto
   margin, which is what cost the language bar 250px of the first screen. */
/* Height on a phone is paid for in clicks: this bar sits above the fold on
   every ad landing page, and the hero call to action is right underneath it.
   The visibility fix was contrast, not size, so the phone keeps the tighter
   metrics and loses nothing. */
@media (max-width: 560px) {
  .consent-bar { padding: 0.5rem var(--pad); font-size: 0.86rem; gap: 0.4rem; }
  .consent-bar p { flex: 1 1 100%; }
  .consent-actions { margin-left: 0; width: 100%; }
  .consent-bar button { flex: 1 1 0; padding: 0.3rem 0.6rem; font-size: 0.85rem; }
}

/* ==========================================================================
   11 Sep 2026 — the consent bar stops costing the first screen on a phone.

   Measured on the live /free-call/ at 375x812 before this change: the bar was
   104px, the header another 119, and the email field — the first thing a paid
   visitor can actually do — sat at 663px. That is 13% of the screen spent on
   a banner, on every landing page an advert pays for, and Reddit traffic is
   almost entirely mobile.

   So on a phone it is pinned to the bottom instead of pushing the page down.
   The reasoning in _consent_bar() still holds and is not being reversed: the
   thing to avoid is a floating overlay ACROSS the first screen, which Google
   treats as an intrusive interstitial. A slim bar along the bottom edge is
   the opposite — it covers no content above the fold, and accept and reject
   are still the same size, one tap each.

   Two fixed elements must not stack in the same corner, so the "Ask a
   question" launcher waits until the banner has been answered. It is a
   secondary route and the wait is measured in seconds; the alternative is
   lifting it by a bar whose height depends on how the text wraps.

   The footer gets padding for the same reason: while the bar is up it would
   otherwise sit on top of the last few lines of the page. */
@media (max-width: 560px) {
  .consent-bar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 44;
    border-bottom: 0;
    border-top: 2px solid var(--ink);
    box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.14);
  }
  .consent-bar:not([hidden]) ~ .widget-launcher { display: none; }
  .consent-bar:not([hidden]) ~ .site-footer { padding-bottom: 8rem; }
}

/* ==========================================================================
   Added 8 Sept 2026 — the guide answer block.
   ========================================================================== */

/* The passage an assistant or a search engine lifts, and the first thing a
   student reading at speed sees. Borrows the .hero .proof treatment so it
   reads as the same kind of object: a claim the page stands behind. */
.answer {
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px 16px;
  margin: 0 0 1.75rem;
  max-width: 40em;
}

.answer p {
  margin: 0;
  font-size: 1.075rem;
  line-height: 1.55;
}


/* ==========================================================================
   Added 28 Aug 2026 — the offers wall.

   A grid of institution tiles with one shared detail panel, driven by hidden
   radios and :checked. It carries information rather than decoration: a
   visitor who does not yet know that Delft is not on UCAS finds that out by
   clicking the tile, which is the question the strategy session answers.

   The panel has a min-height so switching tiles cannot shift the page under
   the pointer that is doing the switching.

   No `margin` shorthand below — see the centring note above.
   ========================================================================== */
.uni-wall { position: relative; }

/* Clipped, not display:none — a hidden radio must stay focusable, or the
   whole thing becomes mouse-only. */
.uni-wall > .uni-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Revised 1 Sep 2026: this label was --muted at 0.76rem on the sage hero band,
   which is the lowest-contrast text in the whole hero and reads as a caption
   rather than as the claim it is. It now uses the headline highlight device —
   ink on lime — which is the one sanctioned solid-lime surface in the design
   system besides a primary button. box-decoration-break keeps the padding on
   both lines where the label wraps in the narrower hero column. */
.uni-eyebrow {
  margin-block: 0 0.8rem;
  font-size: 0.88rem;
  font-weight: 800;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  text-wrap: balance;
  color: var(--on-accent);
}
.uni-eyebrow > span {
  background: var(--accent);
  padding: 0.16rem 0.45rem;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Four across at every width, so the wall is always the same 4 + 3 shape. An
   auto-fit track count reflowed to six-and-a-lonely-one at the width the About
   page gives it. */
.uni-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* The row carries the height floor, not the cells. With `min-height` on the
     cells the eighth box stretched to its row while the tiles sat at their own
     minimum, and the wall came out ragged along the bottom; a minmax track plus
     `min-height: 100%` makes every cell exactly one row tall. */
  grid-auto-rows: minmax(94px, 1fr);
  align-items: stretch;
  gap: 8px;
  margin-block: 0 1rem;
}
.uni { min-height: 100%; }

.uni {
  /* The tiles are <label>s, and the form rule `label { margin: 1.3rem 0 0.4rem }`
     was giving each one a 20.8px top margin. The grid sized its rows around
     that margin, so the eighth cell — a div, with no margin — sat 20.8px above
     the row its siblings occupied and overlapped the row above. Longhands, not
     the shorthand: see the centring note above. */
  margin-block: 0;
  margin-inline: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 11px 6px;
  border: 1px solid var(--line);
  background: var(--paper);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.uni:hover { border-color: var(--ink); }

.uni-mark {
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
}
.uni-logo { width: auto; height: 30px; max-width: 100%; object-fit: contain; }
.uni-name { font-size: 0.69rem; line-height: 1.25; color: var(--muted); }

/* The eighth cell of a 4-wide grid holding seven institutions. It carries the
   .uni class so it cannot drift from the tiles' box model — its own copy of
   those rules is exactly how it ended up a different height, and offset from
   its own row. A div and not a label, so there is nothing to click and nothing
   to select; dashed so it reads as a caption rather than an eighth
   university. */
.uni-fill {
  /* Placed rather than auto-flowed. Left to the auto-placement algorithm it
     landed 20px above the row its own siblings occupy, for reasons that never
     survived inspection; the position is fixed and there is no reason to
     derive it. */
  grid-column: 4;
  grid-row: 2;
  /* The percentage min-height the tiles use to fill their row makes this box
     resolve its own position against the wrong track and ride up over the row
     above. It stretches instead, which gets the same height without the
     percentage. */
  min-height: 0;
  align-self: stretch;
  border-style: dashed;
  background: none;
  cursor: default;
}
.uni-fill:hover { border-color: var(--line); }
.uni-fill-big {
  font-size: 0.86rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--link);
}
.uni-fill-small { font-size: 0.66rem; line-height: 1.2; color: var(--muted); }

/* The rank is the number a parent reads first, so it is set like a number and
   not like a caption. Lime is unreadable on white (1.8:1), so the light
   context takes the olive from the brand palette and only the dark ones get
   the accent. */
.uni-rank {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0 0.28em;
  font-size: 0.82rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--link);
  font-variant-numeric: tabular-nums;
}

/* "#2 Geography" and "#2 world" are different claims, so the number never
   appears without the thing it is a position in. */
.uni-qual {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--muted);
}

/* The accent bar and its 16px indent pushed the panel text off the left edge
   of the grid directly above it, so the block read as belonging to something
   else. The rule moves to the top, where it separates the panel from the
   tiles without shifting it sideways, and the panel lines up with the wall. */
.uni-panel {
  min-height: 6.5rem;
  border-top: 2px solid var(--accent);
  padding: 0.7rem 0 0;
}
.uni-detail { display: none; }
.uni-detail > p { margin-block: 0; }
.uni-head { font-size: 0.95rem; line-height: 1.3; margin-bottom: 0.5rem; }
.uni-head strong { margin-inline-end: 0.4rem; }

/* The rank as a display numeral rather than a sentence. All seven panels used
   to open with the same "Nth in the world for X — QS World University Rankings
   by Subject 2026." construction: a lot of words to deliver one number. The
   numeral is --link olive, not lime; lime on the sage band is about 1.5:1 and
   fails contrast even at display sizes. */
.uni-figure {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  margin-bottom: 0.5rem;
}
.uni-big {
  flex: 0 0 auto;
  font-size: clamp(1.9rem, 5vw, 2.4rem);
  font-weight: 900;
  line-height: 0.9;
  letter-spacing: -0.03em;
  color: var(--link);
}
.uni-fig { display: block; }
.uni-fig-text { display: block; font-size: 0.9rem; line-height: 1.3; color: var(--body); }
.uni-src { display: block; font-size: 0.72rem; letter-spacing: 0.03em; color: var(--muted); }
.uni-line { font-size: 0.9rem; line-height: 1.45; color: var(--body); }
.uni-sys {
  display: inline-block;
  margin-inline-end: 0.45rem;
  padding: 1px 7px;
  border-radius: 2px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: 1px;
}
.uni-note { margin-block: 0.9rem 0; font-size: 0.79rem; line-height: 1.45; color: var(--muted); }

.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-panel > .uni-detail:nth-child(1),
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-panel > .uni-detail:nth-child(2),
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-panel > .uni-detail:nth-child(3),
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-panel > .uni-detail:nth-child(4),
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-panel > .uni-detail:nth-child(5),
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-panel > .uni-detail:nth-child(6),
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-panel > .uni-detail:nth-child(7) { display: block; }

.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-grid > .uni:nth-child(1),
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-grid > .uni:nth-child(2),
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-grid > .uni:nth-child(3),
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-grid > .uni:nth-child(4),
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-grid > .uni:nth-child(5),
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-grid > .uni:nth-child(6),
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-grid > .uni:nth-child(7) {
  border-color: var(--ink);
  background: var(--wash-deep);
}
.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-grid > .uni:nth-child(1) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-grid > .uni:nth-child(2) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-grid > .uni:nth-child(3) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-grid > .uni:nth-child(4) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-grid > .uni:nth-child(5) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-grid > .uni:nth-child(6) .uni-mark,
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-grid > .uni:nth-child(7) .uni-mark { color: var(--ink); }
.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-grid > .uni:nth-child(1) .uni-name,
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-grid > .uni:nth-child(2) .uni-name,
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-grid > .uni:nth-child(3) .uni-name,
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-grid > .uni:nth-child(4) .uni-name,
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-grid > .uni:nth-child(5) .uni-name,
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-grid > .uni:nth-child(6) .uni-name,
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-grid > .uni:nth-child(7) .uni-name { color: var(--body); }
.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-grid > .uni:nth-child(1) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-grid > .uni:nth-child(2) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-grid > .uni:nth-child(3) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-grid > .uni:nth-child(4) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-grid > .uni:nth-child(5) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-grid > .uni:nth-child(6) .uni-rank,
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-grid > .uni:nth-child(7) .uni-rank { color: var(--link); }
.uni-wall > .uni-radio:nth-of-type(1):checked ~ .uni-grid > .uni:nth-child(1) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(2):checked ~ .uni-grid > .uni:nth-child(2) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(3):checked ~ .uni-grid > .uni:nth-child(3) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(4):checked ~ .uni-grid > .uni:nth-child(4) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(5):checked ~ .uni-grid > .uni:nth-child(5) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(6):checked ~ .uni-grid > .uni:nth-child(6) .uni-qual,
.uni-wall > .uni-radio:nth-of-type(7):checked ~ .uni-grid > .uni:nth-child(7) .uni-qual { color: var(--body); }

.uni-wall > .uni-radio:nth-of-type(1):focus-visible ~ .uni-grid > .uni:nth-child(1),
.uni-wall > .uni-radio:nth-of-type(2):focus-visible ~ .uni-grid > .uni:nth-child(2),
.uni-wall > .uni-radio:nth-of-type(3):focus-visible ~ .uni-grid > .uni:nth-child(3),
.uni-wall > .uni-radio:nth-of-type(4):focus-visible ~ .uni-grid > .uni:nth-child(4),
.uni-wall > .uni-radio:nth-of-type(5):focus-visible ~ .uni-grid > .uni:nth-child(5),
.uni-wall > .uni-radio:nth-of-type(6):focus-visible ~ .uni-grid > .uni:nth-child(6),
.uni-wall > .uni-radio:nth-of-type(7):focus-visible ~ .uni-grid > .uni:nth-child(7) {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* The wall used to carry a whole inverted variant for the dark hero. The hero
   is sage-50 now, so the light-ground rules above are the only ones needed and
   the override block is gone. Its three tile_on_hero keys stay defined in the
   format dict below, unused, rather than being pulled out of a shared mapping
   that other blocks also read. (A literal percent-paren in this comment would
   be read as a format token by the % expansion this block goes through.) */

/* ---- two-column hero ----------------------------------------------------- */
/* The wall sits beside the headline on a desktop and under the call to action
   on a phone, where a 7-tile grid above the fold would push the button off it. */
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  gap: 2rem 2.6rem;
  align-items: start;
}
.hero-side { padding-top: 0.4rem; }

/* The headline was sized for the full 1040px wrap. In 56% of it, 6.6vw put
   "applications" over the column and the audit's `h1 { hyphens: auto }`
   dutifully broke it as "ap-plications" — correct behaviour, wrong place. The
   clamp comes down so the word fits whole, and hyphenation is switched back
   off here so a future long word wraps at a space instead of mid-word.
   `overflow-wrap: break-word` is still on globally as the last resort. */
.hero-grid h1 {
  font-size: clamp(2.3rem, 4.2vw, 3.5rem);
  line-height: 0.98;
  hyphens: manual;
  max-width: 13ch;
}

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr); gap: 2.2rem; }
  .hero-side { padding-top: 0; }
}
@media (max-width: 560px) {
  .uni-grid { gap: 6px; grid-auto-rows: minmax(84px, 1fr); }
  .uni { padding: 9px 4px; gap: 4px; }
  .uni-mark { font-size: 0.98rem; }
  .uni-name { font-size: 0.6rem; }
  .uni-panel { min-height: 8rem; }
}
/* ---- hero: the proof card ------------------------------------------------ */
/* Added 1 Sep 2026 after tester feedback. The claim "we came through this cycle
   ourselves" was already here as plain text, and nothing on the front page
   showed that a person was behind it. Same words, now carrying the face, the
   score and a frame.
   Longhand margins throughout this block: the `margin` shorthand resets the
   auto side margins that centre `.page > *`. See the README. */
.proof-card {
  display: flex;
  gap: 1.05rem;
  align-items: center;
  padding: 1rem 1.15rem;
  background: var(--paper);
  border: 2px solid var(--ink);
  box-shadow: 0 6px 0 var(--ink);
  margin-top: 0;
  margin-bottom: 1.7rem;
}
.proof-face {
  width: 76px;
  height: 76px;
  object-fit: cover;
  border: 2px solid var(--ink);
  flex-shrink: 0;
}
.proof-body { flex: 1 1 15rem; }
/* Keep "The full record →" whole: shortening the claim left the arrow
   wrapping onto a line by itself. */
.proof-body a { white-space: nowrap; }
.proof-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 0.6rem;
  margin-top: 0;
  margin-bottom: 0.35rem;
}
.proof-score {
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 0.12rem 0.42rem;
  white-space: nowrap;
}

@media (max-width: 560px) {
  /* On a phone the claim wraps to five lines, and centring a 60px portrait
     against that block leaves it floating beside the middle of the paragraph.
     Back to the top edge here, where it lines up with the name. */
  .proof-card { gap: 0.85rem; padding: 0.85rem 0.9rem; align-items: flex-start; }
  .proof-face { width: 60px; height: 60px; }
}

/* The call to action spans the same column width as the proof card above it,
   so the face and the button read as one stack instead of a banner with a
   small button under it. Scoped to .hero-main, which is the two-column home
   hero in both languages: the /ib/ hero puts two buttons side by side in the
   same .actions row and has to keep them inline. */
.hero-main .actions { display: block; }
.hero-main .actions .button-accent {
  display: block;
  width: 100%;
  min-height: 58px;
  padding-top: 1.05rem;
  padding-bottom: 1.05rem;
  font-size: 1.08rem;
}
.hero-main .actions .note { display: block; margin-top: 0.85rem; }

/* ---- back-to-school sale -------------------------------------------------- */
/* Ink on lime, full width of the pricing row, sitting directly above the tiers
   so the discount is read before any price is. The end date is server-rendered
   text; the script only appends the live remainder to it.
   The [hidden] rule is load-bearing: the UA sheet hides [hidden] with
   display:none, but a display:flex rule here outranks it, so the script's
   sale.hidden = true would do nothing without this. */
.sale-strip {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 0.9rem;
  margin-bottom: 1.7rem;
  padding: 0.8rem 1.1rem;
  background: var(--accent);
  color: var(--on-accent);
  border: 2px solid var(--ink);
  box-shadow: 0 6px 0 var(--ink);
}
.sale-strip[hidden] { display: none; }
.sale-flag {
  font-size: 1.06rem;
  font-weight: 900;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}
.sale-until { font-size: 0.93rem; font-weight: 700; }
.sale-count { font-weight: 900; }
.sale-count:not(:empty)::before { content: " — "; }

/* The regular price, struck through beside the sale price. */
.price-was {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--muted);
  text-decoration: line-through;
  text-decoration-thickness: 2px;
}
/* Screen-reader-only: "was" in front of the struck figure, so the comparison
   is spoken rather than conveyed by strike-through alone. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- what we cover: three framed tiles ----------------------------------- */
/* The tester could not tell from the front page which systems and which IB
   components are covered, and asked for symbols rather than another block of
   prose. Flat geometric marks in the brand palette: no clip art, no emoji,
   square corners and the same hard offset shadow as every other card. */
.cover {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  margin-top: 1.2rem;
  margin-bottom: 1.9rem;
}
.cover-tile {
  border: 2px solid var(--ink);
  box-shadow: 0 6px 0 var(--ink);
  background: var(--paper);
  padding: 1rem 1.05rem 1.15rem;
}
.cover-mark {
  display: block;
  width: 30px;
  height: 30px;
  margin-bottom: 0.65rem;
}
.cover-mark .ink  { fill: var(--ink); }
.cover-mark .lime { fill: var(--accent); }
.cover-mark .rule {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
}
.cover-tile h3 {
  font-size: 1rem;
  margin-top: 0;
  margin-bottom: 0.35rem;
}
.cover-tile p {
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--body);
  margin-top: 0;
  margin-bottom: 0;
}

@media (max-width: 780px) {
  .cover { grid-template-columns: minmax(0, 1fr); gap: 0.85rem; }
  .cover-tile { box-shadow: 0 4px 0 var(--ink); }
}

/* The second flex item in a .choice row: the whole sentence, including any
   price inside it, so it wraps as one thing instead of as three. */
.choice-text { flex: 1 1 auto; min-width: 0; }

/* ==========================================================================
   Added 28 Aug 2026 — the nav, louder.

   It was --on-dark-muted at 0.92rem/500: technically 7.4:1, but grey, small
   and weightless beside a 4.6rem headline, so it read as fine print rather
   than as the way around the site. Now full white at 600, with a rule under
   the item you are on — which the nav never had, on a site with two parallel
   sections where knowing which one you are in is most of the orientation.

   .nav-cross and .lang-switch keep their own treatments: they are mode
   switches, not pages, and the whole point of them is not looking like the
   section links.

   No `margin` shorthand below — see the centring note above.
   ========================================================================== */
.site-header {
  padding-block: 16px;
  border-bottom: 2px solid var(--ink);
}
.site-header nav { gap: 1.15rem; align-items: center; }

.site-header nav a {
  color: var(--ink);
  font-size: 0.95rem;
  font-weight: 600;
}

/* Only the ordinary section links take the underline treatment. */
.site-header nav a:not(.nav-cross):not(.lang-switch) {
  padding-block: 3px;
  border-bottom: 2px solid transparent;
}
.site-header nav a:not(.nav-cross):not(.lang-switch):hover {
  color: var(--link);
  border-bottom-color: var(--accent);
}
/* The reset above is (0,3,2) because of the two :not()s, so a bare
   [aria-current] rule at (0,2,2) loses the border colour to it and the active
   underline never paints — only the text colour survived. Carrying the same
   two :not()s here takes it to (0,4,2), which is also what makes :hover work. */
.site-header nav a[aria-current="page"]:not(.nav-cross):not(.lang-switch) {
  color: var(--ink);
  border-bottom-color: var(--accent);
}
.site-header nav a:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* On a phone the nav already wraps to two lines, so every pixel the louder
   treatment adds is doubled and lands straight on top of the hero call to
   action. The colour and the active rule survive; the breathing room does not. */
@media (max-width: 560px) {
  .site-header { padding-block: 11px; }
  .site-header nav { gap: 0.5rem 0.9rem; }
  .site-header nav a { font-size: 0.9rem; }
  .site-header nav a:not(.nav-cross):not(.lang-switch) { padding-block: 0; }
}

/* ==========================================================================
   Added 28 Aug 2026 — the launcher stops sitting on the hero CTA.

   .widget-launcher is fixed bottom-right; the hero call to action is at the
   bottom-left of the same first screen. On a 375px phone, with the consent bar
   and the nav above it, the two overlapped and the launcher covered half the
   primary button. contact-widget.js adds .is-shown once the hero has scrolled
   out of view, and immediately on pages that have no hero. Without JS the
   launcher was already display:none, so nothing regresses.
   ========================================================================== */

.js .widget-launcher {
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
}

.js .widget-launcher.is-shown {
  opacity: 1;
  visibility: visible;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .widget-launcher { transition: none; }
}

/* ==========================================================================
   Added 28 Aug 2026 — the language switch stops costing a nav row on a phone.

   At 375px the header wrapped to three rows: two of section links, then
   "Deutsch" alone on a third. That third row was ~34px of the first screen on
   every page, and the brand row beside it was half empty. The switch moves up
   there. It is inside <nav>, a different flex container from .brand, so
   absolute positioning is the only route without changing the markup.

   Pages with no German alternate render no switch at all, so nothing is
   positioned and nothing moves.
   ========================================================================== */

@media (max-width: 560px) {
  .site-header { position: relative; }

  .site-header nav a.lang-switch {
    position: absolute;
    top: 11px;
    right: var(--pad);
  }

  /* Same problem on /ib/, where the cross-link "Admissions →" sat alone on a
     third row. That header carries no language switch, so the corner is free.
     :has() picks the case; where it is unsupported nothing moves and the nav
     wraps as it did before. */
  .site-header nav:not(:has(a.lang-switch)) a.nav-cross {
    position: absolute;
    top: 8px;
    right: var(--pad);
  }
}

/* ==========================================================================
   Added 4 Sept 2026 — the booking calendar, inline on the thanks pages.

   min-height reserves the space before cal.com paints, so the paragraph and
   the fallback link below do not jump downwards under the reader's cursor a
   second after the page settles. The month view is taller than it is wide on
   a phone, hence the second value.
   ========================================================================== */

.cal-embed {
  min-height: 620px;
  margin: 0 0 1.6rem;
}

@media (max-width: 560px) {
  .cal-embed { min-height: 760px; }
}

/* ==========================================================================
   Added 10 Oct 2026 — the star rating on /session/.

   Real radios, visually hidden, each followed by its star. The row is laid
   out in reverse (5 first in the markup, 1 first on screen), so "this star
   and every star after it in the markup" is "this star and every star to its
   left", which is what a checked or hovered star must fill.
   ========================================================================== */

.stars { border: 0; padding: 0; margin: 0 0 1.2rem; min-width: 0; }
.stars legend { font-weight: 700; color: var(--ink); padding: 0; margin-bottom: 0.2rem; }
.stars-row { display: inline-flex; flex-direction: row-reverse; gap: 0.1rem; position: relative; }
.stars-row input { position: absolute; left: 0; bottom: 0; width: 1px; height: 1px; opacity: 0; margin: 0; }
.stars-row label {
  font-size: 2.6rem;
  line-height: 1;
  padding: 0.15rem 0.2rem;
  margin: 0;
  color: var(--line);
  cursor: pointer;
}
.stars-row input:checked ~ label,
.stars-row label:hover,
.stars-row label:hover ~ label { color: var(--ink); }
.stars-row input:focus-visible + label { outline: 2px solid var(--ink); outline-offset: 2px; }
.stars-row .star-name {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
