/* ============================================================
   AI Hub — shared stylesheet
   One file. No framework, no build step.
   ============================================================ */

:root {
  --bg: #f9f8f6; --surface: #ffffff; --surface2: #f1f0ec;
  --border: rgba(0,0,0,0.08); --border-med: rgba(0,0,0,0.15);
  /* --text-ter is darkened from the house palette's #888780 (3.4:1) to clear
     WCAG AA 4.5:1 on all three surfaces. This site is aimed at older readers
     and carries real content in small type, so AA is not optional here. */
  --text: #1a1a18; --text-sec: #5f5e5a; --text-ter: #6b6a65;
  --green: #0f6e56;
  --lur-body: #9AD3C6; --green-bg: #e1f5ee;
  --red: #a32d2d; --red-bg: #fcebeb;
  --amber: #854f0b; --amber-bg: #faeeda;
  --blue: #185fa5; --blue-bg: #e6f1fb;

  /* ---- the mark ------------------------------------------------------------
     The Hlur mark's identity, matching hlur.ai/baseline so the two sub-sites
     read as one site: electric blue -> violet -> teal.

     --accent is the SOLID form and does the work — links, the primary button,
     focus. --grad is DISPLAY only and never touches text: its teal stop is
     2.35:1 on the light background, under even the 3.0 large-text floor, so
     gradient-clipped type would have an illegible end. --grad-text is the
     AA-safe pair for that (no teal; 6.09:1 and 6.51:1 on light, 8.2:1 on dark).
     --teal is site chrome, shared with the hlur.ai wordmark. */
  --accent: #285bd9;
  --g1: #2f6bff; --g2: #7a5cff; --g3: #12b8a6;
  --grad: linear-gradient(115deg, var(--g1), var(--g2) 52%, var(--g3));
  --grad-text: linear-gradient(115deg, #2456c9, #5b3bd6);
  /* hlur.ai's own --teal-deep. Its plain --teal (#2d8e7e) is only 3.75:1 here:
     the homepage wordmark is 33px, which clears AA as large text, but this nav
     sets it at 18px, where the same colour fails. Same brand hue, 5.64:1. */
  --teal: #1f6f62;
  --radius: 10px; --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.04);
  /* Container matched to hlur.ai/baseline, measured in a real browser at
     1280 and 1440: .wrap is max-width 940px with 22px horizontal padding.
     Nav padding is kept equal to content padding so the logo lines up with
     the text below it — baseline uses a wider 1200px nav shell, which we
     deliberately do not copy. */
  --wrap: 940px;
  --pad: 22px;
  /* Native controls follow the theme. Without this the root stays
     color-scheme:normal, so a checkbox renders in its LIGHT form whatever the
     page is doing: on a dark card every UNTICKED box was a solid white square,
     brighter than the headline above it, and 36 of them ran down the left edge
     of the checklists. accent-color only ever styled the ticked state, so the
     one that mattered — an empty box that read as already filled in — was
     untouched. */
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Declared again at the SAME value on purpose. Lur's body colour does not change
       between modes — the footer rule is the top of Lur, and she is one colour. Saying
       it here rather than letting it inherit tells the token guard, and the next
       reader, that the sameness is a decision. It reads 10.4:1 on this background.
       (This sat OUTSIDE :root for one build — a declaration where a rule belongs, which
       made the parser discard the whole dark block and flattened dark mode on all six
       pages. The harness caught it as a full-page diff, not a 2px one.) */
    --lur-body: #9AD3C6;
    --bg: #1a1a18; --surface: #242422; --surface2: #2c2c2a;
    --border: rgba(255,255,255,0.08); --border-med: rgba(255,255,255,0.15);
    /* lightened from #888780 (4.31:1 on --surface) for the same reason */
    --text: #f1f0ec; --text-sec: #b4b2a9; --text-ter: #9a9992;
    /* #085041 until 2026-08-09: at 1.85 against the page it was half again as
       heavy as red/amber/blue (1.20/1.23/1.24), which are its peers and sit
       uniform in light mode. On the triage table that inverted the ranking —
       "Nothing happens" shouted and "Someone gets hurt" sank. 1.21 now, and
       --green on it went 4.68:1 -> 7.15:1 at the time. The ink changed afterwards
       (see below), so that pairing now measures 5.89:1 and 7.15 is green on the
       PAGE — two different pairings, one number, which is how a comment starts
       lying. */
    /* INK, not fill. The four FILLS were balanced in the #093026 pass; the inks
       were left alone and their order ran backwards: green 8.68, amber 8.02,
       red 7.83 — descending, where light climbs 5.84 / 6.34 / 6.66. So on the
       triage table "Nothing happens" was the crispest thing in the column and
       "Someone gets hurt" the softest, and every tick/cross pair emphasised the
       tick. Ladder raised as a whole (7.90 / 8.66 / 9.23) rather than only lowering green:
       the smallest SVG diagram labels render at 7.5px at 320px, and a darker green
       lost the raster headroom that kept them legible there.
       Every pairing stays >=4.5 on page, surface and its own tint. */
    --green: #57c19d; --green-bg: #093026;
    --red: #f7a8a8; --red-bg: #501313;
    --amber: #f2a838; --amber-bg: #412402;
    --blue: #85b7eb; --blue-bg: #042c53;
    --accent: #7ba1ff;
    --g1: #6f97ff; --g2: #a48cff; --g3: #3fd6c4;
    --grad-text: linear-gradient(115deg, #85b7eb, #b9a5ff);
    --teal: #4fae9d;
    --shadow: 0 1px 2px rgba(0,0,0,.2), 0 4px 16px rgba(0,0,0,.25);
  }
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* The nav is sticky, so every in-page jump landed its target UNDERNEATH it:
     following #triage put the section label at y=40 with the nav's bottom edge
     at y=55, hiding the label and the top of the heading. All 28 in-page links
     were affected, including the six that point at #main — the skip link, where
     a keyboard user lands on content they cannot see. The nav is 55px on a
     desktop but 87px at 390 and 122px at 320, so the value is per breakpoint,
     and app.js refines it from the nav's measured height at runtime. */
  scroll-padding-top: 70px;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

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

/* Skip link — WCAG 2.4.1. Six nav links precede the content on every page, and
   a keyboard or screen-reader user had to walk all six on every navigation.
   Off-screen until focused, then it lands in the top-left. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--text);
  border: 0.5px solid var(--border-med); border-radius: 0 0 var(--radius-sm) 0;
  padding: 12px 18px; font-size: 15px; font-weight: 560;
  box-shadow: var(--shadow);
}
.skip:focus { left: 0; }

/* A visible focus ring everywhere. Nothing here sets outline:none, so browser
   defaults did survive — but they are thin and low-contrast against these
   surfaces, and this site is aimed partly at older readers. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Two short transitions exist; honour a reader who asked for stillness. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

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

.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 0.5px solid var(--border);
}
.nav-inner {
  max-width: var(--wrap); margin: 0 auto; padding: 10px var(--pad);
  display: flex; align-items: center; gap: 18px;
}
/* Site chrome. /hub used its own "* AI Hub" brand and read as a separate site
   next to hlur.ai and hlur.ai/baseline, which share the orbit mark and wordmark.
   Same mark, same weight and tracking as the rest of the site — but sized to this
   nav rather than copied at 33px, which would tower over 13.5px section links and
   crowd out the six of them on a phone. The section name sits beside it so the
   page still says where you are. */
.wordmark {
  font-weight: 700; font-size: 21px; color: var(--text);
  letter-spacing: -0.02em; white-space: nowrap; text-decoration: none;
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
}
.wordmark:hover { text-decoration: none; }
/* the dot is site chrome — teal on hlur.ai and /baseline, and it was green here */
.wordmark span { color: var(--teal); }
.nav-mark { width: 22px; height: 22px; flex: 0 0 auto; }
.nav-sect {
  font-weight: 600; font-size: 13.5px; color: var(--text-sec);
  white-space: nowrap; padding-left: 12px; margin-left: 2px;
  border-left: 0.5px solid var(--border-med); flex: 0 0 auto;
}
.nav-sect:hover { text-decoration: none; color: var(--text); }
.nav-links {
  display: flex; gap: 4px; margin-left: auto;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.nav-links::-webkit-scrollbar { display: none; }
.nav-links a {
  font-size: 13.5px; color: var(--text-sec); padding: 5px 9px;
  border-radius: var(--radius-sm); white-space: nowrap;
}
.nav-links a:hover { background: var(--surface2); text-decoration: none; color: var(--text); }
.nav-links a[aria-current="page"] { color: var(--text); background: var(--surface2); font-weight: 600; }

/* saturate(180%) is a light-mode trick: over a near-white bar the colour it pulls
   from the content scrolling underneath is invisible. Over a near-black bar the
   same boost turns a tinted panel passing below into a warm smear across the
   header. Blur without the boost in dark, on a slightly more opaque bar.

   This block sits AFTER the base .nav rule deliberately. Placed up in the
   palette block with the colour variables — which is where it reads like it
   belongs — it lost to the base rule further down the file: equal specificity
   means source order decides, and a media query does not change that. It was
   written, it looked right, and it did nothing. */
@media (prefers-color-scheme: dark) {
  .nav {
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(14px);
  }
}


/* The nav mark's accent, theme-aware. It was hardcoded to the light teal when the mark
   was inlined, so on the dark nav it rendered at 4.38:1 — passing, but a different tone
   from the standalone dark SVG, which lifts to #4fae9d (6.53:1). One mark should not
   have two accents depending on which file drew it. */
.nm-acc { fill: #2D8E7E }
/* The link stroke carries the accent too — it is half the "accent on the joint" idea.
   It was missing entirely from the inlined mark for one deploy: six blocks and no bar,
   which reads as II rather than an H. */
.nm-bar { stroke: #2D8E7E }
@media (prefers-color-scheme: dark) { .nm-acc { fill: #4fae9d } .nm-bar { stroke: #4fae9d } }

/* ---------- hero ---------- */

/* The mark's glow behind the hero — colour without clutter, and the one place
   --grad touches a large area. Purely decorative: no text sits on it, it cannot
   be tabbed to, and it is contained by the hero's own stacking context so it
   can never paint over content. */
.hero { padding: 72px 0 44px; position: relative; isolation: isolate; }
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(38% 52% at 30% 28%, color-mix(in srgb, var(--g1) 20%, transparent), transparent 70%),
    radial-gradient(34% 46% at 68% 22%, color-mix(in srgb, var(--g2) 18%, transparent), transparent 70%),
    radial-gradient(40% 50% at 52% 46%, color-mix(in srgb, var(--g3) 14%, transparent), transparent 72%);
}
.hero > .wrap { position: relative; }

.eyebrow {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.07em;
  font-weight: 600; margin: 0 0 14px;
  /* The gradient is painted across the ELEMENT, and a <p> is full-width: the
     text occupied 158px of an 896px box, so the glyphs sampled the first 18%
     and rendered as flat #2455C9 — indistinguishable from the #285BD9 link
     colour, which made every hero eyebrow look clickable. Shrink the box to
     the text and the gradient actually spans the letters. */
  width: fit-content;
  /* The page's identity line carries the mark. --grad-text, never --grad: this
     is 12px type, and the display gradient's teal stop is 2.35:1 on light.
     The solid --accent is set FIRST so a browser without background-clip:text
     shows a readable colour rather than transparent — i.e. nothing at all. */
  color: var(--accent);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .eyebrow {
    background: var(--grad-text);
    -webkit-background-clip: text; background-clip: text;
    color: transparent;
  }
}
.hero h1 {
  font-size: 52px; line-height: 1.15; letter-spacing: -0.025em;
  /* No max-width: the h1 is left-aligned, so capping it would waste page
     width exactly like a misalignment. Matches the reference site, whose
     h1 fills its container. */
  font-weight: 640; margin: 0 0 18px;
}
.hero-sub { font-size: 18px; color: var(--text-sec); margin: 0; line-height: 1.55; }

.btn-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
.btn {
  display: inline-block; padding: 10px 18px; border-radius: 8px;
  font-size: 14.5px; font-weight: 560; border: 0.5px solid transparent;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--accent); color: var(--bg); }
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost { background: var(--surface); color: var(--text); border-color: var(--border-med); box-shadow: var(--shadow); }
.btn-ghost:hover { background: var(--surface2); }

/* ---------- sections ---------- */

section { padding: 40px 0; }
.slabel {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--text-ter); font-weight: 600; margin: 0 0 10px;
}
h2 {
  font-size: 31px; line-height: 1.2; letter-spacing: -0.02em;
  font-weight: 620; margin: 0 0 12px;
}
h3 { font-size: 19.5px; line-height: 1.3; font-weight: 620; margin: 28px 0 8px; letter-spacing: -0.01em; }
h4 { font-size: 16px; font-weight: 620; margin: 20px 0 6px; }
p { margin: 0 0 14px; }
.lead { font-size: 17.5px; color: var(--text-sec); margin-bottom: 22px; }
.muted { color: var(--text-sec); }
.small { font-size: 14.5px; }
.tiny { font-size: 13px; color: var(--text-ter); }

hr { border: 0; border-top: 0.5px solid var(--border); margin: 40px 0; }

/* One rhythm between sections. The gap was set by whatever the section happened
   to end with — a paragraph contributed its 14px bottom margin, a grid or table
   contributed nothing — so the breaks measured 80, 90, 94 and 98px down a single
   page and the tighter sections ran into the next heading. The section's own
   padding sets the spacing now, and nothing else does. */
section > .wrap > *:last-child { margin-bottom: 0; }

/* ---------- cards ---------- */

.card {
  background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
}
/* 20px, not 12: the gutter BETWEEN two cards was smaller than the 18px padding
   INSIDE one, so a row read as a single merged slab rather than discrete items.
   The mobile deck sets its own 12px gap and is unaffected. */
.grid { display: grid; gap: 20px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* These were <h4> purely for their size. Promoted to <h3> so no page skips a
   heading level (WCAG 1.3.1) — the look is preserved here, not in the tag. */
.card h3 { font-size: 16px; margin: 0 0 6px; }
.card p:last-child { margin-bottom: 0; }
.card .num {
  font-size: 12.5px; font-weight: 700; color: var(--text-ter);
  letter-spacing: 0.06em; display: block; margin-bottom: 6px;
}

/* track cards on the front door */
.track {
  display: block; background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
  color: var(--text);
}
.track:hover { text-decoration: none; border-color: var(--accent); }
.track .tnum {
  font-size: 11.5px; font-weight: 700; color: var(--accent);
  letter-spacing: 0.08em; text-transform: uppercase;
}
.track h3 { margin: 5px 0 5px; font-size: 17.5px; }
.track p { font-size: 14.5px; color: var(--text-sec); margin: 0; }

/* ---------- big number / stat ---------- */

.hero-stat {
  background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 26px 22px; text-align: center;
}
.hero-stat .big {
  font-size: 54px; font-weight: 300; letter-spacing: -0.03em;
  line-height: 1; color: var(--accent); display: block;
}
.hero-stat .cap { font-size: 13.5px; color: var(--text-sec); margin-top: 10px; display: block; }

/* ---------- figures (inline SVG diagrams) ----------
   Narrow viewBoxes (~380) so label text is drawn large in diagram units and
   stays legible when the whole figure is 350px wide on a phone; the desktop
   cap keeps the same text from ballooning. Every colour is a theme token, so
   dark mode needs no second drawing. */
.fig { margin: 24px auto; max-width: 520px; }
.fig svg { width: 100%; height: auto; display: block; }
.fig figcaption {
  font-size: 13px; color: var(--text-ter); margin-top: 8px;
  text-align: center; line-height: 1.45;
}

/* ---------- callouts ---------- */

.note {
  border-radius: var(--radius); padding: 15px 17px; margin: 18px 0;
  border: 0.5px solid var(--border-med); font-size: 15.5px;
}
.note :last-child { margin-bottom: 0; }
.note .nlabel {
  display: block; font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; margin-bottom: 5px;
}
.note-do    { background: var(--green-bg); border-color: transparent; }
.note-do    .nlabel { color: var(--green); }
.note-dont  { background: var(--red-bg); border-color: transparent; }
.note-dont  .nlabel { color: var(--red); }
.note-warn  { background: var(--amber-bg); border-color: transparent; }
.note-warn  .nlabel { color: var(--amber); }
.note-info  { background: var(--blue-bg); border-color: transparent; }
.note-info  .nlabel { color: var(--blue); }

/* ---------- tags ---------- */

.tag {
  display: inline-block; font-size: 13px; padding: 4px 11px;
  border-radius: 5px; font-weight: 560;
}
.tag-green { background: var(--green-bg); color: var(--green); }
.tag-red   { background: var(--red-bg);   color: var(--red); }
.tag-amber { background: var(--amber-bg); color: var(--amber); }

/* ---------- accordion (progressive disclosure) ---------- */

.acc {
  background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius); box-shadow: var(--shadow);
  margin-bottom: 10px; overflow: hidden;
}
.acc > summary {
  cursor: pointer; padding: 15px 19px; font-weight: 560; font-size: 16px;
  list-style: none; display: flex; align-items: center; gap: 12px;
}
/* The whole title is ONE flex item, so the 12px gap separates the badge from
   the title and nothing else. Without this, any <em> or <strong> in a heading
   becomes its own flex item and the gap replaces the word spaces around it —
   "you can check" set with 12px between each word. See src/summary-title.js. */
.acc > summary .acc-t { min-width: 0; }
.acc > summary::-webkit-details-marker { display: none; }
.acc > summary::after {
  content: "+"; margin-left: auto; color: var(--text-ter);
  font-size: 19px; font-weight: 400; line-height: 1; flex: none;
}
.acc[open] > summary::after { content: "\2212"; }
.acc > summary:hover { background: var(--surface2); }
.acc > summary .sq {
  font-size: 12px; font-weight: 700; color: var(--text-ter);
  min-width: 1.6em; flex: none;
}
/* a child's own display rule outranks the UA closed-hiding — force it */
.acc:not([open]) > *:not(summary) { display: none; }
.acc-body { padding: 2px 18px 16px; border-top: 0.5px solid var(--border); }
.acc-body > :first-child { margin-top: 14px; }
.acc-body > :last-child { margin-bottom: 0; }

.acc-tools { display: flex; gap: 8px; margin: 0 0 12px; }
.acc-tools button {
  font: inherit; font-size: 12.5px; color: var(--text-sec);
  background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius-sm); padding: 4px 11px; cursor: pointer;
}
.acc-tools button:hover { background: var(--surface2); color: var(--text); }

/* ---------- lists ---------- */

ul, ol { margin: 0 0 14px; padding-left: 22px; }
li { margin-bottom: 7px; }
li:last-child { margin-bottom: 0; }
.clean { list-style: none; padding-left: 0; }
.clean li {
  padding-left: 22px; position: relative;
}
.clean li::before {
  content: "\2192"; position: absolute; left: 0; color: var(--text-ter);
}
.yes li::before { content: "\2713"; color: var(--green); font-weight: 700; }
.no  li::before { content: "\2715"; color: var(--red);   font-weight: 700; }

/* ---------- checklist with saved progress ---------- */

.checklist { list-style: none; padding-left: 0; margin: 0; }
.checklist li {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 10px 0; border-bottom: 0.5px solid var(--border); margin: 0;
}
.checklist li:last-child { border-bottom: 0; }
.checklist input[type=checkbox] {
  margin: 3px 0 0; width: 16px; height: 16px; flex: none;
  accent-color: var(--green); cursor: pointer;
}
.checklist label { cursor: pointer; font-size: 15.5px; }
.checklist input:checked + label { color: var(--text-ter); text-decoration: line-through; }
.progress {
  font-size: 13px; color: var(--text-sec); margin-top: 14px;
  display: flex; align-items: center; gap: 10px;
}
.bar { flex: 1; height: 5px; background: var(--surface2); border-radius: 3px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--grad); width: 0; transition: width .2s; }
.reset-btn {
  font: inherit; font-size: 12.5px; color: var(--text-ter); background: none;
  border: 0; cursor: pointer; text-decoration: underline; padding: 0;
}

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

.tscroll { overflow-x: auto; margin: 0 0 16px; }
table { border-collapse: collapse; width: 100%; font-size: 15px; min-width: 460px; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 0.5px solid var(--border); vertical-align: top; }
/* The outer cells lose their side padding so the table's TEXT lines up with the
   page's text column. With it, every heading and paragraph on the page started
   at x=192 while the table's first column started at x=204 — and the row rule
   beneath ran 12px further left than the header sitting on it, which reads as a
   table nudged out of the column it belongs to. */
th:first-child, td:first-child { padding-left: 0; }
th:last-child, td:last-child { padding-right: 0; }

/* Prose columns share the space evenly. Left to itself the browser gave the
   triage table's middle column 287px and its last 412px, so a four-line cell
   sat beside a one-line one with half the row empty. Fixed layout plus a width
   on the first column only: the first is a short label, and the prose columns
   divide what remains equally without needing to know how many there are. */
table { table-layout: fixed; }
table thead th:first-child { width: 26%; }
th {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-ter); font-weight: 700; border-bottom: 0.5px solid var(--border-med);
}
tbody tr:last-child td { border-bottom: 0; }

/* ---------- prompt / code blocks ---------- */

.prompt {
  background: var(--surface2); border: 0.5px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 16px; margin: 0 0 12px;
  font-size: 14px; line-height: 1.55; white-space: pre-wrap; word-wrap: break-word;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.prompt-wrap { position: relative; }
/* No opacity games. This was opacity:0 revealed on :hover — 11 dead buttons
   on any phone, on a site whose front page promises "Works on a phone". The
   first fix used opacity:0.45 on hover-capable pointers, which was just as
   unreadable: white on #f1f0ec. A control is either there or it isn't, so it
   is always fully opaque and simply styled quietly. */
.copy-btn {
  position: absolute; top: 8px; right: 8px; font: inherit; font-size: 12px;
  background: var(--surface); border: 0.5px solid var(--border-med);
  color: var(--text-sec); border-radius: var(--radius-sm);
  padding: 5px 11px; cursor: pointer; box-shadow: var(--shadow);
  transition: background .15s, color .15s;
  /* .prompt used to be position:relative and follows this button in the DOM,
     so it painted straight over it — a fully opaque, correctly sized, utterly
     unclickable button. Explicit stacking so DOM order cannot bury it again. */
  z-index: 2;
}
.copy-btn:hover, .copy-btn:focus { background: var(--surface2); color: var(--text); }
/* keep the button clear of the first line of a long prompt */
.prompt-wrap .prompt { padding-right: 78px; }

/* ---------- steps ---------- */

.steps { counter-reset: s; list-style: none; padding-left: 0; }
.steps > li {
  counter-increment: s; position: relative; padding-left: 40px; margin-bottom: 18px;
}
.steps > li::before {
  content: counter(s); position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--green-bg); color: var(--green);
  font-size: 13px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.steps h3 { font-size: 16px; margin: 2px 0 5px; line-height: 1.3; }

/* ---------- next / prev ---------- */

.pager { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 8px; }
.pager a {
  flex: 1 1 240px; background: var(--surface); border: 0.5px solid var(--border-med);
  border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow); color: var(--text);
}
.pager a:hover { text-decoration: none; border-color: var(--accent); }
.pager .dir { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-ter); font-weight: 700; }
.pager .ttl { font-weight: 600; display: block; margin-top: 3px; }


/* The four-week plan: each week is a heading plus its checklist and progress bar.
   Wrapped so the group can become one swipe deck on a phone, where stacked they
   ran 1998px — the single biggest remaining block on any hub page.
   Desktop adds NOTHING here on purpose: an earlier version set a 22px gap between
   weeks and zeroed the heading's own top margin, which shortened the desktop page
   by 24px — a change I had said would not happen. The wrapper is layout-neutral
   until the mobile block turns it sideways. */

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

.foot {
  border-top: 0.5px solid var(--border); margin-top: 60px;
  padding: 30px 0 52px; font-size: 14px; color: var(--text-ter);
}
.foot a { color: var(--text-sec); }
.foot-links { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 14px; }
.foot .legal { margin: 10px 0 0; padding-top: 10px; border-top: 0.5px solid var(--border); }
/* Grow the licence link's hit area without moving the text. At 13px it was a
   16px-tall tap target, which is mean on a phone and meaner on a site aimed
   at older readers. Negative margin cancels the layout effect of the padding. */
/* Every inline footer link, not just the licence one. The disclosure link
   added with the usage counter landed at 16px — the same 20px-target failure
   the licence link had, caught by the same guard. Scoped to .foot so prose
   links elsewhere keep their natural line box. */
.foot p a { padding: 5px 3px; margin: -5px -3px; }


/* Footer signature, shared with hlur.ai and /baseline: Lur peeking over a 2px rule.
   Sits above hub's own privacy and licence text, which is unchanged. */
.peek-wrap { display: flex; justify-content: center; }
.peek-wrap svg { width: 150px; height: 81px; display: block; margin-bottom: -2px; }
.foot { border-top: 0; }                 /* the peek's rule replaces the old hairline */
/* Lur's body teal, the same rule hlur.ai and /baseline use. It is NOT a divider —
   it is Lur's body line continuing across the page, which is why it matches the
   peeking SVG's own fill (#9AD3C6) exactly and why it stays the same in dark mode.
   It measures 1.58:1 on the light background and that is the point: a contrasty
   rule would read as a border with a mascot stuck on it, rather than as the top of
   Lur. The mark's gradient sat here briefly; teal is the house rule. */
.foot > .wrap {
  border-top: 2px solid var(--lur-body);
  padding-top: 26px;
}
.foot-site { margin-top: 18px; padding-top: 16px; border-top: 0.5px solid var(--border); }
.foot-site-links { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 12px; }
.foot-copy { font-size: 13.5px; color: var(--text-ter); margin: 0; }

/* ---------- mobile (MUST stay last: equal specificity, source order wins) ---------- */

@media (max-width: 760px) {
  /* The wordmark costs ~140px of a 390px bar, squeezing the scrolling nav links.
     The first fix hid the section label on mobile — and this repo's rendered harness
     rejected it: mobile must show the same text as desktop, and display:none on a
     label IS hiding content from phone users. That rule is stricter than the main
     site's .nav-sm-hide pattern, and it is the right rule; a phone reader should not
     silently get less. So the label stays and the chrome gets compact instead. */
  .wordmark { font-size: 18px; gap: 5px; }
  .nav-mark { width: 19px; height: 19px; }
  .nav-sect { font-size: 12.5px; padding-left: 9px; margin-left: 0; }
  .nav-inner { gap: 10px; }

  :root { --pad: 18px; }
  .hero { padding: 40px 0 28px; }
  .hero h1 { font-size: 34px; }
  .hero-sub { font-size: 16.5px; }
  section { padding: 8px 0 30px; }
  h2 { font-size: 25px; }
  /* ---- swipe decks (ported from /baseline, measured not guessed) --------------
     Card groups stacked one-per-row on a phone, so every extra card cost a full
     card of scrolling: on the home page the grids alone were 34% of the page and
     the numbered steps another 1275px. /baseline solves the same problem with
     scroll-snap decks, and its numbers are the spec followed here — cards at 82vw
     so the NEXT one peeks and the row reads as swipeable, 12px gap, snap-align
     start, and full-bleed by cancelling the wrap padding with a negative inline
     margin so a card can sit against the screen edge.

     Nothing is hidden. Every card stays in the DOM and in the accessibility tree,
     which is what keeps this inside the rule that mobile must show the same text
     as desktop — the content moves sideways, it does not disappear. Without JS the
     row is still a plain scrollable strip, so the no-script path degrades to
     something usable rather than broken. */
  /* Snap to the CONTENT edge, not the screen edge. Without scroll-padding the
     browser aligns the first card's start with the scrollport start and, at
     rest, quietly scrolls the deck by exactly the inline padding — measured at
     18px on EVERY deck on the site. The visible effect is that card one sits
     flush against the bezel while the section label, heading and lead above it
     sit at 18px: one element out of line, on every deck, on every page. */
  .grid-2, .grid-3, .steps, .wk-deck, .acc-deck { scroll-padding-inline-start: var(--pad); }

  .grid-2, .grid-3, .steps {
    display: flex; gap: 12px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    margin-inline: calc(var(--pad) * -1); padding-inline: var(--pad);
    padding-bottom: 2px;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .grid-2::-webkit-scrollbar, .grid-3::-webkit-scrollbar,
  .steps::-webkit-scrollbar { display: none; }
  .grid-2 > *, .grid-3 > *, .steps > li {
    /* 82vw, not 82%. A percentage resolves against the deck's PADDED content box
       (390 - 2*18 = 354), giving 290px cards — visibly narrower than /baseline's
       320px and leaving a wider, emptier peek. vw measures the screen, which is
       what the 82 figure was derived from in the first place. */
    flex: 0 0 82vw; scroll-snap-align: start; min-width: 0;
  }
  /* Vertical rhythm between stacked steps becomes dead space once they sit in a
     row, and equal heights stop one tall card leaving the others stunted. */
  /* Steps size to their own content. `height: auto` does NOT do this — it is the
     initial value, and it is precisely the state in which align-items:stretch
     applies, so the first attempt at this was a no-op that measured identical
     before and after (724,724,724,724 either way). The container has to opt out
     of stretching, which is what .acc-deck already does. Measured on
     further#work: 724,724,724,724 -> 724,120,166,145, i.e. step 2 was carrying
     618px of empty card.
     Grid cards keep equal heights deliberately — they have a surface and a
     border, so the extra reads as padding. A step has neither, so it reads as a
     hole in the page. */
  .steps { align-items: flex-start; }
  .steps > li { margin-bottom: 0; }
  .grid-2 > *, .grid-3 > * { height: auto; }

  /* The four-week plan becomes a deck too: four week-cards, one per swipe, instead
     of 1998px of stacking. Same 82vw card as every other deck so the rhythm holds. */
  .wk-deck {
    display: flex; gap: 12px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    margin-inline: calc(var(--pad) * -1); padding-inline: var(--pad);
    padding-bottom: 2px;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .wk-deck::-webkit-scrollbar { display: none; }
  .wk-deck > .wk { flex: 0 0 82vw; scroll-snap-align: start; min-width: 0; }
  .wk-deck > .wk + .wk { margin-top: 0; }
  .wk-deck > .wk > h3:first-child { margin-top: 0; }
  /* "Week 1 · Fluency" fits one line, the other three wrap to two, so card one
     started 25px above its neighbours and the row read as misaligned before a
     reader had touched it. Reserve two lines for every title. */
  .wk-deck > .wk > h3 { min-height: calc(2 * 1.3em); }

  /* The five accordion lists become decks as well. Ten collapsed rows read as a
     wall of near-identical grey bars you scroll past without opening; one card at
     a time gives each item the whole screen and makes the count obvious from the
     dots. An opened card grows on its own — align-items:flex-start keeps its
     neighbours at their natural height instead of stretching the whole row. */
  .acc-deck {
    display: flex; gap: 12px; align-items: flex-start;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    margin-inline: calc(var(--pad) * -1); padding-inline: var(--pad);
    padding-bottom: 2px;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .acc-deck::-webkit-scrollbar { display: none; }
  /* "Expand all" means "I want to read them all", which a one-card-wide row
     cannot do: every card opens to a different length, the row takes the height
     of the tallest, and swiping to a short one strands up to a full screen of
     blank deck beside it. So expanding all leaves deck mode and becomes the
     plain vertical stack the desktop shows. "Collapse all" restores the deck. */
  .acc-deck.stacked {
    display: block; overflow-x: visible;
    margin-inline: 0; padding-inline: 0;
  }
  .acc-deck.stacked > .acc { width: auto; }
  .acc-deck.stacked > .acc + .acc { margin-top: 10px; }
  .acc-deck > .acc {
    flex: 0 0 82vw; scroll-snap-align: start; min-width: 0;
    margin: 0;   /* the stacked gap is the flex gap now */
  }
  /* A summary is one line on a wide screen and three on a narrow card, so the
     rows would otherwise sit at different heights and the deck would look ragged
     before anything is even opened. */
  .acc-deck > .acc > summary { align-items: flex-start; }
  /* Closed, the deck is a row of title cards and should read as one: a 58px card
     next to a 110px one left 52px of blank deck under the short ones, which looks
     like a rendering fault rather than a design. Match them — but only while the
     whole deck is shut, because once a card is open the same rule would stretch
     every neighbour to the tallest open body. Browsers without :has() simply keep
     the natural heights, which is the behaviour every other deck already has. */
  .acc-deck:not(:has(> .acc[open])) > .acc { align-self: stretch; }
  /* ...and the title centres in the space it was given. Left at the top, a
     one-line title in a card sized for a three-line one reads as a card whose
     body failed to load rather than as a title card with room around it. */
  .acc-deck:not(:has(> .acc[open])) > .acc > summary { height: 100%; align-items: center; }

  /* Progress dots. Rendered by app.js, so they only appear where a deck really
     scrolls — a dot row under a non-scrolling group would be a lie about the UI. */
  .deck-dots { display: flex; gap: 6px; justify-content: center; margin-top: 10px; }
  .deck-dots span {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--border-med); transition: background .18s, width .18s;
  }
  .deck-dots span.on { width: 16px; border-radius: 3px; background: var(--grad); }
  /* ---- tables fit the screen instead of scrolling sideways -----------------
     Every table carried `min-width: 460px`, so on a 354px content column each
     one hid exactly 106px behind a horizontal scroll — six sections' worth of
     comparison data reachable only by swiping a table nobody knows is swipeable.

     On a phone each ROW becomes a card and each CELL a line. The header row
     stays visible and readable as a legend above them rather than being hidden:
     display:none on a <thead> would strip that text from phone users, which is
     the same rule that stopped the section labels being hidden earlier. */
  .tscroll { overflow-x: visible; }
  .tscroll table { min-width: 0; width: 100%; }
  .tscroll tbody tr {
    display: block; padding: 12px 0; border-bottom: 0.5px solid var(--border);
  }
  .tscroll tbody tr:last-child { border-bottom: 0; }
  .tscroll tbody td { display: block; padding: 0; border: 0; }
  .tscroll tbody td + td { margin-top: 8px; }

  /* EVERY CELL CARRIES ITS COLUMN NAME.
     Restacking alone loses what the columns were doing. Three of these tables
     read as a single sentence or a contrast pair ACROSS the row — "Because it's…
     / You would naturally…", "Instead of / Ask for" — so a stacked cell with no
     label is a heading followed by an unrelated paragraph. A legend once at the
     top does not survive to row four. The text comes from the table's own <th>
     via build.js, so it cannot drift from the column it names. */
  .tscroll tbody td::before {
    content: attr(data-label);
    display: block;
    font-size: 11.5px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.07em; color: var(--text-ter);
    margin-bottom: 2px;
  }
  /* The header row is now on every card, so the single row above them is a
     duplicate rather than the only copy — the one condition under which standing
     it down does not take information away from a phone reader. */
  .tscroll thead { display: none; }

  /* the row's subject reads as the card's title */
  .tscroll tbody td:first-child { font-size: 16px; font-weight: 650; }
  .tscroll tbody td:not(:first-child) { color: var(--text-sec); font-size: 14.5px; }

  html { scroll-padding-top: 102px; }   /* 87px nav + breathing room */


  /* ---- the nav must not need a sideways swipe ------------------------------
     Six page links plus the wordmark and the section label wanted 370px of a
     390px bar, so `overflow-x: auto` quietly hid 157px of it — at 320px, 227px.
     "Tools" and "Go Further" were off-screen with no visible affordance, which
     is the worst kind of hidden navigation: not collapsed behind a control the
     reader can see, just gone.

     Labels cannot shrink on mobile — this repo requires phone text to be
     character-identical to desktop — so the LAYOUT gives way instead. The site
     chrome keeps row one; the page links take their own full-width row and
     WRAP rather than scroll, so a narrow screen gets two short rows and
     nothing is ever out of reach. No hamburger: hiding six destinations behind
     a tap is worse for the audience this site is aimed at. */
  .nav-inner { flex-wrap: wrap; gap: 8px 10px; padding-bottom: 9px; }
  .nav-links {
    flex: 1 0 100%;
    margin-left: 0;
    flex-wrap: wrap;
    overflow-x: visible;      /* replaces the base rule's auto — wrap, don't scroll */
    gap: 4px 3px;
  }
  .nav-links a { font-size: 12.5px; padding: 5px 6px; }
  .hero-stat .big { font-size: 44px; }
  .btn { width: 100%; text-align: center; }
}
