/**
 * Proffer Studio design tokens — dark.
 *
 * Reworked from the earlier light-theme version to match the loading screen's
 * near-black field. Every value is named once here so a future mobile app
 * consumes the same tokens rather than a designer eyeballing a screenshot.
 * No page should contain a raw hex code.
 */

/* ===========================================================================
   THEME
   Dark is the default. Light is opt-in via data-theme="light" on <html>, so a
   first paint is never a white flash for the majority case. Both themes define
   the same token names — nothing downstream needs to know which is active.
   =========================================================================== */

:root, :root[data-theme="dark"] {
  color-scheme: dark;
  /* ---- Color ---- */
  --bg:           #0A0A0C;  /* page field — near black, not pure, easier on the eye */
  --bg-raised:    #131317;  /* cards, elevated surfaces */
  --bg-inset:     #08080A;  /* recessed wells, video letterbox */
  --paper:        #F2F0EB;  /* primary text — warm off-white, not stark white */
  --graphite:     #8A8A8C;  /* secondary text — neutral gray */
  --graphite-dim: #7C7C7E;  /* tertiary — 4.7:1 on --bg, clears WCAG AA */
  --rule:         #232329;  /* hairlines, borders */
  --rule-bright:  #34343C;  /* hover borders */
  --silver:       #D8DAE0;  /* particle light at full density */
  --silver-dim:   #85858A;  /* particle light scattered — neutral gray */

  /* Glass. Values tuned for a dark field: a light film over darkness. */
  --glass-fill:   rgba(255,255,255,.055);
  --glass-edge:   rgba(255,255,255,.11);
  /* Nav sits over changing content, so it needs real opacity, not card alpha. */
  --nav-fill:     rgba(18,18,21,.88);
  /* The pill's outline. Deliberately faint: it is a seam, not a frame. At .18
     it read as a hard hairline along the top edge — the light-on-dark sliver —
     because that edge is where the outline meets the page rather than the
     nav's own shadow. Definition comes from the fill and the drop shadow; the
     border only needs to stop the shape dissolving. */
  --nav-edge:     rgba(255,255,255,.10);
  --nav-ink:      rgba(242,240,235,.78);   /* rest-state label ink — the pill was
                                              reading as unclear in both themes */
  --nav-ink-hi:   #F2F0EB;
  --glass-spec:   rgba(255,255,255,.16);   /* the top-edge highlight */
  /* The pointer glow, deliberately separate from --glass-spec. The edge
     highlight is white in both themes because it models light landing on a
     lip. The glow models light gathering under the cursor, so on a light
     surface it has to DARKEN — a white glow on white is nothing at all. */
  --glass-glow:   rgba(255,255,255,.16);
  --glass-blur:   18px;
  --glass-sat:    150%;
  --marker:       #D8DAE0;  /* accent is silver, not blue — dark mode is monochrome */
  --marker-deep:  #2A2B31;  /* accent fill — neutral, no hue */
  --warn:         #E0A26B;
  --danger:       #E8776B;   /* form errors — was hardcoded in four pages */
  --ok:           #7FBF95;

  /* ---- Type ---- */
  --font-display: 'Instrument Serif', Georgia, serif;
  --font-ui:      'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-annot:   'IBM Plex Mono', ui-monospace, monospace;

  --text-xs:   11px;
  --text-sm:   13px;
  --text-base: 16px;
  --text-lg:   20px;
  --text-xl:   28px;
  --text-2xl:  clamp(34px, 5vw, 52px);
  --text-3xl:  clamp(44px, 8vw, 92px);

  /* ---- Spacing (4px base) ---- */
  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;
  --space-4: 16px;  --space-5: 24px;  --space-6: 32px;
  --space-7: 48px;  --space-8: 72px;  --space-9: 112px;
  --space-10: 160px;

  /* ---- Radius / borders ---- */
  /* Softer throughout. 3px read as a hard corner on anything larger than a
     badge; these keep the drawing-office precision while removing the edge. */
  --radius-sm: 6px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-xl: 28px;     /* large panels and cards */
  --border-hairline: 1px solid var(--rule);

  /* ---- Layout ---- */
  --content-max: 1180px;
  --nav-height: 68px;

  /* ---- Motion ---- */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --dur-fast: 180ms;
  --dur-base: 320ms;
  --dur-slow: 640ms;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--paper);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: 1.6;
}

a { color: inherit; text-decoration: none; }

::selection { background: var(--marker-deep); color: var(--paper); }

/* ---- Shared primitives ---- */

.container {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--space-6);
}

/* The mono annotation label — carried over from the pitch document's plate
   numbering system. This is the brand's own vernacular, not decoration. */
.eyebrow {
  font-family: var(--font-annot);
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--marker);
}

.label {
  font-family: var(--font-annot);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--graphite);
}

.display {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.02em;
}

.muted { color: var(--graphite); }

/* ---- Nav ---- */

.nav-mark {
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: 0.045em;   /* widened slightly — deliberate, not a default */
}
.nav-links a:not(.btn) {
  font-family: var(--font-annot);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--graphite);
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-links a:not(.btn):hover { color: var(--paper); }

/* ---- Buttons ---- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-family: var(--font-annot);
  font-size: var(--text-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 14px 26px;
  /**
   * The pill, everywhere.
   *
   * .btn was --radius-sm (12px) while the landing page, the nav and the mode
   * chips were all 999px — so the same product showed five different button
   * shapes depending on which page you were on: 0, 3, 6, 12 and 999. None of
   * them was wrong on its own, which is exactly how it happened.
   */
  border-radius: 999px;
  border: 1px solid var(--rule-bright);
  background: transparent;
  color: var(--paper);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:hover { border-color: var(--marker); color: var(--marker); }
/* .btn:active superseded — see the weighted hover block. */

.btn-primary {
  background: var(--paper);
  color: var(--bg);
  border-color: var(--paper);
}
.btn-primary:hover {
  background: var(--marker);
  border-color: var(--marker);
  color: var(--bg);
}

.btn-block { display: flex; width: 100%; }

/* Group headings inside a plan list. A flat list of eleven features reads as
   noise; three headings naming the MOMENT each group solves let someone find
   themselves in it — which is what actually decides an upgrade. */
.tier li.tier-head {
  list-style: none; margin: var(--space-4) 0 var(--space-1); padding: 0;
  border: 0; font-family: var(--font-annot); font-size: 10px;
  letter-spacing: .09em; text-transform: uppercase; color: var(--graphite-dim);
}
.tier li.tier-head:first-of-type { margin-top: var(--space-3); }

/* Why a package failed, on the card itself.
   A toast lasts six seconds and then the user is left with a red FAILED badge
   and no idea whether the fault is theirs, ours, or a missing API credit —
   which is exactly the state that produces a support email. */
/* Sits inside the card now, below the status pill. The negative margin and
   the horizontal padding were compensating for being a separate grid cell. */
.course-why {
  font-family: var(--font-annot); font-size: 11px; line-height: 1.45;
  color: var(--graphite); margin-top: var(--space-3);
  border-top: 1px solid var(--rule); padding-top: var(--space-3);
}

/* A capability label on a control the current plan cannot use. Shown INSTEAD
   of letting the click fail — a limit you are told about is a pricing fact, a
   limit you discover by clicking is a bug. */
.pro-tag {
  font-family: var(--font-annot); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; padding: 2px 6px; margin-left: 6px;
  border-radius: 999px; border: 1px solid var(--rule); color: var(--graphite);
}

/* ---- Scroll reveal ---- */

/**
 * VISIBLE BY DEFAULT. The hiding is added by script, not by CSS.
 *
 * This used to be `opacity: 0` with JavaScript revealing each section as it
 * scrolled into view — which means a page whose script is slow, blocked or
 * broken renders its nav and its footer with NOTHING in between. That is not a
 * degraded page, it is an empty one, and it happened on the first load of the
 * live site: content sections invisible until a refresh.
 *
 * Inverted, the failure mode is right. No JavaScript means the page appears in
 * full without an animation, which nobody notices. The `.js-reveal` class is
 * set on <html> by the same script that installs the observer, so the sections
 * only ever hide when something is definitely there to show them again.
 */
.reveal {
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.js-reveal .reveal:not(.in) {
  opacity: 0;
  transform: translateY(22px);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* animation-iteration-count matters as much as the duration. Collapsing an
     INFINITE animation to 0.01ms does not stop it — it runs forever at a
     frame-length nobody sees, so the compositor keeps waking up. On a phone
     that is a battery cost paid by exactly the person who asked for less
     motion. Clamping the count is what actually ends it. */
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Footer ---- */

.site-footer {
  border-top: var(--border-hairline);
  margin-top: var(--space-10);
  padding: var(--space-7) 0 var(--space-8);
}
.site-footer .container {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: var(--space-6); flex-wrap: wrap;
}
.footer-mark { font-family: var(--font-display); font-size: 40px; color: var(--rule-bright); line-height: 1; }
.footer-meta { font-family: var(--font-annot); font-size: var(--text-xs); color: var(--graphite-dim); letter-spacing: 0.06em; }

/* ============================================================================
   ATMOSPHERE
   The loading reel has texture, depth and color presence. Flat near-black with
   hairlines reads as a wireframe next to it. Everything below exists to carry
   that same density into the pages themselves.
   ============================================================================ */

/* ---- Mode palettes, lifted from the loading reel ---- */
.theme-architecture {
  --mode-bg:      var(--bg);   /* was a mode tint — now matches the site field */
  --mode-bg-deep: var(--bg);
  --mode-line:    #D8DAE0;
  --mode-glow:    rgba(216, 218, 224, 0.05);
  --mode-accent:  #D8DAE0;
}
.theme-art {
  --mode-bg:      var(--bg);   /* was a mode tint — now matches the site field */
  --mode-bg-deep: var(--bg);
  --mode-line:    #8FD65F;
  --mode-glow:    rgba(143, 214, 95, 0.05);
  --mode-accent:  #9ED97A;
}
.theme-product {
  --mode-bg:      var(--bg);   /* was a mode tint — now matches the site field */
  --mode-bg-deep: var(--bg);
  --mode-line:    #E8B84B;
  --mode-glow:    rgba(232, 184, 75, 0.05);
  --mode-accent:  #E8B84B;
}
.theme-fashion {
  --mode-bg:      var(--bg);   /* was a mode tint — now matches the site field */
  --mode-bg-deep: var(--bg);
  --mode-line:    #C97B93;
  --mode-glow:    rgba(201, 123, 147, 0.05);
  --mode-accent:  #D98CA0;
}
.theme-connect {
  --mode-bg:      var(--bg);   /* was a mode tint — now matches the site field */
  --mode-bg-deep: var(--bg);
  --mode-line:    #5FC7B4;
  --mode-glow:    rgba(216, 218, 224, 0.05);
  --mode-accent:  #5FC7B4;
}
/* Default so pages without a mode class still resolve every var */
body {
  --mode-bg:      var(--bg);
  --mode-bg-deep: var(--bg);
  --mode-line:    var(--marker);
  --mode-glow:    rgba(216, 218, 224, 0.05);
  --mode-accent:  var(--marker);
}

/* ---- Textures, one per mode, matching the reel's backgrounds ---- */

.tex {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.tex-content { position: relative; z-index: 1; }

/* Blueprint grid — architecture */
.tex-blueprint {
  background-color: var(--mode-bg-deep);
  background-image:
    linear-gradient(rgba(216,218,224,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(216,218,224,.045) 1px, transparent 1px),
    linear-gradient(rgba(216,218,224,.022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(216,218,224,.022) 1px, transparent 1px);
  background-size: 96px 96px, 96px 96px, 24px 24px, 24px 24px;
}

/* Canvas weave — art */
.tex-canvas {
  background-color: var(--mode-bg-deep);
  background-image:
    repeating-linear-gradient(45deg,  rgba(232,220,196,.030) 0 1px, transparent 1px 9px),
    repeating-linear-gradient(-45deg, rgba(232,220,196,.030) 0 1px, transparent 1px 9px);
}

/* Pegboard — product */
.tex-pegboard {
  background-color: var(--mode-bg-deep);
  background-image: radial-gradient(circle, rgba(232,184,75,.09) 1.4px, transparent 1.6px);
  background-size: 34px 34px;
}

/* Fabric weave — fashion */
.tex-weave {
  background-color: var(--mode-bg-deep);
  background-image:
    repeating-linear-gradient(0deg,  rgba(242,228,234,.035) 0 1px, transparent 1px 6px),
    repeating-linear-gradient(90deg, rgba(242,228,234,.028) 0 1px, transparent 1px 6px);
}

/* Circuit — connect */
.tex-circuit {
  background-color: var(--mode-bg-deep);
  background-image:
    linear-gradient(rgba(216,218,224,.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(216,218,224,.04) 1px, transparent 1px);
  background-size: 48px 48px;
}

/* Atmospheric wash — sits over the texture, gives the field depth instead of
   a uniform flat plane. Radial so the center lifts and the edges fall away. */
.tex-wash {
  background:
    radial-gradient(ellipse 90% 60% at 50% 0%,  var(--mode-glow), transparent 65%),
    radial-gradient(ellipse 70% 50% at 50% 100%, var(--mode-glow), transparent 60%);
}

/* Vignette — pulls focus to the center, stops the frame feeling like it just ends */
.tex-vignette {
  background: radial-gradient(ellipse 120% 90% at 50% 45%, transparent 40%, rgba(0,0,0,.55) 100%);
}

/**
 * Scroll edge. Content dims as it slides under the floating pill — Apple's
 * scroll-edge effect, built from the page's own background color rather than
 * a second backdrop sample, so it is theme-correct by construction and adds no
 * GPU surface.
 *
 * IT MUST LIVE IN THE SAME STACKING CONTEXT AS THE NAV. On body it did not:
 * app pages wrap everything in `.tex-content`, which has `z-index: 1` and so
 * opens its own stacking context. The nav's z-index 60 is scoped INSIDE that
 * context, while a body pseudo-element at z-index 40 sits outside it — so the
 * gradient painted over the entire wrapper, nav included, and washed the pill's
 * labels out. Measured: the same "Community" link rendered at ink mean 154
 * against 119 on the marketing page, which has no wrapper and was therefore
 * unaffected. Two identical navs, visibly different weight.
 *
 * So it is attached to the wrapper on app pages and to body only where no
 * wrapper exists. Both branches then sit below the nav in their own context.
 */
.tex-content::before,
body:not(:has(.tex-content))::before {
  content: ''; position: fixed; top: 0; left: 0; right: 0; height: 76px;
  z-index: 40; pointer-events: none;
  /* Plain fallback first — a declaration containing color-mix is dropped whole
     by browsers without it, and while losing the scroll dim is harmless, a
     sweep with a standing warning is a sweep people stop reading. */
  background: linear-gradient(180deg, rgba(10,10,12,.82), transparent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg) 82%, transparent), transparent);
}
/* :has() is unsupported before Firefox 121, and one bad selector invalidates a
   whole comma list — so the marketing fallback is declared separately and the
   worst case there is simply no scroll edge, never a broken rule. */
@supports not selector(:has(*)) {
  body::before { content: none; }
}

/* ---- Surfaces with real depth ---- */


.panel-accent {
  border-color: color-mix(in srgb, var(--mode-accent) 38%, transparent);
  box-shadow:
    0 1px 0 rgba(255,255,255,.06) inset,
    0 0 0 1px color-mix(in srgb, var(--mode-accent) 10%, transparent),
    0 18px 52px rgba(0,0,0,.55);
}

/* Corner ticks — the drafting-mark motif from the pitch document, used as a
   frame detail so panels read as drawn plates rather than generic cards. */
.ticked { position: relative; }
/* The ticks themselves are defined AFTER the specular-sheen rules (search
   ".panel.ticked::before") — both jobs share ::before, and whichever rule
   comes later owns the pseudo, so they are written as one rule down there. */

/* ---- Inputs, themed ---- */

.field-input {
  width: 100%;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--paper);
  background: rgba(0,0,0,.34);
  border: 1px solid rgba(255,255,255,.10);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.field-input::placeholder { color: var(--graphite-dim); }
.field-input:focus {
  outline: none;
  border-color: var(--mode-accent);
  background: rgba(0,0,0,.5);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mode-accent) 14%, transparent);
}
textarea.field-input { min-height: 78px; resize: vertical; line-height: 1.55; }

/* ---- Accent button, follows the active mode ---- */
/* The primary action button.
   It previously used --mode-accent, which meant a blue button in light mode
   (architecture's light accent is #2F5580) with hardcoded near-black text — both
   inconsistent with every other primary action and poor contrast. It now follows
   the same inverse-of-background rule as .btn-primary, so it is off-white with
   dark text on the dark theme and near-black with light text on the light theme,
   automatically, with no per-mode special-casing.
   The mode's color still appears — as the focus ring below. */
.btn-mode {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--bg);
}
.btn-mode:hover {
  background: var(--paper);          /* fallback — see .panel note on color-mix */
  background: color-mix(in srgb, var(--paper) 88%, var(--bg));
  border-color: var(--paper);
  border-color: color-mix(in srgb, var(--paper) 88%, var(--bg));
  color: var(--bg);
}
.btn-mode:focus-visible {
  outline: 2px solid var(--mode-accent);
  outline-offset: 2px;
}

/* ---- Plate figure caption, carried from the pitch document ---- */
.fig-cap {
  display: flex; gap: var(--space-3); align-items: baseline;
  padding-top: 7px; margin-top: 9px;
  border-top: 1px solid rgba(255,255,255,.10);
  font-family: var(--font-annot); font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase;
}
.fig-cap .no { color: var(--mode-accent); }
.fig-cap .name { color: var(--graphite); }

/* ============================================================================
   FUNCTIONAL UI
   ============================================================================ */

/* ---- Badges: mock vs live provenance. Always visible where output is shown. */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-annot); font-size: 9.5px;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px; border: 1px solid;
  white-space: nowrap;
}
.badge-mock { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.badge-live { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 45%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); }
.badge-warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.badge-pass { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.badge-fail { color: #E8776B;     border-color: rgba(232,119,107,.45); background: rgba(232,119,107,.1); }

/* ---- Toasts ---- */
.toast-host { position: fixed; right: var(--space-5); bottom: var(--space-5); z-index: 200; display: flex; flex-direction: column; gap: var(--space-2); }
.toast {
  font-family: var(--font-annot); font-size: var(--text-xs); letter-spacing: .04em;
  padding: 12px 16px; border-radius: var(--radius-sm);
  background: var(--bg-raised); border: 1px solid rgba(255,255,255,.12);
  color: var(--paper); box-shadow: 0 12px 34px rgba(0,0,0,.5);
  opacity: 0; transform: translateY(8px);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.toast.in { opacity: 1; transform: none; }
.toast-ok   { border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.toast-warn { border-color: color-mix(in srgb, var(--warn) 40%, transparent); }

/* ---- Nav active state ---- */
.nav-links a:not(.btn).on { color: var(--paper); }

/* ---- Issue list (validation feedback) ---- */
.issue { display: grid; grid-template-columns: 96px 1fr; gap: var(--space-3); padding: var(--space-2) 0; font-size: var(--text-xs); }
.issue .k { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--warn); }
.issue .v { color: var(--graphite); }

/* ---- Editable narrative field ---- */
.edit-area {
  width: 100%; background: transparent; border: 1px solid transparent;
  color: inherit; font: inherit; line-height: inherit; resize: vertical;
  border-radius: var(--radius-sm); padding: 8px 10px; margin: -8px -10px;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.edit-area:hover { border-color: rgba(255,255,255,.09); }
.edit-area:focus { outline: none; border-color: var(--mode-accent); background: rgba(0,0,0,.28); }

/* ---- Meter ---- */
.meter { height: 3px; background: rgba(255,255,255,.1); border-radius: 2px; overflow: hidden; }
/* Fill rides the compositor: scaleX instead of width, so a quota tick never
   costs a layout pass. The origin pins growth to the left edge — the only
   direction a meter reads. */
.meter-fill { height: 100%; width: 100%; background: var(--mode-accent); border-radius: 2px; transform: scaleX(0); transform-origin: left center; transition: transform var(--dur-base) var(--ease-out); }
.meter-fill.over { background: var(--warn); }

/* ---- Empty state ---- */
.empty {
  text-align: center; padding: var(--space-9) var(--space-6);
  border: 1px dashed rgba(255,255,255,.14); border-radius: var(--radius-md);
}
.empty h3 { font-family: var(--font-display); font-size: var(--text-xl); margin: 0 0 var(--space-2); }
.empty p { color: var(--graphite); font-size: var(--text-sm); max-width: 44ch; margin: 0 auto var(--space-5); }

/* ---- Modal ---- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 150; display: none;
  align-items: center; justify-content: center; padding: var(--space-5);
  background: rgba(4,4,6,.72); backdrop-filter: blur(6px);
}
.modal-backdrop.open { display: flex; }
.modal { width: 100%; max-width: 460px; padding: var(--space-6); }
.modal h3 { font-family: var(--font-display); font-size: var(--text-xl); margin: 0 0 var(--space-2); }
.modal p { color: var(--graphite); font-size: var(--text-sm); margin: 0 0 var(--space-5); }

/* ---- Segmented control ---- */
.seg { display: inline-flex; border: 1px solid rgba(255,255,255,.12); border-radius: var(--radius-sm); overflow: hidden; }
/* Segmented controls read as one pill split into parts, rather than square
   tabs sitting inside a rounded frame. */
.seg { border-radius: 999px; overflow: hidden; }
.seg button {
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  padding: 8px 14px; background: transparent; border: none; color: var(--graphite); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.seg button.on { background: var(--mode-accent); color: #0A0A0C; }
.seg button:not(.on):hover { color: var(--paper); }

/* ---- Print: the pitch document export ---- */
@media print {
  .site-nav, .tex, .toast-host, .no-print, .modal-backdrop { display: none !important; }
  body { background: #fff !important; color: #16181C !important; }
  .panel { background: #fff !important; border-color: #DEDFE2 !important; box-shadow: none !important; }
  .tex-content { position: static; }
  a { color: inherit !important; }
}

/* ===========================================================================
   LIGHT THEME
   Not an inversion. On white, the same visual ideas need different physics:
   glass becomes a dark film over light rather than a light film over dark, and
   the particle marks go graphite/metallic instead of silver, because silver on
   white is invisible.
   =========================================================================== */

:root[data-theme="light"] {
  color-scheme: light;

  --bg:           #FBFBFC;
  --bg-raised:    #FFFFFF;
  --bg-inset:     #F1F1F3;
  --paper:        #131417;   /* text — near-black, not pure */
  --graphite:     #5E5E62;   /* neutral gray */
  --graphite-dim: #717175;   /* 4.6:1 on --bg, clears WCAG AA */
  --rule:         #E2E3E7;
  --rule-bright:  #CDCFD5;
  --marker:       #3A3A3E;   /* accent is graphite, not blue — matches dark mode being monochrome */
  --marker-deep:  #E6E6E8;   /* neutral fill */
  --warn:         #A2571F;
  --danger:       #B4342A;   /* darkened for contrast on white */
  --ok:           #2C6B45;

  --silver:       #22242A;   /* particle at full density — near-black */
  --silver-dim:   #ACACB0;   /* scattered — neutral mid gray */

  --glass-fill:   rgba(19,20,23,.045);
  --glass-edge:   rgba(19,20,23,.05);   /* see --lg-depth: two dark rings stacked */
  --nav-fill:     rgba(252,252,253,.96);
  /**
   * Light mode defines the pill by its FILL, not an outline.
   *
   * Any dark border here is darker than both the page above it and the sheen
   * below it, so the top edge reads page(245) -> border(232) -> sheen(254): a
   * dark line sandwiched between two brighter bands. That non-monotonic dip is
   * the sliver. Dark mode has no such problem — there the sequence rises
   * cleanly, 9 -> 41 -> 103 -> falloff — which is why the same token can be
   * visible there and must not be here.
   *
   * The pill still reads as a distinct object: its fill sits ~9 luminance
   * steps above the page, and the drop shadow carries the lower edge.
   */
  --nav-edge:     rgba(19,20,23,.03);
  --nav-ink:      rgba(19,20,23,.74);
  --nav-ink-hi:   #131417;
  --glass-spec:   rgba(255,255,255,.75);
  --glass-glow:   rgba(19,20,23,.10);      /* darkens on light surfaces */
  --glass-blur:   16px;
  --glass-sat:    130%;
}

/* Mode accents need darkening on white or they vibrate. */
:root[data-theme="light"] .theme-architecture { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:#55555A; --mode-glow:rgba(85,85,90,.06);  --mode-accent:#55555A; }
:root[data-theme="light"] .theme-art          { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:#3F6B2A; --mode-glow:rgba(63,107,42,.07);  --mode-accent:#3F6B2A; }
:root[data-theme="light"] .theme-product      { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:#8A6212; --mode-glow:rgba(138,98,18,.07);  --mode-accent:#8A6212; }
:root[data-theme="light"] .theme-fashion      { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:#8E3F58; --mode-glow:rgba(142,63,88,.07);  --mode-accent:#8E3F58; }
:root[data-theme="light"] .theme-connect      { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:#1F6B60; --mode-glow:rgba(31,107,96,.07);  --mode-accent:#1F6B60; }
:root[data-theme="light"] body                { --mode-bg:var(--bg); --mode-bg-deep:var(--bg); --mode-line:var(--marker); --mode-glow:rgba(90,90,95,.05); --mode-accent:var(--marker); }

/* Textures are drawn for a dark field; lighten their strokes on white. */
:root[data-theme="light"] .tex-blueprint { background-image:
  linear-gradient(rgba(90,90,95,.07) 1px, transparent 1px),
  linear-gradient(90deg, rgba(90,90,95,.07) 1px, transparent 1px),
  linear-gradient(rgba(90,90,95,.035) 1px, transparent 1px),
  linear-gradient(90deg, rgba(90,90,95,.035) 1px, transparent 1px); }
:root[data-theme="light"] .tex-canvas { background-image:
  repeating-linear-gradient(45deg, rgba(19,20,23,.035) 0 1px, transparent 1px 9px),
  repeating-linear-gradient(-45deg, rgba(19,20,23,.035) 0 1px, transparent 1px 9px); }
:root[data-theme="light"] .tex-pegboard { background-image: radial-gradient(circle, rgba(138,98,18,.11) 1.4px, transparent 1.6px); }
:root[data-theme="light"] .tex-weave { background-image:
  repeating-linear-gradient(0deg, rgba(19,20,23,.04) 0 1px, transparent 1px 6px),
  repeating-linear-gradient(90deg, rgba(19,20,23,.03) 0 1px, transparent 1px 6px); }
:root[data-theme="light"] .tex-circuit { background-image:
  linear-gradient(rgba(31,107,96,.06) 1px, transparent 1px),
  linear-gradient(90deg, rgba(31,107,96,.06) 1px, transparent 1px); }
:root[data-theme="light"] .tex-vignette { background:
  radial-gradient(ellipse 120% 90% at 50% 45%, transparent 45%, rgba(19,20,23,.07) 100%); }

/* ===========================================================================
   GLASS
   Liquid-Glass-inspired, deliberately restrained. Apple's actual implementation
   uses a private CSS property (-apple-visual-effect) gated behind a WebKit
   preference — it does not work on the open web, so every web version is an
   approximation via backdrop-filter.
   Applied to CHROME ONLY (nav, panels, modals), never behind body text: the
   widely-noted criticism of the real thing is legibility, and this is a landing
   page that has to convert. The specular top edge is what reads as glass rather
   than as a plain blur.
   =========================================================================== */

.glass {
  position: relative;
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}
/* Light catching the top edge — the single cue that sells glass over blur. */
.glass::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, var(--glass-spec) 0%, transparent 42%);
  opacity: .5;
}
.glass > * { position: relative; z-index: 1; }

.glass-strong { --glass-blur: 30px; --glass-sat: 175%; }
.glass-quiet  { --glass-blur: 10px; --glass-sat: 120%; }

/* No backdrop-filter (older browsers, or forced-transparency off) — fall back
   to an opaque surface rather than unreadable text over a busy background. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass { background: var(--bg-raised); }
  .glass::before { display: none; }
}

/* Nav becomes glass in both themes. */

/* Menu tabs: transparent at rest, glass on hover/active — the "transparency on
   different tabs" ask, done so the active state is legible rather than just dim. */
.nav-links a:not(.btn) {
  color: var(--nav-ink);
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
/**
 * Hover is a FILL, not an outline.
 *
 * It used to set border-color to --nav-edge (.18 alpha) over a background of
 * only .09 — an outline twice as strong as the shape it surrounded. The eye
 * read the edge rather than the fill, and along the top, where it meets the
 * nav's own specular sheen, it showed as a hard sliver: dark on the light
 * theme, light on the dark one. Measured 10 luminance steps away from the
 * pill's interior, on a fill that is only ~5 steps from the nav itself.
 *
 * The border stays transparent so the box never resizes, and the fill is
 * raised to carry the state on its own.
 */
.nav-links a:not(.btn):hover {
  background: rgba(255,255,255,.14);
  border-color: transparent;
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.nav-links a:not(.btn):hover { color: var(--nav-ink-hi); }
/* The current page keeps a hairline, because unlike hover it has to be legible
   without the pointer anywhere near it. Softened to sit with the fill rather
   than shout over it. */
.nav-links a:not(.btn)[aria-current="page"] {
  background: rgba(255,255,255,.09);
  border-color: color-mix(in srgb, var(--nav-edge) 55%, transparent);
  color: var(--nav-ink-hi);
}

/* Panels inherit glass rather than the old flat gradient. */
.panel {
  /* The fill carries ~4% of the mode accent — the ambient color pickup Apple
     describes for large surfaces, so architecture glass and fashion glass are
     not the same material. A gradient (4% to 3%) rather than a flat tint, so
     it reads as light falling through, not as a painted wash. */
  /* Plain fill first: a declaration containing color-mix() is discarded whole
     by browsers without it (Safari <16.2, Chrome <111, Firefox <113), which
     would leave this panel with NO background and its text sitting over the
     live particle canvas. Older browsers keep this line; newer ones override
     it with the accent-tinted version below. */
  background: var(--glass-fill);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--mode-accent, transparent) 4%, transparent),
      color-mix(in srgb, var(--mode-accent, transparent) 3%, transparent))
    var(--glass-fill);
  border: 1px solid var(--glass-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  /* Thick glass: a hairline contour seats the slab, a close contact shadow
     grounds it, and the deep ambient one gives it the weight Apple assigns to
     larger elements.

     NO INSET SPEC LINE. It used to sit here as "light landing on the lip", but
     it lands on the same pixel as the 1px border AND the contour ring — three
     edges stacked on one row, which reads as a hard sliver across the top of
     every card rather than a lit edge. Exactly the fault fixed on the nav pill;
     this is where it survived, and it is what shows on the package cards. The
     border and the contour define the edge on their own. */
  box-shadow:
    0 0 0 1px var(--lg-depth),
    0 2px 10px rgba(0,0,0,.20),
    0 22px 56px rgba(0,0,0,.30);
}
:root[data-theme="light"] .panel {
  box-shadow:
    0 0 0 1px var(--lg-depth),
    0 10px 30px rgba(19,20,23,.07);
}

/* ---- Theme toggle ---- */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 999px;
  background: var(--glass-fill); border: 1px solid var(--glass-edge);
  color: var(--graphite); cursor: pointer;
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.theme-toggle:hover { color: var(--paper); border-color: var(--rule-bright); }
.theme-toggle svg { width: 15px; height: 15px; }

/* ===========================================================================
   BRAND LOCKUP  —  identity sheet PL-01/01
   Mark at cap height on a shared baseline with the wordmark. Archivo Semibold,
   tracking -1%. The mark's body uses currentColor so it inverts with the theme:
   INK (#1B1C1E) on light, BONE (#EDEBE4) on dark.
   =========================================================================== */

.pw-lockup {
  display: inline-flex;
  align-items: center;
  gap: 0.34em;
  line-height: 1;
  /* Body color of the mark. Not --paper: the sheet specifies exact values. */
  color: #EDEBE4;
}
:root[data-theme="light"] .pw-lockup { color: #1B1C1E; }

.pw-lockup svg { display: block; flex: none; }

.pw-wordmark {
  font-family: 'Archivo', var(--font-ui), sans-serif;
  font-weight: 600;
  letter-spacing: -0.01em;   /* tracking -1% per the sheet */
  font-size: 1.05em;
}

/* The nav mark is now the lockup, so drop the old serif treatment. */

/* ===========================================================================
   PILL NAV  —  floating, centered, glass
   A full-width bar gave the glass nothing to refract in light mode, so it read
   as a plain solid strip. A floating pill has page content passing behind it,
   which is the only way the material is legible at all.
   =========================================================================== */

.site-nav {
  position: fixed;
  top: 14px; left: 50%;
  transform: translateX(-50%);
  /* Hugs its contents rather than claiming a fixed width — three buttons gives a
     narrow pill, five gives a wider one, with a ceiling so it can never span the
     whole viewport. */
  width: max-content;
  max-width: calc(100vw - 32px);
  height: 58px;
  z-index: 60;

  border-radius: 999px;
  /* Own fill, not --glass-fill. The nav floats over the particle field, the hero
     and the page floor in turn; at the card-level alpha it went effectively
     transparent over the field and the cloud read straight through it. */
  background: var(--nav-fill);
  border: 1px solid var(--nav-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  /* NO inset specular line here. ::before already lays a sheen gradient that
     starts at full --glass-spec on row zero; adding a 1px inset highlight at
     the same row doubles it, and with the border underneath that is three
     light sources stacked on a single pixel. The result was a hard bright
     hairline across the top of the pill — the sliver — rather than a sheen
     that falls away. The gradient alone reads as glass; the line read as an
     edge someone forgot to remove. */
  box-shadow: 0 10px 34px rgba(0,0,0,.28);
  overflow: hidden;   /* clips the specular sheen to the pill */
}
:root[data-theme="light"] .site-nav { box-shadow: 0 8px 26px rgba(19,20,23,.10); }

/* Specular sheen along the top edge — the cue that reads as glass. */
.site-nav::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(180deg, var(--glass-spec) 0%, transparent 46%);
  opacity: .55;
}

.site-nav .container {
  position: relative; z-index: 1;
  max-width: none; height: 100%;
  padding: 0 10px 0 20px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}

/* Mark only on the left, per spec. */
.site-nav .nav-mark { display: inline-flex; align-items: center; flex: none; }

.nav-links { display: flex; align-items: center; }

/* Content must clear the floating pill. */
.tex-content > .site-nav + * ,
.morph { scroll-margin-top: 90px; }

@media (max-width: 720px) {
  .site-nav { height: 52px; top: 10px; }
  .nav-links a:not(.btn) { display: none; }   /* keep the pill from wrapping */
}

/* ---------------------------------------------------------------------------
   GLASS INTERACTION
   Pointer-tracked highlight, so hovering a control moves light across it rather
   than just changing a background color. Uses only transform/opacity and CSS
   custom properties, so it costs nothing per frame and behaves identically on
   macOS, iOS and Windows — this is standard backdrop-filter plus a radial
   gradient, not a platform-private API.
   --------------------------------------------------------------------------- */

.glass-hit {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.glass-hit::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
  background: radial-gradient(
    var(--gr, 140px) circle at var(--gx, 50%) var(--gy, 50%),
    var(--glass-glow), transparent 70%);
}
.glass-hit:hover::after { opacity: .9; }
/* Press: the material illuminates from within, starting under the pointer —
   feedback, not motion design, so it stays on under reduced motion. */
.glass-hit:active::after { opacity: 1; }
/* Gel. Controls flex under the finger; panels never scale — only things sized
   for a fingertip. (.nav-links a:active already presses at .99.) */
.btn:active, .theme-toggle:active, .mode-card:active, .auth-tab:active, .tier:active { transform: scale(.985); }

/* Buttons and tabs pick up the glass material. */
.btn, .nav-links a, .theme-toggle, .mode-card, .auth-tab {
  position: relative;
  overflow: hidden;
}
.btn:not(.btn-primary):not(.btn-mode) {
  background: var(--glass-fill);
  border-color: var(--glass-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}

/* Pressed state: the material compresses slightly, the way a real control would. */
.nav-links a:active { transform: scale(0.99); }   /* buttons handled below */

@media (prefers-reduced-motion: reduce) {
  .glass-hit::after { display: none; }
}

/* Three-state theme toggle: dark / light / auto. Only one icon shows at a time,
   selected by the MODE (not the resolved theme) so 'auto' is visibly distinct
   from whichever theme it happens to have resolved to. */
.theme-toggle .icon-sun,
.theme-toggle .icon-moon,
.theme-toggle .icon-auto { display: none; }
:root[data-theme-mode="dark"]  .theme-toggle .icon-sun  { display: block; }
:root[data-theme-mode="light"] .theme-toggle .icon-moon { display: block; }
:root[data-theme-mode="auto"]  .theme-toggle .icon-auto { display: block; }
/* No mode attribute yet (first ever visit) — show the dark-mode icon. */
:root:not([data-theme-mode]) .theme-toggle .icon-sun { display: block; }

/* ===========================================================================
   DASHBOARD  —  course-card layout
   Modelled on the pattern Canvas uses for course cards: a colored header band
   identifying the category, a white body with the title and meta, and actions
   pinned to the bottom edge. It works here for the same reason it works there —
   the color band lets you find the one you want by category before you have
   read a single word.
   =========================================================================== */

.dash-layout { display: grid; grid-template-columns: 1fr 300px; gap: var(--space-6); align-items: start; }
@media (max-width: 940px) { .dash-layout { grid-template-columns: 1fr; } }

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: var(--space-4); }

.course-card {
  display: flex; flex-direction: column; min-height: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--glass-fill);
  /**
   * A softer edge than the shared glass value.
   *
   * Measured on a dark dashboard: page 11, border 49, fill 38 — the border sat
   * BRIGHTER than the surface it outlines, which reads as a line drawn on the
   * card rather than the edge of it. The category rule and the drop shadow
   * already say where the card stops.
   */
  border: 1px solid color-mix(in srgb, var(--glass-edge) 55%, transparent);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
  min-height: 0;
}
/* Superseded by the weighted hover block below — a 3px hop reads as a toggle. */

/* The color band. Category-coded, so the eye finds it before the text does. */
/**
 * NO COLORED STRIP. The category is the word above the title.
 *
 * This was a 3px band across the top, and it read as a rendering artefact —
 * reported as "the sliver" three times. Moving it to the left edge fixed the
 * measurement and was reported again on the fourth: a strip of color on this
 * card does not look deliberate wherever it is put.
 *
 * So the coding moves to the eyebrow, which already says ARCHITECTURE in
 * words directly beneath it. Text cannot be mistaken for an edge, the
 * information is identical, and one element now does the job two were sharing.
 */
.course-band { display: none; }
.course-card { position: relative; }

.course-band[data-mode="architecture"] { background: #A2A2A6; }
.course-band[data-mode="art"]          { background: #3B7A57; }
.course-band[data-mode="product"]      { background: #C6952F; }
.course-band[data-mode="fashion"]      { background: #B25F58; }
.course-band[data-mode="connect"]      { background: #35797B; }

.course-body { padding: var(--space-5) var(--space-4) var(--space-4); flex: 1; display: flex; flex-direction: column; }
.course-title { font-family: var(--font-display); font-size: var(--text-lg); line-height: 1.2; margin-bottom: 4px; }
.course-meta { font-family: var(--font-annot); font-size: 10px; letter-spacing: .07em; text-transform: uppercase; color: var(--graphite); }
.course-actions {
  display: flex; gap: var(--space-3); margin-top: auto; padding-top: var(--space-4);
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .07em; text-transform: uppercase;
}
/**
 * The whole card is one link, via a stretched overlay. Board and Document are
 * the only elements that need to beat it, so they alone get a z-index — and
 * they dress as small chips rather than bare words, because "tiny little
 * words" was the exact complaint: an action that looks like decoration.
 */
/**
 * Child selectors, not bare classes, because the glass specular block later in
 * this file lifts EVERY card child with `.course-card > * { position:
 * relative; z-index: 1 }` — equal specificity, later position, and it
 * silently flattened this overlay to `relative` (the panel-scoped-override
 * trap, in card form). Two consequences drive the shape here:
 *
 *   1. `.course-card > .course-link` (0,2,0) out-specifies the lift, so the
 *      overlay actually gets to be absolute.
 *   2. `.course-body` must NOT form a stacking context: with the lift's
 *      z-index:1 it does, and then the Board/Document chips inside it could
 *      never paint above a sibling overlay — no z-index of theirs escapes
 *      their parent's context. `z-index: auto` keeps the lift's visual
 *      purpose (positioned content still paints above the specular ::before,
 *      which is pointer-events: none anyway) while letting the chips join the
 *      card's own context, where their z-index: 2 beats the overlay's 1.
 */
.course-card > .course-link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.course-card > .course-link:focus-visible { outline: 2px solid var(--mode-accent); outline-offset: -2px; }
.course-card > .course-body { z-index: auto; }

/**
 * The footer's language row: quiet, wrapping, self-named.
 *
 * This block was spliced into the MIDDLE of the `.course-card > .course-link`
 * selector by the site-language build, which had two silent effects: the row
 * was scoped as `.course-card > .footer-lang` and so never matched anything on
 * the landing page, and `.course-link` lost its `.course-card >` prefix,
 * dropping to (0,1,0) where the specular lift later in this file out-specifies
 * it and flattens the whole-card click overlay. Both restored here.
 */
.footer-lang {
  display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4);
  margin-top: var(--space-4);
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .07em;
}
.footer-lang a { color: var(--graphite-dim); }
.footer-lang a:hover { color: var(--graphite); }

.course-actions { position: relative; z-index: 2; }
.course-actions a {
  color: var(--graphite);
  border: 1px solid color-mix(in srgb, var(--graphite) 40%, transparent);
  border-radius: 999px;
  padding: 3px 10px;
}
.course-actions a:hover {
  color: var(--mode-accent);
  border-color: color-mix(in srgb, var(--mode-accent) 55%, transparent);
}

.course-status { font-family: var(--font-annot); font-size: 9px; letter-spacing: .09em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; }
.course-status.ready  { color: var(--ok);     border: 1px solid color-mix(in srgb, var(--ok) 45%, transparent); }
.course-status.failed { color: var(--danger); border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent); }
.course-status.busy   { color: var(--marker); border: 1px solid color-mix(in srgb, var(--marker) 45%, transparent); }

/* New-package tile, same footprint as a card so the grid stays even. */
.course-new {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  /**
   * SAME CORNER AS THE PACKAGE CARDS BESIDE IT.
   *
   * This read --radius-md (14px), which looked right next to `.course-card`'s
   * own --radius-md rule — but a panel rule ~800 lines later gives every card
   * in the family (.course-card, .rail-card, .tier, .stat) --radius-xl, and
   * the create tile was never added to that list. Same specificity, later
   * position, so the cards rendered at 28px and the tile sat among them at
   * 14px: identical size, identical alignment, visibly different corners.
   * Taking the family's token directly keeps the two matched even if the
   * value changes.
   */
  min-height: 132px; border-radius: var(--radius-xl);
  border: 1px dashed var(--rule-bright); color: var(--graphite);
  transition: border-color var(--dur-base), color var(--dur-base), transform var(--dur-base);
}
.course-new:hover { border-color: var(--marker); color: var(--marker); transform: translateY(-3px); }
.course-new span:first-child { font-family: var(--font-display); font-size: 30px; line-height: 1; }

/* Right rail: the "to do" column, again borrowed from the Canvas pattern. */
.dash-rail { display: grid; gap: var(--space-4); position: sticky; top: 96px; }
.rail-card { padding: var(--space-5); border-radius: var(--radius-md); background: var(--glass-fill); border: 1px solid var(--glass-edge); }
.rail-title { font-family: var(--font-annot); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite); margin-bottom: var(--space-4); }
.rail-item { display: grid; grid-template-columns: 8px 1fr; gap: 10px; padding: 9px 0; border-top: 1px solid var(--rule); font-size: var(--text-sm); }
.rail-item:first-of-type { border-top: none; }
.rail-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--mode-accent); margin-top: 6px; }
.rail-item small { display: block; color: var(--graphite); font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; margin-top: 2px; }

/* Upload / auto-assign dropzone. */
.assign-zone {
  border: 1px dashed var(--rule-bright); border-radius: var(--radius-md);
  padding: var(--space-5); text-align: center; cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.assign-zone:hover, .assign-zone.over { border-color: var(--marker); background: var(--glass-fill); }
.assign-zone.busy { opacity: .6; pointer-events: none; }
.assign-result { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--rule); font-size: var(--text-sm); display: none; }
.assign-result.on { display: block; }
.assign-conf { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--graphite); }

/* On white, a white hover fill is invisible — the pill needs to darken instead. */
:root[data-theme="light"] .nav-links a:not(.btn):hover {
  background: rgba(19,20,23,.075);
}
:root[data-theme="light"] .nav-links a:not(.btn)[aria-current="page"] {
  background: rgba(19,20,23,.06);
}

/* ===========================================================================
   THE ISLAND
   Live state inside the nav pill. The pill is the only element on every page, so
   it is the right place for anything that outlives a single page — a half-written
   brief, a generation running in the background, one that finished while you were
   somewhere else.
   =========================================================================== */

.island-slot { display: none; align-items: center; gap: 6px; margin-left: auto; margin-right: var(--space-4); }
.island-slot.on { display: flex; }

.island {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px 6px 10px;
  border-radius: 999px;
  background: rgba(255,255,255,.07);
  border: 1px solid var(--nav-edge);
  max-width: 260px;
  transition: background var(--dur-fast) var(--ease-out), max-width var(--dur-base) var(--ease-out);
}
.island:hover { background: rgba(255,255,255,.12); }
:root[data-theme="light"] .island { background: rgba(19,20,23,.05); }
:root[data-theme="light"] .island:hover { background: rgba(19,20,23,.09); }

.island-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* Pulses only while work is actually running — a static dot for a finished
   package, so motion always means "in progress". */
.island-dot.pulsing { animation: islandPulse 1.5s var(--ease-in-out) infinite; }
@keyframes islandPulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.72); } }

.island-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.island-label {
  font-family: var(--font-annot); font-size: 8.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--graphite);
}
.island-name {
  font-size: 12px; color: var(--paper);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px;
}
.island-action {
  font-family: var(--font-annot); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--marker); flex: none;
}
.island-x {
  background: none; border: none; cursor: pointer; padding: 2px 6px;
  color: var(--graphite-dim); font-size: 15px; line-height: 1;
}
.island-x:hover { color: var(--paper); }

@media (max-width: 860px) {
  .island-slot { margin-right: var(--space-3); }
  .island-text { display: none; }          /* dot + action only on narrow */
  .island { max-width: none; padding: 6px 10px; }
}
@media (prefers-reduced-motion: reduce) { .island-dot.pulsing { animation: none; } }

/* ===========================================================================
   V7 — PILL SPACING + WIDER GLASS
   =========================================================================== */

/* The pill's controls were cramped at 4px. Real breathing room, and the pill
   itself gets more internal padding so nothing sits against the edge. */
.site-nav .container { padding: 0 var(--space-3) 0 var(--space-5); gap: var(--space-5); }
.nav-links { }
.nav-links a:not(.btn) { padding: 8px 15px; }
.nav-links .btn { margin-left: var(--space-2); padding: 10px 20px; }
.theme-toggle { margin-right: var(--space-2); }
@media (min-width: 861px) {
  .site-nav { height: 62px; }
  .nav-links { gap: var(--space-3); }
}

/* ---- glass, applied more widely ----
   Every raised surface now uses the material rather than a flat fill: form
   fields, the segmented control, the mode cards, dropzones, the island, and the
   app rail. The specular top edge is what distinguishes it from a plain blur, so
   it is applied to each rather than only to panels. */

.field-input,
.assign-zone,
.drop,
.seg,
.rail-card,
.work-panel,
.course-card,
.stat,
.app-rail,
.tier,
.auth-card {
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}

.field-input {
  background: var(--glass-fill);
  border-color: var(--glass-edge);
}
.field-input:focus {
  background: color-mix(in srgb, var(--glass-fill) 60%, rgba(0,0,0,.35));
}

/**
 * Selects, tamed.
 *
 * The closed control and — worse — the OPEN POPUP of a <select> are painted by
 * the browser, not the page. On Windows, Edge rendered both in its light
 * palette over the dark theme: the "super white drop-down". No background rule
 * reaches the popup; the only levers it obeys are the element's color-scheme
 * and explicit colors on the options themselves. So: take over the closed
 * control entirely (appearance: none, our own chevron — a data URI, which
 * img-src already allows), and pin color-scheme plus option colors per theme
 * so the native list matches the page wherever the OS draws it.
 */
select.field-input {
  color-scheme: dark;
  appearance: none;
  -webkit-appearance: none;
  padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23B9BAC0' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
}
select.field-input option,
select.field-input optgroup {
  background-color: var(--bg-raised, #1B1C20);
  color: var(--paper, #F2F0EB);
}
:root[data-theme="light"] select.field-input {
  color-scheme: light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23595B60' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  /* The light theme's .field-input rule uses the `background` SHORTHAND, which
     silently resets repeat/position/size back to initial at a specificity this
     selector must outrank explicitly. Without these three lines the 12px
     chevron tiles across the whole control — the light-mode zigzag. */
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
}
:root[data-theme="light"] select.field-input option,
:root[data-theme="light"] select.field-input optgroup {
  background-color: #FFFFFF;
  color: #131417;
}

/* One-off template editor rows (brief rail), the dashboard templates card,
   and the deep-link flash. Grouped because all three exist for one reason:
   the template feature now has doors where people actually stand. */
.plate-row.tpl-edit { display: flex; align-items: center; gap: 8px; }
.plate-row.tpl-edit .no { cursor: grab; white-space: nowrap; }
.plate-row.tpl-edit input.field-input { flex: 1; min-width: 0; padding: 6px 10px; font-size: 13px; }
.plate-row.tpl-edit .btn { padding: 4px 9px; line-height: 1; }
.tpl-anchor.on { color: var(--mode-accent, #D8DAE0); border-color: var(--mode-accent, #D8DAE0); }
input.field-input.tpl-framing { width: 100%; margin: 2px 0 10px; padding: 6px 10px; font-size: 12px; color: var(--graphite, #8A8A8E); }
.tpl-dash-row { display: block; padding: 8px 10px; border-radius: 10px; color: inherit; text-decoration: none; }
.tpl-dash-row:hover { background: var(--glass-fill); }
.tpl-dash-row small { color: var(--graphite-dim, #6E6E73); }
.anchor-flash { animation: anchorFlash 1.6s ease-out 1; }
@keyframes anchorFlash {
  0%   { box-shadow: 0 0 0 2px var(--mode-accent, #D8DAE0), 0 0 34px color-mix(in srgb, var(--mode-accent, #D8DAE0) 35%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .anchor-flash { animation: none; } }
/* Touch targets: the editor's compact buttons meet the 44pt floor on coarse
   pointers without changing the desktop density. */
@media (pointer: coarse) {
  .plate-row.tpl-edit .btn { padding: 10px 13px; }
}


/* Specular edge on the larger surfaces — the cue that reads as glass. */
.course-card, .stat, .rail-card, .work-panel, .tier, .auth-card, .form-panel {
  position: relative;
}
.course-card::before, .stat::before, .rail-card::before,
.work-panel::before, .tier::before, .form-panel::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, var(--glass-spec) 0%, transparent 38%);
  opacity: .4;
}
.course-card > *, .stat > *, .rail-card > *,
.work-panel > *, .tier > *, .form-panel > * { position: relative; z-index: 1; }

/**
 * The category rule, restated AFTER the block above.
 *
 * That rule sets `position: relative` on every direct child to lift content
 * above the sheen — and it silently defeated the absolute positioning here,
 * so the band rendered at zero height. The sliver went away because the
 * element vanished, which is not the same as fixing it. Specificity ties are
 * decided by source order; this has to come second.
 */
/* The eyebrow carries the category color instead of a strip. */
.course-card .course-meta[data-mode="architecture"] { color: #A2A2A6; }
.course-card .course-meta[data-mode="art"]          { color: #6FA588; }
.course-card .course-meta[data-mode="product"]      { color: #C6952F; }
.course-card .course-meta[data-mode="fashion"]      { color: #C88A98; }
.course-card .course-meta[data-mode="connect"]      { color: #5FC7B4; }

/* The app rail becomes a glass column rather than a flat tint. */
.app-rail {
  background: var(--glass-fill);
  border-right: 1px solid var(--glass-edge);
}

/* ---- context files ---- */
.ctx-zone {
  border: 1px dashed var(--rule-bright);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  text-align: center; cursor: pointer;
  background: var(--glass-fill);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.ctx-zone:hover, .ctx-zone.over { border-color: var(--mode-accent); }
.ctx-list { display: grid; gap: 6px; margin-top: var(--space-3); }
.ctx-item {
  display: grid; grid-template-columns: 1fr auto auto; gap: var(--space-3);
  align-items: center; padding: 8px 11px;
  border-radius: var(--radius-sm);
  background: var(--glass-fill); border: 1px solid var(--glass-edge);
  font-size: var(--text-sm);
}
.ctx-item .kind { font-family: var(--font-annot); font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--graphite); }
.ctx-item .rm { background: none; border: none; color: var(--graphite-dim); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; }
.ctx-item .rm:hover { color: var(--danger); }
.ctx-note { font-family: var(--font-annot); font-size: 10px; line-height: 1.6; color: var(--graphite-dim); margin-top: var(--space-3); }
.ctx-note b { color: var(--mode-accent); font-weight: 400; }

/* ===========================================================================
   THE PILL, EXPANDED
   Resting: mark, state chip, links. On intent it grows downward to reveal
   navigation with hints, and a fuller read of whatever is currently running.
   A permanently wide pill would compete with the page it floats over; expanding
   on hover keeps it quiet until it is wanted.
   =========================================================================== */

.site-nav {
  height: auto;                 /* grows with the drawer */
  min-height: 62px;
  display: flex; flex-direction: column;
  transition: box-shadow var(--dur-base) var(--ease-out);
  border-radius: 30px;          /* softens as it grows */
}
.site-nav > .container { min-height: 62px; flex: none; }

.pill-drawer {
  min-width: 268px;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height var(--dur-slow) var(--ease-out),
              opacity var(--dur-base) var(--ease-out),
              padding var(--dur-slow) var(--ease-out);
  padding: 0 var(--space-4);
  position: relative; z-index: 1;
}
.site-nav.expanded {
  border-radius: 26px;
  box-shadow: 0 22px 60px rgba(0,0,0,.45);
}
/* Height comes from --drawer-h, measured in JS from the drawer's own content, so
   it fits exactly however many rows exist. CSS cannot transition to `auto`, which
   is why this is measured rather than declared. */
.site-nav.expanded .pill-drawer {
  max-height: var(--drawer-h, 420px);
  opacity: 1;
  padding: 0 var(--space-4) var(--space-4);
}
:root[data-theme="light"] .site-nav.expanded { box-shadow: 0 16px 44px rgba(19,20,23,.14); }

/* Status block — the fuller read of what is running. */
.pill-status {
  display: grid; grid-template-columns: 10px 1fr; gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  transition: border-color var(--dur-fast) var(--ease-out);
}
a.pill-status:hover { border-color: var(--rule-bright); }
.pill-status .island-dot { margin-top: 5px; }
.pill-status-label { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite); }
.pill-status-label b { color: var(--marker); font-weight: 400; }
.pill-status-name { font-family: var(--font-display); font-size: var(--text-base); margin: 2px 0 3px; }
.pill-status-note { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .05em; color: var(--graphite-dim); }

/* Navigation rows */
.pill-nav { display: grid; gap: 2px; }
.pill-row {
  display: flex; flex-direction: column; gap: 1px;
  padding: 10px var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.pill-row:hover, .pill-row:focus-visible {
  background: rgba(255,255,255,.07);
  border-color: var(--nav-edge);
  outline: none;
}
:root[data-theme="light"] .pill-row:hover,
:root[data-theme="light"] .pill-row:focus-visible { background: rgba(19,20,23,.055); }
.pill-row.current { background: rgba(255,255,255,.05); border-color: var(--rule-bright); }
:root[data-theme="light"] .pill-row.current { background: rgba(19,20,23,.04); }
.pill-row-label { font-size: var(--text-sm); color: var(--paper); }
/* --graphite rather than --graphite-dim: this sits on the drawer's raised
   glass, not on --bg, where the dim token lands just under 4.5:1. */
.pill-row-hint { font-family: var(--font-annot); font-size: 9px; letter-spacing: .07em; text-transform: uppercase; color: var(--graphite); }

/* Elapsed time in the compact chip */
.island-label b { color: var(--marker); font-weight: 400; }

@media (prefers-reduced-motion: reduce) {
  .pill-drawer { transition: none; }
}
@media (max-width: 720px) {
  .site-nav.expanded .pill-drawer { max-height: 60vh; overflow-y: auto; }
}

/* ===========================================================================
   GLASS, EVERYWHERE
   Every raised or interactive surface now carries the material. Previously it was
   panels and the nav; anything else was flat, which made the treatment read as
   inconsistent rather than as a system.
   =========================================================================== */

.panel, .work-panel, .rail-panel, .rail-card, .course-card, .stat, .tier,
.auth-card, .form-panel, .captions, .panel-block, .ctx-zone, .assign-zone,
.drop, .seg, .field-input, textarea.field-input, .island, .pill-status,
.mode-card, .plate-img, .pkg-thumb, .doc-preview, .byok-note, .conn,
.theme-toggle, .btn:not(.btn-primary):not(.btn-mode) {
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}

/* Fill and edge for surfaces that were previously flat or bare. */
.captions, .panel-block, .seg, .byok-note, .doc-preview, .mode-card {
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
}

/* Specular top edge — the cue that separates glass from a plain blur. Applied
   via a pseudo-element so it never interferes with content. */
.captions, .panel-block, .auth-card, .ctx-zone, .assign-zone, .mode-card, .conn {
  position: relative;
}
.captions::before, .panel-block::before, .auth-card::before,
.mode-card::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg, var(--glass-spec) 0%, transparent 36%);
  opacity: .38;
}
.captions > *, .panel-block > *, .auth-card > *, .mode-card > * { position: relative; z-index: 1; }

/* Inputs: glass rather than a flat inset well. */
.field-input, textarea.field-input {
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
}
.field-input:focus, textarea.field-input:focus {
  border-color: var(--mode-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mode-accent) 12%, transparent),
              0 1px 0 var(--glass-spec) inset;
}

/* Image wells get the material too, so an empty plate reads as a surface rather
   than a hole. */
.plate-img, .pkg-thumb, .doc-preview {
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
}

/* Toasts, if present, should match. */
.toast, #pwToast, [data-toast] {
  background: var(--nav-fill) !important;
  border: 1px solid var(--nav-edge) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}

/* Center the app pages now that the sidebar is gone — content should sit in the
   same column as the landing page rather than hard against the left. */
.container { max-width: var(--content-max); margin-inline: auto; }
.brief-head, .dash-head, .acct-head, .pkg-head { padding-top: calc(var(--nav-height) + 44px); }

/* ===========================================================================
   STAT STRIP
   Restrained to the landing page's vocabulary: glass panel, mono label, serif
   numeral, one hairline of mode color. (Restored — it was removed with the
   unused app-shell block by mistake.)
   =========================================================================== */

.stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-7);
}
.stat {
  padding: var(--space-4) var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
  border-top: 1.5px solid var(--rule-bright);
  display: flex; flex-direction: column; gap: 6px;
  color: var(--paper);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}
.stat-label { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite); }
.stat-value { font-family: var(--font-display); font-size: 34px; line-height: 1; letter-spacing: -0.015em; }
.stat-note  { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--graphite-dim); }

/* One hairline of color each — the only chromatic cue. */
.stat.s-blue   { border-top-color: #A2A2A6; }
.stat.s-green  { border-top-color: #3B7A57; }
.stat.s-gold   { border-top-color: #C6952F; }
.stat.s-copper { border-top-color: #B25F58; }
.stat.s-teal   { border-top-color: #35797B; }

/* ===========================================================================
   LIGHT-MODE SURFACES
   On a dark field, glass is a light film over darkness — a raised surface reads
   brighter. Inverting that literally (a dark film over white) makes every panel a
   gray slab, which is what the account page's "Connected models" block was: the
   fill is only 4.5% but across a large area it reads as a recessed box rather
   than a raised one.
   On white, a raised surface should be WHITE with a hairline. The blur still
   applies, so content passing behind is still softened — the material is intact,
   only the tint direction is corrected.
   =========================================================================== */

:root[data-theme="light"] .panel,
:root[data-theme="light"] .panel-block,
:root[data-theme="light"] .work-panel,
:root[data-theme="light"] .rail-panel,

/* .course-card is listed in the softened-border block below for the same
   reason .panel was: in light theme the card fill (255) is brighter than the
   page (246), so a border at 220 reads as a seam between two lighter bands
   rather than an outline around one shape. */
:root[data-theme="light"] .rail-card,
:root[data-theme="light"] .course-card,
:root[data-theme="light"] .stat,
:root[data-theme="light"] .tier,
:root[data-theme="light"] .auth-card,
:root[data-theme="light"] .form-panel,
:root[data-theme="light"] .captions,
:root[data-theme="light"] .doc-preview,
:root[data-theme="light"] .byok-note,
:root[data-theme="light"] .mode-card {
  background: rgba(255, 255, 255, 0.86);
  /**
   * A card here is BRIGHTER than the page behind it, so a dark border reads as
   * a seam between two lighter bands rather than an outline around one shape.
   * Measured at .10 it ran page(246) → line(231) → fill(255) — the same sliver
   * fixed on the nav pill, showing on every card in the light theme.
   *
   * Softened to a hairline. Definition comes from the fill being lighter than
   * the page and from the drop shadow underneath, which is how a raised
   * surface reads anyway. Dark mode keeps its own value: there the border is
   * lighter than everything around it and the edge rises cleanly.
   */
  border-color: rgba(19, 20, 23, 0.045);
  box-shadow: 0 1px 2px rgba(19, 20, 23, 0.05), 0 8px 24px rgba(19, 20, 23, 0.07);
}

/* The specular sheen is a white gradient — invisible on a white panel, and it
   was adding a faint band across the top. Off in light mode. */
:root[data-theme="light"] .panel::before,
:root[data-theme="light"] .panel-block::before,
:root[data-theme="light"] .work-panel::before,
:root[data-theme="light"] .rail-card::before,
:root[data-theme="light"] .course-card::before,
:root[data-theme="light"] .stat::before,
:root[data-theme="light"] .tier::before,
:root[data-theme="light"] .auth-card::before,
:root[data-theme="light"] .form-panel::before,
:root[data-theme="light"] .captions::before,
:root[data-theme="light"] .mode-card::before {
  display: none;
}

/**
 * Ticked panels: sheen and corner ticks SHARE ::before, in one rule.
 *
 * ::after belongs to the lensed rim, so both ticks moved here — but the
 * specular sheen above also claims ::before on these panels, and two rules on
 * one pseudo merge per property: the sheen's background silently deleted the
 * tick strokes (the original border-drawn ticks survived it only because
 * borders are not backgrounds). So this later rule paints BOTH: the sheen as
 * layer one with its 0.4 baked into the color, the four tick strokes at half
 * strength after it. The light variant drops the sheen layer, matching the
 * sheen-off decision above.
 */
.panel.ticked::before {
  content: ''; position: absolute; inset: -1px; border-radius: inherit;
  pointer-events: none; opacity: 1;
  background-image:
    linear-gradient(180deg, color-mix(in srgb, var(--glass-spec) 40%, transparent) 0%, transparent 38%),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent));
  background-position: 0 0, left top, left top, right bottom, right bottom;
  background-size: 100% 100%, 9px 1px, 1px 9px, 9px 1px, 1px 9px;
  background-repeat: no-repeat;
}
:root[data-theme="light"] .panel.ticked::before {
  background-image:
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent)),
    linear-gradient(color-mix(in srgb, var(--mode-accent) 50%, transparent), color-mix(in srgb, var(--mode-accent) 50%, transparent));
  background-position: left top, left top, right bottom, right bottom;
  background-size: 9px 1px, 1px 9px, 9px 1px, 1px 9px;
  background-repeat: no-repeat;
}

/* Inputs are recessed, not raised, so a faint inset tint is correct there —
   but lighter than the panel fill was, and with a clear border. */
:root[data-theme="light"] .field-input,
:root[data-theme="light"] textarea.field-input {
  background: rgba(255, 255, 255, 0.92);
  border-color: rgba(19, 20, 23, 0.14);
  box-shadow: inset 0 1px 2px rgba(19, 20, 23, 0.04);
}

/* Row separators inside panels were rgba(255,255,255,.07) — white on white,
   invisible. Darken them so the connection rows read as separate. */
:root[data-theme="light"] .conn,
:root[data-theme="light"] .row,
:root[data-theme="light"] .rail-item,
:root[data-theme="light"] .cap-row {
  border-top-color: rgba(19, 20, 23, 0.10);
}

/**
 * The FIRST row has nothing above it to separate from.
 *
 * Its top border landed on the panel's inner edge instead, reading as a dark
 * seam across the top of the card — the same artefact as the stacked specular
 * highlights, arriving by a different route. `.rail-item` and `.note-row`
 * already reset this; these were missed.
 */
.conn:first-child, .row:first-child, .cap-row:first-child { border-top: none; }

/* ===========================================================================
   MOTION PERFORMANCE
   Nothing in the interface declared will-change, so the browser had no warning
   which elements were about to animate and recomposited them from scratch each
   frame. These are the only elements that animate continuously or on a hot path;
   will-change is deliberately NOT applied broadly, because promoting everything
   costs memory and makes things worse.
   =========================================================================== */

.morph-panel   { will-change: opacity, transform; }
.pill-drawer   { will-change: max-height, opacity; }
.course-card,
.mode-card,
.btn           { will-change: transform; }

/* Elements that animate opacity+transform get their own layer so the compositor
   never has to repaint the content beneath them. */
.morph-panel,
.pill-drawer { transform: translateZ(0); backface-visibility: hidden; }

/* Easing.
   The default ease-out is fine for UI acknowledgment but reads mechanical on
   large surfaces. These curves decelerate harder at the end, which is what makes
   motion feel like it has mass rather than like a value being interpolated. */
:root {
  --ease-glide: cubic-bezier(0.16, 1, 0.3, 1);      /* long, soft settle */
  --ease-enter: cubic-bezier(0.22, 1, 0.36, 1);     /* arrival */
}

.morph-panel {
  transition: opacity 520ms var(--ease-glide),
              transform 520ms var(--ease-glide);
}
.pill-drawer {
  transition: max-height 420ms var(--ease-glide),
              opacity 260ms var(--ease-enter),
              padding 420ms var(--ease-glide);
}
.course-card:hover, .mode-card:hover { transition-timing-function: var(--ease-enter); }

/* Scrolling.
   The hero is a sticky scroll sequence; letting the browser smooth-scroll on
   anchor jumps keeps it continuous instead of teleporting mid-sequence. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .morph-panel, .pill-drawer, .course-card, .mode-card, .btn { will-change: auto; }
}

/* Media should never be resampled during layout — fixes soft edges on the
   loading reel while the viewport is being resized. */
video, img { image-rendering: auto; }
.reel-frame video { will-change: transform; transform: translateZ(0); }

/* ===========================================================================
   ENTRY MODE + OUTPUT FORMAT
   =========================================================================== */

/* The samples strip: quieter than the CTA, but the only thing on the page
   that is evidence rather than argument. */
.morph-samples {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--space-3) var(--space-4); margin-top: var(--space-4); max-width: 92vw;
}
.morph-samples-lead {
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--graphite-dim);
}
.morph-sample {
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 7px 14px; border: 1px solid var(--glass-edge); border-radius: 999px;
  color: var(--paper-dim); transition: border-color .18s ease, color .18s ease;
}
.morph-sample b { font-weight: 500; font-size: var(--text-sm); }
.morph-sample span {
  font-family: var(--font-annot); font-size: 9px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--graphite);
}
.morph-sample:hover { border-color: var(--paper); color: var(--paper); }

/* The secondary hero action. Quieter than the button on purpose: it is for
   the reader who wants evidence before they want an account. */
.morph-proof {
  display: inline-block; margin-top: var(--space-3);
  font-family: var(--font-annot); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--graphite);
  border-bottom: 1px solid var(--glass-edge); padding-bottom: 2px;
}
.morph-proof:hover { color: var(--paper); border-color: var(--paper); }

/* A quiet line under a field, explaining a choice without shouting it. */
.field-note {
  margin: var(--space-2) 0 0; font-size: var(--text-xs); line-height: 1.5;
  color: var(--graphite); max-width: 44ch;
}

.entry-toggle {
  display: inline-flex; gap: 2px; margin-bottom: var(--space-3);
  /**
   * The track takes the PILL radius, not --radius-md.
   *
   * The tabs inside are 999px, so a 14px track put a fully round shape inside
   * a rounded rectangle: at the ends the two curves ran at visibly different
   * rates and the 3px padding pinched. A pill inside a pill is the only pair
   * of curves that stay parallel at every point, whatever the height.
   */
  padding: 3px; border-radius: 999px;
  background: var(--glass-fill); border: 1px solid var(--glass-edge);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}
.entry-tab {
  font: inherit; font-size: var(--text-sm);
  padding: 8px 16px; border: none; border-radius: 999px;
  background: transparent; color: var(--graphite); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.entry-tab:hover { color: var(--paper); }
.entry-tab.on { background: var(--paper); color: var(--bg); }
.entry-note {
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .05em;
  color: var(--graphite-dim); margin-bottom: var(--space-5);
}
#promptText { min-height: 190px; resize: vertical; line-height: 1.65; font-size: var(--text-sm); }

/* The structured fields dim rather than disappear in prompt mode — a studio may
   want to paste a brief and still set a style preset. */
#dynamicFields, #moodWrap { transition: opacity var(--dur-base) var(--ease-out); }

/* ===========================================================================
   HOVER — WEIGHT RATHER THAN ACKNOWLEDGMENT
   The previous treatment was translateY(-3px) over 180ms: a card hopping to say
   it heard you. What reads as premium is the opposite — the element barely moves
   but takes a long time to settle, so the response feels like mass rather than a
   toggle. Three rules follow from that:
     1. Scale, do not translate. A 1.5% scale reads as approach; a 3px jump reads
        as a state change.
     2. Long, front-loaded easing. Most of the movement happens early and the
        last 10% takes half the duration.
     3. Nothing overshoots. No bounce, no elastic, no spring.
   =========================================================================== */

:root {
  --dur-lux: 560ms;
  /* Decelerates hard and holds — the curve that makes motion feel weighted. */
  --ease-lux: cubic-bezier(0.16, 1, 0.24, 1);
}

/* Cards ------------------------------------------------------------------- */
/**
 * No baseline `transform` here.
 *
 * It used to read `transform: translateZ(0) scale(1)`, which is identity — and
 * identity still overwrites. `.reveal` sets `transform: translateY(22px)` at the
 * same specificity ~1275 lines earlier, so any card that also reveals lost its
 * entrance offset entirely: the pricing tiers faded in flat while every other
 * revealed element rose. An identity transform is the easiest kind of override
 * to miss, because it changes nothing on its own.
 *
 * `will-change: transform` already gets the compositing layer that translateZ(0)
 * was there for, and a transition from `none` to a scale interpolates correctly
 * from the identity matrix, so nothing is lost by dropping it.
 */
.mode-card, .course-card, .tier, .rail-card, .stat {
  border-radius: var(--radius-xl);
  transition: transform var(--dur-lux) var(--ease-lux),
              box-shadow var(--dur-lux) var(--ease-lux),
              border-color var(--dur-lux) var(--ease-lux),
              background var(--dur-lux) var(--ease-lux);
  will-change: transform, box-shadow;
}
.mode-card:hover, .course-card:hover, .tier:hover, .rail-card:hover,
.mode-card:focus-visible, .course-card:focus-visible {
  /* Deliberately small. The shadow does the lifting, not the transform. */
  transform: translateZ(0) scale(1.014);
  border-color: var(--rule-bright);
  box-shadow: 0 1px 0 var(--glass-spec) inset,
              0 18px 44px -12px rgba(0,0,0,.55),
              0 2px 8px rgba(0,0,0,.2);
}
:root[data-theme="light"] .mode-card:hover,
:root[data-theme="light"] .course-card:hover,
:root[data-theme="light"] .tier:hover {
  box-shadow: 0 2px 4px rgba(19,20,23,.05),
              0 20px 44px -14px rgba(19,20,23,.18);
}

/* The selected mode sits forward permanently, so selection reads as position
   rather than only as color. */
.mode-card.active {
  transform: translateZ(0) scale(1.02);
  /* ::before already lays a sheen on this card; the inset would double it. */
  box-shadow: 0 14px 36px -14px rgba(0,0,0,.5);
}

/* The mode band grows from its center rather than appearing — the one place a
   reveal is better than a fade, because it echoes the mark assembling. */
.mode-card .mode-swatch, .course-band {
  /* Always full width. A bar that changes length between cards reads as a
     layout fault rather than as selection — the state belongs in weight. */
  transform: none;
  opacity: .38;
  transition: opacity var(--dur-lux) var(--ease-lux),
              filter var(--dur-lux) var(--ease-lux);
}
.mode-card:hover .mode-swatch, .course-card:hover .course-band { opacity: .78; }
.mode-card.active .mode-swatch { opacity: 1; filter: saturate(1.15); }

/* Buttons ----------------------------------------------------------------- */
.btn {
  border-radius: 999px;                    /* pill — softest possible */
  transition: transform var(--dur-base) var(--ease-lux),
              background var(--dur-base) var(--ease-lux),
              border-color var(--dur-base) var(--ease-lux),
              box-shadow var(--dur-base) var(--ease-lux);
}
.btn:hover { transform: scale(1.022); }
.btn:active { transform: scale(.988); transition-duration: 90ms; }

/* Inputs ------------------------------------------------------------------ */
.field-input, textarea.field-input, .ctx-zone, .assign-zone, .drop {
  border-radius: var(--radius-md);
  transition: border-color var(--dur-base) var(--ease-lux),
              box-shadow var(--dur-base) var(--ease-lux),
              background var(--dur-base) var(--ease-lux);
}

/* Panels and plates ------------------------------------------------------- */
.panel, .panel-block, .work-panel, .rail-panel, .form-panel, .auth-card,
.captions, .doc-preview, .byok-note, .pill-status {
  border-radius: var(--radius-lg);
}
.plate-img, .pkg-thumb {
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform var(--dur-lux) var(--ease-lux);
}
/* The plate scales inside its frame while the frame holds — the depth cue that
   reads as a photograph being approached rather than a box being nudged. */
.plate:hover .plate-img, .course-card:hover .pkg-thumb { transform: scale(1.03); }
.plate { border-radius: var(--radius-md); overflow: hidden; }

/* Links ------------------------------------------------------------------- */
/* Underline grows from the left rather than fading in. */
.nav-links a:not(.btn), .pill-row {
  position: relative;
}
/**
 * The nav links used to grow a 1px underline on hover — `background:
 * currentColor` at .5 opacity, wiped in with scaleX. Because it inherited the
 * text color it flipped with the theme: a black sliver on the light pill, a
 * white one on the dark. It read as a stray edge rather than an underline,
 * since the hover fill already draws a rounded chip and a straight rule inside
 * a pill has no shape to belong to.
 *
 * Removed deliberately. The hover state is the fill and the ink lift, which is
 * enough — this is a nav, not body copy where an underline carries meaning.
 * Only these links lost it; nothing else in the app used this rule.
 */

@media (prefers-reduced-motion: reduce) {
  .mode-card, .course-card, .tier, .rail-card, .stat, .btn,
  .plate-img, .pkg-thumb { transition: none; transform: none !important; }
}

/* A required field that was left blank. Marked rather than merely named, because
   the error message renders by the submit button and the field can be most of a
   screen above it. */
.field-input.is-missing,
textarea.field-input.is-missing {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 14%, transparent);
}
.field-input.is-missing::placeholder { color: color-mix(in srgb, var(--danger) 55%, var(--graphite-dim)); }

/* ===========================================================================
   TEMPLATE EDITOR
   =========================================================================== */

.tpl-intro { font-size: var(--text-sm); color: var(--graphite); line-height: 1.6; margin: 0 0 var(--space-5); }
.tpl-group { margin-bottom: var(--space-5); }
.tpl-group-label { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite-dim); margin-bottom: var(--space-3); }
.tpl-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4); border-radius: var(--radius-md);
  border: 1px solid var(--glass-edge); background: var(--glass-fill);
  margin-bottom: 8px;
}
.tpl-name { font-size: var(--text-sm); }
.tpl-meta { font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--graphite-dim); margin-top: 3px; }
.tpl-default { font-family: var(--font-annot); font-size: 8.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--mode-accent); border: 1px solid currentColor; border-radius: 999px; padding: 1px 7px; margin-left: 6px; }
.tpl-empty { font-size: var(--text-sm); color: var(--graphite-dim); }

/* Plates ------------------------------------------------------------------ */
.tpl-plates { display: grid; gap: 8px; margin: var(--space-4) 0; }
.tpl-plate {
  display: grid; grid-template-columns: 22px 1fr auto; gap: var(--space-3);
  align-items: start; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-edge); background: var(--glass-fill);
  transition: border-color var(--dur-base) var(--ease-lux), opacity var(--dur-base) var(--ease-lux);
}
/* The anchor is marked structurally, not just by a radio button — it is the
   plate every other one is generated against, so it should look different. */
.tpl-plate.is-anchor { border-color: var(--mode-accent); }
.tpl-plate.dragging  { opacity: .4; }
.tpl-plate.drag-over { border-color: var(--marker); }
.tpl-plate-grip { cursor: grab; color: var(--graphite-dim); font-size: 13px; line-height: 1.9; letter-spacing: -2px; user-select: none; }
.tpl-plate-grip:active { cursor: grabbing; }
.tpl-plate-body { display: grid; gap: 6px; }
.tpl-plate-label { font-size: var(--text-sm); }
.tpl-plate-framing { font-size: var(--text-xs); }
.tpl-plate-side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.tpl-anchor { display: flex; align-items: center; gap: 6px; font-family: var(--font-annot); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--graphite); cursor: pointer; }
.tpl-remove { background: none; border: none; color: var(--graphite-dim); font-size: 16px; line-height: 1; cursor: pointer; padding: 0 2px; }
.tpl-remove:hover { color: var(--danger); }

/* Review notes ------------------------------------------------------------ */
.tpl-notes { display: grid; gap: 5px; margin: var(--space-4) 0; }
.tpl-note { font-family: var(--font-annot); font-size: 10px; line-height: 1.6; letter-spacing: .04em; padding: 7px 10px; border-radius: var(--radius-sm); border-left: 2px solid var(--rule-bright); color: var(--graphite); }
.tpl-note.warn { border-left-color: var(--warn); color: var(--paper); }
.btn.small { padding: 6px 13px; font-size: var(--text-xs); }

/* ===========================================================================
   COVERAGE + SPEAKER NOTES
   =========================================================================== */

.qa-hint { font-size: var(--text-sm); color: var(--graphite); line-height: 1.6; margin: 0 0 var(--space-4); }
.cov-verdict {
  font-family: var(--font-annot); font-size: 10.5px; letter-spacing: .05em; line-height: 1.6;
  padding: 10px 12px; border-radius: var(--radius-sm);
  border-left: 2px solid var(--rule-bright); margin: var(--space-4) 0;
}
.cov-verdict.ok   { border-left-color: var(--ok); }
.cov-verdict.warn { border-left-color: var(--warn); }
.cov-verdict.bad  { border-left-color: var(--danger); color: var(--paper); }

.cov-list { display: grid; gap: 6px; }
.cov-row {
  display: grid; grid-template-columns: 96px 1fr; gap: var(--space-3);
  align-items: start; padding: 9px 11px;
  border-radius: var(--radius-sm); background: var(--glass-fill);
  border: 1px solid var(--glass-edge);
}
.cov-status {
  font-family: var(--font-annot); font-size: 8.5px; letter-spacing: .1em;
  text-transform: uppercase; padding-top: 3px;
}
.cov-row.ok   .cov-status { color: var(--ok); }
.cov-row.warn .cov-status { color: var(--warn); }
.cov-row.bad  .cov-status { color: var(--danger); }
.cov-row.dim  .cov-status { color: var(--graphite-dim); }
.cov-text { font-size: var(--text-sm); }
.cov-note { display: block; font-family: var(--font-annot); font-size: 9.5px; line-height: 1.6; color: var(--graphite-dim); margin-top: 4px; }

.notes-list { display: grid; gap: 8px; }
.note-row { display: grid; grid-template-columns: 46px 1fr; gap: var(--space-3); align-items: start; padding: 8px 0; border-top: 1px solid var(--rule); }
.note-row:first-child { border-top: none; }
.note-time { font-family: var(--font-annot); font-size: 9.5px; color: var(--graphite-dim); padding-top: 2px; }
.note-body { font-size: var(--text-sm); line-height: 1.6; }
.note-body b { display: block; font-weight: 500; margin-bottom: 2px; }
.note-cue { display: block; font-family: var(--font-annot); font-size: 9.5px; color: var(--marker); font-style: normal; margin-top: 4px; }

/* ===========================================================================
   SUPPORT WIDGET
   Bottom-right, opening upward, so it never covers the primary action. Same
   glass and geometry as everything else.
   =========================================================================== */

.support-root { position: fixed; right: 20px; bottom: 20px; z-index: 60; }
.support-toggle {
  width: 46px; height: 46px; border-radius: 999px;
  background: var(--nav-fill); border: 1px solid var(--nav-edge);
  color: var(--paper); cursor: pointer;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  box-shadow: 0 1px 0 var(--glass-spec) inset, 0 10px 30px -10px rgba(0,0,0,.5);
  transition: transform var(--dur-lux) var(--ease-lux), box-shadow var(--dur-lux) var(--ease-lux);
  display: grid; place-items: center;
}
.support-toggle:hover { transform: scale(1.05); }
.support-toggle-mark { font-family: var(--font-display); font-size: 19px; line-height: 1; }
.support-root.open .support-toggle { transform: scale(.94); opacity: .7; }

.support-panel {
  position: absolute; right: 0; bottom: 58px;
  width: min(360px, calc(100vw - 40px));
  max-height: min(520px, calc(100vh - 120px));
  display: flex; flex-direction: column;
  background: var(--nav-fill); border: 1px solid var(--nav-edge);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  box-shadow: 0 1px 0 var(--glass-spec) inset, 0 24px 60px -16px rgba(0,0,0,.55);
  overflow: hidden;
}
/* An explicit `display` on the element beats the `hidden` attribute's UA style,
   so setting panel.hidden = true did nothing and the close button appeared
   broken. Any element given a display value needs this alongside it. */
.support-panel[hidden] { display: none; }

:root[data-theme="light"] .support-panel { box-shadow: 0 2px 6px rgba(19,20,23,.06), 0 24px 60px -18px rgba(19,20,23,.2); }

.support-head { display: flex; align-items: center; justify-content: space-between; padding: var(--space-4) var(--space-4) var(--space-3); }
.support-close { background: none; border: none; color: var(--graphite-dim); font-size: 18px; line-height: 1; cursor: pointer; }
.support-close:hover { color: var(--paper); }

.support-log { flex: 1; overflow-y: auto; padding: 0 var(--space-4); display: grid; gap: var(--space-3); align-content: start; }
.support-empty { font-size: var(--text-sm); color: var(--graphite-dim); line-height: 1.6; padding: var(--space-3) 0; }
.support-msg { display: grid; gap: 3px; }
.support-who { font-family: var(--font-annot); font-size: 8.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite-dim); }
.support-text { font-size: var(--text-sm); line-height: 1.6; white-space: pre-wrap; }
.support-msg.user .support-text { color: var(--graphite); }
.support-msg.pending .support-text { color: var(--graphite-dim); }

.support-form { display: flex; gap: 8px; padding: var(--space-3) var(--space-4); }
.support-form .field-input { flex: 1; font-size: var(--text-sm); }
.support-foot { font-family: var(--font-annot); font-size: 9px; line-height: 1.6; letter-spacing: .04em; color: var(--graphite-dim); padding: 0 var(--space-4) var(--space-4); }
.support-foot a { color: var(--marker); }

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
@media (max-width: 640px) { .two-col { grid-template-columns: 1fr; } }

/* Plate re-render control and share URLs. */
.plate-redo {
  background: none; border: none; color: var(--graphite-dim); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 0 0 8px;
  transition: color var(--dur-base) var(--ease-lux);; border-radius: 999px; }
.plate-redo:hover { color: var(--paper); }
.plate-redo:disabled { opacity: .4; cursor: default; }
.share-url { font-family: var(--font-annot); font-size: 10px; word-break: break-all; }

/* Legal links in the footer. Required by every merchant of record, and the
   place users look for them first. */
.footer-links { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.footer-links a {
  font-family: var(--font-annot); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--graphite-dim); text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
  /* Padding rather than a larger type size: the 10px mono is the intended
     look, and only the hit area was short of the 24px minimum. Negative
     margin keeps the visual position identical to before. */
  display: inline-flex; align-items: center; min-height: 24px;
  padding: 4px 2px; margin: -4px -2px;
}
.footer-links a:hover { color: var(--paper); }

/* ===========================================================================
   LIQUID GLASS — alignment with Apple's material (macOS 26 / iOS 26)
   ===========================================================================

   What Apple's Liquid Glass actually is, and why the existing .glass rule is
   only most of the way there.

   Ordinary frosted glass SCATTERS light: a blur, evenly, across the surface.
   Liquid Glass LENSES it — it bends and concentrates light the way a thick
   piece of real glass does, so the distortion is strongest at the rim and the
   center stays comparatively clear. That rim behavior is the single cue that
   separates the two materials at a glance, and it is what the blur-only version
   below was missing.

   There is no Liquid Glass CSS API. Apple's own implementation is a private
   property behind a WebKit preference and is not available to the open web, so
   every web version is an approximation. This one is built from standard
   backdrop-filter and gradients, which means it degrades predictably and looks
   the same in Safari, Chrome and Firefox rather than only being right on a Mac.

   Three HIG rules this follows, because they are what make the material read as
   Apple's rather than as generic glassmorphism:

     LAYERING     Glass belongs to the functional layer — navigation, controls,
                  transient surfaces. Never the content layer. Content sits on
                  opaque floors, which this codebase already does.
     NO STACKING  Glass over glass turns muddy and illegible. Nested surfaces
                  drop to a flat fill.
     LEGIBILITY   Never long-form text on glass, and always honour Reduce
                  Transparency.
   =========================================================================== */

:root {
  /* Rim lensing. Concentrated at the edge, clearing toward the middle — this
     is the refraction cue, and it is why the value is a gradient rather than a
     single blur radius. */
  --lg-rim:        rgba(255,255,255,.30);
  --lg-rim-soft:   rgba(255,255,255,.07);
  --lg-depth:      rgba(0,0,0,.22);      /* the shadow a thick edge casts inward */
  --lg-lens:       112%;                  /* uniform lift only whispers now — the
                                             concentration of light lives in the rim,
                                             which is where Apple's material puts it,
                                             and a smaller lift cannot clip a hot
                                             backdrop to white on GPU paths */
}
:root[data-theme="light"] {
  --lg-rim:        rgba(255,255,255,.85);
  --lg-rim-soft:   rgba(19,20,23,.05);
  /**
   * Light theme stacks TWO dark rings on a panel's top edge — this contour and
   * the 1px border — over a fill that is BRIGHTER than the page behind it. The
   * result read page(246) → line(231) → fill(255): a dark seam sandwiched
   * between two lighter bands, which is the same sliver fixed on the nav pill.
   *
   * Dark mode has no such problem; there the rings are lighter than everything
   * around them and the edge rises cleanly. So this is softened for light only,
   * and the panel is defined by its fill and its drop shadow instead.
   */
  --lg-depth:      rgba(19,20,23,.03);
  --lg-lens:       112%;
}

/* Size-aware material. Apple's glass gets thicker as it gets bigger; ours is
   one recipe for a 58px pill and a 700px panel. The nav pair takes the thin
   end — less blur, a touch less saturation — so small type stays crisp and the
   heavy lensed slab is reserved for the panels that can carry it. */
.site-nav, .pill-drawer { --glass-blur: 13px; --glass-sat: 140%; }

/**
 * The lensed rim.
 *
 * ::after rather than ::before because ::before already carries the specular
 * top edge, and the two cues are different things: the specular highlight is
 * light landing ON the surface, the rim is light bending THROUGH it. Stacking
 * them is what produces thickness.
 */
.glass::after,
.panel::after,
.site-nav::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; z-index: 0;
  /* Bright at the rim, transparent by ~14% in — the lensing falloff. */
  background:
    linear-gradient(180deg, var(--lg-rim) 0%, transparent 14%, transparent 86%, var(--lg-rim-soft) 100%),
    linear-gradient(90deg,  var(--lg-rim-soft) 0%, transparent 6%, transparent 94%, var(--lg-rim-soft) 100%);
  /* An inner shadow reads as the thickness of the edge rather than as a border. */
  box-shadow: inset 0 0 0 .5px var(--lg-rim-soft), inset 0 -8px 18px -12px var(--lg-depth);
  opacity: .9;
}
/**
 * The ::after rim needs a positioned ancestor, which is all this rule is for.
 *
 * `.site-nav` must NOT be in it. The pill is `position: fixed` (L700), and this
 * rule sits ~1100 lines later at identical specificity, so it silently won the
 * cascade and turned the pill back into a `relative` element — it scrolled away
 * with the page. Nothing about it looked wrong: both declarations are correct in
 * isolation, and the loser is the one that reads like the real intent.
 *
 * `fixed` already establishes a containing block for absolutely positioned
 * descendants, so the rim works without any of this.
 */
.panel { position: relative; }
.panel > *, .site-nav > * { position: relative; z-index: 1; }

/* Light concentrates rather than merely scattering. brightness() is the whole
   difference between "blurred" and "lensed" at the same blur radius. */
.glass, .panel, .site-nav, .pill-drawer {
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) brightness(var(--lg-lens));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) brightness(var(--lg-lens));
}

/**
 * No glass on glass.
 *
 * Two translucent surfaces over one another compound their blur and the result
 * is neither transparent nor opaque — just muddy, and unreadable at small
 * sizes. Apple's guidance is explicit about it. A nested surface keeps the
 * fill and the rim but drops its own backdrop work.
 */
/* The list below is every selector in this file that sets a real
   backdrop-filter, so the rule is closed by construction: any surface nested
   inside any other surface drops its own backdrop work, and adding a new glass
   class to the list above is the only way to opt in. Enumerating pairs by hand
   is what let `.site-nav > .pill-drawer` through — a double lens pass that put a
   hard brightness step across the pill at the drawer's top edge. Both sides use
   :is() so the rule outranks every single-class lens rule regardless of order. */
:is(
  .glass, .panel, .site-nav, .pill-drawer, .pill-status, .modal-backdrop,
  .work-panel, .rail-panel, .rail-card, .course-card, .stat, .tier,
  .auth-card, .form-panel, .captions, .panel-block, .ctx-zone, .assign-zone,
  .drop, .seg, .field-input, textarea.field-input, .island, .app-rail,
  .mode-card, .plate-img, .pkg-thumb, .doc-preview, .byok-note, .conn,
  .theme-toggle, .entry-toggle, .support-panel, .support-toggle,
  .toast, #pwToast, [data-toast], .nav-links a:hover,
  .btn:not(.btn-primary):not(.btn-mode)
)
:is(
  .glass, .panel, .site-nav, .pill-drawer, .pill-status, .modal-backdrop,
  .work-panel, .rail-panel, .rail-card, .course-card, .stat, .tier,
  .auth-card, .form-panel, .captions, .panel-block, .ctx-zone, .assign-zone,
  .drop, .seg, .field-input, textarea.field-input, .island, .app-rail,
  .mode-card, .plate-img, .pkg-thumb, .doc-preview, .byok-note, .conn,
  .theme-toggle, .entry-toggle, .support-panel, .support-toggle,
  .toast, #pwToast, [data-toast], .nav-links a:hover,
  .btn:not(.btn-primary):not(.btn-mode)
) {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/**
 * Compositor hardening for the lens.
 *
 * The one configuration that renders these panels solid white on some
 * Windows hardware-compositing paths (reported on Edge, dark theme) is a
 * brightness() backdrop sampling the animated particle canvas: the canvas
 * repaints every frame in its own accelerated texture, and when the backdrop
 * read misses that texture the sample comes back unpainted — white — and
 * brightness(var(--lg-lens)) clips it the rest of the way to pure white.
 * Software rasterisation (this repo's headless checks) composites the same
 * stack correctly, which is why it never reproduces here.
 *
 * Two hints, both proven visually inert by before/after pixel diff:
 *   - the canvas gets its own committed layer — the same treatment
 *     .reel-frame video already gets above — so the backdrop always has a
 *     stable texture to read rather than an in-flight repaint;
 *   - each lens carrier gets will-change: transform rather than
 *     translateZ(0), because the .reveal note above records how a base
 *     transform silently overwrites animated ones. will-change promotes the
 *     layer without touching the property, and no fixed-position element
 *     lives inside any carrier (checked across dashboard, brief, account),
 *     so its containing-block side effect is moot.
 *   - .pill-drawer already carries translateZ(0) and needs nothing more.
 */
/**
 * Only the CANVAS gets a committed layer.
 *
 * The glass carriers were promoted here too, which was overreach: the backdrop
 * sampling bug was about the canvas repainting in its own accelerated texture,
 * and giving that texture a stable home is what fixes it. Promoting the
 * carriers as well bought nothing and cost something — these elements are
 * centered with translateX(-50%), which lands on a fractional pixel for any odd
 * width (measured x=381.609), and a composited layer rasterised at a half-pixel
 * offset renders text softer than the same text drawn straight to the page.
 * They also hold most of the interface's type. The lens is protected by the
 * brightness reduction in --lg-lens, not by these promotions.
 */
body > canvas { will-change: transform; transform: translateZ(0); backface-visibility: hidden; }

/* The opaque-fill treatment stays narrow: it is right for a large surface
   sitting on another large surface, and wrong for an input or a button, which
   should keep its own translucent fill and simply stop blurring. */
.glass .glass, .glass .panel, .panel .panel, .panel .glass,
.site-nav .glass, .site-nav .panel {
  background: var(--bg-raised);
}
.glass .glass::after, .glass .panel::after, .panel .panel::after { opacity: .4; }

/**
 * Reduce Transparency.
 *
 * A system accessibility setting, and one Apple names directly in the Liquid
 * Glass guidance. People turn it on because translucency makes interfaces hard
 * to read — often for vestibular or low-vision reasons — so honouring it is not
 * a nicety. Everything becomes an opaque surface with a real border: the
 * hierarchy survives, the material does not.
 */
@media (prefers-reduced-transparency: reduce) {
  .glass, .panel, .site-nav, .pill-drawer {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: var(--bg-raised) !important;
    border-color: var(--rule) !important;
  }
  .glass::after, .panel::after, .site-nav::after,
  .glass::before, .panel::before { display: none !important; }
}

/* Forced high contrast: the material is decoration and decoration loses. */
@media (prefers-contrast: more) {
  .glass, .panel, .site-nav {
    backdrop-filter: none; -webkit-backdrop-filter: none;
    background: var(--bg-raised); border-color: var(--paper);
  }
  .glass::before, .panel::before, .site-nav::before,
  .glass::after, .panel::after, .site-nav::after { display: none; }
  body::before { display: none; }
}

/**
 * Concentric radii.
 *
 * Apple nests corner radii so an inner element's curve is concentric with its
 * container's rather than parallel to it: inner = outer − padding. Mismatched
 * curves are the detail that makes an interface read as almost-native.
 */
:root { --r-outer: 20px; --r-inner: 12px; --r-pill: 999px; }
.panel { border-radius: var(--r-outer); }
/* Inputs take the inner radius; BUTTONS DO NOT. This rule was quietly turning
   every button inside a panel back into a 12px rectangle — which is most of
   the buttons in the product, since most of the product is panels. The pill is
   the shape of a button here regardless of what it sits in. */
.panel .field-input { border-radius: var(--r-inner); }

/* ---------------------------------------------------------------------------
   Native-shell affordances.

   Read by the macOS wrapper, which turns the window background transparent and
   lets the system's real Liquid Glass show through behind the page. In a plain
   browser this class is never set and nothing below applies.
   --------------------------------------------------------------------------- */
:root.pw-native {
  /* The window's own material is doing the work; a second opaque layer would
     hide it and produce a flat rectangle inside a translucent window. */
  --bg: transparent;
}
:root.pw-native body { background: transparent; }
/* The traffic-light buttons sit top-left; content clears them. */
:root.pw-native .site-nav { padding-left: 78px; }
/* The page's own field would fight the system material behind it. */
:root.pw-native .morph-canvas { opacity: .5; }

/* ---------------------------------------------------------------------------
   TAP TARGET FLOOR (WCAG 2.2 SC 2.5.8, AA)
   ---------------------------------------------------------------------------
   A handful of standalone controls rendered under the 24x24 minimum on a phone:
   the nav mark (16x20), the package card's title and Board/Document links
   (16-20px tall), and the legal pages' back link (18px). None of them are links
   inside a sentence, so the inline exemption does not cover them.

   The fix is hit area only. Padding raises the box to 24px and an equal negative
   margin pulls the layout back to where it was, so nothing moves visually and
   no type size changes — the designed look is untouched, the target is not.
   ------------------------------------------------------------------------- */
/* The title's wrapper link is gone — the whole card is the link now
   (.course-link below), so the :has() rule that sized it went with it. */
.nav-mark,
.course-actions a,
.rail-item a,
a.back {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding-block: 4px;
  margin-block: -4px;
}
.course-actions { align-items: center; }
/* The mark is a small square glyph — the height rule above left it 16px wide,
   so the floor has to apply on both axes for it to be a real 24px target. */
.nav-mark { min-width: 24px; justify-content: center; }

/* ---------------------------------------------------------------------------
   Small-screen legibility floor.

   LAST IN THE FILE ON PURPOSE. A media query adds no specificity, so these
   would lose to the identical-specificity rules defined further down — which
   is exactly what happened when this block sat mid-file. Nothing below this
   point should redefine these sizes.

   9px annotation type is fine on a desktop pill and unreadable on a 320px
   phone; it lifts to 11px below the phone breakpoint only, so the designed
   sizes at every other width are untouched.
   ------------------------------------------------------------------------ */
@media (max-width: 430px) {
  .pill-row-hint { font-size: 11px; }
  .support-foot  { font-size: 11px; }
}

/* An inline action inside a note: a real button, sized to sit in a sentence
   rather than beside one. Used where an explanation would otherwise describe
   where to find a control instead of simply being it. */
.btn-inline {
  display: inline-flex; align-items: center;
  padding: 5px 14px; margin-left: 6px;
  font-size: 10px; letter-spacing: .08em;
  border-radius: 999px;
}

/* The models line: a reported state, not a control. Sized like a value beside
   its label rather than something you press. */
.engine-state {
  font-family: var(--font-annot); font-size: var(--text-xs);
  color: var(--graphite); letter-spacing: .04em;
}
.engine-state b { color: var(--paper); font-weight: 500; }

/* The embedded example package. Given real height because the argument for
   this product is the document itself — a thumbnail proves nothing. */
.sample-tabs { display: flex; gap: 8px; margin-bottom: var(--space-3); flex-wrap: wrap; }
.sample-tab {
  font-family: var(--font-annot); font-size: var(--text-xs);
  letter-spacing: .06em; text-transform: uppercase;
  padding: 7px 16px; border-radius: 999px;
  border: 1px solid var(--rule-bright); background: transparent;
  color: var(--graphite); cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.sample-tab:hover { color: var(--paper); }
.sample-tab.on { color: var(--paper); border-color: var(--paper); }
/* A scrollable stack of page images. Scrolls inside itself so the reader stays
   where they are on the landing page rather than being thrown into a viewer. */
.sample-pages {
  /* Tall enough that a page is readable rather than a thumbnail — the whole
     point is that someone can actually read the writing. */
  height: 700px; overflow-y: auto; overscroll-behavior: contain;
  border: 1px solid var(--rule); border-radius: var(--radius-md);
  background: #FFFFFF; padding: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.sample-pages img { width: 100%; height: auto; display: block; }
.sample-note { font-family: var(--font-annot); font-size: var(--text-xs); color: var(--graphite-dim); margin-top: var(--space-2); }
@media (max-width: 720px) { .sample-pages { height: 440px; } }

/* THE FIXED HEADER EATS ANCHOR TARGETS.
   The nav is position:fixed and 78px tall, so a section scrolled to by an
   anchor lands at y=0 — underneath it. Clicking MODES or PRICING put the
   heading you were aiming at behind the bar you clicked it from. .morph had
   this rule; the sections themselves never did. */
section[id], [id]:target, .sample-pages, .doc-preview { scroll-margin-top: 96px; }
