/* Palm Forge — styled to the Palm Ora design system (PalmDigitalz).
   The ember is the only hue. Ink and paper carry everything else. */

@import url("https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@300;400;500;600;700;800&family=IBM+Plex+Mono:wght@400;500;600&family=Archivo+Black&display=swap");

:root {
  /* color tokens — tokens/colors.css */
  --ember: #FB4B2A;
  --ember-deep: #DE3A1C;
  --ember-tint: #FEE4DC;
  --ink: #14110F;
  --ink-70: #4E4943;
  --ink-45: #8C867E;
  --ink-25: #C9C4BC;
  --paper: #F7F6F3;
  --panel: #FFFFFF;
  --line: rgba(20, 17, 15, 0.10);
  --text-body: var(--ink);
  --text-muted: #6E6961;
  --text-quiet: var(--ink-45);
  --focus-ring: var(--ember);

  /* type tokens — tokens/typography.css */
  --font-sans: "Hanken Grotesk", -apple-system, "Segoe UI", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
  --font-display: "Archivo Black", "Hanken Grotesk", sans-serif;

  /* type scale (design audit P2-1). Seven steps; a size off this list is a
     defect. Mono metadata may sit at 12px, sans prose never below 13px. */
  --fs-hero: clamp(40px, 7vw, 72px);
  --fs-page: clamp(26px, 5vw, 32px);
  --fs-section: 21px;
  --fs-body: 17px;
  --fs-support: 14.5px;
  --fs-code: 13.5px;       /* mono */
  --fs-meta: 12px;         /* mono */
  --fs-meta-sans: 13px;    /* the floor for sans prose */

  /* spacing & shape — tokens/spacing.css */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px; --space-9: 96px;
  --radius-sm: 10px; --radius-md: 18px; --radius-lg: 28px; --radius-pill: 999px;
  --hairline: 1.5px;
  --shadow-card: 0 12px 34px rgba(20, 17, 15, 0.08);
  --shadow-float: 0 30px 70px rgba(20, 17, 15, 0.14);

  /* outcome tints (ink-anchored, never a second hue) */
  --good-bg: #EDF3EA; --good-fg: #2E5A2B;
  --warn-bg: #F6EEDF; --warn-fg: #6E5312;

  /* semantic states (design audit P2-3). Error keeps the one hue but names
     it, so a failure and the key action can part later without a sweep.
     Info and disabled are ink greys. */
  --error-fg: var(--ember-ink); --error-bg: var(--ember-tint);
  --info-fg: var(--ink-70);     --info-bg: rgba(20, 17, 15, 0.04);
  --disabled-fg: var(--text-muted);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--paper);
  color: var(--text-body);
  font: 400 17px/1.55 var(--font-sans);
  min-height: 100vh;
}
a { color: inherit; }
::selection { background: var(--ember-tint); }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ================================================================= layout
   A sidebar rather than a top bar. The old top navigation carried sixteen
   links plus the brand, the purse, the level, the user and a sign-out on one
   line, and it wrapped to three rows on a laptop -- which is why it read as
   clutter rather than as navigation.

   Three states, and the breakpoint is the only thing that decides which:

     >= 1024px   sidebar is a column, always present, collapsible to a rail
     <  1024px   sidebar is a drawer over the content, opened by the burger
     railed      desktop only: 72px of icons, labels on hover as a tooltip

   The main column is fluid and generous on purpose. The old shell capped at
   1120px including the navigation; the content column now gets up to 1360px
   of its own and uses the whole viewport below that.
   ====================================================================== */

.layout {
  display: grid;
  grid-template-columns: var(--side-w, 264px) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
}
.layout.signedout { grid-template-columns: minmax(0, 1fr); }

.main { min-width: 0; }
.shell {
  max-width: 1360px; margin: 0 auto;
  padding: var(--space-6) var(--space-6) var(--space-8);
}
.layout.signedout .shell { max-width: 1120px; }

/* ------------------------------------------------------------- sidebar */
.sidebar {
  position: sticky; top: 0; align-self: start;
  height: 100vh; height: 100dvh;
  display: flex; flex-direction: column;
  background: var(--panel);
  border-right: var(--hairline) solid var(--line);
  overflow: hidden;
}
.side-head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-3);
}
.sidebar .brand {
  display: flex; align-items: center; gap: 10px;
  text-decoration: none; min-width: 0; flex: 1;
}
.sidebar .brand img { width: 30px; height: 30px; object-fit: cover; flex: none; }
.brand-text { min-width: 0; }
.brand-text b {
  display: block; font-weight: 800; font-size: 16.5px;
  letter-spacing: -0.01em; white-space: nowrap;
}
.brand-text span {
  display: block; font: 400 11px var(--font-mono); color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.ico {
  width: 18px; height: 18px; flex: none; fill: none;
  stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

.railbtn, .iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex: none; padding: 0; cursor: pointer;
  background: transparent; color: var(--text-muted);
  border: var(--hairline) solid transparent; border-radius: var(--radius-sm);
}
.railbtn:hover, .iconbtn:hover { color: var(--ink); background: rgba(20,17,15,0.05); }

/* --------------------------------------------------------- search entry */
.searchtrigger {
  display: flex; align-items: center; gap: 10px;
  margin: 0 var(--space-3) var(--space-3);
  padding: 9px 11px; cursor: pointer; width: calc(100% - var(--space-3) * 2);
  background: var(--paper); color: var(--text-muted);
  border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  font: 500 13.5px var(--font-sans); text-align: left;
}
.searchtrigger:hover { border-color: var(--ink-25); color: var(--ink); }
.searchtrigger .lbl { flex: 1; white-space: nowrap; }
/* The way to buy, for a free account: under the search, above the menu.
   A quiet row, not an ember button: ember belongs to each page's own main
   action, and two of them on one screen compete (UX audit #6, #8). */
.side-plan {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0 var(--space-3) var(--space-3); padding: 9px 11px;
  border-radius: var(--radius-sm); text-decoration: none;
  background: var(--paper); border: var(--hairline) solid var(--line);
  font: 500 13.5px var(--font-sans); color: var(--text-muted);
}
.side-plan:hover { border-color: var(--ink-25); color: var(--ink); }
.side-plan-link { color: var(--ink); font-weight: 600; }
.side-plan-link::after { content: " \2192"; }
.railed .side-plan { display: none; }
.kbd {
  font: 500 10.5px var(--font-mono); color: var(--text-quiet);
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: 5px; padding: 2px 5px; white-space: nowrap;
}

/* ------------------------------------------------------------- the nav */
.sidenav {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  padding: 0 var(--space-3) var(--space-3);
  display: flex; flex-direction: column; gap: 1px;
}
/* Group headings: 11px in --text-muted. 10px in --text-quiet was below
   AA contrast and hard to scan (UX audit #11). More air above each group
   so the eye finds the sections without rules between them. */
.navgroup {
  margin: var(--space-4) 0 4px; padding: 0 11px;
  font: 600 11px var(--font-mono); letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--text-muted);
}
.navgroup:first-child { margin-top: var(--space-1); }
.navitem {
  display: flex; align-items: center; gap: 11px;
  padding: 7px 11px; border-radius: var(--radius-sm);
  text-decoration: none; color: var(--text-muted);
  font: 600 14px var(--font-sans); white-space: nowrap;
}
.navitem:hover { color: var(--ink); background: rgba(20,17,15,0.045); }
.navitem.active { color: var(--ink); background: var(--ember-tint); }
.navitem:focus-visible, .navtoggle:focus-visible, .side-plan:focus-visible {
  outline: 2px solid var(--focus-ring); outline-offset: 1px;
}

/* Administrator sections: the heading is a button that folds the group.
   A rule above sets the operating areas apart from the learner's own. */
.navsection { display: flex; flex-direction: column; gap: 1px; }
.navsection:first-of-type { margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: var(--hairline) solid var(--line); }
.navtoggle {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left;
  border-radius: var(--radius-sm); padding-block: 4px;
}
.navtoggle:hover { color: var(--ink); }
.navtoggle > span:first-child { flex: 1; }
.navchev { width: 14px; height: 14px; transition: transform .18s ease; }
.navsection.closed .navchev { transform: rotate(-90deg); }
.navsection.closed .navsection-body { display: none; }
.navsection-body { display: flex; flex-direction: column; gap: 1px; }
.navbadge-roll { display: none; }
.navsection.closed .navbadge-roll { display: inline-flex; }
/* The rail shows every icon whatever the fold: there is no heading to open. */
.railed .navsection.closed .navsection-body { display: flex; }
.railed .navtoggle { display: none; }
@media (prefers-reduced-motion: reduce) { .navchev { transition: none; } }
.navitem.active .ico { color: var(--ember); }
.navitem .lbl { overflow: hidden; text-overflow: ellipsis; }

/* ------------------------------------------------------------ the foot */
.side-foot {
  padding: var(--space-3); border-top: var(--hairline) solid var(--line);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.side-foot .stats { display: flex; gap: var(--space-2); }
.statchip {
  flex: 1; min-width: 0; text-decoration: none;
  padding: 7px 10px; border-radius: var(--radius-sm);
  background: var(--paper); border: var(--hairline) solid var(--line);
}
.statchip b { display: block; font: 700 13px var(--font-sans); }
.statchip span {
  display: block; font: 400 10.5px var(--font-mono);
  color: var(--text-muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.userchip {
  display: flex; align-items: center; gap: 10px; text-decoration: none;
  padding: 7px 8px; border-radius: var(--radius-sm); min-width: 0;
}
.userchip:hover { background: rgba(20,17,15,0.045); }
.userchip .meta { min-width: 0; }
.userchip .meta .n {
  display: block; font: 600 13.5px var(--font-sans);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.userchip .meta .r { display: block; font: 400 11px var(--font-mono); color: var(--text-muted); }
.signout {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 11px; border-radius: var(--radius-sm);
  text-decoration: none; color: var(--text-quiet);
  font: 500 13px var(--font-sans);
}
.signout:hover { color: var(--ink); background: rgba(20,17,15,0.045); }
.avatar {
  width: 34px; height: 34px; border-radius: 50%; background: var(--ink); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 14px var(--font-sans); overflow: hidden; flex: none;
}

/* ------------------------------------------------------------ the rail
   Labels are removed from the flow rather than hidden, so the icons centre
   properly, and re-appear as a tooltip on hover. nowrap on the tooltip is
   what stops "Command Center" wrapping to two lines inside 72px. */
.railed .layout { --side-w: 72px; }
.railed .brand-text,
.railed .navitem .lbl,
.railed .signout .lbl,
.railed .searchtrigger .lbl,
.railed .searchtrigger .kbd,
.railed .navgroup,
.railed .side-foot .stats,
.railed .userchip .meta { display: none; }

.railed .side-head { flex-direction: column; gap: var(--space-2); padding: var(--space-3) 0; }
.railed .sidebar .brand { justify-content: center; }
.railed .searchtrigger {
  width: 38px; margin-inline: auto; justify-content: center; padding: 9px;
}
.railed .navitem, .railed .signout { justify-content: center; padding: 10px; }
.railed .userchip { justify-content: center; padding: 6px; }
.railed .sidenav { padding-inline: var(--space-2); }

.railed .navitem { position: relative; }
.railed .navitem::after {
  content: attr(data-label); position: absolute; left: calc(100% + 10px);
  top: 50%; transform: translateY(-50%) scale(.96);
  background: var(--ink); color: #fff; white-space: nowrap;
  padding: 6px 10px; border-radius: 8px; font: 600 12.5px var(--font-sans);
  opacity: 0; pointer-events: none; transition: opacity .12s ease, transform .12s ease;
  z-index: 40;
}
.railed .navitem:hover::after, .railed .navitem:focus-visible::after {
  opacity: 1; transform: translateY(-50%) scale(1);
}

/* ------------------------------------------------ signed-out page header
   The landing and login pages carry their own bar, because a visitor has no
   navigation to show -- only a brand and a way in. It kept the old class name
   so those templates did not have to change. */
.topbar {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) 0; margin-bottom: var(--space-5);
  border-bottom: var(--hairline) solid var(--line);
}
.topbar .brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.topbar .brand img { width: 30px; height: 30px; object-fit: cover; }
.topbar .brand b { font-weight: 800; font-size: 17px; letter-spacing: -0.01em; }
.topbar .brand span { font: 400 11.5px var(--font-mono); color: var(--text-muted); }
.topbar .spacer { flex: 1; }

/* --------------------------------------------------- mobile bar + scrim */
.mobilebar { display: none; }
.scrim {
  position: fixed; inset: 0; z-index: 44;
  background: rgba(20,17,15,0.42);
}
.burger {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 4px;
  width: 42px; height: 42px; padding: 10px; cursor: pointer;
  background: transparent; border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm);
}
.burger span {
  display: block; height: 2px; background: var(--ink); border-radius: 2px;
  transition: transform .2s ease, opacity .2s ease;
}
.drawer-open .burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.drawer-open .burger span:nth-child(2) { opacity: 0; }
.drawer-open .burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }.avatar img { width: 100%; height: 100%; object-fit: cover; }

.page-title { font: 600 40px/1.15 var(--font-sans); letter-spacing: -0.015em; margin: 0 0 var(--space-2); }
.page-sub { color: var(--text-muted); margin: 0 0 var(--space-6); font-size: 15.5px; }
.section-title { font: 600 28px/1.25 var(--font-sans); letter-spacing: -0.01em; margin: var(--space-7) 0 var(--space-4); }

/* ------------------------------------------------ cards */
.card {
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-md); padding: var(--space-5);
  box-shadow: var(--shadow-card);
}
.card.lift { transition: transform .25s ease, box-shadow .25s ease; }
.card.lift:hover { transform: translateY(-4px); box-shadow: var(--shadow-float); }
.card h3 { margin: 0 0 var(--space-2); font: 600 19px/1.3 var(--font-sans); }
.card .sub { color: var(--text-muted); font-size: 14px; }
.grid { display: grid; gap: var(--space-4); }
/* `1fr` means `minmax(auto, 1fr)`, and that `auto` floors the track at the
   item's min-content — so one wide table inside one card grows the whole
   column past its container and the page scrolls sideways. Found on
   /talent/me at 375px: the grid was 351 wide and its single track computed
   to 371. min-width: 0 lets a track be as narrow as the space it was given,
   and the scroll rail inside the card takes the overflow instead. */
.grid > * { min-width: 0; }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 960px) { .grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; } }

/* ------------------------------------------------ buttons (Palm Ora pills) */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 24px; font: 600 15px var(--font-sans);
  border-radius: var(--radius-pill); cursor: pointer; text-decoration: none;
  border: var(--hairline) solid var(--ink); background: var(--ink); color: #fff;
  transition: transform .15s ease, background .15s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: none; }
.btn.ghost { background: var(--panel); color: var(--ink); border-color: var(--line); }
.btn.ember { background: var(--ember); border-color: var(--ember); }
.btn.ember:hover { background: var(--ember-deep); border-color: var(--ember-deep); }
.btn.sm { padding: 8px 18px; font-size: 14px; }
.btn.lg { padding: 16px 32px; font-size: 17px; }
.btn[disabled], .btn.disabled { opacity: .45; pointer-events: none; }

/* ------------------------------------------------ machine voice */
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 14px;
  font: 500 12.5px var(--font-mono); border-radius: var(--radius-pill);
  border: var(--hairline) solid var(--line); color: var(--text-muted);
  background: var(--panel);
  /* Not nowrap. A chip is the handiest component in this stylesheet, so it
     ends up holding the occasional full sentence, and nowrap turned one of
     those into a 616px unbreakable box inside a 567px viewport — which
     scrolls the whole page sideways, not just the chip. Found that way on the
     credentials page: "Share of Application Support (L2/L3) skills with
     practice behind them".

     `normal` costs a short chip nothing, because a flex item will not shrink
     below its longest word: two-word pills stay on one line exactly as
     before. Only a chip that genuinely cannot fit wraps, and a wrapped pill
     is a far better failure than a page that slides. */
  white-space: normal;
  max-width: 100%;
}
.chip.ink { background: var(--ink); color: #fff; border-color: var(--ink); }
.chip.ember { border-color: var(--ember); color: var(--ember); }
.chip.good { background: var(--good-bg); color: var(--good-fg); border-color: transparent; }
.chip.warn { background: var(--warn-bg); color: var(--warn-fg); border-color: transparent; }
.mono { font-family: var(--font-mono); }
.quiet { color: var(--text-quiet); }
.muted { color: var(--text-muted); }

/* ------------------------------------------------ stats */
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat .v { font: 600 34px/1.1 var(--font-sans); letter-spacing: -0.02em; }
.stat .v em { font-style: normal; color: var(--ember); }
.stat .k { font: 400 11.5px var(--font-mono); color: var(--text-muted); }

/* progress bar — ink track, ember only when it is THE number */
.bar { height: 6px; background: rgba(20,17,15,0.08); border-radius: 999px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--ink); border-radius: 999px; }
.bar.ember > i { background: var(--ember); }

/* skill strength rows */
.skillrow { display: grid; grid-template-columns: 1fr 90px 44px; gap: 10px; align-items: center; padding: 7px 0; }
.skillrow .lbl { font-size: 14px; }
.skillrow .num { font: 500 12.5px var(--font-mono); color: var(--text-muted); text-align: right; }

/* ------------------------------------------------ tables */
table.data { width: 100%; border-collapse: collapse; font-size: 14.5px; }
table.data th {
  text-align: left; font: 500 11px var(--font-mono); text-transform: lowercase;
  color: var(--text-muted); padding: 8px 10px; border-bottom: var(--hairline) solid var(--line);
}
table.data td { padding: 11px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; }
table.data tr:last-child td { border-bottom: none; }
table.data tr.me { background: var(--ember-tint); }

/* ------------------------------------------------ forms */
label.f { display: block; font: 600 13px var(--font-sans); color: var(--ink-70); margin: var(--space-4) 0 6px; }
input[type=text], input[type=email], select, textarea {
  width: 100%; font: 400 15px var(--font-sans); color: var(--ink);
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm); padding: 11px 14px;
}
textarea { resize: vertical; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--ink-45); }
.error {
  background: var(--ember-tint); color: var(--ember-deep);
  border-radius: var(--radius-sm); padding: 10px 14px; font-size: 14px; margin-bottom: var(--space-4);
}

/* ------------------------------------------------ heatmap */
.heatmap { display: flex; gap: 3px; }
.heatmap .col { display: flex; flex-direction: column; gap: 3px; }
.heatmap .cell { width: 13px; height: 13px; border-radius: 3px; background: rgba(20,17,15,0.06); }
/* three levels 1.33:1 apart are indistinguishable at any size, let alone at
   ten pixels. Wider steps, ending on the deep ember rather than the bright
   one, so the busiest days read as the darkest. */
.heatmap .cell.l1 { background: #FBD9CF; }
.heatmap .cell.l2 { background: #F58A6C; }
.heatmap .cell.l3 { background: var(--ember-deep); }
.heatmap .cell.future { background: transparent; border: 1px dashed var(--line); }

/* ------------------------------------------------ play surfaces */
.playhead { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-5); }
.playhead h1 { font: 600 28px/1.2 var(--font-sans); margin: 0; letter-spacing: -0.01em; flex: 1; min-width: 240px; }
.timer { font: 600 20px var(--font-mono); padding: 6px 16px; border-radius: var(--radius-pill); border: var(--hairline) solid var(--line); background: var(--panel); }
.timer.low { border-color: var(--ember); color: var(--ember); }

pre.artifact {
  background: var(--ink); color: #F1F0ED; border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5); overflow-x: auto;
  font: 400 13.5px/1.55 var(--font-mono); margin: 0;
}
pre.artifact .hl { color: #FFB4A0; }

textarea.codepad {
  font: 400 13.5px/1.55 var(--font-mono); background: var(--ink); color: #F1F0ED;
  border-radius: var(--radius-md); border: var(--hairline) solid var(--ink);
  padding: var(--space-4) var(--space-5); min-height: 320px; width: 100%;
  tab-size: 8; white-space: pre; overflow-x: auto;
}
textarea.codepad:focus { border-color: var(--ember); }

.option {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm); padding: 13px 16px; margin-bottom: 8px;
  font: 400 15px/1.45 var(--font-sans); color: var(--ink);
  transition: transform .12s ease, border-color .12s ease;
}
.option:hover { border-color: var(--ink-45); transform: translateY(-1px); }
.option.picked { border-color: var(--ink); background: rgba(20,17,15,0.04); }
.option .cost { font: 500 12px var(--font-mono); color: var(--text-muted); float: right; }
.option.conclude { border-style: dashed; }
.option.conclude:hover { border-color: var(--ember); }

/* "I do not know" — set apart from the answers, never styled as a failure */
.option.dontknow {
  margin-top: 14px; border-style: dashed; color: var(--text-muted);
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.option.dontknow:hover { border-color: var(--ember); color: var(--ink); }
.option.dontknow .cost {
  float: none; font: 500 11.5px var(--font-mono);
  color: var(--text-quiet); white-space: nowrap;
}

.finding {
  border-left: 3px solid var(--ink-25); padding: 8px 14px; margin: 10px 0;
  font-size: 14.5px; color: var(--ink-70); background: rgba(20,17,15,0.025);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* KPI meters (war room) */
.kpis { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.kpi { flex: 1; min-width: 140px; }
.kpi .k { font: 400 11.5px var(--font-mono); color: var(--text-muted); }
.kpi .v { font: 600 22px var(--font-sans); }

/* debrief */
.scorewrap { text-align: center; padding: var(--space-5) 0; }
.scorewrap .big {
  font-family: var(--font-display); font-size: 72px; line-height: 1.02;
  letter-spacing: -0.01em;
}
.scorewrap .big.pass { color: var(--ink); }
.scorewrap .big.fail { color: var(--ember); }
.reveal { animation: rise .7s ease both; }
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

.check-row { display: flex; gap: 10px; align-items: baseline; padding: 6px 0; font-size: 14.5px; }
.check-row .m { font: 600 12px var(--font-mono); flex: none; width: 46px; }
.check-row .m.pass { color: var(--good-fg); }
.check-row .m.fail { color: var(--ember-deep); }

/* orb pulse — the sanctioned motion signature, used as loading/live marker */
.orb { width: 16px; height: 16px; border-radius: 50%; object-fit: cover; }
.orb.pulse { animation: pulse 1.5s ease-in-out infinite; }
@keyframes pulse { 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.15); opacity: .8; } }

/* landing hero */
.hero { padding: var(--space-9) 0 var(--space-7); max-width: 720px; }
.hero h1 {
  font-family: var(--font-display); font-weight: 400;
  /* The floor was 44px, which overflowed a phone: "OPERATOR" was clipped
     and the page scrolled sideways (UX audit #1). */
  font-size: clamp(30px, 8.5vw, 84px); line-height: 1.02; letter-spacing: -0.01em;
  margin: 0 0 var(--space-5); text-transform: uppercase;
  overflow-wrap: break-word;
}
.hero h1 em { font-style: normal; color: var(--ember); }
.hero p { font-size: 19px; color: var(--ink-70); margin: 0 0 var(--space-6); max-width: 560px; }

/* source lockup — mandatory on chart/data cards */
.lockup {
  display: flex; align-items: center; gap: 8px;
  border-top: var(--hairline) solid var(--line); padding-top: 10px; margin-top: var(--space-4);
}
.lockup img { width: 16px; height: 16px; object-fit: cover; }
.lockup span { font: 400 10.5px var(--font-mono); color: var(--text-muted); }

/* activity bars (admin) */
.actbars { display: flex; align-items: flex-end; gap: 4px; height: 72px; }
.actbars .b { flex: 1; background: var(--ink-25); border-radius: 3px 3px 0 0; min-height: 2px; }
.actbars .b.hot { background: var(--ember); }

/* ------------------------------------------------ Block Builder */
.blocktray, .blockslots { display: flex; flex-direction: column; gap: 8px; min-height: 120px; }
.block {
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm); padding: 11px 14px; cursor: grab;
  font: 500 14px/1.4 var(--font-mono); color: var(--ink);
  box-shadow: 0 1px 0 rgba(20,17,15,.05);
  transition: transform .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.block:hover { border-color: var(--ink-45); transform: translateY(-1px); box-shadow: var(--shadow-card); }
.block:active { cursor: grabbing; }
.block.placed { background: var(--ink); color: #F1F0ED; border-color: var(--ink); box-shadow: none; flex: 1; }

.slot {
  display: flex; align-items: center; gap: 10px; min-height: 46px;
  border: var(--hairline) dashed var(--ink-25); border-radius: var(--radius-sm);
  padding: 4px 6px 4px 10px; transition: border-color .15s ease, background .15s ease;
}
.slot.over {
  border-color: var(--ember); background: var(--ember-tint);
  transform: scale(1.02);
}
.slot { transition: border-color .15s ease, background .15s ease, transform .15s ease; }
.block.dragging { opacity: .4; }
.blocktray.over { outline: 2px dashed var(--ember); outline-offset: 6px; border-radius: var(--radius-sm); }
.slot.filled { border-style: solid; border-color: transparent; padding: 0; }
.slot.filled .slotnum { margin-left: 10px; }
.slotnum { font: 600 12px var(--font-mono); color: var(--text-quiet); flex: none; width: 16px; }
.slothint { font: 400 13px var(--font-sans); color: var(--text-quiet); }

/* ------------------------------------------------ Pathway */
.road { position: relative; height: 58px; }
.road-line {
  position: absolute; left: 0; right: 0; top: 38px; height: 3px; border-radius: 2px;
  background: repeating-linear-gradient(to right,
    var(--ink-25) 0 14px, transparent 14px 26px);
}
.road-markers { position: absolute; left: 0; right: 0; top: 33px; height: 14px; }
.road-marker {
  position: absolute; width: 11px; height: 11px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--ink-25); transform: translateX(-50%);
}
.road-marker.passed { background: var(--ember); border-color: var(--ember); }
.road-car {
  position: absolute; top: 8px; left: 0; width: 46px;
  transition: left .55s cubic-bezier(.34,1.3,.64,1);
}
.road-car img { width: 100%; height: auto; display: block; }
.road-car.moving img { animation: ora-levels .2s ease-in-out 3; }

/* ------------------------------------------------ Ora, the waveform guide
   The mark itself is the untouched brand PNG; only the eyes are ours. */
.ora-wrap { display: flex; align-items: center; gap: var(--space-4); }
.ora { position: relative; width: 104px; flex: none; transform-origin: 50% 62%; }
.ora img { width: 100%; height: auto; display: block; }

.ora-eye {
  position: absolute; width: 14.5%; height: 14.5%; top: 35.5%;
  background: var(--panel); border: 2px solid var(--ink); border-radius: 50%;
  box-sizing: border-box; transition: transform .25s ease, height .25s ease, top .25s ease;
}
.ora-eye.l { left: 30.6%; }
.ora-eye.r { left: 53.4%; }
.ora-eye i {
  position: absolute; width: 42%; height: 42%; top: 32%; left: 31%;
  background: var(--ink); border-radius: 50%; transition: transform .3s ease;
}
/* a single catchlight — the difference between alive and taxidermy */
.ora-eye i::after {
  content: ""; position: absolute; width: 38%; height: 38%; top: 8%; right: 10%;
  background: var(--panel); border-radius: 50%;
}

/* blink — the one motion that never stops */
.ora-eye { animation: ora-blink 5.4s infinite; }
.ora-eye.r { animation-delay: .06s; }
@keyframes ora-blink {
  0%, 92%, 100% { transform: scaleY(1); }
  95%, 97% { transform: scaleY(0.12); }
}

.ora-idle { animation: ora-bob 4.2s ease-in-out infinite; }
.ora-think { animation: ora-tilt 2.4s ease-in-out infinite; }
.ora-think .ora-eye i { transform: translate(-18%, -34%); }
.ora-talking img { animation: ora-levels .42s ease-in-out infinite; }
.ora-happy { animation: ora-hop .7s ease; }
.ora-cheer { animation: ora-hop .55s ease 2; }
/* closed, smiling eyes: a pair of ink arcs rather than a white dome */
.ora-happy .ora-eye, .ora-cheer .ora-eye {
  height: 7.5%; top: 38%; background: transparent;
  border: none; border-top: 3px solid var(--ink); border-radius: 50% 50% 0 0;
}
.ora-happy .ora-eye i, .ora-cheer .ora-eye i { opacity: 0; }
.ora-oops { animation: ora-dip .9s ease; }
.ora-oops .ora-eye i { transform: translateY(38%); }

@keyframes ora-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes ora-tilt { 0%, 100% { transform: rotate(-2.5deg); } 50% { transform: rotate(2.5deg); } }
@keyframes ora-hop {
  0% { transform: translateY(0) scale(1); }
  35% { transform: translateY(-15px) scale(1.05); }
  70% { transform: translateY(0) scale(0.97, 1.03); }
  100% { transform: none; }
}
@keyframes ora-dip {
  0%, 100% { transform: none; }
  45% { transform: translateY(6px) scale(1.05, 0.9); }
}
@keyframes ora-levels { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(0.88); } }
@media (prefers-reduced-motion: reduce) {
  .ora, .ora img, .ora-eye { animation: none !important; }
}

/* the buddy strip — the map, travelling with you */
.buddystrip {
  display: flex; align-items: center; gap: var(--space-4); text-decoration: none;
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-md); padding: 12px 18px; margin-bottom: var(--space-5);
  box-shadow: var(--shadow-card); transition: transform .2s ease, box-shadow .2s ease;
}
.buddystrip:hover { transform: translateY(-2px); box-shadow: var(--shadow-float); }
.buddy-faces { display: flex; align-items: center; }
.buddy-second { margin-left: -14px; }
.buddy-text { flex: 1; font-size: 14.5px; line-height: 1.4; }
.ora.mini { width: 42px; }
.ora.crewface { width: 68px; flex: none; }

/* crew expressions — form, never a second hue */
.eyes-narrow .ora-eye { height: 9%; top: 39%; border-radius: 50% 50% 4px 4px; }
.eyes-wide .ora-eye { width: 17%; height: 17%; top: 33%; }
.eyes-wide .ora-eye.l { left: 29%; } .eyes-wide .ora-eye.r { left: 54%; }
.eyes-calm .ora-eye i { transform: translateY(12%); }
.eyes-sharp .ora-eye { height: 12%; top: 37%; border-radius: 40%; }
.eyes-fierce .ora-eye { height: 11%; top: 38%; border-radius: 50% 50% 2px 2px; }
.eyes-fierce .ora-eye i { transform: translateY(-10%); }

.ora-bubble {
  position: relative; background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-md); padding: 14px 18px; box-shadow: var(--shadow-card);
  font-size: 15.5px; line-height: 1.5; min-height: 52px; flex: 1; max-width: 560px;
}
.ora-bubble::before {
  content: ""; position: absolute; left: -8px; top: 26px; width: 14px; height: 14px;
  background: var(--panel); border-left: var(--hairline) solid var(--line);
  border-bottom: var(--hairline) solid var(--line); transform: rotate(45deg);
}
.ora-bubble.reveal { animation: rise .5s ease both; }

/* ------------------------------------------------ placement */
.placement-stage { max-width: 720px; margin: 0 auto; }
.placement-q { font: 600 21px/1.35 var(--font-sans); margin: 0 0 var(--space-4); }
.level-dots { display: inline-flex; gap: 5px; vertical-align: middle; }
.level-dots i { width: 8px; height: 8px; border-radius: 50%; background: rgba(20,17,15,0.12); }
.level-dots i.on { background: var(--ember); }

/* result reveal */
.placement-level {
  font-family: var(--font-display); font-size: clamp(40px, 7vw, 64px);
  line-height: 1.05; text-transform: uppercase; letter-spacing: -0.01em;
}

/* ------------------------------------------------ journey map */
.world-card { position: relative; overflow: hidden; }
.world-card.locked { opacity: .55; }
.world-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.world-num {
  font-family: var(--font-display); font-size: 30px; color: var(--ink-25);
  line-height: 1;
}
.journey-map { width: 100%; height: auto; display: block; margin-top: 6px; }
.journey-map .trail { fill: none; stroke: var(--ink-25); stroke-width: 2.5; stroke-dasharray: 1 9; stroke-linecap: round; }
.journey-map .trail.walked { stroke: var(--ember); stroke-dasharray: none; opacity: .8; }
.jnode { cursor: pointer; }
.jnode circle.face { fill: var(--panel); stroke: var(--ink-25); stroke-width: 2; }
.jnode.done circle.face { fill: var(--ink); stroke: var(--ink); }
.jnode.current circle.face { stroke: var(--ember); stroke-width: 3; }
.jnode.current .halo { animation: pulse 1.5s ease-in-out infinite; transform-origin: center; transform-box: fill-box; }
.jnode.locked { cursor: default; }
.jnode.locked circle.face { stroke-dasharray: 3 4; }
.jnode text.n { font: 700 13px var(--font-sans); fill: var(--ink-70); text-anchor: middle; }
.jnode.done text.n { fill: #fff; }
.jnode .star { fill: rgba(20,17,15,0.15); }
.jnode .star.on { fill: var(--ember); }
.jnode text.jlabel { font: 500 10px var(--font-mono); fill: var(--text-muted); text-anchor: middle; }
.jnode.boss circle.face { stroke-width: 3; }
/* carried by the Hawk: the road is open, the node is not earned */
.jnode.carried circle.face { fill: var(--panel); stroke: var(--ember); stroke-dasharray: 4 3; }
.jnode.carried text.n { fill: var(--ember); }

.worldstory {
  margin: 4px 0 2px; font-size: 14.5px; line-height: 1.5; color: var(--ink-70);
  border-left: 3px solid var(--ember); padding-left: 14px; max-width: 640px;
}

/* cutscenes — Ora and your buddy, before and after a world */
.cutscene {
  margin: 12px 0 4px; padding: 16px 18px; background: var(--ink);
  border-radius: var(--radius-md); color: #F1F0ED;
}
.cut-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.cut-head .chip { background: transparent; border-color: rgba(255,255,255,.25); color: #F1F0ED; }
.cut-head .chip.ember { border-color: var(--ember); color: var(--ember); }
.cut-dismiss { cursor: pointer; margin-left: auto; font: 500 12.5px var(--font-mono); }
.cut-line {
  display: flex; align-items: flex-start; gap: 14px; padding: 8px 0;
  animation: rise .6s ease both;
}
.cut-line .ora { width: 46px; flex: none; }
.cut-who {
  font: 600 12px var(--font-mono); color: var(--ember);
  text-transform: lowercase; letter-spacing: .04em;
}
.cut-text { margin: 2px 0 0; font-size: 15.5px; line-height: 1.5; }
.cut-line.cut-cont { padding-top: 0; }
.cut-gap { width: 46px; flex: none; }

.hawkcall {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 10px; padding: 14px 18px; background: var(--ember-tint);
  border-radius: var(--radius-md);
}
.jnode.boss text.jlabel { fill: var(--ember-deep); }

/* ------------------------------------------------ plan picker */
.rolecard { position: relative; cursor: pointer; display: block; }
.rolecard input {
  position: absolute; top: 16px; right: 16px; width: 20px; height: 20px;
  accent-color: var(--ember);
}
.rolecard:has(input:checked) { border-color: var(--ember); box-shadow: var(--shadow-float); }
.dutylist { margin: 8px 0 0; padding-left: 18px; }
.dutylist li { font-size: 13.5px; color: var(--ink-70); margin-bottom: 3px; }

/* ------------------------------------------------ glossary: the popup dictionary */
.gls {
  border-bottom: 1.5px dotted var(--ember); cursor: help; border-radius: 2px;
}
.gls:hover, .gls:focus-visible { background: var(--ember-tint); }
.gls-pop {
  position: absolute; z-index: 320; width: 320px; max-width: calc(100vw - 16px);
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-md); padding: 14px 16px; box-shadow: var(--shadow-float);
}
.gls-pop.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: auto;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
.gls-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.gls-head b { font-size: 16px; }
.gls-x {
  margin-left: auto; background: none; border: none; cursor: pointer;
  font: 400 22px/1 var(--font-sans); color: var(--text-muted); padding: 2px 6px;
}
.gls-pop p { margin: 0; font-size: 14.5px; line-height: 1.5; }

/* ------------------------------------------------ phrasebook drawer */
.pb-fab {
  position: fixed; right: 16px; bottom: 16px; z-index: 300;
  padding: 12px 22px; border-radius: var(--radius-pill); cursor: pointer;
  background: var(--ink); color: #F1F0ED; border: var(--hairline) solid var(--ink);
  font: 600 14px var(--font-sans); box-shadow: var(--shadow-float);
}
.pb-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 310; width: 400px;
  max-width: 100vw; background: var(--panel);
  border-left: var(--hairline) solid var(--line); box-shadow: var(--shadow-float);
  transform: translateX(105%); transition: transform .25s ease;
  display: flex; flex-direction: column; padding: 16px;
}
.pb-drawer.open { transform: none; }
.pb-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.pb-drawer input#pb-search { margin-bottom: 10px; }
.pb-list { overflow-y: auto; flex: 1; }
.pb-set { font-size: 11px; margin: 14px 0 6px; }
.pb-entry { border-top: 1px solid var(--line); padding: 10px 0; }
.pb-row { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.pb-row b { font-size: 14px; flex: 1; }
.pb-row .chip { cursor: pointer; }
.pb-code {
  margin: 0; background: var(--ink); color: #F1F0ED; border-radius: var(--radius-sm);
  padding: 8px 10px; font: 400 12px/1.5 var(--font-mono);
  overflow-x: auto; white-space: pre;
}
.pb-doc { margin: 6px 0 0; font-size: 13px; color: var(--text-muted); }

/* ------------------------------------------------ the terminal
   A 3270 depicted as equipment: amber phosphor on near-black, scanlines,
   a command line and a PF-key bar. Amber terminals were real hardware, and
   amber is the brand's own hue — authenticity without a second colour. */
.terminal {
  background: #171310; border-radius: var(--radius-md); padding: 14px 16px 10px;
  box-shadow: inset 0 0 60px rgba(0,0,0,.55), var(--shadow-card);
  position: relative; overflow: hidden;
}
.terminal::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(to bottom,
    transparent 0 2px, rgba(0,0,0,.13) 2px 3px);
}
.term-screen {
  margin: 0; font: 400 13px/1.45 var(--font-mono); color: var(--ember);
  text-shadow: 0 0 6px rgba(251, 75, 42, .45);
  white-space: pre; overflow-x: auto; min-height: 300px;
}
.term-screen .dim { color: var(--ember-deep); text-shadow: none; }
.term-msg { font: 400 12.5px var(--font-mono); color: #F1F0ED; min-height: 18px; margin: 4px 0; }
.term-input-row { display: flex; gap: 8px; align-items: center; position: relative; z-index: 1; }
.term-input-row .lbl { font: 600 13px var(--font-mono); color: var(--ember); white-space: nowrap; }
.term-input-row input {
  flex: 1; background: transparent; border: none; border-bottom: 1.5px solid var(--ember-deep);
  border-radius: 0; color: var(--ember); font: 400 14px var(--font-mono);
  padding: 4px 2px; caret-color: var(--ember); text-transform: uppercase;
}
.term-input-row input:focus { outline: none; border-bottom-color: var(--ember); }
.pfbar {
  display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px;
  position: relative; z-index: 1;
}
.pfbar button {
  background: transparent; border: 1px solid var(--ember-deep); border-radius: 4px;
  color: var(--ember-deep); font: 500 11px var(--font-mono); padding: 4px 9px; cursor: pointer;
}
.pfbar button:hover { color: var(--ember); border-color: var(--ember); }

/* ------------------------------------------------ the admin cockpit
   Terminal on the left, shift-mate on the right. The rail is a handover
   feed: Ora speaks in cards, machine evidence arrives as mono log lines.
   One hue throughout — the ember already owns the phosphor. */
.cockpit {
  display: grid; grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px; align-items: start;
}
.cockpit-rail {
  position: sticky; top: 16px; padding: var(--space-4);
  display: flex; flex-direction: column; max-height: calc(100vh - 32px);
}
.rail-head {
  display: flex; align-items: center; gap: 10px;
  padding-bottom: 12px; border-bottom: var(--hairline) solid var(--line);
}
.rail-head .ora { width: 46px; flex: none; }
.rail-head b { font-size: 15px; }
.feed { overflow-y: auto; padding-top: 4px; scroll-behavior: smooth; }
.feed-msg { margin-top: 12px; animation: feedin .35s ease both; }
.feed-msg.from-ora p {
  margin: 0; background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm) var(--radius-sm) var(--radius-sm) 4px;
  padding: 10px 12px; font-size: 13.5px; line-height: 1.5;
}
.feed-msg.log pre {
  margin: 0; background: var(--ink); color: #F1F0ED; border-radius: var(--radius-sm);
  border-left: 3px solid var(--ember);
  padding: 8px 10px; font: 400 11.5px/1.5 var(--font-mono);
  white-space: pre; overflow-x: auto;
}
.feed-divider {
  font-size: 10.5px; margin: 16px 0 0; letter-spacing: .08em;
  border-top: var(--hairline) solid var(--line); padding-top: 10px;
}
@keyframes feedin {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

footer.site {
  border-top: var(--hairline) solid var(--line); margin-top: var(--space-9);
  padding: var(--space-5) 0; display: flex; align-items: center; gap: 8px;
}
footer.site img { width: 16px; height: 16px; object-fit: cover; }
footer.site span { font: 400 11px var(--font-mono); color: var(--text-muted); }

/* The trademark notice takes the whole of its own line rather than being
   squeezed into the footer's single flex row, where a sentence this long
   would crush the brand line beside it on any narrow screen. */
footer.site { flex-wrap: wrap; }
footer.site span.trademark {
  flex-basis: 100%; line-height: 1.5;
  font-size: 10.5px; opacity: 0.75; margin-top: 4px;
  /* Under the brand line's text, not under its mark (16px image + 8px
     gap), and as wide as the row above it -- held to 78ch it stopped
     halfway across and read as a stray block. */
  padding-left: 24px; box-sizing: border-box; text-wrap: pretty;
}
@media (max-width: 640px) {
  footer.site span.trademark { padding-left: 0; }
}

/* ==================================================================
   Mobile. The estate this teaches is read on phones on the way to
   work, so this is not a courtesy breakpoint — every engine has to be
   genuinely playable at 375px, including the ones built around drag
   and drop, which get an explicit tap path because HTML5 drag events
   never fire on touch.
   ================================================================== */


/* ==================================================== sidebar as a drawer
   Below 1024 the grid loses its first column and the sidebar becomes an
   overlay. `transform` rather than `left` so it composites on the GPU and
   does not repaint the page behind it on every frame of the slide.

   1024 is the breakpoint because it is where an iPad in landscape stops
   being a phone: portrait iPads and everything narrower get the drawer,
   landscape iPads and laptops get the column. */

@media (max-width: 1023px) {
  .layout { grid-template-columns: minmax(0, 1fr); }

  .mobilebar {
    display: flex; align-items: center; gap: var(--space-3);
    position: sticky; top: 0; z-index: 30;
    padding: var(--space-2) var(--space-4);
    padding-top: calc(var(--space-2) + env(safe-area-inset-top));
    background: rgba(247, 246, 243, 0.92);
    backdrop-filter: saturate(1.4) blur(10px);
    -webkit-backdrop-filter: saturate(1.4) blur(10px);
    border-bottom: var(--hairline) solid var(--line);
  }
  .mobilebar .brand {
    display: flex; align-items: center; gap: 8px; text-decoration: none;
    flex: 1; justify-content: center; min-width: 0;
  }
  .mobilebar .brand img { width: 26px; height: 26px; object-fit: cover; }
  .mobilebar .brand b { font-weight: 800; font-size: 15.5px; letter-spacing: -0.01em; }

  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 45;
    width: min(300px, 84vw); height: 100%;
    transform: translateX(-102%);
    transition: transform .22s cubic-bezier(.4, 0, .2, 1);
    box-shadow: var(--shadow-float);
    padding-top: env(safe-area-inset-top);
  }
  .drawer-open .sidebar { transform: translateX(0); }
  .railbtn { display: none; }               /* no rail inside a drawer */

  /* the rail preference must not leak into the drawer */
  .railed .brand-text,
  .railed .navitem .lbl,
  .railed .signout .lbl,
  .railed .searchtrigger .lbl,
  .railed .navgroup,
  .railed .side-foot .stats,
  .railed .userchip .meta { display: revert; }
  .railed .side-head { flex-direction: row; padding: var(--space-4) var(--space-4) var(--space-3); }
  .railed .searchtrigger { width: calc(100% - var(--space-3) * 2); margin-inline: var(--space-3); }
  .railed .navitem, .railed .signout { justify-content: flex-start; padding: 10px 11px; }
  .railed .navitem::after { display: none; }
  .railed .userchip { justify-content: flex-start; padding: 7px 8px; }

  .shell { padding: var(--space-5) var(--space-4) var(--space-8); }
}

@media (min-width: 1024px) {
  .scrim { display: none; }
}

/* An iPad in landscape is exactly 1024, where a 264px sidebar is a quarter of
   the screen. It narrows until there is room to spare. */
@media (min-width: 1024px) and (max-width: 1279px) {
  .layout { --side-w: 216px; }
  .navitem, .signout { font-size: 13.5px; gap: 9px; }
  .shell { padding-inline: var(--space-5); }
}

@media (max-width: 820px) {
  .shell { padding: 0 var(--space-4) var(--space-8); }


  /* type comes down a step so headings do not eat the fold */
  .page-title { font-size: 30px; }
  .section-title { font-size: 22px; margin: var(--space-6) 0 var(--space-3); }
  .page-sub { font-size: 15px; }
  .hero { padding: var(--space-6) 0 var(--space-5); }
  .hero p { font-size: 17px; }

  /* every hard two-column layout stacks */
  .grid { grid-template-columns: 1fr !important; }
  .card { padding: var(--space-4); }

  /* cockpit stacks with the handover first: on a phone you read the
     incident, then you get the machine */
  .cockpit { grid-template-columns: 1fr; }
  .cockpit-rail { position: static; order: -1; max-height: 300px; }

  .buddystrip { flex-wrap: wrap; gap: var(--space-3); padding: 12px 14px; }
  .buddy-text { flex: 1 1 60%; font-size: 14px; }

  /* The map keeps its real size and the phone swipes along it, rather than
     shrinking to a trail of unreadable, unmissable-by-thumb dots. */
  .map-scroll {
    overflow-x: auto; overflow-y: hidden;
    margin: 0 calc(-1 * var(--space-4)); padding: 0 var(--space-4);
    -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity;
  }
  .map-scroll .journey-map { min-width: 640px; }
  .jnode text.n { font-size: 15px; }
  .jnode text.jlabel { font-size: 11.5px; }
  .world-num { font-size: 24px; }
  .world-head { gap: 8px; }

  /* Block Builder: tray above the build, and tapping is the primary verb */
  .blocktray, .blockslots { min-height: 0; }
  .block { padding: 13px 14px; font-size: 13.5px; }
  .slot { min-height: 52px; }

  /* the title gets its own row; sharing one with the chips squeezed it to a
     single word per line */
  .playhead { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
  .playhead h1 { font-size: 22px; width: 100%; flex: none; }
  .timer { font-size: 17px; padding: 5px 12px; }

  pre.artifact { font-size: 12px; padding: var(--space-3); }
  textarea.codepad { font-size: 12.5px; min-height: 240px; }

  .option { padding: 14px 15px; font-size: 15px; }
  .option.dontknow { flex-direction: column; align-items: flex-start; gap: 4px; }

  .kpis { gap: var(--space-3); }
  .kpi { min-width: 100px; }

  .heatmap .cell { width: 10px; height: 10px; }
  .skillrow { grid-template-columns: 1fr 60px 34px; gap: 8px; }

  table.data { font-size: 13px; }
  table.data th, table.data td { padding: 8px 6px; }

  .cut-line .ora { width: 38px; }
  .cut-text { font-size: 14.5px; }
  .hawkcall { padding: 12px 14px; }
  .scorewrap .big { font-size: 54px; }
  .placement-level { font-size: 40px; }
}

@media (max-width: 420px) {
  .shell { padding: 0 var(--space-3) var(--space-7); }
  .page-title { font-size: 26px; }
  .chip { padding: 5px 11px; font-size: 12px; }
  .btn { padding: 12px 20px; }
  .btn.lg { padding: 14px 24px; font-size: 16px; }
  /* stack action rows so nothing gets clipped off the right edge */
  .form-actions, .case-actions { flex-direction: column; align-items: stretch; }
  .heatmap { gap: 2px; }
  /* 8px was unreadable and the whole strip fits 375px at 10 */
  .heatmap .cell { width: 10px; height: 10px; }
}

/* The instructions have to match the device. HTML5 drag events never fire on
   touch, so telling a phone user to "drag from here" is telling them to do
   something that cannot work. */
/* The pick-then-place route works with a mouse, a finger and a keyboard, so
   it is the one named first. Dragging still works and is faster once you know
   the board, but describing only the drag told touch and keyboard users the
   game needed something they do not have. */
.howto::after { content: " · pick a piece, then choose a rung"; }
.howbuild::after { content: " · any rung, in any order — picking a filled rung swaps it"; }

@media (hover: none) and (pointer: coarse) {
  .howto::after { content: " · tap a piece, then tap a rung"; }
  .howbuild::after { content: " · tap a rung to place, tap a placed piece to lift it"; }
  .block { cursor: pointer; }
  .blocktray .block::after {
    content: "tap to place"; display: block; margin-top: 4px;
    font: 500 10.5px var(--font-mono); color: var(--text-quiet);
  }
  .blockslots .block::after {
    content: "tap to remove"; display: block; margin-top: 4px;
    font: 500 10.5px var(--font-mono); color: rgba(241,240,237,.6);
  }
  /* finger-sized targets throughout */
  .btn, .option, .cause-btn { min-height: 44px; }
  .option { display: flex; align-items: center; }
  .navitem, .signout, .searchtrigger { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ================================================================ test mode
   The assessment surface. Austere on purpose: no companion, no stars, and a
   card that is only the question. */
.pickgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
.pick { display: block; cursor: pointer; }
.pick input { position: absolute; opacity: 0; width: 0; height: 0; }
.pick > span {
  display: block; padding: 14px 16px; border-radius: var(--radius-sm);
  border: var(--hairline) solid var(--line); background: var(--panel);
  transition: border-color .15s ease, background .15s ease;
}
.pick > span b { display: block; font-size: 15px; margin-bottom: 6px; }
.pick > span .chip { margin-bottom: 8px; }
.pick > span em { display: block; font-style: normal; font-size: 13px; color: var(--text-muted); }
.pick input:checked + span { border-color: var(--ember); background: var(--ember-tint); }
.pick input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* a horizontal rail so a wide table scrolls itself rather than the page */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-wrap table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.table-wrap th {
  text-align: left; font: 500 11.5px var(--font-mono); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em; padding: 0 10px 8px;
  border-bottom: var(--hairline) solid var(--line); white-space: nowrap;
}
.table-wrap td { padding: 11px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table-wrap tr:last-child td { border-bottom: none; }

/* the wrapped result: a strengths ladder that reads at a glance */
.wrapped { display: grid; gap: var(--space-3); }
.wrapped .row { display: grid; grid-template-columns: 1fr 84px 46px; gap: 12px; align-items: center; }
.wrapped .row .lbl { font-size: 14.5px; }
.wrapped .row .num { font: 500 12.5px var(--font-mono); color: var(--text-muted); text-align: right; }
.archetype {
  border-radius: var(--radius-md); padding: var(--space-5);
  background: var(--ink); color: #F1F0ED;
}
.archetype .kicker { font: 500 11.5px var(--font-mono); color: rgba(241,240,237,.6);
  text-transform: uppercase; letter-spacing: .06em; }
.archetype h2 { font: 700 32px/1.15 var(--font-sans); letter-spacing: -0.02em; margin: 10px 0 12px; }
.archetype p { margin: 0; color: rgba(241,240,237,.85); font-size: 15px; }
.archetype .big { font: 700 64px/1 var(--font-display); color: var(--ember); }
/* Results page (design/ux/RESULTS_EXPERIENCE_SPEC.md): verdict first, one
   next step, the arithmetic folded away until asked for. */
.result-head { margin-bottom: 20px; }
.result-score {
  display: flex; gap: 28px; align-items: flex-end; flex-wrap: wrap; margin-top: 18px;
}
.result-verdict { flex: 1; min-width: 220px; }
.archetype .result-verdict-word {
  font: 700 clamp(26px, 6vw, 34px)/1.1 var(--font-sans); letter-spacing: -0.02em;
  color: #fff; margin: 0 0 8px;
}
.archetype .result-archetype {
  margin-top: 20px; padding-top: 16px; font-size: 14.5px;
  border-top: 1px solid rgba(241,240,237,.16);
}
.result-next { margin-bottom: 20px; border: 1.5px solid var(--ember); }
.result-next-title { font: 700 21px/1.25 var(--font-sans); margin: 0 0 8px; letter-spacing: -0.01em; }
.result-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
.result-scoring > summary {
  list-style: none; cursor: pointer; display: flex; flex-wrap: wrap;
  align-items: baseline; gap: 4px 14px;
}
.result-scoring > summary::-webkit-details-marker { display: none; }
.result-scoring > summary::after {
  content: "Show"; margin-left: auto; font: 500 12px var(--font-mono); color: var(--text-muted);
}
.result-scoring[open] > summary::after { content: "Hide"; }
.result-scoring > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; border-radius: 6px; }
.result-scoring-title { font: 700 17px var(--font-sans); }
.result-scoring-body { margin-top: 14px; }
.result-scoring-body .sub { margin: 0 0 8px; }

/* ============================================================ corrections
   Four findings from the accessibility audit, fixed at the token level so a
   new page inherits the repair rather than repeating the defect. */

/* 1. `outline: none` on every control removed the keyboard focus ring
   platform-wide, leaving a border shift nobody can perceive as the only
   signal. Pointer focus stays quiet; keyboard focus does not. */
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, .option:focus-visible, .pick:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* 2. Ember on white is 3.6:1 — under AA at every size the product uses it,
   and it is the primary call to action on most pages. --ember-deep is 4.6:1
   and the same hue. The fill moves; the palette does not. */
/* Measured again (design audit P0-1): ember-deep is 4.44:1 under white
   15px text, not 4.6, so the primary button still failed. --ember-ink is
   5.6:1 under white and 5.2:1 as text on paper: same hue, one step darker. */
:root { --ember-ink: #C42E12; }
.btn.ember { background: var(--ember-ink); border-color: var(--ember-ink); }
.btn.ember:hover { background: #A82710; border-color: #A82710; }
.chip.ember { color: var(--ember-ink); border-color: var(--ember-ink); }
.stepnum, .legalbody a, .backlink:hover, .legalnav a:hover { color: var(--ember-ink); }
.error { color: var(--ember-ink); }

/* Skip past the navigation (design audit P1-4). */
.skip { position: absolute; left: -9999px; top: 0; }
.skip:focus { left: 16px; top: 16px; z-index: 1000; background: var(--ink); color: #fff;
  padding: 8px 16px; border-radius: var(--radius-pill); text-decoration: none; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* The exam's progress bar used class="meter", which was never defined, so
   the bar had no height and never showed (design audit P0-2). */
.meter { height: 6px; background: rgba(20,17,15,0.08); border-radius: 999px; overflow: hidden; }
.meter > i { display: block; height: 100%; background: var(--ink); border-radius: 999px;
  transition: width .2s ease; }
/* Clock and counter are the most important state on a paper: ink, not quiet. */
#exam-clock, #counter { color: var(--ink); font-size: 14px; }
#exam-clock.low { color: var(--ember-ink); font-weight: 600; }
/* Options while a submit is in flight look unavailable (design audit P1-7). */
.option:disabled { opacity: .55; cursor: progress; transform: none; }
/* Long exhibits can be scrolled by keyboard (assessment audit B-01). */
pre.artifact:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* The terminal kept outline:none over the global ring (design audit P1-6). */
.term-input-row input:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; }

/* Reduced motion: stop loops and hover lifts too (design audit P2-6). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
  .card.lift:hover, .btn:hover, .block:hover, .option:hover, .buddystrip:hover, .startdoor:hover { transform: none; }
}

/* 3. --text-quiet was #8C867E: 3.34:1, used at 10.5-12px for the microlabels
   that carry the caveats — "% of the role tested" is the line that stops a
   manager misreading a score. It now points at the muted value (5.04:1) and
   #8C867E goes back to being a border colour, which is what 3:1 is for. */
:root { --text-quiet: var(--text-muted); }

/* 4. Chips are the filter layer on Scenarios, the axis toggle on Stacks and
   the cause filters on Errors — about forty targets at 27px on a phone. */
@media (max-width: 820px) {
  .chip { min-height: 34px; }
  a.chip, button.chip { min-height: 44px; }
}

/* a compact link row used by result recommendations and stack listings */
.rowlink {
  display: flex; gap: 10px; align-items: baseline; text-decoration: none;
  padding: 8px 10px; border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm); color: var(--ink);
}
.rowlink:hover { border-color: var(--ink-45); }

/* the week as days: seven dots, one per day, with the forgiven one marked.
   10px minimum so it stays legible on a phone, which the practice heatmap
   does not manage at 8px. */
.weekdots { display: flex; gap: 6px; }
.weekdots .dot {
  width: 22px; height: 22px; border-radius: 7px;
  border: var(--hairline) solid var(--line); background: var(--panel);
}
.weekdots .dot.on { background: var(--ember-deep); border-color: var(--ember-deep); }
.weekdots .dot.frozen { background: repeating-linear-gradient(45deg,
  var(--ink-25) 0 3px, transparent 3px 6px); }
.weekdots .dot.today { outline: 2px solid var(--ink); outline-offset: 2px; }

/* the four-column readiness row, as a class rather than inline, so the mobile
   rule can collapse it. Inline grid-template-columns beat the media query and
   overflowed a 375px card by about thirty pixels. */
.skillrow.wide { grid-template-columns: 1fr 90px 44px 110px; }
@media (max-width: 820px) {
  .skillrow.wide { grid-template-columns: 1fr 60px 40px; }
  .skillrow.wide > :last-child { grid-column: 1 / -1; text-align: left; }
}

/* a block waiting for a slot, and the slots that will take it */
.block.armed { border-color: var(--ember-deep); box-shadow: 0 0 0 2px var(--ember-tint); }
.slot.open { border-style: dashed; border-color: var(--ember-deep); }
button.block, button.slothint { font: inherit; cursor: pointer; text-align: left; }
button.slothint { background: transparent; border: none; width: 100%; padding: 0;
  color: var(--text-quiet); }

/* the legend the heatmap never had: three tints with no key is a puzzle */
.heatkey { display: flex; align-items: center; gap: 4px; margin-top: 10px; }
.heatkey .cell { width: 13px; height: 13px; border-radius: 3px; background: rgba(20,17,15,0.06); }
.heatkey .cell.l1 { background: #FBD9CF; }
.heatkey .cell.l2 { background: #F58A6C; }
.heatkey .cell.l3 { background: var(--ember-deep); }

/* the phrasebook button sat inside the iPhone home-indicator band, where an
   upward scroll of the drawer's list is captured by the system gesture. The
   sheet eleven lines above already honours the inset. */
.pb-fab { bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
.pb-drawer { padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
/* a closed drawer is off-screen but still in the tab order: a keyboard user
   on play_console walked into an invisible search box and every chip in it */
.pb-drawer:not(.open) { visibility: hidden; }
.pb-drawer { transition: transform .25s ease, visibility .25s; }

/* ============================================== sitting integrity
   The visible half of app/integrity.py. Read that file's docstring before
   changing anything here: a page cannot block a screenshot, so the watermark
   is the measure that still works after one has been taken. */

/* Faint, tiled, and behind the content rather than over it, so it is legible
   in a capture and ignorable while reading a dump. It is drawn with a
   repeating gradient of nothing plus a text layer, so there is no image to
   strip and removing it means editing the page rather than blocking a URL. */
.wm {
  position: relative;
  isolation: isolate;
  /* The mark is rotated and scaled past the box so it tiles edge to edge with
     no bald corner. Without this it painted over the topbar and pushed the
     page sideways — clipping is what keeps it inside the content it marks. */
  overflow: hidden;
}
.wm::before {
  content: attr(data-wm);
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  overflow: hidden; white-space: pre-wrap; word-break: break-all;
  /* 9 percent was invisible in a capture, which defeats the whole point: a
     mark nobody can read identifies nobody. 15 is legible in a screenshot at
     full size and still recedes behind the question while it is being read. */
  font: 500 12px/3.1 var(--font-mono);
  color: color-mix(in srgb, var(--ink) 15%, transparent);
  transform: rotate(-24deg) scale(1.6);
  transform-origin: center;
  letter-spacing: .22em;
  user-select: none;
}
.wm > * { position: relative; z-index: 1; }

/* No selection anywhere inside a paper. The editors are the exception: a
   candidate has to be able to work in the one they are typing into. */
.noselect, .noselect * { user-select: none; -webkit-user-select: none; }
.noselect input, .noselect textarea,
.noselect input *, .noselect textarea * { user-select: text; -webkit-user-select: text; }

.proctor-curtain {
  position: fixed; inset: 0; z-index: 900; display: none;
  align-items: center; justify-content: center; padding: var(--space-5);
  background: rgba(20, 17, 15, 0.97);
  /* the blur is the point: whatever was on screen stops being readable the
     instant focus is lost, so a paused paper is not a free screenshot */
  backdrop-filter: blur(14px);
}
.proctor-curtain.open { display: flex; }
.proctor-card {
  max-width: 460px; text-align: center; color: #F1F0ED;
}
.proctor-card h2 { font: 700 26px/1.2 var(--font-sans); margin: 10px 0 12px; }
.proctor-card p { color: rgba(241, 240, 237, .8); font-size: 15px; }
.proctor-card .quiet { color: rgba(241, 240, 237, .55); }

.proctor-flash {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(20px);
  z-index: 950; opacity: 0; transition: opacity .18s ease, transform .18s ease;
  background: var(--ink); color: #F1F0ED; border-radius: var(--radius-pill);
  padding: 10px 20px; font: 500 13.5px var(--font-sans);
  box-shadow: var(--shadow-float); pointer-events: none; max-width: 90vw;
  text-align: center;
}
.proctor-flash.open { opacity: 1; transform: translateX(-50%) translateY(0); }

@media print {
  /* a paper has no print stylesheet, it has a refusal */
  body { display: none !important; }
}


/* ================================================================ palette
   Spotlight, not a search box. Anchored high rather than centred because the
   list grows downward and a centred dialog jumps as results arrive.

   `dvh` for the max height so an iOS keyboard does not push the results off
   the bottom of a viewport that vh still believes is full height. */

.palette { position: fixed; inset: 0; z-index: 60; }
.palette[hidden] { display: none; }
.palette-scrim {
  position: absolute; inset: 0;
  background: rgba(20, 17, 15, 0.34);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.palette-box {
  position: relative; margin: 12vh auto 0; width: min(660px, calc(100vw - 32px));
  max-height: min(70dvh, 620px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-md); box-shadow: var(--shadow-float);
  animation: palette-in .13s ease-out;
}
@keyframes palette-in {
  from { opacity: 0; transform: translateY(-8px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

.palette-input {
  display: flex; align-items: center; gap: 10px;
  padding: 13px var(--space-4); flex: none;
  border-bottom: var(--hairline) solid var(--line);
}
.palette-input .ico { color: var(--text-quiet); width: 19px; height: 19px; }
.palette-input input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  color: var(--ink); font: 400 17px var(--font-sans);
}
.palette-input input::placeholder { color: var(--ink-25); }

.palette-list { overflow-y: auto; overscroll-behavior: contain; padding: 6px; }
.palette-empty {
  margin: 0; padding: var(--space-6) var(--space-4);
  text-align: center; color: var(--text-quiet); font-size: 14.5px;
}

.pres {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 9px 11px; border-radius: var(--radius-sm);
  text-decoration: none; color: inherit;
}
.pres.on { background: var(--ember-tint); }
.pkind {
  flex: none; width: 74px; text-align: right;
  font: 500 10.5px var(--font-mono); color: var(--text-quiet);
  text-transform: uppercase; letter-spacing: .06em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pres.on .pkind { color: var(--ember-deep); }
.ptext { flex: 1; min-width: 0; }
.ptext b {
  display: block; font: 600 14.5px var(--font-sans);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pdesc {
  display: block; font-size: 12.5px; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pmeta { flex: none; font-size: 11px; color: var(--text-quiet); }

.palette-foot {
  display: flex; align-items: center; gap: var(--space-3); flex: none;
  padding: 8px var(--space-4); border-top: var(--hairline) solid var(--line);
  font: 400 11.5px var(--font-sans); color: var(--text-quiet);
  background: var(--paper);
}
.palette-foot .grow { flex: 1; }
.palette-foot .kbd { margin-right: 3px; }

@media (max-width: 620px) {
  .palette-box { margin-top: 0; width: 100vw; max-height: 100dvh; height: 100dvh;
                 border-radius: 0; border: 0; }
  .palette-input { padding-top: calc(13px + env(safe-area-inset-top)); }
  .pkind { display: none; }
  .pmeta { display: none; }
  .palette-foot { display: none; }        /* no keyboard, no legend */
}

/* ================================================================== the hand
   holding the phone

   Measured at 375x812 against /journey, /leaderboard and /admin before any of
   this existed. Worth writing down what was NOT wrong, because it shaped what
   follows: no page scrolled sideways, nothing was clipped out of reach, the
   journey map's horizontal scroller was deliberate and worked, and the
   glossary already opened as a bottom sheet under 640px. The layout reflows.

   What failed was everything you do with a thumb. */

/* 1 ------------------------------------------------------------- the fields
   `input[type=text], input[type=email], select, textarea` is an attribute
   selector, and two kinds of field walk straight past it:

       <input name="q">         no type attribute at all. It behaves as text,
                                but [type=text] cannot match an attribute
                                that was never written down.
       <input type="number">    simply absent from the list, as are search,
                                tel, url, password and date.

   Both render as the browser's own control -- 21px tall, 13.3px type, 1px of
   padding, square corners -- in the same filter bar as a 46px branded select.
   Measured on /admin: the roster search and the strength filter, the two
   fields an administrator uses most.

   Appearance only. `width` is deliberately not set: the original rule carries
   width:100%, and inheriting that here would restyle every filter bar on the
   desktop layout, which is neither what was reported nor what was measured. */
input:not([type]),
input[type=search], input[type=number], input[type=password],
input[type=tel], input[type=url], input[type=date] {
  font: 400 15px var(--font-sans); color: var(--ink);
  background: var(--panel);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm);
  padding: 11px 14px;
}

/* 2 --------------------------------------------------------- the iOS zoom
   Safari on iOS zooms the page in when a field under 16px takes focus, and
   does not zoom back out afterwards. The product's field type is 15px, under
   the threshold by one pixel, so tapping the roster search on an iPhone
   magnifies the layout and pushes the rest of the page off-screen -- and
   leaves it there. Coarse pointers only; the desktop type scale is untouched. */
@media (pointer: coarse) {
  input:not([type=hidden]):not([type=checkbox]):not([type=radio]),
  select, textarea { font-size: 16px; }
}

/* 3 ---------------------------------------------------- hover that latches
   A touchscreen has no hover, but it does have a :hover state: the browser
   applies it on tap and leaves it applied until you tap somewhere else. Five
   rules here lift an element on hover, so on a phone a tapped card stays
   raised, a tapped answer stays nudged up, and the page fills with elements
   that look active and are not. Tapping a third option leaves three.

   The rules are right for a mouse, so they are not changed. They are put back
   down where there is no mouse to justify them, each restored to the base
   value it has fifteen lines above rather than to `none`, which would flatten
   shadows the design does want. */
@media (hover: none) {
  .card.lift:hover  { transform: none; box-shadow: var(--shadow-card); }
  .btn:hover        { transform: none; }
  .option:hover     { transform: none; border-color: var(--line); }
  .block:hover      { transform: none; border-color: var(--line);
                      box-shadow: 0 1px 0 rgba(20,17,15,.05); }
  .buddystrip:hover { transform: none; box-shadow: var(--shadow-card); }
}

/* 4 ------------------------------------------------------- finger-sized
   The existing coarse-pointer block sizes .btn, .option and .navitem to 44px
   and stops there. It never reaches the form controls -- which is how the
   roster search stayed a 21px target -- or the small utility links.

   44px is the accessible floor, not a preference: it is roughly the contact
   patch of an adult fingertip, and under it the miss rate climbs steeply. */
@media (pointer: coarse) {
  input:not([type=hidden]):not([type=checkbox]):not([type=radio]),
  select, textarea { min-height: 44px; }

  /* `download roster`, `download training plan` and their siblings sit in
     headings at 19px. They cannot take min-height -- they are inline, and a
     height on an inline box does nothing -- so they become inline-block and
     grow by padding instead. */
  a.quiet { display: inline-block; padding: 6px 2px; }

  /* Glossary terms are different again: they are inside running prose, and
     growing the box would open up the line spacing of every paragraph that
     mentions CICS. So the term keeps its size and the HIT AREA grows around
     it, which costs the layout nothing. 6px vertically rather than the 12 a
     44px target would need -- the line box is 21px, and a taller patch would
     reach into the line above and start eating its taps. */
  .gls { position: relative; }
  .gls::after {
    content: ""; position: absolute; inset: -6px -3px; border-radius: 4px;
  }
}

/* 5 ----------------------------------------------------------- the press
   A tap that produces no acknowledgement reads as a page that did not hear
   you, and the honest response to that is to tap it again. Every press gets
   an answer inside one frame.

   :active rather than a JS ripple, because :active is applied by the browser
   on touchdown -- before any script runs, and without waiting to find out
   whether the gesture becomes a scroll. */
.btn:active,
.option:active,
.chip[href]:active,
.buddystrip:active,
a.quiet:active { transform: scale(.972); }

.card.lift:active { transform: scale(.994); }

/* The 300ms wait Safari keeps for a possible double-tap, removed. It is the
   single biggest contributor to a web page feeling slower than the native app
   beside it, and the product has nothing to double-tap. */
.btn, .option, .chip, .navitem, .block, .gls, .buddystrip,
button, a, input, select, textarea { touch-action: manipulation; }

/* The grey box iOS paints over a tapped link, replaced with the ember tint
   the rest of the product uses. Transparent would be worse than the default:
   it removes the acknowledgement without putting anything in its place. */
a, button, .option, .btn, .gls, .block {
  -webkit-tap-highlight-color: rgba(214, 62, 24, .12);
}

/* Reduced motion takes the scale away and keeps the acknowledgement. The
   global rule near the top of this file collapses durations to .01ms, which
   makes a transition instant but leaves a transform still transforming --
   somebody who asked for no motion still gets the button moving under their
   finger. A border they can see instead. */
@media (prefers-reduced-motion: reduce) {
  .btn:active, .option:active, .chip[href]:active, .buddystrip:active,
  a.quiet:active, .card.lift:active { transform: none; }
  .btn:active, .option:active, .buddystrip:active { border-color: var(--ink); }
}

/* ------------------------------------------------------- locked choices
   A locked option is shown, not hidden. A menu that silently omits four of
   five bands reads as a smaller product; one that shows them locked, with
   the reason, reads as a product you have not bought yet -- and it is the
   same sentence the server would return after the click, delivered before
   it. The disabled input is what actually prevents the choice; this is the
   part that explains it. */
/* The border and background live on `.pick > span`, not on the label, so
   that is what has to carry this -- styling `.pick` itself dims nothing. */
.pick.locked { cursor: not-allowed; }
/* Dimmed by colour, not opacity: opacity multiplied down to the
   explanation text (2.54:1), and a child cannot undo a parent's opacity
   (design audit P1-2). */
.pick.locked > span {
  color: var(--ink-70);
  background: var(--paper) repeating-linear-gradient(
    135deg, transparent, transparent 7px,
    rgba(20, 17, 15, .028) 7px, rgba(20, 17, 15, .028) 14px);
}
.pick.locked:hover > span { border-color: var(--line); transform: none; }
.pick.locked .chip {
  background: var(--ink); color: #F1F0ED; border-color: var(--ink);
}
/* The explanation is the point of showing it at all, so it keeps full
   contrast while the option it sits in is dimmed. */
.pick.locked > span em.quiet { opacity: 1; color: var(--text-muted); }

/* ============================================================ attribution
   A screenshot cannot be detected. It can be made to carry the name of the
   person who took it, which is a different and achievable claim.

   Constraints this has to satisfy at once: legible enough in an image to
   identify a reader, faint enough that it never competes with the scenario,
   and impossible to interact with or to lose by copying the text out. Hence
   a fixed overlay above the content rather than a background behind it --
   a background is cropped away the moment somebody screenshots one card.
   Its own class, not .wm: that name is the test card's in-card watermark, and
   sharing it fixed, rotated and un-clickabled the whole question card. */
.wm-overlay {
  position: fixed; inset: 0; z-index: 4;
  pointer-events: none;           /* never steals a click from the game */
  user-select: none;
  overflow: hidden;
  display: flex; flex-direction: column;
  justify-content: space-around;
  transform: rotate(-24deg) scale(1.5);
  transform-origin: center;
}
.wm-overlay > span {
  display: block; white-space: nowrap; text-align: center;
  font: 600 13px/1 var(--mono, ui-monospace, monospace);
  letter-spacing: .14em;
  color: var(--ink);
  /* Set by looking at it rather than by picking a number. At .055 the mark
     was plainly legible on screen and competed with the scenario text,
     which is how a watermark ends up being argued out of the product. This
     sits at the point where it reads as paper texture while reading and
     still resolves into a name in a screenshot. */
  opacity: .032;
}
/* On the darker ground the same opacity disappears, so it is raised rather
   than left to fail quietly in whichever theme nobody tested. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wm-overlay > span { color: #FFF; opacity: .05; }
}
:root[data-theme="dark"] .wm-overlay > span { color: #FFF; opacity: .05; }

/* Printing is the one case where the mark should be plainly visible: a
   printed scenario leaving the building should say whose copy it is. */
@media print {
  .wm-overlay > span { opacity: .22; }
}

/* A watermark that costs somebody their reading is a watermark that gets
   removed, so it goes entirely for readers who have asked for plain
   contrast rather than being reduced to a smudge. */
@media (prefers-contrast: more) {
  .wm-overlay { display: none; }
}

/* ------------------------------------------------- the unseen-finding badge
   The notification itself. There is no mail transport on this platform, so
   this count in the navigation is how a finding reaches an admin at all. */
.navbadge {
  margin-left: auto;
  min-width: 18px; padding: 1px 5px;
  border-radius: 9px;
  background: var(--ember); color: #FFF;
  font: 600 10.5px/1.6 var(--mono, ui-monospace, monospace);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* The rail collapses to icons at narrow widths, where a number beside a
   hidden label is just a floating digit. A dot carries the same "something
   is waiting" and costs no room. */
@media (max-width: 1080px) {
  .navitem .navbadge {
    min-width: 7px; height: 7px; padding: 0;
    color: transparent; overflow: hidden;
  }
}

/* ============================================================ the first screen
   Three doors and nothing else. The rail is hidden here (`bare`), because
   somebody eleven seconds into the product does not need seventeen
   destinations -- they need one decision, and the rail arrives with the
   page they pick.

   Built as a button rather than a link with a button inside it: the whole
   card is the target, which is the behaviour a pointer and a screen reader
   both expect from something this size. */
.startdoor {
  display: flex; align-items: center; gap: 18px;
  width: 100%; padding: 20px 22px;
  background: var(--surface, #FFF);
  border: 1px solid var(--ink-25);
  border-radius: 14px;
  text-align: left; cursor: pointer;
  font: inherit; color: inherit;
  transition: border-color .14s ease, transform .14s ease, box-shadow .14s ease;
}
.startdoor:hover {
  border-color: var(--ember);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(20, 17, 15, .07);
}
.startdoor:active { transform: translateY(0); }
.startdoor:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.startdoor-icon {
  width: 26px; height: 26px; flex: none;
  color: var(--ember);
}
.startdoor-body { display: flex; flex-direction: column; gap: 4px; }
.startdoor-title { font-size: 16.5px; font-weight: 650; }
/* 13px in --text-quiet broke the Palm Ora rule (quiet only at 18px+) and
   was too small to read comfortably (UX audit #11). */
.startdoor-blurb {
  font-size: 14.5px; line-height: 1.5; color: var(--ink-70);
}
/* The one recommended way in carries the weight; the others stay equal and
   quieter (UX audit #5). */
.startdoor.primary { border: 1.5px solid var(--ember); }
.startdoor-tag {
  font-family: var(--font-mono); font-size: 12px; text-transform: lowercase;
  color: var(--ink-70);  /* ember text at 12px fails AA; the border carries it */
}

/* The hover lift is a pointer affordance. On a touch screen there is no
   hover, and leaving the transform in place makes the card feel like it
   stuck down when it was only tapped. */
@media (hover: none) {
  .startdoor:hover { transform: none; box-shadow: none;
                     border-color: var(--ink-25); }
  .startdoor:active { border-color: var(--ember); }
}
@media (prefers-reduced-motion: reduce) {
  .startdoor { transition: none; }
  .startdoor:hover { transform: none; }
}

/* ------------------------------------------------------------- going back
   A named destination, not a browser-back arrow. "back to the roster" tells
   you where you land; a bare arrow asks you to remember, and is wrong the
   moment somebody arrived by a link or a bookmark rather than by clicking
   through. The one place the label is honest about ORIGIN is where the route
   computed it from the row being shown. */
.backlink {
  display: inline-flex; align-items: center; gap: 6px;
  margin-bottom: 14px;
  font-size: 12.5px; color: var(--text-quiet);
  text-decoration: none;
}
.backlink:hover { color: var(--ember); }
.backlink::before { content: "\2190"; font-size: 14px; line-height: 1; }
.backlink:focus-visible { outline: 2px solid var(--focus-ring);
                          outline-offset: 2px; border-radius: 4px; }

/* ------------------------------------------------------------- the policies
   Read by people with no account and no context: a payment processor's
   reviewer, somebody deciding whether to sign up, occasionally a lawyer.
   Wide measure, generous leading, and nothing clever. */
.legalpage { max-width: 44rem; margin: 0 auto; padding: 40px 0 48px; }
.legalnav {
  display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 28px;
  font-size: 12.5px;
}
.legalnav a { color: var(--text-quiet); text-decoration: none; }
.legalnav a:hover { color: var(--ember); }
.legalnav a.here { color: var(--ink); font-weight: 600; }
.legalbody { margin-top: 26px; }
.legalbody h2 {
  font-size: 18px; font-weight: 650; margin: 34px 0 10px;
  padding-top: 18px; border-top: 1px solid var(--ink-25);
}
.legalbody h2:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.legalbody h3 { font-size: 15px; font-weight: 650; margin: 22px 0 8px; }
.legalbody p { margin: 0 0 13px; line-height: 1.72; }
.legalbody ul { margin: 0 0 15px; padding-left: 20px; }
.legalbody li { margin-bottom: 7px; line-height: 1.68; }
.legalbody code {
  font-family: var(--mono, ui-monospace, monospace); font-size: 13px;
}
.legalbody a { color: var(--ember); }

/* The footer link row, on every page. A processor checks that the policies
   are reachable from the site rather than only by their direct URL. */
.footlegal { display: inline-flex; gap: 10px; flex-wrap: wrap; }
.footlegal a { color: inherit; text-decoration: underline; }


/* ------------------------------------------------- the membership chip
 * In the sidebar foot, under the purse. Full width rather than a pill in
 * the stats row, because the thing it has to say -- "lapsed", "payment
 * overdue" -- does not fit in a pill and is the whole reason it exists.
 */
.memberchip {
  display: flex; flex-direction: column; gap: 1px;
  padding: 7px 11px; margin-bottom: 8px;
  border: var(--hairline) solid var(--line); border-radius: 10px;
  text-decoration: none; color: inherit;
  background: var(--panel-2, transparent);
}
.memberchip:hover { border-color: var(--text-quiet); }
.memberchip b { font-size: 13px; line-height: 1.2; }
.memberchip span {
  font: 500 10.5px var(--font-mono); letter-spacing: .02em;
  color: var(--text-quiet); text-transform: lowercase;
}
.memberchip-warn { border-color: var(--ember, #c2410c); }
.memberchip-warn span { color: var(--ember, #c2410c); }
/* Railed: the label is hidden with the rest of them, so show the tier
 * initial rather than an empty box. */
.railed .memberchip { align-items: center; padding: 7px 4px; }
.railed .memberchip span { display: none; }
.railed .memberchip b { font-size: 11px; }


/* ====================================================== the landing page
 * The previous version was a hero with one action followed by twenty-one
 * cards and a trademark notice. It never said what the platform was, and
 * anybody who read to the bottom had nowhere to go. These are the pieces
 * that fix that: a lede under the headline, a numbered sequence, price
 * cards with something to press, and a closing action.
 */
.topnav-link {
  font: 500 14px var(--font-sans); color: var(--text-quiet);
  text-decoration: none; padding: 8px 12px; border-radius: var(--r-pill);
}
.topnav-link:hover { color: var(--ink); background: var(--paper-2, transparent); }
@media (max-width: 720px) { .topnav-link { display: none; } }

.hero-lede {
  font-size: 18px; line-height: 1.55; max-width: 44rem;
  color: var(--ink);
}
.hero-actions {
  display: flex; gap: 12px; flex-wrap: wrap; align-items: center;
  margin-top: var(--space-4);
}
.hero .hero-note { margin-top: var(--space-3); font-size: 15px; }

.section-lede {
  max-width: 46rem; margin: calc(var(--space-3) * -1) 0 var(--space-4);
  color: var(--text-quiet); font-size: 15.5px; line-height: 1.55;
}

/* The numbered sequence. The number is a label, not decoration -- the
 * three steps happen in this order and the order is the point. */
.card.step { position: relative; padding-top: var(--space-5); }
.stepnum {
  font: 700 12px var(--font-mono); letter-spacing: .14em;
  color: var(--ember); display: block; margin-bottom: 6px;
}
.card.step h3 { margin: 0 0 8px; font-size: 18px; }

.card.compact { padding: var(--space-4); }
.card.compact h3 { margin: 10px 0 6px; font-size: 16px; }
.card.compact .sub { font-size: 13.5px; line-height: 1.5; }

/* Price cards: the action is the reason the card exists, so it is pinned
 * to the bottom and every card has one. Three inert price cards was the
 * complaint, and it was a fair one. */
.price-card { display: flex; flex-direction: column; }
.price-card h3 { margin: 0; font-size: 17px; }
.price {
  font: 700 30px/1.1 var(--font-display, var(--font-sans));
  margin: 6px 0 2px; font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.price-term { margin: 0 0 10px; font-size: 13px; }
.price-reach { flex: 1; font-size: 14px; line-height: 1.5; }
.price-cta { margin-top: var(--space-3); width: 100%; text-align: center; }
.cost-note { max-width: 46rem; margin-top: var(--space-4); }

.org-card {
  display: flex; gap: var(--space-5); align-items: flex-start;
  flex-wrap: wrap; max-width: 920px;
}
.org-card > div:first-child { flex: 1 1 24rem; }
.org-actions { display: flex; flex-direction: column; gap: 10px;
               flex: 0 0 auto; }
@media (max-width: 720px) { .org-actions { width: 100%; } }

.closer { margin-bottom: var(--space-7); }
.closer-card {
  text-align: center; padding: var(--space-7) var(--space-5);
  background: var(--ink); color: #F1F0ED; border-color: var(--ink);
}
.closer-card h2 {
  margin: 0 0 10px; color: #fff;
  font: 600 28px/1.2 var(--font-sans); letter-spacing: -0.01em;
}
.closer-card p { color: rgba(241,240,237,.78); max-width: 34rem;
                 margin: 0 auto; }
.closer-card .hero-actions { justify-content: center; }
.closer-card .btn.ghost {
  background: transparent; color: #F1F0ED;
  border-color: rgba(255,255,255,.3);
}
.closer-card .btn.ghost:hover { background: rgba(255,255,255,.08); }

/* Long unbroken tokens (data set names, glossary terms in error text) pushed
   /errors to 350px at a 320px viewport (6-width sweep, 2026-11-03). */
.main { overflow-wrap: break-word; }
.main .card { min-width: 0; overflow-wrap: anywhere; }

/* A small label above a group. Only `.archetype .kicker` existed, so the
   same class elsewhere (results "your next step", landing groups) rendered
   as plain body text. */
.kicker { font: 500 12px var(--font-mono); letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); }


/* ------------------------------------------------ sidebar, roomier menu
   On a laptop the fixed head and foot left the menu about eight items
   tall, and a super admin scrolled forty links through that slot (owner
   video, 3 Nov). The foot is now two rows: purse and level, then you and
   sign out side by side. */
.side-foot { gap: 6px; padding: var(--space-2) var(--space-3); }
.side-foot .statchip { padding: 5px 9px; }
.foot-row { display: flex; align-items: center; gap: 4px; }
.foot-row .userchip { flex: 1; min-width: 0; padding: 5px 6px; }
.foot-row .signout { padding: 8px; flex: none; }
.railed .foot-row { flex-direction: column; }
/* A shorter head as well: less air around the brand and search. */
.side-head { padding: var(--space-3) var(--space-4) var(--space-2); }
.searchtrigger, .side-plan { margin-bottom: var(--space-2); }

/* Hover is lighter than the current page and has no ember: with the same
   grey pill, hovering one item looked like a second selection. */
.navitem:hover { background: rgba(20,17,15,0.03); color: var(--ink); }
.navitem.active, .navitem.active:hover { background: var(--ember-tint); }
.navitem.active { box-shadow: inset 3px 0 0 var(--ember); }

/* --------------------------------------------- stacks catalogue and topic
   Topics are a catalogue; each topic page is a short, ordered list by level
   (owner video, 3 Nov). */
.tile-status { font: 500 13px var(--font-sans); color: var(--text-muted); }
.topic-list { list-style: none; margin: 12px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px; }
.topic-item { display: flex; gap: 12px; align-items: flex-start; text-decoration: none;
  padding: 10px 12px; border: var(--hairline) solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink); }
.topic-item:hover { border-color: var(--ink-25); background: rgba(20,17,15,0.02); }
.topic-item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.topic-tick { flex: none; width: 22px; height: 22px; border-radius: 50%;
  border: var(--hairline) solid var(--ink-25); display: inline-flex;
  align-items: center; justify-content: center; font-size: 13px; color: #fff; margin-top: 1px; }
.topic-item.done .topic-tick { background: var(--good-fg); border-color: var(--good-fg); }
.topic-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.topic-body b { font-size: 15px; font-weight: 600; }
.topic-meta { font: 400 13px var(--font-sans); color: var(--text-muted); }
.topic-more { margin-top: 8px; }
.topic-more > summary { cursor: pointer; font: 600 14px var(--font-sans); color: var(--ink);
  padding: 6px 0; list-style: none; }
.topic-more > summary::-webkit-details-marker { display: none; }
.topic-more > summary::before { content: "+ "; color: var(--text-muted); }
.topic-more[open] > summary::before { content: "− "; }
.topic-more > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 6px; }

/* What opens a locked band or paper, and the readiness line under each
   band (test hub, 3 Nov). The unlock sits at full contrast: it is the
   point of showing a locked option at all. */
.pick .unlock { display: flex; gap: 6px; align-items: baseline; margin-top: 8px;
  font: 500 14px var(--font-sans); color: var(--ink); }
.pick .unlock[hidden] { display: none; }
.pick .unlock .ico { width: 14px; height: 14px; flex: none; align-self: center; }
.pick .ready { display: block; margin-top: 6px; font: 400 13.5px var(--font-sans); color: var(--text-muted); }
.pick .ready.ok { color: var(--good-fg); }
.pick.locked > span .unlock, .pick.locked > span .ready { opacity: 1; }

/* ------------------------------------------------- spacing utilities
   Stand-ins for the inline margins templates used to carry (design audit
   P2-2). !important because each replaces a style="" that beat every
   selector; without it `.card h3` would win back the old margin. */
.mt-3 { margin-top: var(--space-3) !important; }
.mt-5 { margin-top: var(--space-5) !important; }
.stack-2 > * + * { margin-top: var(--space-2) !important; }
.stack-4 > * + * { margin-top: var(--space-4) !important; }
.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
