/* ═══════════════════════════════════════════════════════════
   Core Thai — marketing site

   Direction: "Tone contours".
   Thai's five tones are what make it hard and what no competitor
   visualises. The pitch curve is the structural device throughout:
   it labels, divides and underlines, and it always encodes something
   true rather than decorating.

   Type    Kanit (display) — drawn by a Thai foundry, so it carries
           Latin and Thai with one voice
           IBM Plex Sans / Sans Thai (body), IBM Plex Mono (data)
   ═══════════════════════════════════════════════════════════ */

/* ── Theme: Trairong ──────────────────────────────────────────
   ธงไตรรงค์ — crimson, flag indigo, cream. The old app's palette,
   which was flag-coloured on purpose, kept on purpose.

   Light is the default and dark is opt-in via [data-theme] on
   <html>, stamped before first paint by the script in the page
   head. The head script is the only thing that decides which
   theme is on: CSS carries no prefers-color-scheme block, because
   two places deciding is how a switcher and a system preference
   end up disagreeing. No JS means the light theme, which is the
   honest failure.
   ───────────────────────────────────────────────────────────── */
:root {
  color-scheme: light;

  --paper:     #f5f0e8;
  --surface:   #ffffff;
  --surface2:  #ece5d9;
  --border:    #d8d0c4;
  --border2:   #c3b8a6;

  --ink:       #2d2a4a;
  --ink2:      #565274;
  --ink3:      #8b86a5;

  --accent:      #a51931;
  --accent2:     #87142a;
  --accent-wash: rgba(165, 25, 49, 0.08);

  --good:      #2d7a4f;
  --good-wash: rgba(45, 122, 79, 0.12);

  --shadow:    0 1px 2px rgba(45, 42, 74, 0.05), 0 6px 24px rgba(45, 42, 74, 0.07);
  --shadow-lg: 0 2px 4px rgba(45, 42, 74, 0.06), 0 16px 48px rgba(45, 42, 74, 0.14);

  --r-sm: 6px;
  --r:    10px;
  --r-lg: 18px;

  /* A calendar glyph for the date pickers. Declared as a variable so the shape
     lives in one place and the colour comes from whatever fills the mask. */
  --cal-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
  --wrap: 1140px;
  --step: clamp(3.5rem, 8vw, 6.5rem);
}

/* Dark is the flag indigo taken all the way down, with the cream promoted to
   the text. Crimson lifts to #e35d70 — a true #a51931 on a dark ground is a
   hole, not an accent. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --paper:     #14112a;
  --surface:   #1e1c30;
  --surface2:  #282544;
  --border:    #3a3760;
  --border2:   #4d497a;

  --ink:       #f5f0e8;
  --ink2:      #b8b4d0;
  --ink3:      #8884b8;

  --accent:      #e35d70;
  --accent2:     #f08292;
  --accent-wash: rgba(227, 93, 112, 0.14);

  --good:      #4aaa72;
  --good-wash: rgba(74, 170, 114, 0.14);

  --shadow:    0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 24px rgba(0, 0, 0, 0.3);
  --shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.5), 0 16px 48px rgba(0, 0, 0, 0.45);
}

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

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

:where(h1, h2, h3, h4) {
  font-family: "Kanit", system-ui, sans-serif;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 0.5em;
  text-wrap: balance;
}

/* Display sizes, brought down from 4.4rem/2.5rem. At the old top end an h1 was
   70px and the first section of a page was taking 88% of a laptop screen before
   a reader reached anything — the headline was the page rather than the way in
   to it. Still the loudest thing on the page, just no longer the whole of it. */
h1 { font-size: clamp(2rem, 4.4vw, 3.1rem); font-weight: 700; }
h2 { font-size: clamp(1.45rem, 2.8vw, 1.95rem); }
h3 { font-size: 1.18rem; font-weight: 600; }

p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

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

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

[lang="th"] { font-family: "IBM Plex Sans Thai", "IBM Plex Sans", sans-serif; }

/* Thai has two everyday letterforms and a learner needs both.
 *
 * The looped face is what printing, government forms and most signage use, and
 * it is the shape the letters are actually taught as. The loopless one is the
 * modern display style — shop fronts, apps, advertising — and it drops the
 * loops that distinguish several letters from each other.
 *
 * The site body is loopless (IBM Plex Sans Thai), so without this a learner
 * would finish Foundation having never seen the form most printed Thai is set
 * in. The old flashcard app showed both side by side and that was right. */
.th-classic { font-family: "Noto Serif Thai", "IBM Plex Sans Thai", serif; }
.th-modern  { font-family: "IBM Plex Sans Thai", sans-serif; }

/* Both faces of one letter, with a label under each. */
.faces { display: flex; gap: 2.5rem; justify-content: center; align-items: baseline; }
.face { text-align: center; }
.face-ch { display: block; font-size: clamp(2.75rem, 9vw, 4.25rem); font-weight: 600; line-height: 1.15; }
.face-k {
  display: block; margin-top: 0.35rem;
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.wrap { width: min(var(--wrap), 100% - 2.5rem); margin-inline: auto; }
.wrap.narrow { width: min(680px, 100% - 2.5rem); }
.wrap.mid    { width: min(880px, 100% - 2.5rem); }
.centred { text-align: center; }

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: var(--paper);
  padding: 0.7rem 1.1rem; border-radius: 0 0 var(--r-sm) 0;
}
.skip:focus { left: 0; top: 0; z-index: 100; }

/* ── The signature: tone contours ─────────────────────────── */

.tone { display: block; width: 44px; height: 18px; color: var(--accent); overflow: visible; }
.tone-sm { width: 26px; height: 11px; }
.tone-inline { display: inline-block; vertical-align: -2px; width: 26px; height: 11px; margin-left: 0.45rem; }

.tone-rule {
  display: flex; align-items: center; gap: 0.7rem;
  color: var(--border2);
  margin: var(--step) 0;
}
.tone-rule::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}

/* Headings carry their own contour, drawn as an underline */
.tone-head { display: inline-flex; flex-direction: column; gap: 0.35rem; }
.tone-head .tone { width: 72px; height: 14px; color: var(--accent); }

/* ── Buttons ──────────────────────────────────────────────── */

.btn, .btn-ghost {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.8rem 1.6rem;
  border-radius: var(--r-sm);
  font-family: "Kanit", sans-serif;
  font-weight: 500; font-size: 1rem; line-height: 1.2;
  text-decoration: none; border: 1.5px solid transparent;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, color 0.18s, transform 0.12s;
}
.btn { background: var(--accent); color: #fff; }
.btn:hover { background: var(--accent2); color: #fff; transform: translateY(-1px); }
.btn-ghost { border-color: var(--border2); color: var(--ink); background: transparent; }
.btn-ghost:hover { border-color: var(--ink); color: var(--ink); }
.btn-lg { padding: 1rem 2rem; font-size: 1.08rem; }
.btn-block { display: flex; width: 100%; justify-content: center; }

/* ── Header ───────────────────────────────────────────────── */

.site-head {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}
.head-inner { display: flex; align-items: center; gap: 1.5rem; min-height: 66px; }

.logo {
  font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1.15rem;
  color: var(--ink); text-decoration: none;
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  letter-spacing: -0.01em;
}
.logo-th { font-family: "IBM Plex Sans Thai", sans-serif; color: var(--accent); font-weight: 600; }

.nav { display: flex; gap: 1.7rem; margin-left: auto; }
.nav a {
  color: var(--ink2); text-decoration: none; font-size: 0.95rem;
  padding: 0.2rem 0; border-bottom: 1.5px solid transparent;
}
.nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }

.head-cta { display: flex; gap: 0.6rem; align-items: center; }
.head-cta .btn, .head-cta .btn-ghost { padding: 0.55rem 1.15rem; font-size: 0.93rem; }

.burger { display: none; background: none; border: 0; padding: 0.5rem; cursor: pointer; }
.burger span { display: block; width: 22px; height: 2px; background: var(--ink); margin: 4px 0; transition: transform 0.2s; }

@media (max-width: 900px) {
  .nav {
    position: fixed; inset: 66px 0 auto; flex-direction: column; gap: 0;
    background: var(--surface); border-bottom: 1px solid var(--border);
    padding: 0.25rem 1.25rem 1.25rem; margin: 0; display: none;
  }
  body.nav-open .nav { display: flex; }
  .nav a { padding: 0.9rem 0; border-bottom: 1px solid var(--border); }
  .burger { display: block; order: 3; }
  .head-cta { margin-left: auto; }
  .head-cta .btn-ghost { display: none; }
}

/* ── Hero ─────────────────────────────────────────────────── */

.hero { padding: clamp(2.25rem, 5vw, 4rem) 0 clamp(1.75rem, 4vw, 2.75rem); }
.hero h1 { margin-bottom: 0.35em; }

.hero-home {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(20rem, 0.95fr);
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center;
}
.hero-home .lede { max-width: none; }
.page-home .hero { padding-block: clamp(2.6rem, 6vw, 5rem); }
.page-home .hero h1 { font-size: clamp(2.35rem, 5vw, 3.65rem); }

@media (max-width: 800px) {
  .hero-home { grid-template-columns: 1fr; }
}
.hero-curve { width: 120px; height: 26px; color: var(--accent); margin-bottom: 2rem; }

.eyebrow {
  font-family: "IBM Plex Mono", monospace;
  text-transform: uppercase; letter-spacing: 0.14em;
  font-size: 0.73rem; color: var(--accent);
  margin-bottom: 1.2rem;
}
.lede { font-size: clamp(1.02rem, 1.7vw, 1.16rem); color: var(--ink2); max-width: 52ch; }

.hero-cta { display: flex; gap: 0.8rem; flex-wrap: wrap; margin: 2.2rem 0 1rem; }
.hero-note { font-size: 0.9rem; color: var(--ink3); }

/* The five-tones demo — the page's thesis */
.tones {
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  margin: 2.5rem 0 1.2rem;
}
.tone-card {
  background: var(--surface);
  padding: 1.6rem 1.2rem 1.4rem;
  text-align: center;
  border: 0; width: 100%;
  font: inherit; color: inherit; cursor: pointer;
  transition: background 0.18s;
}
.tone-card:hover { background: var(--accent-wash); }
.tone-card.playing { background: var(--accent-wash); }
.tone-card .tc-th {
  font-family: "IBM Plex Sans Thai", sans-serif;
  font-size: 2.4rem; line-height: 1.35; display: block;
}
.tone-card .tone { width: 58px; height: 20px; margin: 0.6rem auto; color: var(--ink3); transition: color 0.18s; }
.tone-card:hover .tone, .tone-card.playing .tone { color: var(--accent); }
.tone-card .tc-roman {
  font-family: "IBM Plex Mono", monospace; font-size: 0.82rem;
  color: var(--ink3); text-transform: uppercase; letter-spacing: 0.06em;
}
.tone-card .tc-en { font-size: 0.98rem; color: var(--ink2); margin-top: 0.15rem; }

/* All five tones, named. Stops three example words reading as "three tones". */
.tone-legend-label {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
  margin: 0 0 0.7rem;
}
.tone-legend {
  list-style: none; padding: 0; margin: 0 0 1.8rem;
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.6rem;
}
.tone-legend li { display: flex; align-items: center; gap: 0.45rem; }
.tone-legend .tone { width: 30px; height: 13px; color: var(--ink3); }
.tone-legend span {
  font-family: "IBM Plex Mono", monospace; font-size: 0.78rem;
  color: var(--ink2); text-transform: lowercase;
}

/* ── Sections ─────────────────────────────────────────────── */

.band { padding: var(--step) 0; }
.band .sub { color: var(--ink2); font-size: 1.05rem; }
.band-tint { background: var(--surface2); }

/* Inverted band. These rules must stay after `.band .sub` — they carry the
   same specificity, so source order is what decides the winner. */
.band-ink { background: var(--ink); color: var(--paper); }
.band-ink :where(h1, h2, h3) { color: var(--paper); }
.band-ink p, .band-ink .sub { color: color-mix(in srgb, var(--paper) 82%, transparent); }
.band-ink .hero-note { color: color-mix(in srgb, var(--paper) 66%, transparent); }
/* Magenta does not clear 4.5:1 against ink, and inverts in dark mode.
   Paper plus an underline is legible in both. */
.band-ink a { color: var(--paper); text-decoration-color: color-mix(in srgb, var(--paper) 45%, transparent); }
.band-ink a:hover { color: var(--paper); text-decoration-thickness: 2px; }

/* ── Phrase cards ─────────────────────────────────────────── */

.phrase-list {
  display: grid; gap: 0.85rem;
  grid-template-columns: repeat(auto-fit, minmax(235px, 1fr));
  margin: 2rem 0;
}
.phrase {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.15rem 1.25rem;
}
.phrase-main { display: flex; align-items: center; gap: 0.6rem; }
.phrase-th { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.5rem; line-height: 1.5; }
.phrase-play {
  margin-left: auto; flex: none;
  display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 50%;
  border: 1.5px solid var(--border2); background: transparent;
  color: var(--accent); cursor: pointer; padding: 0;
  transition: background 0.18s, border-color 0.18s, color 0.18s;
}
.phrase-play svg { width: 10px; height: 12px; margin-left: 1px; }
.phrase-play:hover, .phrase-play.playing {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
.phrase-roman {
  font-family: "IBM Plex Mono", monospace; font-size: 0.87rem;
  color: var(--accent); margin-top: 0.15rem;
}
.phrase-en { color: var(--ink2); font-size: 0.95rem; }
.phrase-note { font-size: 0.87rem; color: var(--ink3); margin: 0.55rem 0 0; }

/* ── The single word, on a "how to say" page ──────────────── */
/* One card, deliberately large: the word and its recording are what the page
   was opened for, so they go above the explanation rather than after it. */

.word-card {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r); padding: 1.5rem 1.6rem;
  margin: 2rem 0 1.5rem; max-width: 30rem;
}
.word-main { display: flex; align-items: center; gap: 0.9rem; }
.word-th {
  font-family: "IBM Plex Sans Thai", sans-serif;
  font-size: clamp(2.2rem, 7vw, 3rem); line-height: 1.45;
}
.word-play { width: 42px; height: 42px; }
.word-play svg { width: 13px; height: 15px; }
.word-roman {
  font-family: "IBM Plex Mono", monospace;
  font-size: 1.05rem; color: var(--accent); margin: 0.4rem 0 0;
}
.word-en { color: var(--ink2); margin: 0.15rem 0 0; }

/* The two-sentence answer to the query. Set slightly larger than body copy
   because it is the part most readers will read and nothing else. */
.word-answer p:first-child { font-size: 1.12rem; }

.syl-table { max-width: 32rem; }
.syl-th { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.25rem; }
.syl-r { font-family: "IBM Plex Mono", monospace; font-size: 0.92rem; }
.syl-tone { white-space: nowrap; }
.syl-tone .tone { color: var(--accent); margin-right: 0.45rem; }
.syl-tone span { font-size: 0.9rem; color: var(--ink2); }

/* ── Steps ────────────────────────────────────────────────── */

.steps { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); margin-top: 2.5rem; }
.step { border-top: 2px solid var(--ink); padding-top: 1.1rem; }
.step-k {
  font-family: "IBM Plex Mono", monospace; font-size: 0.73rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent);
  display: block; margin-bottom: 0.6rem;
}
.step p { color: var(--ink2); font-size: 0.97rem; }

/* ── The course path ──────────────────────────────────────── */

/* Four levels side by side, so the whole course reads left to right in one
   glance. The top rule on each column is the shared line they hang from —
   it stands in for the rail the old stacked list had. */
.path {
  list-style: none; padding: 0; margin: 2.2rem 0 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.6rem;
}
.path li {
  padding: 1.2rem 0 0; border-top: 2px solid var(--border);
}
.path li.is-live { border-top-color: var(--accent); }
.path-k {
  font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1.5rem;
  color: var(--ink3); line-height: 1.1;
  display: block; margin-bottom: 0.7rem;
}
.path li.is-live .path-k { color: var(--accent); }
.path h3 {
  display: flex; align-items: flex-start; gap: 0.5rem;
  flex-direction: column; margin-bottom: 0.45rem;
}
.path p { color: var(--ink2); font-size: 0.95rem; margin: 0; }

.tag {
  font-family: "IBM Plex Mono", monospace; font-size: 0.66rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.1em;
  padding: 0.25rem 0.55rem; border-radius: var(--r-sm);
  background: var(--surface2); color: var(--ink2); border: 1px solid var(--border);
}
.tag-live { background: var(--good-wash); color: var(--good); border-color: transparent; }
.tag-soon { background: transparent; color: var(--ink3); }
.tag-paid { background: var(--accent-wash); color: var(--accent); border-color: transparent; }

.callout {
  border-left: 2px solid var(--accent);
  padding: 0.15rem 0 0.15rem 1.3rem;
  color: var(--ink2); font-size: 0.97rem;
  margin: 2rem 0;
}

/* ── Article pages ────────────────────────────────────────── */

.article { padding: clamp(2.5rem, 6vw, 4.5rem) 0 0; }
.article-head { margin-bottom: 3rem; }
.article-head h1 { margin-bottom: 0.4em; }
.article-meta {
  font-family: "IBM Plex Mono", monospace; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink3);
}
.article-body { font-size: 1.05rem; }
.article-body h2 { margin-top: 2.8rem; }
.article-body h3 { margin-top: 2rem; font-size: 1.1rem; }
.article-body ul, .article-body ol { padding-left: 1.25rem; }
.article-body li { margin-bottom: 0.55rem; }
.article-body table { width: 100%; border-collapse: collapse; margin: 1.8rem 0; font-size: 0.97rem; }
.article-body th, .article-body td { text-align: left; padding: 0.7rem 0.9rem 0.7rem 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.article-body th {
  font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 0.75rem;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink3);
  border-bottom-color: var(--ink);
}
.article-body blockquote {
  margin: 1.8rem 0; padding: 1.2rem 1.4rem;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 2px solid var(--accent); border-radius: 0 var(--r) var(--r) 0;
}
.article-body blockquote p { margin: 0; }
.article-body blockquote [lang="th"] { font-size: 1.35rem; }

.tie-in {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 2rem; margin: 3.5rem 0 2rem;
}
.tie-in h2 { margin-top: 0; font-size: 1.3rem; }
.tie-in p { color: var(--ink2); font-size: 0.98rem; margin-bottom: 1.4rem; }

.related { margin-bottom: 4rem; }
.related h2 { font-size: 0.78rem; font-family: "IBM Plex Mono", monospace; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink3); }
.related ul { list-style: none; padding: 0; }
.related li { border-top: 1px solid var(--border); }
.related a { display: block; padding: 0.85rem 0; text-decoration: none; color: var(--ink); font-weight: 500; }
.related a:hover { color: var(--accent); }

/* ── Cards ────────────────────────────────────────────────── */

.card-grid { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); margin-top: 2.2rem; }
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.6rem; text-decoration: none; color: inherit;
  display: flex; flex-direction: column; gap: 0.4rem;
  transition: border-color 0.18s, transform 0.15s, box-shadow 0.2s;
}
.card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow); color: inherit; }
.card h3 { margin: 0; }
.card p { color: var(--ink2); font-size: 0.94rem; margin: 0; }
.card-k {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink3);
}

/* ── Pricing ──────────────────────────────────────────────── */

.plans { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); margin: 2.5rem 0; align-items: start; }
.plan {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 2rem;
}
.plan-feature { border-color: var(--accent); box-shadow: var(--shadow); }
.plan-k {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3); margin-bottom: 0.9rem;
}
.plan-feature .plan-k { color: var(--accent); }
.plan-price { font-family: "Kanit", sans-serif; font-weight: 700; font-size: 2.6rem; line-height: 1; letter-spacing: -0.02em; }
.plan-per { font-family: "IBM Plex Sans", sans-serif; font-weight: 400; font-size: 0.95rem; color: var(--ink3); }
.plan ul { list-style: none; padding: 0; margin: 1.5rem 0; }
/* A small caption above a plan's button. Currently unused — it held "Opens
   when A1 does" while subscriptions were shut. Kept because the next thing to
   go there ("Founding rate — 500 places") wants the same treatment. */
.plan-soon {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
  text-align: center; margin: 0 0 0.7rem;
}
.plan li { padding: 0.45rem 0 0.45rem 1.6rem; position: relative; font-size: 0.96rem; color: var(--ink2); }
.plan li::before {
  content: ""; position: absolute; left: 0; top: 0.95rem;
  width: 9px; height: 9px; border-radius: 50%; background: var(--good);
}
.plan li.no::before { background: none; border: 1.5px solid var(--border2); }

.counter {
  display: inline-flex; align-items: baseline; gap: 0.5rem;
  font-family: "IBM Plex Mono", monospace; font-size: 0.82rem;
  color: var(--ink2); background: var(--surface2);
  border-radius: var(--r-sm); padding: 0.45rem 0.8rem; margin-bottom: 1.5rem;
}
.counter b { color: var(--accent); font-weight: 600; }

/* ── Auth ─────────────────────────────────────────────────── */

.auth { min-height: 70vh; display: grid; place-items: center; padding: 3rem 0; }
.auth-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 2.4rem; width: min(420px, 100%);
}
.auth-card h1 { font-size: 1.9rem; margin-bottom: 0.3em; }
.auth-card .sub { color: var(--ink2); font-size: 0.97rem; margin-bottom: 1.8rem; }
.field { margin-bottom: 1.1rem; }
.field label {
  display: block; font-family: "IBM Plex Mono", monospace;
  font-size: 0.73rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink3); margin-bottom: 0.4rem;
}
.field input {
  width: 100%; padding: 0.75rem 0.9rem; font: inherit; font-size: 0.98rem;
  background: var(--paper); color: var(--ink);
  border: 1.5px solid var(--border2); border-radius: var(--r-sm);
}
.field input:focus { border-color: var(--accent); outline: none; }
.field label .opt {
  text-transform: none; letter-spacing: 0; font-size: 0.72rem;
  color: var(--ink3); opacity: 0.75; margin-left: 0.4em;
}
.field-hint { font-size: 0.82rem; color: var(--ink3); margin: 0.4rem 0 0; }
.auth-alt { text-align: center; font-size: 0.93rem; color: var(--ink2); margin-top: 1.5rem; }

/* Failures are the whole point of the form having a server behind it, so they
   get stated plainly rather than as a red asterisk somewhere. */
.auth-error {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm);
  padding: 0.75rem 0.95rem; margin-bottom: 1.4rem;
  font-size: 0.91rem; color: var(--ink);
}
.auth-error a { color: var(--accent); }

/* Log out is a form so that it cannot be spent by a prefetch; it still has to
   look like the link everyone expects. */
.logout { display: inline; }
.logout button {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--ink3); cursor: pointer; text-decoration: none;
}
.logout button:hover { color: var(--accent); }
.head-out button { font-size: 0.93rem; }

/* ── Footer ───────────────────────────────────────────────── */

.site-foot { border-top: 1px solid var(--border); padding: 3.5rem 0 1.5rem; margin-top: var(--step); }
.foot-inner { display: grid; gap: 2.5rem; grid-template-columns: 1.6fr repeat(4, 1fr); }
.foot-brand p { color: var(--ink2); font-size: 0.92rem; max-width: 32ch; margin-top: 0.9rem; }
.foot-col h3 {
  font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.11em; color: var(--ink3); margin-bottom: 0.9rem;
}
.foot-col a { display: block; color: var(--ink2); text-decoration: none; font-size: 0.93rem; margin-bottom: 0.5rem; }
.foot-col a:hover { color: var(--accent); }
.foot-legal {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem;
  border-top: 1px solid var(--border); margin-top: 3rem; padding-top: 1.3rem;
  font-size: 0.86rem; color: var(--ink3);
}
.foot-legal p { margin: 0; }
.foot-legal a { color: var(--ink3); }

.foot-note { flex-basis: 100%; color: var(--ink3); font-size: 0.8rem; }

/* Kit — a recommendation sitting next to the lesson that raises it. Quiet on
   purpose: it borrows the module row's shape so it reads as part of the course
   rather than as something bolted over the top of it. Named "kit" everywhere,
   for the reason set out in site/kit.php. */
/* --kit-l and --kit-d come in on the element from site/content/kit.php; this is
   the only place that decides which of the two is live, so the switcher changing
   theme re-tints every slot without a reload. */
/* --kit is the readable one, used for text. --edge is the untouched brand
   colour, used only where contrast does not apply: the stripe and the wash.
   Falls back to --kit, so a partner without a tint_edge is unchanged. */
.kit { --kit: var(--kit-l, var(--accent)); --edge: var(--kit-e, var(--kit)); }
:root[data-theme="dark"] .kit { --kit: var(--kit-d, var(--accent)); }

.kit {
  display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;
  /* A wash rather than a fill: enough of the brand to be recognised across the
     page, not enough to stop the module rows underneath being the loudest thing
     on it. */
  background: color-mix(in srgb, var(--edge) 5%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--edge) 28%, var(--border));
  border-left: 3px solid var(--edge); border-radius: var(--r);
  padding: 1.15rem 1.3rem; margin: 2rem 0;
}
.kit-body { flex: 1; min-width: 15rem; }
.kit-kicker {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--kit);
  margin: 0 0 0.3rem;
}
.kit-name { font-size: 1.03rem; margin: 0 0 0.35rem; }
.kit-blurb { font-size: 0.9rem; color: var(--ink2); margin: 0; max-width: 62ch; }

/* The button borrows the brand colour too, but keeps the ghost shape — a solid
   brand-coloured button would outrank "Start review" on the same page. */
.kit-go { flex: none; border-color: color-mix(in srgb, var(--kit) 45%, transparent); color: var(--kit); }
.kit-go:hover { border-color: var(--kit); color: var(--kit); background: color-mix(in srgb, var(--kit) 8%, transparent); }

/* In the module stack it is one of the rows, so it takes the row rhythm rather
   than the standalone spacing above. */
.level .kit { margin: 0.65rem 0 1.6rem; }

/* The links page. A stack of whole-row links rather than cards with buttons in
   them: this page is opened from a bio link on the phone that was watching the
   video, so the tap target is the row and the "cta" is a label, not a control.

   It borrows .kit's tint mechanism — same two custom properties, set inline per
   row — so a partner looks the same here as it does beside a lesson. */
.linkrow { --kit: var(--kit-l, var(--accent)); --edge: var(--kit-e, var(--kit)); }
:root[data-theme="dark"] .linkrow { --kit: var(--kit-d, var(--accent)); }

.linkstack { display: grid; gap: 0.5rem; margin-top: 1.3rem; }

/* Rows are tight on purpose. This is a bio link: somebody arrives from a video
   wanting to find one thing, and every row that does not fit on the first
   screen is a row most people never see. nowrap keeps the call to action beside
   the text instead of dropping it onto a line of its own, which on a phone was
   costing about 40px per row across nine rows. */
.linkrow {
  /* Named because two things have to agree on it: the row is painted in it, and
     so is the glyph inside the icon block. That is what makes the icon look cut
     out of the block rather than drawn on it. */
  --row-bg: color-mix(in srgb, var(--edge) 5%, var(--surface));
  display: flex; align-items: center; gap: 1rem; flex-wrap: nowrap;
  background: var(--row-bg);
  border: 1px solid color-mix(in srgb, var(--edge) 28%, var(--border));
  border-left: 3px solid var(--edge); border-radius: var(--r);
  padding: 0.85rem 1.05rem;
  color: inherit; text-decoration: none;
  transition: border-color 0.12s, transform 0.12s, box-shadow 0.12s;
}
.linkrow:hover {
  color: inherit;
  border-color: color-mix(in srgb, var(--edge) 55%, var(--border));
  border-left-color: var(--edge);
  transform: translateY(-2px); box-shadow: var(--shadow);
}

/* The block on the left. Solid edge colour, with the icon stroked back in the
   row's own background so it reads as a knockout — the same device as the
   badge, turned inside out. */
.linkrow-icon {
  flex: none; display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem; border-radius: calc(var(--r) - 3px);
  background: var(--edge); color: var(--row-bg);
}
.linkrow-icon svg { width: 1.3rem; height: 1.3rem; display: block; }

.linkrow-body { flex: 1; min-width: 0; display: block; }
.linkrow-kicker {
  display: block;
  font-family: "IBM Plex Mono", monospace; font-size: 0.64rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--kit);
  margin-bottom: 0.15rem;
}
.linkrow-name {
  display: block; font-family: "Kanit", sans-serif; font-weight: 600;
  font-size: 1rem; line-height: 1.25; margin-bottom: 0.15rem;
}
/* No clamp. There used to be a two-line ellipsis here to cap how tall a row
   could get, and it cut sentences off mid-word — which on the one page built
   for someone arriving cold is the worst place to be coy. The blurbs were
   rewritten short enough not to need it instead (`blurb_links` in kit.php is
   the per-row override), so what the row shows is the whole sentence. */
.linkrow-blurb { font-size: 0.85rem; line-height: 1.4; color: var(--ink2); }

/* The per-row disclosure. Small, but above the fold of the row and next to the
   link itself, which is what the footer line on its own cannot claim to be. */
.linkrow-aff { display: block; font-size: 0.7rem; color: var(--ink3); margin-top: 0.3rem; }

.linkrow-end { flex: none; display: flex; align-items: center; gap: 0.9rem; }
.linkrow-badge {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  padding: 0.25rem 0.5rem; border-radius: 999px;
  background: color-mix(in srgb, var(--edge) 14%, transparent); color: var(--kit);
  white-space: nowrap;
}
/* Looks like the ghost button it replaces, but it is a label — the row is the
   link, and a real button inside it would be a nested interactive element. */
.linkrow-cta {
  font-size: 0.88rem; font-weight: 500; color: var(--kit); white-space: nowrap;
}
.linkrow-cta::after { content: " \2192"; }

.links-hero { padding-bottom: 0; }

/* A bio link should say whose it is before it says anything else, so the hero
   is the shape people already know from every other link-in-bio page: face,
   name, handle, one line. */
.links-id { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.1rem; }
.links-id img {
  width: 4.5rem; height: 4.5rem; border-radius: 50%; object-fit: cover;
  border: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
  flex: none;
}
.links-id-name { display: block; font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1.15rem; line-height: 1.2; }
.links-id-handle {
  display: block; font-family: "IBM Plex Mono", monospace; font-size: 0.78rem;
  color: var(--accent); margin-top: 0.15rem;
}

/* The links page sets its own vertical rhythm. .band is built for prose, where
   a full --step of air either side earns its keep; here the sections are stacks
   of rows and the gaps read as the page having ended. Worse, a tone rule at the
   top of a band stacked a third --step on top of the two paddings — 312px of
   nothing between "Start here" and "Follow along". */
.linksec { padding: clamp(1.4rem, 3vw, 2.25rem) 0; }
.linksec .tone-rule { margin: 0 0 clamp(0.9rem, 2vw, 1.4rem); }
.linksec h2 { margin-bottom: 0.2rem; font-size: clamp(1.25rem, 2.6vw, 1.6rem); }
.linksec .sub { margin-top: 0.5rem; font-size: 0.92rem; }

/* The hero is an introduction, not a landing page. It should not use up the
   first screen on its own. */
.links-hero { padding-top: clamp(1.75rem, 4vw, 3rem); }
.links-hero h1 { font-size: clamp(1.75rem, 5vw, 2.6rem); }
.links-hero .lede { font-size: clamp(0.98rem, 2vw, 1.1rem); margin-top: 0.8rem; }

@media (max-width: 560px) {
  .linkrow { gap: 0.7rem; padding: 0.8rem 0.9rem; }
  .linkrow-icon { width: 2.1rem; height: 2.1rem; }
  .linkrow-icon svg { width: 1.1rem; height: 1.1rem; }
  /* The label goes and the arrow stays. "Check Thailand plans →" is four words
     of chrome on a screen where the whole row is already the link. */
  .linkrow-cta { font-size: 0; }
  .linkrow-cta::after { font-size: 1.1rem; }
  .linkrow-badge { font-size: 0.66rem; padding: 0.2rem 0.4rem; }
}

/* ── The guide storefront ──────────────────────────────────────────────────
   Cover on the left, the decision on the right. Everything somebody needs to
   buy a book is above the fold and nothing is stacked behind a headline. */
.shopfront-wrap { padding: clamp(1.75rem, 4vw, 3rem) 0 clamp(1.5rem, 3vw, 2.5rem); }

.shopfront { display: grid; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
@media (min-width: 760px) {
  .shopfront { grid-template-columns: minmax(200px, 300px) minmax(0, 1fr); gap: 3rem; }
}

/* The cover stands like a book rather than sitting like a screenshot: a thin
   spine down the binding edge, and a shadow that falls away from it. */
.shopfront-cover img {
  display: block; width: 100%; height: auto;
  border-radius: 2px 5px 5px 2px;
  border: 1px solid var(--border);
  box-shadow:
    inset 6px 0 10px -6px rgba(45, 42, 74, 0.35),
    0 1px 2px rgba(45, 42, 74, 0.10),
    0 14px 34px rgba(45, 42, 74, 0.18);
  background: #fff;
}
@media (max-width: 759px) {
  /* On a phone the cover sits beside the title rather than above it. Stacked,
     it pushed the price and the button off the bottom of the first screen,
     which is the one thing a storefront cannot afford.

     A float rather than a grid: the text needs to run alongside the cover and
     then close back up underneath it, which is exactly what floats do and what
     a two-column grid cannot without a fixed row height. */
  .shopfront { display: block; }
  .shopfront-cover {
    float: left; width: 104px; margin: 0 1rem 0.5rem 0;
  }
  /* Only the eyebrow and the title run alongside the cover. Letting a
     two-line subtitle wrap round it drops its last line flush against the left
     edge under the cover, which reads as a mistake rather than as typesetting. */
  .shopfront-sub { clear: left; padding-top: 0.85rem; margin-bottom: 1.1rem; }
  .shopfront-blurb { padding-top: 1rem; max-width: none; }
  /* Down to one column, everything runs the full measure. A panel and a spec
     list stopping short of the margin look like they failed to load. */
  /* The panel and the spec list are full width at every size — see --measure
     below, which they simply exceed on a phone. */
}

/* One measure for the whole right-hand column. The blurb wants a reading
   width rather than the full column, and everything under it has to share that
   edge — a panel running wider than the paragraph above it steps out and reads
   as a mistake. Below this width, which is every phone, they are all simply
   full width. */
.shopfront-detail { --measure: 28rem; }
.shopfront-detail h1 { margin: 0 0 0.35rem; max-width: var(--measure); }
/* The subtitle is part of the title, so it is set like one — larger than the
   body and in the display face. Run at body size directly above the blurb the
   two read as a single paragraph, which is what they were doing. */
.shopfront-sub {
  font-family: "Kanit", ui-sans-serif, system-ui, sans-serif; font-weight: 500;
  font-size: clamp(1.05rem, 1.9vw, 1.3rem); line-height: 1.35;
  color: var(--ink2); margin: 0 0 1.4rem; max-width: var(--measure);
}
.shopfront-blurb {
  font-size: 0.94rem; line-height: 1.6; color: var(--ink2);
  margin: 0 0 1.6rem; max-width: var(--measure);
  padding-top: 1.1rem; border-top: 1px solid var(--border);
}

/* The buying block is the one panel on the page, so the eye lands on it. */
/* Full width of the column it sits in. Clamped narrower it read as a widget
   dropped into the page rather than part of it, and the ragged right edge
   against the blurb above was the tell. */
.shopfront-buy {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.2rem 1.3rem;
  max-width: var(--measure);
}
/* Price and button on one line: the number and the thing that acts on it
   belong together, and stacked they made the panel taller than the decision. */
.shopfront-row {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}
.shopfront-row .btn { flex: 1 1 auto; justify-content: center; white-space: nowrap; }
.shopfront-price {
  font-family: "Kanit", sans-serif; font-weight: 700;
  font-size: 1.9rem; line-height: 1; color: var(--ink);
  margin: 0; flex: none;
  font-variant-numeric: tabular-nums;
}
.shopfront-owned {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--good);
  margin: 0 0 0.7rem;
}
.shopfront-note { font-size: 0.83rem; color: var(--ink3); margin: 0.8rem 0 0; }

.shopfront-facts {
  list-style: none; padding: 0; margin: 1.5rem 0 0;
  display: grid; gap: 0.4rem; max-width: var(--measure);
  font-size: 0.88rem; color: var(--ink2);
}
.shopfront-facts li {
  display: flex; gap: 0.9rem;
  padding-bottom: 0.4rem; border-bottom: 1px solid var(--border);
}
.shopfront-facts li:last-child { border-bottom: 0; }
.shopfront-facts span {
  flex: none; width: 5.5rem; color: var(--ink3);
  font-family: "IBM Plex Mono", monospace; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.1em; padding-top: 0.2rem;
}

/* The DTV sales page. Two components only, because a sales page that invents
   its own visual language reads as a different site and undoes the trust the
   rest of the pages built. */
.sell { list-style: none; padding: 0; margin: 2rem 0 0; display: grid; gap: 1.4rem; }
.sell li {
  padding-left: 1.1rem; border-left: 2px solid var(--accent);
  font-size: 0.98rem; color: var(--ink2);
}
.sell strong { display: block; color: var(--ink); font-weight: 600; margin-bottom: 0.2rem; }

/* Pages of the actual PDF. They scroll sideways on a phone rather than
   shrinking to thumbnails nobody can read. */
.peek {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(140px, 1fr);
  gap: 0.75rem; margin-top: 1.75rem;
  overflow-x: auto; padding-bottom: 0.5rem;
  scroll-snap-type: x mandatory;
}
.peek img {
  width: 100%; height: auto; scroll-snap-align: start;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  box-shadow: var(--shadow); background: #fff;
}

/* Author photo. A ring rather than a plain border: the gap between the image
   and the line keeps the crop from looking clipped, and it reads as deliberate
   at any size. */
.byline-photo {
  display: flex; flex-direction: column; align-items: center;
  gap: 0.9rem; margin: 0 0 2rem; text-align: center;
}
.byline-photo img {
  width: 160px; height: 160px; flex: none;
  border-radius: 50%; object-fit: cover;
  border: 3px solid var(--accent);
  outline: 1px solid var(--border);
  outline-offset: 4px;
  background: var(--surface2);
}
.byline-photo-cap {
  margin: 0; font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--ink3);
}

/* The pointer between the course and the guide. Named for what it is rather
   than what it does, for the reason set out in site/helpers.php — a class
   called anything an ad blocker recognises is a class that disappears on a
   large minority of visits without ever looking broken.

   Quieter than .tie-in on purpose: it is a second path for somebody who did
   not take the first one, so it must not outshout the thing it follows. */
.alongside {
  background: var(--surface2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r) var(--r) 0;
  padding: 1.5rem 1.6rem;
  margin: 0;
}

/* Two of these on a tools page used to sit on top of each other with no
   gap. Side by side where there is room, stacked with a real gap where
   there is not. */
.alongside-pair {
  display: grid;
  gap: 1.25rem;
  align-items: stretch;
}
@media (min-width: 780px) {
  .alongside-pair { grid-template-columns: 1fr 1fr; gap: 1.5rem; }
}
.alongside-pair .alongside { height: 100%; }
.alongside-k {
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent);
  margin: 0 0 0.5rem;
}
.alongside h2 { font-size: 1.2rem; margin: 0 0 0.5rem; }
.alongside-b { color: var(--ink2); font-size: 0.96rem; margin: 0 0 1.2rem; }
.alongside-n { font-size: 0.83rem; color: var(--ink3); margin: 0.9rem 0 0; }

/* With a cover alongside the copy. The claim in the note is "a 46-page PDF",
   and a picture of page one is the cheapest way to make that true rather than
   asserted. Text first in the source so the block still reads in order without
   the image; the grid puts the cover on the left where there is room. */
.alongside.has-media {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 1.8rem;
  align-items: center;
}
.alongside.has-media .alongside-media { grid-column: 1; grid-row: 1; }
.alongside.has-media .alongside-say { grid-column: 2; grid-row: 1; }
.alongside-media img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.22);
}

/* Below this the cover is competing with the copy for the same column rather
   than sitting beside it. Cap it so it stays a thumbnail and does not become
   the whole block. */
@media (max-width: 660px) {
  .alongside.has-media { grid-template-columns: 1fr; gap: 1.3rem; }
  .alongside.has-media .alongside-media { grid-column: 1; grid-row: 1; max-width: 160px; }
  .alongside.has-media .alongside-say { grid-column: 1; grid-row: 2; }
}

/* Inside the post-payment card, which is already a panel, so the pointer needs
   air above it rather than a second frame around it. */
.return-next { margin-top: 2.5rem; text-align: left; }

/* ── Preview lightbox ──────────────────────────────────────────────────────
   The sample pages, full size. <dialog> supplies the backdrop and the focus
   handling; this only has to make it look like something worth opening. */
.peek-open {
  display: block; padding: 0; border: 0; background: none; cursor: zoom-in;
  scroll-snap-align: start; border-radius: var(--r-sm);
  transition: transform 0.12s;
}
.peek-open:hover { transform: translateY(-2px); }
.peek-open img { display: block; width: 100%; height: auto; }
.peek-hint { font-size: 0.83rem; color: var(--ink3); margin: 0.9rem 0 0; }

.peeker {
  border: 0; padding: 0; background: none;
  max-width: min(94vw, 780px); max-height: 94vh;
  overflow: visible;
}
.peeker::backdrop { background: rgba(20, 17, 42, 0.82); }
.peeker-img {
  display: block; width: 100%; height: auto; max-height: 88vh;
  object-fit: contain;
  border-radius: var(--r-sm); background: #fff;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

/* Controls sit on the backdrop rather than over the page, so they never cover
   the thing somebody opened the dialog to read. */
.peeker-close, .peeker-step {
  position: fixed; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; padding: 0;
  border: 1px solid rgba(245, 240, 232, 0.25); border-radius: 999px;
  background: rgba(20, 17, 42, 0.7); color: #f5f0e8;
  font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.peeker-close:hover, .peeker-step:hover { background: rgba(20, 17, 42, 0.95); }
.peeker-close { top: 1rem; right: 1rem; font-size: 1.6rem; }
.peeker-step { top: 50%; left: 1rem; transform: translateY(-50%); }
.peeker-next { left: auto; right: 1rem; }
.peeker-count {
  position: fixed; left: 50%; bottom: 1rem; transform: translateX(-50%);
  margin: 0; font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(245, 240, 232, 0.75);
}

@media (max-width: 560px) {
  /* No room beside the page on a phone, so the steppers go under it. */
  .peeker-step { top: auto; bottom: 1rem; transform: none; }
  .peeker-step { left: calc(50% - 4.5rem); }
  .peeker-next { left: auto; right: calc(50% - 4.5rem); }
  .peeker-count { bottom: 4.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .peek-open, .peek-open:hover { transition: none; transform: none; }
}

/* ── Guide index list ──────────────────────────────────────────────────────
   Cards, but one per row. The card is the site's existing shape for "a thing
   you can go and read"; what did not work was the auto-fit grid, which put
   five items into three columns and left a hole beside the last two. One
   column keeps the card and gives each excerpt a real reading measure. */
.guide-list { display: grid; gap: 0.75rem; margin-top: 2rem; }

.guide-row {
  display: flex; align-items: baseline; gap: 1.5rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.25rem 1.4rem;
  text-decoration: none; color: inherit;
  transition: border-color 0.18s, transform 0.15s, box-shadow 0.2s;
}
.guide-row:hover {
  color: inherit; border-color: var(--accent);
  transform: translateY(-2px); box-shadow: var(--shadow);
}
/* The Thai word on /how-to-say. It leads the row because it is what somebody
   scanning that page is actually looking for, and it is set in the Thai face at
   a size that makes the tone marks legible. */
.guide-row-lead {
  flex: none; font-family: "IBM Plex Sans Thai", sans-serif; font-weight: 600;
  font-size: 1.5rem; line-height: 1.2; color: var(--accent);
  min-width: 4.5rem;
}
.guide-row-body { flex: 1; min-width: 0; display: block; }
.guide-row-title {
  display: block; font-family: "Kanit", ui-sans-serif, system-ui, sans-serif;
  font-weight: 600; font-size: 1.1rem; line-height: 1.3; margin-bottom: 0.3rem;
}
.guide-row-excerpt {
  display: block; font-size: 0.92rem; line-height: 1.5;
  color: var(--ink2); max-width: 62ch;
}
.guide-row-meta {
  flex: none; font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink3); white-space: nowrap;
}

@media (max-width: 560px) {
  /* The read time goes under the excerpt rather than squeezing the title. */
  .guide-row { flex-wrap: wrap; gap: 0.5rem; padding: 1.1rem 1.15rem; }
  .guide-row-meta { width: 100%; }
  /* The word keeps its own line above the title rather than squeezing it. */
  .guide-row-lead { width: 100%; min-width: 0; font-size: 1.35rem; }
}

/* A row for something that is written but not ported yet. Same shape, no
   hover, and it says so — better than a link that 404s or an item silently
   missing from a list that is meant to be the whole set. */
.guide-row.is-soon { opacity: 0.62; cursor: default; }
.guide-row.is-soon:hover { border-color: var(--border); transform: none; box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .guide-row, .guide-row:hover { transition: none; transform: none; }
}

/* ── Stay tracker ──────────────────────────────────────────────────────────
   Shares the tool shell with the 90 day calculator; all it adds is the bar. */
.stay-bar { height: 6px; border-radius: 999px; background: var(--surface2); overflow: hidden; margin-bottom: 1.1rem; }
/* width:0 by default. Without it the fill is an ordinary block and starts at
   the full width of the track, so the first result animates from 100% down to
   its real value — which reads, for a fifth of a second, as though somebody has
   used up their whole allowance. */
.stay-bar-fill { width: 0; height: 100%; background: var(--border2); border-radius: 999px; transition: width 0.2s; }
.tool-result.is-open .stay-bar-fill    { background: var(--good); }
.tool-result.is-grace .stay-bar-fill   { background: #b8860b; }
.tool-result.is-overdue .stay-bar-fill { background: var(--accent); }
.tool-result.is-waiting .stay-bar-fill { background: var(--border2); }

@media (prefers-reduced-motion: reduce) { .stay-bar-fill { transition: none; } }

/* ── Tax residency extras ─────────────────────────────────────────────────
   Everything else is the shared tool shell. */
.year-tabs { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; margin-bottom: 1.2rem; }
.year-tab {
  font: inherit; font-size: 0.82rem; line-height: 1; font-variant-numeric: tabular-nums;
  padding: 0.45rem 0.85rem; border: 0; background: none; cursor: pointer; color: var(--ink3);
}
.year-tab + .year-tab { border-left: 1px solid var(--border); }
.year-tab:hover { color: var(--ink); background: var(--surface2); }
.year-tab.is-on { background: var(--accent-wash); color: var(--accent); font-weight: 600; }

.abs-head {
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
  margin: 1.5rem 0 0.6rem;
}
.abs-row { display: flex; gap: 0.6rem; align-items: flex-end; margin-bottom: 0.6rem; }
.abs-col { flex: 1; min-width: 0; }
.abs-k {
  display: block; font-size: 0.72rem; color: var(--ink3); margin-bottom: 0.25rem;
}
.abs-row .tool-date { font-size: 0.92rem; padding: 0.5rem 0.6rem; }
.abs-remove {
  flex: none; width: 2rem; height: 2.2rem; border: 1px solid var(--border);
  border-radius: var(--r-sm); background: none; color: var(--ink3);
  font-size: 1.05rem; line-height: 1; cursor: pointer;
}
.abs-remove:hover { border-color: var(--accent); color: var(--accent); }

.tax-bd { margin: 1.2rem 0 0; padding-top: 1rem; border-top: 1px solid var(--border); display: grid; gap: 0.35rem; }
.tax-bd > div { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.85rem; }
.tax-bd dt { color: var(--ink3); margin: 0; }
.tax-bd dd { margin: 0; font-weight: 500; font-variant-numeric: tabular-nums; }

@media (max-width: 520px) {
  .abs-row { flex-wrap: wrap; }
  .abs-col { flex: 1 1 45%; }
}

/* ── FAQ ───────────────────────────────────────────────────────────────────
   Question and answer, visibly separate. Run together at one weight this reads
   as a wall of text and the questions stop working as the thing you scan for. */
.faq { display: grid; gap: 0; margin-top: 1.75rem; border-top: 1px solid var(--border); }
.faq-item { padding: 1.35rem 0; border-bottom: 1px solid var(--border); }
.faq-q {
  font-family: "Kanit", ui-sans-serif, system-ui, sans-serif; font-weight: 600;
  font-size: 1.05rem; line-height: 1.35; color: var(--ink);
  margin: 0 0 0.45rem; text-wrap: balance;
}
.faq-a {
  font-size: 0.95rem; line-height: 1.65; color: var(--ink2);
  margin: 0; max-width: 68ch;
}
.faq-a + .faq-a { margin-top: 0.65rem; }

/* Two columns, as a modifier rather than a second `.faq` rule — the base
   component is shared with /pricing and the three tool pages, and redefining
   it here restyled all of them once already.
   column-gap only: the row rule between items is what separates them
   vertically, so a row-gap would leave the border floating in the middle of
   the space instead of sitting against the next question. */
.faq-split { column-gap: 3rem; }
@media (min-width: 760px) {
  .faq-split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Groups, when a FAQ is long enough to need them. */
.faq-group {
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
  margin: 2rem 0 0.2rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border2);
}
.faq-group:first-of-type { margin-top: 0.4rem; }

/* ── Tools ─────────────────────────────────────────────────────────────────
   Input on one side, answer on the other. The answer is the loudest thing on
   the page because it is what somebody came for. */
.tool { display: grid; gap: 1.25rem; align-items: start; }
@media (min-width: 780px) { .tool { grid-template-columns: minmax(0,1fr) minmax(0,1.15fr); gap: 2rem; } }

.tool-input {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.4rem 1.5rem;
}
.tool-label {
  display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.6rem;
}
.tool-date {
  width: 100%; font: inherit; font-size: 1.05rem;
  padding: 0.7rem 0.85rem; border-radius: var(--r-sm);
  border: 1.5px solid var(--border2); background: var(--paper); color: var(--ink);
  /* NOT `light dark`. That hands the choice of widget chrome to the operating
     system, so somebody whose OS is dark and who has chosen Light here got a
     white calendar icon on cream. :root already carries the site's decision —
     inheriting it keeps the native parts in step with the painted ones. */
  color-scheme: inherit;
}

/* The picker button, replaced rather than recoloured. The native icon is an
   image whose colour comes from the widget chrome, so a filter can only push it
   around; masking a shape and filling it with the accent gives the real colour
   in both themes from one asset. Webkit only — Firefox draws its own, which the
   color-scheme above now gets right. */
.tool-date::-webkit-calendar-picker-indicator {
  width: 1.15rem; height: 1.15rem; padding: 0; margin-left: 0.35rem;
  opacity: 1; cursor: pointer;
  /* The shorthand, not background-color: the browser draws its own calendar
     image on this pseudo-element and background-color leaves it in place, so
     the native glyph showed through the mask as a dark shape with a red edge. */
  background: var(--accent);
  -webkit-mask: var(--cal-icon) center / contain no-repeat;
          mask: var(--cal-icon) center / contain no-repeat;
  transition: background-color 0.12s;
}
.tool-date::-webkit-calendar-picker-indicator:hover { background: var(--accent2); }
.tool-date:focus::-webkit-calendar-picker-indicator { background: var(--accent2); }
.tool-date:focus { outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--accent); }
.tool-hint { font-size: 0.85rem; color: var(--ink2); margin: 0.8rem 0 0; }
.tool-hint-quiet { color: var(--ink3); font-size: 0.8rem; }

.tool-result {
  background: color-mix(in srgb, var(--accent) 4%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--border));
  border-radius: var(--r); padding: 1.4rem 1.5rem;
}
.tool-result-k {
  font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent);
  margin: 0 0 0.5rem;
}
.tool-due {
  font-family: "Kanit", ui-sans-serif, system-ui, sans-serif; font-weight: 700;
  font-size: clamp(1.5rem, 3.4vw, 2.1rem); line-height: 1.1; color: var(--ink);
  margin: 0 0 1.1rem;
}
.tool-due.is-empty { color: var(--ink3); font-weight: 500; font-size: 1.15rem; }

.tool-window { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 1.1rem; }
.tool-window > div {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 0.7rem 0.85rem;
}
.tool-window-k {
  display: block; font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink3); margin-bottom: 0.2rem;
}
.tool-window-v { display: block; font-weight: 600; font-size: 1rem; }
.tool-window-s { display: block; font-size: 0.75rem; color: var(--ink3); margin-top: 0.1rem; }

/* Status carries its own colour, because "overdue" and "not open yet" are not
   the same news and should not look the same. */
.tool-status {
  display: block; border-radius: var(--r-sm); padding: 0.9rem 1rem; margin: 0;
  border-left: 3px solid var(--border2); background: var(--surface);
}
.tool-status-t { display: block; font-weight: 600; font-size: 0.98rem; margin-bottom: 0.2rem; }
.tool-status-d { display: block; font-size: 0.88rem; color: var(--ink2); }
.tool-status.is-open    { border-left-color: var(--good);   background: var(--good-wash); }
.tool-status.is-waiting { border-left-color: var(--ink3); }
.tool-status.is-grace   { border-left-color: #b8860b; background: color-mix(in srgb, #b8860b 10%, var(--surface)); }
.tool-status.is-overdue { border-left-color: var(--accent); background: var(--accent-wash); }

.tool-next { font-size: 0.85rem; color: var(--ink2); margin: 1rem 0 0; }

.tool-offer {
  margin-top: 1.25rem; padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}
.tool-offer-t { font-weight: 600; margin: 0 0 0.35rem; }
.tool-offer-d { font-size: 0.88rem; color: var(--ink2); margin: 0 0 1rem; }

/* Colour theme. Three states rather than a two-way toggle: "system" is a real
   answer and a toggle cannot express it — once you flip a two-way switch you
   have opted out of the system preference forever without being told. */
.theme-pick {
  display: inline-flex; border: 1px solid var(--border); border-radius: 999px;
  overflow: hidden; background: var(--surface);
}
.theme-pick button {
  font: inherit; font-size: 0.78rem; line-height: 1;
  padding: 0.4rem 0.75rem; border: 0; background: none; cursor: pointer;
  color: var(--ink3); transition: background 0.12s, color 0.12s;
}
.theme-pick button + button { border-left: 1px solid var(--border); }
.theme-pick button:hover { color: var(--ink); background: var(--surface2); }
.theme-pick button[aria-pressed="true"] {
  background: var(--accent-wash); color: var(--accent); font-weight: 500;
}

/* Five columns is too many below a wide desktop: the brand paragraph and the
   link lists start wrapping into each other. Drop the brand onto its own row
   first, then go to two columns. */
@media (max-width: 1080px) {
  .foot-inner { grid-template-columns: repeat(4, 1fr); }
  .foot-brand { grid-column: 1 / -1; }
}

@media (max-width: 800px) {
  .foot-inner { grid-template-columns: 1fr 1fr; gap: 2rem; }
  .foot-brand { grid-column: 1 / -1; }
  .path { grid-template-columns: 1fr 1fr; gap: 1.4rem; }
}

@media (max-width: 520px) {
  .path { grid-template-columns: 1fr; gap: 1.2rem; }
}

/* ═══════════════════════════════════════════════════════════
   Logged-in shell (prototype)

   Different job from the marketing pages: get someone into
   today's session in one glance. Quieter, denser, no selling.
   ═══════════════════════════════════════════════════════════ */

.app-shell .site-head .nav,
.app-shell .site-head .head-cta .btn { display: none; }
.app-shell .site-foot { margin-top: 2rem; }
.app-band { padding-top: 2rem; }

.app-nav { border-bottom: 1px solid var(--border); background: var(--surface); }
.app-nav-inner { display: flex; gap: 1.6rem; align-items: center; }
.app-nav a {
  padding: 0.9rem 0; color: var(--ink2); text-decoration: none;
  font-size: 0.94rem; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.app-nav a:hover { color: var(--ink); }
.app-nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); font-weight: 500; }
/* Opens the gap between the course pages and the account pair (Billing, Log
   out). Styling only — the links themselves look like every other nav link. */
.app-nav-right { margin-left: auto; }
.app-nav-out button { font-size: 0.94rem; }
/* One log out per page: the shell has its own, so the marketing header's goes. */
.app-shell .site-head .head-out { display: none; }

.dash-head { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; margin-bottom: 2rem; }
.dash-head h1 { font-size: clamp(1.9rem, 4vw, 2.8rem); margin: 0; }
.dash-head .eyebrow { margin-bottom: 0.4rem; }

.streak { position: relative; width: 48px; height: 48px; flex: none; color: var(--accent); }
.streak svg { width: 48px; height: 48px; display: block; }
.streak-n {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1rem; color: var(--ink);
}

/* ── Tiles ────────────────────────────────────────────────── */

.dash-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

.tile {
  display: flex; flex-direction: column; gap: 0.35rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 1.6rem;
  text-decoration: none; color: inherit;
  transition: border-color 0.18s, transform 0.15s;
}
a.tile:hover { border-color: var(--accent); transform: translateY(-2px); color: inherit; }
.tile-primary { background: var(--ink); border-color: var(--ink); color: var(--paper); }
a.tile-primary:hover { color: var(--paper); border-color: var(--ink); }
.tile-quiet { background: transparent; }

.tile-k {
  font-family: "IBM Plex Mono", monospace; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.13em; color: var(--ink3);
}
.tile-primary .tile-k { color: color-mix(in srgb, var(--paper) 60%, transparent); }
.tile-big { font-family: "Kanit", sans-serif; font-weight: 700; font-size: 3.2rem; line-height: 1; letter-spacing: -0.03em; }
.tile-title { font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1.25rem; line-height: 1.25; margin-top: 0.2rem; }
.tile-sub { font-size: 0.95rem; color: var(--ink2); }
.tile-primary .tile-sub { color: color-mix(in srgb, var(--paper) 78%, transparent); }
.tile-meta { font-size: 0.85rem; color: var(--ink3); }
.tile-go { margin-top: auto; padding-top: 0.9rem; font-size: 0.9rem; font-weight: 500; color: var(--accent); }
.tile-primary .tile-go { color: var(--paper); }

.goal-dots { display: flex; gap: 0.4rem; margin: 0.5rem 0 0.2rem; }
.goal-dot { width: 26px; height: 6px; border-radius: 3px; background: var(--border2); }
.goal-dot.on { background: var(--good); }

.week { display: flex; gap: 0.35rem; margin-top: auto; padding-top: 1rem; }
.wd {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: "IBM Plex Mono", monospace; font-size: 0.68rem;
  background: var(--surface2); color: var(--ink3); border: 1px solid transparent;
}
.wd.on { background: var(--good); color: #fff; }
.wd.today { border-color: var(--accent); color: var(--accent); }
.wd.future { opacity: 0.45; }

/* ── Course progress ──────────────────────────────────────── */

.dash-progress { margin-top: 2.5rem; }
.dp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.dp-head h2 { font-size: 1.3rem; margin: 0; }
.dp-head a { font-size: 0.9rem; text-decoration: none; }
.dp-links { display: flex; gap: 1.2rem; flex-wrap: wrap; margin: 0; }
.dp-bar, .xp-bar, .lv-bar {
  height: 8px; border-radius: 4px; background: var(--surface2);
  overflow: hidden; margin: 0.9rem 0 0.6rem;
}
.dp-bar span, .xp-bar span, .lv-bar span { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.xp-bar span { background: var(--good); }
.dp-meta { font-size: 0.9rem; color: var(--ink2); }
.dp-meta strong { color: var(--ink); font-weight: 600; }

.lv-row { display: grid; gap: 0.7rem; margin-top: 1.4rem; }
.lv { display: grid; grid-template-columns: 2.6rem 1fr 3rem; gap: 0.9rem; align-items: center; }
.lv-code { font-family: "IBM Plex Mono", monospace; font-size: 0.8rem; color: var(--ink3); }
.lv-part .lv-code, .lv-done .lv-code { color: var(--ink); font-weight: 500; }
.lv .lv-bar { margin: 0; }
.lv-done .lv-bar span { background: var(--good); }
.lv-pct { font-family: "IBM Plex Mono", monospace; font-size: 0.78rem; color: var(--ink3); text-align: right; }

.dash-split { display: grid; gap: 2.5rem; grid-template-columns: 1fr 1fr; margin-top: 3rem; }
.dash-split h2 { font-size: 1.15rem; }
@media (max-width: 700px) { .dash-split { grid-template-columns: 1fr; } }

.badges { list-style: none; padding: 0; margin: 0.9rem 0 0; display: grid; gap: 0.55rem; }
.badges li { display: flex; align-items: center; gap: 0.7rem; font-size: 0.93rem; color: var(--ink3); }
.badges li .tone { width: 26px; height: 11px; flex: none; color: var(--border2); }
.badges li.on { color: var(--ink); }
.badges li.on .tone { color: var(--good); }

.nudge {
  display: flex; gap: 1.2rem; align-items: flex-start;
  background: var(--accent-wash); border-radius: var(--r-lg);
  padding: 1.8rem; margin-top: 2.5rem;
}
.nudge > .tone { width: 44px; flex: none; margin-top: 0.4rem; }
.nudge h3 { margin-bottom: 0.4rem; }
.nudge p { color: var(--ink2); font-size: 0.96rem; margin-bottom: 1.1rem; }
.nudge .btn-ghost { margin-left: 0.5rem; }

/* ── The path ─────────────────────────────────────────────── */

.level { margin-top: 3rem; }
.level-head { display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
.level-head h2 { margin: 0; font-size: 1.5rem; }
.level-pct { margin-left: auto; font-family: "IBM Plex Mono", monospace; font-size: 0.8rem; color: var(--ink3); white-space: nowrap; }
.level-sep { margin: 0 0.35rem; opacity: 0.5; }

.module {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); margin-bottom: 0.65rem; overflow: hidden;
}
.module.is-active { border-color: var(--accent); }
.module.is-locked { opacity: 0.62; }
/* Paid, not unwritten. Deliberately not dimmed the way is-locked is: this
   content exists and is finished, and making it look faded would undersell the
   thing being asked for. */
.module.is-paid .mod-mark { background: var(--accent-wash); color: var(--accent); }
.module > summary {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: 1.15rem 1.3rem; cursor: pointer; list-style: none;
}
.module > summary::-webkit-details-marker { display: none; }
.module > summary:hover { background: var(--surface2); }
.mod-mark {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-size: 0.8rem;
  background: var(--surface2); color: var(--ink3);
}
.module.is-done .mod-mark { background: var(--good); color: #fff; }
.module.is-active .mod-mark { background: var(--accent); color: #fff; }
.mod-body { flex: 1; display: flex; flex-direction: column; gap: 0.2rem; }
.mod-name { font-family: "Kanit", sans-serif; font-weight: 600; font-size: 1.03rem; display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.mod-blurb { font-size: 0.9rem; color: var(--ink2); }
.mod-count {
  font-family: "IBM Plex Mono", monospace; font-size: 0.78rem; color: var(--ink3);
  flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 0.25rem;
}
.mod-words { font-size: 0.72rem; white-space: nowrap; }
/* A lesson someone is not entitled to. Says what it is and what it costs, and
   does not render one word of the lesson behind it. */
.locked { max-width: 46ch; }
.locked h1 { margin: 0.3rem 0 0.8rem; }
.locked .lede { color: var(--ink2); margin: 0 0 1.8rem; }
.locked-ctas { display: flex; gap: 0.7rem; flex-wrap: wrap; align-items: center; }

/* ── Billing ──────────────────────────────────────────────────
   The subscribe button is a form, not a link, so that buying carries a CSRF
   token and cannot be spent by a prefetch. That puts a <form> in the middle of
   a row of buttons, and this is what stops it introducing a line break and a
   stray margin the <a> next to it does not have. */
.upgrade-form { display: contents; }

.bill-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 2rem; margin-bottom: 1.2rem;
}
.bill-plan {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink3); margin: 0 0 0.7rem;
}
.bill-card .lede { color: var(--ink2); margin: 0 0 1.6rem; max-width: 62ch; }
.bill-card .lede strong { color: var(--ink); }
.bill-foot { font-size: 0.88rem; color: var(--ink3); margin: 1.3rem 0 0; max-width: 62ch; }

.mod-soon { padding: 0 1.3rem 1.3rem 3.3rem; font-size: 0.9rem; color: var(--ink3); margin: 0; }
.mod-note {
  padding: 0 1.3rem 0 3.3rem; margin: 0 0 0.6rem;
  font-size: 0.88rem; color: var(--ink2); max-width: 62ch;
}

.lessons { list-style: none; padding: 0 1.3rem 1rem 3.3rem; margin: 0; }
.lessons li { border-top: 1px solid var(--border); }
.ls { display: flex; justify-content: space-between; gap: 1rem; align-items: center; padding: 0.7rem 0; text-decoration: none; color: var(--ink); }
a.ls:hover .ls-name { color: var(--accent); }
.ls-name { font-size: 0.95rem; }
.ls-meta { font-family: "IBM Plex Mono", monospace; font-size: 0.75rem; color: var(--ink3); flex: none; }
.lessons li.is-locked .ls { color: var(--ink3); }
.lessons li.is-active .ls-name { font-weight: 600; color: var(--accent); }
.lessons li.is-done .ls-name { color: var(--ink2); }
.lessons li.is-done .ls-meta { color: var(--good); }
.lessons li.is-check .ls-name { font-style: italic; }
.lessons li.is-paid .ls-name { color: var(--ink2); }
.lessons li.is-paid .ls-meta { color: var(--accent); }

/* ── Lesson brief ─────────────────────────────────────────── */

.crumb { font-size: 0.87rem; color: var(--ink3); margin-bottom: 0.8rem; }
.crumb a { color: var(--ink2); text-decoration: none; }
.crumb a:hover { color: var(--accent); }
.lesson-meta { display: flex; gap: 1.6rem; flex-wrap: wrap; margin: 1.5rem 0 2rem; font-size: 0.92rem; color: var(--ink2); }
.lesson-meta strong { font-family: "Kanit", sans-serif; font-size: 1.05rem; color: var(--ink); }

.track-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.track-head h2 { margin: 0; }
.track-toggle { display: inline-flex; border: 1px solid var(--border2); border-radius: var(--r-sm); overflow: hidden; }
.track-toggle button {
  border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 0.85rem; padding: 0.45rem 0.9rem; color: var(--ink2);
}
.track-toggle button.on { background: var(--ink); color: var(--paper); }
.track-note { font-size: 0.92rem; color: var(--ink3); margin-top: 0.6rem; }

/* ── Stats ────────────────────────────────────────────────── */

.stat-row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); margin: 2rem 0 3rem; }
.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 1.4rem; }
.stat-n { font-family: "Kanit", sans-serif; font-weight: 700; font-size: 2.1rem; line-height: 1; letter-spacing: -0.02em; }
.stat-l { font-size: 0.87rem; color: var(--ink2); margin-top: 0.35rem; }

.forecast { display: flex; align-items: flex-end; gap: 4px; height: 130px; margin: 1.5rem 0 0.6rem; }
.fc { flex: 1; background: var(--accent); border-radius: 3px 3px 0 0; min-height: 3px; opacity: 0.85; }
.fc:hover { opacity: 1; }
.fc-labels { display: flex; gap: 4px; }
.fc-labels span { flex: 1; text-align: center; font-family: "IBM Plex Mono", monospace; font-size: 0.65rem; color: var(--ink3); }

/* ── Course design review ─────────────────────────────────── */

.design-lessons { padding: 0 1.3rem 1.2rem 1.3rem; }
.dl { border-top: 1px solid var(--border); padding: 0.9rem 0; }
.dl-check { opacity: 0.6; }
.dl-head { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; }
.dl-n {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem;
  color: var(--ink3); flex: none; width: 1.1rem;
}
.dl-name { font-weight: 600; font-size: 0.97rem; }
/* Written and playable. Green rather than the accent, because this is a
   completion signal and the accent is reserved for what still needs work. */
.dl-built {
  font-size: 0.8rem; color: var(--good); flex: none;
  background: var(--good-wash); border-radius: 100px;
  width: 1.15rem; height: 1.15rem; display: inline-grid; place-items: center;
}
.dl-frame {
  font-family: "IBM Plex Sans Thai", sans-serif; font-size: 0.85rem;
  background: var(--accent-wash); color: var(--accent);
  padding: 0.1rem 0.5rem; border-radius: var(--r-sm);
}
.dl-words { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.6rem 0 0 1.8rem; }
.dl-note {
  margin: 0.55rem 0 0 1.8rem; font-size: 0.86rem; color: var(--ink3);
  border-left: 2px solid var(--border2); padding-left: 0.7rem;
}

.wchip {
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  border-radius: var(--r-sm); padding: 0.28rem 0.6rem;
  font-size: 0.95rem; border: 1px solid transparent;
}
.wchip em { font-style: normal; font-size: 0.75rem; opacity: 0.75; }
.wchip.is-have   { background: var(--good-wash); color: var(--good); }
.wchip.is-new    { background: var(--accent-wash); color: var(--accent); border-color: currentColor; }
.wchip.is-letter { background: var(--surface2); color: var(--ink2); font-size: 1.15rem; padding: 0.15rem 0.55rem; }

.gap-list { display: grid; gap: 1.1rem; margin-top: 1.5rem; }
.gap {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.1rem 1.3rem;
}
.gap h3 {
  font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink3); margin-bottom: 0.5rem;
}
.gap p { margin: 0; font-size: 1.02rem; color: var(--accent); }
.root-table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.95rem; }
.root-table th {
  text-align: left; padding: 0.5rem 0.8rem 0.5rem 0; border-bottom: 1px solid var(--ink);
  font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink3);
}
.root-table td { padding: 0.55rem 0.8rem 0.55rem 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.rt-root { font-size: 1.1rem; color: var(--accent); white-space: nowrap; }
.rt-in { color: var(--ink2); }
.mod-can { font-size: 0.9rem; color: var(--good); }
.article-body h3, .app-band h3 { margin-top: 2rem; }

/* ── Certificate ──────────────────────────────────────────── */

.cert {
  position: relative;
  aspect-ratio: 1.91 / 1;
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--r-lg);
  padding: clamp(1.4rem, 3.4vw, 2.8rem);
  display: flex; flex-direction: column;
  margin: 2rem 0 1.2rem;
  overflow: hidden;
  container-type: inline-size;
}
.cert::after {
  content: ""; position: absolute; inset: auto -10% -35% auto;
  width: 60%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle, rgba(224,0,108,0.22), transparent 70%);
  pointer-events: none;
}

.cert-top { display: flex; justify-content: space-between; align-items: baseline; }
.cert-brand { font-family: "Kanit", sans-serif; font-weight: 600; font-size: clamp(0.8rem, 1.7cqw, 1.05rem); }
.cert-brand [lang="th"] { color: var(--accent); }
.cert-date {
  font-family: "IBM Plex Mono", monospace; font-size: clamp(0.6rem, 1.2cqw, 0.75rem);
  color: color-mix(in srgb, var(--paper) 55%, transparent);
}

.cert-mark { display: flex; align-items: center; gap: 1.1rem; margin-top: clamp(0.6rem, 2cqw, 1.4rem); }
.cert-level {
  font-family: "Kanit", sans-serif; font-weight: 700;
  font-size: clamp(2.6rem, 8.5cqw, 5.2rem); line-height: 0.9; letter-spacing: -0.04em;
}
.cert-curves { display: flex; flex-direction: column; gap: 0.2rem; color: var(--accent); }
.cert-curves .tone { width: clamp(28px, 5cqw, 52px); height: auto; }

.cert-name {
  font-family: "Kanit", sans-serif; font-weight: 600;
  font-size: clamp(1.1rem, 3.2cqw, 2rem); margin: clamp(0.4rem, 1.4cqw, 0.9rem) 0 0;
}
.cert-what {
  font-size: clamp(0.72rem, 1.4cqw, 0.92rem); margin: 0.1rem 0 0;
  color: color-mix(in srgb, var(--paper) 65%, transparent);
}

.cert-cans {
  list-style: none; padding: 0; margin: clamp(0.5rem, 1.6cqw, 1rem) 0 0;
  display: grid; gap: clamp(0.15rem, 0.6cqw, 0.4rem);
}
.cert-cans li {
  font-size: clamp(0.68rem, 1.45cqw, 0.98rem);
  padding-left: 1.1rem; position: relative; line-height: 1.35;
}
.cert-cans li::before {
  content: "\2713"; position: absolute; left: 0; color: var(--accent); font-size: 0.85em;
}

.cert-foot {
  display: flex; gap: clamp(0.8rem, 2.4cqw, 2rem); margin-top: auto;
  padding-top: clamp(0.5rem, 1.6cqw, 1rem);
  font-size: clamp(0.62rem, 1.25cqw, 0.85rem);
  color: color-mix(in srgb, var(--paper) 70%, transparent);
}
.cert-foot strong { color: var(--paper); font-family: "Kanit", sans-serif; }

.cert-small {
  margin: clamp(0.3rem, 1cqw, 0.7rem) 0 0;
  font-size: clamp(0.54rem, 1.05cqw, 0.72rem); line-height: 1.4;
  color: color-mix(in srgb, var(--paper) 48%, transparent);
  max-width: 62%;
}

.cert-actions { display: flex; gap: 0.7rem; flex-wrap: wrap; margin-bottom: 1rem; }
.tag-core { background: var(--accent-wash); color: var(--accent); border-color: transparent; }
/* Where B1's core tier stops. A scope line as much as an ordering one. */
.tier-break {
  display: flex; align-items: center; gap: 0.9rem;
  margin: 1.6rem 0; color: var(--ink3);
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.tier-break::before, .tier-break::after {
  content: ""; flex: 1; height: 1px; background: var(--border2);
}
.tier-note {
  margin: -0.8rem 0 1.2rem;
  color: var(--ink3);
  font-size: 0.92rem;
}

/* ── Review: a sentence card ───────────────────────────────────
   Same card as a word, smaller type and wrapping enabled. A full
   sentence set at word size runs off the edge, and Thai has no
   spaces to break on, so the line-break behaviour matters more
   here than the size does. */
.rv-sentence .rv-th {
  font-size: clamp(1.4rem, 5vw, 2rem);
  line-height: 1.5;
  word-break: break-word;
  text-align: center;
}
.rv-sentence .rv-face { flex-wrap: wrap; }
.rv-tag-s { background: var(--accent-wash); color: var(--accent); }

/* ── Look up ───────────────────────────────────────────────────
   A dictionary row carries four things and they have four
   different jobs, so they get four weights rather than four
   colours: the Thai is the answer, the gloss is the meaning, the
   schedule state is a status, the lesson is a link out. */
.lookup { display: flex; gap: 0.6rem; margin: 1.6rem 0 1.2rem; }
.lookup input[type="search"] {
  flex: 1; min-width: 0; font-family: inherit; font-size: 1rem;
  padding: 0.7rem 1rem; color: var(--ink); background: var(--surface);
  border: 1px solid var(--border2); border-radius: var(--r-sm);
}
.lookup input[type="search"]::placeholder { color: var(--ink3); }
.lookup-count {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink3);
  margin: 1.8rem 0 0.6rem;
}
.lookup-list {
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); overflow: hidden;
}
.lk { padding: 0.95rem 1.1rem; border-top: 1px solid var(--border); }
.lk:first-child { border-top: 0; }
.lk-main { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.lk-th { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.35rem; font-weight: 600; }
.lk-th-s { font-size: 1.1rem; line-height: 1.6; word-break: break-word; }
.lk-r { font-family: "IBM Plex Mono", monospace; font-size: 0.82rem; color: var(--ink3); }
.lk-en { margin: 0.2rem 0 0; color: var(--ink2); font-size: 0.95rem; }
.lk-meta {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  margin: 0.5rem 0 0; font-size: 0.82rem; color: var(--ink3);
}
.lk-where a { color: var(--ink2); }
.lk-play {
  flex: none; width: 1.6rem; height: 1.6rem; display: inline-grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--accent-wash); color: var(--accent);
}
.lk-play svg { width: 0.55rem; height: 0.62rem; margin-left: 1px; }
.lk-play.playing { background: var(--accent); color: var(--paper); }
/* Where a recording would be if they were subscribed. Quiet — it marks a
   boundary rather than nagging at one. */
.lk-locked {
  flex: none; width: 1.6rem; height: 1.6rem; display: inline-grid; place-items: center;
  border-radius: 50%; background: var(--surface2); color: var(--ink3);
}
.lk-locked:hover { color: var(--accent); background: var(--accent-wash); }
.lk-state {
  font-family: "IBM Plex Mono", monospace; font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase;
  padding: 0.18rem 0.5rem; border-radius: 100px; white-space: nowrap;
}
.lk-new   { background: var(--surface2); color: var(--ink3); }
.lk-sched { background: var(--surface2); color: var(--ink2); }
.lk-due   { background: var(--accent-wash); color: var(--accent); }
.lk-known { background: var(--good-wash); color: var(--good); }
.lk-skip  { background: var(--surface2); color: var(--ink3); }

/* "I already know this" on a meet card. Quiet on purpose — it sits under the
   primary action and should not compete with it, because taking it when you do
   not mean it costs the learner a word. */
.pl-known {
  display: block; width: 100%; margin-top: 0.6rem;
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 0.86rem; color: var(--ink3);
  padding: 0.55rem; border-radius: var(--r-sm);
}
.pl-known:hover { color: var(--ink2); background: var(--surface2); }

/* Look up — the browse filters.
   Two strips: what you know, and what kind of word it is. The first is the one
   the old app could not offer, so it goes first. */
.lk-filters { display: flex; flex-direction: column; gap: 0.55rem; margin: 1.4rem 0 0.4rem; }
.lk-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.lk-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.82rem; padding: 0.34rem 0.72rem; border-radius: 100px;
  background: var(--surface); color: var(--ink2);
  border: 1px solid var(--border2); text-decoration: none;
}
.lk-chip:hover { border-color: var(--ink3); color: var(--ink); }
.lk-chip span {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem; color: var(--ink3);
  font-variant-numeric: tabular-nums;
}
.lk-chip.on {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}
.lk-chip.on span { color: var(--paper); opacity: 0.7; }
/* The status chips carry the same colours as the state pills in the rows, so a
   filter and the thing it filters to are visibly the same idea. */
.lk-chip-known:not(.on) { border-color: var(--good); color: var(--good); }
.lk-chip-due:not(.on)   { border-color: var(--accent); color: var(--accent); }

.lk-pages {
  display: flex; align-items: center; gap: 1rem; justify-content: center;
  margin: 1.2rem 0 0; font-size: 0.86rem; color: var(--ink3);
}
.lk-pages a { color: var(--ink2); }

/* ── Read something ────────────────────────────────────────────
   The passage is the thing on this page, so it is set large and
   the marking is a background wash rather than a border or a
   colour on the text — Thai needs its shapes readable, and
   recolouring the glyphs fights that. */
.rd-form { display: flex; flex-direction: column; gap: 0.7rem; margin: 1.6rem 0 1.2rem; }
.rd-form textarea {
  font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.15rem; line-height: 1.7;
  padding: 0.9rem 1rem; color: var(--ink); background: var(--surface);
  border: 1px solid var(--border2); border-radius: var(--r-sm); resize: vertical;
}
.rd-form textarea::placeholder { color: var(--ink3); }
.rd-form .btn { align-self: flex-start; }

.rd-out {
  font-family: "IBM Plex Sans Thai", sans-serif;
  font-size: clamp(1.3rem, 4vw, 1.7rem); line-height: 2.1;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 1.1rem 1.2rem;
}
.rd-tok {
  border-radius: var(--r-sm); padding: 0.1rem 0.22rem;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
.rd-out [data-audio] { cursor: pointer; }
.rd-sp { display: inline-block; width: 0.5em; }

.rd-known    { background: var(--good-wash);   color: var(--good); }
.rd-due      { background: var(--accent-wash); color: var(--accent); }
.rd-learning { background: var(--surface2);    color: var(--ink); }
.rd-new      { background: transparent;        color: var(--ink2); }
/* Not a word the course has at all — dotted, because it is a gap in coverage
   rather than a gap in the learner. */
.rd-out-of {
  color: var(--ink3); border-bottom: 2px dotted var(--border2);
  border-radius: 0; padding-bottom: 0;
}

.rd-key { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.7rem 0 0; font-size: 0.78rem; }
.rd-key .rd-tok { font-family: var(--body); }

/* ── Lesson player ────────────────────────────────────────── */

.player-shell .site-head, .player-shell .site-foot, .player-shell .app-nav { display: none; }
.player { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }

.pl-top {
  display: flex; align-items: center; gap: 1rem;
  padding: 1rem clamp(1rem, 4vw, 2rem);
  width: min(680px, 100%); margin-inline: auto;
}
.pl-quit { font-size: 1.6rem; line-height: 1; color: var(--ink3); text-decoration: none; }
.pl-quit:hover { color: var(--ink); }
.pl-bar { flex: 1; height: 8px; border-radius: 4px; background: var(--surface2); overflow: hidden; }
.pl-bar span { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 4px; transition: width 0.35s cubic-bezier(.4,0,.2,1); }
.pl-count { font-family: "IBM Plex Mono", monospace; font-size: 0.72rem; color: var(--ink3); min-width: 3.4rem; text-align: right; }

/* Column, not row: every beat renders a single card and centres identically
   either way, but the finish screen can append a second one below it. */
.pl-stage {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1.5rem;
  padding: 1rem clamp(1rem, 4vw, 2rem); overflow-y: auto;
}
.pl-card { width: min(560px, 100%); text-align: center; flex: none; }

/* Two cards no longer fit, so stop centring them — a centred overflowing flex
   column clips its own top and leaves it unreachable. Flow from the top and
   let the stage scroll properly instead. */
.pl-stage-stacked { justify-content: flex-start; padding-block: 2rem; }

.pl-kicker {
  font-family: "IBM Plex Mono", monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink3); margin-bottom: 1.6rem;
}

.pl-th-row { display: flex; align-items: center; justify-content: center; gap: 1rem; }
.pl-th { font-family: "IBM Plex Sans Thai", sans-serif; font-size: clamp(2.4rem, 9vw, 3.6rem); line-height: 1.45; }
.pl-roman { font-family: "IBM Plex Mono", monospace; color: var(--accent); font-size: 1rem; margin: 0.5rem 0 0; }
.pl-en { font-size: 1.25rem; color: var(--ink); margin: 0.3rem 0 0; }
.pl-note {
  margin: 1.4rem auto 0; max-width: 40ch; font-size: 0.92rem;
  color: var(--ink2); border-top: 1px solid var(--border); padding-top: 1rem;
}
.pl-prompt { font-size: clamp(1.4rem, 5vw, 2rem); font-family: "Kanit", sans-serif; font-weight: 600; margin: 0 0 1.8rem; }

.pl-audio {
  display: inline-grid; place-items: center; flex: none;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1.5px solid var(--border2); background: transparent;
  color: var(--accent); cursor: pointer; padding: 0;
  transition: background 0.18s, border-color 0.18s, color 0.18s, transform 0.15s;
}
.pl-audio svg { width: 13px; height: 15px; margin-left: 2px; }
.pl-audio:hover { border-color: var(--accent); }
.pl-audio.playing { background: var(--accent); border-color: var(--accent); color: #fff; transform: scale(1.06); }
.pl-audio-lg { width: 64px; height: 64px; }
.pl-audio-lg svg { width: 18px; height: 21px; }
.pl-listen { display: flex; justify-content: center; margin-bottom: 2rem; }

.pl-opts { display: grid; gap: 0.6rem; margin-top: 2rem; }
.pl-opt {
  padding: 0.95rem 1.2rem; border-radius: var(--r);
  border: 1.5px solid var(--border); background: var(--surface);
  font: inherit; font-size: 1rem; color: var(--ink); cursor: pointer; text-align: center;
  transition: border-color 0.15s, background 0.15s, transform 0.12s;
}
.pl-opt:hover { border-color: var(--ink3); transform: translateY(-1px); }
.pl-opt.is-ok { border-color: var(--good); background: var(--good-wash); color: var(--good); }
.pl-opt.is-no { border-color: var(--accent); background: var(--accent-wash); color: var(--accent); }
.pl-opts-th .pl-opt { display: flex; flex-direction: column; gap: 0.15rem; }
.pl-opts-th [lang="th"] { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.5rem; }
.pl-opts-th em { font-style: normal; font-family: "IBM Plex Mono", monospace; font-size: 0.75rem; opacity: 0.7; }

.pl-match { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; margin-top: 1.5rem; }
.pl-col { display: grid; gap: 0.6rem; align-content: start; }
.pl-tile {
  padding: 0.85rem 0.7rem; border-radius: var(--r);
  border: 1.5px solid var(--border); background: var(--surface);
  font: inherit; font-size: 0.95rem; color: var(--ink); cursor: pointer;
  transition: border-color 0.15s, background 0.15s, opacity 0.25s;
  min-height: 3.2rem;
}
.pl-tile[lang="th"] { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.2rem; }
.pl-tile:hover { border-color: var(--ink3); }
.pl-tile.is-on { border-color: var(--accent); background: var(--accent-wash); }
.pl-tile.is-done { opacity: 0.28; pointer-events: none; border-style: dashed; }
.pl-tile.is-bad { border-color: var(--accent); animation: plShake 0.35s; }
@keyframes plShake { 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }

/* build — tap tiles in order to assemble a sentence */
.pl-build-answer {
  display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;
  min-height: 4.4rem; padding: 0.7rem; margin-bottom: 1.6rem;
  border-radius: var(--r); border: 1.5px dashed var(--border);
  background: var(--surface-2, transparent);
  transition: border-color 0.2s, background 0.2s;
}
.pl-build-answer.is-empty::before {
  content: "Tap the words below, in order";
  color: var(--ink3); font-size: 0.85rem; padding: 0 0.4rem;
}
.pl-build-answer.is-ok { border-style: solid; border-color: var(--good); background: var(--good-wash); }
.pl-build-answer.is-no { border-style: solid; border-color: var(--accent); background: var(--accent-wash); }

.pl-build-bank { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
.pl-build-bank:empty { min-height: 3.4rem; }

.pl-word {
  display: inline-flex; flex-direction: column; align-items: center; gap: 0.1rem;
  min-height: 0; padding: 0.55rem 0.85rem;
}
.pl-word [lang="th"] { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.25rem; line-height: 1.3; }
.pl-word em { font-style: normal; font-family: "IBM Plex Mono", monospace; font-size: 0.7rem; opacity: 0.65; }
.pl-build-answer .pl-word { border-color: var(--ink3); }

.pl-done-trim { color: var(--good); font-size: 0.9rem; margin-top: 0.6rem; }
.pl-done-xp:empty { display: none; }
.pl-done-xp { color: var(--ink2); font-size: 0.9rem; margin-top: 0.4rem; }

/* the reading track — a letter carries its class, a decode beat its options */
.pl-class {
  display: inline-block; margin: 0.2rem 0 0; padding: 0.2rem 0.7rem;
  border-radius: 999px; font-size: 0.75rem; letter-spacing: 0.04em;
  text-transform: uppercase; border: 1.5px solid var(--border); color: var(--ink2);
}
.pl-class-mid  { border-color: var(--good);    color: var(--good); }
.pl-class-high { border-color: var(--accent); color: var(--accent); }
.pl-class-low  { border-color: var(--ink3);    color: var(--ink2); }

.pl-decode .pl-th { font-size: clamp(2.6rem, 11vw, 4.2rem); }

/* note — the beat that explains rather than asks */
.pl-note-title { font-family: "Kanit", sans-serif; font-size: 1.35rem; margin: 0 0 1rem; }
.pl-note-body { text-align: left; font-size: 0.98rem; line-height: 1.6; color: var(--ink2); }
.pl-note-body p { margin: 0 0 0.9rem; }
.pl-note-body strong { color: var(--ink); font-weight: 600; }
.pl-note-body [lang="th"] { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.15em; }
.pl-note-body .tone-eg {
  display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem;
}
.pl-note-body .tone-eg span {
  padding: 0.35rem 0.7rem; border-radius: var(--r);
  border: 1.5px solid var(--border); background: var(--surface); font-size: 0.9rem;
}
.pl-opt-r { font-family: "IBM Plex Mono", monospace; font-size: 0.95rem; }

.pl-foot { padding: 1rem clamp(1rem, 4vw, 2rem) 1.6rem; width: min(560px, 100%); margin-inline: auto; }
.pl-verdict { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.pl-verdict span { font-weight: 500; flex: 1; }
.pl-verdict.is-ok span { color: var(--good); }
.pl-verdict.is-no span { color: var(--accent); }

.pl-done h1 { font-size: clamp(1.9rem, 6vw, 2.8rem); }
.pl-done-sub { color: var(--ink2); margin-bottom: 2rem; }
.pl-done-curves { display: flex; justify-content: center; gap: 0.5rem; color: var(--accent); margin-bottom: 1.5rem; }
.pl-done-curves svg { width: 40px; height: 17px; opacity: 0; animation: plRise 0.5s ease-out forwards; }
@keyframes plRise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.pl-done-stats { display: flex; justify-content: center; gap: 2.4rem; margin: 2rem 0; }
.pl-done-stats div { display: flex; flex-direction: column; }
.pl-done-stats b { font-family: "Kanit", sans-serif; font-size: 2rem; line-height: 1; }
.pl-done-stats span { font-size: 0.82rem; color: var(--ink3); margin-top: 0.3rem; }
.pl-done-note { font-size: 0.93rem; color: var(--ink2); max-width: 38ch; margin-inline: auto; }

/* ── Review ───────────────────────────────────────────────────
   Deliberately the lesson player's chrome. A review and a lesson
   are two halves of one session, and the old app looking like a
   different product was the tell that it was one. */

.rv-loading { color: var(--ink3); font-size: 0.95rem; }

.rv-tag {
  font-family: "IBM Plex Mono", monospace; font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--good); margin: 0 0 1.2rem;
}
.rv-face { display: flex; flex-direction: column; align-items: center; gap: 1.1rem; }
.rv-th {
  font-family: "IBM Plex Sans Thai", sans-serif;
  font-size: clamp(2.6rem, 12vw, 4.2rem); line-height: 1.35; font-weight: 600;
}
.rv-back { margin-top: 2rem; animation: plRise 0.28s ease-out both; }
.rv-roman {
  font-family: "IBM Plex Mono", monospace; font-size: 1.05rem;
  color: var(--ink2); margin: 0 0 0.4rem;
}
.rv-en { font-size: 1.35rem; margin: 0; }
.rv-note { font-size: 0.9rem; color: var(--ink3); margin: 0.9rem 0 0; }

/* Four grades. Wide enough to hit on a phone without looking, because the
   interval underneath is the thing being chosen, not the word. */
.rv-grades { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; }
.rv-grade {
  display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
  padding: 0.75rem 0.3rem; cursor: pointer;
  background: var(--surface); color: var(--ink);
  border: 1.5px solid var(--border2); border-radius: var(--r-sm);
  font: inherit; transition: border-color 0.12s, background 0.12s, transform 0.12s;
}
.rv-grade:hover { transform: translateY(-1px); }
.rv-grade-l { font-size: 0.88rem; font-weight: 500; }
.rv-grade-i {
  font-family: "IBM Plex Mono", monospace; font-size: 0.68rem; color: var(--ink3);
}
.rv-again:hover { border-color: var(--accent); background: var(--accent-wash); }
.rv-hard:hover  { border-color: var(--ink3); }
.rv-good:hover  { border-color: var(--good); background: var(--good-wash); }
.rv-easy:hover  { border-color: var(--good); background: var(--good-wash); }

.rv-skip {
  display: block; width: 100%; margin-top: 0.7rem; padding: 0.4rem;
  background: none; border: 0; font: inherit; font-size: 0.85rem;
  color: var(--ink3); cursor: pointer;
}
.rv-skip:hover { color: var(--accent); }

@media (max-width: 420px) {
  .rv-grades { gap: 0.35rem; }
  .rv-grade { padding: 0.65rem 0.15rem; }
  .rv-grade-l { font-size: 0.8rem; }
  .rv-grade-i { font-size: 0.62rem; }
}

/* ── End of a level ───────────────────────────────────────────
   The exit ramp. Deliberately quiet: it is an argument made by
   showing someone a sentence they cannot quite read, not a
   banner. If it looks like an advert it will be dismissed like
   one, and the demonstration is the persuasive part. */

.pl-milestone {
  border-top: 1px solid var(--border); padding-top: 2rem;
  animation: plRise 0.5s ease-out both; animation-delay: 0.3s;
}
.pl-milestone h2 { font-size: clamp(1.3rem, 4vw, 1.7rem); margin-bottom: 0.6rem; }
.pl-ms-lead { font-size: 0.95rem; color: var(--ink2); max-width: 44ch; margin: 0 auto 1.8rem; }

.pl-ms-sentence {
  font-family: "IBM Plex Sans Thai", sans-serif;
  font-size: clamp(1.8rem, 7vw, 2.6rem); line-height: 1.5;
  margin: 0 0 1rem; word-break: keep-all;
}
.pl-ms-gap {
  font-weight: 600; color: var(--accent);
  border-bottom: 3px solid var(--accent-wash); padding-bottom: 2px;
}
.pl-ms-prompt {
  font-size: 0.9rem; color: var(--ink3); max-width: 40ch;
  margin: 0 auto 1.6rem;
}
.pl-ms-prompt [lang="th"] { font-family: "IBM Plex Sans Thai", sans-serif; }

.pl-ms-answer { animation: plRise 0.4s ease-out both; }
.pl-ms-roman {
  font-family: "IBM Plex Mono", monospace; font-size: 0.95rem;
  color: var(--ink2); margin: 0 0 0.3rem;
}
.pl-ms-en { font-size: 1.05rem; margin: 0 0 1.6rem; }

/* The one word they are missing, given its own frame — it is the
   whole point of the screen. */
.pl-ms-word {
  display: flex; align-items: center; justify-content: center; gap: 0.75rem;
  flex-wrap: wrap; background: var(--accent-wash);
  border-radius: var(--r-sm); padding: 0.9rem 1.1rem; margin-bottom: 1.4rem;
}
.pl-ms-word [lang="th"] {
  font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.5rem; font-weight: 600;
}
.pl-ms-word-r { font-family: "IBM Plex Mono", monospace; font-size: 0.88rem; color: var(--ink2); }
.pl-ms-word-en { font-size: 0.92rem; color: var(--ink2); }
.pl-ms-play {
  background: none; border: 0; padding: 0.3rem; cursor: pointer;
  color: var(--accent); line-height: 0;
}
.pl-ms-play svg { width: 12px; height: 14px; }
.pl-ms-play.playing { opacity: 0.45; }

.pl-ms-reveal-body {
  font-size: 0.93rem; color: var(--ink2); line-height: 1.65;
  max-width: 46ch; margin: 0 auto 1.8rem; text-align: left;
}
.pl-ms-ctas { display: flex; flex-direction: column; gap: 0.6rem; }

/* Review-page banner. Only ever on /learn/design/ramp. */
.ramp-note {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 40;
  background: var(--surface2); border-top: 1px solid var(--border);
  padding: 0.55rem 1rem; margin: 0; text-align: center;
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  line-height: 1.5; color: var(--ink3);
}
.ramp-note code { font-size: 0.95em; color: var(--ink2); }
/* The note is fixed, so keep it off the footer buttons underneath it. */
.ramp-review .pl-foot { padding-bottom: 4rem; }
.ls-play {
  color: var(--accent); font-family: "IBM Plex Mono", monospace;
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.09em;
}
.ls.is-playable .ls-name { font-weight: 600; }

.home-demo {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: clamp(1.3rem, 3vw, 2rem);
  box-shadow: var(--shadow);
  text-align: center;
}
.home-demo .pl-prompt { margin-bottom: 0.35rem; }
.home-demo-hint { color: var(--ink3); font-size: 0.92rem; margin: 0 0 1.1rem; }
.home-demo .btn { margin-top: 1.2rem; }
.home-demo .pl-word {
  min-width: 7.5rem;
}
.home-demo .pl-word strong {
  font-weight: 500; font-size: 0.92rem; color: var(--ink);
}
.home-demo .pl-word [lang="th"] {
  font-size: 1.05rem; color: var(--ink2); line-height: 1.3;
}
.home-demo-show {
  display: block; width: 100%; margin-top: 0.7rem;
  background: none; border: 0; padding: 0.2rem;
  font: inherit; color: var(--ink3); cursor: pointer;
}
.home-demo-show:hover { color: var(--ink); }
.home-demo-done {
  margin: 1.1rem 0 0;
  font-family: "IBM Plex Sans Thai", "IBM Plex Sans", sans-serif;
  color: var(--ink2);
  font-size: 1.05rem;
}

/* ── The proof strip ──────────────────────────────────────────
   Four measured numbers, straight under the hero. Every one of
   them is asserted by scripts/verify-figures.php or printed by
   import-content.php — none is a remembered figure. */
.proof {
  display: grid; gap: 1px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  /* Space underneath, or the card's bottom edge butts straight into the tinted
     band that follows and stops reading as a card sitting on the paper. */
  margin: 0 0 clamp(2.5rem, 5vw, 4rem);
}
.proof > div { background: var(--surface); padding: 1.6rem 1.25rem; }
.proof dt {
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(1.4rem, 3vw, 1.75rem); color: var(--accent);
  line-height: 1; margin-bottom: 0.4rem;
}
.proof dd { margin: 0; font-size: 0.9rem; color: var(--ink2); }

@media (max-width: 720px) {
  .proof { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── The product screens ──────────────────────────────────────
   Drawn in markup rather than screenshotted, so they restyle with
   the theme and never go stale against the real player. They are
   representative, not live: the only interactive thing on this
   page is the hero demo. */
.screens {
  display: grid; gap: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  margin-top: 3rem; align-items: start;
}
/* Four screens want a 2x2, not a 3+1 orphan: at the wrap width four columns
   of 280 do not fit, and auto-fit silently drops the fourth onto its own row. */
.screens-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem; }
@media (max-width: 700px) { .screens-4 { grid-template-columns: 1fr; } }

/* The dashboard mock's two tiles */
.shot-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; }
.shot-stat {
  display: flex; flex-direction: column; gap: 0.15rem;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 0.7rem 0.75rem; background: var(--paper);
}
.shot-stat-n {
  font-family: "IBM Plex Mono", monospace; font-size: 1.6rem;
  color: var(--accent); line-height: 1;
}
.shot-stat-k { font-size: 0.72rem; color: var(--ink3); }
.screen { display: flex; flex-direction: column; gap: 0.9rem; }
.screen h3 { margin: 0; }
.screen > p { color: var(--ink2); font-size: 0.97rem; margin: 0; }

.shot {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow);
  overflow: hidden; display: flex; flex-direction: column;
}
.shot-bar {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--border);
  background: var(--surface2);
}
.shot-bar i { width: 8px; height: 8px; border-radius: 50%; background: var(--border2); }
.shot-bar b {
  margin-left: auto; font-family: "IBM Plex Mono", monospace;
  font-size: 0.65rem; font-weight: 400; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink3);
}
.shot-body {
  padding: 1.1rem; display: flex; flex-direction: column;
  gap: 0.75rem; min-height: 236px;
}
.shot-bar-prog { height: 5px; border-radius: 99px; background: var(--surface2); overflow: hidden; }
.shot-bar-prog i { display: block; height: 100%; background: var(--accent); border-radius: 99px; }

/* Static stand-ins for the player's tiles. .pl-tile is the real
   thing and carries hover and click states these must not claim. */
.shot-tile {
  padding: 0.35rem 0.6rem; border-radius: var(--r-sm);
  background: var(--surface2); border: 1px solid var(--border);
  font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.1rem;
  color: var(--ink);
}
.shot-tile.is-used { opacity: 0.3; border-style: dashed; }
.shot-row { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.shot-slot {
  display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center;
  padding: 0.6rem; min-height: 46px;
  border: 1.5px dashed var(--border2); border-radius: var(--r);
  background: var(--paper);
}
.shot-word { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 2.4rem; line-height: 1.2; }
.shot-roman { font-family: "IBM Plex Mono", monospace; font-size: 0.78rem; color: var(--ink3); margin-top: 0.2rem; }
.shot-en { font-size: 0.95rem; color: var(--ink2); margin-top: 0.5rem; }

.shot-read { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.3rem; line-height: 2.1; color: var(--ink); }
.shot-read mark {
  background: var(--accent-wash); color: inherit;
  border-bottom: 2px solid var(--accent);
  padding: 0.1rem 0.1rem 0; border-radius: 3px 3px 0 0;
}
.shot-gloss {
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 0.6rem 0.75rem; background: var(--paper);
}
.shot-gloss b { font-family: "IBM Plex Sans Thai", sans-serif; font-size: 1.15rem; display: block; font-weight: 400; }
.shot-gloss em {
  font-style: normal; font-family: "IBM Plex Mono", monospace;
  font-size: 0.72rem; color: var(--ink3); display: block; margin: 0.1rem 0 0.25rem;
}
.shot-gloss span { font-size: 0.88rem; color: var(--ink2); }

/* The home page's FAQ reuses .faq / .faq-item / .faq-q / .faq-a from the
   pricing page rather than defining its own. A second `.faq` rule here
   silently restyled /pricing and all three tool pages, which share it. */


/* Starting test */
.place-hero { padding-bottom: 0; }
.place-prog {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink3);
  margin: 0 0 1.2rem;
}
.place-foot { margin-top: 1.4rem; display: flex; flex-direction: column; gap: 0.8rem; }
.place-skip {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--ink3); cursor: pointer; text-align: center; text-decoration: none;
}
.place-skip:hover { color: var(--ink); }
.place-quiet { color: var(--ink3); font-size: 0.92rem; margin: 0.4rem 0 1.2rem; }
.place-copy { color: var(--ink2); font-size: 1.05rem; line-height: 1.6; margin: 0 auto; max-width: 46ch; }
.page-start .pl-card {
  width: 100%; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: clamp(1.4rem, 4vw, 2.2rem);
  box-shadow: var(--shadow);
}

/* ── Confirm-your-email banner ──────────────────────────────────────────────
   Sits under the app nav until the address is proved. Deliberately quiet —
   it is a nudge, not an alert — but it uses the accent so it does not read as
   another nav row. See verify_banner() in site/helpers.php. */
.verify-banner {
  background: var(--accent-wash);
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
  color: var(--ink);
  padding: 0.6rem 0;
}
.verify-banner .wrap { display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: baseline; }
.verify-banner a { color: var(--accent); font-weight: 600; }

/* ── The admin panel ───────────────────────────────────────────────────────
   /learn/admin. Dense on purpose: it is a working tool read by one person on a
   laptop, so it trades the site's air for getting the whole business onto one
   screen. Everything is a token, so it themes with the rest. */
.admin-env {
  font-family: "IBM Plex Mono", monospace; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink3);
  margin: 0; text-align: right;
}
/* Six cards per band, and they are meant to sit on one line — the default
   .stat-row minimum leaves the sixth on a row of its own looking like an
   afterthought. */
.admin-stats { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }
.admin-h { font-size: 1.1rem; margin: 2.5rem 0 0.9rem; }
.admin-h:first-of-type { margin-top: 0; }
.admin-note { font-size: 0.87rem; color: var(--ink2); margin: 0 0 1rem; max-width: 52ch; }
.admin-empty { font-size: 0.9rem; color: var(--ink3); margin: 0 0 1rem; }

.admin-notice {
  padding: 0.7rem 1rem; border-radius: var(--r); font-size: 0.9rem;
  border: 1px solid var(--border); margin: 0 0 1.5rem;
}
.admin-notice-good { background: var(--good-wash); border-color: color-mix(in srgb, var(--good) 30%, transparent); }
.admin-notice-bad  { background: color-mix(in srgb, var(--accent) 8%, transparent); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }

/* The flag line: things that need somebody to act, above the tables that would
   otherwise be where you found out. */
.admin-flag {
  font-size: 0.9rem; color: var(--ink2); margin: -1.5rem 0 1.5rem;
  padding: 0.7rem 1rem; border-left: 3px solid var(--accent);
  background: var(--surface); border-radius: 0 var(--r) var(--r) 0;
}

.admin-table { overflow-x: auto; margin-bottom: 0.5rem; }
.admin-table table {
  width: 100%; min-width: 640px; border-collapse: collapse;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); overflow: hidden; font-size: 0.87rem;
}
.admin-table th {
  text-align: left; padding: 0.6rem 0.9rem; white-space: nowrap;
  font-family: "IBM Plex Mono", monospace; font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--ink2); border-bottom: 1px solid var(--border);
}
.admin-table td { padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table tbody tr:hover td { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.admin-who { font-weight: 500; }
.admin-dim { color: var(--ink3); font-size: 0.82rem; }

.admin-pill {
  display: inline-block; margin-left: 0.3rem;
  font-family: "IBM Plex Mono", monospace; font-size: 0.62rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 0.15rem 0.4rem; border-radius: 999px;
  background: color-mix(in srgb, var(--ink2) 12%, transparent); color: var(--ink2);
}
.admin-who .admin-pill:first-child, td > .admin-pill:first-child { margin-left: 0; }
.admin-pill-good { background: var(--good-wash); color: var(--good); }
.admin-pill-warn { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); }

.admin-grant { display: grid; gap: 0.8rem; max-width: 26rem; }
.admin-grant label { display: grid; gap: 0.3rem; }
.admin-grant label span { font-size: 0.8rem; color: var(--ink2); }
.admin-grant input, .admin-grant select {
  padding: 0.55rem 0.7rem; font: inherit; font-size: 0.9rem;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--border); border-radius: var(--r);
}
.admin-grant button { justify-self: start; }

.admin-events { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.admin-events li { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.85rem; }
.admin-events code { font-size: 0.78rem; }
