/* TabulaKit shared framework — baseline stylesheet.
   Served statically (public/) and linked from the Base layout, so it loads
   deterministically in both dev and build (no reliance on Astro's dev-mode CSS
   injection, which is fragile inside the embedded preview iframe). A project's
   customization/theme/theme.css is injected after this and overrides it.

   One cohesive palette drives two layouts:
     - portal  — top-nav, centered column, frosted header + footer (modeled on
                 tnman-2026-portal)
     - sidebar — left docs nav with a branded header (modeled on the Mojo docs
                 site, @mojo/static-site-ui)
   Both share a teal primary (links, active states, brand) and a warm accent
   (section headings), the common ground between the two references. Light is the
   default; the toggle (top-right) flips data-theme on <html> and the shell sets
   the initial value from the Kit's default-preview-theme (TABKIT_PREVIEW_THEME). */

:root {
  /* color-scheme paints the browser canvas to match, so page-to-page navigation
     never flashes the wrong background. */
  color-scheme: light;

  --bg: #ffffff;
  --fg: #1b2330;
  --muted: #5b6573;
  --border: #e5e8ec;

  --surface: #f7f8fa; /* sidebar / header fill */
  --surface-2: #eef1f5; /* hover */
  --surface-3: #e8edf2;

  --primary: #1f7078; /* teal — links, active nav, brand */
  --primary-strong: #195a60;
  --primary-soft: #e4f2f3; /* active-nav background */

  --heading: #c2410c; /* warm vermilion — h2 + section labels */

  --accent-dot: #ea580c; /* portal brand dot */

  --code-bg: #f5f6f8;
  --code-inline-bg: #eef1f5;

  --shadow-1: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.08);
  --radius: 10px;
  --radius-sm: 7px;
  --content-max: 820px;
  --portal-max: 900px;
  --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
    monospace;
}

/* Navy dark theme (tnman-flavored). Every preview ships light + dark. */
html[data-theme='dark'] {
  color-scheme: dark;

  --bg: #0b1220;
  --fg: #e7ecf5;
  --muted: #94a1ba;
  --border: #25304a;

  --surface: #121b30;
  --surface-2: #1a2742;
  --surface-3: #1c2742;

  --primary: #4fd1d9;
  --primary-strong: #6fe0e7;
  --primary-soft: rgba(79, 209, 217, 0.14);

  --heading: #fb8a3a;

  --accent-dot: #f97316;

  --code-bg: #0f1830;
  --code-inline-bg: #18233c;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 12px rgba(0, 0, 0, 0.25);
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg);
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  color: var(--fg);
  background: var(--bg);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

/* Astro's view-transition route announcer is for screen readers only — keep it
   functional but visually hidden so it doesn't add a scrollbar or show the page
   title at the bottom on navigation. */
.astro-route-announcer {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Typography -------------------------------------------------------------- */
article {
  max-width: var(--content-max);
}

article > :first-child {
  margin-top: 0;
}

h1,
h2,
h3,
h4 {
  line-height: 1.25;
  margin: 1.6em 0 0.6em;
  font-weight: 700;
}
h1 {
  font-size: 2rem;
  letter-spacing: -0.01em;
}
h2 {
  font-size: 1.4rem;
  color: var(--heading);
}
h3 {
  font-size: 1.15rem;
}
h4 {
  font-size: 1rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

p {
  margin: 0.85em 0;
}

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

ul,
ol {
  padding-left: 1.4em;
}
li + li {
  margin-top: 0.25em;
}

strong {
  font-weight: 650;
}

img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}

code {
  font-family: var(--font-mono);
  background: var(--code-inline-bg);
  padding: 0.12em 0.38em;
  border-radius: 4px;
  font-size: 0.88em;
}

pre {
  background: var(--code-bg);
  padding: 1rem 1.1rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  overflow: auto;
  line-height: 1.5;
}
pre code {
  background: none;
  padding: 0;
  font-size: 0.85em;
}

blockquote {
  margin: 1em 0;
  padding: 0.4em 1em;
  border-left: 3px solid var(--primary);
  background: var(--surface);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: var(--muted);
}
blockquote p {
  margin: 0.3em 0;
}

/* A content table scrolls inside its own box (#7809), the same rule .tkg-scroll
   follows below: `width: 100%` is a floor for a border-collapse table, not a
   ceiling, so a wide one used to widen the PAGE and take the header, the nav and
   the footer sideways with it on a phone. The wrapper is added at build time by
   rehype-scroll-tables.mjs, since markdown gives a table no element to hang this
   on. The table keeps its own margin; the wrapper carries none. */
.tk-table-scroll {
  overflow-x: auto;
  max-width: 100%;
}

table {
  border-collapse: collapse;
  width: 100%;
  margin: 1em 0;
  font-size: 0.95rem;
}
th,
td {
  border: 1px solid var(--border);
  padding: 0.5em 0.7em;
  text-align: left;
  vertical-align: top;
}
th {
  background: var(--surface-2);
  font-weight: 650;
  border-bottom: 2px solid var(--border);
}
/* Zebra striping for scanability on wide tables. */
tbody tr:nth-child(2n) td {
  background: var(--surface);
}

hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 2em 0;
}

/* --- Form fields ------------------------------------------------------------
   Themed inputs so forms in content never render as white-on-white on dark
   themes (the browser default when a page sets a dark background but leaves the
   native input unstyled). All palette-driven, so every theme covers forms for
   free. Checkbox/radio/range/color/file keep native chrome (just accent-color). */
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='color']):not([type='file']),
textarea,
select {
  font: inherit;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.5em 0.65em;
  max-width: 100%;
}
textarea {
  min-height: 6em;
}
input::placeholder,
textarea::placeholder {
  color: var(--muted);
  opacity: 0.75;
}
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-soft);
}
input[type='checkbox'],
input[type='radio'],
input[type='range'] {
  accent-color: var(--primary);
}

/* --- Small inline extras (themed for completeness) -------------------------- */
::selection {
  background: var(--primary-soft);
  color: var(--fg);
}
mark {
  background: var(--primary-soft);
  color: var(--fg);
  padding: 0 0.2em;
  border-radius: 3px;
}
kbd {
  font-family: var(--font-mono);
  font-size: 0.82em;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 0.05em 0.4em;
}

/* --- Light/dark toggle (fixed, both layouts) --------------------------------- */
#tk-theme-toggle {
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 50;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--muted);
  font-size: 0.95rem;
  cursor: pointer;
  line-height: 1;
  box-shadow: var(--shadow-1);
}
#tk-theme-toggle:hover {
  color: var(--fg);
  border-color: var(--primary);
}
/* Show the icon for the mode you'd switch TO. */
.tk-when-dark {
  display: none;
}
.tk-when-light {
  display: inline;
}
html[data-theme='dark'] .tk-when-dark {
  display: inline;
}
html[data-theme='dark'] .tk-when-light {
  display: none;
}

/* --- Theme picker (#7777) ----------------------------------------------------
   Drawn INSTEAD of the toggle above, on a site whose theme.json declares palettes.
   Same position, same chrome, one row taller in content: palette above, mode below.
   The toggle's own glyph-swap rules are reused rather than restated — they key on
   .tk-when-dark / .tk-when-light, which both controls carry. */
.tk-theme {
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 50;
}
#tk-theme-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 2rem;
  padding: 0 0.5rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--muted);
  font: inherit;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-1);
}
#tk-theme-trigger:hover {
  color: var(--fg);
  border-color: var(--primary);
}
/* The palette dot: the palette's canvas and its accent, split on the diagonal — the
   same two values the app's picker draws, so one look reads the same on both surfaces. */
.tk-theme-chip {
  width: 0.875rem;
  height: 0.875rem;
  flex: none;
  border-radius: 3px;
  display: inline-block;
  background: linear-gradient(
    135deg,
    var(--tk-chip-ground, var(--bg)) 0 55%,
    var(--tk-chip-accent, var(--primary)) 55% 100%
  );
  box-shadow: inset 0 0 0 1px rgb(128 128 128 / 0.35);
}
.tk-theme-menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  width: 12.5rem;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow-2, var(--shadow-1));
}
.tk-theme-menu[hidden] {
  display: none;
}
.tk-theme-head {
  padding: 0.125rem 0.5rem 0.5rem;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.tk-theme-menu [data-palette-pick] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  height: 2rem;
  padding: 0 0.5rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}
.tk-theme-menu [data-palette-pick]:hover,
.tk-theme-menu [data-mode-pick]:hover {
  background: var(--surface-2, var(--primary-soft));
}
.tk-theme-menu [aria-checked='true'] {
  background: var(--primary-soft);
  color: var(--primary-strong, var(--fg));
}
.tk-theme-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tk-theme-rule {
  height: 1px;
  margin: 0.375rem 0;
  background: var(--border);
}
/* Mode is one axis with two values, so it is a segmented pair rather than two more
   rows — drawn as a list it would read as two more palettes. */
.tk-theme-modes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
}
.tk-theme-menu [data-mode-pick] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  height: 1.875rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}

/* ============================================================================
   SIDEBAR layout — docs (modeled on the Mojo docs site)
   ============================================================================ */
.layout {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  min-height: 100vh;
}

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--surface);
}

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  height: 56px;
  flex-shrink: 0;
  padding: 0 1.1rem;
  border-bottom: 1px solid var(--border);
}
/* In-drawer close (X). Hidden on wide screens; shown in the mobile media query. */
.sidebar-close {
  display: none;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
}
.sidebar-close svg {
  width: 1.1rem;
  height: 1.1rem;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-width: 0;
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.2px;
  color: var(--primary-strong);
  text-decoration: none;
}
.sidebar-brand > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-brand:hover {
  text-decoration: none;
}

/* Optional site icon (manifest `icon`) beside the brand, in both layouts. */
.brand-icon {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  object-fit: contain;
  border-radius: 50%;
}

.sidebar nav {
  min-height: 0;
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0.9rem 0.75rem 1.5rem;
}

/* Thin, themed scrollbars across the rendered site (#981) — thumb in the
   heading color (brand chrome) over a subtle --surface-2 track, so both the
   foreground and background read as themed rather than browser-default. The
   portal nav opts out below (scrollbar-width: none) and keeps winning on
   specificity. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--heading) var(--surface-2);
}
*::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
*::-webkit-scrollbar-thumb {
  background-color: var(--heading);
  border-radius: 3px;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: var(--primary);
}
*::-webkit-scrollbar-track {
  background: var(--surface-2);
}
*::-webkit-scrollbar-corner {
  background: var(--surface-2);
}

/* Thin sidebar scrollbar, matching the docs reference. */
.sidebar nav::-webkit-scrollbar {
  width: 6px;
}
.sidebar nav::-webkit-scrollbar-thumb {
  background-color: var(--heading);
  border-radius: 3px;
}
.sidebar nav {
  scrollbar-width: thin;
}

/* Nav links — used for both <a> pages and <button> folder labels. */
.sidebar nav .nav-link {
  display: block;
  width: 100%;
  padding: 0.4rem 0.6rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.92rem;
  line-height: 1.4;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.sidebar nav .nav-link:hover {
  background: var(--surface-2);
  color: var(--fg);
  text-decoration: none;
}
.sidebar nav .nav-link.active {
  background: var(--primary-soft);
  color: var(--primary-strong);
  font-weight: 600;
}

/* Section header: a static, uppercase label (the COMPANY / PRODUCTS pattern). */
.nav-section {
  margin-top: 1.25rem;
}
.nav-section:first-child {
  margin-top: 0.25rem;
}
.nav-section-label {
  padding: 0 0.6rem;
  margin-bottom: 0.35rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--heading);
}

/* Collapsible group: a row (link or folder label + chevron) and its children. */
.nav-row {
  display: flex;
  align-items: center;
  gap: 0.1rem;
}
.nav-row .nav-link {
  flex: 1;
  min-width: 0;
}
.nav-expander {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.nav-expander:hover {
  background: var(--surface-2);
  color: var(--fg);
}
.nav-expander svg {
  width: 1rem;
  height: 1rem;
  transition: transform 0.15s ease;
}
.nav-group.open > .nav-row > .nav-expander svg {
  transform: rotate(90deg);
}
.nav-children {
  display: none;
  margin-left: 0.7rem;
  padding-left: 0.4rem;
  border-left: 1px solid var(--border);
}
.nav-group.open > .nav-children {
  display: block;
}

.content {
  padding: 2.75rem 2.5rem 5rem;
  min-width: 0;
}
.content article {
  margin: 0 auto;
}
/* Docs layout leads with a warm page title, like the reference Handbook. */
.content h1 {
  color: var(--heading);
}

/* Hamburger + scrim: only meaningful on small screens (rules in the media query
   below). The sidebar is static and always visible on wide screens. */
.sidebar-toggle {
  display: none;
  position: fixed;
  top: 0.6rem;
  left: 0.6rem;
  z-index: 61;
  width: 2.25rem;
  height: 2.25rem;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
  box-shadow: var(--shadow-1);
}
.sidebar-toggle svg {
  width: 1.25rem;
  height: 1.25rem;
}
.sidebar-scrim {
  display: none;
}

/* ============================================================================
   PORTAL layout — top nav (modeled on tnman-2026-portal)
   ============================================================================ */
.portal {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.portal-header {
  position: sticky;
  top: 0;
  z-index: 30;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  -webkit-backdrop-filter: saturate(150%) blur(10px);
}

.portal-header-inner {
  max-width: var(--portal-max);
  margin: 0 auto;
  padding: 0.8rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.portal-brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.3px;
  font-size: 1.05rem;
  color: var(--fg);
  text-decoration: none;
  white-space: nowrap;
}
.portal-brand:hover {
  text-decoration: none;
}
.portal-brand .dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--accent-dot);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-dot) 24%, transparent);
  flex-shrink: 0;
}

/* Nav is its own full-width band below the brand header (tnman-style), not
   inline in the header. */
.portal-nav {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.portal-nav-inner {
  max-width: var(--portal-max);
  margin: 0 auto;
  padding: 0.4rem 1.5rem;
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.portal-nav-inner::-webkit-scrollbar {
  display: none;
}
/* DIRECT CHILDREN ONLY (#7809). These are the styles of a top-level TAB, and they
   were written as "every `a` inside the nav" when every link in the nav was one.
   A dropdown panel lives inside this same band, so the descendant form silently
   restyled all of its entries — nowrap, tab padding, muted colour — which is
   exactly what it did to the AT 2027 portal's thirteen-entry panel when these
   components went in by hand. The child combinator is the whole fix, and it is the
   rule every adopting site's own nav CSS needs too. */
.portal-nav-inner > a {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.95rem;
  border-radius: 8px;
  color: var(--muted);
  font-weight: 600;
  font-size: 0.95rem;
  white-space: nowrap;
  text-decoration: none;
  transition: background 0.12s ease, color 0.12s ease;
}
.portal-nav-inner > a:hover {
  background: var(--surface-2);
  color: var(--fg);
  text-decoration: none;
}
.portal-nav-inner > a.active {
  background: var(--surface-2);
  color: var(--fg);
}

.portal-main {
  flex: 1;
  width: 100%;
  max-width: var(--portal-max);
  margin: 0 auto;
  padding: 2.5rem 1.5rem 5rem;
  font-size: 1.0625rem; /* 17px — the reference reads a touch larger */
}
.portal-main article {
  max-width: none;
}

/* Portal headings follow the tnman palette: warm-orange sections and teal
   sub-headings. */
.portal h2 {
  color: var(--accent-dot);
}
.portal h3 {
  color: var(--primary);
}

.portal-footer {
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.85rem;
  text-align: center;
  padding: 1.1rem 1.5rem;
}

/* ============================================================================
   HYBRID layout — portal-style top nav of the top-level sections + a secondary
   sidebar of the active section's sub-pages. Reuses .portal-header/.portal-nav
   for the top bar and .sidebar/.content below. The top bar is non-sticky here so
   the sticky secondary sidebar owns the viewport height without offset math.
   ============================================================================ */
.hybrid {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.hybrid .portal-header,
.hybrid .portal-nav {
  position: static;
}
.hybrid-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
}
/* No active section (e.g. home / a top-level page) — content spans full width. */
.hybrid-body.no-secondary {
  grid-template-columns: minmax(0, 1fr);
}

/* --- Responsive -------------------------------------------------------------
   ONE BREAKPOINT, 640px (#7809) — the TabulaKit Design System's --breakpoint-phone.
   It was 720px, which put tablets on the phone layout and, more to the point, left
   the framework disagreeing with the phone chrome below (which keys on 640px). Two
   breakpoints a few pixels apart is how a width ends up with neither a nav band nor
   a menu button, so there is now one number and both sides read it. */
@media (max-width: 640px) {
  /* Docs sidebar becomes a slide-in drawer, closed by default. The hamburger
     opens it; the scrim (or following a link) closes it. */
  .layout {
    grid-template-columns: 1fr;
  }
  .sidebar-toggle {
    display: inline-flex;
  }
  /* When the drawer is open the in-drawer X is the control; hide the hamburger. */
  .layout.sidebar-open .sidebar-toggle {
    display: none;
  }
  .sidebar-close {
    display: inline-flex;
  }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: 280px;
    max-width: 82vw;
    z-index: 60;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: var(--shadow-1);
  }
  .layout.sidebar-open .sidebar {
    transform: none;
  }
  .layout.sidebar-open .sidebar-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(0, 0, 0, 0.45);
  }
  /* Make room for the fixed hamburger above the page title. */
  .content {
    padding: 3.5rem 1.25rem 4rem;
  }

  .portal-header-inner {
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
  }
  /* THE PORTAL HEADER BAR NEVER WRAPS (#7809). It now carries the drawer's menu
     button, and a wrapped two-row header would push the nav band and the content
     down the screen on the layout that has least of it. The title truncates
     instead.

     THE RIGHT PADDING IS THE THEME CONTROL'S FOOTPRINT, and there are two controls,
     not one. Both are position:fixed at 0.75rem from the edge, so neither is in the
     flow and neither pushes the title: the header has to keep the room by hand or a
     long title runs underneath. #tk-theme-toggle is the 2rem square a site gets by
     default. #tk-theme is the palette picker a site gets by DECLARING PALETTES, and
     it is wider — 1rem padding + a 0.875rem chip + a 0.375rem gap + the glyph +
     borders, about 3.5rem — which is the case that matters, because a design system
     shipping palettes is exactly the kind that ships a long site title too. The
     selector asks which control is on the page rather than reserving one number for
     both. Keep each value in step with the control it names above. */
  .portal .portal-header-inner {
    flex-wrap: nowrap;
    gap: 0.6rem;
    padding-right: calc(0.75rem + 2rem + 0.5rem);
  }
  body:has(#tk-theme) .portal .portal-header-inner {
    padding-right: calc(0.75rem + 3.5rem + 0.5rem);
  }
  .portal .portal-brand {
    min-width: 0;
  }
  .portal .portal-brand > span {
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .portal-main {
    padding: 1.75rem 1.25rem 4rem;
  }

  /* Hybrid: the secondary sidebar becomes a slide-in drawer (same pattern as the
     docs sidebar); the top nav stacks above the content. The hamburger sits on
     the right so it clears the brand on the left. */
  .hybrid-body {
    grid-template-columns: 1fr;
  }
  .hybrid .sidebar-toggle {
    left: auto;
    right: 0.6rem;
  }
  .hybrid.sidebar-open .sidebar-toggle {
    display: none;
  }
  .hybrid .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: 280px;
    max-width: 82vw;
    z-index: 60;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: var(--shadow-1);
  }
  .hybrid.sidebar-open .sidebar {
    transform: none;
  }
  .hybrid.sidebar-open .sidebar-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(0, 0, 0, 0.45);
  }
}

/* ── Timeline / Gantt embed (tabkit-gantt.js, #1012) ──────────────────────────
   Lives here rather than in its own stylesheet so a page that drops in the
   script gets the styling with no second <link> to remember. Every color comes
   from the palette variables above, so light and dark both work with no extra
   rules — only the status hues are defined per theme. */
.tkg {
  margin: 1.25rem 0;
  font-family: var(--font-sans);
  color: var(--fg);
}
.tkg-title {
  font-weight: 600;
  margin-bottom: 0.5rem;
}
/* The chart scrolls inside ITS OWN container — the page body never scrolls
   sideways because a timeline is wide. */
.tkg-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 4px 0;
}
.tkg-svg {
  display: block;
}
.tkg-foot,
.tkg-loading,
.tkg-empty,
.tkg-signin,
.tkg-error {
  font-size: 0.875rem;
  color: var(--muted);
}
.tkg-foot {
  margin-top: 0.4rem;
}
.tkg-loading,
.tkg-empty,
.tkg-signin,
.tkg-error {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 0.75rem 1rem;
}
.tkg-error {
  border-color: #d9534f;
}
.tkg-signin a {
  color: var(--primary);
}

.tkg-grid {
  stroke: var(--border);
  stroke-width: 1;
}
.tkg-axis,
.tkg-label,
.tkg-group,
.tkg-today-label {
  font-family: var(--font-sans);
  fill: var(--muted);
  font-size: 11px;
}
.tkg-group {
  fill: var(--heading);
  font-weight: 600;
  font-size: 12px;
}
.tkg-label {
  fill: var(--fg);
}
.tkg-today {
  stroke: var(--accent-dot);
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
}
.tkg-today-label {
  fill: var(--accent-dot);
}
.tkg-row:hover .tkg-bar {
  opacity: 0.85;
}
/* The actual-duration overlay: deliberately quiet, so it reads as an annotation
   under the planned bar rather than competing with it. */
.tkg-actual {
  fill: var(--fg);
  opacity: 0.4;
}

/* Status-derived bar color (#1100). The VALUES are not here: --tk-status-*-bar is
   emitted into the page <head> by Base.astro from shared/status-palette.mjs — the
   one authored source the shell's task badges read too — and a kit or project
   overrides it per token from its customization/theme/theme.css, which is injected
   after. Adding a status means adding it to the palette and to STATUS_CLASS in
   tabkit-gantt.js; do not reintroduce literal hexes in this file. */
.tkg-bar {
  fill: var(--tk-status-open-bar);
}
.tkg-bar.tkg-open {
  fill: var(--tk-status-open-bar);
}
.tkg-bar.tkg-done {
  fill: var(--tk-status-done-bar);
}
.tkg-bar.tkg-review {
  fill: var(--tk-status-review-bar);
}
.tkg-bar.tkg-active {
  fill: var(--tk-status-active-bar);
}
.tkg-bar.tkg-pending {
  fill: var(--tk-status-pending-bar);
}
.tkg-bar.tkg-backlog {
  fill: var(--tk-status-backlog-bar);
}
.tkg-bar.tkg-triage {
  fill: var(--tk-status-triage-bar);
}

/* ============================================================================
   TOP-NAV SUB-NAVIGATION — the dropdown (#7809)
   Vendored from the TNSG design package, shared/nav-menu.css (v1, 2026-09-23).
   Tokens only, so every theme in the cascade gets it without a new variable.

   CHANGES MADE WHILE VENDORING, and why each one:
   - The source also targets `.app-nav`, the AT 2027 portal's own band. Nothing in
     this framework renders that class, so those selectors are dropped rather than
     shipped as rules no page can match.
   - The band-level rules are scoped to `.portal`. The hybrid layout reuses
     `.portal-nav` for its top bar and has no dropdowns in it; the spec's own check
     is that hybrid is unchanged, and unscoped these would have changed how its band
     wraps at every width.
   - The panel takes a max-height and scrolls. A section with thirteen entries is
     not hypothetical — that is the TNSG3R staff section — and without this the
     panel runs off the bottom of the viewport with no way to reach the end.

   Markup: a top-level item with children is a .nav-menu — a trigger button plus a
   panel. The trigger never navigates; a parent with its own page gets it as the
   panel's first link, labelled "Overview". The caret is drawn in CSS, so there is
   no icon font to load. Behaviour is the inline script in Base.astro's <head>. */

.portal .portal-nav { position: relative; }

.nav-menu { position: relative; flex: 0 0 auto; display: flex; }

.nav-menu-trigger {
  display: inline-flex; align-items: center; gap: 0.5em;
  padding: 0.5rem 0.95rem; border: 0; border-radius: 8px; background: none;
  color: var(--muted); font: inherit; font-weight: 600; font-size: 0.95rem;
  white-space: nowrap; cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.nav-menu-trigger::after {
  content: ''; width: 0.4em; height: 0.4em; margin-top: -0.2em;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform 0.15s ease, margin 0.15s ease;
}
.nav-menu-trigger:hover,
.nav-menu-trigger.active,
.nav-menu.open > .nav-menu-trigger { background: var(--surface-2); color: var(--fg); }
.nav-menu.open > .nav-menu-trigger::after { transform: rotate(225deg); margin-top: 0.2em; }
.nav-menu-trigger:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.nav-menu-panel {
  display: none; position: absolute; z-index: 40;
  top: calc(100% + 6px); left: 0;
  min-width: 240px; max-width: 320px; padding: 6px;
  flex-direction: column; gap: 2px;
  /* A long section scrolls inside its own panel; the page never grows a scrollbar
     because a nav menu is tall. Contained overscroll keeps the wheel in the panel. */
  max-height: min(70vh, 520px); overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-2, var(--shadow-1));
}
.nav-menu.open > .nav-menu-panel { display: flex; }
.nav-menu:last-child > .nav-menu-panel { left: auto; right: 0; }

.nav-menu-panel a {
  display: flex; flex-direction: column; gap: 1px;
  padding: 0.55rem 0.7rem; border-radius: var(--radius-sm);
  color: var(--fg); font-weight: 600; font-size: 0.92rem; line-height: 1.35;
  white-space: normal; text-decoration: none;
}
.nav-menu-panel a small { color: var(--muted); font-weight: 400; font-size: 0.8rem; }
.nav-menu-panel a:hover { background: var(--surface-2); text-decoration: none; }
.nav-menu-panel a.active { background: var(--primary-soft); color: var(--primary-strong); }
.nav-menu-panel a:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.nav-menu-panel hr { border: 0; border-top: 1px solid var(--border); margin: 4px 6px; }

/* Wide: the band stops scrolling sideways and wraps instead — a scroller clips an
   absolutely positioned panel, so the two cannot coexist. */
@media (min-width: 641px) {
  .portal .portal-nav-inner { overflow: visible; flex-wrap: wrap; }
}
/* Narrow, with no drawer present: the band keeps its sideways scroll, so the panel
   anchors to the whole band instead of its trigger and drops as a full-width sheet
   underneath it, out of the scroller's reach. In THIS framework the portal band is
   hidden at these widths and the drawer takes over, so this is the fallback for a
   page that loads the dropdown styles without the phone chrome. */
@media (max-width: 640px) {
  .nav-menu { position: static; }
  .nav-menu-panel,
  .nav-menu:last-child > .nav-menu-panel {
    left: 8px; right: 8px; top: calc(100% + 4px); max-width: none; min-width: 0;
  }
  .nav-menu-panel a { padding: 0.75rem 0.8rem; }
}

/* ============================================================================
   PHONE NAVIGATION — the drawer and the quick-access bar (#7809)
   Vendored from the TNSG design package, shared/mobile-nav.css (v1, 2026-09-24).

   TAKE THIS VERSION AND NOT AN EARLIER COPY. The first release reserved 60px of
   body padding for a bar that is 61px tall, so a page's footer sat one pixel under
   it. The derivation of the number is in the comment beside it, next to the rule
   it has to agree with, and there is a test asserting the two match.

   CHANGES MADE WHILE VENDORING:
   - `.app-nav` selectors dropped; nothing here renders that class.
   - THE BAND HIDE IS SCOPED TO `.portal`. The source hides `.portal-nav` outright,
     and the hybrid layout reuses that same class for its top bar while having no
     drawer to replace it — so unscoped, this rule left every hybrid site on a phone
     with no navigation at all. That is precisely the "no width strands a user"
     check, and it fails at 640px rather than at some odd width.
   - `--eyebrow` and `--font-heading` are TNSG theme tokens the framework does not
     define. They are read with a fallback to `--muted` and `--font-sans`, so a site
     with no TNSG theme gets the framework's own type rather than the browser's.

   At 640px and below: the portal's nav band is hidden and .mnav-toggle appears;
   .mnav is the drawer, carrying every nav item with sub-pages grouped under their
   parent; .qbar, when the site declares quickNav, is a fixed bottom bar of 2-5
   pages. Above 640px none of it renders. */

.mnav-toggle, .mnav, .mnav-scrim, .qbar { display: none; }

@media (max-width: 640px) {
  .portal .portal-nav { display: none; }

  .mnav-toggle {
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    width: 44px; height: 44px; margin-left: -8px; padding: 0;
    border: 0; border-radius: var(--radius-sm); background: none;
    color: var(--fg); font-size: 24px; cursor: pointer;
  }
  .mnav-toggle:hover { background: var(--surface-2); }
  .mnav-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

  .mnav-scrim {
    display: block; position: fixed; inset: 0; z-index: 70;
    background: var(--scrim-strong, rgba(0, 0, 0, 0.5));
    opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
  }
  html.mnav-open .mnav-scrim { opacity: 1; pointer-events: auto; }

  .mnav {
    display: flex; flex-direction: column;
    position: fixed; inset: 0 auto 0 0; z-index: 71;
    width: 300px; max-width: 84vw;
    background: var(--surface); border-right: 1px solid var(--border);
    box-shadow: var(--shadow-sheet, var(--shadow-2, var(--shadow-1)));
    transform: translateX(-100%); visibility: hidden;
    transition: transform 0.2s ease, visibility 0s linear 0.2s;
  }
  html.mnav-open .mnav { transform: none; visibility: visible; transition: transform 0.2s ease, visibility 0s; }
  html.mnav-open, html.mnav-open body { overflow: hidden; }

  /* 61px = 1px border-top + 4px + 52px item + 4px. Keep in sync with .qbar below. */
  body:has(.qbar) { padding-bottom: calc(61px + env(safe-area-inset-bottom)); }
  .qbar {
    display: flex; justify-content: space-around; gap: 2px;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
    padding: 4px 6px calc(4px + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    backdrop-filter: saturate(140%) blur(10px); -webkit-backdrop-filter: saturate(140%) blur(10px);
    border-top: 1px solid var(--border);
  }
}

/* Drawer internals */
.mnav-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 60px; padding: 0 6px 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.mnav-brand {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  color: var(--fg); text-decoration: none;
  font-family: var(--font-heading, var(--font-sans)); font-weight: 600; font-size: 1.05rem;
  letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap;
}
.mnav-brand:hover { text-decoration: none; }
.mnav-brand img { width: 26px; height: 26px; flex-shrink: 0; }
.mnav-brand span { overflow: hidden; text-overflow: ellipsis; }
.mnav-brand small { color: var(--muted); font-size: 0.72rem; font-weight: 500; letter-spacing: 0.1em; }
.mnav-close {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--muted); font-size: 22px; cursor: pointer;
}
.mnav-close:hover { background: var(--surface-2); color: var(--fg); }

.mnav-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; padding: 10px 10px 16px; }
.mnav-link {
  display: flex; align-items: center; width: 100%; min-height: 44px; padding: 0 12px;
  border: 0; border-radius: var(--radius-sm); background: none; text-align: left;
  color: var(--fg); font: inherit; font-weight: 600; font-size: 1rem; text-decoration: none; cursor: pointer;
}
.mnav-link:hover { background: var(--surface-2); text-decoration: none; }
.mnav-link.active { background: var(--primary-soft); color: var(--primary-strong); }
.mnav-link:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.mnav-group { display: flex; flex-direction: column; gap: 2px; margin-top: 14px; }
.mnav-group-label {
  padding: 0 12px 4px;
  font-family: var(--font-heading, var(--font-sans)); font-weight: 500; font-size: 0.72rem;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--eyebrow, var(--muted));
}
/* A left rule squares its left corners. */
.mnav-group .mnav-link {
  margin-left: 12px; width: calc(100% - 12px); padding-left: 14px; font-weight: 500;
  border-left: 1px solid var(--border); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.mnav-group .mnav-link.active { border-left-color: var(--primary); }
.mnav-foot {
  flex-shrink: 0; display: flex; flex-direction: column; gap: 6px;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border); font-size: 0.88rem; color: var(--muted);
}

/* Bottom bar internals */
.qbar-item {
  flex: 1 1 0; min-width: 0; max-width: 120px; min-height: 52px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  padding: 0 4px; border: 0; border-radius: var(--radius-sm); background: none; cursor: pointer;
  color: var(--muted); text-decoration: none;
  font-family: var(--font-heading, var(--font-sans)); font-weight: 500; font-size: 0.7rem;
  letter-spacing: 0.06em; text-transform: uppercase;
}
.qbar-item span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qbar-item i { font-size: 22px; }
.qbar-item:hover { color: var(--fg); text-decoration: none; }
.qbar-item.active { color: var(--primary-strong); }
.qbar-item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

/* Reduced motion: no slide. A short fade still says where the drawer came from. */
@media (prefers-reduced-motion: reduce) {
  .mnav { transform: none; opacity: 0; transition: opacity 0.12s ease, visibility 0s linear 0.12s; }
  html.mnav-open .mnav { opacity: 1; transition: opacity 0.12s ease, visibility 0s; }
  .mnav-scrim { transition: opacity 0.12s ease; }
}

/* ============================================================================
   Practice quiz (#8555) — a ```quiz block, run in the page by tabkit-quiz.js.
   Built from the palette above, so a course theme recolors it with everything
   else; right and wrong are a fixed green and red mixed into the theme's own
   surface, which reads in light and dark alike.
   ============================================================================ */
.tk-quiz {
  --tk-quiz-right: #2e7d32;
  --tk-quiz-wrong: #b3261e;
  margin: 1.5rem 0;
  padding: 1.25rem 1.25rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-1);
}
html[data-theme='dark'] .tk-quiz {
  --tk-quiz-right: #66bb6a;
  --tk-quiz-wrong: #ef7a72;
}
.tk-quiz-title {
  margin: 0 0 0.35rem;
}
.tk-quiz-sub,
.tk-quiz-saving,
.tk-quiz-empty,
.tk-quiz-fallback {
  color: var(--muted);
  margin: 0.25rem 0;
}
.tk-quiz-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 1rem 0 0.25rem;
}
.tk-quiz-btn {
  font: inherit;
  font-weight: 600;
  padding: 0.55rem 1.2rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
}
.tk-quiz-btn:hover {
  background: var(--surface-2);
}
.tk-quiz-btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.tk-quiz-btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
.tk-quiz-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--bg);
}
.tk-quiz-primary:hover {
  background: var(--primary-strong);
}
.tk-quiz-small {
  padding: 0.2rem 0.6rem;
  font-weight: 500;
  font-size: 0.875em;
}
.tk-quiz-questions {
  padding-left: 1.4rem;
  margin: 1rem 0 0;
}
.tk-quiz-q {
  margin: 0 0 1rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
}
.tk-quiz-q.is-right {
  border-color: var(--tk-quiz-right);
  background: color-mix(in srgb, var(--tk-quiz-right) 8%, var(--bg));
}
.tk-quiz-q.is-wrong {
  border-color: var(--tk-quiz-wrong);
  background: color-mix(in srgb, var(--tk-quiz-wrong) 8%, var(--bg));
}
.tk-quiz-q-text {
  font-weight: 600;
  margin: 0 0 0.5rem;
}
.tk-quiz-topic {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0 0.5rem;
  border-radius: 999px;
  background: var(--primary-soft);
  color: var(--primary-strong);
  font-size: 0.75em;
  font-weight: 500;
  vertical-align: middle;
}
.tk-quiz-options {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}
.tk-quiz-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.tk-quiz-option {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tk-quiz-option:hover {
  background: var(--surface-2);
}
.tk-quiz-option input {
  margin-top: 0.3rem;
  flex-shrink: 0;
}
.tk-quiz-option.is-answer {
  font-weight: 600;
  background: color-mix(in srgb, var(--tk-quiz-right) 16%, var(--bg));
}
.tk-quiz-option.is-chosen {
  text-decoration: line-through;
  background: color-mix(in srgb, var(--tk-quiz-wrong) 14%, var(--bg));
}
.tk-quiz-feedback:not(:empty) {
  margin-top: 0.6rem;
  padding: 0.5rem 0.8rem;
  border-left: 3px solid var(--border);
  font-size: 0.95em;
}
.is-right > .tk-quiz-feedback {
  border-left-color: var(--tk-quiz-right);
}
.is-wrong > .tk-quiz-feedback {
  border-left-color: var(--tk-quiz-wrong);
}
.tk-quiz-feedback p {
  margin: 0.3rem 0;
}
.tk-quiz-score {
  margin: 1rem 0;
  padding: 0.9rem 1rem;
  border: 2px solid var(--primary);
  border-radius: var(--radius-sm);
  background: var(--bg);
}
.tk-quiz-result {
  font-size: 1.2em;
  font-weight: 700;
  margin: 0 0 0.25rem;
}
.tk-quiz-status,
.tk-quiz-missing {
  margin: 0.25rem 0;
}
.tk-quiz-missing,
.tk-quiz-error {
  color: var(--tk-quiz-wrong);
}
.tk-quiz-problem {
  margin: 0.75rem 0;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--tk-quiz-wrong, #b3261e);
  border-radius: var(--radius-sm);
}
.tk-quiz-history {
  margin-top: 1.25rem;
}
.tk-quiz-history-title {
  margin: 1rem 0 0.4rem;
}
.tk-quiz-table-wrap {
  overflow-x: auto;
}
.tk-quiz-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.925em;
}
.tk-quiz-table th,
.tk-quiz-table td {
  text-align: left;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.tk-quiz-review {
  margin: 0.25rem 0;
  padding-left: 1.2rem;
}
.tk-quiz-review li {
  margin: 0 0 0.6rem;
  padding-left: 0.5rem;
  border-left: 3px solid var(--border);
}
.tk-quiz-review li.is-right {
  border-left-color: var(--tk-quiz-right);
}
.tk-quiz-review li.is-wrong {
  border-left-color: var(--tk-quiz-wrong);
}
.tk-quiz-review p {
  margin: 0.15rem 0;
}
