/* SYNC-FROM-LOOM:sha384-hobVRVipkJ+5cp4SM7y3nyPlp0MkvZ2BrriZklE+z9X6cRW/nBvDWmTLhfPjHWXT — auto-synced by forge render at build */
/* ===================================================================
 * loom-skin.css — semantic component layer for the Loom design system
 * ===================================================================
 *
 * ARCHITECTURE
 * ------------
 * This file is organized into named CSS @layer blocks. The cascade
 * order is fixed once at the top — later layers always win over
 * earlier ones, regardless of source order or specificity. This
 * gives plugin authors a predictable extension point and removes
 * specificity-war footguns.
 *
 * Cascade (lowest precedence first):
 *
 *   reset       universal box-sizing + zero defaults
 *   tokens      surfaces design tokens as CSS custom properties on :root
 *               (also re-exported as component-scoped --comp-* tokens
 *               via inheritance)
 *   primitives  layout primitives (.loom-page, .loom-stack, .loom-row)
 *   components  composite components (.loom-card-battle, .loom-leader,
 *               .loom-hero, .loom-nav, etc.)
 *   plugins     OPEN — downstream apps drop overrides here. Empty
 *               by default. Overrides land via an unwrapped block
 *               in a separate stylesheet imported AFTER this one.
 *   utilities   single-purpose helpers (.loom-sr-only) — wins over
 *               components by design (utilities should be the final
 *               override knob)
 *
 * COMPOSITION MODEL ("mixins / traits / inheritance" in CSS)
 * ----------------------------------------------------------
 * 1. CSS custom properties as MIXINS — any component declares its own
 *    --comp-* tokens that downstream rules can override. E.g.:
 *      .loom-card { --comp-card-pad: var(--loom-space-5); padding: var(--comp-card-pad); }
 *      .loom-panel.is-roomy { --comp-card-pad: var(--loom-space-8); }
 *
 * 2. Attribute-driven VARIANTS — components dispatch on
 *    `data-variant`, `data-tone`, `data-size`, `aria-current`,
 *    `aria-pressed` etc. instead of stacked class names. The
 *    attribute is the schema; the rendered HTML is unambiguous.
 *
 * 3. INHERITANCE via custom-property cascade — parent components
 *    can set --comp-* tokens that children inherit automatically
 *    (CSS custom properties inherit by default).
 *
 * 4. PLUGIN extension — the @layer plugins block is intentionally
 *    empty here. A site that wants a different brand voice writes
 *    its own unwrapped block (e.g. ".loom-brand { ... }") in a
 *    separate file imported after this one. No specificity war.
 *
 * STRICT INVARIANTS (enforced by `loom lint`)
 * -------------------------------------------
 * - No raw color literals (hex / rgb / hsl) outside this file's
 *   tokens layer. Every paint reads through var(--loom-color-*) or
 *   a component-scoped --comp-* alias.
 * - No raw px / em values for spacing, type, or radii. Every
 *   measurement reads through var(--loom-space-*), var(--loom-font-*),
 *   var(--loom-radius-*).
 * - All margins / padding / borders compose from the spacing
 *   token scale.
 * - Every text element gets `overflow-wrap: anywhere` from the
 *   reset layer. Every container gets `min-width: 0` so flex/grid
 *   children don't blow out.
 * - All animations honor `prefers-reduced-motion`.
 * - Mobile-first: breakpoints scale up only.
 *
 * MOBILE-FIRST + STRICT OVERFLOW
 * ------------------------------
 * Default rules target a 320 px viewport. `@media (min-width: …)`
 * upgrades layouts as space grows. The page never produces
 * horizontal scroll: `html { overflow-x: clip; }` and every
 * potentially-long string has `text-overflow: ellipsis` or
 * `overflow-wrap: anywhere`.
 *
 * WHY THIS LAYERING (vs the old flat sheet)
 * -----------------------------------------
 * Plain CSS resolves rules by source-order + specificity. With
 * many contributors + plugin extensions, both become unreliable
 * fast — a deep selector accidentally wins; a later import
 * silently shadows. `@layer` makes the precedence ladder explicit
 * and reviewable. A reviewer reads the layer order at the top and
 * knows the truth without grep'ing the whole sheet.
 * =================================================================== */

/* @layer cascade dropped for older-browser compat */
/* ============================================================ *
 * @layer reset                                                  *
 * ============================================================ */

/* unwrapped @layer */

  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; padding: 0; min-width: 0; }

  /* Diagnostic: the "CSS NOT LOADING" yellow banner in pages is
   * styled with `.loom-css-loaded`. If skin.css applies, the banner
   * disappears. If owner sees the banner, the bug is "browser
   * didn't apply CSS" — verifiable from a single screenshot. */
  .loom-css-loaded { display: none !important; }

  /* WCAG 2.5.5 + iOS HIG floor for every interactive element on
   * mobile-friendly layouts. Pages can opt out per-element via
   * data-tap="compact" for hover-driven desktop UIs (e.g. dense
   * data tables). Anything that wants to be smaller MUST justify
   * itself with that attribute. */
  @media (pointer: coarse), (max-width: 1024px) {
    button:not([data-tap="compact"]),
    a:not([data-tap="compact"]),
    input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([data-tap="compact"]),
    select:not([data-tap="compact"]),
    textarea:not([data-tap="compact"]),
    [role="button"]:not([data-tap="compact"]),
    [role="link"]:not([data-tap="compact"]),
    summary:not([data-tap="compact"]) {
      min-height: var(--loom-tap-min);
      min-width: var(--loom-tap-min);
    }
    /* Inputs and selects also need width — typing inside a 0-pad
     * box makes content scroll mid-character. */
    input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([data-tap="compact"]),
    select:not([data-tap="compact"]),
    textarea:not([data-tap="compact"]) {
      min-width: var(--loom-tap-min);
      padding: var(--loom-space-2) var(--loom-space-3);
    }
  }

  html, body {
    overflow-x: clip;
    max-width: 100%;
    text-size-adjust: 100%;
    -webkit-text-size-adjust: 100%;
  }
  html {
    /* Set bg on html so body's gradient overlays cleanly + the
     * canvas behind any centered .loom-page max-width still
     * reads as the themed surface (was rendering white in
     * crawler screenshots). */
    background: var(--loom-color-bg-canvas);
    color: var(--loom-color-ink);
    color-scheme: light dark;
    min-height: 100%;
  }
  /* Honor OS preference when no explicit data-theme is set. */
  @media (prefers-color-scheme: dark) {
    html:not([data-theme]) {
      color-scheme: dark;
    }
  }
  html { scroll-behavior: smooth; }
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
  }

  body {
    font-family: var(--loom-font-body, -apple-system, BlinkMacSystemFont,
                 "Segoe UI", Roboto, system-ui, sans-serif);
    font-size: var(--loom-font-base);
    line-height: var(--loom-leading-base, 1.5);
    color: var(--loom-color-ink);
    background:
      /* Subtle ambient gradient — gives flat dark a sense of light source. */
      radial-gradient(1200px 600px at 80% -10%,
        hsl(218 83% 65% / 0.08), transparent 60%),
      radial-gradient(900px 500px at -10% 20%,
        hsl(272 84% 65% / 0.06), transparent 60%),
      var(--loom-color-bg-canvas);
    background-attachment: fixed;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: "ss01", "cv11", "cv02";
  }

  img, video, svg, canvas, picture {
    max-width: 100%; height: auto; display: block;
  }

  /* Universal long-content protections. Every text element wraps. */
  p, h1, h2, h3, h4, h5, h6, span, a, button, li, td, th, label,
  blockquote, dt, dd, figcaption {
    overflow-wrap: anywhere;
    hyphens: auto;
  }
  pre, code, kbd, samp { overflow-x: auto; max-width: 100%; }

  a { color: inherit; text-decoration: none; }
  button { font: inherit; cursor: pointer; border: 0; background: none; color: inherit; }

  /* Keyboard focus ring. `:focus-visible` is the modern UA-mediated
   * selector (only fires for keyboard / programmatic-after-keyboard
   * focus); `:focus` is the pre-spec compatibility selector that also
   * fires on programmatic focus calls (which is what crawler runtime
   * detectors use to verify visible focus indicators). Listing both
   * means real users on real browsers get the ring on keyboard nav
   * AND the audit infrastructure detects it during synthetic focus
   * sweeps. The mouse-click side-effect (briefly showing the ring on
   * button click) is the standard trade-off for guaranteeing
   * visibility under all focus-trigger paths. */
  :focus,
  :focus-visible {
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary);
    outline-offset: var(--loom-stroke-thin);
    border-radius: var(--loom-radius-sm);
  }
  /* For mouse clicks, suppress the ring when the browser confirms
   * the focus was NOT keyboard-driven. This restores the
   * keyboard-only-ring UX when :focus-visible is supported. */
  :focus:not(:focus-visible) { outline: none; }

  /* T70c (closes #659): rich focus + hover indicators.
   * CSS-only patterns showing what's focused/hovered/active.
   * All prefers-reduced-motion safe (motion clamps to 0).
   *
   * Opt-in via data-loom-rich-focus="lift|glow|magnetic":
   *
   *   lift:     subtle Y-translate + shadow gain on hover/focus.
   *   glow:     halo of accent color expands on focus-visible.
   *   magnetic: pointer-pulled translate via JS-set --mouse-x/-y
   *             custom props (works as plain hover w/o JS).
   *
   * Pure-CSS by default; magnetic variant gracefully degrades if
   * no JS is provided to set the mouse-position custom props.
   */
  [data-loom-rich-focus] {
    transition:
      transform var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease),
      box-shadow var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease),
      filter var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease);
  }
  [data-loom-rich-focus="lift"]:hover,
  [data-loom-rich-focus="lift"]:focus-visible {
    transform: translateY(-2px);
    box-shadow:
      0 8px 24px color-mix(in oklab, var(--loom-color-ink) 10%, transparent),
      0 2px 6px color-mix(in oklab, var(--loom-color-ink) 6%, transparent);
  }
  [data-loom-rich-focus="glow"]:focus-visible {
    box-shadow:
      0 0 0 4px color-mix(in oklab, var(--loom-color-primary) 18%, transparent),
      0 0 0 1px var(--loom-color-primary);
    outline: none; /* the glow replaces the ring */
  }
  [data-loom-rich-focus="glow"]:hover {
    filter: brightness(1.05);
  }
  [data-loom-rich-focus="magnetic"] {
    /* JS sets --mouse-x / --mouse-y in 0..1 range from element-
     * relative pointer position. Without JS the var(...) falls
     * back to 0 (no offset). */
    transform: translate(
      calc((var(--mouse-x, 0.5) - 0.5) * 4px),
      calc((var(--mouse-y, 0.5) - 0.5) * 4px)
    );
  }
  [data-loom-rich-focus="magnetic"]:hover {
    filter: brightness(1.04);
  }
  /* Selected/active marker — for tab triggers, picker chips,
   * active nav. Distinct from :focus so it can co-exist with
   * the keyboard ring. */
  [data-loom-selected="true"] {
    background: color-mix(in oklab, var(--loom-color-primary) 12%, var(--loom-color-surface));
    color: var(--loom-color-primary);
    box-shadow: inset 0 -2px 0 var(--loom-color-primary);
  }
  /* Cursor-tracking spotlight (opt-in via data-loom-spotlight).
   * JS sets --mouse-x / --mouse-y in CSS pixels; no-JS falls back
   * to no spotlight. */
  [data-loom-spotlight] {
    position: relative;
    isolation: isolate;
  }
  [data-loom-spotlight]::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(
      280px circle at var(--mouse-x, -1000px) var(--mouse-y, -1000px),
      color-mix(in oklab, var(--loom-color-primary) 16%, transparent) 0%,
      transparent 70%
    );
    opacity: 0;
    transition: opacity var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease);
    z-index: -1;
  }
  [data-loom-spotlight]:hover::before { opacity: 1; }
  @media (prefers-reduced-motion: reduce) {
    [data-loom-rich-focus]:hover,
    [data-loom-rich-focus]:focus-visible,
    [data-loom-rich-focus="magnetic"] {
      transform: none;
      transition: none;
    }
    [data-loom-spotlight]::before { transition: none; }
  }

  /* Hide content from sighted users without removing from a11y tree. */
  [hidden] { display: none !important; }

  /* T70b (closes #658): scroll-snap carousels.
   * Composable, no JS required (touch + trackpad scroll natively).
   * Opt-in via .loom-carousel wrapper + .loom-carousel__track
   * inner scroller + .loom-carousel__slide children.
   *
   * Direction:
   *   default       horizontal
   *   data-axis="y" vertical (rare but works for sidebar mosaics)
   *
   * Snap behavior:
   *   default          snap to start of each slide (mandatory)
   *   data-snap="proximity"  loose snap (most-discoverable, doesn't
   *                          fight power-users scrolling fast)
   *
   * Slide sizing:
   *   default                 slide width = 90% of container
   *   data-size="card"        320px slides (compact)
   *   data-size="hero"        100% slides (one-at-a-time)
   *   data-size="auto"        intrinsic content width
   *
   * Pagination dots: separate component .loom-carousel__dots
   * with <button> per slide; CSS only highlights based on
   * aria-current. JS hook is optional (sets aria-current on
   * the focused/visible slide).
   */
  .loom-carousel {
    position: relative;
    isolation: isolate;
  }
  .loom-carousel__track {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-color: var(--loom-color-primary) transparent;
    scrollbar-width: thin;
    gap: var(--loom-space-3);
    padding: var(--loom-space-2);
    /* Edge fade — visual cue that there's more content offscreen. */
    mask-image: linear-gradient(
      90deg,
      transparent 0,
      black var(--loom-space-3),
      black calc(100% - var(--loom-space-3)),
      transparent 100%
    );
  }
  .loom-carousel[data-axis="y"] .loom-carousel__track {
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    scroll-snap-type: y mandatory;
    mask-image: linear-gradient(
      0deg,
      transparent 0,
      black var(--loom-space-3),
      black calc(100% - var(--loom-space-3)),
      transparent 100%
    );
  }
  .loom-carousel[data-snap="proximity"] .loom-carousel__track {
    scroll-snap-type: x proximity;
  }
  .loom-carousel[data-snap="proximity"][data-axis="y"] .loom-carousel__track {
    scroll-snap-type: y proximity;
  }
  .loom-carousel__slide {
    flex: 0 0 90%;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  .loom-carousel[data-size="card"] .loom-carousel__slide { flex-basis: 320px; }
  .loom-carousel[data-size="hero"] .loom-carousel__slide { flex-basis: 100%; }
  .loom-carousel[data-size="auto"] .loom-carousel__slide { flex-basis: auto; }
  /* Pagination dots — one button per slide. JS sets aria-current
   * on the visible slide's button; CSS-only state still works
   * for keyboard nav via :focus-visible. */
  .loom-carousel__dots {
    display: flex;
    justify-content: center;
    gap: var(--loom-space-2);
    padding: var(--loom-space-3);
    list-style: none;
    margin: 0;
  }
  .loom-carousel__dot {
    width: 8px;
    height: 8px;
    border-radius: var(--loom-radius-pill);
    background: color-mix(in oklab, var(--loom-color-ink) 20%, transparent);
    cursor: pointer;
    transition: background var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease),
      transform var(--loom-transition-fast, 120ms) var(--loom-ease-out, ease);
  }
  .loom-carousel__dot:hover {
    background: color-mix(in oklab, var(--loom-color-primary) 40%, var(--loom-color-ink));
  }
  .loom-carousel__dot[aria-current="true"] {
    background: var(--loom-color-primary);
    transform: scale(1.4);
  }
  .loom-carousel__dot:focus-visible {
    outline: 2px solid var(--loom-color-primary);
    outline-offset: 3px;
  }
  /* Prev/next buttons — optional. Position absolutely on the carousel. */
  .loom-carousel__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    border-radius: var(--loom-radius-pill);
    background: color-mix(in oklab, var(--loom-color-surface) 92%, transparent);
    border: 1px solid var(--loom-color-border);
    color: var(--loom-color-ink);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 12px color-mix(in oklab, var(--loom-color-ink) 12%, transparent);
    z-index: 2;
    backdrop-filter: blur(8px);
  }
  .loom-carousel__nav[data-dir="prev"] { left: 8px; }
  .loom-carousel__nav[data-dir="next"] { right: 8px; }
  .loom-carousel__nav:hover {
    background: var(--loom-color-surface);
    border-color: var(--loom-color-primary);
  }
  .loom-carousel__nav:focus-visible {
    outline: 2px solid var(--loom-color-primary);
    outline-offset: 3px;
  }
  @media (prefers-reduced-motion: reduce) {
    .loom-carousel__track {
      scroll-behavior: auto;
    }
    .loom-carousel__dot {
      transition: none;
    }
  }

  /* T65 (closes #648): animation primitives.
   * Opt-in via data-loom-anim="<name>". Every animation collapses
   * to instant under prefers-reduced-motion (WCAG 2.3.3).
   *
   * Available animations:
   *   fade-in, fade-up, fade-down, fade-left, fade-right
   *   scale-in, scale-out
   *   slide-up, slide-down, slide-left, slide-right
   *   pulse, shake, spin
   *
   * Speed:
   *   data-loom-anim-duration="fast|base|slow|slower"
   *   default: base (var(--loom-motion-base, 220ms))
   *
   * Delay:
   *   data-loom-anim-delay="N" (N in ms)
   *
   * Easing:
   *   uses --loom-ease-out by default; data-loom-anim-easing="spring"
   *   switches to var(--loom-ease-spring).
   */
  @keyframes loom-anim-fade-in { from { opacity: 0; } to { opacity: 1; } }
  @keyframes loom-anim-fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
  @keyframes loom-anim-fade-down { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: translateY(0); } }
  @keyframes loom-anim-fade-left { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: translateX(0); } }
  @keyframes loom-anim-fade-right { from { opacity: 0; transform: translateX(-12px); } to { opacity: 1; transform: translateX(0); } }
  @keyframes loom-anim-scale-in { from { opacity: 0; transform: scale(0.92); } to { opacity: 1; transform: scale(1); } }
  @keyframes loom-anim-scale-out { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.92); } }
  @keyframes loom-anim-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
  @keyframes loom-anim-slide-down { from { transform: translateY(-100%); } to { transform: translateY(0); } }
  @keyframes loom-anim-slide-left { from { transform: translateX(100%); } to { transform: translateX(0); } }
  @keyframes loom-anim-slide-right { from { transform: translateX(-100%); } to { transform: translateX(0); } }
  @keyframes loom-anim-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.04); } }
  @keyframes loom-anim-shake { 10%, 90% { transform: translateX(-2px); } 20%, 80% { transform: translateX(4px); } 30%, 50%, 70% { transform: translateX(-6px); } 40%, 60% { transform: translateX(6px); } }
  @keyframes loom-anim-spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }

  [data-loom-anim] {
    --comp-anim-duration: var(--loom-motion-base, 220ms);
    --comp-anim-easing: var(--loom-ease-out, ease);
    --comp-anim-delay: 0ms;
    animation-duration: var(--comp-anim-duration);
    animation-timing-function: var(--comp-anim-easing);
    animation-delay: var(--comp-anim-delay);
    animation-fill-mode: both;
  }
  [data-loom-anim-duration="fast"] { --comp-anim-duration: var(--loom-motion-fast, 120ms); }
  [data-loom-anim-duration="slow"] { --comp-anim-duration: var(--loom-motion-slow, 420ms); }
  [data-loom-anim-duration="slower"] { --comp-anim-duration: 800ms; }
  [data-loom-anim-easing="spring"] { --comp-anim-easing: var(--loom-ease-spring, ease-out); }
  /* Per-name binding. Plain CSS-only — no JS triggers, runs on
   * page load for each element with the attribute. */
  [data-loom-anim="fade-in"]      { animation-name: loom-anim-fade-in; }
  [data-loom-anim="fade-up"]      { animation-name: loom-anim-fade-up; }
  [data-loom-anim="fade-down"]    { animation-name: loom-anim-fade-down; }
  [data-loom-anim="fade-left"]    { animation-name: loom-anim-fade-left; }
  [data-loom-anim="fade-right"]   { animation-name: loom-anim-fade-right; }
  [data-loom-anim="scale-in"]     { animation-name: loom-anim-scale-in; }
  [data-loom-anim="scale-out"]    { animation-name: loom-anim-scale-out; }
  [data-loom-anim="slide-up"]     { animation-name: loom-anim-slide-up; }
  [data-loom-anim="slide-down"]   { animation-name: loom-anim-slide-down; }
  [data-loom-anim="slide-left"]   { animation-name: loom-anim-slide-left; }
  [data-loom-anim="slide-right"]  { animation-name: loom-anim-slide-right; }
  [data-loom-anim="pulse"]        { animation-name: loom-anim-pulse; animation-iteration-count: infinite; animation-duration: 2s; }
  [data-loom-anim="shake"]        { animation-name: loom-anim-shake; animation-duration: 600ms; }
  [data-loom-anim="spin"]         { animation-name: loom-anim-spin; animation-iteration-count: infinite; animation-timing-function: linear; animation-duration: 1.4s; }
  /* WCAG 2.3.3: reduced-motion clamps every animation to no-op. */
  @media (prefers-reduced-motion: reduce) {
    [data-loom-anim] {
      animation: none !important;
    }
  }

  /* T71 (closes #654): typography rhythm.
   * Establishes a vertical rhythm + leading scale so every page
   * has consistent breathing room without per-element tuning.
   *
   * Modular scale: Perfect Fourth (1.333) for headings, base-1
   * for body. Line-heights tight on display (1.1-1.2) and loose
   * on body (1.55-1.7) per WCAG 1.4.12 + readable-typography
   * doctrine.
   *
   * Heading top-margin pattern: large top-margin (de-cramps
   * content above), zero bottom-margin (paragraph spacing
   * handled by p { margin-top }). Per CSS-Wizardry's classic
   * "trimming the top" rhythm pattern.
   *
   * Cascade-scoped to main#content so site chrome (header /
   * nav / footer) keeps its own rhythm.
   */
  main#content {
    line-height: 1.55;
  }
  /* Scope is wrapped in :where() so these prose defaults carry only
   * element-level specificity (0,0,1). They style author-written prose
   * headings but never out-specify component heading rules (e.g.
   * .loom-feature-spotlight__title) that live inside main#content — the
   * #content id would otherwise win at (1,0,2) and clobber component
   * font/size/margin. */
  :where(main#content) h1, :where(main#content) h2, :where(main#content) h3,
  :where(main#content) h4, :where(main#content) h5, :where(main#content) h6 {
    font-family: var(--loom-font-display, inherit);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -.012em;
    text-wrap: balance;
    margin: var(--loom-space-8, 2rem) 0 0 0;
  }
  :where(main#content) h1 { font-size: var(--loom-font-4xl, 2.25rem); }
  :where(main#content) h2 { font-size: var(--loom-font-3xl, 1.875rem); }
  :where(main#content) h3 { font-size: var(--loom-font-2xl, 1.5rem); }
  :where(main#content) h4 { font-size: var(--loom-font-xl, 1.25rem); }
  :where(main#content) h5 { font-size: var(--loom-font-lg, 1.125rem); }
  :where(main#content) h6 {
    font-size: var(--loom-font-base, 1rem);
    text-transform: uppercase;
    letter-spacing: .04em;
  }
  /* First heading in a flow has no top-margin (the section's
   * pad already provides breathing room). */
  main#content > h1:first-child,
  main#content > h2:first-child,
  main#content > h3:first-child,
  main#content > *:first-child > h1:first-child,
  main#content > *:first-child > h2:first-child,
  main#content > *:first-child > h3:first-child {
    margin-top: 0;
  }
  main#content p {
    margin: var(--loom-space-4, 1rem) 0 0 0;
    line-height: 1.65;
    text-wrap: pretty;
    max-width: 70ch;
  }
  main#content p:first-child { margin-top: 0; }
  /* Lists — generous leading + nested indent. */
  main#content ul, main#content ol {
    margin: var(--loom-space-4, 1rem) 0 0 0;
    padding-left: var(--loom-space-6, 1.5rem);
    line-height: 1.65;
  }
  main#content li + li { margin-top: var(--loom-space-2, 0.5rem); }
  main#content li > ul, main#content li > ol { margin-top: var(--loom-space-2, 0.5rem); }
  /* Blockquote — pull-quote treatment with accent border. */
  main#content blockquote {
    margin: var(--loom-space-6, 1.5rem) 0 0 0;
    padding: var(--loom-space-3, 0.75rem) var(--loom-space-5, 1.25rem);
    border-left: 3px solid var(--loom-color-primary, currentColor);
    background: color-mix(in oklab, var(--loom-color-primary, currentColor) 5%, transparent);
    color: var(--loom-color-ink-muted, inherit);
    font-style: italic;
  }
  main#content blockquote p { max-width: none; }
  /* Inline code — distinct mono font + soft bg. */
  main#content code {
    font-family: var(--loom-font-mono, ui-monospace, monospace);
    font-size: .92em;
    padding: 1px 6px;
    border-radius: var(--loom-radius-sm, 6px);
    background: color-mix(in oklab, var(--loom-color-ink, currentColor) 6%, transparent);
  }
  main#content pre {
    margin: var(--loom-space-5, 1.25rem) 0 0 0;
    padding: var(--loom-space-4, 1rem);
    border-radius: var(--loom-radius-md, 10px);
    background: var(--loom-color-surface-muted, var(--loom-bg, #f4f4f5));
    overflow-x: auto;
    font-family: var(--loom-font-mono, ui-monospace, monospace);
    font-size: .92em;
    line-height: 1.55;
  }
  main#content pre code {
    background: none;
    padding: 0;
    font-size: 1em;
  }
  /* HR — subtle divider on accent. */
  main#content hr {
    margin: var(--loom-space-8, 2rem) 0;
    border: 0;
    height: 1px;
    background: linear-gradient(
      90deg,
      transparent 0%,
      var(--loom-color-border, currentColor) 50%,
      transparent 100%
    );
  }


/* ============================================================ *
 * @layer tokens                                                 *
 * ------------------------------------------------------------ *
 * loom-tokens.css already declared every base token under :root.
 * This layer adds COMPONENT-SCOPED tokens — "trait" defaults that
 * components compose. Children that want to retune a component
 * override these on their own scope without changing the global
 * design token table.                                            *
 * ============================================================ */

/* unwrapped @layer */

  :root {
    /* Component-trait tokens. Downstream overrides land here. */
    --loom-radius-component:    var(--loom-radius-lg);
    --loom-border-component:    1px solid var(--loom-color-border);
    --loom-border-strong:       1px solid var(--loom-color-border-strong);

    /* T660 P-pill (added 2026-05-17): canonical pill-shape radius
     * for nav-link backgrounds, avatar wrappers, and any tag /
     * chip / round-button. Was referenced as --loom-radius-full at
     * 7 sites in this file but never DEFINED — browsers were
     * silently falling back to the `initial` value (0) on those
     * declarations. Pinning the token at 9999px (the de-facto pill
     * idiom — any value > the element's half-side rounds to a pill)
     * fixes those + gives downstream sites a stable token to
     * reference instead of hardcoding 999px or 9999px (which the
     * forge tokens-phase warns on). */
    --loom-radius-full:         9999px;
    --loom-radius-pill:         9999px;

    /* Layered shadow stack — premium depth.
     * Three rays at different distances + a soft ambient =
     * convincing volume without looking heavy. */
    --loom-shadow-flat:
      0 1px 0 hsl(220 33% 0% / 0.06);
    --loom-shadow-soft:
      0 1px 2px hsl(220 33% 0% / 0.10),
      0 4px 12px hsl(220 33% 0% / 0.10);
    --loom-shadow-elevated:
      0 1px 2px hsl(220 33% 0% / 0.18),
      0 8px 20px hsl(220 33% 0% / 0.30),
      0 24px 48px hsl(220 33% 0% / 0.20);
    --loom-shadow-cta:
      0 4px 16px hsl(220 90% 28% / 0.35),
      0 1px 2px hsl(220 33% 0% / 0.40);
    --loom-shadow-glow-accent:
      0 0 0 1px hsl(218 83% 65% / 0.35),
      0 8px 32px hsl(218 83% 65% / 0.20);
    /* Tailwind shadow-2xl in the layered-shadow idiom: one deep, soft
       drop — the premium "lifted hero image / floating card" elevation.
       Opt-in only; no primitive applies it by default. hsl 0% lightness
       is black, so this equals rgb(0 0 0 / 0.25) at the Tailwind offset. */
    --loom-shadow-2xl:
      0 25px 50px -12px hsl(220 33% 0% / 0.25);

    --loom-pad-card:            var(--loom-space-4);
    --loom-pad-panel:           var(--loom-space-5);
    --loom-pad-band:            var(--loom-space-6);
    --loom-gap-stack:           var(--loom-space-4);
    --loom-gap-row:             var(--loom-space-3);
    --loom-gap-grid:            var(--loom-space-4);
    --loom-blur-glass:          16px;
    --loom-saturate-glass:      160%;

    /* T38 (2026-05-14): typed size scale for fixed-dimension UI
     * primitives. These were raw pixel literals before; tokenising
     * them lets a single edit re-skin every icon / avatar / dot
     * across the system. The scale is 4-multiple-aligned so it
     * composes cleanly with --loom-space-* (which is also
     * 4-multiple). */
    --loom-size-dot:            6px;
    --loom-size-icon-sm:        20px;
    --loom-size-icon-md:        24px;
    --loom-size-avatar-sm:      40px;
    --loom-size-avatar-md:      48px;
    /* Font micro-sizes for caption / overline text that fall
     * below the body font-scale floor. */
    --loom-text-micro:          10px;
    /* Skeleton placeholder dimensions (em-relative so they scale
     * with the surrounding text). */
    --loom-skeleton-h-text:     1em;
    --loom-skeleton-h-title:    1.4em;
    /* Per-component minimum heights to keep cards visually
     * substantial across content density. */
    --loom-min-h-card:          120px;

    /* Motion — premium curves. The "out" curve is what ships
     * everywhere by default; the "spring" curve is for delight
     * moments (button-press release, reveal). */
    --loom-ease-out:            cubic-bezier(.16, 1, .3, 1);
    --loom-ease-spring:         cubic-bezier(.34, 1.56, .64, 1);
    --loom-transition-fast:     180ms var(--loom-ease-out);
    --loom-transition-base:     280ms var(--loom-ease-out);
    --loom-transition-slow:     520ms var(--loom-ease-out);
    /* T35: shared loop duration for pulse / shimmer / etc. Single
     * source of truth — change here, every loop animation tracks
     * the new tempo without per-rule edits. */
    --loom-animation-loop:      1.6s;
    /* T35: stagger step for [data-stagger="N"] cascade. Each tier
     * is calc(var(--loom-stagger-step) * N) for animation-delay.
     * Tweaking pace = edit one token. */
    --loom-stagger-step:        60ms;
    --loom-stroke-thin:         1px;
    --loom-stroke-strong:       2px;

    /* Tap-target / sizing tokens. WCAG 2.5.5 + iOS HIG floor for
     * any interactive element on coarse pointers. The default
     * ships at 44 — increase per surface, never decrease. */
    --loom-tap-min:             44px;
    /* Right-rail width on desktop layouts. Single source of truth
     * for the column shape so .loom-page-rail and any future
     * fixed-width columns track together. */
    --loom-rail-width:          320px;
    /* Animation displacement distances — used by entrance keyframes
     * (slide-in-right, fade-in-up) so they share a vocabulary. */
    --loom-motion-rise:         12px;
    --loom-motion-shift:        16px;
    /* Z-index scale — semantic stacking, not raw integers. Each
     * step leaves room (10s) for one-off page-local overrides
     * while keeping global precedence reviewable. */
    --loom-z-base:              0;
    --loom-z-raised:            1;
    --loom-z-overlay:           10;
    --loom-z-sticky:            50;
    --loom-z-tooltip:           55;
    --loom-z-toast:             60;
    --loom-z-modal:             100;

    /* Typography premium tier — display weights for hero copy. */
    --loom-font-display: ui-rounded, "SF Pro Display",
                         "Segoe UI Variable Display", "Inter Display",
                         "Inter", system-ui, -apple-system, sans-serif;
    --loom-font-body:    -apple-system, BlinkMacSystemFont,
                         "Inter", "Segoe UI", Roboto,
                         "Helvetica Neue", system-ui, sans-serif;
    --loom-font-mono:    ui-monospace, "JetBrains Mono", "Fira Code",
                         "SF Mono", Menlo, Consolas, monospace;
    --loom-track-tight:  -0.025em;
    --loom-track-snug:   -0.015em;
    --loom-track-loose:  0.06em;
    --loom-leading-tight: 1.1;
    --loom-leading-snug:  1.3;
    --loom-leading-base:  1.5;

    /* ── Atomic-primitive cascade tokens (#336) ──
     * Every var() consumed by skin.css below needs a base
     * declaration here so theme_consistency's "consumed but
     * undefined" gate stays green. Values mirror the
     * fallback strings each cascade rule carries inline. */
    --loom-color-bg: #ffffff;
    --loom-color-text: hsl(222 47% 11%);
    --loom-color-muted: hsl(215 16% 47%);
    --loom-color-accent-2: hsl(239 84% 67%);
    --loom-color-focus: hsl(217 91% 60%);
    --loom-color-on-primary: #ffffff;
    --loom-color-on-dark: #ffffff;
    --loom-color-primary-hover: hsl(220 90% 22%);

    /* Hero / Quote / Code / Table / Avatar / Kbd surfaces */
    --loom-color-hero-bg: transparent;
    --loom-color-quote-bg: transparent;
    --loom-color-quote-text: hsl(215 25% 27%);
    --loom-color-code-bg: hsl(214 32% 95%);
    --loom-color-code-text: hsl(222 47% 11%);
    --loom-color-table-stripe: hsl(210 40% 98%);
    --loom-color-progress-track: hsl(214 32% 91%);
    --loom-color-avatar-bg: hsl(214 32% 91%);
    --loom-color-avatar-text: hsl(215 25% 27%);
    --loom-color-kbd-bg: hsl(214 32% 95%);
    --loom-color-kbd-text: hsl(215 25% 27%);
    --loom-color-kbd-border: hsl(213 27% 84%);

    /* Stat trend + Stepper state */
    --loom-color-trend-up: hsl(158 64% 39%);
    --loom-color-trend-down: hsl(347 77% 49%);
    --loom-color-stepper-done: hsl(158 64% 39%);
    --loom-color-stepper-done-bg: hsl(152 81% 96%);
    --loom-color-stepper-current: hsl(221 83% 53%);
    --loom-color-stepper-current-bg: hsl(214 100% 97%);

    /* Badge tones — each carries {bg, text, border} */
    --loom-color-badge-neutral-bg: hsl(214 32% 95%);
    --loom-color-badge-neutral-text: hsl(215 25% 27%);
    --loom-color-badge-neutral-border: hsl(213 27% 84%);
    --loom-color-badge-info-bg: hsl(204 94% 94%);
    --loom-color-badge-info-text: hsl(201 90% 35%);
    --loom-color-badge-info-border: hsl(199 95% 74%);
    --loom-color-badge-success-bg: hsl(149 80% 90%);
    --loom-color-badge-success-text: hsl(158 64% 30%);
    --loom-color-badge-success-border: hsl(156 72% 67%);
    --loom-color-badge-warning-bg: hsl(48 96% 89%);
    --loom-color-badge-warning-text: hsl(23 83% 31%);
    --loom-color-badge-warning-border: hsl(46 97% 65%);
    --loom-color-badge-danger-bg: hsl(356 100% 94%);
    --loom-color-badge-danger-text: hsl(345 83% 41%);
    --loom-color-badge-danger-border: hsl(352 96% 79%);
    --loom-color-badge-accent-bg: hsl(226 100% 94%);
    --loom-color-badge-accent-text: hsl(229 76% 40%);
    --loom-color-badge-accent-border: hsl(230 94% 77%);

    /* Legacy-namespace bridge. ~478 places in skin.css consume the
       pre-#336 var names (--loom-accent / --loom-fg / --loom-bg /
       --loom-border / --loom-muted / --loom-primary) directly. The
       modern tenant_style emits the --loom-color-* namespace only.
       Without this bridge, tenants who set their palette via
       forge.toml [style.palette] see partial colorization: rules
       using new vars pick up the override, rules using legacy vars
       fall through to hardcoded fallbacks. Substrate-general
       (works for any tenant, not tied to any specific palette). */
    --loom-accent:   var(--loom-color-accent, hsl(239 84% 67%));
    --loom-accent-2: var(--loom-color-accent-2, hsl(239 84% 67%));
    --loom-fg:       var(--loom-color-ink, hsl(222 47% 11%));
    --loom-bg:       var(--loom-color-bg, #ffffff);
    --loom-border:   var(--loom-color-border, hsl(214 32% 91%));
    --loom-muted:    var(--loom-color-muted, hsl(215 16% 47%));
    --loom-primary:  var(--loom-color-primary, hsl(217 91% 60%));
    --loom-secondary:var(--loom-color-secondary, hsl(143 64% 32%));
    --loom-link:     var(--loom-color-link, var(--loom-color-primary, hsl(217 91% 60%)));
    --loom-text:     var(--loom-color-text, var(--loom-color-ink, hsl(222 47% 11%)));
    --loom-surface:  var(--loom-color-surface, var(--loom-color-bg, #ffffff));
  }
  @media (min-width: 768px) {
    :root {
      --loom-pad-card:  var(--loom-space-5);
      --loom-pad-panel: var(--loom-space-6);
      --loom-pad-band:  var(--loom-space-8);
    }
  }


/* ============================================================ *
 * @layer primitives                                             *
 * ============================================================ */

/* unwrapped @layer */

  .loom-page {
    width: 100%;
    /* Body content column tracks the same content-width knob the
     * page-header / utility-strip insets use (--loom-page-content-max,
     * set per data-content-width on <body>) via the SAME centering
     * model: a full-width element whose content is inset to a centered
     * column by symmetric inline padding, floored at 1.25rem on small
     * screens. Using padding-inline rather than max-width + margin:auto
     * avoids the double-count where the centring gutter AND a uniform
     * pad stacked, leaving the body column narrower than the header
     * above it. Block padding keeps the band's breathing room below the
     * header and above the footer. Falls back to the xl breakpoint when
     * content-max is unset. */
    padding-block: var(--loom-pad-band);
    padding-inline: max(1.25rem, calc((100% - var(--loom-page-content-max, var(--loom-break-xl))) / 2));
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-5);
  }
  /* T76 cycle 95b: 2-column layout when a sidebar exists.
   * Mobile-first stacking is the default; widens to feed | sidebar
   * at 960px+. Banner/hero/composer always span both columns.
   * `:has()` is supported in every modern browser since 2023; falls
   * back gracefully (single-column stack) elsewhere. */
  @media (min-width: 960px) {
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 320px;
      gap: var(--loom-space-6);
      align-items: start;
    }
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) > .loom-banner,
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) > .loom-section-hero,
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) > .loom-composer {
      grid-column: 1 / -1;
    }
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) > .loom-card-feed {
      grid-column: 1;
    }
    .loom-page:has(> .loom-sidebar):has(> .loom-card-feed) > .loom-sidebar {
      grid-column: 2;
      position: sticky;
      top: var(--loom-space-5);
    }
  }

  .loom-stack {
    display: flex;
    flex-direction: column;
    gap: var(--loom-gap-stack);
  }
  .loom-stack[data-gap="sm"] { gap: var(--loom-space-2); }
  .loom-stack[data-gap="md"] { gap: var(--loom-space-4); }
  .loom-stack[data-gap="lg"] { gap: var(--loom-space-8); }

  .loom-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--loom-gap-row);
    align-items: center;
  }
  .loom-row[data-justify="between"] { justify-content: space-between; }
  .loom-row[data-justify="end"]     { justify-content: flex-end; }
  .loom-row[data-justify="center"]  { justify-content: center; }

  .loom-grid-app {
    display: grid;
    gap: var(--loom-space-6);
    grid-template-columns: 1fr;
  }
  @media (min-width: 1024px) {
    .loom-grid-app {
      grid-template-columns: minmax(0, 1fr) var(--loom-rail-width);
      gap: var(--loom-space-8);
    }
  }

  /* Skip-link target — accessibility primitive.
   * 44×44 minimum hit-box per WCAG 2.5.5 + iOS HIG. */
  .loom-skip {
    position: absolute;
    inset-inline-start: var(--loom-space-2);
    inset-block-start: var(--loom-space-2);
    padding: var(--loom-space-2) var(--loom-space-3);
    background: var(--loom-color-surface);
    color: var(--loom-color-ink);
    border-radius: var(--loom-radius-md);
    transform: translateY(-150%);
    transition: transform var(--loom-transition-fast);
    z-index: var(--loom-z-modal);
    display: inline-flex;
    align-items: center;
    min-height: var(--loom-tap-min);
    min-width: var(--loom-tap-min);
  }
  .loom-skip:focus {
    transform: translateY(0);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary);
    outline-offset: var(--loom-stroke-thin);
  }

  /* Page-shell chrome (brand + nav-links).
   *
   * BASE: every chrome anchor enforces the WCAG 2.5.5 / iOS HIG
   * 44×44 tap-target floor REGARDLESS of pointer type. The
   * generic reset rule guards this only under
   * `@media (pointer: coarse), (max-width: 1024px)` — top-of-page
   * navigation runs on every device, every viewport, and every
   * pointer type, so it ALWAYS needs the floor.
   *
   * REGRESSION-GUARD: the crawler ui-overflow detector flagged 5
   * elements per page at 19px height (text-baseline). The reset
   * rule's media-query gating made the 44px rule invisible on
   * desktop. The fix is targeted to chrome elements (brand + nav)
   * rather than dropping the gating entirely — body content can
   * still trust the gating on desktop without dropping density. */
  /* REGRESSION-GUARD cycle 58: rendered HTML uses BEM class
   * `.loom-page-nav__link` (loom-cms-render lib.rs:2671).
   * Pre-cycle-58 the rule selectors said `.loom-page-nav-link`
   * (kebab-case) which silently never matched — every
   * SkillShots header nav link rendered at 24×24 px instead of
   * the 44×44 floor. The crawler --no-baseline run surfaced 7
   * frozen tap-target warns. Both selectors are kept now so
   * the rule applies to either future class form. */
  .loom-page-brand,
  .loom-page-nav__link,
  .loom-page-nav-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--loom-tap-min);
    min-width: var(--loom-tap-min);
    padding: var(--loom-space-2) var(--loom-space-3);
  }

  /* Chrome focus ring — explicit outline on :focus so the
   * runtime-focus detector sees a change even under programmatic
   * .focus(). The :focus:not(:focus-visible) global suppresses
   * the default outline for mouse focus, but these chrome
   * elements need to remain detectable under any focus-trigger
   * path. */
  .loom-page-brand:focus,
  .loom-page-nav__link:focus,
  .loom-page-nav-link:focus {
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary);
    outline-offset: var(--loom-stroke-thin);
    border-radius: var(--loom-radius-sm);
  }


/* ============================================================ *
 * @layer components                                             *
 * ============================================================ */

/* unwrapped @layer */


  /* ---------- Brand wordmark ---------- */
  .loom-brand {
    --comp-brand-size: var(--loom-font-lg);
    font-size: var(--comp-brand-size);
    font-weight: 800;
    letter-spacing: -0.025em;
    background: linear-gradient(
      110deg,
      var(--loom-color-gradient-a),
      var(--loom-color-gradient-b)
    );
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  @media (min-width: 768px) {
    .loom-brand { --comp-brand-size: var(--loom-font-xl); }
  }
  .loom-brand[data-size="lg"]  { --comp-brand-size: var(--loom-font-2xl); }
  .loom-brand[data-size="xl"]  { --comp-brand-size: var(--loom-font-3xl); }

  /* ---------- Top nav ---------- */
  .loom-nav {
    /* Themed surface — auto-adapts to light + dark via tokens. */
    --comp-nav-bg: color-mix(in oklab,
                             var(--loom-color-bg-canvas) 70%,
                             transparent);
    position: sticky;
    top: 0;
    z-index: var(--loom-z-sticky);
    background: var(--comp-nav-bg);
    backdrop-filter: blur(var(--loom-blur-glass)) saturate(var(--loom-saturate-glass));
    -webkit-backdrop-filter: blur(var(--loom-blur-glass)) saturate(var(--loom-saturate-glass));
    border-bottom: var(--loom-border-component);
  }
  .loom-nav-inner {
    max-width: var(--loom-break-xl);
    margin: 0 auto;
    padding: var(--loom-space-3) var(--loom-space-4);
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: var(--loom-space-4);
  }
  /* The brand wordmark is usually wrapped in an <a> for the home
   * link; ensure that anchor honors the 44px hit-box even when the
   * inner glyph is short. */
  .loom-nav-inner > a {
    display: inline-flex;
    align-items: center;
    min-height: var(--loom-tap-min);
    min-width: var(--loom-tap-min);
  }
  @media (min-width: 768px) {
    .loom-nav-inner {
      grid-template-columns: auto 1fr auto;
      padding: var(--loom-space-3) var(--loom-space-6);
      gap: var(--loom-space-8);
    }
  }

  .loom-navlinks {
    display: flex;
    gap: 2px;
    justify-self: end;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    max-width: 100%;
  }
  .loom-navlinks::-webkit-scrollbar { display: none; }
  @media (min-width: 768px) {
    .loom-navlinks { justify-self: center; overflow: visible; scroll-snap-type: none; }
  }
  .loom-navlinks a {
    padding: var(--loom-space-2) var(--loom-space-3);
    border-radius: var(--loom-radius-md);
    color: var(--loom-color-ink-muted);
    font-size: var(--loom-font-sm);
    font-weight: 600;
    white-space: nowrap;
    scroll-snap-align: start;
    flex-shrink: 0;
    transition: color var(--loom-transition-fast),
                background var(--loom-transition-fast);
    /* WCAG 2.5.5 + iOS HIG: 44px minimum hit-box. */
    display: inline-flex;
    align-items: center;
    min-height: var(--loom-tap-min);
  }
  .loom-navlinks a:hover {
    color: var(--loom-color-ink);
    background: var(--loom-color-bg-overlay);
  }
  .loom-navlinks a[aria-current="page"] {
    color: var(--loom-color-ink);
    background: var(--loom-color-bg-overlay);
  }

  .loom-nav-actions {
    display: none;
    gap: var(--loom-space-2);
    align-items: center;
  }
  @media (min-width: 768px) { .loom-nav-actions { display: flex; } }

  /* ---------- Buttons ---------- */
  .loom-btn {
    --comp-btn-pad-x:  var(--loom-space-4);
    --comp-btn-pad-y:  var(--loom-space-2);
    --comp-btn-size:   var(--loom-font-sm);
    --comp-btn-radius: var(--loom-radius-control, var(--loom-radius-md));
    --comp-btn-bg:     transparent;
    --comp-btn-fg:     var(--loom-color-ink);
    --comp-btn-border: 0;
    --comp-btn-shadow: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--loom-space-2);
    padding: var(--comp-btn-pad-y) var(--comp-btn-pad-x);
    border-radius: var(--comp-btn-radius);
    font-size: var(--comp-btn-size);
    font-weight: 600;
    background: var(--comp-btn-bg);
    color: var(--comp-btn-fg);
    border: var(--comp-btn-border);
    box-shadow: var(--comp-btn-shadow);
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: filter var(--loom-transition-fast),
                transform var(--loom-transition-fast),
                background var(--loom-transition-fast);
    user-select: none;
  }
  .loom-btn:hover  { filter: brightness(1.08); }
  .loom-btn:active { transform: translateY(1px); }
  .loom-btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

  .loom-btn[data-variant="primary"] {
    --comp-btn-bg: linear-gradient(110deg,
      var(--loom-color-gradient-a),
      var(--loom-color-gradient-b));
    --comp-btn-fg: var(--loom-color-primary-fg);
    --comp-btn-shadow: var(--loom-shadow-cta);
    position: relative;
    overflow: hidden;
  }
  /* Sheen sweep on hover — premium interaction polish. */
  .loom-btn[data-variant="primary"]::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(110deg,
      transparent 30%,
      hsl(0 0% 100% / 0.20) 50%,
      transparent 70%);
    transform: translateX(-100%);
    transition: transform var(--loom-transition-slow);
    pointer-events: none;
  }
  .loom-btn[data-variant="primary"]:hover::before { transform: translateX(100%); }
  .loom-btn[data-variant="ghost"]   {
    --comp-btn-fg: var(--loom-color-ink-muted);
  }
  .loom-btn[data-variant="ghost"]:hover {
    --comp-btn-bg: var(--loom-color-bg-overlay);
    --comp-btn-fg: var(--loom-color-ink);
  }
  .loom-btn[data-variant="outline"] {
    --comp-btn-border: var(--loom-stroke-thin) solid var(--loom-color-border-strong);
    --comp-btn-fg: var(--loom-color-ink);
  }
  .loom-btn[data-variant="outline"]:hover {
    --comp-btn-bg: var(--loom-color-bg-overlay);
  }

  .loom-btn[data-size="sm"] {
    --comp-btn-pad-x: var(--loom-space-3);
    --comp-btn-pad-y: var(--loom-space-1);
    --comp-btn-size:  var(--loom-font-xs);
  }
  .loom-btn[data-size="lg"] {
    --comp-btn-pad-x: var(--loom-space-6);
    --comp-btn-pad-y: var(--loom-space-3);
    --comp-btn-size:  var(--loom-font-base);
  }

  /* ---------- Live badge ---------- */
  .loom-live-badge {
    --comp-badge-bg:     hsl(218 83% 65% / 0.15);
    --comp-badge-border: hsl(218 83% 65% / 0.4);
    --comp-badge-fg:     var(--loom-color-accent-glow);
    --comp-badge-dot-bg: var(--loom-color-success);
    --comp-badge-dot-pulse-ring-color: hsl(160 84% 55% / 0.7);
    display: inline-flex;
    align-items: center;
    gap: var(--loom-space-2);
    padding: var(--loom-space-1) var(--loom-space-3);
    background: var(--comp-badge-bg);
    border: var(--loom-stroke-thin) solid var(--comp-badge-border);
    border-radius: var(--loom-radius-full);
    color: var(--comp-badge-fg);
    font-size: var(--loom-font-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .loom-live-badge[data-tone="warn"] {
    --comp-badge-bg:     hsl(33 100% 66% / 0.12);
    --comp-badge-border: hsl(33 100% 66% / 0.4);
    --comp-badge-fg:     var(--loom-color-warn);
    --comp-badge-dot-bg: var(--loom-color-warn);
    --comp-badge-dot-pulse-ring-color: hsl(33 100% 66% / 0.6);
  }
  .loom-live-badge[data-tone="danger"] {
    --comp-badge-bg:     hsl(0 72% 65% / 0.12);
    --comp-badge-border: hsl(0 72% 65% / 0.4);
    --comp-badge-fg:     var(--loom-color-danger);
    --comp-badge-dot-bg: var(--loom-color-danger);
    --comp-badge-dot-pulse-ring-color: hsl(0 72% 65% / 0.6);
  }

  .loom-pulse-dot {
    width: var(--loom-size-dot); height: var(--loom-size-dot);
    border-radius: var(--loom-radius-full);
    background: var(--comp-badge-dot-bg);
    flex-shrink: 0;
    animation: loom-pulse var(--loom-animation-loop) infinite;
  }
  @keyframes loom-pulse {
    0%   { box-shadow: 0 0 0 0   var(--comp-badge-dot-pulse-ring-color); }
    70%  { box-shadow: 0 0 0 8px transparent; }
    100% { box-shadow: 0 0 0 0   transparent; }
  }
  @media (prefers-reduced-motion: reduce) {
    .loom-pulse-dot { animation: none; }
  }

  /* ---------- Hero band ---------- */
  .loom-hero {
    --comp-hero-pad: var(--loom-pad-band);
    position: relative;
    padding: var(--comp-hero-pad);
    border-radius: var(--loom-radius-xl);
    background:
      /* Top-right glow */
      radial-gradient(900px 320px at 100% -10%,
        hsl(218 83% 65% / 0.30),
        transparent 60%),
      /* Bottom-left bloom */
      radial-gradient(700px 320px at -5% 110%,
        hsl(272 84% 65% / 0.18),
        transparent 60%),
      /* Underlying surface tint */
      linear-gradient(135deg,
        hsl(218 83% 65% / 0.10),
        hsl(272 84% 65% / 0.06) 60%,
        transparent),
      var(--loom-color-surface-muted);
    border: var(--loom-border-strong);
    overflow: hidden;
    margin-bottom: var(--loom-pad-band);
    box-shadow: var(--loom-shadow-elevated);
  }
  .loom-hero::before {
    /* Subtle grid pattern overlay — design-system signature. */
    content: "";
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(to right, hsl(0 0% 100% / 0.025) 1px, transparent 1px),
      linear-gradient(to bottom, hsl(0 0% 100% / 0.025) 1px, transparent 1px);
    background-size: 32px 32px;
    pointer-events: none;
    mask-image: linear-gradient(to bottom, black, transparent 70%);
    -webkit-mask-image: linear-gradient(to bottom, black, transparent 70%);
  }
  .loom-hero-title {
    margin-top: var(--loom-space-3);
    font-family: var(--loom-font-display, var(--loom-font-body));
    font-size: var(--loom-font-2xl);
    font-weight: 800;
    letter-spacing: var(--loom-track-tight, -0.025em);
    line-height: var(--loom-leading-tight, 1.1);
    /* Subtle gradient on the title itself for richness. */
    background: linear-gradient(180deg,
      var(--loom-color-ink) 0%,
      color-mix(in oklab, var(--loom-color-ink) 75%, var(--loom-color-accent-glow)) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  @media (min-width: 768px)  { .loom-hero-title { font-size: var(--loom-font-3xl); } }
  @media (min-width: 1024px) { .loom-hero-title { font-size: var(--loom-font-4xl); } }
  .loom-hero-meta {
    margin-top: var(--loom-space-2);
    color: var(--loom-color-ink-muted);
    font-size: var(--loom-font-sm);
  }
  .loom-hero-actions {
    margin-top: var(--loom-space-6);
    display: flex;
    flex-wrap: wrap;
    gap: var(--loom-space-3);
  }

  /* ---------- Stat bar ---------- */
  .loom-stat-bar {
    margin-top: var(--loom-space-6);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: var(--loom-space-4);
  }
  .loom-stat { min-width: 0; }
  .loom-stat-label {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  .loom-stat-value {
    font-size: var(--loom-font-xl);
    font-weight: 800;
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  @media (min-width: 768px) { .loom-stat-value { font-size: var(--loom-font-2xl); } }
  .loom-stat-value[data-tone="success"] { color: var(--loom-color-success); }
  .loom-stat-value[data-tone="warn"]    { color: var(--loom-color-warn); }
  .loom-stat-value[data-tone="danger"]  { color: var(--loom-color-danger); }

  /* ---------- Section head + pill tabs ---------- */
  .loom-section-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: var(--loom-space-3);
    margin-bottom: var(--loom-space-4);
  }
  .loom-section-head h2 {
    font-size: var(--loom-font-lg);
    font-weight: 700;
    letter-spacing: -0.01em;
  }
  @media (min-width: 768px) { .loom-section-head h2 { font-size: var(--loom-font-xl); } }

  .loom-pill-tabs {
    display: flex;
    gap: 2px;
    background: var(--loom-color-surface-muted);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-full);
    padding: 3px;
    overflow-x: auto;
    scrollbar-width: none;
    max-width: 100%;
  }
  .loom-pill-tabs::-webkit-scrollbar { display: none; }
  .loom-pill-tabs button {
    padding: 6px var(--loom-space-3);
    border-radius: var(--loom-radius-full);
    color: var(--loom-color-ink-muted);
    font-size: var(--loom-font-xs);
    font-weight: 600;
    white-space: nowrap;
    flex-shrink: 0;
    transition: color var(--loom-transition-fast),
                background var(--loom-transition-fast);
  }
  .loom-pill-tabs button:hover { color: var(--loom-color-ink); }
  .loom-pill-tabs button[aria-pressed="true"] {
    background: var(--loom-color-bg-overlay);
    color: var(--loom-color-ink);
  }

  /* ---------- Feed grid + battle card ---------- */
  .loom-feed-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--loom-gap-grid);
  }
  @media (min-width: 480px) {
    .loom-feed-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (min-width: 768px) {
    .loom-feed-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }

  /* ============================================================ *
   * Prose paragraph + standalone Heading                          *
   * ----------                                                    *
   * The two bridge primitives that survived without their own     *
   * skin rules through 13 ticks of regen-tide work — fixed by    *
   * `loom audit-bridge` (T13) drift detection.                   *
   * ============================================================ */
  .loom-prose {
    font-size: var(--loom-font-base);
    color: var(--loom-color-ink);
    line-height: 1.6;
    margin: 0 0 var(--loom-space-3) 0;
    max-width: 70ch;
  }
  .loom-prose:last-child { margin-bottom: 0; }

  .loom-heading {
    font-family: var(--loom-font-display);
    font-weight: 600;
    color: var(--loom-color-ink);
    line-height: 1.3;
    letter-spacing: var(--loom-track-tight);
    margin: var(--loom-space-4) 0 var(--loom-space-2) 0;
  }
  .loom-heading[data-loom-level="2"] { font-size: var(--loom-font-2xl, 1.5rem); }
  .loom-heading[data-loom-level="3"] { font-size: var(--loom-font-xl, 1.25rem); }
  .loom-heading[data-loom-level="4"] { font-size: var(--loom-font-lg, 1.125rem); }
  .loom-heading:first-child { margin-top: 0; }

  /* ============================================================ *
   * Banner                                                        *
   * ----------                                                    *
   * Top-of-page persistent notice. Tone-tinted background via     *
   * data-tone (info / warn / success / danger). Optional close    *
   * button with data-loom-banner-dismiss for client JS to wire.   *
   * ============================================================ */
  .loom-banner {
    display: flex;
    align-items: flex-start;
    gap: var(--loom-space-3);
    padding: var(--loom-space-3) var(--loom-space-4);
    border-radius: var(--loom-radius-component);
    border: var(--loom-border-component);
    background: var(--loom-color-surface-muted);
    color: var(--loom-color-ink);
  }
  .loom-banner[data-tone="info"] {
    background: color-mix(in oklab, var(--loom-color-primary) 12%, var(--loom-color-surface));
    border-color: color-mix(in oklab, var(--loom-color-primary) 30%, transparent);
  }
  .loom-banner[data-tone="warn"] {
    background: color-mix(in oklab, var(--loom-color-warn) 18%, var(--loom-color-surface));
    border-color: color-mix(in oklab, var(--loom-color-warn) 40%, transparent);
  }
  .loom-banner[data-tone="success"] {
    background: color-mix(in oklab, var(--loom-color-success) 18%, var(--loom-color-surface));
    border-color: color-mix(in oklab, var(--loom-color-success) 40%, transparent);
  }
  .loom-banner[data-tone="danger"] {
    background: color-mix(in oklab, var(--loom-color-danger) 18%, var(--loom-color-surface));
    border-color: color-mix(in oklab, var(--loom-color-danger) 40%, transparent);
  }
  .loom-banner__text {
    flex: 1 1 auto;
    margin: 0;
    font-size: var(--loom-font-sm, 0.875rem);
    line-height: 1.5;
  }
  .loom-banner__dismiss {
    flex: 0 0 auto;
    background: none;
    border: none;
    color: inherit;
    font-size: var(--loom-font-lg, 1.25rem);
    line-height: 1;
    cursor: pointer;
    min-width: var(--loom-tap-min);
    min-height: var(--loom-tap-min);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--loom-radius-sm);
  }
  .loom-banner__dismiss:hover,
  .loom-banner__dismiss:focus-visible {
    background: color-mix(in oklab, var(--loom-color-ink) 10%, transparent);
  }
  .loom-banner__dismiss:focus-visible {
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary);
    outline-offset: var(--loom-stroke-thin);
  }

  /* ============================================================ *
   * Form (multi-step)                                             *
   * ----------                                                    *
   * Wrapper section + step indicator + per-step fieldsets +       *
   * submit row. Used for post-skill upload flow. Loom-namespaced. *
   * ============================================================ */
  .loom-form-section {
    background: var(--loom-color-surface);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-component);
    padding: var(--loom-space-5);
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-4);
  }
  .loom-form-section__legend {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-xl, 1.25rem);
    font-weight: 600;
    color: var(--loom-color-ink);
    line-height: 1.2;
    margin: 0;
  }
  .loom-form-section__steps {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--loom-space-3);
  }
  .loom-form-section__step {
    display: flex;
    align-items: center;
    gap: var(--loom-space-2);
    padding: var(--loom-space-2) var(--loom-space-3);
    border-radius: var(--loom-radius-pill);
    background: var(--loom-color-surface-muted);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    font-size: var(--loom-font-sm, 0.875rem);
  }
  .loom-form-section__step[data-state="current"] {
    background: color-mix(in oklab, var(--loom-color-primary, currentColor) 12%, transparent);
    color: var(--loom-color-ink);
    font-weight: 600;
  }
  .loom-form-section__step[data-state="done"] {
    background: color-mix(in oklab, var(--loom-color-success, currentColor) 12%, transparent);
  }
  .loom-form-section__step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--loom-size-icon-md);
    height: var(--loom-size-icon-md);
    border-radius: 50%;
    background: var(--loom-color-surface);
    color: var(--loom-color-ink);
    font-weight: 600;
    font-size: var(--loom-font-sm, 0.875rem);
  }
  .loom-form {
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-4);
  }
  .loom-form[data-invalid="true"] {
    outline: var(--loom-stroke-strong) dashed var(--loom-color-warning, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-form__step {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-3);
  }
  .loom-form__step-legend {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-base);
    font-weight: 600;
    color: var(--loom-color-ink);
    padding: 0 0 var(--loom-space-2) 0;
  }
  .loom-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-1);
  }
  .loom-form-field__label {
    font-size: var(--loom-font-sm, 0.875rem);
    color: var(--loom-color-ink);
    font-weight: 500;
  }
  /* T76 (Crawler dogfood 2026-05-14): visible required-field
     marker. Closes form.required-no-indicator at the source —
     every Forge-generated form picks it up automatically.
     aria-hidden="true" on the span keeps screen readers from
     double-announcing ("required" + "asterisk"); the `required`
     attribute on the input handles AT announcement. */
  .loom-form-field__required {
    color: var(--loom-color-danger, currentColor);
    margin-inline-start: var(--loom-space-1);
    font-weight: 600;
  }
  .loom-form-field__input,
  .loom-form-field__textarea,
  .loom-form-field__select {
    background: var(--loom-color-surface);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-sm);
    padding: var(--loom-space-2) var(--loom-space-3);
    color: var(--loom-color-ink);
    font: inherit;
    min-height: var(--loom-tap-min);
    transition: border-color var(--loom-transition-fast);
  }
  .loom-form-field__textarea {
    min-height: calc(var(--loom-tap-min) * 2);
    resize: vertical;
  }
  .loom-form-field__input:focus-visible,
  .loom-form-field__textarea:focus-visible,
  .loom-form-field__select:focus-visible {
    border-color: var(--loom-color-primary, currentColor);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-form-field__input[readonly] {
    background: var(--loom-color-surface-muted);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
  }
  .loom-form-field__hint {
    font-size: var(--loom-font-sm, 0.875rem);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    line-height: 1.4;
    margin: 0;
  }
  .loom-form__submit-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--loom-space-2);
    padding-top: var(--loom-space-3);
    border-top: var(--loom-border-component);
  }
  .loom-form__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--loom-space-2) var(--loom-space-4);
    border-radius: var(--loom-radius-sm);
    background: var(--loom-color-surface-muted);
    color: var(--loom-color-ink);
    font-weight: 600;
    border: none;
    cursor: pointer;
    min-height: var(--loom-tap-min);
    transition: filter var(--loom-transition-fast);
  }
  .loom-form__btn[data-variant="primary"] {
    background: var(--loom-color-primary, var(--loom-color-ink));
    color: var(--loom-color-primary-fg, var(--loom-color-surface));
  }
  .loom-form__btn:hover {
    filter: brightness(1.05);
  }
  .loom-form__btn:focus-visible {
    filter: brightness(1.05);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }

  /* ============================================================ *
   * Sidebar + Panel                                               *
   * ----------                                                    *
   * Right-rail aside containing a stack of typed panels.          *
   * Panel body shapes: List ({label, value, href?}) or Text       *
   * (paragraphs).                                                 *
   * ============================================================ */
  .loom-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-4);
  }
  .loom-panel {
    --comp-panel-accent: var(--loom-color-primary);
    background: var(--loom-color-surface);
    border: 1px solid var(--loom-color-border);
    border-radius: var(--loom-radius-component);
    padding: var(--loom-space-4) var(--loom-space-5);
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-3);
    box-shadow:
      0 1px 2px color-mix(in oklab, var(--loom-color-ink) 5%, transparent),
      0 1px 3px color-mix(in oklab, var(--loom-color-ink) 4%, transparent);
    position: relative;
    overflow: hidden;
  }
  .loom-panel::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: linear-gradient(180deg,
      var(--comp-panel-accent) 0%,
      color-mix(in oklab, var(--comp-panel-accent) 40%, transparent) 100%);
  }
  .loom-panel__title {
    padding-left: var(--loom-space-1);
  }
  /* Per-panel accent rotation — sites can override via inline
   * style="--comp-panel-accent: …" or via data-tone="X". */
  .loom-sidebar > .loom-panel:nth-of-type(1) { --comp-panel-accent: hsl(244 65% 50%); }
  .loom-sidebar > .loom-panel:nth-of-type(2) { --comp-panel-accent: hsl(15 75% 55%); }
  .loom-sidebar > .loom-panel:nth-of-type(3) { --comp-panel-accent: hsl(155 65% 40%); }
  .loom-sidebar > .loom-panel:nth-of-type(4) { --comp-panel-accent: hsl(280 70% 55%); }
  .loom-sidebar > .loom-panel:nth-of-type(5) { --comp-panel-accent: hsl(38 90% 45%); }
  .loom-panel[data-tone="violet"] { --comp-panel-accent: hsl(280 75% 50%); }
  .loom-panel[data-tone="indigo"] { --comp-panel-accent: hsl(244 65% 50%); }
  .loom-panel[data-tone="ocean"]  { --comp-panel-accent: hsl(210 80% 45%); }
  .loom-panel[data-tone="forest"] { --comp-panel-accent: hsl(155 65% 40%); }
  .loom-panel[data-tone="amber"]  { --comp-panel-accent: hsl(38 90% 45%); }
  .loom-panel[data-tone="rose"]   { --comp-panel-accent: hsl(340 80% 50%); }
  .loom-panel__title {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-base);
    font-weight: 600;
    color: var(--loom-color-ink);
    line-height: 1.2;
    margin: 0;
  }
  .loom-panel__list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-2);
  }
  .loom-panel__list-item {
    display: flex;
    align-items: center;
  }
  .loom-panel__list-link {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--loom-space-3);
    padding: var(--loom-space-2);
    border-radius: var(--loom-radius-sm);
    color: var(--loom-color-ink);
    text-decoration: none;
    /* T76 (Crawler dogfood 2026-05-14): aside-panel links were
       invisible-until-hover — same color and weight as surrounding
       text. Bumping to 600 (semibold) gives sighted users a clear
       visible affordance even at rest. The Crawler's linkUnderline
       detector now also passes under the stricter no-chrome-exception
       interpretation (≥200-unit weight contrast vs parent's 400). */
    font-weight: 600;
    min-height: var(--loom-tap-min);
  }
  .loom-panel__list-link:hover {
    background: var(--loom-color-surface-muted);
  }
  .loom-panel__list-link:focus-visible {
    background: var(--loom-color-surface-muted);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-panel__list-link[data-invalid="true"] {
    outline: var(--loom-stroke-strong) dashed var(--loom-color-warning, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-panel__list-label {
    flex: 1 1 auto;
    color: var(--loom-color-ink);
    font-size: var(--loom-font-sm, 0.875rem);
    line-height: 1.4;
  }
  .loom-panel__list-value {
    flex: 0 0 auto;
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    font-size: var(--loom-font-sm, 0.875rem);
    font-weight: 600;
    line-height: 1.2;
  }
  .loom-panel__body {
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-2);
  }
  .loom-panel__paragraph {
    font-size: var(--loom-font-sm, 0.875rem);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    line-height: 1.5;
    margin: 0;
  }

  /* ============================================================ *
   * CardFeed                                                      *
   * ----------                                                    *
   * Vertical list of loom-card-feed-item articles. Each card is   *
   * a real anchor wrapping avatar + body (title, host, stats,     *
   * tag). Used for battle feeds, vote queues, leaderboard rows.   *
   * ============================================================ */
  .loom-card-feed {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--loom-space-4);
  }
  /* T70d cycle 96 iter 6: auto-fit card grid. Each card gets at
   * least 18rem of width OR a full column if not enough room.
   * Featured first card spans the full row regardless. Sites
   * that want a vertical list set data-layout="list" on
   * .loom-card-feed. REGRESSION-GUARD: prior fixed 2-col grid
   * broke at 200% zoom (root font-size = 32px), squeezing card
   * bodies to clientWidth=0. auto-fit + min(100%, 18rem) means
   * the grid degrades gracefully under zoom + narrow viewports. */
  .loom-card-feed:not([data-layout="list"]) {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  }
  .loom-card-feed:not([data-layout="list"]):not([data-featured="off"]) > .loom-card-feed-item:first-of-type {
    grid-column: 1 / -1;
  }
  .loom-card-feed__heading {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-xl, 1.25rem);
    font-weight: 600;
    color: var(--loom-color-ink);
    line-height: 1.2;
    margin: 0 0 var(--loom-space-2) 0;
  }
  .loom-card-feed-item {
    background: var(--loom-color-surface);
    border: 1px solid color-mix(in oklab, var(--loom-color-border) 60%, transparent);
    border-radius: var(--loom-radius-component);
    /* Layered depth — three shadow stacks for real elevation. */
    box-shadow:
      0 1px 2px color-mix(in oklab, var(--loom-color-ink) 5%, transparent),
      0 4px 12px color-mix(in oklab, var(--loom-color-ink) 6%, transparent),
      0 16px 32px color-mix(in oklab, var(--loom-color-ink) 4%, transparent);
    overflow: hidden;
    transition: transform var(--loom-transition-fast),
                border-color var(--loom-transition-fast),
                box-shadow var(--loom-transition-fast);
    position: relative;
  }
  /* Featured first card — stronger visual hierarchy. Operators
   * who want EVERY card uniform set data-featured="off" on the
   * .loom-card-feed wrapper to disable. */
  .loom-card-feed:not([data-featured="off"]) > .loom-card-feed-item:first-of-type {
    background:
      radial-gradient(600px 200px at 100% 0%,
        color-mix(in oklab, var(--loom-color-gradient-b) 14%, transparent) 0%,
        transparent 60%),
      var(--loom-color-surface);
    border-color: color-mix(in oklab, var(--loom-color-primary) 35%, var(--loom-color-border));
    box-shadow:
      0 18px 40px color-mix(in oklab, var(--loom-color-primary) 18%, transparent),
      0 6px 12px color-mix(in oklab, var(--loom-color-ink) 8%, transparent);
  }
  .loom-card-feed:not([data-featured="off"]) > .loom-card-feed-item:first-of-type::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--loom-grad-hero, linear-gradient(90deg, var(--loom-color-primary), var(--loom-color-gradient-b)));
    z-index: 1;
  }
  .loom-card-feed:not([data-featured="off"]) > .loom-card-feed-item:first-of-type .loom-card-feed-item__title {
    font-size: var(--loom-font-2xl, 1.5rem);
  }
  .loom-card-feed:not([data-featured="off"]) > .loom-card-feed-item:first-of-type .loom-card-feed-item__link {
    padding: var(--loom-space-6);
  }
  /* T70a: optional 16:9 media slot above the card body. Activates
   * when the CMS markup includes <div class="loom-card-feed-item__media">
   * with an <img>/<video>/<picture>. Lazy-load via loading="lazy".
   * Falls back to a soft gradient placeholder when empty so we
   * never ship a blank media slot. */
  .loom-card-feed-item__media {
    position: relative;
    aspect-ratio: 16 / 9;
    width: 100%;
    overflow: hidden;
    background: linear-gradient(135deg,
      color-mix(in oklab, var(--loom-color-primary) 18%, var(--loom-color-surface-muted)) 0%,
      color-mix(in oklab, var(--loom-color-gradient-b) 18%, var(--loom-color-surface-muted)) 100%);
  }
  .loom-card-feed-item__media img,
  .loom-card-feed-item__media video,
  .loom-card-feed-item__media picture > img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .loom-card-feed-item__media[data-empty="true"]::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
      radial-gradient(60% 60% at 30% 30%,
        color-mix(in oklab, var(--loom-color-primary) 10%, transparent),
        transparent 70%);
  }
  .loom-card-feed-item:hover,
  .loom-card-feed-item:focus-within {
    transform: translateY(-2px);
    border-color: color-mix(in oklab, var(--loom-color-primary) 60%, var(--loom-color-border));
    box-shadow:
      0 14px 28px color-mix(in oklab, var(--loom-color-ink) 12%, transparent),
      0 4px 8px color-mix(in oklab, var(--loom-color-ink) 8%, transparent);
  }
  @media (prefers-reduced-motion: reduce) {
    .loom-card-feed-item { transition: none; }
    .loom-card-feed-item:hover,
    .loom-card-feed-item:focus-within { transform: none; }
  }
  .loom-card-feed-item__link {
    display: flex;
    align-items: flex-start;
    gap: var(--loom-space-4);
    padding: var(--loom-space-5);
    color: var(--loom-color-ink);
    text-decoration: none;
    min-height: var(--loom-tap-min);
  }
  .loom-card-feed-item__link:focus-visible {
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: calc(var(--loom-stroke-thin) * -1);
    border-radius: var(--loom-radius-component);
  }
  .loom-card-feed-item__link[data-invalid="true"] {
    outline: var(--loom-stroke-strong) dashed var(--loom-color-warning, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-card-feed-item__avatar {
    flex: 0 0 auto;
    width: var(--loom-size-avatar-md);
    height: var(--loom-size-avatar-md);
    border-radius: 50%;
    background: linear-gradient(135deg,
      color-mix(in oklab, var(--loom-color-primary) 65%, var(--loom-color-gradient-b) 35%) 0%,
      color-mix(in oklab, var(--loom-color-gradient-b) 70%, var(--loom-color-primary) 30%) 100%);
    color: var(--loom-color-primary-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--loom-font-display);
    font-weight: 700;
    font-size: var(--loom-font-base);
    letter-spacing: -.02em;
    overflow: hidden;
    box-shadow:
      inset 0 1px 0 color-mix(in oklab, white 30%, transparent),
      0 2px 6px color-mix(in oklab, var(--loom-color-primary) 30%, transparent);
  }
  .loom-card-feed-item__body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-2);
    min-width: 0;
  }
  .loom-card-feed-item__tag {
    /* General-purpose tag chip. Any site sets either a curated
     * tone via data-tone="X" OR a custom color via inline
     * style="--tag-color: <hsl|hex|hsl()>". Falls back to the
     * site's primary brand color when nothing is set. SkillShots
     * categorisation lives in the CMS data, NOT in this CSS. */
    --tag-color: var(--loom-color-primary);
    align-self: flex-start;
    padding: var(--loom-space-1) var(--loom-space-3);
    border-radius: var(--loom-radius-pill);
    background: color-mix(in oklab, var(--tag-color) 14%, var(--loom-color-surface));
    color: color-mix(in oklab, var(--tag-color) 80%, var(--loom-color-ink));
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-sm, 0.875rem);
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
    line-height: 1.4;
    border: 1px solid color-mix(in oklab, var(--tag-color) 25%, transparent);
  }
  /* Curated tone palette — any site can opt in via data-tone="X".
   * Custom colours via inline style="--tag-color: …" still work. */
  .loom-card-feed-item__tag[data-tone="violet"] { --tag-color: hsl(280 75% 50%); }
  .loom-card-feed-item__tag[data-tone="indigo"] { --tag-color: hsl(244 65% 50%); }
  .loom-card-feed-item__tag[data-tone="ocean"]  { --tag-color: hsl(210 80% 45%); }
  .loom-card-feed-item__tag[data-tone="forest"] { --tag-color: hsl(155 65% 40%); }
  .loom-card-feed-item__tag[data-tone="amber"]  { --tag-color: hsl(38 90% 45%); }
  .loom-card-feed-item__tag[data-tone="rose"]   { --tag-color: hsl(340 80% 50%); }
  .loom-card-feed-item__tag[data-tone="ruby"]   { --tag-color: hsl(0 75% 48%); }
  .loom-card-feed-item__tag[data-tone="walnut"] { --tag-color: hsl(28 60% 35%); }
  .loom-card-feed-item__tag[data-tone="slate"]  { --tag-color: hsl(225 15% 38%); }
  .loom-card-feed-item__tag[data-tone="teal"]   { --tag-color: hsl(180 70% 35%); }
  .loom-card-feed-item__title {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-xl, 1.25rem);
    font-weight: 700;
    color: var(--loom-color-ink);
    line-height: 1.25;
    letter-spacing: -.012em;
    margin: 0;
    text-wrap: pretty;
  }
  .loom-card-feed-item__host {
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    font-size: var(--loom-font-sm, 0.875rem);
    line-height: 1.45;
    margin: 0;
  }
  .loom-card-feed-item__stats {
    list-style: none;
    padding: var(--loom-space-3) 0 0 0;
    margin: var(--loom-space-2) 0 0 0;
    border-top: 1px solid color-mix(in oklab, var(--loom-color-border) 80%, transparent);
    display: flex;
    gap: var(--loom-space-6);
    flex-wrap: wrap;
  }
  .loom-card-feed-item__stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .loom-card-feed-item__stat-label {
    font-size: var(--loom-font-xs, 0.75rem);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 600;
  }
  .loom-card-feed-item__stat-value {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-2xl, 1.5rem);
    font-weight: 800;
    color: var(--loom-color-ink);
    line-height: 1;
    letter-spacing: -.018em;
    font-variant-numeric: tabular-nums;
  }
  /* Highlight the primary stat (votes %, pot $) — first stat
   * gets the primary color treatment for visual anchor. */
  .loom-card-feed-item__stat:first-child .loom-card-feed-item__stat-value {
    color: var(--loom-color-primary);
  }

  /* ============================================================ *
   * Section: Hero                                                 *
   * ----------                                                    *
   * Top-of-page band: optional eyebrow pill, h2 title, optional   *
   * lede paragraph, optional primary CTA button. Loom-namespaced  *
   * (no Tailwind), composes from existing tokens. Used by         *
   * cms-render's CmsSection::Hero.                                *
   * ============================================================ */
  .loom-section-hero {
    padding: clamp(var(--loom-space-8), 6vw, var(--loom-space-16)) clamp(var(--loom-space-6), 5vw, var(--loom-space-10));
    border-radius: var(--loom-radius-lg, 24px);
    background:
      radial-gradient(900px 500px at 85% -20%,
        color-mix(in oklab, var(--loom-color-gradient-b) 38%, transparent) 0%,
        transparent 55%),
      radial-gradient(700px 400px at -10% 110%,
        color-mix(in oklab, var(--loom-color-gradient-a) 30%, transparent) 0%,
        transparent 55%),
      linear-gradient(135deg,
        color-mix(in oklab, var(--loom-color-gradient-a) 14%, var(--loom-color-surface)) 0%,
        var(--loom-color-surface) 50%,
        color-mix(in oklab, var(--loom-color-gradient-b) 12%, var(--loom-color-surface)) 100%);
    border: 1px solid color-mix(in oklab, var(--loom-color-primary) 20%, var(--loom-color-border));
    box-shadow:
      0 2px 4px color-mix(in oklab, var(--loom-color-ink) 5%, transparent),
      0 24px 48px color-mix(in oklab, var(--loom-color-primary) 18%, transparent),
      0 48px 96px color-mix(in oklab, var(--loom-color-ink) 10%, transparent);
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-5);
    position: relative;
    overflow: hidden;
    isolation: isolate;
  }
  /* Decorative blob behind the hero — adds depth/dimension.
   * REGRESSION-GUARD (cycle 96 iter 2): tablet uiOverflow +7
   * was caused by the previous transform:translate(15%,25%)
   * pushing the ::after box past the hero edge. Even with
   * overflow:hidden, scrollWidth includes transformed children.
   * Fix: stay strictly inside the box (no transform), AND add
   * contain:paint as belt-and-braces so any future ::after
   * won't accidentally bleed out. */
  .loom-section-hero::after {
    content: "";
    position: absolute;
    /* Strictly inside the hero box. -2% bleed in iter 2 still
     * triggered uiOverflow on themed-page baselines. */
    inset: 30% 0 0 auto;
    width: 38%;
    height: 100%;
    background: radial-gradient(closest-side,
      color-mix(in oklab, var(--loom-color-gradient-b) 36%, transparent) 0%,
      transparent 70%);
    pointer-events: none;
    z-index: -1;
    filter: blur(28px);
  }
  .loom-section-hero { contain: paint; }
  .loom-section-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
      radial-gradient(1px 1px at 20% 30%, color-mix(in oklab, var(--loom-color-primary) 40%, transparent), transparent 50%),
      radial-gradient(1px 1px at 70% 60%, color-mix(in oklab, var(--loom-color-gradient-b) 40%, transparent), transparent 50%);
    background-size: 60px 60px, 80px 80px;
    opacity: .35;
    mask-image: linear-gradient(180deg, transparent, black 30%, black 70%, transparent);
  }
  .loom-section-hero > * {
    position: relative;
    z-index: 1;
  }
  .loom-section-hero__eyebrow {
    display: inline-block;
    align-self: flex-start;
    padding: var(--loom-space-1) var(--loom-space-3);
    border-radius: var(--loom-radius-pill);
    background: color-mix(in oklab, var(--loom-color-primary, currentColor) 12%, transparent);
    color: var(--loom-color-primary, currentColor);
    font-size: var(--loom-font-sm, 0.875rem);
    font-weight: 600;
    line-height: 1.2;
  }
  .loom-section-hero__title {
    font-family: var(--loom-font-display);
    /* T70 cycle 96: clamp floor reduced from 1.5rem to 1.25rem.
     * REGRESSION-GUARD: prior floor caused .loom-section-hero
     * to overflow its container on 375px-viewport mobile (the
     * uiOverflow detector flagged 7 pages). Bound also adds
     * overflow-wrap:anywhere so a single long word never breaks
     * the box, regardless of viewport. */
    font-size: clamp(1.25rem, 5.5vw, var(--loom-font-5xl, 3rem));
    font-weight: 800;
    color: var(--loom-color-ink);
    line-height: 1.1;
    letter-spacing: -.022em;
    margin: 0;
    text-wrap: balance;
    overflow-wrap: anywhere;
    min-width: 0;
  }
  .loom-section-hero {
    min-width: 0;
  }
  .loom-section-hero__lede {
    font-size: var(--loom-font-lg, 1.125rem);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    line-height: 1.5;
    max-width: 60ch;
    margin: 0;
  }
  .loom-section-hero__cta {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    padding: var(--loom-space-3) var(--loom-space-6);
    border-radius: var(--loom-radius-pill);
    background: linear-gradient(135deg,
      var(--loom-color-primary) 0%,
      color-mix(in oklab, var(--loom-color-primary) 70%, var(--loom-color-gradient-b)) 100%);
    color: var(--loom-color-primary-fg, var(--loom-color-surface));
    text-decoration: none;
    font-weight: 600;
    font-size: 1.02rem;
    letter-spacing: -.005em;
    min-height: var(--loom-tap-min);
    box-shadow:
      0 6px 16px color-mix(in oklab, var(--loom-color-primary) 30%, transparent),
      0 2px 4px color-mix(in oklab, var(--loom-color-primary) 20%, transparent),
      inset 0 1px 0 color-mix(in oklab, white 30%, transparent);
    transition: transform var(--loom-transition-fast),
                box-shadow var(--loom-transition-fast),
                filter var(--loom-transition-fast);
  }
  .loom-section-hero__cta:hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow:
      0 10px 24px color-mix(in oklab, var(--loom-color-primary) 35%, transparent),
      0 3px 6px color-mix(in oklab, var(--loom-color-primary) 25%, transparent),
      inset 0 1px 0 color-mix(in oklab, white 40%, transparent);
  }
  @media (prefers-reduced-motion: reduce) {
    .loom-section-hero__cta { transition: none; }
    .loom-section-hero__cta:hover { transform: none; }
  }
  .loom-section-hero__cta:focus-visible {
    filter: brightness(1.05);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-section-hero__cta[data-invalid="true"] {
    outline: var(--loom-stroke-strong) dashed var(--loom-color-warning, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }

  /* ============================================================ *
   * Section: Group                                                *
   * ----------                                                    *
   * Heading + paragraph(s) framed as a card. Used for explainer   *
   * blocks (rules, FAQs, "how it works").                         *
   * ============================================================ */
  .loom-section-group {
    padding: var(--loom-space-5);
    border-radius: var(--loom-radius-component);
    background: var(--loom-color-surface);
    border: var(--loom-border-component);
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-3);
  }
  .loom-section-group__title {
    font-family: var(--loom-font-display);
    font-size: var(--loom-font-xl, 1.25rem);
    font-weight: 600;
    color: var(--loom-color-ink);
    line-height: 1.2;
    margin: 0;
  }
  .loom-section-group__body {
    font-size: var(--loom-font-base);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    line-height: 1.6;
    margin: 0;
  }

  /* ============================================================ *
   * Composer (feed-top FB-style)                                  *
   * ----------                                                    *
   * Tokens it consumes:                                           *
   *   --loom-color-surface           outer card bg                *
   *   --loom-color-surface-muted     prompt + avatar bg           *
   *   --loom-color-ink-muted         prompt placeholder color     *
   *   --loom-radius-component        card radius                 *
   *   --loom-space-{2,3,4,5}         density-tunable gaps + pads  *
   *   --loom-border-component        outer border                 *
   * Modifiers:                                                    *
   *   [data-size="compact"]          dense (header use)           *
   *   [data-size="comfortable"]      default feed-top size        *
   *   [data-invalid="true"]          surfaces a build-time error  *
   * ============================================================ */
  .loom-composer {
    --comp-composer-pad: var(--loom-space-5);
    --comp-composer-gap: var(--loom-space-4);
    --comp-composer-prompt-pad: var(--loom-space-3) var(--loom-space-5);
    background: var(--loom-color-surface);
    border: 1px solid var(--loom-color-border);
    border-radius: var(--loom-radius-component);
    padding: var(--comp-composer-pad);
    display: flex;
    flex-direction: column;
    gap: var(--comp-composer-gap);
    box-shadow:
      0 1px 2px color-mix(in oklab, var(--loom-color-ink) 5%, transparent),
      0 1px 3px color-mix(in oklab, var(--loom-color-ink) 4%, transparent);
  }
  .loom-composer[data-size="compact"] {
    --comp-composer-pad: var(--loom-space-2);
    --comp-composer-gap: var(--loom-space-2);
    --comp-composer-prompt-pad: var(--loom-space-2) var(--loom-space-3);
  }
  .loom-composer[data-invalid="true"] {
    /* Visible build-error state. The phantom_button forge phase
     * also flags an invalid endpoint at build time; this is a
     * fallback for cases the build missed. */
    outline: var(--loom-stroke-strong) dashed var(--loom-color-warning, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-composer__row {
    display: flex;
    align-items: center;
    gap: var(--comp-composer-gap);
  }
  .loom-composer__avatar {
    flex: 0 0 auto;
    width: calc(var(--loom-size-avatar-md, 48px));
    height: calc(var(--loom-size-avatar-md, 48px));
    border-radius: 50%;
    background: linear-gradient(135deg,
      color-mix(in oklab, var(--loom-color-primary) 65%, var(--loom-color-gradient-b) 35%) 0%,
      color-mix(in oklab, var(--loom-color-gradient-b) 70%, var(--loom-color-primary) 30%) 100%);
    color: var(--loom-color-primary-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--loom-font-display);
    font-weight: 700;
    font-size: var(--loom-font-base);
    letter-spacing: -.02em;
    overflow: hidden;
    box-shadow:
      inset 0 1px 0 color-mix(in oklab, white 30%, transparent),
      0 2px 6px color-mix(in oklab, var(--loom-color-primary) 30%, transparent);
  }
  .loom-composer__prompt {
    flex: 1 1 auto;
    display: block;
    padding: var(--loom-space-4) var(--loom-space-5);
    background: var(--loom-color-surface-muted);
    color: var(--loom-color-ink-muted, var(--loom-color-ink));
    border-radius: var(--loom-radius-pill);
    border: 1px solid var(--loom-color-border);
    text-decoration: none;
    min-height: var(--loom-tap-min);
    line-height: 1.4;
    font-size: 1.02rem;
    transition: background var(--loom-transition-fast),
                border-color var(--loom-transition-fast),
                color var(--loom-transition-fast);
  }
  .loom-composer__prompt:hover {
    background: color-mix(in oklab, var(--loom-color-surface-muted) 92%, var(--loom-color-ink) 8%);
    color: var(--loom-color-ink);
  }
  .loom-composer__prompt:focus-visible {
    background: color-mix(in oklab, var(--loom-color-surface-muted) 92%, var(--loom-color-ink) 8%);
    color: var(--loom-color-ink);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-composer__actions {
    display: flex;
    align-items: stretch;
    gap: var(--loom-space-2);
    padding-top: var(--loom-space-2);
    border-top: var(--loom-border-component);
  }
  .loom-composer__action {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--loom-space-2);
    padding: var(--loom-space-3) var(--loom-space-4);
    border-radius: var(--loom-radius-sm);
    color: var(--loom-color-ink-muted);
    text-decoration: none;
    min-height: var(--loom-tap-min);
    font-weight: 600;
    font-size: 0.96rem;
    transition: background var(--loom-transition-fast),
                color var(--loom-transition-fast);
  }
  .loom-composer__action:hover {
    background: color-mix(in oklab, var(--loom-color-primary) 8%, var(--loom-color-surface-muted));
    color: var(--loom-color-primary);
  }
  .loom-composer__action-icon svg {
    width: 1.25rem;
    height: 1.25rem;
  }
  .loom-composer__action:focus-visible {
    background: var(--loom-color-surface-muted);
    outline: var(--loom-stroke-strong) solid var(--loom-color-primary, currentColor);
    outline-offset: var(--loom-stroke-thin);
  }
  .loom-composer__action-icon {
    display: inline-flex;
    align-items: center;
    width: var(--loom-size-icon-md);
    height: var(--loom-size-icon-md);
    color: var(--loom-color-primary, currentColor);
  }
  /* REGRESSION-GUARD cycle 59: explicit color on the action
   * label. Pre-fix in dark mode the label inherited primary
   * blue (3.81:1 on #141414) — axe-static-a11y serious
   * color-contrast violation on the SkillShots feed CTA, only
   * fired under the keyboard journey (which forces dark theme
   * via prefers-color-scheme). The action link's parent rule
   * sets color:ink but a stacking inline-style override on
   * `a { color: var(--loom-link) }` in the page-shell
   * baseline CSS bleeds through. Explicit color on the label
   * span shuts the cascade. */
  .loom-composer__action-label {
    font-size: var(--loom-font-sm, 0.875rem);
    font-weight: 500;
    color: var(--loom-color-ink);
  }
  /* Compact action layout: icon-only on tight viewports. */
  @media (max-width: 540px) {
    .loom-composer__action-label { display: none; }
    .loom-composer__action { gap: 0; }
  }

  /* ============================================================ *
   * Picture                                                       *
   * ----------                                                    *
   * Responsive image base. Component renders <picture> with       *
   * AVIF / WebP / JPG sources; the <img> always carries           *
   * .loom-picture so we can normalize layout behavior here.       *
   * Modifiers: --cover (object-fit:cover), --contain.             *
   * Size of the box is determined by intrinsic width/height (set  *
   * as attributes for CLS prevention) plus any ancestor sizing.   *
   * ============================================================ */
  .loom-picture {
    /* Width/height attrs reserve aspect ratio; let the box adapt
     * to its container width and scale height proportionally. */
    max-width: 100%;
    height: auto;
    display: block;
    /* Soft asset-load shimmer so a slow JPEG is visibly loading
     * rather than appearing as a flash of empty box. Uses an
     * existing token color so it inherits the active theme. */
    background-color: var(--loom-color-surface-muted);
  }
  .loom-picture--cover    { object-fit: cover; height: 100%; width: 100%; }
  .loom-picture--contain  { object-fit: contain; height: 100%; width: 100%; }

  .loom-card-battle {
    --comp-card-radius: var(--loom-radius-component);
    --comp-card-bg:
      linear-gradient(180deg,
        color-mix(in oklab, var(--loom-color-surface-muted) 100%, white 4%) 0%,
        var(--loom-color-surface-muted) 60%);
    --comp-card-border: var(--loom-border-component);
    background: var(--comp-card-bg);
    border: var(--comp-card-border);
    border-radius: var(--comp-card-radius);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow: var(--loom-shadow-soft);
    transition: transform var(--loom-transition-base),
                border-color var(--loom-transition-base),
                box-shadow var(--loom-transition-base);
  }
  .loom-card-battle:hover {
    transform: translateY(-3px);
    border-color: var(--loom-color-border-strong);
    box-shadow: var(--loom-shadow-elevated);
  }
  .loom-card-battle .thumb {
    aspect-ratio: 9 / 16;
    background: radial-gradient(80% 60% at 50% 0%,
      hsl(220 24% 18%) 0%,
      hsl(220 33% 8%) 70%);
    position: relative;
    display: flex;
    align-items: end;
    padding: var(--loom-space-3);
  }
  .loom-card-battle .thumb::after {
    content: "▶";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: hsl(0 0% 100% / 0.85);
    font-size: var(--loom-font-3xl);
    text-shadow: 0 2px 12px hsl(0 0% 0% / 0.6);
    pointer-events: none;
  }
  /* Category-tinted thumb gradient + decorative SVG pattern. CSP-safe
   * (SVG inlined as data URL, no external assets). The SVG patterns
   * stack ABOVE the gradient at low opacity so the centered emoji
   * icon stays the focal point. Each category has a motif:
   *   basketball  basketball seams
   *   parkour     zigzag jump trail
   *   billiards   scattered balls
   *   mind        neural network
   *   skate       ramp + wheels
   *   darts       bullseye rings
   *   cafe        coffee beans
   * Adding a new category: add a data-category="X" rule with the
   * category's SVG pattern above the radial-gradient layer. Pattern
   * SHOULD use ~%23ffffff15 stroke/fill values so it sits subtly
   * over the gradient regardless of which tint is active.
   */
  .loom-card-battle .thumb[data-category="basketball"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'><circle cx='40' cy='40' r='28' fill='none' stroke='%23ffffff22' stroke-width='1.5'/><path d='M12 40 Q40 20 68 40 M12 40 Q40 60 68 40 M40 12 Q60 40 40 68 M40 12 Q20 40 40 68' stroke='%23ffffff18' stroke-width='1.2' fill='none'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(28 95% 55% / 0.4) 0%, hsl(15 85% 25% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="parkour"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M0 80 L20 30 L40 70 L60 20 L80 60 L100 25' stroke='%23ffffff20' stroke-width='2' fill='none' stroke-linejoin='round'/><circle cx='20' cy='30' r='2.5' fill='%23ffffff35'/><circle cx='60' cy='20' r='2.5' fill='%23ffffff35'/><circle cx='100' cy='25' r='2.5' fill='%23ffffff35'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(86 70% 45% / 0.4) 0%, hsl(140 70% 20% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="billiards"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='90' height='90' viewBox='0 0 90 90'><circle cx='20' cy='25' r='8' fill='%23ffd70030'/><circle cx='60' cy='30' r='8' fill='%23dc143c30'/><circle cx='35' cy='55' r='8' fill='%2300008030'/><circle cx='75' cy='65' r='8' fill='%2300800030'/><circle cx='15' cy='70' r='8' fill='%23ffffff20'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(212 60% 30% / 0.5) 0%, hsl(150 50% 20% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="mind"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><circle cx='20' cy='20' r='2.5' fill='%23ffffff40'/><circle cx='50' cy='15' r='2.5' fill='%23ffffff40'/><circle cx='80' cy='25' r='2.5' fill='%23ffffff40'/><circle cx='30' cy='50' r='2.5' fill='%23ffffff40'/><circle cx='70' cy='55' r='2.5' fill='%23ffffff40'/><circle cx='20' cy='80' r='2.5' fill='%23ffffff40'/><circle cx='55' cy='85' r='2.5' fill='%23ffffff40'/><circle cx='85' cy='75' r='2.5' fill='%23ffffff40'/><path d='M20 20 L50 15 L80 25 M50 15 L30 50 L70 55 M30 50 L20 80 L55 85 L85 75' stroke='%23ffffff15' stroke-width='1' fill='none'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(280 50% 50% / 0.4) 0%, hsl(230 50% 25% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="skate"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M0 80 Q25 30 50 80 Q75 30 100 80' stroke='%23ffffff25' stroke-width='2' fill='none'/><circle cx='25' cy='80' r='4' fill='%23ffffff20'/><circle cx='50' cy='80' r='4' fill='%23ffffff20'/><circle cx='75' cy='80' r='4' fill='%23ffffff20'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(28 80% 50% / 0.4) 0%, hsl(0 70% 25% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="darts"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'><circle cx='40' cy='40' r='30' fill='none' stroke='%23ffffff20' stroke-width='1'/><circle cx='40' cy='40' r='22' fill='none' stroke='%23ffffff25' stroke-width='1'/><circle cx='40' cy='40' r='14' fill='none' stroke='%23ffffff30' stroke-width='1'/><circle cx='40' cy='40' r='6' fill='%23dc143c40'/><circle cx='40' cy='40' r='2' fill='%23ffd70060'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(0 70% 45% / 0.4) 0%, hsl(35 70% 25% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  .loom-card-battle .thumb[data-category="cafe"] {
    background:
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='90' height='90' viewBox='0 0 90 90'><ellipse cx='25' cy='30' rx='6' ry='9' fill='%238b451325' transform='rotate(-25 25 30)'/><ellipse cx='60' cy='40' rx='6' ry='9' fill='%238b451330' transform='rotate(20 60 40)'/><ellipse cx='35' cy='65' rx='6' ry='9' fill='%238b451325' transform='rotate(-15 35 65)'/><ellipse cx='70' cy='70' rx='6' ry='9' fill='%238b451330' transform='rotate(35 70 70)'/></svg>"),
      radial-gradient(80% 60% at 50% 0%,
      hsl(35 60% 50% / 0.4) 0%, hsl(25 50% 20% / 0.95) 70%),
      hsl(220 33% 8%);
  }
  /* Centered category emoji at large display size. Replaces the
   * generic ▶ glyph from `.loom-card-battle .thumb::after` when
   * the markup includes <span class="loom-thumb-icon">EMOJI</span>. */
  .loom-thumb-icon {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--loom-font-4xl, 4rem);
    line-height: 1;
    text-shadow: 0 2px 18px hsl(0 0% 0% / 0.55);
    pointer-events: none;
    user-select: none;
    z-index: var(--loom-z-base);
  }
  .loom-thumb-badge {
    position: relative;
    z-index: var(--loom-z-raised);
    padding: 2px var(--loom-space-2);
    background: hsl(0 0% 0% / 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-radius: var(--loom-radius-sm);
    font-size: var(--loom-font-xs);
    font-weight: 700;
    color: var(--loom-color-ink);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    max-width: calc(100% - var(--loom-space-2));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-card-battle .body {
    padding: var(--loom-pad-card);
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-3);
    flex: 1;
  }
  .loom-card-battle .title {
    font-size: var(--loom-font-base);
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: -0.01em;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .loom-card-battle .host {
    display: flex;
    align-items: center;
    gap: var(--loom-space-2);
  }
  .loom-card-battle .host span {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-card-battle .stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--loom-space-3);
    padding-top: var(--loom-space-3);
    border-top: var(--loom-border-component);
    margin-top: auto;
  }
  .loom-card-battle .stat-label {
    font-size: var(--loom-text-micro);
    color: var(--loom-color-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  .loom-card-battle .stat-value {
    font-size: var(--loom-font-base);
    font-weight: 700;
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-card-battle .stat-value[data-tone="success"] { color: var(--loom-color-success); }

  /* ---------- Avatar ---------- */
  .loom-avatar {
    --comp-avatar-size: 24px;
    width: var(--comp-avatar-size);
    height: var(--comp-avatar-size);
    border-radius: var(--loom-radius-full);
    background: linear-gradient(135deg,
      var(--loom-color-gradient-a),
      var(--loom-color-gradient-b));
    flex-shrink: 0;
  }
  .loom-avatar[data-size="sm"] { --comp-avatar-size: 20px; }
  .loom-avatar[data-size="lg"] { --comp-avatar-size: 36px; }
  .loom-avatar[data-size="xl"] { --comp-avatar-size: 48px; }

  /* ---------- Sidebar panel ---------- */
  .loom-panel {
    --comp-panel-pad: var(--loom-pad-panel);
    background: var(--loom-color-surface-muted);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-component);
    padding: var(--comp-panel-pad);
    margin-bottom: var(--loom-space-4);
  }
  .loom-panel-title {
    font-size: var(--loom-font-sm);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--loom-color-ink-muted);
    font-weight: 700;
    margin-bottom: var(--loom-space-3);
  }
  .loom-panel-body {
    font-size: var(--loom-font-sm);
    color: var(--loom-color-ink-muted);
    line-height: 1.55;
  }
  .loom-panel-body p + p { margin-top: var(--loom-space-2); }

  /* ---------- Leaderboard row ---------- */
  .loom-leader {
    display: grid;
    grid-template-columns: 22px 24px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--loom-space-3);
    padding: var(--loom-space-2) 0;
    border-bottom: var(--loom-border-component);
  }
  .loom-leader:last-child { border-bottom: 0; }
  .loom-leader .rank {
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    font-size: var(--loom-font-sm);
    color: var(--loom-color-ink-muted);
  }
  .loom-leader[data-rank="1"] .rank { color: var(--loom-color-tier-medium); }
  .loom-leader .name {
    font-weight: 600;
    font-size: var(--loom-font-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-leader .earnings {
    font-weight: 700;
    font-size: var(--loom-font-sm);
    color: var(--loom-color-success);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* ---------- Vote-row ---------- */
  .loom-vote-row {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--loom-space-3);
    padding: var(--loom-space-3) 0;
    border-bottom: var(--loom-border-component);
  }
  .loom-vote-row:last-child { border-bottom: 0; }
  .loom-vote-row .body { min-width: 0; }
  .loom-vote-row .body .title {
    font-weight: 600;
    font-size: var(--loom-font-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-vote-row .body .meta {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .loom-vote-row .closes {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-warn);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* ---------- Page title (h1 at top of view) ---------- */
  .loom-page-title {
    font-size: var(--loom-font-2xl);
    font-weight: 800;
    letter-spacing: -0.02em;
  }
  @media (min-width: 768px) {
    .loom-page-title { font-size: var(--loom-font-3xl); }
  }
  .loom-page-title + .loom-page-subtitle {
    color: var(--loom-color-ink-muted);
    margin-top: var(--loom-space-1);
    font-size: var(--loom-font-base);
  }

  /* Spacing modifier for any block. data-mb / data-mt set margin
   * from the spacing token scale. Prefer a parent stack with
   * data-gap; this is for the rare "I need a margin between
   * stacked sections" case. */
  [data-mb="sm"] { margin-bottom: var(--loom-space-2); }
  [data-mb="md"] { margin-bottom: var(--loom-space-4); }
  [data-mb="lg"] { margin-bottom: var(--loom-space-6); }
  [data-mb="xl"] { margin-bottom: var(--loom-space-8); }

  /* ---------- Breadcrumb ---------- */
  .loom-breadcrumb {
    margin-bottom: var(--loom-space-4);
    font-size: var(--loom-font-sm);
    color: var(--loom-color-ink-muted);
  }
  .loom-breadcrumb a:hover { color: var(--loom-color-ink); }

  /* ---------- Profile-shaped hero header ---------- */
  .loom-profile-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--loom-space-6);
    align-items: center;
  }

  /* Avatar — extra sizes. */
  .loom-avatar[data-size="2xl"] { --comp-avatar-size: 96px; }

  /* ---------- Result rank chips (W / L / DQ) ---------- */
  .loom-leader[data-result="win"]  .rank { color: var(--loom-color-success); }
  .loom-leader[data-result="loss"] .rank { color: var(--loom-color-danger); }
  .loom-leader[data-result="dq"]   .rank { color: var(--loom-color-ink-muted); }
  .loom-leader[data-result="loss"] .earnings,
  .loom-leader[data-result="dq"]   .earnings {
    color: var(--loom-color-ink-muted);
  }

  /* ---------- Thumb badge tones (won / lost) ---------- */
  .loom-thumb-badge[data-tone="success"] {
    background: hsl(160 84% 30% / 0.7);
  }
  .loom-thumb-badge[data-tone="danger"] {
    background: hsl(0 72% 51% / 0.7);
  }

  /* ---------- Badge grid (compact tile grid) ---------- */
  .loom-badge-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: var(--loom-gap-grid);
  }
  .loom-badge-card {
    background: var(--loom-color-surface-muted);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-component);
    padding: var(--loom-space-4);
    text-align: center;
  }
  .loom-badge-card .glyph {
    font-size: var(--loom-font-3xl);
    line-height: 1;
  }
  .loom-badge-card .label {
    font-weight: 700;
    margin-top: var(--loom-space-2);
  }
  .loom-badge-card .desc {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
    margin-top: 2px;
  }

  /* ---------- Form primitives (used in post-skill etc.) ---------- */
  .loom-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--loom-space-2);
  }
  .loom-form-field label {
    font-size: var(--loom-font-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--loom-color-ink-muted);
    font-weight: 700;
  }
  .loom-form-field input,
  .loom-form-field textarea,
  .loom-form-field select {
    font: inherit;
    color: inherit;
    background: var(--loom-color-bg-canvas);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-md);
    padding: var(--loom-space-3) var(--loom-space-4);
    width: 100%;
    max-width: 100%;
    transition: border-color var(--loom-transition-fast);
  }
  .loom-form-field input:focus,
  .loom-form-field textarea:focus,
  .loom-form-field select:focus {
    outline: 0;
    border-color: var(--loom-color-primary);
  }
  .loom-form-field textarea {
    min-height: var(--loom-min-h-card);
    resize: vertical;
  }
  .loom-form-field .hint {
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
  }

  /* ---------- Step indicator (multi-step forms) ---------- */
  .loom-step-indicator {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--loom-space-2);
    margin-bottom: var(--loom-space-6);
  }
  @media (min-width: 768px) {
    .loom-step-indicator {
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    }
  }
  .loom-step-indicator .step {
    padding: var(--loom-space-3);
    background: var(--loom-color-surface-muted);
    border: var(--loom-border-component);
    border-radius: var(--loom-radius-md);
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
    display: flex;
    align-items: center;
    gap: var(--loom-space-2);
  }
  .loom-step-indicator .step[data-state="current"] {
    border-color: var(--loom-color-primary);
    color: var(--loom-color-ink);
  }
  .loom-step-indicator .step[data-state="done"] {
    border-color: var(--loom-color-success);
    color: var(--loom-color-ink-muted);
  }
  .loom-step-indicator .num {
    display: inline-flex;
    width: var(--loom-size-icon-sm);
    height: var(--loom-size-icon-sm);
    align-items: center;
    justify-content: center;
    border-radius: var(--loom-radius-full);
    background: var(--loom-color-bg-overlay);
    font-weight: 700;
    flex-shrink: 0;
  }
  .loom-step-indicator .step[data-state="current"] .num {
    background: var(--loom-color-primary);
    color: var(--loom-color-primary-fg);
  }

  /* ---------- Block button (full-width modifier) ---------- */
  .loom-btn[data-block="true"] {
    width: 100%;
    margin-top: var(--loom-space-3);
  }

  /* ---------- Inline strong (used in panel-body emphasis) ---------- */
  .loom-emph { color: var(--loom-color-ink); font-weight: 700; }
  .loom-emph[data-tone="success"] { color: var(--loom-color-success); }
  .loom-emph[data-tone="warn"]    { color: var(--loom-color-warn); }
  .loom-emph[data-tone="danger"]  { color: var(--loom-color-danger); }
  .loom-emph[data-tone="muted"]   { color: var(--loom-color-ink-muted); }

  /* ---------- KV row (label : value pair, used in stats panels) ---------- */
  .loom-kv {
    display: flex;
    justify-content: space-between;
    gap: var(--loom-space-3);
    font-size: var(--loom-font-sm);
  }
  .loom-kv .label { color: var(--loom-color-ink-muted); }
  .loom-kv .value { font-weight: 700; }

  /* ---------- Footer ---------- */
  .loom-footer {
    border-top: var(--loom-border-component);
    padding: var(--loom-space-8) var(--loom-space-4);
    color: var(--loom-color-ink-muted);
    font-size: var(--loom-font-xs);
    text-align: center;
  }
  .loom-footer .tag {
    color: var(--loom-color-ink);
    font-weight: 600;
  }

  /* ---------- PoC banner ---------- */
  .loom-poc-banner {
    background: linear-gradient(110deg,
      hsl(218 83% 65% / 0.15),
      hsl(272 84% 65% / 0.12));
    border-bottom: var(--loom-border-component);
    padding: var(--loom-space-2) var(--loom-space-4);
    text-align: center;
    font-size: var(--loom-font-xs);
    color: var(--loom-color-ink-muted);
  }
  .loom-poc-banner code {
    background: var(--loom-color-bg-overlay);
    padding: 0 6px; /* loom-allow: micro padding for inline code; sub-token by design (between space-1=4px and space-2=8px) */
    border-radius: var(--loom-radius-sm);
    color: var(--loom-color-ink);
    font-family: ui-monospace, "JetBrains Mono", Menlo, monospace;
    font-size: var(--loom-font-xs);
  }


/* ============================================================ *
 * @layer plugins                                                *
 * ------------------------------------------------------------ *
 * Sanctioned aesthetic + font variants. Each is opt-in via an
 * HTML attribute:
 *
 *   <html data-theme="dark|light|hc-dark|hc-light|sepia">
 *   <html data-font="display|serif|mono">
 *   <html data-density="comfortable|compact|spacious">
 *
 * All layered into @layer plugins so downstream apps can still
 * override on top via their own @layer plugins blocks (CSS @layer
 * permits cumulative additions — multiple stylesheets all
 * @layer plugins-name compose).
 *
 * Adding a new theme: append a `:root[data-theme="<name>"] { ... }`
 * block here, listing every base token it overrides. Adding a
 * font variant: append a `:root[data-font="<name>"] { ... }` block
 * with --loom-font-display / --loom-font-body / --loom-font-mono.
 * ============================================================ */

/* unwrapped @layer */

  /* High-contrast dark — bumps borders, text, removes
   * decorative gradients on title that lower contrast. */
  /* Default palette. Matches hc-dark — every page that ships
   * without a data-theme attribute (e.g. SSG output before
   * theme.js runs) still resolves --loom-color-* to a usable
   * value. theme.js layers light/sepia/etc. via data-theme.
   *
   * REGRESSION-GUARD: do NOT remove this :root block. The themed
   * :root[data-theme="..."] siblings shadow these per-theme;
   * stripping the base would silently invalidate every CSS
   * declaration that uses var(--loom-color-*) at first paint.
   * Confirmed via headless probe: brand/nav/skip-link focus
   * outline computed as 'none' before this block existed because
   * --loom-color-primary was undefined → outline shorthand was
   * rejected as invalid CSS. */
  :root {
    /* T76 cycle 95b: premium palette default — warm violet + coral
     * accent on a soft warm-white canvas. Light by default; the
     * `@media (prefers-color-scheme: dark)` block below auto-flips
     * to premium dark. Existing `data-theme="hc-dark"`, `hc-light`,
     * `sepia` variants are unchanged. WCAG AA contrast pinned: ink
     * #1B1F2A on bg #FBFAF7 = 16.4:1; primary #4338CA on bg = 7.6:1
     * (AAA normal-text). Validated via loom theme contrast.
     *
     * T99 (2026-05-18): added accent + on-accent + info + info-subtle
     * + success-subtle + warning-subtle + danger-subtle + surface-subtle.
     * Closes T99 — `loom theme validate` flagged these as consumed-
     * but-undeclared. Subtle backgrounds at 92-97% L; info hsl(210)
     * blue family; accent = coral (matches gradient-b hue). */
    --loom-color-bg-canvas:     hsl(40 33% 98%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(40 25% 95%);
    --loom-color-surface-subtle: hsl(40 30% 97%);
    --loom-color-bg-overlay:    hsl(225 20% 14% / 6%);
    --loom-color-ink:           hsl(225 25% 10%);
    --loom-color-ink-muted:     hsl(225 15% 32%);
    --loom-color-border:        hsl(36 16% 80%);
    --loom-color-border-strong: hsl(36 12% 60%);
    --loom-color-primary:       hsl(244 65% 38%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(15 85% 45%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(258 65% 45%);
    --loom-color-info:          hsl(210 70% 35%);
    --loom-color-info-subtle:   hsl(210 75% 95%);
    --loom-color-success:       hsl(142 75% 22%);
    --loom-color-success-subtle: hsl(142 55% 92%);
    --loom-color-warn:          hsl(28 95% 30%);
    --loom-color-warning:       hsl(28 95% 30%);
    --loom-color-warning-subtle: hsl(28 80% 92%);
    --loom-color-danger:        hsl(0 75% 35%);
    --loom-color-danger-subtle: hsl(0 70% 94%);
    --loom-color-tier-medium:   hsl(38 95% 30%);
    --loom-color-gradient-a:    hsl(244 65% 38%);
    --loom-color-gradient-b:    hsl(15 85% 45%);
  }
  /* Premium dark — owner directive (2026-05-15): default to
   * LIGHT regardless of OS pref. Auto-flip only when the page
   * explicitly opts in via data-theme="auto" (matches the
   * BASE_THEME_CSS contract in loom-cms-render). The in-page
   * theme switcher (T72) sets data-theme="light"|"dark"|"auto"
   * and persists to localStorage. */
  @media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] {
      --loom-color-bg-canvas:     hsl(232 24% 8%);
      --loom-color-surface:       hsl(232 22% 11%);
      --loom-color-surface-muted: hsl(232 20% 15%);
      --loom-color-surface-subtle: hsl(232 21% 13%);
      --loom-color-bg-overlay:    hsl(0 0% 100% / 6%);
      --loom-color-ink:           hsl(228 33% 95%);
      --loom-color-ink-muted:     hsl(225 12% 65%);
      --loom-color-border:        hsl(232 18% 22%);
      --loom-color-border-strong: hsl(232 14% 38%);
      --loom-color-primary:       hsl(238 100% 78%);
      --loom-color-primary-fg:    hsl(232 24% 8%);
      --loom-color-accent:        hsl(15 95% 70%);
      --loom-color-on-accent:     hsl(232 24% 8%);
      --loom-color-accent-glow:   hsl(258 100% 78%);
      --loom-color-info:          hsl(210 90% 70%);
      --loom-color-info-subtle:   hsl(210 50% 18%);
      --loom-color-success:       hsl(142 60% 60%);
      --loom-color-success-subtle: hsl(142 35% 18%);
      --loom-color-warn:          hsl(28 95% 65%);
      --loom-color-warning:       hsl(28 95% 65%);
      --loom-color-warning-subtle: hsl(28 50% 18%);
      --loom-color-danger:        hsl(0 80% 65%);
      --loom-color-danger-subtle: hsl(0 40% 22%);
      --loom-color-tier-medium:   hsl(45 95% 60%);
      --loom-color-gradient-a:    hsl(238 100% 78%);
      --loom-color-gradient-b:    hsl(15 95% 70%);
    }
  }
  /* Explicit data-theme="dark" — same palette as auto-dark above. */
  :root[data-theme="dark"] {
    --loom-color-bg-canvas:     hsl(232 24% 8%);
    --loom-color-surface:       hsl(232 22% 11%);
    --loom-color-surface-muted: hsl(232 20% 15%);
    --loom-color-surface-subtle: hsl(232 21% 13%);
    --loom-color-bg-overlay:    hsl(0 0% 100% / 6%);
    --loom-color-ink:           hsl(228 33% 95%);
    --loom-color-ink-muted:     hsl(225 12% 65%);
    --loom-color-border:        hsl(232 18% 22%);
    --loom-color-border-strong: hsl(232 14% 38%);
    --loom-color-primary:       hsl(238 100% 78%);
    --loom-color-primary-fg:    hsl(232 24% 8%);
    --loom-color-accent:        hsl(15 95% 70%);
    --loom-color-on-accent:     hsl(232 24% 8%);
    --loom-color-accent-glow:   hsl(258 100% 78%);
    --loom-color-info:          hsl(210 90% 70%);
    --loom-color-info-subtle:   hsl(210 50% 18%);
    --loom-color-success:       hsl(142 60% 60%);
    --loom-color-success-subtle: hsl(142 35% 18%);
    --loom-color-warn:          hsl(28 95% 65%);
    --loom-color-warning:       hsl(28 95% 65%);
    --loom-color-warning-subtle: hsl(28 50% 18%);
    --loom-color-danger:        hsl(0 80% 65%);
    --loom-color-danger-subtle: hsl(0 40% 22%);
    --loom-color-tier-medium:   hsl(45 95% 60%);
    --loom-color-gradient-a:    hsl(238 100% 78%);
    --loom-color-gradient-b:    hsl(15 95% 70%);
  }

  /* data-theme="dark-amoled" — true-black variant for OLED displays.
   *
   * bg-canvas at hsl(0 0% 0%) so OLED pixels are literally off:
   * substantial battery savings on mobile, maximum contrast for
   * low-vision readers without crossing into hc-dark's higher-
   * contrast brand colors.
   *
   * Elevation grays (surface / surface-muted) stay near-black —
   * just enough delta to convey depth in card stacks + modals
   * without lighting up pixels that don't need to be on. Brand
   * colors (primary, gradient-a/b, accent-glow, success/warn/
   * danger) match the standard `dark` theme so the visual brand
   * stays consistent across the two dark variants.
   *
   * Per `feedback_dark_theme_amoled_true_black`: OLED + max
   * contrast + low-battery friendly. Selected via Crawler test
   * matrix axis ?_theme=dark-amoled (see PlausiDen-Forge
   * docs/TESTING.md for the 24-combo matrix).
   */
  :root[data-theme="dark-amoled"] {
    --loom-color-bg-canvas:     hsl(0 0% 0%);
    --loom-color-surface:       hsl(0 0% 5%);
    --loom-color-surface-muted: hsl(0 0% 8%);
    --loom-color-surface-subtle: hsl(0 0% 6%);
    --loom-color-bg-overlay:    hsl(0 0% 100% / 6%);
    --loom-color-ink:           hsl(228 33% 95%);
    --loom-color-ink-muted:     hsl(225 12% 65%);
    --loom-color-border:        hsl(0 0% 18%);
    --loom-color-border-strong: hsl(0 0% 36%);
    --loom-color-primary:       hsl(238 100% 78%);
    --loom-color-primary-fg:    hsl(0 0% 0%);
    --loom-color-accent:        hsl(15 95% 70%);
    --loom-color-on-accent:     hsl(0 0% 0%);
    --loom-color-accent-glow:   hsl(258 100% 78%);
    --loom-color-info:          hsl(210 90% 70%);
    --loom-color-info-subtle:   hsl(210 50% 12%);
    --loom-color-success:       hsl(142 60% 60%);
    --loom-color-success-subtle: hsl(142 35% 12%);
    --loom-color-warn:          hsl(28 95% 65%);
    --loom-color-warning:       hsl(28 95% 65%);
    --loom-color-warning-subtle: hsl(28 50% 12%);
    --loom-color-danger:        hsl(0 80% 65%);
    --loom-color-danger-subtle: hsl(0 40% 15%);
    --loom-color-tier-medium:   hsl(45 95% 60%);
    --loom-color-gradient-a:    hsl(238 100% 78%);
    --loom-color-gradient-b:    hsl(15 95% 70%);
  }
  /* Explicit data-theme="light" — same as default :root. */
  :root[data-theme="light"] {
    --loom-color-bg-canvas:     hsl(40 33% 98%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(40 25% 95%);
    --loom-color-surface-subtle: hsl(40 30% 97%);
    --loom-color-bg-overlay:    hsl(225 20% 14% / 6%);
    --loom-color-ink:           hsl(225 25% 10%);
    --loom-color-ink-muted:     hsl(225 15% 32%);
    --loom-color-border:        hsl(36 16% 80%);
    --loom-color-border-strong: hsl(36 12% 60%);
    --loom-color-primary:       hsl(244 65% 38%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(15 85% 45%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(258 65% 45%);
    --loom-color-info:          hsl(210 70% 35%);
    --loom-color-info-subtle:   hsl(210 75% 95%);
    --loom-color-success:       hsl(142 75% 22%);
    --loom-color-success-subtle: hsl(142 55% 92%);
    --loom-color-warn:          hsl(28 95% 30%);
    --loom-color-warning:       hsl(28 95% 30%);
    --loom-color-warning-subtle: hsl(28 80% 92%);
    --loom-color-danger:        hsl(0 75% 35%);
    --loom-color-danger-subtle: hsl(0 70% 94%);
    --loom-color-tier-medium:   hsl(38 95% 30%);
    --loom-color-gradient-a:    hsl(244 65% 38%);
    --loom-color-gradient-b:    hsl(15 85% 45%);
  }
  :root[data-theme="hc-dark"] {
    --loom-color-bg-canvas:     hsl(0 0% 0%);
    --loom-color-surface:       hsl(0 0% 4%);
    --loom-color-surface-muted: hsl(0 0% 8%);
    --loom-color-surface-subtle: hsl(0 0% 6%);
    --loom-color-bg-overlay:    hsl(0 0% 100% / 12%);
    --loom-color-ink:           hsl(0 0% 100%);
    --loom-color-ink-muted:     hsl(0 0% 86%);
    --loom-color-border:        hsl(0 0% 50%);
    --loom-color-border-strong: hsl(0 0% 70%);
    --loom-color-primary:       hsl(220 100% 75%);
    --loom-color-primary-fg:    hsl(0 0% 0%);
    --loom-color-accent:        hsl(45 100% 70%);
    --loom-color-on-accent:     hsl(0 0% 0%);
    --loom-color-accent-glow:   hsl(220 100% 88%);
    --loom-color-info:          hsl(210 100% 80%);
    --loom-color-info-subtle:   hsl(210 80% 18%);
    --loom-color-success:       hsl(140 100% 60%);
    --loom-color-success-subtle: hsl(140 80% 16%);
    --loom-color-warn:          hsl(35 100% 70%);
    --loom-color-warning:       hsl(35 100% 70%);
    --loom-color-warning-subtle: hsl(45 100% 18%);
    --loom-color-danger:        hsl(0 100% 70%);
    --loom-color-danger-subtle: hsl(0 100% 22%);
    --loom-color-tier-medium:   hsl(45 100% 65%);
    --loom-color-gradient-a:    hsl(220 100% 75%);
    --loom-color-gradient-b:    hsl(280 100% 75%);
  }
  :root[data-theme="hc-dark"] .loom-hero-title {
    background: none;
    -webkit-background-clip: unset;
    background-clip: unset;
    color: var(--loom-color-ink);
  }

  /* High-contrast light — for users with low-vision who need
   * AAA on a light background. */
  :root[data-theme="hc-light"] {
    --loom-color-bg-canvas:     hsl(0 0% 100%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(0 0% 96%);
    --loom-color-surface-subtle: hsl(0 0% 98%);
    --loom-color-bg-overlay:    hsl(0 0% 0% / 8%);
    --loom-color-ink:           hsl(0 0% 0%);
    --loom-color-ink-muted:     hsl(0 0% 24%);
    --loom-color-border:        hsl(0 0% 30%);
    --loom-color-border-strong: hsl(0 0% 10%);
    --loom-color-primary:       hsl(220 100% 30%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(15 100% 32%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(220 100% 45%);
    --loom-color-info:          hsl(210 100% 28%);
    --loom-color-info-subtle:   hsl(210 100% 92%);
    --loom-color-success:       hsl(140 100% 22%);
    --loom-color-success-subtle: hsl(140 80% 88%);
    --loom-color-warn:          hsl(35 100% 32%);
    --loom-color-warning:       hsl(35 100% 32%);
    --loom-color-warning-subtle: hsl(35 100% 88%);
    --loom-color-danger:        hsl(0 100% 35%);
    --loom-color-danger-subtle: hsl(0 100% 92%);
    --loom-color-tier-medium:   hsl(45 100% 38%);
    --loom-color-gradient-a:    hsl(220 100% 30%);
    --loom-color-gradient-b:    hsl(280 100% 35%);
  }
  :root[data-theme="hc-light"] .loom-hero-title {
    background: none;
    -webkit-background-clip: unset;
    background-clip: unset;
    color: var(--loom-color-ink);
  }

  /* T66 (closes #649): named site palettes. Operators set
   * `[render] theme = "warm" | "ocean" | "forest" | "violet" |
   * "rose"` in forge.toml to ship their site with the named
   * brand palette. Light by default; reuses the cycle 95f
   * data-theme allow-list contract. Each palette is internally
   * AA-contrast verified. */
  :root[data-theme="warm"] {
    --loom-color-bg-canvas:     hsl(35 35% 97%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(35 25% 93%);
    --loom-color-surface-subtle: hsl(35 30% 95%);
    --loom-color-bg-overlay:    hsl(25 35% 16% / 6%);
    --loom-color-ink:           hsl(25 35% 12%);
    --loom-color-ink-muted:     hsl(25 18% 36%);
    --loom-color-border:        hsl(30 25% 80%);
    --loom-color-border-strong: hsl(30 20% 60%);
    --loom-color-primary:       hsl(20 80% 38%);
    --loom-color-primary-fg:    hsl(35 35% 97%);
    --loom-color-accent:        hsl(35 90% 45%);
    --loom-color-on-accent:     hsl(35 35% 97%);
    --loom-color-accent-glow:   hsl(20 90% 50%);
    --loom-color-info:          hsl(210 75% 35%);
    --loom-color-info-subtle:   hsl(210 75% 94%);
    --loom-color-success:       hsl(140 60% 26%);
    --loom-color-success-subtle: hsl(140 60% 90%);
    --loom-color-warn:          hsl(35 90% 32%);
    --loom-color-warning:       hsl(35 90% 32%);
    --loom-color-warning-subtle: hsl(35 90% 90%);
    --loom-color-danger:        hsl(0 75% 38%);
    --loom-color-danger-subtle: hsl(0 75% 93%);
    --loom-color-tier-medium:   hsl(38 90% 38%);
    --loom-color-gradient-a:    hsl(20 80% 38%);
    --loom-color-gradient-b:    hsl(35 80% 50%);
  }
  :root[data-theme="ocean"] {
    --loom-color-bg-canvas:     hsl(210 40% 98%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(210 35% 94%);
    --loom-color-surface-subtle: hsl(210 35% 96%);
    --loom-color-bg-overlay:    hsl(210 50% 14% / 6%);
    --loom-color-ink:           hsl(210 50% 12%);
    --loom-color-ink-muted:     hsl(210 25% 36%);
    --loom-color-border:        hsl(210 30% 80%);
    --loom-color-border-strong: hsl(210 25% 60%);
    --loom-color-primary:       hsl(210 80% 36%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(195 95% 38%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(195 95% 42%);
    --loom-color-info:          hsl(210 80% 30%);
    --loom-color-info-subtle:   hsl(210 80% 94%);
    --loom-color-success:       hsl(155 65% 26%);
    --loom-color-success-subtle: hsl(155 65% 90%);
    --loom-color-warn:          hsl(28 90% 32%);
    --loom-color-warning:       hsl(28 90% 32%);
    --loom-color-warning-subtle: hsl(28 90% 92%);
    --loom-color-danger:        hsl(0 75% 38%);
    --loom-color-danger-subtle: hsl(0 75% 93%);
    --loom-color-tier-medium:   hsl(38 90% 38%);
    --loom-color-gradient-a:    hsl(210 80% 36%);
    --loom-color-gradient-b:    hsl(195 95% 50%);
  }
  :root[data-theme="forest"] {
    --loom-color-bg-canvas:     hsl(120 20% 97%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(120 18% 93%);
    --loom-color-surface-subtle: hsl(120 20% 95%);
    --loom-color-bg-overlay:    hsl(140 40% 14% / 6%);
    --loom-color-ink:           hsl(140 40% 12%);
    --loom-color-ink-muted:     hsl(140 18% 32%);
    --loom-color-border:        hsl(130 20% 80%);
    --loom-color-border-strong: hsl(130 18% 58%);
    --loom-color-primary:       hsl(155 65% 28%);
    --loom-color-primary-fg:    hsl(120 20% 97%);
    --loom-color-accent:        hsl(85 70% 36%);
    --loom-color-on-accent:     hsl(120 20% 97%);
    --loom-color-accent-glow:   hsl(155 75% 36%);
    --loom-color-info:          hsl(210 70% 32%);
    --loom-color-info-subtle:   hsl(210 70% 94%);
    --loom-color-success:       hsl(140 70% 22%);
    --loom-color-success-subtle: hsl(140 70% 88%);
    --loom-color-warn:          hsl(35 95% 30%);
    --loom-color-warning:       hsl(35 95% 30%);
    --loom-color-warning-subtle: hsl(35 95% 90%);
    --loom-color-danger:        hsl(0 75% 36%);
    --loom-color-danger-subtle: hsl(0 75% 93%);
    --loom-color-tier-medium:   hsl(40 90% 36%);
    --loom-color-gradient-a:    hsl(155 65% 28%);
    --loom-color-gradient-b:    hsl(85 70% 40%);
  }
  :root[data-theme="violet"] {
    --loom-color-bg-canvas:     hsl(270 25% 98%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(270 22% 94%);
    --loom-color-surface-subtle: hsl(270 22% 96%);
    --loom-color-bg-overlay:    hsl(265 35% 14% / 6%);
    --loom-color-ink:           hsl(265 35% 12%);
    --loom-color-ink-muted:     hsl(265 18% 36%);
    --loom-color-border:        hsl(270 22% 80%);
    --loom-color-border-strong: hsl(270 18% 60%);
    --loom-color-primary:       hsl(265 70% 42%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(310 80% 45%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(285 85% 55%);
    --loom-color-info:          hsl(210 70% 35%);
    --loom-color-info-subtle:   hsl(210 75% 94%);
    --loom-color-success:       hsl(155 65% 26%);
    --loom-color-success-subtle: hsl(155 65% 90%);
    --loom-color-warn:          hsl(35 90% 32%);
    --loom-color-warning:       hsl(35 90% 32%);
    --loom-color-warning-subtle: hsl(35 90% 92%);
    --loom-color-danger:        hsl(0 75% 38%);
    --loom-color-danger-subtle: hsl(0 75% 94%);
    --loom-color-tier-medium:   hsl(38 90% 38%);
    --loom-color-gradient-a:    hsl(265 70% 42%);
    --loom-color-gradient-b:    hsl(310 80% 55%);
  }
  :root[data-theme="rose"] {
    --loom-color-bg-canvas:     hsl(340 25% 98%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(340 20% 95%);
    --loom-color-surface-subtle: hsl(340 22% 96%);
    --loom-color-bg-overlay:    hsl(340 40% 14% / 6%);
    --loom-color-ink:           hsl(340 35% 12%);
    --loom-color-ink-muted:     hsl(340 18% 36%);
    --loom-color-border:        hsl(340 22% 82%);
    --loom-color-border-strong: hsl(340 18% 62%);
    --loom-color-primary:       hsl(340 75% 42%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(15 80% 48%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(355 85% 55%);
    --loom-color-info:          hsl(210 70% 35%);
    --loom-color-info-subtle:   hsl(210 75% 94%);
    --loom-color-success:       hsl(155 65% 26%);
    --loom-color-success-subtle: hsl(155 65% 90%);
    --loom-color-warn:          hsl(28 90% 32%);
    --loom-color-warning:       hsl(28 90% 32%);
    --loom-color-warning-subtle: hsl(28 90% 92%);
    --loom-color-danger:        hsl(0 80% 38%);
    --loom-color-danger-subtle: hsl(0 80% 94%);
    --loom-color-tier-medium:   hsl(38 90% 38%);
    --loom-color-gradient-a:    hsl(340 75% 42%);
    --loom-color-gradient-b:    hsl(15 80% 55%);
  }

  /* Sepia — warm low-strain reading variant. */
  /* data-theme="press" — monochrome editorial palette. Newspaper /
   * literary-press / academic-publication aesthetic. Single muted
   * red accent for emphasis only (link, current-page chip); the
   * substrate runs on ink + paper + 3 levels of gray. Distinct
   * from light/sepia in that it refuses chromatic decoration. */
  :root[data-theme="press"] {
    --loom-color-bg-canvas:     hsl(40 6% 96%);
    --loom-color-surface:       hsl(0 0% 100%);
    --loom-color-surface-muted: hsl(40 4% 92%);
    --loom-color-surface-subtle: hsl(40 5% 94%);
    --loom-color-bg-overlay:    hsl(0 0% 12% / 6%);
    --loom-color-ink:           hsl(0 0% 10%);
    --loom-color-ink-muted:     hsl(0 0% 38%);
    --loom-color-border:        hsl(0 0% 82%);
    --loom-color-border-strong: hsl(0 0% 62%);
    --loom-color-primary:       hsl(0 65% 38%);
    --loom-color-primary-fg:    hsl(0 0% 100%);
    --loom-color-accent:        hsl(0 65% 38%);
    --loom-color-on-accent:     hsl(0 0% 100%);
    --loom-color-accent-glow:   hsl(0 50% 50%);
    --loom-color-info:          hsl(0 0% 10%);
    --loom-color-info-subtle:   hsl(0 0% 92%);
    --loom-color-success:       hsl(0 0% 10%);
    --loom-color-success-subtle: hsl(0 0% 92%);
    --loom-color-warn:          hsl(0 0% 10%);
    --loom-color-warning:       hsl(0 0% 10%);
    --loom-color-warning-subtle: hsl(0 0% 92%);
    --loom-color-danger:        hsl(0 65% 38%);
    --loom-color-danger-subtle: hsl(0 60% 92%);
    --loom-color-tier-medium:   hsl(0 0% 38%);
    --loom-color-gradient-a:    hsl(0 0% 10%);
    --loom-color-gradient-b:    hsl(0 0% 38%);
  }

  :root[data-theme="sepia"] {
    --loom-color-bg-canvas:     hsl(35 35% 91%);
    --loom-color-surface:       hsl(35 35% 95%);
    --loom-color-surface-muted: hsl(35 30% 87%);
    --loom-color-surface-subtle: hsl(35 35% 89%);
    --loom-color-bg-overlay:    hsl(25 35% 18% / 8%);
    --loom-color-ink:           hsl(25 35% 18%);
    --loom-color-ink-muted:     hsl(25 25% 36%);
    --loom-color-border:        hsl(30 25% 75%);
    --loom-color-border-strong: hsl(30 25% 60%);
    --loom-color-primary:       hsl(20 70% 38%);
    --loom-color-primary-fg:    hsl(35 35% 95%);
    --loom-color-accent:        hsl(20 80% 42%);
    --loom-color-on-accent:     hsl(35 35% 95%);
    --loom-color-accent-glow:   hsl(20 70% 55%);
    --loom-color-info:          hsl(210 60% 32%);
    --loom-color-info-subtle:   hsl(35 50% 86%);
    --loom-color-success:       hsl(140 50% 28%);
    --loom-color-success-subtle: hsl(140 45% 84%);
    --loom-color-warn:          hsl(35 80% 32%);
    --loom-color-warning:       hsl(35 80% 32%);
    --loom-color-warning-subtle: hsl(35 70% 84%);
    --loom-color-danger:        hsl(0 60% 38%);
    --loom-color-danger-subtle: hsl(0 60% 88%);
    --loom-color-tier-medium:   hsl(40 90% 40%);
    --loom-color-gradient-a:    hsl(20 70% 50%);
    --loom-color-gradient-b:    hsl(35 80% 45%);
  }

  /* data-theme="editorial" — magazine / publication aesthetic.
   * Warm-neutral cream canvas, dark ink, accent in publication-red.
   * Designed for kinfolk / NYT Magazine / longform-editorial sites.
   * Pairs with [style.fonts] override for serif display + serif body;
   * the substrate intentionally does NOT bundle a serif font because
   * font-loading is a tenant decision (license, performance, network).
   * Per docs/SUBSTRATE_GAP_CATALOG_2026_05_27.md Tier 1 #2 + #358. */
  :root[data-theme="editorial"] {
    --loom-color-bg-canvas:     hsl(38 22% 95%);
    --loom-color-surface:       hsl(38 25% 97%);
    --loom-color-surface-muted: hsl(38 18% 90%);
    --loom-color-surface-subtle: hsl(38 20% 93%);
    --loom-color-bg-overlay:    hsl(28 30% 18% / 8%);
    --loom-color-ink:           hsl(28 30% 14%);
    --loom-color-ink-muted:     hsl(28 18% 38%);
    --loom-color-border:        hsl(38 14% 78%);
    --loom-color-border-strong: hsl(38 14% 58%);
    --loom-color-primary:       hsl(0 55% 38%);
    --loom-color-primary-fg:    hsl(38 25% 97%);
    --loom-color-accent:        hsl(0 55% 38%);
    --loom-color-on-accent:     hsl(38 25% 97%);
    --loom-color-accent-glow:   hsl(0 45% 50%);
    --loom-color-info:          hsl(210 50% 32%);
    --loom-color-info-subtle:   hsl(38 30% 88%);
    --loom-color-success:       hsl(140 38% 28%);
    --loom-color-success-subtle: hsl(140 35% 86%);
    --loom-color-warn:          hsl(35 75% 32%);
    --loom-color-warning:       hsl(35 75% 32%);
    --loom-color-warning-subtle: hsl(35 60% 86%);
    --loom-color-danger:        hsl(0 55% 38%);
    --loom-color-danger-subtle: hsl(0 50% 90%);
    --loom-color-tier-medium:   hsl(28 15% 42%);
    --loom-color-gradient-a:    hsl(0 55% 38%);
    --loom-color-gradient-b:    hsl(28 30% 14%);
  }

  /* Font variants — swap the entire type system.
   * data-font="display" is the default (already set in tokens layer). */
  :root[data-font="serif"] {
    --loom-font-display: ui-serif, Georgia, Cambria, "Times New Roman",
                         Times, serif;
    --loom-font-body:    ui-serif, Georgia, Cambria, "Times New Roman",
                         Times, serif;
  }
  :root[data-font="mono"] {
    --loom-font-display: ui-monospace, "JetBrains Mono", "SF Mono",
                         Menlo, Consolas, monospace;
    --loom-font-body:    ui-monospace, "JetBrains Mono", "SF Mono",
                         Menlo, Consolas, monospace;
    --loom-track-tight:  -0.01em;
  }
  :root[data-font="rounded"] {
    --loom-font-display: ui-rounded, "SF Pro Rounded",
                         "Hiragino Maru Gothic ProN", "Quicksand",
                         system-ui, sans-serif;
    --loom-font-body:    ui-rounded, "SF Pro Rounded",
                         "Hiragino Maru Gothic ProN", "Quicksand",
                         system-ui, sans-serif;
  }

  /* Density variants — swap the spacing scale globally.
   * data-density="comfortable" is the default. */
  :root[data-density="compact"] {
    --loom-pad-card:   var(--loom-space-3);
    --loom-pad-panel:  var(--loom-space-4);
    --loom-pad-band:   var(--loom-space-5);
    --loom-gap-stack:  var(--loom-space-3);
    --loom-gap-row:    var(--loom-space-2);
    --loom-gap-grid:   var(--loom-space-3);
  }
  :root[data-density="spacious"] {
    --loom-pad-card:   var(--loom-space-6);
    --loom-pad-panel:  var(--loom-space-8);
    --loom-pad-band:   var(--loom-space-12);
    --loom-gap-stack:  var(--loom-space-6);
    --loom-gap-row:    var(--loom-space-4);
    --loom-gap-grid:   var(--loom-space-6);
  }


/* ============================================================ *
 * @layer utilities                                              *
 * ============================================================ */

/* unwrapped @layer */

  .loom-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  /* Display utilities — last word in the cascade. Use sparingly. */
  .loom-hidden       { display: none !important; }
  .loom-show-md      { display: none; }
  @media (min-width: 768px) { .loom-show-md { display: revert; } }


/* ============================================================ *
 * Accessibility — applied across all layers                    *
 * ------------------------------------------------------------ *
 * Honors user-system preferences. Always-on, no opt-in, no JS  *
 * required for the core a11y pillars.                          *
 * ============================================================ */

/* Forced-colors mode (Windows High Contrast). All decorative
 * gradients fall back to system tokens; component shapes
 * preserved. */
@media (forced-colors: active) {
  .loom-brand,
  .loom-btn[data-variant="primary"],
  .loom-card-battle .thumb {
    background: ButtonFace !important;
    color: ButtonText !important;
    -webkit-text-fill-color: currentColor !important;
  }
  .loom-card-battle:hover {
    border-color: Highlight !important;
  }
  .loom-pulse-dot { background: Highlight !important; }
}

/* Reduced motion — disable transitions + animations for users
 * who set the OS preference. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Increased-contrast — bump border + text contrast when the OS
 * preference is set. */
@media (prefers-contrast: more) {
  :root {
    --loom-color-border:        var(--loom-color-border-strong);
    --loom-color-ink-muted:     var(--loom-color-ink);
  }
  .loom-card-battle, .loom-panel, .loom-hero {
    border-width: 2px;
  }
}

/* Larger touch targets on coarse pointers (touch / pen). */
@media (pointer: coarse) {
  .loom-btn {
    --comp-btn-pad-x: var(--loom-space-5);
    --comp-btn-pad-y: var(--loom-space-3);
  }
  .loom-navlinks a {
    padding: var(--loom-space-3) var(--loom-space-4);
  }
}
/* ============================================================ *
 * Complex animation + complex-component primitives             *
 * ------------------------------------------------------------ *
 * Owner directive 2026-05-04: complex animations + complex UI. *
 * Lives outside the @layer system because it appends to the    *
 * cascade tail and must always win over earlier component      *
 * defaults (deliberate).                                       *
 * ============================================================ */

/* Motion utility classes — drop on any element. All honor
 * prefers-reduced-motion via the universal reset rule. */
.loom-anim-fade-in       { animation: loom-fade-in       var(--loom-transition-base) both; }
.loom-anim-fade-in-up    { animation: loom-fade-in-up    var(--loom-transition-slow) both; }
.loom-anim-scale-in      { animation: loom-scale-in      var(--loom-transition-base) both; transform-origin: center; }
.loom-anim-slide-in-right{ animation: loom-slide-in-right var(--loom-transition-slow) both; }
.loom-anim-pop           { animation: loom-pop           var(--loom-transition-base) both; }
.loom-anim-shimmer {
  background: linear-gradient(90deg,
    var(--loom-color-surface-muted) 0%,
    var(--loom-color-bg-overlay) 50%,
    var(--loom-color-surface-muted) 100%);
  background-size: 200% 100%;
  animation: loom-shimmer var(--loom-animation-loop) linear infinite;
}
/* T35: stagger delays driven by --loom-stagger-step. Tweaking
 * the cascade pace = edit one token; every tier follows. */
[data-stagger="1"]  { animation-delay: calc(var(--loom-stagger-step) *  1); }
[data-stagger="2"]  { animation-delay: calc(var(--loom-stagger-step) *  2); }
[data-stagger="3"]  { animation-delay: calc(var(--loom-stagger-step) *  3); }
[data-stagger="4"]  { animation-delay: calc(var(--loom-stagger-step) *  4); }
[data-stagger="5"]  { animation-delay: calc(var(--loom-stagger-step) *  5); }
[data-stagger="6"]  { animation-delay: calc(var(--loom-stagger-step) *  6); }
[data-stagger="7"]  { animation-delay: calc(var(--loom-stagger-step) *  7); }
[data-stagger="8"]  { animation-delay: calc(var(--loom-stagger-step) *  8); }
[data-stagger="9"]  { animation-delay: calc(var(--loom-stagger-step) *  9); }
[data-stagger="10"] { animation-delay: calc(var(--loom-stagger-step) * 10); }
[data-stagger="11"] { animation-delay: calc(var(--loom-stagger-step) * 11); }
[data-stagger="12"] { animation-delay: calc(var(--loom-stagger-step) * 12); }

@keyframes loom-fade-in    { from { opacity: 0; } to { opacity: 1; } }
@keyframes loom-fade-in-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes loom-scale-in   { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: scale(1); } }
@keyframes loom-slide-in-right { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } }
@keyframes loom-pop        { 0% { transform: scale(1); } 50% { transform: scale(1.04); } 100% { transform: scale(1); } }
@keyframes loom-shimmer    { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* Skeleton loader — shimmer over surface tokens. */
.loom-skeleton {
  border-radius: var(--loom-radius-md);
  background: linear-gradient(90deg,
    var(--loom-color-surface-muted) 25%,
    var(--loom-color-bg-overlay) 50%,
    var(--loom-color-surface-muted) 75%);
  background-size: 200% 100%;
  animation: loom-shimmer var(--loom-animation-loop) linear infinite;
  color: transparent;
  user-select: none;
}
.loom-skeleton[data-shape="text"]   { height: var(--loom-skeleton-h-text); width: 100%; }
.loom-skeleton[data-shape="title"]  { height: var(--loom-skeleton-h-title); width: 60%; }
.loom-skeleton[data-shape="circle"] {
  border-radius: var(--loom-radius-full);
  aspect-ratio: 1; width: var(--loom-space-12);
}

/* Tooltip — pure-CSS, no JS. Add data-tooltip + .loom-tooltip. */
.loom-tooltip { position: relative; }
.loom-tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + var(--loom-space-2));
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  font-size: var(--loom-font-xs);
  font-weight: 600;
  padding: var(--loom-space-1) var(--loom-space-2);
  border: var(--loom-border-strong);
  border-radius: var(--loom-radius-md);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--loom-transition-fast),
              transform var(--loom-transition-fast);
  z-index: var(--loom-z-overlay);
}
.loom-tooltip:hover::after,
.loom-tooltip:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Drawer — slides in from the right. Toggle via
 * body[data-loom-drawer-open]. */
.loom-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(420px, calc(100vw - var(--loom-space-8)));
  background: var(--loom-color-surface);
  border-left: var(--loom-border-strong);
  box-shadow: var(--loom-shadow-elevated);
  transform: translateX(100%);
  transition: transform var(--loom-transition-slow);
  z-index: var(--loom-z-toast);
  overflow-y: auto;
  padding: var(--loom-pad-band);
}
body[data-loom-drawer-open] .loom-drawer { transform: translateX(0); }
.loom-drawer-scrim {
  position: fixed; inset: 0;
  background: hsl(220 33% 0% / 0.5);
  opacity: 0; pointer-events: none;
  transition: opacity var(--loom-transition-base);
  z-index: var(--loom-z-tooltip);
}
body[data-loom-drawer-open] .loom-drawer-scrim {
  opacity: 1; pointer-events: auto;
}

/* Aesthetic picker chip layout (used by Forge sidebar). */
.loom-aesthetic-row {
  display: flex; flex-wrap: wrap;
  gap: var(--loom-space-2);
  margin-bottom: var(--loom-space-3);
}
.loom-aesthetic-row:last-child { margin-bottom: 0; }

/* ============================================================
 * Asymmetric / broken-grid primitives (task #48)
 *
 * Layout primitives that intentionally violate the regular grid
 * within their declared safe-overlap zones. Used by brutalist /
 * post-digital / editorial style packs to read as "designed by
 * a human with taste" rather than "generated by a template
 * engine."
 *
 * Each primitive has a tight overflow contract: the BOUNDING
 * BOX is the same as a regular .loom-section, only the INNER
 * children violate the grid. Selectability + a11y + reflow
 * behavior all preserved. Reduced-motion respected on every
 * animated variant.
 *
 * Per ARCHITECTURE_PRINCIPLES.md §3 — "controlled chaos is
 * itself a primitive."
 * ============================================================ */

/* loom-offset — single child offset within container bounds.
 * data-offset-x="N" + data-offset-y="N" in token-units (1 = 4px).
 * Child cannot overflow parent; clamp keeps it inside.
 */
.loom-offset {
  position: relative;
  overflow: hidden;
}
.loom-offset > * {
  position: relative;
  transform: translate(
    clamp(-50%, calc(var(--loom-offset-x, 0) * var(--loom-space-1)), 50%),
    clamp(-50%, calc(var(--loom-offset-y, 0) * var(--loom-space-1)), 50%)
  );
}
.loom-offset[data-offset-x="2"] { --loom-offset-x: 2; }
.loom-offset[data-offset-x="4"] { --loom-offset-x: 4; }
.loom-offset[data-offset-x="-2"] { --loom-offset-x: -2; }
.loom-offset[data-offset-x="-4"] { --loom-offset-x: -4; }
.loom-offset[data-offset-y="2"] { --loom-offset-y: 2; }
.loom-offset[data-offset-y="-2"] { --loom-offset-y: -2; }

/* loom-overlap — declared overlap zone with managed z-axis.
 * Children stack with explicit z-index per data-z (1-5).
 * Container has contain:layout so the overlap doesn't escape.
 */
.loom-overlap {
  position: relative;
  contain: layout style;
}
.loom-overlap > * { position: absolute; }
.loom-overlap > *[data-z="1"] { z-index: 1; }
.loom-overlap > *[data-z="2"] { z-index: 2; }
.loom-overlap > *[data-z="3"] { z-index: 3; }
.loom-overlap > *[data-z="4"] { z-index: 4; }
.loom-overlap > *[data-z="5"] { z-index: 5; }
/* Convenience anchors: corners + center within the overlap box. */
.loom-overlap > *[data-anchor="top-left"]     { top: 0; left: 0; }
.loom-overlap > *[data-anchor="top-right"]    { top: 0; right: 0; }
.loom-overlap > *[data-anchor="bottom-left"]  { bottom: 0; left: 0; }
.loom-overlap > *[data-anchor="bottom-right"] { bottom: 0; right: 0; }
.loom-overlap > *[data-anchor="center"] {
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
}

/* loom-broken — intentional grid violation. Each child gets a
 * pseudo-random offset based on its DOM position. Bounded so
 * it can't overflow + so reading order stays sane.
 */
.loom-broken {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--loom-space-4);
  overflow: hidden;
}
.loom-broken > *:nth-child(3n+1) { transform: translateY(calc(-1 * var(--loom-space-2))); }
.loom-broken > *:nth-child(3n+2) { transform: translateY(var(--loom-space-3)); }
.loom-broken > *:nth-child(3n+3) { transform: translateY(calc(-1 * var(--loom-space-1))); }

/* loom-diagonal — content rotated within a horizontal band.
 * data-rotate="±N" in degrees (clamped 0-15).
 * Bounding box stays axis-aligned; only the inner content
 * tilts.
 */
.loom-diagonal {
  position: relative;
  overflow: hidden;
  padding-block: var(--loom-space-6);
}
.loom-diagonal > * {
  transform: rotate(calc(var(--loom-diagonal-rotate, 0) * 1deg));
  transform-origin: center center;
}
.loom-diagonal[data-rotate="3"]   { --loom-diagonal-rotate: 3; }
.loom-diagonal[data-rotate="-3"]  { --loom-diagonal-rotate: -3; }
.loom-diagonal[data-rotate="6"]   { --loom-diagonal-rotate: 6; }
.loom-diagonal[data-rotate="-6"]  { --loom-diagonal-rotate: -6; }
.loom-diagonal[data-rotate="12"]  { --loom-diagonal-rotate: 12; }
.loom-diagonal[data-rotate="-12"] { --loom-diagonal-rotate: -12; }

/* loom-marquee — horizontally scrolling text band.
 * Animation is REQUIRED to be guarded by prefers-reduced-motion:
 * vestibular-disorder readers see motion they didn't opt in to
 * otherwise. The animation is wrapped accordingly + falls back
 * to a static layout when reduce is set.
 */
.loom-marquee {
  overflow: hidden;
  white-space: nowrap;
  position: relative;
}
.loom-marquee > * {
  display: inline-block;
  padding-inline: var(--loom-space-8);
}
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-marquee-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
  }
  .loom-marquee > * {
    animation: loom-marquee-scroll 30s linear infinite;
  }
  .loom-marquee[data-speed="fast"] > * { animation-duration: 15s; }
  .loom-marquee[data-speed="slow"] > * { animation-duration: 60s; }
}
/* Reduce: no animation, content shows once + clips on overflow. */
@media (prefers-reduced-motion: reduce) {
  .loom-marquee > * { animation: none; }
}

/* loom-scattered — children placed at pseudo-random offsets
 * (driven by nth-child) within a contained box. Similar to
 * loom-broken but with finer granularity + explicit
 * non-overlapping zones.
 */
.loom-scattered {
  position: relative;
  min-height: 20rem;
  contain: layout style;
}
.loom-scattered > * {
  position: absolute;
  max-width: 33%;
}
.loom-scattered > *:nth-child(1) { top: 8%;  left: 12%; }
.loom-scattered > *:nth-child(2) { top: 22%; right: 8%; }
.loom-scattered > *:nth-child(3) { bottom: 18%; left: 24%; }
.loom-scattered > *:nth-child(4) { top: 45%; right: 25%; }
.loom-scattered > *:nth-child(5) { bottom: 8%; right: 12%; }
.loom-scattered > *:nth-child(6) { top: 60%; left: 6%; }

/* loom-kinetic — short-duration sweep animation on the
 * container itself. Used by brutalist + 90s-zine packs for
 * deliberate motion energy. Reduced-motion respected.
 */
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-kinetic-sweep {
    0%, 100% { transform: translateX(0); }
    50%      { transform: translateX(var(--loom-space-2)); }
  }
  .loom-kinetic {
    animation: loom-kinetic-sweep 8s ease-in-out infinite;
  }
}

/* ============================================================
 * Motion primitives (task #47)
 *
 * Scroll-linked / magnetic-hover / scrub-timeline / spring-feel
 * primitives. Every one of them lives inside a
 * @media (prefers-reduced-motion: no-preference) block so the
 * vestibular-affected reader sees them as static.
 *
 * Uses the CSS scroll-driven-animations spec (animation-timeline:
 * view()) — Chromium 115+, Safari 17.4+, Firefox progressive.
 * Where unsupported, the @keyframes simply doesn't run and the
 * element renders in its end state. No JS, no IntersectionObserver,
 * no layout thrashing.
 *
 * Spring physics is faked via overshoot-cubic-bezier — a real
 * spring solver would need JS. The cubic-bezier
 * (0.34, 1.56, 0.64, 1) hits ~6% overshoot which reads as
 * "spring" without RWD jitter.
 *
 * Per ARCHITECTURE_PRINCIPLES.md §3 — "motion is a primitive,
 * not a decoration."
 * ============================================================ */

/* loom-reveal-on-view — scroll-linked fade + rise as the element
 * enters the viewport. Uses view() timeline; no JS observer.
 *
 * data-distance="N" picks the rise distance in token-units
 * (default 4 = 16px).
 */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes loom-reveal-on-view-frames {
      from { opacity: 0; transform: translateY(var(--loom-reveal-distance, var(--loom-space-4))); }
      to   { opacity: 1; transform: translateY(0); }
    }
    .loom-reveal-on-view {
      animation: loom-reveal-on-view-frames linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 30%;
    }
    .loom-reveal-on-view[data-distance="2"]  { --loom-reveal-distance: var(--loom-space-2); }
    .loom-reveal-on-view[data-distance="4"]  { --loom-reveal-distance: var(--loom-space-4); }
    .loom-reveal-on-view[data-distance="8"]  { --loom-reveal-distance: var(--loom-space-8); }
  }
}

/* loom-slide-on-view — same envelope as reveal but horizontal.
 * data-from="left" | "right".
 */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes loom-slide-on-view-from-left {
      from { opacity: 0; transform: translateX(calc(-1 * var(--loom-space-8))); }
      to   { opacity: 1; transform: translateX(0); }
    }
    @keyframes loom-slide-on-view-from-right {
      from { opacity: 0; transform: translateX(var(--loom-space-8)); }
      to   { opacity: 1; transform: translateX(0); }
    }
    .loom-slide-on-view {
      animation: loom-slide-on-view-from-left linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 30%;
    }
    .loom-slide-on-view[data-from="right"] {
      animation-name: loom-slide-on-view-from-right;
    }
  }
}

/* loom-parallax-y — vertical parallax driven by the page's own
 * scroll timeline. data-depth="N" picks the parallax factor (1-3).
 * Bounded translation so it never escapes its column.
 */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    @keyframes loom-parallax-y-frames {
      from { transform: translateY(calc(-1 * var(--loom-parallax-depth, 1) * var(--loom-space-6))); }
      to   { transform: translateY(calc(var(--loom-parallax-depth, 1) * var(--loom-space-6))); }
    }
    .loom-parallax-y {
      animation: loom-parallax-y-frames linear both;
      animation-timeline: scroll(nearest);
      animation-range: cover 0% cover 100%;
    }
    .loom-parallax-y[data-depth="1"] { --loom-parallax-depth: 1; }
    .loom-parallax-y[data-depth="2"] { --loom-parallax-depth: 2; }
    .loom-parallax-y[data-depth="3"] { --loom-parallax-depth: 3; }
  }
}

/* loom-scrub-rotate — scroll-progress-driven rotation. Used for
 * decorative spinning marks (logos, glyphs).
 * data-revolutions="N" sets the count (0.5 / 1 / 2 / 4).
 */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    @keyframes loom-scrub-rotate-frames {
      from { transform: rotate(0deg); }
      to   { transform: rotate(calc(var(--loom-scrub-revolutions, 1) * 360deg)); }
    }
    .loom-scrub-rotate {
      animation: loom-scrub-rotate-frames linear both;
      animation-timeline: scroll(nearest);
      animation-range: cover 0% cover 100%;
    }
    .loom-scrub-rotate[data-revolutions="0.5"] { --loom-scrub-revolutions: 0.5; }
    .loom-scrub-rotate[data-revolutions="1"]   { --loom-scrub-revolutions: 1; }
    .loom-scrub-rotate[data-revolutions="2"]   { --loom-scrub-revolutions: 2; }
    .loom-scrub-rotate[data-revolutions="4"]   { --loom-scrub-revolutions: 4; }
  }
}

/* loom-magnetic-hover — pointer-driven lift + scale with
 * spring-feel cubic-bezier on hover/focus. No JS — uses transform
 * + transition only.
 *
 * data-intensity="subtle" | "medium" | "strong".
 */
@media (prefers-reduced-motion: no-preference) {
  .loom-magnetic-hover {
    --loom-magnetic-lift: calc(-1 * var(--loom-space-1));
    --loom-magnetic-scale: 1.02;
    transition: transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1);
    will-change: transform;
  }
  .loom-magnetic-hover:hover,
  .loom-magnetic-hover:focus-visible {
    transform: translateY(var(--loom-magnetic-lift)) scale(var(--loom-magnetic-scale));
  }
  .loom-magnetic-hover[data-intensity="subtle"] {
    --loom-magnetic-lift: calc(-0.5 * var(--loom-space-1));
    --loom-magnetic-scale: 1.01;
  }
  .loom-magnetic-hover[data-intensity="strong"] {
    --loom-magnetic-lift: calc(-1 * var(--loom-space-2));
    --loom-magnetic-scale: 1.04;
  }
}

/* loom-spring-pop — entrance animation with overshoot. Used by
 * modal contents + toasts + CTA reveals. Self-running on mount
 * (one-shot).
 *
 * data-delay="1".."5" reuses the existing --loom-stagger-step
 * token so it composes with [data-stagger="N"] siblings.
 */
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-spring-pop-frames {
    from { opacity: 0; transform: scale(0.92); }
    to   { opacity: 1; transform: scale(1); }
  }
  .loom-spring-pop {
    animation: loom-spring-pop-frames 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  }
  .loom-spring-pop[data-delay="1"] { animation-delay: calc(var(--loom-stagger-step) * 1); }
  .loom-spring-pop[data-delay="2"] { animation-delay: calc(var(--loom-stagger-step) * 2); }
  .loom-spring-pop[data-delay="3"] { animation-delay: calc(var(--loom-stagger-step) * 3); }
  .loom-spring-pop[data-delay="4"] { animation-delay: calc(var(--loom-stagger-step) * 4); }
  .loom-spring-pop[data-delay="5"] { animation-delay: calc(var(--loom-stagger-step) * 5); }
}

/* loom-attention-pulse — small recurring scale to nudge the eye
 * toward a single CTA. Distinct from .loom-pulse-dot (which is a
 * status indicator glyph). data-speed="slow"|"normal"|"fast".
 */
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-attention-pulse-frames {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.04); }
  }
  .loom-attention-pulse {
    animation: loom-attention-pulse-frames 2.4s ease-in-out infinite;
  }
  .loom-attention-pulse[data-speed="slow"] { animation-duration: 4s; }
  .loom-attention-pulse[data-speed="fast"] { animation-duration: 1.2s; }
}

/* ============================================================
 * Foundational layout primitives — Every-Layout family
 * (task #46, batch 1 of N).
 *
 * Pickering / Bell's "Every Layout" gave us a small set of
 * compositional layout primitives that any modern site can
 * be built from. We adopt the canonical names + add a few
 * extensions of our own, all driven by --loom-space-* tokens
 * + responsive at container-query granularity where supported.
 *
 * Each primitive:
 *   * uses --loom-space-* tokens (no magic numbers)
 *   * accepts a closed-enum size via data attributes
 *   * has bounded reflow behavior (no horizontal scroll
 *     surprises)
 *   * composes with itself and the existing primitives
 *
 * Naming kept tight + 1:1 with the literature so future
 * documentation can reference well-known names.
 * ============================================================ */

/* loom-box — semantic container with bounded padding +
 * border-radius. The base every other primitive composes
 * inside.
 */
.loom-box {
  padding: var(--loom-space-4);
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
}
.loom-box[data-pad="0"] { padding: 0; }
.loom-box[data-pad="2"] { padding: var(--loom-space-2); }
.loom-box[data-pad="6"] { padding: var(--loom-space-6); }
.loom-box[data-pad="8"] { padding: var(--loom-space-8); }

/* loom-cluster — horizontal flow that wraps when out of space.
 * Used for tag rows, navigation chips, action button rows.
 * Gap driven by --loom-space-3 by default.
 */
.loom-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--loom-space-3);
  align-items: center;
  justify-content: flex-start;
}
.loom-cluster[data-align="center"]  { justify-content: center; }
.loom-cluster[data-align="end"]     { justify-content: flex-end; }
.loom-cluster[data-align="between"] { justify-content: space-between; }
.loom-cluster[data-gap="2"] { gap: var(--loom-space-2); }
.loom-cluster[data-gap="4"] { gap: var(--loom-space-4); }
.loom-cluster[data-gap="6"] { gap: var(--loom-space-6); }

/* loom-center — max-width content rail centered horizontally.
 * For long-form reading + form column layouts.
 */
.loom-center {
  box-sizing: content-box;
  margin-inline: auto;
  max-width: 60ch;
  padding-inline: var(--loom-space-4);
}
.loom-center[data-max="40"] { max-width: 40ch; }
.loom-center[data-max="80"] { max-width: 80ch; }
.loom-center[data-max="prose"]   { max-width: 65ch; }
.loom-center[data-max="page"]    { max-width: 72rem; }
.loom-center[data-max="full"]    { max-width: 100%; padding-inline: 0; }

/* loom-cover — viewport-cover with optional centered child.
 * Used for hero sections + landing splashes.
 */
.loom-cover {
  display: flex;
  flex-direction: column;
  min-block-size: 100svh;
  padding: var(--loom-space-6);
}
.loom-cover > * {
  margin-block: var(--loom-space-4);
}
.loom-cover > [data-center] {
  margin-block: auto;
}
.loom-cover[data-min="50"] { min-block-size: 50svh; }
.loom-cover[data-min="75"] { min-block-size: 75svh; }

/* loom-sidebar — asymmetric "content + sidebar" pair.
 * Wraps when below the side-by-side threshold.
 */
.loom-sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--loom-space-6);
}
.loom-sidebar > :first-child {
  flex-basis: 20rem;
  flex-grow: 1;
}
.loom-sidebar > :last-child {
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: 50%;
}
.loom-sidebar[data-side="right"] > :first-child {
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: 50%;
}
.loom-sidebar[data-side="right"] > :last-child {
  flex-basis: 20rem;
  flex-grow: 1;
}

/* loom-switcher — content row that collapses to a stack when
 * the container drops below a threshold. Threshold driven by
 * --loom-switcher-threshold (default 40rem).
 */
.loom-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--loom-space-4);
}
.loom-switcher > * {
  flex-grow: 1;
  flex-basis: calc((var(--loom-switcher-threshold, 40rem) - 100%) * 999);
}

/* loom-grid — auto-fit responsive grid. Cell minimum driven
 * by --loom-grid-min (default 16rem); shrinks gracefully on
 * narrow screens.
 */
.loom-grid {
  display: grid;
  gap: var(--loom-space-4);
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(var(--loom-grid-min, 16rem), 100%), 1fr)
  );
}
.loom-grid[data-min="12"] { --loom-grid-min: 12rem; }
.loom-grid[data-min="20"] { --loom-grid-min: 20rem; }
.loom-grid[data-min="24"] { --loom-grid-min: 24rem; }

/* loom-frame — aspect-ratio media wrapper. Defaults 16:9.
 * Use for cards / cover images / video embeds.
 */
.loom-frame {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.loom-frame > img,
.loom-frame > video,
.loom-frame > picture {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
.loom-frame[data-ratio="1-1"] { aspect-ratio: 1 / 1; }
.loom-frame[data-ratio="3-2"] { aspect-ratio: 3 / 2; }
.loom-frame[data-ratio="4-3"] { aspect-ratio: 4 / 3; }
.loom-frame[data-ratio="21-9"] { aspect-ratio: 21 / 9; }

/* loom-reel — horizontal-scroll strip. Used for carousels,
 * filter pills, thumbnail strips. Snap on the items.
 */
.loom-reel {
  display: flex;
  block-size: auto;
  overflow-x: auto;
  overflow-y: hidden;
  gap: var(--loom-space-3);
  padding-block: var(--loom-space-2);
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.loom-reel > * {
  flex-shrink: 0;
  scroll-snap-align: start;
}
.loom-reel[data-snap="none"] { scroll-snap-type: none; }

/* loom-imposter — absolute-positioned overlay that centers
 * over a positioned ancestor. Use for modal anchors, hover
 * tooltips, focus rings. Pair with .loom-overlap or any
 * .loom-* container with position:relative.
 */
.loom-imposter {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  transform: translate(-50%, -50%);
  max-inline-size: 100%;
  max-block-size: 100%;
}
.loom-imposter[data-anchor="top"] {
  inset-block-start: 0;
  transform: translate(-50%, 0);
}
.loom-imposter[data-anchor="bottom"] {
  inset-block-start: auto;
  inset-block-end: 0;
  transform: translate(-50%, 0);
}

/* loom-icon — square icon wrapper with consistent sizing.
 * Closed-enum size keyword.
 */
.loom-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1.25em;
  block-size: 1.25em;
  flex-shrink: 0;
}
.loom-icon > svg { inline-size: 100%; block-size: 100%; }
.loom-icon[data-size="sm"] { inline-size: 1em;    block-size: 1em; }
.loom-icon[data-size="md"] { inline-size: 1.25em; block-size: 1.25em; }
.loom-icon[data-size="lg"] { inline-size: 1.5em;  block-size: 1.5em; }
.loom-icon[data-size="xl"] { inline-size: 2em;    block-size: 2em; }

/* ============================================================
 * Typography primitives — vocabulary batch 2 (task #46).
 *
 * Type scale follows a 1.25 (major-third) ratio over the
 * --loom-type-base token. Line-heights tuned per scale step:
 * tighter for display, looser for body, very loose for prose
 * to match Bringhurst-style print rhythm.
 *
 * All sizes use clamp() so they fluidly scale with viewport
 * width without breaking at any breakpoint. min < base < max.
 *
 * Every primitive composes inside the Every-Layout primitives
 * from batch 1 (.loom-center / .loom-stack / .loom-box).
 * ============================================================ */

/* Display + heading scale.
 * Display is hero-sized + only used once per page (per design
 * doctrine). h1-h6 are the standard semantic scale.
 */
.loom-display {
  font-family: var(--loom-font-display, var(--loom-font-sans));
  font-size: clamp(2.5rem, 4vw + 1rem, 4.5rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--loom-color-ink);
  margin: 0;
}
.loom-heading-1 {
  font-size: clamp(2rem, 3vw + 0.8rem, 3rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin: 0;
}
.loom-heading-2 {
  font-size: clamp(1.5rem, 2vw + 0.6rem, 2.25rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0;
}
.loom-heading-3 {
  font-size: clamp(1.25rem, 1.5vw + 0.4rem, 1.75rem);
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
}
.loom-heading-4 {
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 0;
}
.loom-heading-5 {
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 0;
}
.loom-heading-6 {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 0;
}

/* Body text scale.
 * Lead is the standout intro paragraph; body is the reading
 * default; small + caption are for metadata.
 */
.loom-text-lead {
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--loom-color-ink);
  margin: 0;
}
.loom-text-body {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--loom-color-ink);
  margin: 0;
}
.loom-text-small {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  margin: 0;
}
.loom-text-caption {
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  letter-spacing: 0.02em;
  margin: 0;
}

/* Prose wrapper — applies vertical rhythm to nested elements
 * so long-form articles render correctly without per-element
 * margin tweaks. Bringhurst-style: every block element has
 * predictable space-after.
 */
.loom-prose {
  font-size: 1rem;
  line-height: 1.65;
  color: var(--loom-color-ink);
  max-inline-size: 65ch;
}
/* Editorial paragraph decorations.
 *
 * Pure visual nudges authored via the typed ParagraphDecoration
 * enum on CmsSection::Paragraph. Always class-additive over
 * loom-prose so the text stays inside the readability box.
 */
.loom-paragraph--lead {
  font-size: clamp(1.1rem, 1.4vw, 1.3rem);
  line-height: 1.55;
  font-weight: 500;
  color: color-mix(in oklab, var(--loom-color-ink) 92%, transparent);
  max-inline-size: 60ch;
}
.loom-paragraph--dropcap::first-letter {
  font-family: var(--loom-font-display);
  font-weight: 700;
  font-size: clamp(2.8em, 5vw, 4em);
  float: inline-start;
  line-height: 0.85;
  padding-inline-end: 0.12em;
  padding-block-start: 0.05em;
  color: var(--loom-color-primary);
  /* Bind the cap to the same baseline as the body so the wrap
   * looks intentional, not orphaned. */
  shape-outside: margin-box;
}
.loom-paragraph--aside {
  font-size: 0.95rem;
  line-height: 1.6;
  color: color-mix(in oklab, var(--loom-color-ink) 72%, transparent);
  border-inline-start: 3px solid color-mix(in oklab, var(--loom-color-primary) 55%, transparent);
  padding-inline-start: 1rem;
  margin-block: 1.5rem;
  max-inline-size: 58ch;
}
.loom-prose > * + * { margin-block-start: 1em; }
.loom-prose > h1,
.loom-prose > h2,
.loom-prose > h3 { margin-block-start: 1.5em; }
.loom-prose > h1 + *,
.loom-prose > h2 + *,
.loom-prose > h3 + * { margin-block-start: 0.5em; }
.loom-prose ul,
.loom-prose ol { padding-inline-start: 1.5em; }
.loom-prose li + li { margin-block-start: 0.25em; }
.loom-prose blockquote {
  border-inline-start: 4px solid var(--loom-color-accent);
  padding-inline-start: var(--loom-space-4);
  font-style: italic;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}

/* Code + inline-code + kbd. Monospace stack.
 */
.loom-code {
  font-family: var(--loom-font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-sm, 0.25rem);
  color: var(--loom-color-ink);
}
.loom-pre {
  font-family: var(--loom-font-mono);
  font-size: 0.875rem;
  line-height: 1.45;
  padding: var(--loom-space-4);
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-md, 0.5rem);
  overflow-x: auto;
  color: var(--loom-color-ink);
}
.loom-pre > code { background: transparent; padding: 0; }
.loom-kbd {
  font-family: var(--loom-font-mono);
  font-size: 0.8em;
  padding: 0.15em 0.45em;
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-block-end-width: 2px;
  border-radius: var(--loom-radius-sm, 0.25rem);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
}

/* Quote + cite. Pull-quote feel.
 */
.loom-quote {
  font-size: 1.25rem;
  line-height: 1.4;
  font-style: italic;
  color: var(--loom-color-ink);
  border-inline-start: 4px solid var(--loom-color-accent);
  padding-inline-start: var(--loom-space-4);
  margin: 0;
}
.loom-cite {
  font-size: 0.875rem;
  font-style: normal;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  margin-block-start: var(--loom-space-2);
  display: block;
}
.loom-cite::before { content: "— "; }

/* Links — default + muted variants.
 */
.loom-link {
  color: var(--loom-color-accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}
.loom-link:hover,
.loom-link:focus-visible {
  text-decoration-thickness: 2px;
}
.loom-link-muted {
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  text-decoration: none;
}
.loom-link-muted:hover,
.loom-link-muted:focus-visible {
  text-decoration: underline;
}

/* Lists — semantic + decorative variants.
 */
.loom-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.loom-list > li { padding-block: var(--loom-space-1); }
.loom-list[data-divider="true"] > li + li {
  border-block-start: 1px solid var(--loom-color-border, var(--loom-color-ink));
}

/* Truncation + ellipsis helpers.
 */
.loom-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.loom-truncate-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.loom-truncate-3 {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ============================================================
 * Surface primitives — vocabulary batch 3 (task #46).
 *
 * Cards / panels / dividers / badges / banners / chips / skeleton.
 * All token-driven (--loom-color-* + --loom-space-* +
 * --loom-radius-*). All shadow elevation through closed-enum
 * data-attrs so depth is auditable.
 *
 * Composes inside the layout primitives from batch 1 + uses the
 * typography classes from batch 2.
 * ============================================================ */

/* Card variants.
 * data-elevation="0|1|2|3" — closed-enum depth.
 * Interactive variant adds hover lift; reduced-motion guards
 * the lift transition.
 */
.loom-card-base {
  padding: var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid transparent;
}
.loom-card-raised {
  padding: var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 4px 12px rgba(0, 0, 0, 0.04);
}
.loom-card-outlined {
  padding: var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-card-floating {
  padding: var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08), 0 12px 32px rgba(0, 0, 0, 0.08);
}
.loom-card-interactive {
  padding: var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  cursor: pointer;
}
@media (prefers-reduced-motion: no-preference) {
  .loom-card-interactive {
    transition: transform 200ms ease, box-shadow 200ms ease;
  }
  .loom-card-interactive:hover,
  .loom-card-interactive:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
  }
}

/* Panel — flat surface (no shadow, no border by default).
 * Used for in-page section grouping.
 */
.loom-panel {
  padding: var(--loom-space-6);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-lg, 0.75rem);
}
.loom-panel-subtle {
  padding: var(--loom-space-6);
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-lg, 0.75rem);
}

/* Dividers — horizontal default, vertical variant.
 */
.loom-divider {
  border: 0;
  block-size: 1px;
  background: var(--loom-color-border, var(--loom-color-ink));
  opacity: 0.4;
  margin-block: var(--loom-space-4);
}
.loom-divider-vertical {
  border: 0;
  inline-size: 1px;
  align-self: stretch;
  background: var(--loom-color-border, var(--loom-color-ink));
  opacity: 0.4;
  margin-inline: var(--loom-space-4);
}

/* Badges — closed-enum tone.
 */
.loom-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-1);
  padding: 0.15em 0.5em;
  border-radius: var(--loom-radius-pill, 999px);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.4;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
}
.loom-badge[data-tone="info"] {
  background: var(--loom-color-info-subtle, var(--loom-color-surface));
  color: var(--loom-color-info, var(--loom-color-ink));
}
.loom-badge[data-tone="success"] {
  background: var(--loom-color-success-subtle, var(--loom-color-surface));
  color: var(--loom-color-success, var(--loom-color-ink));
}
.loom-badge[data-tone="warning"] {
  background: var(--loom-color-warning-subtle, var(--loom-color-surface));
  color: var(--loom-color-warning, var(--loom-color-ink));
}
.loom-badge[data-tone="danger"] {
  background: var(--loom-color-danger-subtle, var(--loom-color-surface));
  color: var(--loom-color-danger, var(--loom-color-ink));
}

/* Tag — like a badge but for content classification (categories,
 * keywords). Optional removable variant carries a trailing × button.
 */
.loom-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-1);
  padding: 0.25em 0.75em;
  border-radius: var(--loom-radius-sm, 0.25rem);
  font-size: 0.875rem;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
}
.loom-tag-removable { padding-inline-end: 0.25em; }
.loom-tag-removable > button {
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0.15em 0.4em;
  color: inherit;
  opacity: 0.6;
}
.loom-tag-removable > button:hover { opacity: 1; }

/* Chips + pills — interactive selection.
 */
.loom-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: 0.4em 0.85em;
  border-radius: var(--loom-radius-pill, 999px);
  font-size: 0.875rem;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
  border: 1px solid transparent;
  cursor: pointer;
}
.loom-chip[aria-pressed="true"] {
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, white);
}
.loom-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.25em 0.75em;
  border-radius: var(--loom-radius-pill, 999px);
  font-size: 0.75rem;
  font-weight: 500;
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  background: transparent;
  color: var(--loom-color-ink);
}

/* Banners — full-width attention surfaces. Closed-enum tone.
 */
.loom-banner {
  padding: var(--loom-space-3) var(--loom-space-4);
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
  border-inline-start: 4px solid var(--loom-color-accent);
}
.loom-banner[data-tone="info"] {
  background: var(--loom-color-info-subtle, var(--loom-color-surface));
  border-inline-start-color: var(--loom-color-info, var(--loom-color-accent));
}
.loom-banner[data-tone="success"] {
  background: var(--loom-color-success-subtle, var(--loom-color-surface));
  border-inline-start-color: var(--loom-color-success, var(--loom-color-accent));
}
.loom-banner[data-tone="warning"] {
  background: var(--loom-color-warning-subtle, var(--loom-color-surface));
  border-inline-start-color: var(--loom-color-warning, var(--loom-color-accent));
}
.loom-banner[data-tone="danger"] {
  background: var(--loom-color-danger-subtle, var(--loom-color-surface));
  border-inline-start-color: var(--loom-color-danger, var(--loom-color-accent));
}

/* Empty-state — "nothing here yet" placeholder.
 */
.loom-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--loom-space-3);
  padding: var(--loom-space-8) var(--loom-space-4);
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}

/* Skeleton — loading placeholder. Pulse guarded by
 * prefers-reduced-motion.
 */
.loom-skeleton {
  display: block;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-sm, 0.25rem);
  block-size: 1em;
  inline-size: 100%;
}
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-skeleton-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.5; }
  }
  .loom-skeleton {
    animation: loom-skeleton-pulse 1.6s ease-in-out infinite;
  }
}
.loom-skeleton[data-shape="circle"] {
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: 50%;
}
.loom-skeleton[data-shape="title"] {
  block-size: 1.5em;
  inline-size: 60%;
}
.loom-skeleton[data-shape="paragraph"] {
  block-size: 1em;
  inline-size: 100%;
  margin-block-end: var(--loom-space-2);
}

/* Spinner — indeterminate progress indicator. Reduced-motion
 * variant is a static "..." (CSS content).
 */
@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-spinner-rotate {
    to { transform: rotate(360deg); }
  }
  .loom-spinner {
    display: inline-block;
    inline-size: 1.25em;
    block-size: 1.25em;
    border: 2px solid var(--loom-color-border, currentColor);
    border-block-start-color: transparent;
    border-radius: 50%;
    animation: loom-spinner-rotate 0.9s linear infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .loom-spinner::after {
    content: "…";
  }
}

/* Avatar — square or round, sized via data-size.
 */
.loom-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: 50%;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
  font-weight: 600;
  overflow: hidden;
}
.loom-avatar > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
.loom-avatar[data-size="sm"] { inline-size: 1.5rem; block-size: 1.5rem; font-size: 0.75rem; }
.loom-avatar[data-size="md"] { inline-size: 2.5rem; block-size: 2.5rem; }
.loom-avatar[data-size="lg"] { inline-size: 4rem; block-size: 4rem; font-size: 1.5rem; }
.loom-avatar[data-shape="square"] { border-radius: var(--loom-radius-md, 0.5rem); }

/* ============================================================
 * Form primitives — vocabulary batch 4 (task #46).
 *
 * Inputs / textareas / selects / checkboxes / radios / switches /
 * fieldsets / labels / helper text / buttons / button groups /
 * file drops. All accessibility-first: labels are not optional,
 * error states are typed via data-state="error", focus rings are
 * visible at every focus-visible.
 *
 * No reliance on user-agent native styling beyond what's
 * accessible — every native control gets explicit focus + hover
 * + disabled + error visuals tied to --loom-color-* tokens.
 *
 * Composes inside .loom-stack / .loom-cluster / .loom-sidebar
 * layout primitives from batch 1.
 * ============================================================ */

/* Form container + layout helpers.
 */
.loom-form {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-4);
}
.loom-form-row {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-1);
}
.loom-form-actions {
  display: flex;
  gap: var(--loom-space-3);
  justify-content: flex-end;
  margin-block-start: var(--loom-space-4);
}
.loom-fieldset {
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  padding: var(--loom-space-4);
  margin: 0;
}
.loom-legend {
  font-weight: 600;
  font-size: 0.875rem;
  padding-inline: var(--loom-space-2);
}

/* Labels — required variant adds a visual indicator on the
 * marker character so screen readers still get the input's
 * aria-required attribute as the primary signal.
 */
.loom-label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--loom-color-ink);
  margin-block-end: var(--loom-space-1);
}
.loom-label-required::after {
  content: " *";
  color: var(--loom-color-danger, var(--loom-color-ink));
}

/* Helper + error text.
 */
.loom-helper-text {
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  margin-block-start: var(--loom-space-1);
}
.loom-error-text {
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--loom-color-danger, var(--loom-color-ink));
  margin-block-start: var(--loom-space-1);
}

/* Text inputs — base + states.
 * data-state="error" enforces the error visuals so consumers
 * don't have to maintain two separate classnames.
 */
.loom-input {
  display: block;
  inline-size: 100%;
  padding: 0.55em 0.85em;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--loom-color-ink);
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  box-sizing: border-box;
}
.loom-input:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
  border-color: var(--loom-color-accent);
}
.loom-input:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.loom-input[data-state="error"],
.loom-input[aria-invalid="true"] {
  border-color: var(--loom-color-danger, var(--loom-color-ink));
}
.loom-input[data-state="error"]:focus-visible,
.loom-input[aria-invalid="true"]:focus-visible {
  outline-color: var(--loom-color-danger, var(--loom-color-accent));
}

/* Textarea — inherits .loom-input look but allows resize.
 */
.loom-textarea {
  display: block;
  inline-size: 100%;
  min-block-size: 6em;
  padding: 0.55em 0.85em;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.45;
  color: var(--loom-color-ink);
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  resize: vertical;
  box-sizing: border-box;
}
.loom-textarea:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
  border-color: var(--loom-color-accent);
}

/* Select — kept simple; native control kept for accessibility,
 * caret painted via background-image SVG (data-URI inline).
 */
.loom-select {
  display: block;
  inline-size: 100%;
  padding: 0.55em 2em 0.55em 0.85em;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--loom-color-ink);
  background-color: var(--loom-color-surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='currentColor' stroke-width='1.5' d='M1 1.5l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.85em center;
  background-size: 0.75em;
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  appearance: none;
  box-sizing: border-box;
}
.loom-select:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}

/* Input-group — prefix/suffix slot wrapper.
 */
.loom-input-group {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface);
  overflow: hidden;
}
.loom-input-group:focus-within {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}
.loom-input-group > .loom-input {
  border: 0;
  border-radius: 0;
  flex: 1 1 auto;
}
.loom-input-group > .loom-input:focus-visible {
  outline: none;
}
.loom-input-group > [data-slot="prefix"],
.loom-input-group > [data-slot="suffix"] {
  display: flex;
  align-items: center;
  padding-inline: var(--loom-space-3);
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}

/* Checkboxes + radios — accessible-by-default custom look.
 * Native control hidden visually but kept in the layout for
 * screen-reader access; ::before draws the box.
 */
.loom-checkbox,
.loom-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
  cursor: pointer;
  user-select: none;
}
.loom-checkbox > input,
.loom-radio > input {
  position: absolute;
  inline-size: 1.125rem;
  block-size: 1.125rem;
  opacity: 0;
  pointer-events: none;
}
.loom-checkbox::before,
.loom-radio::before {
  content: "";
  display: inline-block;
  inline-size: 1.125rem;
  block-size: 1.125rem;
  border: 1.5px solid var(--loom-color-border, var(--loom-color-ink));
  background: var(--loom-color-surface);
  flex-shrink: 0;
}
.loom-checkbox::before { border-radius: 0.25rem; }
.loom-radio::before    { border-radius: 50%; }
.loom-checkbox:has(input:checked)::before {
  background: var(--loom-color-accent);
  border-color: var(--loom-color-accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M3 7l3 3 5-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 75%;
}
.loom-radio:has(input:checked)::before {
  background: radial-gradient(
    var(--loom-color-accent) 35%,
    var(--loom-color-surface) 40%
  );
  border-color: var(--loom-color-accent);
}
.loom-checkbox:has(input:focus-visible)::before,
.loom-radio:has(input:focus-visible)::before {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}

/* Switch — toggle (boolean control). Visually distinct from
 * checkbox because the semantics differ (immediate effect vs
 * staged).
 */
.loom-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
  cursor: pointer;
  user-select: none;
}
.loom-switch > input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.loom-switch::before {
  content: "";
  display: inline-block;
  inline-size: 2.25rem;
  block-size: 1.25rem;
  border-radius: 999px;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  position: relative;
  flex-shrink: 0;
}
.loom-switch::after {
  content: "";
  position: absolute;
  inline-size: 0.875rem;
  block-size: 0.875rem;
  border-radius: 50%;
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  inset-block-start: 50%;
  inset-inline-start: 0.2rem;
  transform: translateY(-50%);
}
@media (prefers-reduced-motion: no-preference) {
  .loom-switch::after {
    transition: inset-inline-start 180ms ease;
  }
}
.loom-switch:has(input:checked)::before {
  background: var(--loom-color-accent);
  border-color: var(--loom-color-accent);
}
.loom-switch:has(input:checked)::after {
  inset-inline-start: 1.125rem;
  background: var(--loom-color-on-accent, white);
  border-color: transparent;
}

/* Buttons — closed-enum variant + size.
 */
.loom-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--loom-space-2);
  padding: 0.55em 1.1em;
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.2;
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid transparent;
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, white);
  cursor: pointer;
  text-decoration: none;
}
.loom-button:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}
.loom-button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
@media (prefers-reduced-motion: no-preference) {
  .loom-button {
    transition: filter 160ms ease, transform 160ms ease,
                background-color 220ms ease, color 220ms ease,
                border-color 220ms ease;
  }
  .loom-button:hover:not(:disabled) { filter: brightness(1.08); }
  .loom-button:active:not(:disabled) { transform: translateY(1px); }
}

/* Tenant-overridable hover color swap. Default (no override) leaves
   the brightness shift above as the only hover feedback. When a
   tenant sets one of these CSS variables via tenant_style, the
   button :hover state swaps to that color — supports any palette,
   not tied to any specific tenant's brand. */
.loom-button:hover:not(:disabled) {
  background: var(--loom-color-button-bg-hover, inherit);
  color: var(--loom-color-button-fg-hover, inherit);
}
.loom-button[data-variant="primary"]:hover:not(:disabled) {
  background: var(--loom-color-button-primary-hover,
                  var(--loom-color-button-bg-hover,
                  var(--loom-color-accent)));
  color: var(--loom-color-button-primary-fg-hover,
              var(--loom-color-on-accent, #ffffff));
}
.loom-button[data-variant="secondary"]:hover:not(:disabled) {
  background: var(--loom-color-button-secondary-hover,
                  var(--loom-color-button-bg-hover,
                  var(--loom-color-surface-subtle,
                  var(--loom-color-surface))));
  color: var(--loom-color-button-secondary-fg-hover,
              var(--loom-color-ink));
}
.loom-button[data-variant="ghost"]:hover:not(:disabled) {
  background: var(--loom-color-button-ghost-hover,
                  color-mix(in oklab, var(--loom-color-primary) 10%, transparent));
  color: var(--loom-color-button-ghost-fg-hover,
              var(--loom-color-primary));
}
.loom-button[data-variant="primary"] {
  background: var(--loom-color-primary, var(--loom-color-accent));
  color: var(--loom-color-on-primary, var(--loom-color-on-accent, #fff));
  border-color: transparent;
}
.loom-button[data-variant="secondary"] {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-ink);
  border-color: var(--loom-color-border, var(--loom-color-ink));
}
.loom-button[data-variant="tertiary"] {
  background: transparent;
  color: var(--loom-color-accent);
  border-color: transparent;
}
.loom-button[data-variant="ghost"] {
  background: transparent;
  color: var(--loom-color-ink);
  border-color: transparent;
}
.loom-button[data-variant="destructive"] {
  background: var(--loom-color-danger, var(--loom-color-ink));
  color: var(--loom-color-on-accent, white);
}
.loom-button[data-size="sm"] { padding: 0.35em 0.85em; font-size: 0.875rem; }
.loom-button[data-size="lg"] { padding: 0.7em 1.5em; font-size: 1.125rem; }
.loom-button[data-block="true"] { inline-size: 100%; }

/* Button-group — segmented control.
 */
.loom-button-group {
  display: inline-flex;
}
.loom-button-group > .loom-button {
  border-radius: 0;
  border-color: var(--loom-color-border, var(--loom-color-ink));
}
.loom-button-group > .loom-button:first-child {
  border-start-start-radius: var(--loom-radius-md, 0.5rem);
  border-end-start-radius: var(--loom-radius-md, 0.5rem);
}
.loom-button-group > .loom-button:last-child {
  border-start-end-radius: var(--loom-radius-md, 0.5rem);
  border-end-end-radius: var(--loom-radius-md, 0.5rem);
}
.loom-button-group > .loom-button + .loom-button {
  margin-inline-start: -1px;
}

/* File drop zone.
 */
.loom-file-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-6);
  border: 2px dashed var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  text-align: center;
  cursor: pointer;
}
.loom-file-drop[data-state="hover"],
.loom-file-drop:hover {
  border-color: var(--loom-color-accent);
  color: var(--loom-color-ink);
}

/* ============================================================
 * Navigation primitives — vocabulary batch 5 (task #46).
 *
 * Tabs / breadcrumbs / pagination / stepper / sidebar nav /
 * toolbar / app bar / bottom nav / menu / link-list. Built on
 * top of the surface primitives from batch 3 — chips, pills,
 * dividers — and the form primitives from batch 4 (buttons).
 *
 * Every nav primitive uses semantic markup signals (aria-current,
 * aria-selected, aria-expanded) for state. Visual state mirrors
 * the semantic state so consumers don't have to maintain two
 * separate signals.
 * ============================================================ */

/* Tabs — horizontal list + active indicator.
 * Use with <ul role="tablist"> + <li role="tab" aria-selected="…">
 * + <div role="tabpanel">.
 */
.loom-tabs {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-3);
}
.loom-tab-list {
  display: flex;
  gap: var(--loom-space-1);
  list-style: none;
  margin: 0;
  padding: 0;
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
  overflow-x: auto;
}
.loom-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-2) var(--loom-space-4);
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  background: transparent;
  border: 0;
  border-block-end: 2px solid transparent;
  cursor: pointer;
  margin-block-end: -1px;
}
.loom-tab:hover { color: var(--loom-color-ink); }
.loom-tab[aria-selected="true"],
.loom-tab[aria-current="page"] {
  color: var(--loom-color-ink);
  border-block-end-color: var(--loom-color-accent);
}
.loom-tab:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}
.loom-tab-panel {
  padding-block-start: var(--loom-space-3);
}

/* Breadcrumb — trail of links with separator.
 */
.loom-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--loom-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.875rem;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-breadcrumb-item {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
}
.loom-breadcrumb-item > a {
  color: inherit;
  text-decoration: none;
}
.loom-breadcrumb-item > a:hover { text-decoration: underline; }
.loom-breadcrumb-item[aria-current="page"] {
  color: var(--loom-color-ink);
  font-weight: 500;
}
.loom-breadcrumb-sep {
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  user-select: none;
}
.loom-breadcrumb-sep::before { content: "/"; }

/* Pagination — page-number list with prev/next.
 */
.loom-pagination {
  display: flex;
  align-items: center;
  gap: var(--loom-space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.loom-pagination-item {
  display: inline-flex;
}
.loom-pagination-item > a,
.loom-pagination-item > button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 2.25rem;
  block-size: 2.25rem;
  padding-inline: var(--loom-space-2);
  font-size: 0.875rem;
  color: var(--loom-color-ink);
  background: transparent;
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-sm, 0.25rem);
  text-decoration: none;
  cursor: pointer;
}
.loom-pagination-item[aria-current="page"] > a,
.loom-pagination-item[aria-current="page"] > button {
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, white);
  border-color: var(--loom-color-accent);
}
.loom-pagination-item > a:focus-visible,
.loom-pagination-item > button:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}
.loom-pagination-item[aria-hidden="true"] {
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  pointer-events: none;
}

/* Stepper — multi-step process indicator.
 * Step states via data-state="complete|active|pending".
 */
.loom-stepper {
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.loom-step {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
}
.loom-step + .loom-step::before {
  content: "";
  display: inline-block;
  inline-size: 2rem;
  block-size: 1px;
  background: var(--loom-color-border, var(--loom-color-ink));
}
.loom-step > .loom-step-marker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  border-radius: 50%;
  border: 1.5px solid var(--loom-color-border, var(--loom-color-ink));
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  background: var(--loom-color-surface);
}
.loom-step[data-state="complete"] > .loom-step-marker {
  background: var(--loom-color-success, var(--loom-color-accent));
  border-color: var(--loom-color-success, var(--loom-color-accent));
  color: var(--loom-color-on-accent, white);
}
.loom-step[data-state="active"] > .loom-step-marker {
  background: var(--loom-color-accent);
  border-color: var(--loom-color-accent);
  color: var(--loom-color-on-accent, white);
}
.loom-step > .loom-step-label {
  font-size: 0.875rem;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-step[data-state="active"] > .loom-step-label {
  color: var(--loom-color-ink);
  font-weight: 500;
}

/* Sidebar navigation — vertical link list, common in app shells.
 */
.loom-sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.loom-sidebar-nav-item {
  display: flex;
}
.loom-sidebar-nav-item > a {
  display: flex;
  align-items: center;
  gap: var(--loom-space-3);
  inline-size: 100%;
  padding: var(--loom-space-2) var(--loom-space-3);
  font-size: 0.9375rem;
  color: var(--loom-color-ink);
  text-decoration: none;
  border-radius: var(--loom-radius-md, 0.5rem);
}
.loom-sidebar-nav-item > a:hover {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}
.loom-sidebar-nav-item[aria-current="page"] > a,
.loom-sidebar-nav-item > a[aria-current="page"] {
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, white);
}
.loom-sidebar-nav-item > a:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}

/* Toolbar — horizontal action strip.
 */
.loom-toolbar {
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-2);
  background: var(--loom-color-surface);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
}

/* App bar — page-top navigation chrome.
 */
.loom-app-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--loom-space-4);
  padding: var(--loom-space-3) var(--loom-space-4);
  background: var(--loom-color-surface);
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
}

/* Bottom nav — mobile-shell navigation.
 */
.loom-bottom-nav {
  display: flex;
  justify-content: space-around;
  align-items: stretch;
  gap: var(--loom-space-2);
  padding: var(--loom-space-2);
  background: var(--loom-color-surface);
  border-block-start: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-bottom-nav-item {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--loom-space-1);
  font-size: 0.75rem;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  text-decoration: none;
  padding: var(--loom-space-2);
}
.loom-bottom-nav-item[aria-current="page"] {
  color: var(--loom-color-accent);
}

/* Menu — dropdown / popover list.
 */
.loom-menu {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: var(--loom-space-1);
  min-inline-size: 12rem;
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.loom-menu-item {
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-2) var(--loom-space-3);
  font-size: 0.875rem;
  color: var(--loom-color-ink);
  text-decoration: none;
  border-radius: var(--loom-radius-sm, 0.25rem);
  cursor: pointer;
}
.loom-menu-item:hover {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}
.loom-menu-item:focus-visible {
  outline: 2px solid var(--loom-color-accent);
  outline-offset: 2px;
}
.loom-menu-item[data-destructive="true"] {
  color: var(--loom-color-danger, var(--loom-color-ink));
}
.loom-menu-divider {
  block-size: 1px;
  background: var(--loom-color-border, var(--loom-color-ink));
  opacity: 0.4;
  margin-block: var(--loom-space-1);
}

/* Link list — generic vertical list of links (sitemap, related
 * articles, footer column).
 */
.loom-link-list {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.loom-link-list > li > a {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-2);
  color: var(--loom-color-ink);
  text-decoration: none;
}
.loom-link-list > li > a:hover {
  text-decoration: underline;
}

/* ============================================================
 * Data primitives — vocabulary batch 6 (task #46).
 *
 * Tables / definition lists / metrics / KPI cards / progress
 * bars + rings / meters. Data-density-aware: data-density="compact"
 * / "comfortable" / "spacious" on tables for the three reading
 * registers operators need.
 *
 * Composes inside .loom-card-* + .loom-panel surfaces from
 * batch 3.
 * ============================================================ */

/* Tables — semantic HTML <table> with explicit class on
 * the table element. Variants via data-attrs.
 */
.loom-table {
  inline-size: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
  color: var(--loom-color-ink);
  background: var(--loom-color-surface);
}
.loom-table th,
.loom-table td {
  padding: var(--loom-space-3) var(--loom-space-4);
  text-align: start;
  vertical-align: middle;
}
.loom-table thead th {
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-table tbody tr + tr td {
  border-block-start: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-table[data-density="compact"] th,
.loom-table[data-density="compact"] td {
  padding: var(--loom-space-2) var(--loom-space-3);
  font-size: 0.875rem;
}
.loom-table[data-density="spacious"] th,
.loom-table[data-density="spacious"] td {
  padding: var(--loom-space-4) var(--loom-space-5, var(--loom-space-4));
}
.loom-table[data-striped="true"] tbody tr:nth-child(odd) td {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}
.loom-table[data-bordered="true"] {
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  overflow: hidden;
}
.loom-table[data-bordered="true"] th,
.loom-table[data-bordered="true"] td {
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-table[data-hover="true"] tbody tr:hover td {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}

/* Table action row + utility states.
 */
.loom-table-empty,
.loom-table-loading {
  padding: var(--loom-space-6) var(--loom-space-4);
  text-align: center;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}

/* Key/value pair — single labelled datum.
 */
.loom-key-value {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-1);
}
.loom-key-value > [data-slot="key"] {
  font-size: 0.75rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-key-value > [data-slot="value"] {
  font-size: 1rem;
  color: var(--loom-color-ink);
}

/* Definition list — semantic <dl> wrapper.
 */
.loom-dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--loom-space-2) var(--loom-space-4);
  margin: 0;
}
.loom-dt {
  font-weight: 500;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-dd {
  margin: 0;
  color: var(--loom-color-ink);
}

/* Metric — single KPI display. Value + label + optional trend.
 */
.loom-metric {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-1);
}
.loom-metric-value {
  font-size: 2.25rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--loom-color-ink);
  font-feature-settings: "tnum" 1; /* tabular nums for stable digit widths */
}
.loom-metric-label {
  font-size: 0.875rem;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-metric-trend {
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-1);
  font-size: 0.8125rem;
  font-weight: 500;
}
.loom-metric-trend[data-direction="up"]   { color: var(--loom-color-success, var(--loom-color-ink)); }
.loom-metric-trend[data-direction="down"] { color: var(--loom-color-danger, var(--loom-color-ink)); }
.loom-metric-trend[data-direction="flat"] { color: var(--loom-color-ink-muted, var(--loom-color-ink)); }

/* Stat card — metric wrapped in a card surface.
 */
.loom-stat-card {
  padding: var(--loom-space-4) var(--loom-space-5, var(--loom-space-4));
  background: var(--loom-color-surface);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-2);
}

/* Progress bar — linear indeterminate or determinate.
 */
.loom-progress-bar {
  display: block;
  inline-size: 100%;
  block-size: 0.5rem;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-pill, 999px);
  overflow: hidden;
}
.loom-progress-bar-fill {
  display: block;
  block-size: 100%;
  background: var(--loom-color-accent);
  border-radius: inherit;
  /* Width is set inline via style="--fill: X%" */
  inline-size: var(--loom-progress-fill, 0%);
}
.loom-progress-bar[data-tone="success"] .loom-progress-bar-fill {
  background: var(--loom-color-success, var(--loom-color-accent));
}
.loom-progress-bar[data-tone="warning"] .loom-progress-bar-fill {
  background: var(--loom-color-warning, var(--loom-color-accent));
}
.loom-progress-bar[data-tone="danger"] .loom-progress-bar-fill {
  background: var(--loom-color-danger, var(--loom-color-accent));
}
.loom-progress-bar[data-size="lg"] { block-size: 0.75rem; }
.loom-progress-bar[data-size="sm"] { block-size: 0.25rem; }

/* Progress ring — circular SVG progress. Stroke-dashoffset
 * controlled by --loom-progress-fill (0..1).
 *
 * Consumer markup:
 *   <svg class="loom-progress-ring" viewBox="0 0 36 36" style="--loom-progress-fill: 0.75">
 *     <circle class="loom-progress-ring-track" cx="18" cy="18" r="15.9"/>
 *     <circle class="loom-progress-ring-bar"   cx="18" cy="18" r="15.9"/>
 *   </svg>
 */
.loom-progress-ring {
  display: inline-block;
  inline-size: 3rem;
  block-size: 3rem;
}
.loom-progress-ring-track {
  fill: none;
  stroke: var(--loom-color-surface-subtle, var(--loom-color-surface));
  stroke-width: 3.5;
}
.loom-progress-ring-bar {
  fill: none;
  stroke: var(--loom-color-accent);
  stroke-width: 3.5;
  stroke-linecap: round;
  /* circumference ≈ 99.9 (r=15.9, 2πr); dasharray = circumference,
   * dashoffset = circumference * (1 - fill). */
  stroke-dasharray: 99.9;
  stroke-dashoffset: calc(99.9 * (1 - var(--loom-progress-fill, 0)));
  transform: rotate(-90deg);
  transform-origin: center;
}
@media (prefers-reduced-motion: no-preference) {
  .loom-progress-ring-bar {
    transition: stroke-dashoffset 300ms ease;
  }
}

/* Meter — semantic <meter> styling. Closed-enum data-tone.
 */
.loom-meter {
  inline-size: 100%;
  block-size: 0.625rem;
  -webkit-appearance: none;
  appearance: none;
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-pill, 999px);
  border: 0;
}
.loom-meter::-webkit-meter-bar {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  border-radius: var(--loom-radius-pill, 999px);
  border: 0;
}
.loom-meter::-webkit-meter-optimum-value {
  background: var(--loom-color-success, var(--loom-color-accent));
  border-radius: var(--loom-radius-pill, 999px);
}
.loom-meter::-webkit-meter-suboptimum-value {
  background: var(--loom-color-warning, var(--loom-color-accent));
  border-radius: var(--loom-radius-pill, 999px);
}
.loom-meter::-webkit-meter-even-less-good-value {
  background: var(--loom-color-danger, var(--loom-color-accent));
  border-radius: var(--loom-radius-pill, 999px);
}
.loom-meter::-moz-meter-bar {
  background: var(--loom-color-success, var(--loom-color-accent));
  border-radius: var(--loom-radius-pill, 999px);
}

/* Sparkline placeholder — host element for inline <svg> sparkline.
 */
.loom-sparkline {
  display: inline-block;
  block-size: 2rem;
  inline-size: 6rem;
  color: var(--loom-color-accent);
}
.loom-sparkline > svg {
  inline-size: 100%;
  block-size: 100%;
  display: block;
}

/* ============================================================
 * Overlay primitives — vocabulary batch 7 (task #46).
 *
 * Modal / drawer / toast / popover / tooltip / bottom-sheet /
 * command-palette. All assume the native HTML <dialog> element
 * for modal + drawer where possible — its built-in focus-trap +
 * inert behavior is the correct accessibility baseline. CSS
 * here paints the visual; consumers manage open/close via
 * native `<dialog>.showModal()`.
 *
 * Motion: every appear / dismiss transition guarded by
 * @media (prefers-reduced-motion: no-preference). Reduced-motion
 * readers see instant render, instant dismiss.
 * ============================================================ */

/* Modal — built on native <dialog>.
 * Pair markup: <dialog class="loom-modal">…</dialog>
 * Backdrop styled via ::backdrop (universally supported now).
 */
.loom-modal {
  padding: 0;
  border: 0;
  border-radius: var(--loom-radius-lg, 0.75rem);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
  max-inline-size: min(32rem, 92vw);
  inline-size: 100%;
  margin: auto;
}
.loom-modal::backdrop {
  background: rgba(0, 0, 0, 0.4);
}
.loom-modal[data-size="sm"] { max-inline-size: 24rem; }
.loom-modal[data-size="lg"] { max-inline-size: 48rem; }
.loom-modal[data-size="full"] {
  max-inline-size: 96vw;
  max-block-size: 92vh;
}
.loom-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--loom-space-3);
  padding: var(--loom-space-4) var(--loom-space-5, var(--loom-space-4));
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
}
.loom-modal-body {
  padding: var(--loom-space-5, var(--loom-space-4));
  overflow-y: auto;
  max-block-size: 60vh;
}
.loom-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--loom-space-3);
  padding: var(--loom-space-4) var(--loom-space-5, var(--loom-space-4));
  border-block-start: 1px solid var(--loom-color-border, var(--loom-color-ink));
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-modal-appear {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0)   scale(1); }
  }
  .loom-modal[open] {
    animation: loom-modal-appear 180ms cubic-bezier(0.34, 1.1, 0.64, 1) both;
  }
}

/* Drawer — slide-in from an edge.
 * Pair with <dialog class="loom-drawer" data-side="right">; the
 * native dialog still provides the focus trap.
 */
.loom-drawer {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  box-shadow: -8px 0 32px rgba(0, 0, 0, 0.12);
  block-size: 100vh;
  inline-size: min(28rem, 92vw);
  margin: 0;
  inset-block-start: 0;
  inset-inline-end: 0;
  inset-inline-start: auto;
}
.loom-drawer::backdrop { background: rgba(0, 0, 0, 0.4); }
.loom-drawer[data-side="left"] {
  inset-inline-start: 0;
  inset-inline-end: auto;
  box-shadow: 8px 0 32px rgba(0, 0, 0, 0.12);
}
.loom-drawer[data-side="bottom"] {
  inset-inline-start: 0;
  inset-inline-end: 0;
  inset-block-end: 0;
  inset-block-start: auto;
  inline-size: 100vw;
  block-size: auto;
  max-block-size: 80vh;
  border-start-start-radius: var(--loom-radius-lg, 0.75rem);
  border-start-end-radius: var(--loom-radius-lg, 0.75rem);
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.12);
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-drawer-slide-right {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
  }
  @keyframes loom-drawer-slide-left {
    from { transform: translateX(-100%); }
    to   { transform: translateX(0); }
  }
  @keyframes loom-drawer-slide-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  .loom-drawer[open]:not([data-side]) ,
  .loom-drawer[open][data-side="right"] {
    animation: loom-drawer-slide-right 240ms cubic-bezier(0.34, 1.1, 0.64, 1) both;
  }
  .loom-drawer[open][data-side="left"] {
    animation: loom-drawer-slide-left 240ms cubic-bezier(0.34, 1.1, 0.64, 1) both;
  }
  .loom-drawer[open][data-side="bottom"] {
    animation: loom-drawer-slide-up 240ms cubic-bezier(0.34, 1.1, 0.64, 1) both;
  }
}

/* Toast stack + individual toast. Toasts live in a fixed corner
 * region; consumers append + dismiss via JS but the CSS is
 * positionally self-contained.
 */
.loom-toast-stack {
  position: fixed;
  inset-block-end: var(--loom-space-4);
  inset-inline-end: var(--loom-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-2);
  z-index: 100;
  max-inline-size: 24rem;
  pointer-events: none; /* let children opt in */
}
.loom-toast-stack[data-anchor="top-right"] {
  inset-block-end: auto;
  inset-block-start: var(--loom-space-4);
}
.loom-toast-stack[data-anchor="top-left"] {
  inset-block-end: auto;
  inset-block-start: var(--loom-space-4);
  inset-inline-end: auto;
  inset-inline-start: var(--loom-space-4);
}
.loom-toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-3) var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
  font-size: 0.875rem;
}
.loom-toast[data-tone="success"] {
  border-inline-start: 4px solid var(--loom-color-success, var(--loom-color-accent));
}
.loom-toast[data-tone="warning"] {
  border-inline-start: 4px solid var(--loom-color-warning, var(--loom-color-accent));
}
.loom-toast[data-tone="danger"] {
  border-inline-start: 4px solid var(--loom-color-danger, var(--loom-color-accent));
}
.loom-toast[data-tone="info"] {
  border-inline-start: 4px solid var(--loom-color-info, var(--loom-color-accent));
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes loom-toast-slide-in {
    from { opacity: 0; transform: translateX(20px); }
    to   { opacity: 1; transform: translateX(0); }
  }
  .loom-toast { animation: loom-toast-slide-in 200ms ease-out both; }
}

/* Snackbar — single-line toast variant; bottom-center.
 */
.loom-snackbar {
  position: fixed;
  inset-block-end: var(--loom-space-4);
  inset-inline-start: 50%;
  transform: translateX(-50%);
  padding: var(--loom-space-3) var(--loom-space-5, var(--loom-space-4));
  background: var(--loom-color-ink);
  color: var(--loom-color-surface);
  border-radius: var(--loom-radius-pill, 999px);
  font-size: 0.875rem;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  gap: var(--loom-space-3);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.2);
}
.loom-snackbar > button {
  background: transparent;
  border: 0;
  color: var(--loom-color-accent);
  cursor: pointer;
  font-weight: 500;
}

/* Popover — anchored floating card.
 * Consumers position with Popover API (popover="" + popovertarget)
 * OR via JS. CSS just paints.
 */
.loom-popover {
  padding: var(--loom-space-3) var(--loom-space-4);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-radius: var(--loom-radius-md, 0.5rem);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  max-inline-size: 20rem;
  font-size: 0.875rem;
}
.loom-popover[popover] {
  margin: auto;
  inset: unset;
  position: fixed;
}

/* Tooltip — minimal popover. Use with title="..." or aria-describedby.
 */
.loom-tooltip {
  padding: 0.35em 0.7em;
  background: var(--loom-color-ink);
  color: var(--loom-color-surface);
  border-radius: var(--loom-radius-sm, 0.25rem);
  font-size: 0.75rem;
  line-height: 1.3;
  max-inline-size: 16rem;
  pointer-events: none;
}

/* Bottom-sheet — mobile-shell drawer that pulls up from the
 * bottom. Alias for .loom-drawer[data-side="bottom"] but with
 * its own classname so consumers can target the pattern
 * explicitly.
 */
.loom-bottom-sheet {
  position: fixed;
  inset-block-end: 0;
  inset-inline-start: 0;
  inset-inline-end: 0;
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  border-start-start-radius: var(--loom-radius-lg, 0.75rem);
  border-start-end-radius: var(--loom-radius-lg, 0.75rem);
  padding: var(--loom-space-5, var(--loom-space-4));
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.12);
  max-block-size: 80vh;
  overflow-y: auto;
  z-index: 90;
}

/* Command palette — ⌘K style modal with search + result list.
 * The structural shell; consumers fill in input + list.
 */
.loom-command-palette {
  padding: 0;
  border: 0;
  border-radius: var(--loom-radius-lg, 0.75rem);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  inline-size: min(36rem, 92vw);
  max-block-size: 70vh;
  margin: 10vh auto;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
}
.loom-command-palette::backdrop { background: rgba(0, 0, 0, 0.4); }
.loom-command-palette-input {
  inline-size: 100%;
  padding: var(--loom-space-3) var(--loom-space-4);
  font-size: 1rem;
  border: 0;
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
  background: transparent;
  color: var(--loom-color-ink);
  outline: none;
}
.loom-command-palette-list {
  list-style: none;
  margin: 0;
  padding: var(--loom-space-2);
  overflow-y: auto;
  flex: 1 1 auto;
}
.loom-command-palette-item {
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
  padding: var(--loom-space-2) var(--loom-space-3);
  border-radius: var(--loom-radius-sm, 0.25rem);
  cursor: pointer;
}
.loom-command-palette-item[aria-selected="true"],
.loom-command-palette-item:hover {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
}

/* ============================================================
 * Marketing primitives — vocabulary batch 8 (task #46, final).
 *
 * Hero / feature grid / pricing / testimonial / logo cloud /
 * CTA / FAQ / stats / team / comparison. The landing-page
 * layer — what most public-facing marketing sites need.
 *
 * Composes inside .loom-cover / .loom-center / .loom-grid /
 * .loom-card-* from earlier batches.
 *
 * After this batch the vocabulary reaches the ~200 target.
 * ============================================================ */

/* Hero — page-leading marketing block.
 */
.loom-hero {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-4);
  padding: var(--loom-space-8) var(--loom-space-5, var(--loom-space-4));
  text-align: start;
}
.loom-hero[data-align="center"] {
  text-align: center;
  align-items: center;
}
.loom-hero-eyebrow {
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--loom-color-accent);
}
.loom-hero-title {
  font-size: clamp(2.25rem, 4vw + 0.8rem, 4rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--loom-color-ink);
  max-inline-size: 22ch;
  margin: 0;
}
.loom-hero-subtitle {
  font-size: clamp(1.125rem, 1vw + 0.9rem, 1.375rem);
  line-height: 1.5;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  max-inline-size: 60ch;
  margin: 0;
}
.loom-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--loom-space-3);
  margin-block-start: var(--loom-space-3);
}
.loom-hero-media {
  margin-block-start: var(--loom-space-6);
  border-radius: var(--loom-radius-lg, 0.75rem);
  overflow: hidden;
}

/* Feature — single feature card (icon + title + desc).
 */
.loom-feature {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-2);
  padding: var(--loom-space-4);
}
.loom-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  color: var(--loom-color-accent);
  margin-block-end: var(--loom-space-2);
}
.loom-feature-title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0;
}
.loom-feature-desc {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  margin: 0;
}
.loom-feature-grid {
  display: grid;
  gap: var(--loom-space-5, var(--loom-space-4));
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
}

/* Pricing table.
 */
.loom-pricing-table {
  display: grid;
  gap: var(--loom-space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  align-items: stretch;
}
.loom-pricing-tier {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-3);
  padding: var(--loom-space-5, var(--loom-space-4));
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-lg, 0.75rem);
}
.loom-pricing-tier-featured {
  border-color: var(--loom-color-accent);
  box-shadow: 0 0 0 2px var(--loom-color-accent);
  position: relative;
}
/* Legacy `.loom-pricing-tier-featured::before { content: "Popular" }`
 * removed: hardcoded English label in CSS was a localization bug AND
 * a substrate-de-consumer-shaping violation (no opt-out, no per-tier
 * configuration). Authors who want a badge now set
 * `PricingTier.badge: Some("Most popular")` (or any locale-appropriate
 * label) and the renderer emits a `.loom-pricing__badge` span the
 * tier-card CSS positions identically. */
.loom-pricing-price {
  display: inline-flex;
  align-items: baseline;
  gap: var(--loom-space-1);
  font-size: 2.5rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--loom-color-ink);
  font-feature-settings: "tnum" 1;
}
.loom-pricing-period {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-pricing-features {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-2);
  font-size: 0.9375rem;
}
.loom-pricing-features > li {
  display: flex;
  align-items: center;
  gap: var(--loom-space-2);
}

/* Testimonial.
 */
.loom-testimonial {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-4);
  padding: var(--loom-space-5, var(--loom-space-4));
  background: var(--loom-color-surface);
  border-radius: var(--loom-radius-lg, 0.75rem);
}
.loom-testimonial-quote {
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--loom-color-ink);
  font-style: italic;
  margin: 0;
}
.loom-testimonial-author {
  display: flex;
  align-items: center;
  gap: var(--loom-space-3);
}
.loom-testimonial-avatar {
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: 50%;
  overflow: hidden;
}
/* ============================================================
 * Rich page footer (CmsPage.footer typed slot)
 * ============================================================ */
.loom-page-footer.loom-page-footer--rich {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 3rem 1.75rem 2rem;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  color: var(--loom-color-ink, var(--loom-fg));
  font-size: 0.92rem;
  background: color-mix(in oklab, var(--loom-color-surface, var(--loom-bg)) 85%, transparent);
}
.loom-page-footer__columns {
  display: grid;
  /* #557: a 16rem auto-fit floor lets the footer wrap 4 → 2 → 1 across
   * desktop / tablet / phone without hardcoding a column count, so a
   * footer with any number of columns collapses gracefully. (The old
   * 8rem floor kept 4 cramped columns down into tablet widths.) */
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1.25rem;
  max-width: 80rem;
  width: 100%;
  margin: 0 auto;
}
.loom-page-footer__col {
  min-width: 0;
}
.loom-page-footer__col-body {
  margin: 0 0 .75rem;
  font-size: .92rem;
  line-height: 1.5;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
.loom-page-footer__col-button {
  margin-top: 0.75rem;
}
.loom-page-footer__col h3.loom-page-footer__heading {
  margin: 0 0 0.75rem;
  /* #551: footer column headings default to a mono micro-caps label
   * (the premium Loom footer look). Each dimension is tenant-tunable
   * via forge.toml so a brand wanting Title-case sans headings opts in
   * without per-site CSS: family ← [style.fonts] footer_heading,
   * size ← [style.sizes] footer_heading, weight ← [style.weights]
   * footer_heading, case ← [style.text] footer_heading_transform,
   * tracking ← [style.text] footer_heading_tracking. Fallbacks
   * reproduce the prior look byte-for-byte, so demos are unchanged. */
  font:
    var(--loom-weight-footer-heading, 700)
    var(--loom-size-footer-heading, 0.72rem)/1.4
    var(--loom-font-footer-heading, var(--loom-font-mono, ui-monospace, monospace));
  letter-spacing: var(--loom-text-footer-heading-tracking, 0.14em);
  text-transform: var(--loom-text-footer-heading-transform, uppercase);
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
}
/* #552: footer brand-mark lockup — a tenant repeats its header logo
 * (icon glyph + two-tone wordmark) at the top of a footer column. It
 * carries both `loom-page-footer__brand` and `loom-page-brand`, so the
 * flex/gap layout, the boxed-icon fill, and the `__name-accent` color all
 * inherit from the shared header-brand rules (visual parity, zero
 * duplication). This block only sets the footer-scoped wordmark size +
 * spacing. Inside a dark footer role `--loom-color-ink` is remapped to the
 * on-* color, so the wordmark head reads light automatically while the
 * accent tail keeps the primary brand color — matching real
 * plausiden.com. Emitted only when a column carries `brand`. */
.loom-page-footer__brand {
  margin: 0 0 1rem;
  font-family: var(--loom-font-display, var(--loom-font-body, system-ui, sans-serif));
  font-size: 1.2rem;
  font-weight: var(--loom-weight-heading, 700);
  line-height: 1.2;
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
}
a.loom-page-footer__brand:hover {
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-page-footer__links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-page-footer__links a {
  text-decoration: none;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
.loom-page-footer__links a:hover {
  color: var(--loom-color-primary, var(--loom-accent));
  text-decoration: underline;
}
.loom-page-footer__contact p {
  margin: 0 0 0.4rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
  display: flex;
  align-items: center;
  gap: 0.55em;
}
/* Per-field contact line-icons. Pure-CSS SVG masks (no external font,
   no <img>): the shape comes from the mask, the color from
   background-color so it inherits the contact-line text color and
   recolors on hover/theme. data: SVG masks are CSP-clean under the
   page-shell's `img-src 'self' data:`. Keyed by CmsFooterContact field
   type so the renderer can pick the right glyph with no CMS change. */
.loom-page-footer__contact-icon {
  flex: none;
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.loom-page-footer__contact-icon--location {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 21.5s7-7.4 7-12.5a7 7 0 1 0-14 0c0 5.1 7 12.5 7 12.5z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><circle cx='12' cy='9' r='2.6' fill='none' stroke='black' stroke-width='2'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 21.5s7-7.4 7-12.5a7 7 0 1 0-14 0c0 5.1 7 12.5 7 12.5z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/><circle cx='12' cy='9' r='2.6' fill='none' stroke='black' stroke-width='2'/></svg>");
}
.loom-page-footer__contact-icon--phone {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.5 3.5h3.2l1.5 4.2-2.1 1.6a11.5 11.5 0 0 0 5.1 5.1l1.6-2.1 4.2 1.5v3.2a2 2 0 0 1-2.1 2A16.2 16.2 0 0 1 4.5 5.6a2 2 0 0 1 2-2.1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M6.5 3.5h3.2l1.5 4.2-2.1 1.6a11.5 11.5 0 0 0 5.1 5.1l1.6-2.1 4.2 1.5v3.2a2 2 0 0 1-2.1 2A16.2 16.2 0 0 1 4.5 5.6a2 2 0 0 1 2-2.1z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>");
}
.loom-page-footer__contact-icon--email {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='14' rx='2' fill='none' stroke='black' stroke-width='2'/><path d='M4 7.5l8 5.5 8-5.5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='14' rx='2' fill='none' stroke='black' stroke-width='2'/><path d='M4 7.5l8 5.5 8-5.5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
.loom-page-footer__contact-icon--region {
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='9' fill='none' stroke='black' stroke-width='2'/><path d='M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18' fill='none' stroke='black' stroke-width='2'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='9' fill='none' stroke='black' stroke-width='2'/><path d='M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18' fill='none' stroke='black' stroke-width='2'/></svg>");
}
.loom-page-footer__contact a {
  color: inherit;
  text-decoration: none;
}
.loom-page-footer__contact a:hover {
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-page-footer__jurisdiction {
  font-style: italic;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent) !important;
}
.loom-page-footer__contact {
  position: relative;
}
.loom-page-footer__contact-bg {
  position: absolute;
  right: 0;
  top: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: right center;
  opacity: 0.18;
  pointer-events: none;
  z-index: 0;
}
.loom-page-footer__contact > * {
  position: relative;
  z-index: 1;
}
.loom-page-footer__col-lang {
  margin-top: 0.4rem;
}
.loom-page-footer__col-lang .loom-lang-selector {
  position: static;
}
.loom-page-footer__col-lang .loom-lang-selector > summary {
  background: var(--loom-color-surface, #fff);
  border: 1px solid color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 25%, transparent);
  border-radius: 0.25rem;
  padding: 0.4rem 0.6rem;
  font-size: 0.92rem;
}
.loom-page-footer__legal {
  max-width: 80rem;
  width: 100%;
  margin: 0 auto;
  padding-top: 1rem;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
}
.loom-page-footer__legal ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  font-size: 0.85rem;
}
.loom-page-footer__legal a {
  text-decoration: none;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
}
.loom-page-footer__legal a:hover {
  color: var(--loom-color-primary, var(--loom-accent));
  text-decoration: underline;
}
.loom-page-footer__colophon {
  max-width: 80rem;
  width: 100%;
  margin: 0.5rem auto 0;
  font-size: 0.82rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent);
}
/* Non-link footer column entries (capability/category names with no
   destination). Match the resting link color so they read as peers of
   the linked items, minus the hover affordance. */
.loom-page-footer__text-item {
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
/* Opt-in single-row bottom bar: one shared top border, colophon + legal
   links. Mobile-first — stacked + centered below 768px, then a
   justify-between row (colophon left, legal right) at the 768px tier,
   mirroring the columns grid's own collapse. The legal links stay on a
   single non-wrapping row at both tiers. Only emitted when
   CmsFooter.bottom_bar_inline is set. */
.loom-page-footer__bottombar {
  max-width: 80rem;
  width: 100%;
  margin: 4rem auto 0;
  padding-top: 2rem;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1rem;
  font-size: 0.75rem;
}
.loom-page-footer__bottombar .loom-page-footer__legal,
.loom-page-footer__bottombar .loom-page-footer__colophon {
  max-width: none;
  width: auto;
  margin: 0;
  padding-top: 0;
  border-top: none;
  font-size: inherit;
}
.loom-page-footer__bottombar .loom-page-footer__legal ul {
  flex-wrap: nowrap;
  gap: 1.5rem;
  justify-content: center;
  font-size: inherit;
}
@media (min-width: 768px) {
  .loom-page-footer__bottombar {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    text-align: left;
  }
}

/* Editorial: pull-quote-style typography, no card chrome. */
.loom-testimonial.deco-editorial {
  padding: 0;
  background: transparent;
  border-radius: 0;
  border-left: 2px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 60%, transparent);
  padding-left: 1.5rem;
  max-width: 50rem;
  margin-inline: auto;
}
.loom-testimonial.deco-editorial .loom-testimonial__body {
  font: italic 600 clamp(1.25rem, 2.2vw, 1.65rem)/1.4 var(--loom-font-display);
  letter-spacing: -0.015em;
}
.loom-testimonial.deco-editorial .loom-testimonial__author {
  margin-top: 1rem;
  font-size: 0.92rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
}
/* Minimal: quote + attribution line. */
.loom-testimonial.deco-minimal {
  padding: 0;
  background: transparent;
  border-radius: 0;
  max-width: 42rem;
}
.loom-testimonial.deco-minimal .loom-testimonial__body {
  font-style: normal;
  font-size: 1rem;
  margin: 0 0 0.25rem;
}
.loom-testimonial.deco-minimal .loom-testimonial__author {
  font-size: 0.85rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent);
}

.loom-testimonial-avatar > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Logo cloud — wordmark / partner strip.
 * Renderer (loom-cms-render LogoCloud) emits:
 *   <section class="loom-logo-cloud">
 *     <h2 class="loom-logo-cloud__heading">…</h2>?
 *     <div class="loom-logo-cloud__row">
 *       <span class="loom-logo-cloud__item">…</span> …
 *     </div>
 *   </section>
 * Substrate-de-consumer-shaping (2026-05-20):
 *   Default = start-aligned row + full-color items. Centered marketing
 *   layout is opt-in via `.loom-logo-cloud--centered`. Grayscale-mute
 *   for the brand-graveyard SaaS look is opt-in via
 *   `.loom-logo-cloud--muted` (also fades any future <img>/<svg> children).
 */
.loom-logo-cloud {
  padding: var(--loom-space-6) 0;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  text-align: start;
}
.loom-logo-cloud__heading {
  margin: 0 0 var(--loom-space-4);
  font: 600 0.78rem/1 var(--loom-font-display, inherit);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
}
.loom-logo-cloud__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--loom-space-6);
}
.loom-logo-cloud__item {
  font: 600 1rem/1 var(--loom-font-display, inherit);
  letter-spacing: -0.01em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-logo-cloud--centered { text-align: center; }
.loom-logo-cloud--centered .loom-logo-cloud__row { justify-content: center; }
.loom-logo-cloud--muted .loom-logo-cloud__item {
  opacity: 0.7;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
}
.loom-logo-cloud--muted img,
.loom-logo-cloud--muted svg {
  opacity: 0.7;
  filter: grayscale(100%);
}

/* FAQ — built on native <details>/<summary> for free accessibility.
 */
.loom-faq {
  display: flex;
  flex-direction: column;
  gap: var(--loom-space-2);
}
.loom-faq-item {
  border: 1px solid var(--loom-color-border, var(--loom-color-ink));
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-surface);
  overflow: hidden;
}
.loom-faq-question {
  padding: var(--loom-space-4);
  font-weight: 600;
  font-size: 1rem;
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--loom-space-3);
}
.loom-faq-question::-webkit-details-marker { display: none; }
.loom-faq-question::after {
  content: "+";
  font-size: 1.25rem;
  font-weight: 400;
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
}
.loom-faq-item[open] .loom-faq-question::after { content: "−"; }
.loom-faq-answer {
  padding: 0 var(--loom-space-4) var(--loom-space-4);
  color: var(--loom-color-ink-muted, var(--loom-color-ink));
  line-height: 1.55;
}

/* Stats row — KPI strip (horizontal flex of .loom-metric).
 */
.loom-stats-row {
  display: grid;
  gap: var(--loom-space-5, var(--loom-space-4));
  grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
}

/* Team member card + grid.
 */
.loom-team-member {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--loom-space-3);
  padding: var(--loom-space-4);
  text-align: center;
}
.loom-team-member > .loom-avatar {
  inline-size: 5rem;
  block-size: 5rem;
}
.loom-team-grid {
  display: grid;
  gap: var(--loom-space-5, var(--loom-space-4));
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

/* Comparison table — feature-vs-feature comparison.
 */
.loom-comparison-table {
  inline-size: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}
.loom-comparison-table th,
.loom-comparison-table td {
  padding: var(--loom-space-3) var(--loom-space-4);
  border-block-end: 1px solid var(--loom-color-border, var(--loom-color-ink));
  text-align: center;
}
.loom-comparison-table th:first-child,
.loom-comparison-table td:first-child {
  text-align: start;
  font-weight: 500;
}
.loom-comparison-table thead th {
  background: var(--loom-color-surface-subtle, var(--loom-color-surface));
  font-weight: 600;
}
.loom-comparison-table [data-yes]::before { content: "✓"; color: var(--loom-color-success, var(--loom-color-accent)); }
.loom-comparison-table [data-no]::before  { content: "—"; color: var(--loom-color-ink-muted, var(--loom-color-ink)); }

/* ============================================================
 * T660 P4 — primitive batch v2
 * ImageHero / SplitHero / FeatureSpotlight / StatBand / Steps
 * Pricing / Faq / Marquee / CallToAction / PullQuote
 * Plus the scroll-reveal animation system + full-bleed escape.
 * ============================================================ */

/* Full-bleed escape — breaks out of the main#content max-width
 * so heroes + marquees + CTA bands span viewport edge-to-edge.
 * Works with the standard main#content { max-width: 64rem; margin: 0 auto; }
 * layout: the negative margin equals (100vw - 100%) / 2. */
.loom-bleed {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

/* Scroll-reveal: subtle fade-up the first time the element
 * scrolls into view. Native CSS scroll-driven animations on
 * supporting browsers; a no-op fallback elsewhere so the
 * element is visible immediately. Respects prefers-reduced-motion. */
[data-loom-reveal] {
  --reveal-y: 24px;
  opacity: 1;
  transform: none;
}
@supports (animation-timeline: view()) {
  [data-loom-reveal] {
    opacity: 0;
    transform: translateY(var(--reveal-y));
    animation: loom-reveal-up linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 40%;
  }
}
@keyframes loom-reveal-up {
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  [data-loom-reveal] {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
}

/* Button system — typed primary / secondary / ghost / size
 * variants. Used by all the new heroes + CTA bands + tier cards. */
.loom-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.4rem;
  /* Default to a pill (999px) so demos/ProsperityClub are unchanged,
   * but consume the tenant control-radius token (#537) when set — a
   * tenant wanting rounded-rect buttons sets [style.radius] control
   * and gets it on every .loom-btn (hero CTAs + nav actions). The
   * earlier token rule (--comp-btn-radius ← --loom-radius-control) was
   * being overridden by this later top-level rule's hardcoded 999px. */
  border-radius: var(--loom-radius-control, 999px);
  font: 600 0.95rem/1 var(--loom-font-display, var(--loom-font-stack));
  letter-spacing: -0.012em;
  text-decoration: none;
  min-height: 44px;
  border: 1px solid transparent;
  cursor: pointer;
  transition:
    background var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease),
    border-color var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease),
    transform var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease),
    box-shadow var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-btn--primary {
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, white);
  box-shadow:
    0 1px 0 oklch(100% 0 0 / 0.2) inset,
    0 6px 18px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent);
}
.loom-btn--primary:hover {
  transform: translateY(-1px);
  box-shadow:
    0 1px 0 oklch(100% 0 0 / 0.25) inset,
    0 12px 26px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 40%, transparent);
}
.loom-btn--secondary {
  background: var(--loom-color-surface, transparent);
  color: var(--loom-color-ink, var(--loom-fg));
  border-color: var(--loom-color-border, var(--loom-border));
}
/* #544: success/emerald variant — light fill, success-colored border
 * + text. Reads as a "safe / verified" action (e.g. an encrypted
 * inquiry button). Generic; any tenant opts in via variant="success"
 * and can retint by setting palette.success. */
.loom-btn--success {
  background: var(--loom-color-bg, #fff);
  color: var(--loom-color-success, hsl(142 75% 22%));
  border-color: color-mix(in oklab, var(--loom-color-success, hsl(142 75% 22%)) 50%, transparent);
}
.loom-btn--success:hover {
  background: color-mix(in oklab, var(--loom-color-success, hsl(142 75% 22%)) 9%, var(--loom-color-bg, #fff));
  border-color: color-mix(in oklab, var(--loom-color-success, hsl(142 75% 22%)) 70%, transparent);
}
.loom-btn--ghost {
  background: transparent;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-btn--ghost:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent);
}
/* Outline button — transparent fill + 1px border + ink text.
 * Distinct from --secondary (which fills a surface): outline reads
 * as a true secondary action over any backdrop, picking up the
 * inherited text color so it inverts correctly on dark sections. */
.loom-btn--outline {
  background: transparent;
  color: currentColor;
  border-color: color-mix(in oklab, currentColor 32%, transparent);
}
.loom-btn--outline:hover {
  border-color: color-mix(in oklab, currentColor 60%, transparent);
  background: color-mix(in oklab, currentColor 6%, transparent);
}
.loom-btn--lg {
  padding: 1rem 1.75rem;
  font-size: 1.05rem;
  min-height: 52px;
}

/* ============================================================
 * ImageHero — full-bleed atmospheric hero
 * ============================================================ */
.loom-image-hero {
  display: grid;
  place-items: center;
  text-align: center;
  padding: clamp(4rem, 10vw, 8rem) clamp(1.5rem, 4vw, 3rem);
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.loom-image-hero.h-compact     { padding-top: clamp(2.5rem, 4vw, 3.5rem); padding-bottom: clamp(2.5rem, 4vw, 3.5rem); }
.loom-image-hero.h-comfortable { padding-top: clamp(3.5rem, 5vw, 5rem);   padding-bottom: clamp(3.5rem, 5vw, 5rem); }
.loom-image-hero.h-tall        { padding-top: clamp(5rem, 7vw, 7rem);     padding-bottom: clamp(5rem, 7vw, 7rem); }
/* Substrate-de-consumer-shaping (2026-05-20): when the cms author
 * sets `align: start` on an ImageHero, override the default center
 * posture with left-aligned editorial. SaaS-marketing default
 * (data-align="center" or absent) stays centered to preserve
 * backwards compatibility with existing Loom consumer sites. */
.loom-image-hero[data-align="start"] {
  place-items: start;
  text-align: start;
}
.loom-image-hero[data-align="start"] .loom-image-hero__inner {
  align-items: flex-start;
}
.loom-image-hero__inner {
  max-width: 64rem;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
}
.loom-image-hero__eyebrow {
  display: inline-block;
  padding: 0.4rem 0.95rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-surface, var(--loom-bg)) 80%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  font: 500 0.78rem/1 var(--loom-font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
}
/* Solid brand-tinted badge variant (#535): filled primary tint + primary
 * text, no glass blur. Opt-in via ImageHero.eyebrow_badge = true. */
.loom-image-hero__eyebrow--badge {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 32%, transparent);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* #541: the glass eyebrow base forces mono + UPPERCASE + wide
   * tracking (a "techy" treatment). The solid badge variant reads as
   * an editorial label, so render it Title Case in the body font with
   * tight tracking. Opt-in variant only; the glass base is unchanged. */
  font-family: var(--loom-font-body);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--loom-color-primary, var(--loom-accent));
}
/* Per-item pill label on feature_spotlight items (#602): brand-tinted
 * badge above the card title, mirroring the ImageHero eyebrow-badge.
 * Renders only when SpotlightItem.eyebrow is set. */
.loom-feature-spotlight__eyebrow {
  display: inline-block;
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 32%, transparent);
  font-family: var(--loom-font-body);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  color: var(--loom-color-primary, var(--loom-accent));
  margin-bottom: 0.7rem;
}
/* Breadcrumb trail (#602): "Site » Section » Page" rendered above the
 * first body section. Small, muted, » separators between items. */
.loom-breadcrumb {
  margin: 0 0 1.5rem;
  font-family: var(--loom-font-body);
  font-size: 0.82rem;
}
.loom-breadcrumb__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.loom-breadcrumb__item {
  display: inline-flex;
  align-items: center;
  color: color-mix(in oklab, var(--loom-color-ink, currentColor) 60%, transparent);
}
.loom-breadcrumb__item + .loom-breadcrumb__item::before {
  content: "»";
  margin-right: 0.5rem;
  color: color-mix(in oklab, var(--loom-color-ink, currentColor) 40%, transparent);
}
.loom-breadcrumb__link {
  color: color-mix(in oklab, var(--loom-color-ink, currentColor) 65%, transparent);
  text-decoration: none;
}
.loom-breadcrumb__link:hover,
.loom-breadcrumb__link:focus-visible {
  color: var(--loom-color-primary, var(--loom-accent));
  text-decoration: underline;
}
.loom-breadcrumb__current {
  color: var(--loom-color-ink, currentColor);
  font-weight: 600;
}
/* Dark "solid"-fill hero (#602). CSP-safe: the page-shell CSP blocks
 * inline style attributes, so the fill is applied here in the stylesheet
 * rather than inline. A `.bg-solid` hero paints the primary brand color
 * with on-primary text; the eyebrow badge + lede shift to a light-on-dark
 * treatment so they stay legible against the dark fill. */
.loom-image-hero.bg-solid {
  background: var(--loom-color-primary);
  color: var(--loom-color-on-primary, #fff);
}
.loom-image-hero.bg-solid .loom-image-hero__lede {
  color: color-mix(in oklab, var(--loom-color-on-primary, #fff) 85%, transparent);
}
.loom-image-hero.bg-solid .loom-image-hero__eyebrow--badge {
  background: color-mix(in oklab, #fff 16%, transparent);
  color: var(--loom-color-on-primary, #fff);
  border-color: color-mix(in oklab, #fff 32%, transparent);
}
/* Title base goes on-primary (white) on a dark solid fill; an inner
 * wrapper otherwise resets it to ink. The `__title-accent` span keeps
 * its own accent color (more specific direct rule). */
.loom-image-hero.bg-solid .loom-image-hero__title {
  color: var(--loom-color-on-primary, #fff);
}
.loom-image-hero__title {
  margin: 0;
  font-family: var(--loom-font-display);
  font-weight: var(--loom-weight-display, 900);
  letter-spacing: -0.035em;
  /* #567: tenant-tunable via [style.sizes] hero_title_leading →
   * --loom-size-hero-title-leading. REAL marketing heroes (e.g.
   * plausiden.com) run a looser 1.1 leading on the headline; the
   * substrate default stays the tight 1.02 expressive setting so
   * demos/ProsperityClub are unchanged. */
  line-height: var(--loom-size-hero-title-leading, 1.02);
  /* #538: tenant-tunable via [style.sizes] hero_title → --loom-size-
   * hero-title. Default clamp keeps the substrate's expressive ceiling
   * (88px); a tenant whose reference hero is smaller can dial it down
   * without hand-authored CSS. */
  font-size: var(--loom-size-hero-title, clamp(2.5rem, 7vw, 5.5rem));
  color: var(--loom-color-ink);
  max-width: 22ch;
}
.loom-image-hero__lede {
  margin: 0;
  /* #570: lede type-scale is tenant-tunable so a site can match its real
   * design (next.plausiden: 1.25rem / lh 1.4 = Tailwind text-xl/leading-7).
   * Defaults reproduce the substrate's fluid clamp + 1.5 leading. */
  font-size: var(--loom-size-hero-lede, clamp(1.05rem, 1.6vw, 1.35rem));
  line-height: var(--loom-size-hero-lede-leading, 1.5);
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
  /* #568: the lede is a flex column child under align-items:flex-start,
   * so a bare `max-width` is NOT honored on the cross axis — it would
   * stretch to the full inner column (≈64rem). Drive the measure through
   * `width: min(token, 100%)` instead, which IS honored: the inner is
   * width:100%/max-width:64rem so the `100%` resolves to the column, and
   * min() caps the lede at the token on desktop (42rem → 672px, the real
   * site's max-w-2xl) while shrinking with the column on narrow viewports
   * (verified no overflow at 390). A bare definite width is NOT usable
   * here — max-width:100% is swallowed by the same flex quirk, so a fixed
   * width overflows mobile. Default min(100%,100%)=100% preserves the
   * generic full-stretch behavior (demos/ProsperityClub unchanged). */
  max-width: var(--loom-size-hero-lede-max, 60ch);
  width: min(var(--loom-size-hero-lede-max, 100%), 100%);
}
/* Two-tone headline: the trailing accent fragment renders in the
 * brand accent color (real marketing pattern — highlight the second
 * half of a long title). */
.loom-image-hero__title-accent {
  color: var(--loom-color-accent, var(--loom-accent));
}
/* Dual-CTA row: primary + secondary side by side, wrapping on
 * narrow viewports. */
.loom-image-hero__cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem;
}
/* #554: trailing icon glyph inside the primary hero CTA (opt-in via the
 * CTA's icon_slug, e.g. "arrow-right"). Inline-flex keeps the glyph
 * centered with the label; the SVG inherits the button text color and
 * scales with the label via em units. Generic — any vetted slug. */
.loom-image-hero__cta-icon {
  display: inline-flex;
  align-items: center;
  margin-left: 0.5rem;
  vertical-align: middle;
}
.loom-image-hero__cta-icon-svg {
  width: 1.05em;
  height: 1.05em;
}
/* #567: REAL marketing heroes use chunky CTAs (plausiden.com: ~18px
 * label, 24px/32px padding, ~76px tall) whereas the base .loom-btn is
 * a compact 0.95rem / 0.75rem-1.4rem / 44px control. These tunable
 * tokens let a tenant dial the hero CTA up via [style.sizes]
 * (hero_cta / hero_cta_pad_y / hero_cta_pad_x / hero_cta_min /
 * hero_cta_leading) with no hand-authored CSS. Each falls back to the
 * current .loom-btn base value, so demos/ProsperityClub render byte-
 * identical. The compound .loom-image-hero__cta.loom-btn selector
 * (0,2,0) beats both .loom-btn and .loom-btn--lg (0,1,0) regardless of
 * source order, so it scopes the chunk to hero CTAs only. */
.loom-image-hero__cta.loom-btn {
  padding: var(--loom-size-hero-cta-pad-y, 0.75rem) var(--loom-size-hero-cta-pad-x, 1.4rem);
  font-size: var(--loom-size-hero-cta, 0.95rem);
  line-height: var(--loom-size-hero-cta-leading, 1);
  min-height: var(--loom-size-hero-cta-min, 44px);
}

/* Backdrops */
.loom-image-hero.bg-gradient-mesh::before,
.loom-cta-band.bg-gradient-mesh::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(60rem 38rem at 88% 0%,
      color-mix(in oklab, var(--loom-color-accent) 55%, transparent) 0%, transparent 55%),
    radial-gradient(54rem 36rem at 0% 30%,
      color-mix(in oklab, var(--loom-color-primary) 55%, transparent) 0%, transparent 55%),
    radial-gradient(46rem 32rem at 50% 110%,
      color-mix(in oklab, var(--loom-color-accent-glow) 40%, transparent) 0%, transparent 60%),
    var(--loom-color-bg-canvas);
}
.loom-image-hero.bg-stripes::before,
.loom-cta-band.bg-stripes::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    repeating-linear-gradient(
      135deg,
      color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, var(--loom-color-surface, var(--loom-bg))) 0 22px,
      var(--loom-color-surface, var(--loom-bg)) 22px 48px
    );
}
.loom-image-hero.bg-dots::before,
.loom-cta-band.bg-dots::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(circle, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent) 1.5px, transparent 1.6px) 0 0 / 24px 24px,
    var(--loom-color-surface, var(--loom-bg));
}
/* Faint square grid-line backdrop — orthogonal ruled lines in the
 * primary color at low opacity over the surface. Enterprise /
 * engineering register (vs the softer dot-grid). */
.loom-image-hero.bg-grid::before,
.loom-cta-band.bg-grid::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to right, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 7%, transparent) 1px, transparent 1px) 0 0 / 48px 48px,
    linear-gradient(to bottom, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 7%, transparent) 1px, transparent 1px) 0 0 / 48px 48px,
    var(--loom-color-surface, var(--loom-bg));
  -webkit-mask-image: radial-gradient(120% 100% at 50% 0%, #000 55%, transparent 100%);
          mask-image: radial-gradient(120% 100% at 50% 0%, #000 55%, transparent 100%);
}

/* Photo background: an <img> child sized to cover the hero +
 * an overlay scrim via ::after so the title stays legible.
 * CSP-strict-safe (no inline styles, no url() in CSS for the
 * dynamic src).
 */
.loom-image-hero.bg-photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.loom-image-hero__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: -2;
}
.loom-image-hero.bg-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.loom-image-hero__photo.ov-dark ~ .loom-image-hero__inner,
.loom-image-hero.bg-photo:has(.ov-dark)::after {
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--loom-color-bg-canvas) 55%, transparent) 0%,
    color-mix(in oklab, var(--loom-color-bg-canvas) 75%, transparent) 100%
  );
}
.loom-image-hero.bg-photo:has(.ov-light)::after {
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--loom-color-surface) 55%, transparent) 0%,
    color-mix(in oklab, var(--loom-color-surface) 75%, transparent) 100%
  );
}
.loom-image-hero.bg-photo:has(.ov-none)::after {
  background: transparent;
}
.loom-image-hero__slot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: center;
  width: 100%;
}
.loom-image-hero__slot--before-headline {
  margin-bottom: 0.25rem;
}
.loom-image-hero__slot--after-cta {
  margin-top: 0.5rem;
  opacity: 0.85;
  font-size: 0.92em;
}
.loom-image-hero__slot > * { margin: 0; }

/* ============================================================
 * SplitHero — text + visual side-by-side
 * ============================================================ */
.loom-split-hero {
  display: grid;
  gap: clamp(2rem, 4vw, 4rem);
  align-items: center;
  padding: clamp(3rem, 6vw, 6rem) 1.5rem;
  grid-template-columns: 1fr 1fr;
}
.loom-split-hero.visual-left  { grid-template-areas: "visual text"; }
.loom-split-hero.visual-right { grid-template-areas: "text visual"; }
.loom-split-hero__text   { grid-area: text; }
.loom-split-hero__visual { grid-area: visual; }
@media (max-width: 800px) {
  .loom-split-hero { grid-template-columns: 1fr; grid-template-areas: "text" "visual"; }
}
.loom-split-hero__eyebrow {
  display: inline-block;
  font: 600 0.75rem/1 var(--loom-font-mono);
  color: var(--loom-color-primary, var(--loom-accent));
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 1rem;
}
.loom-split-hero__title {
  margin: 0 0 1rem;
  font: 800 clamp(2rem, 4.5vw, 3.5rem)/1.08 var(--loom-font-display);
  letter-spacing: -0.028em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-split-hero__lede {
  margin: 0 0 1.5rem;
  font-size: 1.1rem;
  line-height: 1.5;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
  max-width: 55ch;
}
.loom-split-hero__code {
  background: var(--loom-color-ink, oklch(20% 0.02 280));
  color: var(--loom-color-surface, white);
  padding: 1.25rem;
  border-radius: 12px;
  font: 0.85rem/1.55 var(--loom-font-mono);
  overflow-x: auto;
  box-shadow:
    0 1px 0 oklch(100% 0 0 / 0.05) inset,
    0 20px 40px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 25%, transparent);
}
.loom-split-hero__stat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 2rem;
  border-radius: 16px;
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid var(--loom-color-border, var(--loom-border));
  box-shadow: var(--loom-shadow-md);
}
.loom-split-hero__stat-value {
  font: 900 clamp(3rem, 7vw, 5rem)/1 var(--loom-font-display);
  letter-spacing: -0.04em;
  background: linear-gradient(135deg, var(--loom-color-primary, var(--loom-accent)), var(--loom-color-accent-2, var(--loom-accent-2)));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.loom-split-hero__stat-label {
  margin-top: 0.5rem;
  font-size: 0.95rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-split-hero__asset {
  aspect-ratio: 4 / 3;
  border-radius: 16px;
  background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 30%, transparent));
  display: grid; place-items: center;
  border: 1px solid var(--loom-color-border, var(--loom-border));
  box-shadow: var(--loom-shadow-lg);
}
/* `.loom-asset-placeholder` removed in PlausiDen-Loom 1ca0947:
 * all primitives now emit real <img> tags. The selector no
 * longer matches anything in rendered output. */

/* ============================================================
 * FeatureSpotlight — multi-column feature grid
 * Default: left-aligned editorial heading. Center is opt-in via
 * `.loom-feature-spotlight--centered` on the outer element
 * (callers that explicitly want the centered-marketing shape).
 * ============================================================ */
.loom-feature-spotlight {
  /* #572: vertical padding is tenant-tunable via --loom-size-feature-py so a
   * tenant can match a source site that bakes generous py INSIDE the section
   * band (e.g. plausiden.com features py-24 = 96px) rather than relying on the
   * page-level flex gap. Default = the current thin clamp, so every existing
   * site (ProsperityClub/demos) stays byte-identical. Horizontal stays 0. */
  padding: var(--loom-size-feature-py, clamp(.5rem, 1vw, .9rem)) 0;
}
.loom-feature-spotlight__heading {
  font: var(--loom-weight-heading, 800) clamp(1.75rem, 3.5vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  margin: 0 0 0.75rem;
  text-align: start;
}
.loom-feature-spotlight.loom-feature-spotlight--centered .loom-feature-spotlight__heading,
.loom-feature-spotlight.loom-feature-spotlight--centered .loom-feature-spotlight__lede {
  text-align: center;
  margin-inline: auto;
}
.loom-feature-spotlight__lede {
  margin: 0 0 3rem;
  max-width: 60ch;
  text-align: start;
  font-size: 1.1rem;
  line-height: 1.5;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-feature-spotlight__grid {
  display: grid;
  gap: 2rem 1.75rem;
}
.loom-feature-spotlight.cols-1 .loom-feature-spotlight__grid { grid-template-columns: 1fr; }
.loom-feature-spotlight.cols-2 .loom-feature-spotlight__grid { grid-template-columns: repeat(2, 1fr); }
.loom-feature-spotlight.cols-3 .loom-feature-spotlight__grid { grid-template-columns: repeat(3, 1fr); }
.loom-feature-spotlight.cols-4 .loom-feature-spotlight__grid { grid-template-columns: repeat(4, 1fr); }
/* #555: tablet tier — 3-/4-up grids step down to 2 columns before
 * collapsing to a single column on phones, instead of jumping straight
 * to 1. cols-1/cols-2 are left alone here (already ≤2). Generic.
 * #581: boundary aligned to the canonical 768 md breakpoint (was 800)
 * so cols-3/4 hold full width at 768 like mainstream Tailwind grids. */
@media (max-width: 767.98px) {
  .loom-feature-spotlight.cols-3 .loom-feature-spotlight__grid,
  .loom-feature-spotlight.cols-4 .loom-feature-spotlight__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  .loom-feature-spotlight__grid { grid-template-columns: 1fr !important; }
}
.loom-feature-spotlight__item {
  /* #540: more generous padding + a soft resting elevation so the
   * default card reads premium (not boxed-in) at rest, then lifts
   * further on hover. Generic; the flat deco variants (editorial /
   * minimal) null this shadow back out below.
   * #571: card padding is tenant-tunable (default = the #540 clamp, so
   * untouched sites are byte-identical). next.plausiden opts into
   * REAL's p-6/md:p-8 rhythm via --loom-size-feature-card-pad. */
  padding: var(--loom-size-feature-card-pad, clamp(1.75rem, 2.2vw, 2.25rem));
  /* #571: card corner is tenant-tunable; default 16px preserves every
   * existing site. next.plausiden opts into REAL's rounded-xl (12px)
   * via --loom-size-feature-card-radius. */
  border-radius: var(--loom-size-feature-card-radius, 16px);
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  box-shadow:
    0 1px 2px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 5%, transparent),
    0 10px 30px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent);
  transition: transform var(--loom-motion-base, 280ms) var(--loom-ease-spring, ease), box-shadow var(--loom-motion-base, 280ms) var(--loom-ease-out, ease), border-color var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-feature-spotlight__item:hover {
  transform: translateY(-4px);
  box-shadow:
    0 24px 48px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 14%, transparent),
    0 8px 16px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent);
  border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent);
}
.loom-feature-spotlight__icon {
  display: grid;
  place-items: center;
  /* #571: icon box size + icon→title gap are tenant-tunable; defaults
   * (48px / 1.2rem) preserve every existing site. next.plausiden opts
   * into REAL's w-14 (56px) box + mb-6 (1.5rem) gap. */
  width: var(--loom-size-feature-icon, 48px); height: var(--loom-size-feature-icon, 48px);
  /* #571: icon tile corner + fill are tenant-tunable; defaults (12px /
   * the diagonal primary→accent gradient) preserve every existing site.
   * next.plausiden opts into REAL's rounded-2xl (16px) tile + flat
   * bg-primary/5 wash via --loom-size-feature-icon-radius /
   * --loom-color-feature-icon-bg. */
  border-radius: var(--loom-size-feature-icon-radius, 12px);
  background: var(--loom-color-feature-icon-bg, linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 22%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 22%, transparent)));
  margin-bottom: var(--loom-size-feature-icon-gap, 1.2rem);
  font: 700 1.4rem/1 var(--loom-font-display);
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-feature-spotlight__photo {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--loom-img-radius, var(--loom-radius, 10px));
  margin-bottom: 1rem;
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent);
}
/* Portrait media mode (#602): centered natural-aspect headshot for
 * team/profile cards, overriding the default 16:10 cover. !important
 * to win over the decoration-specific aspect-ratio rules. */
.loom-feature-spotlight__photo--portrait {
  width: auto !important;
  max-width: 220px;
  aspect-ratio: auto !important;
  object-fit: contain !important;
  margin-left: auto;
  margin-right: auto;
}
.loom-feature-spotlight.deco-editorial .loom-feature-spotlight__photo {
  aspect-ratio: 4 / 3;
  border-radius: var(--loom-img-radius, var(--loom-radius-sm, 6px));
  margin-bottom: 1.1rem;
}
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__photo {
  aspect-ratio: 1 / 1;
  border-radius: 0;
}
.loom-feature-spotlight__title {
  margin: 0 0 0.55rem;
  font: 700 1.2rem/1.25 var(--loom-font-body);
  letter-spacing: -0.018em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-feature-spotlight__body {
  margin: 0 0 1rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 68%, transparent);
  line-height: 1.55;
  font-size: 0.97rem;
}
/* Structured sub-blocks (SpotlightItem.segments) — bold lead-in
   heading + paragraph, e.g. case-study "What mattered / What we
   shipped / Outcome" sections. */
.loom-feature-spotlight__segment {
  margin: 0 0 0.85rem;
}
.loom-feature-spotlight__segment-heading {
  display: block;
  font-weight: 700;
  color: var(--loom-color-ink, var(--loom-fg));
  margin-bottom: 0.15rem;
}
.loom-feature-spotlight__segment-body {
  margin: 0;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 68%, transparent);
  line-height: 1.55;
  font-size: 0.97rem;
}
/* Numbered process steps (FeatureSpotlight.numbered) — circular badge. */
.loom-feature-spotlight__number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
  font-weight: 700;
  font-size: 1.05rem;
  margin-bottom: 0.75rem;
  font-variant-numeric: tabular-nums;
}
/* Per-card meta row (SpotlightItem.meta) — date · read-time chips. */
.loom-feature-spotlight__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
  font-size: 0.82rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.loom-feature-spotlight__meta-sep { opacity: 0.5; }
.loom-feature-spotlight__more {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--loom-color-primary, var(--loom-accent));
  font-weight: 600;
  font-size: 0.92rem;
  text-decoration: none;
}
.loom-feature-spotlight__more:hover {
  text-decoration: underline;
}
/* Trailing inline body link — final phrase of the body paragraph
   continues into a brand-secondary linked phrase (in-flow). */
.loom-feature-spotlight__body-link {
  color: var(--loom-color-secondary, var(--loom-color-primary));
  font-weight: 700;
  text-decoration: none;
}
.loom-feature-spotlight__body-link:hover {
  text-decoration: underline;
}

/* ============================================================
 * FeatureSpotlight decoration variants (substrate-de-consumer-shaping)
 * ============================================================ */
/* Editorial: strip card chrome to typography + top accent rule. */
.loom-feature-spotlight.deco-editorial .loom-feature-spotlight__item {
  padding: 1rem 0 1.25rem;
  border: 0;
  border-top: 2px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 60%, transparent);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transition: none;
}
.loom-feature-spotlight.deco-editorial .loom-feature-spotlight__item:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
}
.loom-feature-spotlight.deco-editorial .loom-feature-spotlight__icon {
  width: auto; height: auto;
  background: none;
  margin-bottom: 0.5rem;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--loom-color-primary, var(--loom-accent));
  display: inline-flex;
}
.loom-feature-spotlight.deco-editorial .loom-feature-spotlight__title {
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}
/* Minimal: tightest, no decoration at all. */
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__item {
  padding: 0.5rem 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  transition: none;
}
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__item:hover {
  transform: none;
  box-shadow: none;
}
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__icon {
  display: none;
}
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__title {
  font-weight: 600;
  font-size: 0.98rem;
  margin-bottom: 0.25rem;
}
.loom-feature-spotlight.deco-minimal .loom-feature-spotlight__body {
  font-size: 0.9rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
}
/* #583: Soft feature-spotlight cards — filled muted surface + hairline
 * border + rounded corners + generous padding. No icon tile, no top
 * accent, no hover lift. The quiet "info card" shape (mission / vision
 * / values blurbs). Themable: surface + border tokens flip with theme. */
.loom-feature-spotlight.deco-soft .loom-feature-spotlight__item {
  padding: 2rem;
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 4%, transparent));
  border: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  border-radius: var(--loom-radius-lg, 1rem);
  box-shadow: none;
}
.loom-feature-spotlight.deco-soft .loom-feature-spotlight__item:hover {
  transform: none;
  box-shadow: none;
}
.loom-feature-spotlight.deco-soft .loom-feature-spotlight__icon {
  display: none;
}
.loom-feature-spotlight.deco-soft .loom-feature-spotlight__title {
  margin: 0 0 1rem;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--loom-color-text, inherit);
}
.loom-feature-spotlight.deco-soft .loom-feature-spotlight__body {
  margin: 0;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
}

/* ============================================================
 * PillGrid (#584) — centered heading over a grid of compact
 * icon+label pills. Each pill is a horizontal row (icon chip +
 * short label) in a light card. For value lists / capability
 * checklists / certifications / trust badges. Collapses to one
 * column below 640px; cols-N sets the wide-breakpoint count.
 * ============================================================ */
.loom-pill-grid {
  max-width: 48rem;
  margin: 0 auto;
  padding: 4rem 1.5rem;
  text-align: center;
}
.loom-pill-grid__heading {
  margin: 0 0 2rem;
  font-family: var(--loom-font-display, var(--loom-font-body));
  font-size: 1.875rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--loom-color-text, inherit);
}
.loom-pill-grid__lede {
  margin: -1rem auto 2rem;
  max-width: 40rem;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
}
.loom-pill-grid__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  text-align: left;
}
.loom-pill-grid__item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem;
  background: var(--loom-color-surface, var(--loom-color-bg, #fff));
  border: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  border-radius: var(--loom-radius-md, 0.75rem);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.loom-pill-grid__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0.25rem;
  border-radius: 999px;
  color: var(--loom-color-primary, var(--loom-primary));
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
}
.loom-pill-grid__icon-svg {
  width: 1rem;
  height: 1rem;
}
.loom-pill-grid__label {
  font-weight: 500;
  color: var(--loom-color-text, inherit);
}
@media (min-width: 640px) {
  .loom-pill-grid.cols-2 .loom-pill-grid__grid { grid-template-columns: repeat(2, 1fr); }
  .loom-pill-grid.cols-3 .loom-pill-grid__grid { grid-template-columns: repeat(3, 1fr); }
  .loom-pill-grid.cols-4 .loom-pill-grid__grid { grid-template-columns: repeat(4, 1fr); }
}

/* ============================================================
 * StatBand — row of large animated numbers
 * ============================================================ */
/* StatBand — default left-aligned. Centered alignment opt-in
 * via .loom-stat-band--centered on the outer element. Mirrors
 * the same pattern in FeatureSpotlight + Pricing + LogoCloud. */
.loom-stat-band {
  padding: clamp(3rem, 5vw, 5rem) 0;
  text-align: start;
}
.loom-stat-band.loom-stat-band--centered {
  text-align: center;
}
.loom-stat-band__heading {
  font: 800 clamp(1.5rem, 3vw, 2.25rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  margin: 0 0 0.5rem;
}
.loom-stat-band__lede {
  margin: 0 auto 2.5rem;
  max-width: 55ch;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-stat-band__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: 2rem;
  padding: 2rem 1.5rem;
  /* Default: no card chrome. Substrate stays neutral; the row is
   * just a grid. Opt-in card chrome via .loom-stat-band--carded
   * on the outer element (caller chooses). */
  border-radius: 0;
  background: transparent;
  border: none;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border) 60%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border) 60%, transparent);
}
.loom-stat-band.loom-stat-band--carded .loom-stat-band__row {
  border-radius: 18px;
  border: 1px solid color-mix(in oklab, var(--loom-color-border) 70%, transparent);
  background:
    linear-gradient(
      135deg,
      color-mix(in oklab, var(--loom-color-primary) 6%, var(--loom-color-surface)),
      color-mix(in oklab, var(--loom-color-accent) 6%, var(--loom-color-surface))
    );
}
.loom-stat-band__item {
  display: flex; flex-direction: column; align-items: center;
}
.loom-stat-band__value {
  /* Default: solid ink. Drops the SaaS gradient-clipped-text
   * trope. Higher contrast on dark themes (where the gradient
   * mid-stop fell into low-readable violet). Caller can opt
   * back into the gradient via .loom-stat-band--gradient on
   * the outer element if a brand explicitly wants it. */
  font: 900 clamp(2.5rem, 5vw, 4rem)/1 var(--loom-font-display);
  letter-spacing: -0.04em;
  color: var(--loom-color-primary);
}
.loom-stat-band.loom-stat-band--gradient .loom-stat-band__value {
  background: linear-gradient(135deg, var(--loom-color-primary), var(--loom-color-accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.loom-stat-band__label {
  margin-top: 0.5rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-stat-band__hint {
  margin-top: 0.25rem;
  font-size: 0.82rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
}

/* ============================================================
 * Steps — numbered process
 * ============================================================ */
.loom-steps {
  padding: clamp(3rem, 5vw, 5rem) 0;
}
.loom-steps__heading {
  font: 800 clamp(1.75rem, 3.5vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  margin: 0 0 0.5rem;
  text-align: center;
}
.loom-steps__lede {
  margin: 0 auto 3rem;
  max-width: 60ch;
  text-align: center;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-steps__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 1.5rem;
  counter-reset: loom-step;
}
.loom-steps__item {
  position: relative;
  padding: 1.5rem 1.5rem 1.75rem;
  border-radius: 14px;
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
}
.loom-steps__num {
  display: grid;
  place-items: center;
  width: 36px; height: 36px;
  border-radius: 50%;
  font: 800 1.05rem/1 var(--loom-font-display);
  color: var(--loom-color-on-primary, white);
  background: linear-gradient(135deg, var(--loom-color-primary, var(--loom-accent)), var(--loom-color-accent-2, var(--loom-accent-2)));
  margin-bottom: 1rem;
  box-shadow: 0 6px 18px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent);
}
.loom-steps__title {
  margin: 0 0 0.4rem;
  font: 700 1.1rem/1.2 var(--loom-font-display);
  letter-spacing: -0.018em;
}
.loom-steps__text {
  margin: 0;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
  line-height: 1.55;
  font-size: 0.95rem;
}

/* ============================================================
 * Pricing — tier card row
 * ============================================================ */
.loom-pricing {
  padding: clamp(3rem, 5vw, 5rem) 0;
}
.loom-pricing__heading {
  font: 800 clamp(1.75rem, 3.5vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  margin: 0 0 0.5rem;
  /* Default: align with body content. Centered headings are
   * opt-in via .loom-pricing--centered on the outer element. */
  text-align: start;
}
.loom-pricing__lede {
  margin: 0 0 3rem;
  max-width: 60ch;
  color: color-mix(in oklab, var(--loom-color-ink) 65%, transparent);
}
/* ============================================================
 * KvPair — typed definition-list editorial panel
 * ============================================================ */
.loom-kv-section {
  padding: clamp(2rem, 4vw, 3.5rem) 0;
}
.loom-kv-heading {
  font: 800 clamp(1.5rem, 3vw, 2.25rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.022em;
  margin: 0 0 1.5rem;
  color: var(--loom-color-ink);
}
.loom-kv-list {
  display: grid;
  grid-template-columns: minmax(8rem, 12rem) 1fr;
  gap: 0.5rem 2rem;
  margin: 0;
  padding: 0;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border) 65%, transparent);
}
.loom-kv-row {
  display: contents;
}
.loom-kv-row > .loom-kv-key,
.loom-kv-row > .loom-kv-value {
  padding-block: 1rem;
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border) 65%, transparent);
}
.loom-kv-key {
  margin: 0;
  font: 600 0.78rem/1.4 var(--loom-font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklab, var(--loom-color-ink) 70%, transparent);
  align-self: start;
}
.loom-kv-value {
  margin: 0;
  font-size: 1.02rem;
  line-height: 1.55;
  color: var(--loom-color-ink);
}
.loom-kv-text {
  display: block;
  font-weight: 600;
}
.loom-kv-hint {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.9rem;
  font-weight: 400;
  color: color-mix(in oklab, var(--loom-color-ink) 65%, transparent);
}
/* Mobile: stack the columns so keys sit above values. */
@media (max-width: 640px) {
  .loom-kv-list {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .loom-kv-row > .loom-kv-key {
    padding-block: 1rem 0.25rem;
    border-bottom: none;
  }
  .loom-kv-row > .loom-kv-value {
    padding-block: 0 1rem;
  }
}

.loom-pricing.loom-pricing--centered .loom-pricing__heading,
.loom-pricing.loom-pricing--centered .loom-pricing__lede {
  text-align: center;
  margin-inline: auto;
}
.loom-pricing__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1.5rem;
  align-items: stretch;
}
.loom-pricing__tier {
  display: flex;
  flex-direction: column;
  padding: 2rem 1.75rem;
  /* Default: flat editorial card. Square hairline border, no
   * lift-on-hover, no heavy shadow. The pricing-as-marketing-
   * spectacle look (rounded card + shadow + hover lift + glow
   * on highlighted tier) is opt-in via .loom-pricing--carded
   * on the outer element. */
  background: var(--loom-color-surface);
  border: 1px solid var(--loom-color-border);
  border-radius: 0;
  position: relative;
}
.loom-pricing__tier.is-highlighted {
  border-color: var(--loom-color-primary);
  border-width: 2px;
  /* Default highlight: heavier border, same flat shape. */
}
.loom-pricing.loom-pricing--carded .loom-pricing__tier {
  border-radius: 18px;
  transition: transform var(--loom-motion-base, 280ms) var(--loom-ease-spring, ease), box-shadow var(--loom-motion-base, 280ms) var(--loom-ease-out, ease);
}
.loom-pricing.loom-pricing--carded .loom-pricing__tier:hover {
  transform: translateY(-4px);
  box-shadow: 0 24px 48px color-mix(in oklab, var(--loom-color-ink) 12%, transparent);
}
.loom-pricing.loom-pricing--carded .loom-pricing__tier.is-highlighted {
  border-radius: 18px;
  box-shadow:
    0 0 0 1px var(--loom-color-primary) inset,
    0 24px 48px color-mix(in oklab, var(--loom-color-primary) 22%, transparent);
  transform: translateY(-6px);
}
.loom-pricing__badge {
  position: absolute;
  top: -0.7rem; left: 1.25rem;
  /* Default: flat solid label. Squared corners, single ink, no
   * gradient. The pill-shaped gradient "MOST POPULAR" badge is
   * opt-in via .loom-pricing--carded which restores the
   * rounded-pill + gradient look. */
  padding: 0.3rem 0.8rem;
  border-radius: 2px;
  font: 600 0.72rem/1 var(--loom-font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--loom-color-primary);
  color: var(--loom-color-primary-fg);
}
.loom-pricing.loom-pricing--carded .loom-pricing__badge {
  top: -0.7rem; left: 50%;
  transform: translateX(-50%);
  border-radius: 999px;
  background: linear-gradient(135deg, var(--loom-color-primary), var(--loom-color-accent));
}
.loom-pricing__name {
  margin: 0 0 0.75rem;
  font: 700 1.15rem/1 var(--loom-font-display);
  letter-spacing: -0.018em;
}
.loom-pricing__price-row {
  display: flex; align-items: baseline; gap: 0.4rem;
  margin-bottom: 0.5rem;
}
.loom-pricing__price {
  font: 900 2.5rem/1 var(--loom-font-display);
  letter-spacing: -0.035em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-pricing__period {
  font-size: 0.95rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
}
.loom-pricing__tagline {
  margin: 0 0 1.25rem;
  font-size: 0.92rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-pricing__features {
  list-style: none;
  padding: 0;
  margin: 1.25rem 0;
  display: flex; flex-direction: column;
  gap: 0.65rem;
}
.loom-pricing__feature {
  padding-left: 1.6rem;
  position: relative;
  font-size: 0.95rem;
  line-height: 1.45;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
.loom-pricing__feature::before {
  /* Default: typographic em-dash, not the green-checkmark trope.
   * The "✓" gloss is reserved for opt-in .loom-pricing--carded. */
  content: "—";
  position: absolute; left: 0; top: 0;
  font-weight: 700;
  color: color-mix(in oklab, var(--loom-color-ink) 50%, transparent);
}
.loom-pricing.loom-pricing--carded .loom-pricing__feature::before {
  content: "✓";
  color: var(--loom-color-primary);
}
.loom-pricing__cta {
  margin-top: auto;
  align-self: stretch;
}

/* ============================================================
 * Faq — accordion of questions
 * ============================================================ */
.loom-faq {
  padding: clamp(3rem, 5vw, 5rem) 0;
  max-width: 56rem;
  margin: 0 auto;
}
.loom-faq__heading {
  font: 800 clamp(1.75rem, 3.5vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  margin: 0 0 0.5rem;
  text-align: center;
}
.loom-faq__lede {
  margin: 0 auto 2.5rem;
  max-width: 55ch;
  text-align: center;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-faq__list {
  display: flex; flex-direction: column; gap: 0.65rem;
}
.loom-faq__item {
  border: 1px solid var(--loom-color-border, var(--loom-border));
  border-radius: 12px;
  background: var(--loom-color-surface, var(--loom-bg));
  overflow: hidden;
  transition: border-color var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-faq__item[open] {
  border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 40%, transparent);
}
.loom-faq__question {
  padding: 1.1rem 1.4rem;
  font: 600 1.02rem/1.35 var(--loom-font-display);
  letter-spacing: -0.015em;
  cursor: pointer;
  list-style: none;
  position: relative;
  padding-right: 3.5rem;
}
.loom-faq__question::-webkit-details-marker { display: none; }
.loom-faq__question::after {
  content: "+";
  position: absolute;
  right: 1.4rem; top: 50%;
  transform: translateY(-50%);
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
  font: 700 1.1rem/1 var(--loom-font-display);
  transition: transform var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease), background var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-faq__item[open] .loom-faq__question::after {
  content: "−";
  transform: translateY(-50%) rotate(180deg);
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, white);
}
.loom-faq__answer {
  padding: 0 1.4rem 1.2rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
  line-height: 1.55;
}
.loom-faq__paragraph { margin: 0 0 0.8rem; }
.loom-faq__paragraph:last-child { margin-bottom: 0; }

/* ============================================================
 * Marquee — full-bleed scrolling band
 * ============================================================ */
.loom-marquee {
  overflow: hidden;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
  background: color-mix(in oklab, var(--loom-color-surface, var(--loom-bg)) 96%, var(--loom-color-primary, var(--loom-accent)));
  padding: 1rem 0;
  mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
}
.loom-marquee__track {
  display: inline-flex;
  gap: 3rem;
  white-space: nowrap;
  will-change: transform;
}
.loom-marquee.marquee-left  .loom-marquee__track { animation: loom-marquee-left  40s linear infinite; }
.loom-marquee.marquee-right .loom-marquee__track { animation: loom-marquee-right 40s linear infinite; }
.loom-marquee[data-speed="1"]  .loom-marquee__track { animation-duration: 80s; }
.loom-marquee[data-speed="2"]  .loom-marquee__track { animation-duration: 65s; }
.loom-marquee[data-speed="3"]  .loom-marquee__track { animation-duration: 55s; }
.loom-marquee[data-speed="4"]  .loom-marquee__track { animation-duration: 48s; }
.loom-marquee[data-speed="5"]  .loom-marquee__track { animation-duration: 40s; }
.loom-marquee[data-speed="6"]  .loom-marquee__track { animation-duration: 32s; }
.loom-marquee[data-speed="7"]  .loom-marquee__track { animation-duration: 26s; }
.loom-marquee[data-speed="8"]  .loom-marquee__track { animation-duration: 22s; }
.loom-marquee[data-speed="9"]  .loom-marquee__track { animation-duration: 18s; }
.loom-marquee[data-speed="10"] .loom-marquee__track { animation-duration: 14s; }
@keyframes loom-marquee-left  { from { transform: translateX(0); }      to { transform: translateX(-50%); } }
@keyframes loom-marquee-right { from { transform: translateX(-50%); }   to { transform: translateX(0); } }
.loom-marquee__item {
  font: 700 1.15rem/1 var(--loom-font-display);
  letter-spacing: -0.02em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  .loom-marquee__track { animation: none !important; padding-left: 0; }
}

/* ============================================================
 * CallToAction — full-bleed CTA band
 * ============================================================ */
.loom-cta-band {
  display: grid;
  place-items: center;
  /* #572: vertical padding tenant-tunable via --loom-size-cta-py. The default
   * clamp is already generous (64-112px); a tenant matching a tighter source
   * CTA band (e.g. plausiden.com py-20 = 80px) dials it DOWN. Default = current
   * clamp (byte-identical for existing sites). */
  padding: var(--loom-size-cta-py, clamp(4rem, 8vw, 7rem)) clamp(1.5rem, 4vw, 3rem);
  text-align: center;
  position: relative; overflow: hidden;
  isolation: isolate;
}
/* Substrate-de-consumer-shaping (2026-05-20): opt-out from the
 * SaaS-marketing center default. Editorial sites set
 * `align: start` on the CallToAction variant; the renderer emits
 * data-align="start" and these rules take over. */
.loom-cta-band[data-align="start"] {
  place-items: start;
  text-align: start;
}
.loom-cta-band[data-align="start"] .loom-cta-band__inner {
  align-items: flex-start;
}
.loom-cta-band__inner {
  max-width: 56rem;
  display: flex; flex-direction: column;
  align-items: center;
  gap: 1.25rem;
}
.loom-cta-band__eyebrow {
  display: inline-block;
  padding: 0.4rem 0.95rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-surface, var(--loom-bg)) 80%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  font: 500 0.78rem/1 var(--loom-font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
}
.loom-cta-band__title {
  margin: 0;
  font: 900 clamp(1.75rem, 3vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.025em;
  color: var(--loom-color-ink, var(--loom-fg));
  max-width: 28ch;
}
.loom-cta-band__lede {
  margin: 0;
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
  max-width: 55ch;
  line-height: 1.5;
}

/* ============================================================
 * PullQuote — editorial large quote
 * ============================================================ */
.loom-pull-quote {
  margin: 3rem auto;
  max-width: 50rem;
  padding: 2rem;
  text-align: center;
  position: relative;
}
.loom-pull-quote::before {
  content: """;
  position: absolute;
  top: -1.5rem; left: 1rem;
  font: 900 7rem/1 var(--loom-font-display);
  color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 18%, transparent);
  line-height: 1;
  pointer-events: none;
}
.loom-pull-quote__body {
  margin: 0;
  font: italic 600 clamp(1.35rem, 2.5vw, 1.85rem)/1.35 var(--loom-font-display);
  letter-spacing: -0.018em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-pull-quote__attribution {
  margin-top: 1.25rem;
  font-size: 0.95rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
  font-weight: 500;
}

/* ============================================================
 * Epigraph — article-opening quote / motto / poem fragment
 * Distinct from PullQuote: smaller, narrower, italic but
 * understated; sits ABOVE the first paragraph, never breaks flow.
 * ============================================================ */
.loom-epigraph {
  margin: 2.5rem auto 2rem;
  max-width: 36rem;
  padding: 0 1.5rem;
  border-left: 2px solid color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 30%, transparent);
  text-align: left;
}
.loom-epigraph__body {
  margin: 0;
  font: italic 400 1.05rem/1.55 var(--loom-font-serif, var(--loom-font-display));
  letter-spacing: 0.005em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 78%, transparent);
}
.loom-epigraph__attribution {
  margin-top: 0.75rem;
  font: 500 0.85rem/1.3 var(--loom-font-sans, sans-serif);
  letter-spacing: 0.02em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent);
  font-style: normal;
}

/* ============================================================
 * AccountSummary — logged-in user at-a-glance card
 * ============================================================ */
.loom-account-summary {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1.5rem;
  align-items: center;
  padding: 1.5rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border) 60%, transparent);
  max-inline-size: 36rem;
}
.loom-account-summary__avatar { line-height: 0; }
.loom-account-summary__name {
  margin: 0;
  font: 800 1.4rem/1.1 var(--loom-font-display);
  letter-spacing: -0.018em;
  color: var(--loom-color-ink);
}
.loom-account-summary__handle {
  margin: 0.15rem 0 0;
  font: 500 0.92rem/1 var(--loom-font-mono);
  color: color-mix(in oklab, var(--loom-color-ink) 65%, transparent);
}
.loom-account-summary__meta {
  margin: 0.75rem 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 1rem;
}
.loom-account-summary__row { display: contents; }
.loom-account-summary__row > dt {
  font: 600 0.78rem/1.5 var(--loom-font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklab, var(--loom-color-ink) 60%, transparent);
}
.loom-account-summary__row > dd {
  margin: 0;
  font-size: 0.95rem;
  color: var(--loom-color-ink);
}

/* ============================================================
 * ProfileEdit — identity-facing form
 * ============================================================ */
.loom-profile-edit {
  padding: clamp(1.5rem, 3vw, 2.5rem) 0;
  max-inline-size: 36rem;
}
.loom-profile-edit__heading {
  margin: 0 0 1.5rem;
  font: 800 clamp(1.5rem, 3vw, 2rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.022em;
}
.loom-profile-edit__form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.loom-profile-edit__row {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.loom-profile-edit__row label {
  font-weight: 600;
  font-size: 0.92rem;
}
.loom-profile-edit__row input[type="text"],
.loom-profile-edit__row textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--loom-color-border);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  font: inherit;
}
.loom-profile-edit__submit { margin-top: 0.5rem; }

/* ============================================================
 * LegalDoc — ToS / Privacy / etc. typed layout
 * ============================================================ */
.loom-legal-doc {
  max-inline-size: 42rem;
  padding: clamp(2rem, 4vw, 3rem) 0;
}
.loom-legal-doc__header {
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border) 60%, transparent);
  padding-bottom: 1rem;
  margin-bottom: 2rem;
}
.loom-legal-doc__title {
  margin: 0 0 0.3rem;
  font: 800 clamp(1.75rem, 3.5vw, 2.5rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.022em;
}
.loom-legal-doc__updated {
  margin: 0;
  font: 500 0.85rem/1.5 var(--loom-font-mono);
  color: color-mix(in oklab, var(--loom-color-ink) 60%, transparent);
}
.loom-legal-doc__summary,
.loom-legal-doc__section-summary {
  padding: 0.85rem 1rem;
  border-inline-start: 3px solid color-mix(in oklab, var(--loom-color-primary) 50%, transparent);
  background: color-mix(in oklab, var(--loom-color-primary) 6%, var(--loom-color-surface));
  font-size: 0.92rem;
  line-height: 1.55;
  margin-block: 1rem;
}
.loom-legal-doc__toc {
  margin-bottom: 2rem;
  padding: 1rem 1.25rem;
  border: 1px dashed color-mix(in oklab, var(--loom-color-border) 70%, transparent);
}
.loom-legal-doc__toc-heading {
  margin: 0 0 0.5rem;
  font: 600 0.78rem/1 var(--loom-font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklab, var(--loom-color-ink) 70%, transparent);
}
.loom-legal-doc__toc-list {
  margin: 0;
  padding-inline-start: 1.2rem;
}
.loom-legal-doc__toc-list li { margin-block: 0.2rem; }
.loom-legal-doc__section { margin-block: 2rem; }
.loom-legal-doc__section-heading {
  font: 700 1.25rem/1.2 var(--loom-font-display);
  letter-spacing: -0.018em;
  margin: 0 0 0.5rem;
}
.loom-legal-doc__paragraph {
  margin: 0 0 0.85rem;
  line-height: 1.65;
}

/* ============================================================
 * SettingsPanel — typed user-preferences surface
 *
 * Server-rendered, no JS dependency. Renders as a stacked
 * fieldset-per-category with definition-list-style label +
 * control rows. Danger buttons get their own form so submit
 * doesn't conflate "Save" with "Delete account".
 * ============================================================ */
.loom-settings-panel {
  padding: clamp(2rem, 4vw, 3rem) 0;
  max-inline-size: 48rem;
}
.loom-settings-panel__heading {
  font: 800 clamp(1.5rem, 3vw, 2.25rem)/1.1 var(--loom-font-display);
  letter-spacing: -0.022em;
  margin: 0 0 0.5rem;
  color: var(--loom-color-ink);
}
.loom-settings-panel__lede {
  margin: 0 0 2rem;
  color: color-mix(in oklab, var(--loom-color-ink) 65%, transparent);
  max-inline-size: 56ch;
}
.loom-settings-panel__form {
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}
.loom-settings-category {
  border: 1px solid color-mix(in oklab, var(--loom-color-border) 65%, transparent);
  border-radius: 0;
  padding: 1rem 1.25rem 1.25rem;
  margin: 0;
}
.loom-settings-category__name {
  font: 600 0.78rem/1 var(--loom-font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: color-mix(in oklab, var(--loom-color-ink) 70%, transparent);
  padding: 0 0.5rem;
  margin: 0;
}
.loom-settings-category__items {
  margin: 0.75rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.loom-settings-item {
  display: grid;
  grid-template-columns: minmax(10rem, 14rem) 1fr;
  gap: 1rem;
  align-items: start;
  padding-block: 0.85rem;
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border) 50%, transparent);
}
.loom-settings-item:last-child { border-bottom: none; }
.loom-settings-item__label {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--loom-color-ink);
}
.loom-settings-item__label label { cursor: pointer; }
.loom-settings-item__hint {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.85rem;
  font-weight: 400;
  color: color-mix(in oklab, var(--loom-color-ink) 60%, transparent);
}
.loom-settings-item__control {
  margin: 0;
}
.loom-settings-item__control input[type="text"],
.loom-settings-item__control textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--loom-color-border);
  background: var(--loom-color-surface);
  color: var(--loom-color-ink);
  font: inherit;
}
.loom-settings-item__control input[type="checkbox"] {
  width: 1.1rem;
  height: 1.1rem;
}
.loom-settings-item__danger-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.loom-settings-item__danger-confirm {
  margin: 0;
  font-size: 0.88rem;
  color: color-mix(in oklab, var(--loom-color-ink) 70%, transparent);
}
.loom-btn--danger {
  background: var(--loom-color-danger, oklch(60% 0.22 25));
  color: var(--loom-color-on-accent, white);
  border-color: var(--loom-color-danger, oklch(60% 0.22 25));
}
.loom-settings-panel__submit {
  margin-top: 0.5rem;
}
@media (max-width: 640px) {
  .loom-settings-item {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }
}

/* ============================================================
 * Polish tokens — bounded per-element typographic nudges
 *
 * Each token is ONE step from default. Substrate-bound: no
 * polish CSS rule should produce more than a single named
 * adjustment, and stacking N tokens never exceeds N steps of
 * cumulative deviation. Authors who want stronger emphasis
 * pick a different primitive.
 *
 * Maps to `loom-polish--<slug>` class names emitted by the
 * typed PolishToken enum in loom-cms-render.
 * ============================================================ */
.loom-polish--tighter { letter-spacing: -0.025em; }
.loom-polish--looser  { letter-spacing: 0.04em; }
.loom-polish--taller  { line-height: 1.75; }
.loom-polish--shorter { line-height: 1.15; }
.loom-polish--bolder  { font-weight: 800; }
.loom-polish--lighter { font-weight: 300; }
.loom-polish--larger  { font-size: 1.125em; }
.loom-polish--smaller { font-size: 0.9em; }
.loom-polish--more-saturated {
  color: color-mix(in oklab, var(--loom-color-ink) 92%, var(--loom-color-primary));
}
.loom-polish--less-saturated {
  color: color-mix(in oklab, var(--loom-color-ink) 60%, transparent);
}

/* ============================================================
 * Marginalia — editorial sidenote
 *
 * Wide viewports (≥ 1024px): floats to the side of the body
 * column, bounded by the main#content gutter. Narrow viewports
 * (< 1024px): renders as a small offset block with a left rule,
 * inline with the surrounding prose.
 *
 * Used in long-form essays / academic publications / literary
 * press — anywhere the body wants commentary that doesn't break
 * the reading rhythm.
 * ============================================================ */
.loom-marginalia {
  display: block;
  margin-block: 1rem 0;
  padding: 0.6rem 0 0.6rem 0.9rem;
  border-inline-start: 2px solid color-mix(in oklab, var(--loom-color-primary) 55%, transparent);
  font-size: 0.9rem;
  line-height: 1.5;
  color: color-mix(in oklab, var(--loom-color-ink) 72%, transparent);
  max-inline-size: 28ch;
}
.loom-marginalia__body { display: block; }
@media (min-width: 1024px) {
  .loom-marginalia--right {
    float: inline-end;
    clear: inline-end;
    margin-inline-start: 1.5rem;
    margin-inline-end: -8rem;
  }
  .loom-marginalia--left {
    float: inline-start;
    clear: inline-start;
    margin-inline-end: 1.5rem;
    margin-inline-start: -8rem;
  }
}

/* ============================================================
 * T660 P5 — primitive catalogue v3 (101 new components)
 * Layout / Editorial / Marketing / Media / Commerce / Social
 * Forms / Navigation / Feedback / Game-Forum-Video
 * ============================================================ */

/* Layout */
.loom-container { margin: 0 auto; padding: 0 1.25rem; }
.loom-container.w-narrow      { max-width: 42rem; }
.loom-container.w-comfortable { max-width: 64rem; }
.loom-container.w-wide        { max-width: 90rem; }
.loom-container.w-full        { max-width: none; }
.loom-divider { border: 0; height: 1px; background: var(--loom-color-border, var(--loom-border)); margin: 2.5rem 0; }
.loom-divider.style-dots    { background: radial-gradient(circle, var(--loom-color-border) 1px, transparent 1.2px) 0 0/16px 4px repeat-x; height: 4px; background-color: transparent; }
.loom-divider.style-zigzag  { height: 8px; background: linear-gradient(135deg, var(--loom-color-border) 25%, transparent 25%) 0 0/8px 8px, linear-gradient(225deg, var(--loom-color-border) 25%, transparent 25%) 0 0/8px 8px; }
.loom-divider.style-sparkle { height: 12px; background: radial-gradient(circle, var(--loom-color-primary, var(--loom-accent)) 1px, transparent 1.4px) 0 0/24px 12px; }
.loom-spacer.size-tight       { height: 0.5rem; }
.loom-spacer.size-comfortable { height: 1.5rem; }
.loom-spacer.size-loose       { height: 3rem; }
.loom-spacer.size-generous    { height: 5rem; }
.loom-columns { display: grid; gap: 1.5rem; margin: 1.5rem 0; }
.loom-columns.cols-2 { grid-template-columns: repeat(2, 1fr); }
.loom-columns.cols-3 { grid-template-columns: repeat(3, 1fr); }
.loom-columns.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 700px) { .loom-columns { grid-template-columns: 1fr !important; } }
.loom-stack { display: flex; flex-direction: column; }
.loom-stack.size-tight       { gap: 0.5rem; }
.loom-stack.size-comfortable { gap: 1rem; }
.loom-stack.size-loose       { gap: 1.75rem; }
.loom-stack.size-generous    { gap: 3rem; }
.loom-cluster { display: flex; flex-wrap: wrap; align-items: center; }
.loom-cluster.size-tight       { gap: 0.4rem; }
.loom-cluster.size-comfortable { gap: 0.75rem; }
.loom-cluster.size-loose       { gap: 1.25rem; }
.loom-cluster.size-generous    { gap: 2rem; }
.loom-cluster__chip { padding: 0.3rem 0.7rem; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent); font-size: 0.85rem; }
.loom-grid { display: grid; gap: 1rem; }
.loom-grid.cols-1 { grid-template-columns: 1fr; }
.loom-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.loom-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.loom-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.loom-grid.cols-5 { grid-template-columns: repeat(5, 1fr); }
.loom-grid.cols-6 { grid-template-columns: repeat(6, 1fr); }
.loom-tabs__bar { display: flex; gap: 0.5rem; border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); margin-bottom: 1.5rem; }
.loom-tabs__tab { padding: 0.75rem 1.1rem; border: 0; background: transparent; cursor: pointer; font-weight: 500; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.loom-tabs__tab[aria-selected="true"] { color: var(--loom-color-primary, var(--loom-accent)); border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-tabs__pane[aria-hidden="true"] { display: none; }
.loom-accordion__item { border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-accordion__title { padding: 1rem 0; font-weight: 600; cursor: pointer; list-style: none; }
.loom-accordion__title::-webkit-details-marker { display: none; }
.loom-accordion__body { padding: 0 0 1.25rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent); }
.loom-reveal { transition: opacity 600ms var(--loom-ease-out, ease), transform 600ms var(--loom-ease-out, ease); }

/* Editorial */
.loom-article { max-width: 42rem; margin: 0 auto; font-size: 1.06rem; line-height: 1.7; }
.loom-subhead { margin-top: 2rem; font-family: var(--loom-font-display); letter-spacing: -0.02em; }
.loom-lede { font-size: 1.25rem; line-height: 1.55; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent); margin-bottom: 2rem; }
.loom-sublede {
  margin: -1rem 0 1.75rem;
  font: italic 400 1.05rem/1.5 var(--loom-font-serif, var(--loom-font-display));
  letter-spacing: 0.005em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-kicker {
  display: inline-block;
  margin-bottom: 0.5rem;
  padding: 0.18rem 0.55rem;
  font: 700 0.72rem/1 var(--loom-font-mono, ui-monospace, monospace);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--loom-color-primary, var(--loom-accent));
  border: 1px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 50%, transparent);
  border-radius: 2px;
}
.loom-byline {
  margin: 0 0 1.5rem;
  font: 500 0.92rem/1.5 var(--loom-font-sans, sans-serif);
  letter-spacing: 0.01em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-byline__author { font-weight: 700; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 90%, transparent); }
.loom-byline__role,
.loom-byline__dateline,
.loom-byline__reading-time { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); }
.loom-endnote {
  margin: 0.5rem 0;
  font: 400 0.92rem/1.5 var(--loom-font-serif, var(--loom-font-display));
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent);
  padding-left: 1.5rem;
  text-indent: -1.5rem;
}
.loom-endnote__num {
  font-weight: 700;
  font-family: var(--loom-font-mono, ui-monospace, monospace);
  color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 80%, transparent);
}
.loom-dropcap::first-letter { initial-letter: 3; -webkit-initial-letter: 3; font-family: var(--loom-font-display); font-weight: 900; color: var(--loom-color-primary, var(--loom-accent)); margin-right: 0.4rem; }
.loom-figure { margin: 2rem 0; }
.loom-figure__media {
  aspect-ratio: 16 / 9;
  border-radius: 12px;
  overflow: hidden;
  background: linear-gradient(135deg,
    color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 18%, transparent),
    color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 18%, transparent));
  display: grid;
  place-items: center;
}
.loom-figure__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.loom-figure__caption {
  margin-top: 0.6rem;
  font-size: 0.92rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
  font-style: italic;
  line-height: 1.45;
}
.loom-figure__credit {
  font-style: normal;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
}
.loom-figure__caption { margin-top: 0.75rem; font-size: 0.92rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
.loom-figure__credit { font-style: italic; }
.loom-caption { font-size: 0.92rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); margin-top: 0.5rem; }
.loom-footnote { font-size: 0.88rem; padding: 0.5rem 0; border-top: 1px dashed var(--loom-color-border, var(--loom-border)); }
.loom-footnote__num { font-weight: 700; color: var(--loom-color-primary, var(--loom-accent)); }
.loom-aside-note { padding: 1rem 1.25rem; border-radius: 10px; border-left: 4px solid var(--loom-color-primary, var(--loom-accent)); background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, var(--loom-color-surface, var(--loom-bg))); margin: 1.5rem 0; }
.loom-aside-note.tone-success { border-color: oklch(60% 0.18 150); background: color-mix(in oklab, oklch(60% 0.18 150) 8%, var(--loom-color-surface, var(--loom-bg))); }
.loom-aside-note.tone-warning { border-color: oklch(72% 0.16 75); background: color-mix(in oklab, oklch(72% 0.16 75) 8%, var(--loom-color-surface, var(--loom-bg))); }
.loom-aside-note.tone-danger  { border-color: oklch(60% 0.22 25); background: color-mix(in oklab, oklch(60% 0.22 25) 8%, var(--loom-color-surface, var(--loom-bg))); }
.loom-aside-note.tone-neutral { border-color: var(--loom-color-border, var(--loom-border)); }
.loom-deflist { display: grid; grid-template-columns: 12rem 1fr; gap: 0.75rem 1.5rem; margin: 1.5rem 0; }
.loom-deflist__term { font-weight: 700; color: var(--loom-color-ink, var(--loom-fg)); }
.loom-deflist__def { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent); }
@media (max-width: 600px) { .loom-deflist { grid-template-columns: 1fr; gap: 0.25rem 0; } .loom-deflist__def { margin-bottom: 0.75rem; } }
.loom-toc { padding: 1.25rem; border-radius: 12px; background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 4%, var(--loom-color-surface, var(--loom-bg))); margin: 1.5rem 0; }
.loom-toc__heading { font-weight: 700; margin: 0 0 0.5rem; }
.loom-toc__list { padding-left: 1.25rem; }
.loom-diagram { padding: 1.25rem; border-radius: 12px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-diagram__source { font-family: var(--loom-font-mono); font-size: 0.85rem; white-space: pre-wrap; }
.loom-math.display { padding: 1rem; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 4%, transparent); border-radius: 8px; text-align: center; font-family: var(--loom-font-serif); font-style: italic; font-size: 1.15rem; }
.loom-math.inline { font-family: var(--loom-font-serif); font-style: italic; }
.loom-citation { margin: 2rem 0; padding-left: 1.25rem; border-left: 3px solid var(--loom-color-primary, var(--loom-accent)); }
.loom-citation__text { font-style: italic; font-size: 1.05rem; line-height: 1.5; }
.loom-citation__source { font-size: 0.88rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
.loom-pull-stat { display: inline-flex; flex-direction: column; padding: 1rem 1.5rem; border-radius: 12px; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 14%, transparent)); }
.loom-pull-stat__value { font: 900 2rem/1 var(--loom-font-display); color: var(--loom-color-primary, var(--loom-accent)); }
.loom-pull-stat__label { font-size: 0.92rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent); }

/* Marketing extras */
.loom-testimonial { padding: 2rem; border-radius: 16px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); max-width: 36rem; margin: 1.5rem auto; }
.loom-testimonial__body { font: italic 500 1.15rem/1.55 var(--loom-font-display); color: var(--loom-color-ink, var(--loom-fg)); }
.loom-testimonial__author { display: flex; align-items: center; gap: 0.75rem; margin-top: 1.25rem; }
.loom-testimonial__avatar { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, var(--loom-color-primary, var(--loom-accent)), var(--loom-color-accent-2, var(--loom-accent-2))); }
.loom-testimonial__name { font-weight: 700; }
.loom-testimonial__role { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
/* LogoCloud — default left-aligned + flex-start. Centered
 * opt-in via .loom-logo-cloud--centered on the outer element. */
.loom-logo-cloud { padding: 2rem 0; text-align: start; }
.loom-logo-cloud__heading { font-size: 0.92rem; letter-spacing: 0.1em; text-transform: uppercase; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); margin-bottom: 1.25rem; }
.loom-logo-cloud__row { display: flex; flex-wrap: wrap; gap: 2rem; justify-content: flex-start; align-items: center; }
.loom-logo-cloud.loom-logo-cloud--centered { text-align: center; }
.loom-logo-cloud.loom-logo-cloud--centered .loom-logo-cloud__row { justify-content: center; }
.loom-logo-cloud__item { font: 700 1.1rem/1 var(--loom-font-display); letter-spacing: -0.018em; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); transition: color 200ms; }
.loom-logo-cloud__item:hover { color: var(--loom-color-ink, var(--loom-fg)); }
.loom-comparison__table { width: 100%; border-collapse: collapse; }
.loom-comparison__table th, .loom-comparison__table td { padding: 0.85rem 1rem; text-align: left; border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-comparison__table th { font-weight: 700; }
.loom-timeline__list { list-style: none; padding: 0; position: relative; }
.loom-timeline__list::before { content: ""; position: absolute; left: 12px; top: 0; bottom: 0; width: 2px; background: var(--loom-color-border, var(--loom-border)); }
.loom-timeline__item { padding-left: 2.5rem; position: relative; margin-bottom: 1.5rem; }
.loom-timeline__item::before { content: ""; position: absolute; left: 6px; top: 0.5rem; width: 14px; height: 14px; border-radius: 50%; background: var(--loom-color-primary, var(--loom-accent)); box-shadow: 0 0 0 4px var(--loom-color-surface, var(--loom-bg)), 0 0 0 5px var(--loom-color-primary, var(--loom-accent)); }
.loom-timeline__when { font-size: 0.85rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-timeline__title { margin: 0.25rem 0; font: 700 1.05rem/1.2 var(--loom-font-display); }
.loom-roadmap { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; margin: 2rem 0; }
@media (max-width: 800px) { .loom-roadmap { grid-template-columns: 1fr; } }
.loom-roadmap__col { padding: 1.5rem; border-radius: 12px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); }
.loom-roadmap__col.col-now { border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-roadmap__heading { font: 700 0.85rem/1 var(--loom-font-mono); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 1rem; color: var(--loom-color-primary, var(--loom-accent)); }
.loom-case-study { padding: 2rem; border-radius: 16px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-case-study__headline { margin: 0 0 1rem; font: 800 1.5rem/1.2 var(--loom-font-display); letter-spacing: -0.022em; }
.loom-case-study__metrics { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 1.25rem 0; }
.loom-case-study__metric-value { display: block; font: 900 2rem/1 var(--loom-font-display); color: var(--loom-color-primary, var(--loom-accent)); }
.loom-case-study__metric-label { font-size: 0.88rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
.loom-announcement-bar { display: flex; gap: 1rem; justify-content: center; align-items: center; padding: 0.75rem 1.5rem; font-size: 0.95rem; }
.loom-announcement-bar.tone-info     { background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, var(--loom-color-surface, var(--loom-bg))); color: var(--loom-color-ink, var(--loom-fg)); }
.loom-announcement-bar.tone-success  { background: color-mix(in oklab, oklch(60% 0.18 150) 18%, var(--loom-color-surface, var(--loom-bg))); }
.loom-announcement-bar.tone-warning  { background: color-mix(in oklab, oklch(72% 0.16 75) 18%, var(--loom-color-surface, var(--loom-bg))); }
.loom-announcement-bar.tone-danger   { background: color-mix(in oklab, oklch(60% 0.22 25) 18%, var(--loom-color-surface, var(--loom-bg))); color: white; }
.loom-announcement-bar.tone-neutral  { background: var(--loom-color-surface, var(--loom-bg)); border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-cookie-notice { position: fixed; bottom: 1rem; left: 1rem; right: 1rem; max-width: 32rem; margin: 0 auto; padding: 1.25rem; border-radius: 14px; background: var(--loom-color-ink, oklch(20% 0.02 280)); color: var(--loom-color-surface, white); box-shadow: 0 24px 60px oklch(0% 0 0 / 0.4); z-index: 100; }
.loom-cookie-notice__text { margin: 0 0 1rem; font-size: 0.92rem; line-height: 1.5; }
.loom-cookie-notice__actions { display: flex; gap: 0.5rem; }
.loom-promo-strip { display: flex; gap: 1rem; align-items: center; justify-content: space-between; padding: 1rem 1.5rem; border-radius: 12px; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, var(--loom-color-surface, var(--loom-bg))), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 10%, var(--loom-color-surface, var(--loom-bg)))); margin: 1.5rem 0; }
.loom-award-badges__list { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 1rem; }
.loom-award-badges__item { padding: 0.5rem 1rem; border-radius: 999px; border: 1px solid var(--loom-color-border, var(--loom-border)); font-weight: 600; font-size: 0.92rem; }
/* Newsletter signup — substrate default is editorial. The
 * gradient-card "Join our mailing list" trope is opt-in via
 * `.loom-newsletter-signup--carded` on the outer element.
 * Substrate-de-consumer-shaping PRIORITY 6: the default shape
 * was a centered gradient-card with pill-rounded inputs (the
 * textbook mailing-list trope). Default is now flush-left,
 * no chrome, no gradient — typography only.
 */
.loom-newsletter-signup {
  padding: 1.5rem 0;
  max-width: 42rem;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
}
.loom-newsletter-signup__heading { margin: 0 0 0.5rem; font: 700 1.4rem/1.2 var(--loom-font-display); letter-spacing: -0.02em; }
.loom-newsletter-signup__form { display: flex; gap: 0.5rem; max-width: 32rem; margin: 1.25rem 0 0; }
.loom-newsletter-signup__form input { flex: 1; padding: 0.6rem 0.85rem; border-radius: 4px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); }
/* Opt-in carded variant — restores the legacy trope shape. */
.loom-newsletter-signup.loom-newsletter-signup--carded {
  padding: 2.5rem;
  border: 0;
  border-radius: 16px;
  text-align: center;
  background: linear-gradient(135deg,
    color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, var(--loom-color-surface, var(--loom-bg))),
    color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 8%, var(--loom-color-surface, var(--loom-bg))));
}
.loom-newsletter-signup.loom-newsletter-signup--carded .loom-newsletter-signup__form { margin: 1.25rem auto 0; }
.loom-newsletter-signup.loom-newsletter-signup--carded .loom-newsletter-signup__form input { border-radius: 999px; padding: 0.75rem 1rem; }
.loom-contact-strip { display: flex; flex-wrap: wrap; gap: 1rem; padding: 1.5rem 0; justify-content: center; }
.loom-contact-strip__item { padding: 0.6rem 1.1rem; border-radius: 999px; border: 1px solid var(--loom-color-border, var(--loom-border)); text-decoration: none; color: var(--loom-color-ink, var(--loom-fg)); }
.loom-contact-strip__item:hover { background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, transparent); }

/* Media */
.loom-image-grid, .loom-figure-group, .loom-product-gallery { display: grid; gap: 1rem; margin: 1.5rem 0; }
.loom-image-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.loom-image-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.loom-image-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.loom-image-grid.cols-5 { grid-template-columns: repeat(5, 1fr); }
.loom-image-grid.cols-6 { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 700px) {
  .loom-image-grid, .loom-figure-group, .loom-product-gallery, .loom-mosaic { grid-template-columns: repeat(2, 1fr) !important; }
}
.loom-image-grid__cell, .loom-figure-group__cell, .loom-product-gallery__cell {
  aspect-ratio: 4 / 3;
  border-radius: 12px;
  background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 20%, transparent));
  display: grid; place-items: center;
  overflow: hidden;
}
.loom-image-grid__caption { padding: 0.5rem; font-size: 0.85rem; }
.loom-mosaic { display: grid; gap: 1rem; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 120px; grid-auto-flow: dense; }
.loom-mosaic__cell { background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 20%, transparent)); border-radius: 8px; display: grid; place-items: center; }
.loom-mosaic__cell:nth-child(3n) { grid-row: span 2; }
.loom-mosaic__cell:nth-child(5n) { grid-column: span 2; }
.loom-video-embed, .loom-audio-embed { margin: 1.5rem 0; }
.loom-video-embed video, .loom-audio-embed audio { width: 100%; border-radius: 12px; }
.loom-slideshow { position: relative; aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden; }
.loom-slideshow__slide { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; transition: opacity 600ms; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 30%, transparent)); }
.loom-slideshow__slide[data-active="true"] { opacity: 1; }
.loom-before-after { position: relative; aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden; }
.loom-before-after figure { position: absolute; inset: 0; margin: 0; display: grid; place-items: center; }
.loom-before-after__after { clip-path: inset(0 0 0 50%); background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 30%, transparent), color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent)); }
.loom-before-after__slider { position: absolute; left: 0; right: 0; bottom: 1rem; }
.loom-lightbox { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.loom-lightbox__thumb { width: 6rem; height: 6rem; border-radius: 8px; border: 0; cursor: pointer; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 20%, transparent)); }
.loom-icon-row { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.loom-icon-row__icon { width: 28px; height: 28px; display: grid; place-items: center; color: var(--loom-color-primary, var(--loom-accent)); }
.loom-badge-grid { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.loom-badge-grid__item { display: inline-flex; gap: 0.4rem; align-items: center; padding: 0.4rem 0.85rem; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent); font-size: 0.88rem; }

/* Commerce */
.loom-product-card { background: var(--loom-color-surface, var(--loom-bg)); border-radius: 14px; overflow: hidden; border: 1px solid var(--loom-color-border, var(--loom-border)); transition: transform 200ms, box-shadow 200ms; }
.loom-product-card:hover { transform: translateY(-3px); box-shadow: 0 20px 40px color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 14%, transparent); }
.loom-product-card__link { display: block; text-decoration: none; color: inherit; padding: 1rem; }
.loom-product-card__image { aspect-ratio: 4 / 3; border-radius: 10px; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 30%, transparent)); display: grid; place-items: center; margin: 0 0 0.85rem; }
.loom-product-card__name { margin: 0 0 0.4rem; font: 700 1rem/1.25 var(--loom-font-display); }
.loom-product-card__price { font: 700 1.05rem/1 var(--loom-font-display); color: var(--loom-color-primary, var(--loom-accent)); }
.loom-product-card__rating { display: flex; gap: 0.05rem; margin-top: 0.5rem; }
.loom-star { color: oklch(72% 0.16 75); font-size: 0.95rem; }
.loom-star.empty { color: color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 80%, transparent); }
.loom-product-grid__row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr)); }
.loom-price-tag { display: inline-flex; align-items: baseline; gap: 0.2rem; }
.loom-price-tag__was { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); }
.loom-cart-drawer, .loom-wishlist { background: transparent; border: 0; padding: 0.5rem; position: relative; cursor: pointer; font-size: 1.5rem; }
.loom-cart-drawer__badge, .loom-wishlist__count { position: absolute; top: 0; right: 0; min-width: 18px; height: 18px; padding: 0 4px; border-radius: 999px; background: var(--loom-color-primary, var(--loom-accent)); color: white; font-size: 0.7rem; line-height: 18px; text-align: center; font-weight: 700; }
.loom-review-card { padding: 1.25rem; border-radius: 12px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); margin: 1rem 0; }
.loom-review-card__header { display: flex; justify-content: space-between; margin-bottom: 0.5rem; }
.loom-review-card__author { font-weight: 700; }
.loom-review-card__date { font-size: 0.85rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-product-spec { display: grid; grid-template-columns: 10rem 1fr; gap: 0.5rem 1.5rem; margin: 1rem 0; }
.loom-product-spec__term { font-weight: 700; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 75%, transparent); }
.loom-review-stars__count { font-size: 0.88rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }

/* Social */
.loom-avatar { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, var(--loom-color-primary, var(--loom-accent)), var(--loom-color-accent-2, var(--loom-accent-2))); color: white; font-weight: 700; display: inline-grid; place-items: center; font-size: 0.85rem; }
.loom-avatar[data-kind="image"] { object-fit: cover; }
.loom-avatar-section { display: inline-flex; align-items: center; gap: 0.5rem; }
.loom-avatar-stack { display: inline-flex; }
.loom-avatar-stack .loom-avatar { margin-left: -0.6rem; border: 2px solid var(--loom-color-surface, var(--loom-bg)); }
.loom-avatar-stack .loom-avatar:first-child { margin-left: 0; }
.loom-avatar-stack__more { margin-left: -0.6rem; width: 40px; height: 40px; border-radius: 50%; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 14%, transparent); color: var(--loom-color-ink, var(--loom-fg)); display: inline-grid; place-items: center; font-weight: 700; border: 2px solid var(--loom-color-surface, var(--loom-bg)); font-size: 0.85rem; }
.loom-chat-bubble { max-width: 70%; padding: 0.7rem 0.95rem; border-radius: 14px; margin: 0.4rem 0; }
.loom-chat-bubble.mine   { background: var(--loom-color-primary, var(--loom-accent)); color: white; margin-left: auto; border-bottom-right-radius: 4px; }
.loom-chat-bubble.theirs { background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent); color: var(--loom-color-ink, var(--loom-fg)); border-bottom-left-radius: 4px; }
.loom-chat-bubble__author { display: block; font-size: 0.78rem; opacity: 0.7; }
.loom-chat-bubble__body { margin: 0.25rem 0 0; }
.loom-chat-thread { display: flex; flex-direction: column; }
.loom-reaction-row { display: flex; gap: 0.4rem; }
.loom-reaction-row__item { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.6rem; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent); border: 0; cursor: pointer; font-size: 0.85rem; }
.loom-mention, .loom-hashtag { color: var(--loom-color-primary, var(--loom-accent)); font-weight: 600; text-decoration: none; }
.loom-mention:hover, .loom-hashtag:hover { text-decoration: underline; }
.loom-share-row { display: flex; gap: 0.4rem; }
/* 44x44 tap target — WCAG 2.5.5 AAA + Apple HIG + Material Design. */
.loom-share-row__btn { width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); cursor: pointer; font-size: 1rem; }
.loom-follow-btn { gap: 0.4rem; }
.loom-follow-btn__count { font-weight: 500; opacity: 0.85; }
.loom-profile-card { padding: 1.5rem; border-radius: 14px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); text-align: center; }
.loom-profile-card .loom-avatar { width: 64px; height: 64px; margin: 0 auto 0.75rem; font-size: 1.1rem; }
.loom-profile-card__name { margin: 0 0 0.25rem; font: 800 1.1rem/1.2 var(--loom-font-display); }
.loom-profile-card__handle { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); margin: 0 0 0.85rem; font-size: 0.92rem; }
.loom-profile-card__bio { margin: 0 0 1rem; }

/* Forms */
.loom-form-input, .loom-form-select, .loom-form-date, .loom-form-file, .loom-form-color, .loom-form-textarea, .loom-form-slider { display: flex; flex-direction: column; gap: 0.4rem; margin: 0.75rem 0; }
.loom-form-input__label, .loom-form-select__label, .loom-form-date__label, .loom-form-file__label, .loom-form-color__label, .loom-form-textarea__label, .loom-form-slider__label { font-size: 0.9rem; font-weight: 600; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent); }
.loom-form-input input, .loom-form-select select, .loom-form-date input, .loom-form-file input, .loom-form-color input, .loom-form-textarea textarea { padding: 0.65rem 0.85rem; border-radius: 10px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); color: var(--loom-color-ink, var(--loom-fg)); font: inherit; min-height: 44px; }
.loom-form-input input:focus-visible, .loom-form-select select:focus-visible, .loom-form-date input:focus-visible, .loom-form-textarea textarea:focus-visible { outline: 2px solid var(--loom-color-primary, var(--loom-accent)); outline-offset: 2px; border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-form-toggle { display: inline-flex; align-items: center; gap: 0.6rem; cursor: pointer; margin: 0.75rem 0; }
.loom-form-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.loom-form-toggle__track { width: 44px; height: 24px; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 14%, transparent); position: relative; transition: background 200ms; }
.loom-form-toggle__track::after { content: ""; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%; background: white; transition: transform 200ms; box-shadow: 0 1px 3px oklch(0% 0 0 / 0.2); }
.loom-form-toggle input:checked + .loom-form-toggle__track { background: var(--loom-color-primary, var(--loom-accent)); }
.loom-form-toggle input:checked + .loom-form-toggle__track::after { transform: translateX(20px); }
.loom-form-search { display: flex; gap: 0.5rem; }
.loom-form-search input { flex: 1; padding: 0.65rem 1rem; border-radius: 999px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); }

/* Navigation */
.loom-breadcrumb { font-size: 0.92rem; }
.loom-breadcrumb__list { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.loom-breadcrumb__sep { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent); }
.loom-pagination { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.loom-pagination__page { padding: 0.5rem 0.85rem; border-radius: 8px; min-width: 40px; text-align: center; text-decoration: none; color: var(--loom-color-ink, var(--loom-fg)); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-pagination__page.current { background: var(--loom-color-primary, var(--loom-accent)); color: white; border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-nav-tabs { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-nav-tabs__tab { padding: 0.65rem 1rem; text-decoration: none; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.loom-nav-tabs__tab.current { color: var(--loom-color-primary, var(--loom-accent)); border-color: var(--loom-color-primary, var(--loom-accent)); font-weight: 600; }
.loom-vertical-nav { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.5rem; border-radius: 12px; }
.loom-vertical-nav__item { padding: 0.5rem 0.85rem; border-radius: 8px; text-decoration: none; color: var(--loom-color-ink, var(--loom-fg)); }
.loom-vertical-nav__item:hover { background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent); }
.loom-vertical-nav__item.current { background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, transparent); color: var(--loom-color-primary, var(--loom-accent)); font-weight: 600; }
.loom-mega-menu { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 1.5rem; padding: 1.5rem; background: var(--loom-color-surface, var(--loom-bg)); border-radius: 14px; box-shadow: 0 24px 60px oklch(0% 0 0 / 0.1); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-mega-menu__heading { margin: 0 0 0.5rem; font-weight: 700; font-size: 0.92rem; letter-spacing: 0.05em; text-transform: uppercase; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent); }
.loom-back-to-top { position: fixed; bottom: 1.5rem; right: 1.5rem; width: 44px; height: 44px; border-radius: 50%; background: var(--loom-color-primary, var(--loom-accent)); color: white; display: grid; place-items: center; text-decoration: none; box-shadow: 0 8px 20px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 40%, transparent); z-index: 50; }
.loom-anchor-list ol { list-style: decimal-leading-zero; padding-left: 2rem; }
.loom-lang-switch { display: inline-flex; gap: 0.4rem; align-items: center; font-size: 0.88rem; }

/* Feedback */
.loom-alert { padding: 1rem 1.25rem; border-radius: 10px; border-left: 4px solid var(--loom-color-primary, var(--loom-accent)); background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, var(--loom-color-surface, var(--loom-bg))); margin: 1rem 0; position: relative; }
.loom-alert.tone-success { border-color: oklch(60% 0.18 150); background: color-mix(in oklab, oklch(60% 0.18 150) 10%, var(--loom-color-surface, var(--loom-bg))); }
.loom-alert.tone-warning { border-color: oklch(72% 0.16 75); background: color-mix(in oklab, oklch(72% 0.16 75) 10%, var(--loom-color-surface, var(--loom-bg))); }
.loom-alert.tone-danger  { border-color: oklch(60% 0.22 25); background: color-mix(in oklab, oklch(60% 0.22 25) 10%, var(--loom-color-surface, var(--loom-bg))); }
.loom-alert.tone-neutral { border-color: var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); }
.loom-alert__title { display: block; margin-bottom: 0.25rem; }
.loom-alert__dismiss { position: absolute; top: 0.5rem; right: 0.75rem; border: 0; background: transparent; cursor: pointer; font-size: 1.2rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-toast { padding: 0.75rem 1.25rem; border-radius: 10px; background: var(--loom-color-ink, oklch(20% 0.02 280)); color: var(--loom-color-surface, white); box-shadow: 0 16px 40px oklch(0% 0 0 / 0.3); }
.loom-toast.tone-success { background: oklch(50% 0.18 150); }
.loom-toast.tone-warning { background: oklch(60% 0.16 75); }
.loom-toast.tone-danger  { background: oklch(50% 0.22 25); }
.loom-modal { padding: 0; border: 0; max-width: 32rem; border-radius: 14px; background: var(--loom-color-surface, var(--loom-bg)); }
.loom-modal[open] { padding: 1.75rem; }
.loom-modal__title { margin: 0 0 0.75rem; font: 800 1.25rem/1.2 var(--loom-font-display); letter-spacing: -0.018em; }
.loom-modal__actions { display: flex; gap: 0.5rem; margin-top: 1.25rem; justify-content: flex-end; }
.loom-drawer { position: fixed; top: 0; bottom: 0; width: min(24rem, 90vw); background: var(--loom-color-surface, var(--loom-bg)); padding: 1.5rem; box-shadow: 0 24px 60px oklch(0% 0 0 / 0.3); z-index: 50; }
.loom-drawer.side-right { right: 0; }
.loom-drawer.side-left  { left: 0; }
.loom-drawer__header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
.loom-drawer__close { border: 0; background: transparent; cursor: pointer; font-size: 1.5rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
.loom-tooltip { position: relative; display: inline-block; }
.loom-tooltip__trigger { border-bottom: 1px dashed var(--loom-color-border, var(--loom-border)); cursor: help; }
.loom-tooltip__body { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); margin-top: 0.5rem; padding: 0.5rem 0.85rem; border-radius: 8px; background: var(--loom-color-ink, oklch(20% 0.02 280)); color: var(--loom-color-surface, white); font-size: 0.85rem; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 200ms; z-index: 10; }
.loom-tooltip:hover .loom-tooltip__body, .loom-tooltip__trigger:focus + .loom-tooltip__body { opacity: 1; }
.loom-progress__track { height: 8px; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 10%, transparent); overflow: hidden; }
.loom-progress__fill { height: 100%; width: var(--loom-progress-val); background: linear-gradient(90deg, var(--loom-color-primary, var(--loom-accent)), var(--loom-color-accent-2, var(--loom-accent-2))); transition: width 600ms var(--loom-ease-out, ease); }
.loom-progress__label { display: block; margin-bottom: 0.3rem; font-size: 0.85rem; }
.loom-skeleton__row { height: 1rem; margin-bottom: 0.75rem; border-radius: 6px; background: linear-gradient(90deg, color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent), color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 16%, transparent), color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent)); background-size: 200% 100%; animation: loom-skeleton-pulse 1.5s ease-in-out infinite; }
.loom-skeleton.size-tight .loom-skeleton__row       { height: 0.7rem; }
.loom-skeleton.size-loose .loom-skeleton__row       { height: 1.5rem; }
.loom-skeleton.size-generous .loom-skeleton__row    { height: 2rem; }
@keyframes loom-skeleton-pulse { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
.loom-empty-state { text-align: center; padding: 3rem 1.5rem; }
.loom-empty-state__title { font: 800 1.5rem/1.2 var(--loom-font-display); letter-spacing: -0.022em; margin: 0 0 0.5rem; }
.loom-empty-state__body { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); max-width: 36rem; margin: 0 auto 1.5rem; }

/* Game / Forum / Video */
.loom-game-tile { position: relative; border-radius: 14px; overflow: hidden; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); transition: transform 220ms, box-shadow 220ms; }
.loom-game-tile:hover { transform: translateY(-3px); box-shadow: 0 20px 40px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 24%, transparent); border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent); }
.loom-game-tile__link { display: block; text-decoration: none; color: inherit; padding: 0.85rem; }
.loom-game-tile__thumb { aspect-ratio: 4 / 3; border-radius: 10px; background: linear-gradient(135deg, oklch(40% 0.15 280), oklch(60% 0.22 290)); display: grid; place-items: center; color: white; margin-bottom: 0.75rem; font: 800 1rem/1 var(--loom-font-display); }
.loom-game-tile__title { margin: 0 0 0.3rem; font: 700 0.95rem/1.2 var(--loom-font-display); }
.loom-game-tile__genre { font-size: 0.78rem; padding: 0.15rem 0.5rem; border-radius: 999px; background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, transparent); color: var(--loom-color-primary, var(--loom-accent)); margin-right: 0.4rem; }
.loom-game-tile__online { font-size: 0.78rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-game-grid__row { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr)); }
.loom-thread-row { padding: 1rem 1.25rem; border-bottom: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-thread-row__link { text-decoration: none; color: inherit; display: block; }
.loom-thread-row__link:hover { color: var(--loom-color-primary, var(--loom-accent)); }
.loom-thread-row__title { margin: 0 0 0.25rem; font: 600 1rem/1.3 var(--loom-font-display); }
.loom-thread-row__author { margin: 0; font-size: 0.85rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-video-card { border-radius: 12px; overflow: hidden; background: var(--loom-color-surface, var(--loom-bg)); }
.loom-video-card__link { display: block; text-decoration: none; color: inherit; }
.loom-video-card__thumb { aspect-ratio: 16 / 9; position: relative; background: linear-gradient(135deg, oklch(35% 0.1 250), oklch(15% 0.05 280)); border-radius: 12px; margin-bottom: 0.6rem; display: grid; place-items: center; color: white; font-weight: 700; }
.loom-video-card__duration { position: absolute; right: 0.4rem; bottom: 0.4rem; padding: 0.15rem 0.4rem; background: oklch(0% 0 0 / 0.85); color: white; border-radius: 4px; font: 500 0.75rem var(--loom-font-mono); }
.loom-video-card__title { margin: 0 0.5rem 0.25rem; font: 600 0.95rem/1.3 var(--loom-font-display); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.loom-video-card__meta { margin: 0 0.5rem 0.6rem; font-size: 0.82rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-video-grid__row { display: grid; gap: 1.25rem 1rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); }
.loom-comment { padding: 0.75rem 0; border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent); }
.loom-comment__author { font-weight: 700; }
.loom-comment__at { font-size: 0.82rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }
.loom-feed-post { padding: 1.25rem; border-radius: 12px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); margin: 0.75rem 0; }
.loom-feed-post__header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; font-size: 0.92rem; }
.loom-feed-post__author { font-weight: 700; }
.loom-feed-post__handle { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); }
.loom-feed-post__at { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); }
.loom-feed-post__body { margin: 0 0 0.85rem; }
.loom-feed-post__footer { font-size: 0.85rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }

/* ============================================================
 * T660 P6 — auth + Crucible widget primitives
 * AuthCard / MfaPrompt / CrucibleWidget / AuthFlowStepper /
 * SignedInCard / PasswordReset
 * ============================================================ */

.loom-auth-card {
  max-width: 26rem;
  margin: 2rem auto;
  padding: 2rem;
  border-radius: 16px;
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid var(--loom-color-border, var(--loom-border));
  box-shadow: var(--loom-shadow-lg);
}
.loom-auth-card__header { text-align: center; margin-bottom: 1.5rem; }
.loom-auth-card__title  { margin: 0 0 0.5rem; font: 800 1.5rem/1.2 var(--loom-font-display); letter-spacing: -0.022em; }
.loom-auth-card__tagline{ margin: 0; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent); }
.loom-auth-card__methods{ display: flex; flex-direction: column; gap: 0.6rem; }
.loom-auth-card__footer { margin-top: 1.25rem; font-size: 0.82rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); text-align: center; }

.loom-auth-method { width: 100%; }
.loom-auth-method__icon { width: 22px; display: inline-grid; place-items: center; }
.loom-auth-method--social.provider-github   .loom-auth-method__icon::before { content: "🐙"; }
.loom-auth-method--social.provider-google   .loom-auth-method__icon::before { content: "G"; font-weight: 900; }
.loom-auth-method--social.provider-apple    .loom-auth-method__icon::before { content: ""; }
.loom-auth-method--social.provider-microsoft .loom-auth-method__icon::before { content: "▤"; }
.loom-auth-method--magic-link, .loom-auth-method--sms-otp, .loom-auth-method--password, .loom-auth-method--sso { display: flex; flex-direction: column; gap: 0.5rem; }
.loom-auth-method input { padding: 0.7rem 0.9rem; border-radius: 10px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); min-height: 44px; font: inherit; }
.loom-auth-method input:focus-visible { outline: 2px solid var(--loom-color-primary, var(--loom-accent)); outline-offset: 2px; border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-auth-method__row { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; }
.loom-auth-method__forgot { font-size: 0.85rem; color: var(--loom-color-primary, var(--loom-accent)); text-decoration: none; }

.loom-auth-method-divider { display: flex; align-items: center; gap: 0.75rem; margin: 0.5rem 0; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); font-size: 0.85rem; }
.loom-auth-method-divider__line { flex: 1; height: 1px; background: var(--loom-color-border, var(--loom-border)); }

.loom-mfa-prompt {
  max-width: 26rem; margin: 2rem auto; padding: 2rem;
  border-radius: 16px;
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid var(--loom-color-border, var(--loom-border));
  box-shadow: var(--loom-shadow-lg); text-align: center;
}
.loom-mfa-prompt__title       { margin: 0 0 0.5rem; font: 800 1.4rem/1.2 var(--loom-font-display); letter-spacing: -0.022em; }
.loom-mfa-prompt__instructions{ color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent); margin-bottom: 1.5rem; }
.loom-mfa-prompt__otp { display: flex; gap: 0.5rem; justify-content: center; margin-bottom: 1.25rem; }
.loom-mfa-prompt__digit {
  width: 3rem; height: 3.5rem;
  text-align: center; font: 700 1.5rem/1 var(--loom-font-mono);
  border-radius: 10px; border: 2px solid var(--loom-color-border, var(--loom-border));
  background: var(--loom-color-surface, var(--loom-bg)); color: var(--loom-color-ink, var(--loom-fg));
}
.loom-mfa-prompt__digit:focus-visible { outline: 0; border-color: var(--loom-color-primary, var(--loom-accent)); box-shadow: 0 0 0 4px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent); }
.loom-mfa-prompt__webauthn { display: inline-flex; align-items: center; gap: 0.6rem; padding: 1rem 1.5rem; }
.loom-mfa-prompt__submit   { width: 100%; }
.loom-mfa-prompt__switch   { display: block; margin-top: 0.75rem; width: 100%; font-size: 0.88rem; }

.loom-crucible {
  max-width: 32rem; margin: 2rem auto; padding: 1.5rem;
  border-radius: 16px;
  background: var(--loom-color-surface, var(--loom-bg));
  border: 1px solid var(--loom-color-border, var(--loom-border));
  box-shadow: var(--loom-shadow-lg);
}
.loom-crucible__header { margin-bottom: 1.25rem; }
.loom-crucible__badge { display: inline-block; padding: 0.25rem 0.7rem; border-radius: 999px; font: 700 0.7rem/1 var(--loom-font-mono); letter-spacing: 0.08em; text-transform: uppercase; background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, transparent); color: var(--loom-color-primary, var(--loom-accent)); }
.loom-crucible.difficulty-medium      .loom-crucible__badge { background: color-mix(in oklab, oklch(72% 0.16 75) 18%, transparent); color: oklch(50% 0.18 75); }
.loom-crucible.difficulty-hard        .loom-crucible__badge { background: color-mix(in oklab, oklch(60% 0.22 25) 18%, transparent); color: oklch(40% 0.22 25); }
.loom-crucible.difficulty-adversarial .loom-crucible__badge { background: oklch(0% 0 0); color: oklch(98% 0 0); }
.loom-crucible__prompt { margin: 0.5rem 0 0; font: 600 1.05rem/1.4 var(--loom-font-display); letter-spacing: -0.018em; }
.loom-crucible__options {
  display: grid; gap: 0.5rem;
  grid-template-columns: repeat(3, 1fr); /* default 3-column grid; image-classify n=9 fits cleanly */
  margin-bottom: 1.25rem;
}
.loom-crucible.kind-semantic-similarity .loom-crucible__options,
.loom-crucible.kind-prompt-injection-detect .loom-crucible__options,
.loom-crucible.kind-math-arithmetic .loom-crucible__options { grid-template-columns: 1fr; }
.loom-crucible__option {
  aspect-ratio: 1; padding: 0; border: 2px solid var(--loom-color-border, var(--loom-border));
  border-radius: 10px; background: linear-gradient(135deg, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 14%, transparent), color-mix(in oklab, var(--loom-color-accent-2, var(--loom-accent-2)) 14%, transparent));
  cursor: pointer; transition: transform var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease), border-color var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-crucible.kind-semantic-similarity .loom-crucible__option,
.loom-crucible.kind-prompt-injection-detect .loom-crucible__option { aspect-ratio: auto; padding: 0.85rem 1.1rem; text-align: left; }
.loom-crucible__option:hover { transform: scale(1.02); border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-crucible__option[aria-pressed="true"] { border-color: var(--loom-color-primary, var(--loom-accent)); box-shadow: 0 0 0 4px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent); }
.loom-crucible__option-glyph { display: block; width: 100%; height: 100%; }
.loom-crucible__footer { display: flex; flex-direction: column; gap: 0.6rem; }
.loom-crucible__submit { width: 100%; }
.loom-crucible__attribution { margin: 0; font-size: 0.78rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent); text-align: center; }

.loom-auth-stepper { margin: 1rem auto 2rem; max-width: 36rem; }
.loom-auth-stepper__list { list-style: none; padding: 0; display: flex; gap: 0.5rem; counter-reset: step; }
.loom-auth-stepper__step { flex: 1; display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.85rem; border-radius: 10px; border: 1px solid var(--loom-color-border, var(--loom-border)); font-size: 0.88rem; }
.loom-auth-stepper__num { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 14%, transparent); font: 700 0.78rem/1 var(--loom-font-mono); }
.loom-auth-stepper__step.is-current { border-color: var(--loom-color-primary, var(--loom-accent)); }
.loom-auth-stepper__step.is-current .loom-auth-stepper__num { background: var(--loom-color-primary, var(--loom-accent)); color: var(--loom-color-on-primary, white); }
.loom-auth-stepper__step.is-done { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent); }
.loom-auth-stepper__step.is-done .loom-auth-stepper__num { background: color-mix(in oklab, oklch(60% 0.18 150) 35%, transparent); color: white; }
.loom-auth-stepper__step.is-done .loom-auth-stepper__num::before { content: "✓"; }
.loom-auth-stepper__step.is-done .loom-auth-stepper__num span { display: none; }

.loom-signed-in-card { display: flex; align-items: center; gap: 0.85rem; padding: 0.85rem 1rem; border-radius: 12px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-signed-in-card__body { display: flex; flex-direction: column; flex: 1; }
.loom-signed-in-card__name { font-weight: 700; }
.loom-signed-in-card__handle { font-size: 0.85rem; color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent); }

.loom-password-reset { max-width: 24rem; margin: 2rem auto; padding: 2rem; border-radius: 14px; background: var(--loom-color-surface, var(--loom-bg)); border: 1px solid var(--loom-color-border, var(--loom-border)); }
.loom-password-reset__title { margin: 0 0 0.5rem; font: 800 1.3rem/1.2 var(--loom-font-display); letter-spacing: -0.022em; }
.loom-password-reset__description { color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent); margin-bottom: 1.25rem; }
.loom-password-reset__form { display: flex; flex-direction: column; gap: 0.6rem; }
.loom-password-reset__form input { padding: 0.7rem 0.9rem; border-radius: 10px; border: 1px solid var(--loom-color-border, var(--loom-border)); background: var(--loom-color-surface, var(--loom-bg)); min-height: 44px; }

/* ============================================================
 * T660 P7 — FloatingPill chrome
 *
 * Replaces the page-shell sticky bar with a glass-morphism pill
 * anchored top-center. Drops the cream three-radial body
 * backdrop in favour of a single soft halo above the hero +
 * a flat surface below. Body switches via `data-chrome`
 * attribute on <body>.
 * ============================================================ */

body[data-chrome="floating-pill"] {
  background: var(--loom-color-bg-canvas);
  background-image: none;
  background-attachment: scroll;
}
body[data-chrome="floating-pill"]::before {
  content: ""; position: fixed; inset: 0 0 auto 0; height: 60vh; z-index: -1;
  background:
    radial-gradient(
      80rem 38rem at 50% -10%,
      color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 18%, transparent) 0%,
      transparent 60%
    ),
    radial-gradient(
      40rem 22rem at 88% 12%,
      color-mix(in oklab, var(--loom-color-accent) 14%, transparent) 0%,
      transparent 65%
    );
  pointer-events: none;
}
body[data-chrome="floating-pill"]::after {
  content: ""; position: fixed; inset: 0; z-index: -2;
  background:
    radial-gradient(circle at center,
      color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 4%, transparent) 1px,
      transparent 1.2px) 0 0 / 28px 28px;
  mask-image: linear-gradient(180deg, black, transparent 70%);
  pointer-events: none; opacity: 0.6;
}

.loom-floating-pill {
  position: fixed; top: 0.85rem; left: 50%;
  transform: translateX(-50%);
  width: min(72rem, calc(100vw - 1.5rem));
  z-index: 80;
}
.loom-floating-pill__nav {
  display: flex; align-items: center; gap: 1.25rem;
  padding: 0.5rem 0.55rem 0.5rem 1.25rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-surface, var(--loom-bg)) 65%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  box-shadow:
    0 1px 0 oklch(100% 0 0 / 0.5) inset,
    0 0 0 1px color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent) inset,
    0 14px 40px oklch(20% 0.02 280 / 0.10);
}
.loom-floating-pill__brand {
  font: 800 1.05rem/1 var(--loom-font-display, var(--loom-font-stack));
  letter-spacing: -0.025em;
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  flex: 0 0 auto;
  padding: 0.3rem 0;
}
.loom-floating-pill__links {
  display: flex; gap: 0.3rem; flex: 1 1 auto;
  /* 2026-05-20: allow pills to wrap to a second row at tablet widths
   * (~760-1100px) where the brand + nav + actions can't fit on one
   * line. Without this the labels squeeze and each word wraps INSIDE
   * its pill, which is unreadable. */
  flex-wrap: wrap;
  margin-left: 0.75rem;
  row-gap: 0.4rem;
}
.loom-floating-pill__links a {
  text-decoration: none;
  /* 2026-05-20: nowrap on the label keeps each nav item on a single
   * line. The wrap happens at the container (flex-wrap above), not
   * inside the pill. */
  white-space: nowrap;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
  padding: 0.45rem 0.9rem;
  border-radius: 999px;
  font: 500 0.92rem/1 var(--loom-font-display, var(--loom-font-stack));
  letter-spacing: -0.005em;
  /* min-height 44px = WCAG 2.5.5 AAA + Apple HIG + Material Design
   * recommended tap-target size. Lowered from 36px (previous
   * value would trigger Crawler `tap.below-recommended` warn on
   * every page). */
  min-height: 44px;
  display: inline-flex; align-items: center;
  transition:
    color var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease),
    background var(--loom-motion-fast, 140ms) var(--loom-ease-out, ease);
}
.loom-floating-pill__links a:hover {
  color: var(--loom-color-ink, var(--loom-fg));
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent);
}
.loom-floating-pill__links a[aria-current="page"] {
  color: var(--loom-color-primary, var(--loom-accent));
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  font-weight: 600;
}
.loom-floating-pill__actions {
  display: flex; gap: 0.4rem; align-items: center;
  flex: 0 0 auto;
}
/* Chrome-neutral nav-action button (#533): emitted by render_nav_actions
 * for BOTH FloatingPill (inside .loom-floating-pill__actions) and
 * PageShell (inside .loom-page-nav__actions). */
.loom-nav-action {
  padding: 0.55rem 1rem !important;
  /* 44px = WCAG 2.5.5 AAA tap-target floor (Apple HIG + Material
   * Design agree). Was 36px — failed AAA + triggered Crawler
   * tap_targets.below-recommended warn. */
  min-height: 44px !important;
  font-size: 0.88rem !important;
}
/* Nav-action buttons are <a> elements inside the nav, so the inline
 * critical baseline `nav.loom-page-nav a` (0,1,2) forces --loom-muted
 * onto their text + icon (the icon is currentColor). The variant's own
 * `color` (e.g. .loom-btn--success at 0,1,0) loses on specificity, so
 * only the variant border survives — a success button reads slate-gray
 * with a faint green border instead of full emerald, a primary button
 * would lose its on-color text, etc. Re-assert each variant's
 * foreground at (0,3,2) so the button reads correctly inside the nav,
 * mirroring the .loom-page-nav__home-icon fix. Generic — any tenant's
 * nav actions inherit this; no per-site CSS. */
nav.loom-page-nav a.loom-nav-action.loom-btn--success {
  color: var(--loom-color-success, hsl(142 75% 22%));
}
nav.loom-page-nav a.loom-nav-action.loom-btn--primary {
  color: var(--loom-color-on-primary, #fff);
}
nav.loom-page-nav a.loom-nav-action.loom-btn--secondary,
nav.loom-page-nav a.loom-nav-action.loom-btn--outline,
nav.loom-page-nav a.loom-nav-action.loom-btn--ghost {
  color: var(--loom-color-ink, var(--loom-fg));
}
/* #563: the SAME inline-critical baseline `nav.loom-page-nav a`
 * (0,1,2) that clobbers nav-action color above ALSO forces
 * `border-radius: var(--loom-radius-full)` (the pill) onto every nav
 * anchor — beating `.loom-btn`'s own `var(--loom-radius-control,999px)`
 * (0,1,0). So a tenant that sets [style.radius] control gets rounded
 * hero CTAs but the nav actions stay pill, an inconsistency. Re-assert
 * the control radius at (0,2,2) so nav-action buttons honor the tenant
 * token too. Fallback is the full pill, so demos/ProsperityClub (which
 * set no control radius) are byte-for-byte unchanged. Generic; no
 * per-site CSS. */
nav.loom-page-nav a.loom-nav-action {
  border-radius: var(--loom-radius-control, var(--loom-radius-full, 9999px));
}
/* #544: leading icon-mark on a nav action. .loom-btn is inline-flex
 * with gap, so the icon auto-spaces from the label — only sizing is
 * needed. Inherits button text color via currentColor. */
.loom-nav-action__icon {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
}
.loom-floating-pill .loom-theme-toggle {
  /* 44x44 = tap-target floor; was 36x36. */
  width: 44px; height: 44px;
  border-radius: 999px;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  background: transparent;
  font-size: 0.95rem;
}

/* When FloatingPill chrome is active, add top-padding so the
 * fixed pill never overlaps the hero's first pixel. */
body[data-chrome="floating-pill"] main#content {
  padding-top: 5.5rem;
}
body[data-chrome="floating-pill"] .loom-image-hero,
body[data-chrome="floating-pill"] .loom-split-hero {
  margin-top: -3rem;
  padding-top: 6rem;
}

/* Mobile compaction */
@media (max-width: 760px) {
  .loom-floating-pill { left: 0.75rem; right: 0.75rem; transform: none; width: auto; }
  .loom-floating-pill__nav { gap: 0.6rem; padding: 0.4rem 0.45rem 0.4rem 1rem; }
  .loom-floating-pill__links { display: none; }
  .loom-floating-pill__brand { font-size: 0.95rem; }
}

/* Minimal chrome — body adjustments so first section has top
 * breathing room since there's no header reserving space. */
body[data-chrome="minimal"] main#content { padding-top: 2rem; }

/* ============================================================
 * Editorial composition primitives — substrate-deepening additions
 * for HeroEditorial / CodeShell / PullQuote-editorial / KvPair tone
 * Shipped as part of the editorial-vocabulary completion arc.
 *
 * Shape commitments enforced upstream in tests:
 *   * No SaaS-trope ornaments (no decorative quote-mark glyphs,
 *     no fake macOS traffic-light circles, no gradient header
 *     bars, no italic-by-default).
 *   * AMOLED tone uses true black (#000) for OLED pixels-off.
 *   * Density attributes drive vertical rhythm; horizontal sizing
 *     remains the layout grid's responsibility.
 * ============================================================ */

/* HeroEditorial — asymmetric 2-col composition, no eyebrow pill,
 * no gradient overlay, no fade-in animations.
 * Three background tones via [data-background] attribute. */
.loom-section-hero-editorial {
  padding: clamp(3rem, 7vw, 6rem) clamp(1rem, 4vw, 2.5rem);
  background: var(--loom-color-bg, var(--loom-bg, #f8fafc));
  color: var(--loom-color-ink, var(--loom-fg, #0f172a));
}
.loom-section-hero-editorial[data-background="plain"] {
  background: var(--loom-color-surface, #fff);
}
.loom-section-hero-editorial[data-background="amoled"] {
  background: #000;
  color: var(--loom-color-on-dark, #e5e7eb);
}
.loom-section-hero-editorial__grid {
  max-width: 80rem;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}
@media (min-width: 768px) {
  .loom-section-hero-editorial__grid {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 3rem;
  }
}
.loom-section-hero-editorial__lead {
  display: flex;
  flex-direction: column;
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
}
.loom-section-hero-editorial__kicker {
  font: 500 0.8125rem/1.4 var(--loom-font-mono, ui-monospace, "JetBrains Mono", monospace);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: color-mix(in oklab, currentColor 60%, transparent);
  margin: 0;
}
.loom-section-hero-editorial__headline {
  font: 600 clamp(2.25rem, 5vw, 3.75rem)/1.05 var(--loom-font-display, ui-serif, Georgia, serif);
  letter-spacing: -0.02em;
  margin: 0;
}
.loom-section-hero-editorial__accent {
  color: var(--loom-color-primary, var(--loom-accent, #2563eb));
}
.loom-section-hero-editorial__lede {
  font: 400 clamp(1rem, 1.5vw, 1.125rem)/1.55 var(--loom-font-body, system-ui, sans-serif);
  max-width: 65ch;
  color: color-mix(in oklab, currentColor 80%, transparent);
  margin: 0;
}
.loom-section-hero-editorial__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.625rem 1.25rem;
  border: 1px solid currentColor;
  text-decoration: none;
  color: inherit;
  font-weight: 500;
  align-self: flex-start;
  transition: background-color 120ms ease, color 120ms ease;
}
.loom-section-hero-editorial__cta:hover {
  background: currentColor;
  color: var(--loom-color-bg, #fff);
}
.loom-section-hero-editorial__cta:hover .loom-section-hero-editorial__cta-label {
  color: inherit;
}
.loom-section-hero-editorial__cta[data-invalid="true"] {
  outline: 2px dashed var(--loom-color-warn, #f59e0b);
  outline-offset: 4px;
}

/* CodeShell — typed-line terminal transcript. Per-line semantic
 * coloring; prompt glyph unselectable so copying yields just the
 * command. No fake traffic-light circles, no gradient header. */
.loom-code-shell {
  border: 1px solid var(--loom-color-border, #e2e8f0);
  background: var(--loom-color-surface, #f8fafc);
  color: var(--loom-color-ink, #0f172a);
  overflow: hidden;
  margin: 1.5rem 0;
}
.loom-code-shell[data-tone="amoled"] {
  background: #000;
  color: var(--loom-color-on-dark, #e5e7eb);
  border-color: #1e293b;
}
.loom-code-shell__header {
  padding: 0.5rem 1rem;
  font: 500 0.75rem/1 var(--loom-font-mono, ui-monospace, monospace);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: color-mix(in oklab, currentColor 65%, transparent);
  background: color-mix(in oklab, currentColor 6%, transparent);
  border-bottom: 1px solid color-mix(in oklab, currentColor 12%, transparent);
}
.loom-code-shell__pre {
  margin: 0;
  padding: 1rem;
  overflow-x: auto;
  font: 0.875rem/1.55 var(--loom-font-mono, ui-monospace, monospace);
}
.loom-code-shell__code {
  display: block;
  font-family: inherit;
}
.loom-code-shell__line {
  display: block;
}
.loom-code-shell__line--command {
  color: currentColor;
}
.loom-code-shell__line--output {
  color: color-mix(in oklab, currentColor 75%, transparent);
  padding-left: 1rem;
}
.loom-code-shell__line--comment {
  color: color-mix(in oklab, currentColor 50%, transparent);
  font-style: italic;
}
.loom-code-shell__line--error {
  color: var(--loom-color-danger, #b91c1c);
  padding-left: 1rem;
}
.loom-code-shell[data-tone="amoled"] .loom-code-shell__line--error {
  color: #f87171;
}
.loom-code-shell__prompt {
  opacity: 0.55;
  user-select: none;
  -webkit-user-select: none;
}

/* PullQuote — editorial overrides via [data-emphasis="display"]
 * + [data-tone="amoled"]. The legacy default (centered, decorative
 * quote-mark glyph, italic) stays for back-compat callers that
 * don't opt into the editorial composition. */
.loom-pull-quote[data-emphasis="display"] {
  text-align: left;
  padding: 1.5rem 0 1.5rem 2rem;
  border-left: 2px solid color-mix(in oklab, currentColor 30%, transparent);
  max-width: 60rem;
  margin: 2rem auto;
}
.loom-pull-quote[data-emphasis="display"]::before {
  content: none;
}
.loom-pull-quote[data-emphasis="display"] .loom-pull-quote__body {
  font: 600 clamp(1.5rem, 3vw, 2.25rem)/1.25 var(--loom-font-display, ui-serif, Georgia, serif);
  font-style: normal;
  letter-spacing: -0.018em;
}
.loom-pull-quote[data-tone="amoled"] {
  background: #000;
  color: var(--loom-color-on-dark, #e5e7eb);
  padding-block: 2rem;
}
.loom-pull-quote[data-tone="amoled"] .loom-pull-quote__body {
  color: #f1f5f9;
}
.loom-pull-quote[data-tone="amoled"] .loom-pull-quote__attribution {
  color: color-mix(in oklab, #f1f5f9 65%, transparent);
}
.loom-pull-quote[data-tone="amoled"][data-emphasis="display"] {
  border-left-color: color-mix(in oklab, #f1f5f9 30%, transparent);
}

/* KvPair — density + tone overrides. The default skin (from line
 * ~7200) handles the comfortable / slate baseline. */
.loom-kv-section[data-density="compact"] .loom-kv-row {
  padding-block: 0.5rem;
}
.loom-kv-section[data-density="spacious"] .loom-kv-row {
  padding-block: 1.25rem;
}
.loom-kv-section[data-tone="amoled"] {
  background: #000;
  color: var(--loom-color-on-dark, #e5e7eb);
  padding: 1.5rem;
}
.loom-kv-section[data-tone="amoled"] .loom-kv-key {
  color: color-mix(in oklab, #f1f5f9 65%, transparent);
}
.loom-kv-section[data-tone="amoled"] .loom-kv-value {
  color: #f1f5f9;
}
.loom-kv-section[data-tone="amoled"] .loom-kv-hint {
  color: color-mix(in oklab, #f1f5f9 55%, transparent);
}

/* prefers-reduced-motion — the editorial primitives ship no
 * animations by design, but ensure any future motion is gated. */
@media (prefers-reduced-motion: reduce) {
  .loom-section-hero-editorial,
  .loom-code-shell,
  .loom-pull-quote {
    animation: none !important;
    transition: none !important;
  }
}

/* ============================================================
 * Polish tokens — refinement-tier decoration classes (#107).
 * Generated by loom-tokens::polish::PolishToken. Each class is
 * opt-in: primitives emit them only when the operator (or a
 * consumer-shaped doctrine override) requests the polish.
 *
 * 12 polish variants across 4 categories — background, border,
 * glow, motion. The Rust enum is the source of truth; if you
 * change a class name here, change the enum slug to match.
 * ============================================================ */

/* ----- Background polish ----- */

.loom-polish-dot-grid {
  background-image:
    radial-gradient(
      circle at 1px 1px,
      color-mix(in oklab, var(--loom-color-ink, #0f172a) 12%, transparent) 1px,
      transparent 0
    );
  background-size: 24px 24px;
  background-position: 0 0;
}
.loom-polish-linear-mesh {
  background-image:
    linear-gradient(
      135deg,
      color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 4%, transparent) 0%,
      transparent 60%
    );
}
.loom-polish-topographic {
  background-image:
    repeating-radial-gradient(
      ellipse at 30% 50%,
      transparent 0,
      transparent 40px,
      color-mix(in oklab, var(--loom-color-ink, #0f172a) 6%, transparent) 41px,
      color-mix(in oklab, var(--loom-color-ink, #0f172a) 6%, transparent) 42px
    );
}

/* ----- Border polish ----- */

.loom-polish-editorial-rule {
  border-top: 2px solid var(--loom-color-primary, var(--loom-accent));
  padding-top: 1.5rem;
}
.loom-polish-inset-frame {
  position: relative;
}
.loom-polish-inset-frame::after {
  content: "";
  position: absolute;
  inset: 0.5rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-ink, #0f172a) 20%, transparent);
  pointer-events: none;
}
.loom-polish-blueprint-corner {
  position: relative;
}
.loom-polish-blueprint-corner::before,
.loom-polish-blueprint-corner::after {
  content: "";
  position: absolute;
  width: 0.75rem;
  height: 0.75rem;
  border: 1px solid var(--loom-color-primary, var(--loom-accent));
  pointer-events: none;
}
.loom-polish-blueprint-corner::before {
  top: 0;
  left: 0;
  border-right: none;
  border-bottom: none;
}
.loom-polish-blueprint-corner::after {
  bottom: 0;
  right: 0;
  border-left: none;
  border-top: none;
}

/* ----- Glow polish ----- */

.loom-polish-soft-glow {
  box-shadow:
    0 0 24px -4px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 22%, transparent);
}
.loom-polish-brand-halo {
  box-shadow:
    0 0 48px -8px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 40%, transparent);
}
.loom-polish-amoled-rim {
  box-shadow:
    0 0 0 1px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 60%, transparent);
}

/* ----- Motion polish — all honor prefers-reduced-motion ----- */

@keyframes loom-polish-slow-reveal {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.loom-polish-slow-reveal {
  animation: loom-polish-slow-reveal 600ms ease-out both;
}
.loom-polish-page-turn {
  animation: loom-polish-slow-reveal 320ms ease-out both;
}
.loom-polish-cursor-tilt {
  transition: transform 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
  transform-origin: center;
}
.loom-polish-cursor-tilt:hover {
  transform: rotate(1deg);
}

@media (prefers-reduced-motion: reduce) {
  .loom-polish-slow-reveal,
  .loom-polish-page-turn,
  .loom-polish-cursor-tilt,
  .loom-polish-cursor-tilt:hover {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
}

/* ===================================================================
 * Atomic CmsBlock primitives — Loom PR #84 + Button follow-on.
 *
 * Block primitives carry composition (typography, spacing, alignment)
 * via data-* attributes resolved against the tenant's [style] config.
 * Two tenants composing identical block trees render distinctly
 * because their palettes / fonts / density tokens differ.
 * =================================================================== */

.loom-compose {
  margin-block: 2.5rem;
}
.loom-compose__heading {
  margin: 0 0 1.25rem;
  font: 700 1.75rem/1.2 var(--loom-font-display);
  letter-spacing: -0.022em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-compose__tree {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Text + Heading */
.loom-block-text {
  margin: 0;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
  line-height: 1.65;
  font-size: 1.02rem;
}
.loom-block-heading {
  margin: 0;
  font-family: var(--loom-font-display);
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-heading[data-level="1"] { font-size: 2.5rem; line-height: 1.1; }
.loom-block-heading[data-level="2"] { font-size: 1.75rem; line-height: 1.2; }
.loom-block-heading[data-level="3"] { font-size: 1.35rem; line-height: 1.25; }
.loom-block-heading[data-level="4"] { font-size: 1.15rem; line-height: 1.3; }
.loom-block-heading[data-level="5"] { font-size: 1.02rem; line-height: 1.35; }
.loom-block-heading[data-level="6"] { font-size: 0.92rem; line-height: 1.4; text-transform: uppercase; letter-spacing: 0.04em; }

/* Image */
.loom-block-image {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--loom-img-radius, var(--loom-radius, 10px));
}

/* Link + Button */
.loom-block-link {
  color: var(--loom-color-primary, var(--loom-link));
  text-decoration: underline;
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.18em;
}
.loom-block-link[data-invalid="true"] {
  outline: 2px dashed color-mix(in oklab, var(--loom-color-danger, #ef4444) 70%, transparent);
  outline-offset: 2px;
}
.loom-block-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-family: var(--loom-font-display);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--loom-radius, 10px);
  border: 1px solid transparent;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              transform var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
  cursor: pointer;
  min-height: var(--loom-tap-min, 44px);
}
.loom-block-button[data-size="sm"] { padding: 0.4rem 0.85rem; font-size: 0.9rem; }
.loom-block-button[data-size="md"] { padding: 0.55rem 1.15rem; font-size: 1rem; }
.loom-block-button[data-size="lg"] { padding: 0.7rem 1.5rem; font-size: 1.1rem; }
.loom-block-button[data-variant="primary"] {
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
}
.loom-block-button[data-variant="primary"]:hover {
  background: var(--loom-color-primary-hover, color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 80%, black));
}
.loom-block-button[data-variant="secondary"] {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
  border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 30%, transparent);
}
.loom-block-button[data-variant="secondary"]:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 16%, transparent);
}
.loom-block-button[data-variant="ghost"] {
  background: transparent;
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-button[data-variant="ghost"]:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
}
.loom-block-button[data-variant="destructive"] {
  background: var(--loom-color-danger, #b91c1c);
  color: #fff;
}
.loom-block-button[data-variant="destructive"]:hover {
  background: color-mix(in oklab, var(--loom-color-danger, #b91c1c) 80%, black);
}
.loom-block-button[data-invalid="true"] {
  outline: 2px dashed color-mix(in oklab, var(--loom-color-danger, #ef4444) 70%, transparent);
  outline-offset: 2px;
}

/* Spacer */
.loom-block-spacer[data-size="none"] { height: 0; }
.loom-block-spacer[data-size="xs"]   { height: 0.25rem; }
.loom-block-spacer[data-size="sm"]   { height: 0.5rem; }
.loom-block-spacer[data-size="md"]   { height: 1rem; }
.loom-block-spacer[data-size="lg"]   { height: 2rem; }
.loom-block-spacer[data-size="xl"]   { height: 3rem; }
.loom-block-spacer[data-size="xxl"]  { height: 4.5rem; }

/* Divider */
.loom-block-divider {
  border: 0;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  margin: 1.5rem 0;
}

/* Container / Row / Column */
.loom-block-container { display: block; }
.loom-block-container[data-padding="xs"]  { padding: 0.5rem; }
.loom-block-container[data-padding="sm"]  { padding: 0.75rem; }
.loom-block-container[data-padding="md"]  { padding: 1rem; }
.loom-block-container[data-padding="lg"]  { padding: 1.5rem; }
.loom-block-container[data-padding="xl"]  { padding: 2rem; }
.loom-block-container[data-padding="xxl"] { padding: 3rem; }

.loom-block-row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: stretch;
}
.loom-block-column {
  display: flex;
  flex-direction: column;
}
.loom-block-row[data-gap="xs"],   .loom-block-column[data-gap="xs"]   { gap: 0.25rem; }
.loom-block-row[data-gap="sm"],   .loom-block-column[data-gap="sm"]   { gap: 0.5rem; }
.loom-block-row[data-gap="md"],   .loom-block-column[data-gap="md"]   { gap: 1rem; }
.loom-block-row[data-gap="lg"],   .loom-block-column[data-gap="lg"]   { gap: 1.5rem; }
.loom-block-row[data-gap="xl"],   .loom-block-column[data-gap="xl"]   { gap: 2rem; }
.loom-block-row[data-gap="xxl"],  .loom-block-column[data-gap="xxl"]  { gap: 3rem; }
.loom-block-row[data-align="start"],    .loom-block-column[data-align="start"]    { align-items: flex-start; }
.loom-block-row[data-align="center"],   .loom-block-column[data-align="center"]   { align-items: center; }
.loom-block-row[data-align="end"],      .loom-block-column[data-align="end"]      { align-items: flex-end; }
.loom-block-row[data-align="stretch"],  .loom-block-column[data-align="stretch"]  { align-items: stretch; }
.loom-block-row[data-align="baseline"], .loom-block-column[data-align="baseline"] { align-items: baseline; }

/* === CmsBlock::Switch ============================================== */
.loom-block-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  user-select: none;
}
.loom-block-switch__input {
  /* Hidden but focusable + click-through to track. Visually-hidden
   * technique so screen readers + keyboard users get the native
   * <input type=checkbox role=switch> semantics. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.loom-block-switch__track {
  display: inline-flex;
  align-items: center;
  width: 2.5rem;
  height: 1.4rem;
  padding: 0.15rem;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 25%, transparent);
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
  flex-shrink: 0;
}
.loom-block-switch__thumb {
  display: block;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 9999px;
  background: var(--loom-color-bg, var(--loom-bg, #fff));
  box-shadow: var(--loom-shadow-sm, 0 1px 2px rgba(0,0,0,0.1));
  transition: transform var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-switch__input:checked + .loom-block-switch__track {
  background: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-switch__input:checked + .loom-block-switch__track .loom-block-switch__thumb {
  transform: translateX(1.1rem);
}
.loom-block-switch__input:focus-visible + .loom-block-switch__track {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 3px;
}
.loom-block-switch__input:disabled ~ .loom-block-switch__track {
  opacity: 0.5;
}
.loom-block-switch__input:disabled ~ * {
  cursor: not-allowed;
}
.loom-block-switch__label {
  color: var(--loom-color-ink, var(--loom-fg));
  font-size: 0.95rem;
}

/* === CmsBlock::Slider ============================================== */
.loom-block-slider {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.loom-block-slider__label {
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}
.loom-block-slider__value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-slider__input {
  width: 100%;
  accent-color: var(--loom-color-primary, var(--loom-accent));
  cursor: pointer;
}
.loom-block-slider__input:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 4px;
  border-radius: 4px;
}
.loom-block-slider__input:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* === CmsBlock::Tooltip ============================================= */
.loom-block-tooltip {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  cursor: help;
  outline-offset: 4px;
}
.loom-block-tooltip__trigger {
  font: inherit;
  color: inherit;
}
.loom-block-tooltip__content {
  position: absolute;
  z-index: 100;
  max-width: 18rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--loom-radius-sm, 6px);
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 92%, transparent);
  color: var(--loom-color-bg, var(--loom-bg, #fff));
  font-size: 0.85rem;
  line-height: 1.4;
  font-weight: 400;
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.15));
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              transform var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              visibility 0s var(--loom-motion-fast, 120ms);
  pointer-events: none;
  white-space: normal;
}
.loom-block-tooltip:hover > .loom-block-tooltip__content,
.loom-block-tooltip:focus-visible > .loom-block-tooltip__content,
.loom-block-tooltip:focus-within > .loom-block-tooltip__content {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}
/* Placement variants — anchor + offset the body relative to the trigger. */
.loom-block-tooltip[data-placement="top"]    > .loom-block-tooltip__content { bottom: calc(100% + 8px); left: 50%; transform: translate(-50%, 4px); }
.loom-block-tooltip[data-placement="top"]:hover    > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="top"]:focus-visible > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="top"]:focus-within  > .loom-block-tooltip__content { transform: translate(-50%, 0); }
.loom-block-tooltip[data-placement="bottom"] > .loom-block-tooltip__content { top: calc(100% + 8px); left: 50%; transform: translate(-50%, -4px); }
.loom-block-tooltip[data-placement="bottom"]:hover > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="bottom"]:focus-visible > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="bottom"]:focus-within  > .loom-block-tooltip__content { transform: translate(-50%, 0); }
.loom-block-tooltip[data-placement="left"]   > .loom-block-tooltip__content { right: calc(100% + 8px); top: 50%; transform: translate(4px, -50%); }
.loom-block-tooltip[data-placement="left"]:hover > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="left"]:focus-visible > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="left"]:focus-within  > .loom-block-tooltip__content { transform: translate(0, -50%); }
.loom-block-tooltip[data-placement="right"]  > .loom-block-tooltip__content { left: calc(100% + 8px); top: 50%; transform: translate(-4px, -50%); }
.loom-block-tooltip[data-placement="right"]:hover > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="right"]:focus-visible > .loom-block-tooltip__content,
.loom-block-tooltip[data-placement="right"]:focus-within  > .loom-block-tooltip__content { transform: translate(0, -50%); }

/* === CmsBlock::Dialog ============================================== */
.loom-block-dialog {
  padding: 0;
  border: 0;
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  max-width: min(40rem, calc(100vw - 2rem));
  width: 100%;
  box-shadow: var(--loom-shadow-lg, 0 18px 40px rgba(0,0,0,0.15));
}
.loom-block-dialog::backdrop {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.loom-block-dialog__close-row {
  display: flex;
  justify-content: flex-end;
  margin: 0;
  padding: 0.75rem 0.75rem 0;
}
.loom-block-dialog__close {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--loom-radius-full, 9999px);
  width: 2rem;
  height: 2rem;
  font-size: 1.4rem;
  line-height: 1;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-dialog__close:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent);
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-dialog__close:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
.loom-block-dialog__title {
  margin: 0;
  padding: 0 1.5rem 0.5rem;
  font-family: var(--loom-font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-dialog__content {
  padding: 0.5rem 1.5rem 1.5rem;
  color: var(--loom-color-ink, var(--loom-fg));
}

/* === CmsBlock::Tabs ================================================ */
.loom-block-tabs {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.loom-block-tabs__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  border-bottom: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
}
.loom-block-tabs__trigger {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 0.65rem 1rem;
  font: inherit;
  font-weight: 500;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
  cursor: pointer;
  transition: color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
  margin-bottom: -1px;
  white-space: nowrap;
}
.loom-block-tabs__trigger:hover {
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-tabs__trigger[aria-selected="true"] {
  color: var(--loom-color-primary, var(--loom-accent));
  border-bottom-color: var(--loom-color-primary, var(--loom-accent));
  font-weight: 600;
}
.loom-block-tabs__trigger:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
  border-radius: var(--loom-radius-sm, 6px);
}
.loom-block-tabs__panel {
  padding: 0.5rem 0;
}
.loom-block-tabs__panel:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
  border-radius: var(--loom-radius-sm, 6px);
}

/* === CmsBlock::Popover ============================================= */
.loom-block-popover {
  position: relative;
  display: inline-block;
}
.loom-block-popover__trigger {
  appearance: none;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 25%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  padding: 0.45rem 0.85rem;
  font: inherit;
  font-size: 0.9rem;
  color: var(--loom-color-primary, var(--loom-accent));
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-popover__trigger:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 16%, transparent);
}
.loom-block-popover__trigger:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
/* Native popover positioning — browser places the popover at the
 * top of the layout layer; we anchor it relative to the trigger
 * via inset-area when CSS anchor-positioning is available, else
 * fall back to position:fixed centred. */
.loom-block-popover__content {
  margin: 0;
  padding: 0.75rem 1rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.08));
  max-width: 22rem;
  font-size: 0.95rem;
  line-height: 1.5;
}
.loom-block-popover__content:popover-open + * {
  /* Trigger gets aria-expanded toggled by browser when supported. */
}
/* Anchor positioning (Chromium 125+, Safari TP) — when available */
@supports (anchor-name: --x) {
  .loom-block-popover__trigger {
    anchor-name: --loom-popover-anchor;
  }
  .loom-block-popover__content {
    position-anchor: --loom-popover-anchor;
  }
  .loom-block-popover__content[data-placement="top"]    { position-area: top span-all; }
  .loom-block-popover__content[data-placement="bottom"] { position-area: bottom span-all; }
  .loom-block-popover__content[data-placement="left"]   { position-area: left span-all; }
  .loom-block-popover__content[data-placement="right"]  { position-area: right span-all; }
}

/* === CmsBlock::DropdownMenu ========================================= */
.loom-block-dropdown {
  position: relative;
  display: inline-block;
}
.loom-block-dropdown__trigger {
  appearance: none;
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  padding: 0.5rem 0.9rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.loom-block-dropdown__trigger:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 4%, transparent);
}
.loom-block-dropdown__trigger:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
.loom-block-dropdown__menu {
  margin: 0;
  padding: 0.3rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  box-shadow: var(--loom-shadow-lg, 0 18px 40px rgba(0,0,0,0.12));
  min-width: 11rem;
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
}
.loom-block-dropdown__item {
  display: block;
  padding: 0.45rem 0.7rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  background: transparent;
  border: 0;
  border-radius: var(--loom-radius-sm, 6px);
  text-align: left;
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-dropdown__item:hover:not([aria-disabled="true"]):not([disabled]) {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-dropdown__item:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: -1px;
}
.loom-block-dropdown__item[aria-disabled="true"],
.loom-block-dropdown__item[disabled] {
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 40%, transparent);
  cursor: not-allowed;
  pointer-events: none;
}
.loom-block-dropdown__item[data-invalid="true"] {
  color: var(--loom-color-danger, #b91c1c);
}
.loom-block-dropdown__separator {
  border: 0;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
  margin: 0.3rem 0;
}

/* === CmsBlock::Combobox ============================================ */
.loom-block-combobox {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-combobox__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-combobox__input {
  appearance: none;
  width: 100%;
  padding: 0.55rem 0.85rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.95rem;
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-combobox__input:focus-visible {
  outline: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-combobox__input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* === CmsBlock::Toast =============================================== */
.loom-block-toast {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.08));
  max-width: 28rem;
  color: var(--loom-color-ink, var(--loom-fg));
  position: relative;
}
.loom-block-toast[data-tone="info"]    { border-left: 4px solid var(--loom-color-primary, var(--loom-accent)); }
.loom-block-toast[data-tone="success"] { border-left: 4px solid var(--loom-color-success, #16a34a); }
.loom-block-toast[data-tone="warning"] { border-left: 4px solid var(--loom-color-warning, #d97706); }
.loom-block-toast[data-tone="error"]   { border-left: 4px solid var(--loom-color-danger,  #b91c1c); }
.loom-block-toast__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}
.loom-block-toast__title {
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-toast__message {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.4;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 80%, transparent);
}
.loom-block-toast__close {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--loom-radius-full, 9999px);
  width: 1.75rem;
  height: 1.75rem;
  font-size: 1.2rem;
  line-height: 1;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 60%, transparent);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-toast__close:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent);
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-toast__close:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}

/* === CmsBlock::NumberField ========================================= */
.loom-block-number-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-number-field__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-number-field__input {
  appearance: textfield;
  padding: 0.55rem 0.85rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  width: 100%;
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-number-field__input:focus-visible {
  outline: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-number-field__input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.loom-block-number-field__input:invalid:not(:focus) {
  border-color: var(--loom-color-danger, #b91c1c);
}

/* === CmsBlock::TextField (text / email / tel / url) =============== *
 * Single-line text input — same chrome as NumberField so form rows
 * read consistently. */
.loom-block-text-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-text-field__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-text-field__input {
  padding: 0.55rem 0.85rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.95rem;
  width: 100%;
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-text-field__input:focus-visible {
  outline: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-text-field__input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.loom-block-text-field__input:invalid:not(:focus) {
  border-color: var(--loom-color-danger, #b91c1c);
}

/* === CmsBlock::DatePicker / TimeField / ColorPicker ================ *
 * Shared form-field cascade — labels + input chrome track the
 * NumberField + Combobox treatment for consistent visual rhythm
 * across all form primitives. */
.loom-block-date-picker,
.loom-block-time-field,
.loom-block-color-picker {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-date-picker__label,
.loom-block-time-field__label,
.loom-block-color-picker__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-date-picker__input,
.loom-block-time-field__input {
  appearance: textfield;
  padding: 0.55rem 0.85rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-date-picker__input:focus-visible,
.loom-block-time-field__input:focus-visible,
.loom-block-color-picker__input:focus-visible {
  outline: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-color-picker__input {
  appearance: none;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  width: 3.5rem;
  height: 2.5rem;
  padding: 0.25rem;
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  cursor: pointer;
}
.loom-block-date-picker__input:disabled,
.loom-block-time-field__input:disabled,
.loom-block-color-picker__input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.loom-block-date-picker__input:invalid:not(:focus),
.loom-block-time-field__input:invalid:not(:focus) {
  border-color: var(--loom-color-danger, #b91c1c);
}

/* === CmsBlock::Checkbox ============================================ */
.loom-block-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  user-select: none;
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-checkbox__input {
  accent-color: var(--loom-color-primary, var(--loom-accent));
  width: 1.05rem;
  height: 1.05rem;
  cursor: pointer;
  flex-shrink: 0;
}
.loom-block-checkbox__input:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 3px;
  border-radius: 2px;
}
.loom-block-checkbox__input:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.loom-block-checkbox__input:disabled ~ .loom-block-checkbox__label {
  opacity: 0.5;
  cursor: not-allowed;
}

/* === CmsBlock::RadioGroup ========================================== */
.loom-block-radio-group {
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  padding: 0.85rem 1rem 1rem;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.loom-block-radio-group__legend {
  padding: 0 0.4rem;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-radio-group__item {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-radio-group__input {
  accent-color: var(--loom-color-primary, var(--loom-accent));
  width: 1.05rem;
  height: 1.05rem;
  cursor: pointer;
  flex-shrink: 0;
}
.loom-block-radio-group__input:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 3px;
  border-radius: 9999px;
}
.loom-block-radio-group:disabled,
.loom-block-radio-group__input:disabled {
  opacity: 0.6;
}
.loom-block-radio-group__input:disabled ~ .loom-block-radio-group__label {
  opacity: 0.6;
  cursor: not-allowed;
}

/* === CmsBlock::Select ============================================== */
.loom-block-select {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-select__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-select__input {
  appearance: none;
  -webkit-appearance: none;
  padding: 0.55rem 2.25rem 0.55rem 0.85rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff))
              url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='currentColor' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>")
              right 0.85rem center no-repeat;
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.95rem;
  cursor: pointer;
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-select__input:focus-visible {
  outline: none;
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-select__input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* === CmsBlock::TagInput ============================================ */
.loom-block-tag-input {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.loom-block-tag-input__label {
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-tag-input__chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  transition: border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              box-shadow var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
  min-height: 2.4rem;
}
.loom-block-tag-input__chips:focus-within {
  border-color: var(--loom-color-primary, var(--loom-accent));
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 20%, transparent);
}
.loom-block-tag-input__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.25rem 0.2rem 0.55rem;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
  font-size: 0.85rem;
  font-weight: 500;
}
.loom-block-tag-input__chip-remove {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: 9999px;
  width: 1.2rem;
  height: 1.2rem;
  font-size: 0.95rem;
  line-height: 1;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.loom-block-tag-input__chip-remove:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 22%, transparent);
}
.loom-block-tag-input__chip-remove:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 1px;
}
.loom-block-tag-input__input {
  flex: 1;
  min-width: 6rem;
  appearance: none;
  background: transparent;
  border: 0;
  outline: 0;
  padding: 0.25rem 0.2rem;
  font: inherit;
  font-size: 0.95rem;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-tag-input__input:disabled,
.loom-block-tag-input__chip-remove:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* === CmsBlock::Breadcrumb ========================================== */
.loom-block-breadcrumb {
  font-size: 0.9rem;
}
.loom-block-breadcrumb__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 0.25rem;
}
.loom-block-breadcrumb__item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 65%, transparent);
}
.loom-block-breadcrumb__link {
  color: var(--loom-color-primary, var(--loom-accent));
  text-decoration: none;
}
.loom-block-breadcrumb__link:hover {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.loom-block-breadcrumb__current {
  color: var(--loom-color-ink, var(--loom-fg));
  font-weight: 500;
}
.loom-block-breadcrumb__separator {
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 45%, transparent);
  user-select: none;
}

/* === CmsBlock::Pagination ========================================== */
.loom-block-pagination {
  display: flex;
  justify-content: center;
}
.loom-block-pagination__list {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 0.25rem;
  align-items: center;
}
.loom-block-pagination__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.25rem;
  height: 2.25rem;
  padding: 0 0.6rem;
  border-radius: var(--loom-radius-sm, 6px);
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  font-size: 0.92rem;
  font-variant-numeric: tabular-nums;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              border-color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-pagination__link:hover:not([aria-disabled="true"]):not(.loom-block-pagination__link--current) {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 8%, transparent);
  border-color: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 35%, transparent);
}
.loom-block-pagination__link--current {
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
  border-color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-pagination__link[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.loom-block-pagination__link:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
.loom-block-pagination__ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  user-select: none;
}

/* === CmsBlock::ScrollArea ========================================== */
.loom-block-scroll-area {
  overflow: auto;
  border-radius: var(--loom-radius-sm, 6px);
  /* Sensible defaults; tenant [style.spacing] override-friendly. */
  scrollbar-color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 35%, transparent) transparent;
  scrollbar-width: thin;
}
.loom-block-scroll-area[data-axis="vertical"]   { overflow-y: auto;  overflow-x: hidden; }
.loom-block-scroll-area[data-axis="horizontal"] { overflow-x: auto;  overflow-y: hidden; }
.loom-block-scroll-area[data-axis="both"]       { overflow: auto; }
.loom-block-scroll-area[data-max-height="xs"]  { max-height: 4rem; }
.loom-block-scroll-area[data-max-height="sm"]  { max-height: 8rem; }
.loom-block-scroll-area[data-max-height="md"]  { max-height: 16rem; }
.loom-block-scroll-area[data-max-height="lg"]  { max-height: 24rem; }
.loom-block-scroll-area[data-max-height="xl"]  { max-height: 36rem; }
.loom-block-scroll-area[data-max-height="xxl"] { max-height: 48rem; }

/* === CmsBlock::NavigationMenu ====================================== */
.loom-block-nav-menu {
  font-size: 0.95rem;
}
.loom-block-nav-menu__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.loom-block-nav-menu__item {
  position: relative;
  display: inline-block;
}
.loom-block-nav-menu__link,
.loom-block-nav-menu__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.85rem;
  border-radius: var(--loom-radius-sm, 6px);
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  font-weight: 500;
  cursor: pointer;
  background: transparent;
  border: 0;
  font: inherit;
  list-style: none;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-nav-menu__link:hover,
.loom-block-nav-menu__trigger:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, transparent);
}
.loom-block-nav-menu__link[aria-current="page"] {
  color: var(--loom-color-primary, var(--loom-accent));
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
}
.loom-block-nav-menu__link:focus-visible,
.loom-block-nav-menu__trigger:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
.loom-block-nav-menu__link[data-invalid="true"] {
  color: var(--loom-color-danger, #b91c1c);
}
.loom-block-nav-menu__submenu summary::-webkit-details-marker {
  display: none;
}
.loom-block-nav-menu__submenu summary::marker {
  content: "";
}
.loom-block-nav-menu__trigger::after {
  content: "▾";
  font-size: 0.7em;
  margin-left: 0.15rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent);
}
.loom-block-nav-menu__submenu[open] > .loom-block-nav-menu__sublist {
  display: block;
}
.loom-block-nav-menu__sublist {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  display: none;
  min-width: 12rem;
  margin: 0;
  padding: 0.3rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.08));
  list-style: none;
  flex-direction: column;
  gap: 0.05rem;
}
.loom-block-nav-menu__sublist .loom-block-nav-menu__link,
.loom-block-nav-menu__sublist .loom-block-nav-menu__trigger {
  display: block;
  width: 100%;
  padding: 0.4rem 0.7rem;
  font-size: 0.92rem;
  font-weight: 400;
}
.loom-block-nav-menu__sublist .loom-block-nav-menu__sublist {
  position: static;
  display: none;
  border: 0;
  box-shadow: none;
  padding: 0 0 0 0.5rem;
  background: transparent;
}
.loom-block-nav-menu__sublist .loom-block-nav-menu__submenu[open] > .loom-block-nav-menu__sublist {
  display: block;
}

/* === CmsBlock::Menubar ============================================= */
.loom-block-menubar {
  display: inline-flex;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
}
.loom-block-menubar__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.05rem;
  margin: 0;
  padding: 0.15rem;
  list-style: none;
}
.loom-block-menubar__item {
  position: relative;
}
.loom-block-menubar__leaf,
.loom-block-menubar__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.75rem;
  border-radius: var(--loom-radius-sm, 6px);
  background: transparent;
  border: 0;
  color: var(--loom-color-ink, var(--loom-fg));
  font: inherit;
  font-size: 0.92rem;
  cursor: pointer;
  list-style: none;
}
.loom-block-menubar__leaf:hover:not([aria-disabled="true"]):not([disabled]),
.loom-block-menubar__trigger:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-menubar__leaf:focus-visible,
.loom-block-menubar__trigger:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: -1px;
}
.loom-block-menubar__leaf[aria-disabled="true"],
.loom-block-menubar__leaf[disabled] {
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 40%, transparent);
  cursor: not-allowed;
  pointer-events: none;
}
.loom-block-menubar__leaf-accelerator {
  font-family: var(--loom-font-mono, monospace);
  font-size: 0.78rem;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  background: transparent;
  border: 0;
  padding: 0;
}
.loom-block-menubar__submenu summary::-webkit-details-marker { display: none; }
.loom-block-menubar__submenu summary::marker { content: ""; }
.loom-block-menubar__trigger::after {
  content: "▾";
  font-size: 0.7em;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 55%, transparent);
}
.loom-block-menubar__sublist {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  display: none;
  min-width: 13rem;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.08));
}
.loom-block-menubar__submenu[open] > .loom-block-menubar__sublist {
  display: block;
}
.loom-block-menubar__sublist .loom-block-menubar__leaf,
.loom-block-menubar__sublist .loom-block-menubar__trigger {
  display: flex;
  justify-content: space-between;
  width: 100%;
}

/* === CmsBlock::Resizable =========================================== */
.loom-block-resizable {
  display: flex;
  width: 100%;
  height: 100%;
  min-height: 12rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  overflow: hidden;
}
.loom-block-resizable[data-orientation="horizontal"] {
  flex-direction: row;
}
.loom-block-resizable[data-orientation="vertical"] {
  flex-direction: column;
}
.loom-block-resizable__pane {
  flex-grow: 0;
  flex-shrink: 0;
  overflow: auto;
  padding: 1rem;
}
.loom-block-resizable__handle {
  flex: 0 0 auto;
  background: color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  cursor: col-resize;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-resizable[data-orientation="horizontal"] > .loom-block-resizable__handle {
  width: 4px;
  cursor: col-resize;
}
.loom-block-resizable[data-orientation="vertical"] > .loom-block-resizable__handle {
  height: 4px;
  cursor: row-resize;
}
.loom-block-resizable__handle:hover,
.loom-block-resizable__handle:focus-visible {
  background: var(--loom-color-primary, var(--loom-accent));
  outline: none;
}

/* === CmsBlock::ContextMenu ========================================= */
.loom-block-context-menu {
  margin: 0;
  padding: 0.3rem;
  min-width: 12rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 70%, transparent);
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  box-shadow: var(--loom-shadow-lg, 0 18px 40px rgba(0,0,0,0.12));
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  /* popover=manual visibility flips on .showPopover(); CSS just
   * pre-styles the surface so the open transition looks correct. */
}
.loom-block-context-menu__item {
  display: block;
  padding: 0.45rem 0.7rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  background: transparent;
  border: 0;
  border-radius: var(--loom-radius-sm, 6px);
  text-align: left;
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-context-menu__item:hover:not([aria-disabled="true"]):not([disabled]) {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 10%, transparent);
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-block-context-menu__item:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: -1px;
}
.loom-block-context-menu__item[aria-disabled="true"],
.loom-block-context-menu__item[disabled] {
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 40%, transparent);
  cursor: not-allowed;
  pointer-events: none;
}
.loom-block-context-menu__item[data-invalid="true"] {
  color: var(--loom-color-danger, #b91c1c);
}
.loom-block-context-menu__separator {
  border: 0;
  border-top: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 50%, transparent);
  margin: 0.3rem 0;
}

/* === CmsBlock::AspectRatio ========================================= */
.loom-block-aspect-ratio {
  width: 100%;
  overflow: hidden;
  border-radius: var(--loom-radius-sm, 6px);
}
.loom-block-aspect-ratio > * {
  width: 100%;
  height: 100%;
  display: block;
}

/* === CmsBlock::Toolbar ============================================= */
.loom-block-toolbar {
  display: flex;
  gap: 0.4rem;
  padding: 0.4rem;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  flex-wrap: wrap;
  align-items: center;
}
.loom-block-toolbar[data-orientation="vertical"] {
  flex-direction: column;
  align-items: stretch;
  width: max-content;
}

/* === CmsBlock::ToggleGroup ========================================= */
.loom-block-toggle-group {
  display: inline-flex;
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  border-radius: var(--loom-radius-sm, 6px);
  overflow: hidden;
  background: var(--loom-color-surface, var(--loom-bg, #fff));
}
.loom-block-toggle-group__item {
  appearance: none;
  background: transparent;
  border: 0;
  border-right: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  padding: 0.45rem 0.85rem;
  font: inherit;
  font-size: 0.9rem;
  color: var(--loom-color-ink, var(--loom-fg));
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-block-toggle-group__item:last-child {
  border-right: 0;
}
.loom-block-toggle-group__item:hover:not([disabled]) {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 5%, transparent);
}
.loom-block-toggle-group__item[aria-pressed="true"] {
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
}
.loom-block-toggle-group__item:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: -1px;
  z-index: 1;
}
.loom-block-toggle-group__item:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* === CmsBlock::Sheet =============================================== */
.loom-block-sheet {
  padding: 0;
  border: 0;
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  box-shadow: var(--loom-shadow-lg, 0 18px 40px rgba(0,0,0,0.18));
  overflow: hidden;
  max-width: none;
  max-height: none;
}
.loom-block-sheet::backdrop {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.loom-block-sheet[data-edge="right"] {
  top: 0; right: 0; bottom: 0; left: auto;
  height: 100vh;
  width: min(28rem, 100vw);
  margin: 0 0 0 auto;
  border-radius: 0;
}
.loom-block-sheet[data-edge="left"] {
  top: 0; left: 0; bottom: 0; right: auto;
  height: 100vh;
  width: min(28rem, 100vw);
  margin: 0 auto 0 0;
  border-radius: 0;
}
.loom-block-sheet[data-edge="top"] {
  top: 0; left: 0; right: 0; bottom: auto;
  width: 100vw;
  height: min(28rem, 100vh);
  margin: 0 auto auto;
  border-radius: 0;
}
.loom-block-sheet[data-edge="bottom"] {
  bottom: 0; left: 0; right: 0; top: auto;
  width: 100vw;
  height: min(28rem, 100vh);
  margin: auto auto 0;
  border-radius: var(--loom-radius-lg, 18px) var(--loom-radius-lg, 18px) 0 0;
}
.loom-block-sheet__close-row {
  display: flex;
  justify-content: flex-end;
  margin: 0;
  padding: 0.75rem 0.75rem 0;
}
.loom-block-sheet__close {
  appearance: none;
  background: transparent;
  border: 0;
  border-radius: var(--loom-radius-full, 9999px);
  width: 2rem;
  height: 2rem;
  font-size: 1.4rem;
  line-height: 1;
  color: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 70%, transparent);
  cursor: pointer;
}
.loom-block-sheet__close:hover {
  background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 8%, transparent);
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-sheet__close:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 2px;
}
.loom-block-sheet__title {
  margin: 0;
  padding: 0 1.5rem 0.5rem;
  font-family: var(--loom-font-display);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-block-sheet__content {
  padding: 0.5rem 1.5rem 1.5rem;
  overflow-y: auto;
}

/* === CmsBlock::HoverCard =========================================== */
.loom-block-hover-card {
  position: relative;
  display: inline-block;
  border-bottom: 1px dotted color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 50%, transparent);
  cursor: help;
}
.loom-block-hover-card__content {
  position: absolute;
  z-index: 100;
  width: 18rem;
  padding: 1rem;
  border-radius: var(--loom-radius, 10px);
  background: var(--loom-color-surface, var(--loom-bg, #fff));
  color: var(--loom-color-ink, var(--loom-fg));
  border: 1px solid color-mix(in oklab, var(--loom-color-border, var(--loom-border)) 60%, transparent);
  box-shadow: var(--loom-shadow-md, 0 4px 14px rgba(0,0,0,0.08));
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity var(--loom-motion-base, 220ms) var(--loom-ease-out, ease),
              transform var(--loom-motion-base, 220ms) var(--loom-ease-out, ease),
              visibility 0s var(--loom-motion-base, 220ms);
  pointer-events: none;
  font-size: 0.92rem;
  line-height: 1.5;
}
.loom-block-hover-card:hover > .loom-block-hover-card__content,
.loom-block-hover-card:focus-visible > .loom-block-hover-card__content,
.loom-block-hover-card:focus-within > .loom-block-hover-card__content {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 100ms;
  pointer-events: auto;
}
.loom-block-hover-card[data-placement="top"]    > .loom-block-hover-card__content { bottom: calc(100% + 8px); left: 50%; transform: translate(-50%, 4px); }
.loom-block-hover-card[data-placement="top"]:hover    > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="top"]:focus-visible > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="top"]:focus-within  > .loom-block-hover-card__content { transform: translate(-50%, 0); }
.loom-block-hover-card[data-placement="bottom"] > .loom-block-hover-card__content { top: calc(100% + 8px); left: 50%; transform: translate(-50%, -4px); }
.loom-block-hover-card[data-placement="bottom"]:hover > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="bottom"]:focus-visible > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="bottom"]:focus-within  > .loom-block-hover-card__content { transform: translate(-50%, 0); }
.loom-block-hover-card[data-placement="left"]   > .loom-block-hover-card__content { right: calc(100% + 8px); top: 50%; transform: translate(4px, -50%); }
.loom-block-hover-card[data-placement="left"]:hover > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="left"]:focus-visible > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="left"]:focus-within  > .loom-block-hover-card__content { transform: translate(0, -50%); }
.loom-block-hover-card[data-placement="right"]  > .loom-block-hover-card__content { left: calc(100% + 8px); top: 50%; transform: translate(-4px, -50%); }
.loom-block-hover-card[data-placement="right"]:hover > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="right"]:focus-visible > .loom-block-hover-card__content,
.loom-block-hover-card[data-placement="right"]:focus-within  > .loom-block-hover-card__content { transform: translate(0, -50%); }

/* === CmsBlock::Form ================================================ */
.loom-block-form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  max-width: 32rem;
}
.loom-block-form > * {
  margin: 0;
}
.loom-block-form__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 0.5rem;
}
.loom-block-form__submit {
  appearance: none;
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
  border: 0;
  border-radius: var(--loom-radius, 10px);
  padding: 0.6rem 1.25rem;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
  min-height: var(--loom-tap-min, 44px);
}
.loom-block-form__submit:hover {
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 80%, black);
}
.loom-block-form__submit:focus-visible {
  outline: 2px solid var(--loom-color-primary, var(--loom-accent));
  outline-offset: 3px;
}
.loom-block-form[data-invalid="true"] {
  outline: 2px dashed var(--loom-color-danger, #b91c1c);
  outline-offset: 4px;
}

/* === CmsBlock::List ================================================ */
/* Semantic ul/ol primitive. Marker style + indent come from tenant
   [style] config so visual register varies between tenants without
   per-instance overrides. */
.loom-block-list {
  margin: 0;
  padding-left: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  color: var(--loom-color-text, inherit);
  font-size: var(--loom-font-body, 1rem);
  line-height: var(--loom-leading-body, 1.6);
}
.loom-block-list[data-style="unordered"] {
  list-style-type: var(--loom-list-bullet, disc);
}
.loom-block-list[data-style="ordered"] {
  list-style-type: var(--loom-list-marker, decimal);
}
.loom-block-list__item {
  padding-left: 0.25rem;
}
.loom-block-list__item::marker {
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 55%, transparent));
}

/* === CmsBlock::Quote =============================================== */
/* Semantic <blockquote> primitive. Editorial register (rule color,
   indent, italics) flows from tenant [style] config; substrate only
   asserts the structural intent. */
.loom-block-quote {
  margin: 0;
  padding: 0.75rem 1rem 0.75rem 1.25rem;
  border-left: 3px solid var(--loom-color-accent, var(--loom-color-primary, currentColor));
  background: var(--loom-color-quote-bg, transparent);
  color: var(--loom-color-quote-text, var(--loom-color-text, inherit));
  font-style: var(--loom-quote-style, italic);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.loom-block-quote__text {
  margin: 0;
  font-size: var(--loom-font-quote, 1.05rem);
  line-height: var(--loom-leading-quote, 1.55);
}
.loom-block-quote__cite {
  font-style: normal;
  font-size: 0.875rem;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 60%, transparent));
}
.loom-block-quote__cite::before {
  content: "— ";
}

/* === CmsBlock::Code ================================================ */
/* Pre-formatted code block. Substrate emits NO syntax highlighting;
   tenants plug a highlighter (Prism, highlight.js, server-side
   syntect) over the same DOM. Cascade asserts monospace + scrollable
   on overflow + tenant-overridable surface color. */
.loom-block-code {
  margin: 0;
  padding: 0.875rem 1rem;
  background: var(--loom-color-code-bg, color-mix(in oklab, var(--loom-color-text, currentColor) 6%, transparent));
  color: var(--loom-color-code-text, var(--loom-color-text, inherit));
  font-family: var(--loom-font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace);
  font-size: var(--loom-font-code, 0.9rem);
  line-height: var(--loom-leading-code, 1.55);
  border-radius: var(--loom-radius-code, var(--loom-radius-sm, 0.25rem));
  overflow-x: auto;
  white-space: pre;
}
.loom-block-code__code {
  font-family: inherit;
  background: transparent;
  color: inherit;
}

/* === CmsBlock::Grid ================================================ */
/* CSS-grid layout primitive. Explicit column count (clamped 1..=12)
   with token-scale gap. Cascade collapses to a single column at
   narrow viewports per tenant responsive density config.

   Distinct from .loom-block-row / .loom-block-column (flexbox) —
   Grid projects display: grid for cell-based layouts. */
.loom-block-grid {
  display: grid;
  gap: 1rem;
}
.loom-block-grid[data-columns="1"]  { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.loom-block-grid[data-columns="2"]  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.loom-block-grid[data-columns="3"]  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.loom-block-grid[data-columns="4"]  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.loom-block-grid[data-columns="5"]  { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.loom-block-grid[data-columns="6"]  { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.loom-block-grid[data-columns="7"]  { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.loom-block-grid[data-columns="8"]  { grid-template-columns: repeat(8, minmax(0, 1fr)); }
.loom-block-grid[data-columns="9"]  { grid-template-columns: repeat(9, minmax(0, 1fr)); }
.loom-block-grid[data-columns="10"] { grid-template-columns: repeat(10, minmax(0, 1fr)); }
.loom-block-grid[data-columns="11"] { grid-template-columns: repeat(11, minmax(0, 1fr)); }
.loom-block-grid[data-columns="12"] { grid-template-columns: repeat(12, minmax(0, 1fr)); }
.loom-block-grid[data-gap="none"] { gap: 0; }
.loom-block-grid[data-gap="xs"]   { gap: 0.25rem; }
.loom-block-grid[data-gap="sm"]   { gap: 0.5rem; }
.loom-block-grid[data-gap="md"]   { gap: 1rem; }
.loom-block-grid[data-gap="lg"]   { gap: 2rem; }
.loom-block-grid[data-gap="xl"]   { gap: 3rem; }
.loom-block-grid[data-gap="xxl"]  { gap: 4rem; }
@media (max-width: 640px) {
  .loom-block-grid[data-columns]:not([data-columns="1"]) {
    grid-template-columns: 1fr;
  }
}

/* === CmsBlock::Card ================================================ */
/* Visual card surface with header / body / footer slots. Substrate
   asserts structure + slot data-attrs; visual register (surface
   color, radius, shadow, border) flows from tenant [style] config. */
.loom-block-card {
  display: flex;
  flex-direction: column;
  background: var(--loom-color-surface, color-mix(in oklab, var(--loom-color-text, currentColor) 3%, transparent));
  color: var(--loom-color-text, inherit);
  border: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 12%, transparent));
  border-radius: var(--loom-radius-card, var(--loom-radius-md, 0.5rem));
  box-shadow: var(--loom-shadow-card, 0 1px 2px rgb(0 0 0 / 0.04));
  overflow: hidden;
}
.loom-block-card__header,
.loom-block-card__body,
.loom-block-card__footer {
  padding: var(--loom-card-padding, 1rem 1.25rem);
}
.loom-block-card__header {
  border-bottom: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
}
.loom-block-card__footer {
  border-top: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 1.5%, transparent));
}

/* === CmsBlock::Video =============================================== */
/* Native <video> embed. Substrate emits controls + preload="metadata"
   only; autoplay / muted-autoplay / scroll-trigger are intentionally
   absent (engagement-pattern decisions live at the tenant tier). */
.loom-block-video {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 4%, transparent));
  border-radius: var(--loom-radius-video, var(--loom-radius-md, 0.5rem));
}
.loom-block-video__fallback {
  padding: 1rem;
  font-size: 0.9rem;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 60%, transparent));
}

/* === CmsBlock::Iframe ============================================== */
/* Sandboxed third-party embed. Substrate emits sandbox + lazy-load +
   no-referrer; visual chrome (aspect ratio, border, radius) flows
   from tenant [style] config. */
.loom-block-iframe {
  display: block;
  width: 100%;
  max-width: 100%;
  border: 0;
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 4%, transparent));
  border-radius: var(--loom-radius-iframe, var(--loom-radius-md, 0.5rem));
  aspect-ratio: var(--loom-iframe-aspect, 16 / 9);
}

/* === CmsBlock::Badge =============================================== */
/* Inline status pill. Substrate emits structure + tone data-attr;
   visual register flows from tenant [style] config via the canonical
   tone tokens. Non-interactive — distinct from CmsBlock::Button. */
.loom-block-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.25;
  border-radius: var(--loom-radius-badge, 9999px);
  border: 1px solid transparent;
  white-space: nowrap;
}
.loom-block-badge[data-tone="neutral"] {
  background: var(--loom-color-badge-neutral-bg, color-mix(in oklab, var(--loom-color-text, currentColor) 8%, transparent));
  color: var(--loom-color-badge-neutral-text, var(--loom-color-text, inherit));
  border-color: var(--loom-color-badge-neutral-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
}
.loom-block-badge[data-tone="info"] {
  background: var(--loom-color-badge-info-bg, #e0f2fe);
  color: var(--loom-color-badge-info-text, #075985);
  border-color: var(--loom-color-badge-info-border, #7dd3fc);
}
.loom-block-badge[data-tone="success"] {
  background: var(--loom-color-badge-success-bg, #dcfce7);
  color: var(--loom-color-badge-success-text, #166534);
  border-color: var(--loom-color-badge-success-border, #86efac);
}
.loom-block-badge[data-tone="warning"] {
  background: var(--loom-color-badge-warning-bg, #fef3c7);
  color: var(--loom-color-badge-warning-text, #92400e);
  border-color: var(--loom-color-badge-warning-border, #fcd34d);
}
.loom-block-badge[data-tone="danger"] {
  background: var(--loom-color-badge-danger-bg, #fee2e2);
  color: var(--loom-color-badge-danger-text, #991b1b);
  border-color: var(--loom-color-badge-danger-border, #fca5a5);
}
.loom-block-badge[data-tone="accent"] {
  background: var(--loom-color-badge-accent-bg, color-mix(in oklab, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)) 18%, transparent));
  color: var(--loom-color-badge-accent-text, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)));
  border-color: var(--loom-color-badge-accent-border, color-mix(in oklab, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)) 35%, transparent));
}

/* === CmsBlock::Avatar ============================================== */
/* User / brand avatar with initials fallback. Substrate asserts
   structure + size data-attr; visual register (fill, radius, font)
   flows from tenant [style] config. */
.loom-block-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--loom-color-avatar-bg, color-mix(in oklab, var(--loom-color-text, currentColor) 8%, transparent));
  color: var(--loom-color-avatar-text, var(--loom-color-text, inherit));
  border-radius: var(--loom-radius-avatar, 9999px);
  font-weight: 600;
  user-select: none;
  flex-shrink: 0;
}
.loom-block-avatar[data-size="sm"] {
  width: var(--loom-avatar-sm, 1.75rem);
  height: var(--loom-avatar-sm, 1.75rem);
  font-size: 0.625rem;
}
.loom-block-avatar[data-size="md"] {
  width: var(--loom-avatar-md, 2.5rem);
  height: var(--loom-avatar-md, 2.5rem);
  font-size: 0.875rem;
}
.loom-block-avatar[data-size="lg"] {
  width: var(--loom-avatar-lg, 3.5rem);
  height: var(--loom-avatar-lg, 3.5rem);
  font-size: 1.125rem;
}
.loom-block-avatar__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.loom-block-avatar__initials {
  line-height: 1;
  letter-spacing: 0.02em;
}

/* === CmsBlock::Alert =============================================== */
/* Block-level banner notification. Reuses the Badge tone palette
   (--loom-color-badge-<tone>-{bg,text,border}) so a tenant's
   [style] config drives both inline pills and block banners
   through the same six semantic tokens.

   role=alert/status set by the renderer per WAI-ARIA Authoring
   Practices: alert for Danger/Warning (urgent), status for the
   informational tones. */
.loom-block-alert {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 0.875rem 1rem;
  border: 1px solid transparent;
  border-radius: var(--loom-radius-alert, var(--loom-radius-md, 0.5rem));
  margin: 0;
}
.loom-block-alert__title {
  font-weight: 700;
  line-height: 1.3;
}
.loom-block-alert__body {
  margin: 0;
  line-height: 1.55;
}
.loom-block-alert[data-tone="neutral"] {
  background: var(--loom-color-badge-neutral-bg, color-mix(in oklab, var(--loom-color-text, currentColor) 6%, transparent));
  color: var(--loom-color-badge-neutral-text, var(--loom-color-text, inherit));
  border-color: var(--loom-color-badge-neutral-border, color-mix(in oklab, var(--loom-color-text, currentColor) 16%, transparent));
}
.loom-block-alert[data-tone="info"] {
  background: var(--loom-color-badge-info-bg, #e0f2fe);
  color: var(--loom-color-badge-info-text, #075985);
  border-color: var(--loom-color-badge-info-border, #7dd3fc);
}
.loom-block-alert[data-tone="success"] {
  background: var(--loom-color-badge-success-bg, #dcfce7);
  color: var(--loom-color-badge-success-text, #166534);
  border-color: var(--loom-color-badge-success-border, #86efac);
}
.loom-block-alert[data-tone="warning"] {
  background: var(--loom-color-badge-warning-bg, #fef3c7);
  color: var(--loom-color-badge-warning-text, #92400e);
  border-color: var(--loom-color-badge-warning-border, #fcd34d);
}
.loom-block-alert[data-tone="danger"] {
  background: var(--loom-color-badge-danger-bg, #fee2e2);
  color: var(--loom-color-badge-danger-text, #991b1b);
  border-color: var(--loom-color-badge-danger-border, #fca5a5);
}
.loom-block-alert[data-tone="accent"] {
  background: var(--loom-color-badge-accent-bg, color-mix(in oklab, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)) 14%, transparent));
  color: var(--loom-color-badge-accent-text, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)));
  border-color: var(--loom-color-badge-accent-border, color-mix(in oklab, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)) 30%, transparent));
}

/* === CmsBlock::Table =============================================== */
/* Semantic data table. Substrate asserts <table>/<thead>/<tbody>/
   <caption>/scope=col; tenant [style] config drives the visual
   register (zebra striping, border, density). */
.loom-block-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--loom-font-table, 0.9375rem);
  line-height: 1.45;
  color: var(--loom-color-text, inherit);
}
.loom-block-table__caption {
  caption-side: top;
  text-align: left;
  padding: 0 0 0.5rem;
  font-weight: 600;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
}
.loom-block-table__th {
  text-align: left;
  font-weight: 600;
  padding: 0.625rem 0.75rem;
  border-bottom: 2px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 4%, transparent));
}
.loom-block-table__td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  vertical-align: top;
}
.loom-block-table__tr:nth-child(even) > .loom-block-table__td {
  background: var(--loom-color-table-stripe, color-mix(in oklab, var(--loom-color-text, currentColor) 2%, transparent));
}

/* === CmsBlock::DefinitionList ====================================== */
/* Semantic <dl>/<dt>/<dd> primitive. The HTML spec's purpose-built
   shape for key/value content. Substrate asserts structure; tenant
   [style] config drives the visual register (term weight, desc
   indent, density). */
.loom-block-deflist {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: 0.5rem 1.5rem;
  color: var(--loom-color-text, inherit);
  font-size: var(--loom-font-body, 1rem);
  line-height: 1.55;
}
.loom-block-deflist__term {
  font-weight: 600;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
}
.loom-block-deflist__desc {
  margin: 0;
}
@media (max-width: 640px) {
  .loom-block-deflist {
    grid-template-columns: 1fr;
    gap: 0.125rem 0;
  }
  .loom-block-deflist__desc {
    margin-bottom: 0.5rem;
  }
}

/* === CmsBlock::Stat ================================================ */
/* Single-number callout. Semantic <dl>/<dt>/<dd> so the value is the
   description of the label. Substrate asserts structure + trend
   data-attr; tenant [style] drives font scale + trend colors. */
.loom-block-stat {
  margin: 0;
  display: flex;
  flex-direction: column-reverse;
  gap: 0.125rem;
}
.loom-block-stat__label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 65%, transparent));
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.loom-block-stat__value {
  margin: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
}
.loom-block-stat__value-text {
  font-size: var(--loom-font-stat, 2.5rem);
  font-weight: 700;
  line-height: 1;
  color: var(--loom-color-text, inherit);
  font-variant-numeric: tabular-nums;
}
.loom-block-stat__trend {
  font-size: 0.875rem;
  font-weight: 600;
}
.loom-block-stat__trend[data-trend="up"]   { color: var(--loom-color-trend-up, #16a34a); }
.loom-block-stat__trend[data-trend="down"] { color: var(--loom-color-trend-down, #dc2626); }
.loom-block-stat__trend[data-trend="flat"] { color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 60%, transparent)); }

/* === CmsBlock::Timeline ============================================ */
/* Sequential events display. Semantic <ol> with one <li> per event;
   substrate ships the vertical rail + node marker, tenants restyle
   via cascade variables. */
.loom-block-timeline {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0 0 0 2rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.loom-block-timeline::before {
  content: "";
  position: absolute;
  left: 0.625rem;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 2px;
  background: var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
  border-radius: 1px;
}
.loom-block-timeline__item {
  position: relative;
}
.loom-block-timeline__marker {
  position: absolute;
  left: -2rem;
  top: 0.375rem;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 9999px;
  background: var(--loom-color-accent, var(--loom-color-primary, currentColor));
  border: 2px solid var(--loom-color-bg, #fff);
  box-shadow: 0 0 0 2px var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
}
.loom-block-timeline__body {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.loom-block-timeline__when {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 60%, transparent));
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
.loom-block-timeline__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--loom-color-text, inherit);
  line-height: 1.3;
}
.loom-block-timeline__desc {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--loom-color-text, inherit);
}

/* === CmsBlock::ProgressBar ========================================= */
/* Native HTML <progress> with tenant-tone styling. The base
   <progress> element ships with browser-default chrome; the cascade
   normalises and tones it via :: pseudo-elements (WebKit + Firefox
   selectors). */
.loom-block-progress {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.loom-block-progress__label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 65%, transparent));
}
.loom-block-progress__bar {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 0.5rem;
  border: 0;
  border-radius: 9999px;
  background: var(--loom-color-progress-track, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  overflow: hidden;
}
/* WebKit / Blink */
.loom-block-progress__bar::-webkit-progress-bar {
  background: var(--loom-color-progress-track, color-mix(in oklab, var(--loom-color-text, currentColor) 10%, transparent));
  border-radius: 9999px;
}
.loom-block-progress__bar::-webkit-progress-value {
  border-radius: 9999px;
  transition: width 220ms ease;
}
/* Firefox */
.loom-block-progress__bar::-moz-progress-bar {
  border-radius: 9999px;
  transition: width 220ms ease;
}
.loom-block-progress__value {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--loom-color-text, inherit);
  align-self: flex-end;
  font-variant-numeric: tabular-nums;
}
.loom-block-progress[data-tone="neutral"]  .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-badge-neutral-text, currentColor); }
.loom-block-progress[data-tone="neutral"]  .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-badge-neutral-text, currentColor); }
.loom-block-progress[data-tone="info"]     .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-badge-info-text, #0284c7); }
.loom-block-progress[data-tone="info"]     .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-badge-info-text, #0284c7); }
.loom-block-progress[data-tone="success"]  .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-badge-success-text, #16a34a); }
.loom-block-progress[data-tone="success"]  .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-badge-success-text, #16a34a); }
.loom-block-progress[data-tone="warning"]  .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-badge-warning-text, #ca8a04); }
.loom-block-progress[data-tone="warning"]  .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-badge-warning-text, #ca8a04); }
.loom-block-progress[data-tone="danger"]   .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-badge-danger-text, #dc2626); }
.loom-block-progress[data-tone="danger"]   .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-badge-danger-text, #dc2626); }
.loom-block-progress[data-tone="accent"]   .loom-block-progress__bar::-webkit-progress-value { background: var(--loom-color-accent, var(--loom-color-primary, #3a7afe)); }
.loom-block-progress[data-tone="accent"]   .loom-block-progress__bar::-moz-progress-bar      { background: var(--loom-color-accent, var(--loom-color-primary, #3a7afe)); }

/* === CmsBlock::EmptyState ========================================== */
/* Canonical "no items yet" pattern. Centered title + supporting prose
   + optional action link. Tenant cascade drives spacing + accent
   colors; substrate ships an a11y-correct shape (role=status, ordered
   text hierarchy). */
.loom-block-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  text-align: center;
  padding: 2.5rem 1.5rem;
  border: 1px dashed var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
  border-radius: var(--loom-radius-empty, var(--loom-radius-md, 0.75rem));
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 3%, transparent));
  color: var(--loom-color-text, inherit);
}
.loom-block-empty__title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.3;
}
.loom-block-empty__body {
  margin: 0;
  max-width: 28rem;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 65%, transparent));
  font-size: 0.9375rem;
  line-height: 1.55;
}
.loom-block-empty__action {
  display: inline-flex;
  align-items: center;
  margin-top: 0.5rem;
  padding: 0.5rem 1rem;
  background: var(--loom-color-accent, var(--loom-color-primary, #3a7afe));
  color: var(--loom-color-on-accent, #fff);
  border-radius: var(--loom-radius-button, var(--loom-radius-sm, 0.375rem));
  font-weight: 600;
  font-size: 0.9375rem;
  text-decoration: none;
  transition: opacity 150ms ease;
}
.loom-block-empty__action:hover {
  opacity: 0.92;
}
.loom-block-empty__action:focus-visible {
  outline: 2px solid var(--loom-color-focus, var(--loom-color-accent, #3a7afe));
  outline-offset: 2px;
}

/* === CmsBlock::Stepper ============================================= */
/* Multi-step process indicator. Semantic <ol> with one <li> per step;
   per-step data-state drives color + checkmark / number rendering. */
.loom-block-stepper {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.loom-block-stepper__step {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.625rem;
  border-radius: 9999px;
  font-size: 0.875rem;
  font-weight: 500;
  transition: background-color 150ms ease;
}
.loom-block-stepper__index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 700;
  background: currentColor;
  color: var(--loom-color-bg, #fff);
  flex-shrink: 0;
}
.loom-block-stepper__label {
  white-space: nowrap;
}
.loom-block-stepper__step[data-state="done"] {
  color: var(--loom-color-stepper-done, var(--loom-color-badge-success-text, #16a34a));
  background: var(--loom-color-stepper-done-bg, color-mix(in oklab, var(--loom-color-stepper-done, #16a34a) 12%, transparent));
}
.loom-block-stepper__step[data-state="current"] {
  color: var(--loom-color-stepper-current, var(--loom-color-accent, var(--loom-color-primary, #3a7afe)));
  background: var(--loom-color-stepper-current-bg, color-mix(in oklab, var(--loom-color-stepper-current, #3a7afe) 14%, transparent));
}
.loom-block-stepper__step[data-state="upcoming"] {
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 50%, transparent));
  background: transparent;
}

/* === CmsBlock::KbdShortcut ========================================= */
/* Semantic <kbd> elements with cascade-driven keycap appearance.
   Tenant cascade overrides via --loom-color-kbd-{bg,text,border}. */
.loom-block-kbd {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  vertical-align: baseline;
  font-family: var(--loom-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}
.loom-block-kbd__key {
  display: inline-block;
  padding: 0.0625rem 0.4rem;
  background: var(--loom-color-kbd-bg, color-mix(in oklab, var(--loom-color-text, currentColor) 5%, transparent));
  color: var(--loom-color-kbd-text, var(--loom-color-text, inherit));
  border: 1px solid var(--loom-color-kbd-border, color-mix(in oklab, var(--loom-color-text, currentColor) 18%, transparent));
  border-bottom-width: 2px;
  border-radius: var(--loom-radius-kbd, 0.25rem);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  box-shadow: inset 0 -1px 0 0 var(--loom-color-kbd-border, color-mix(in oklab, var(--loom-color-text, currentColor) 12%, transparent));
}
.loom-block-kbd__sep {
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 50%, transparent));
  font-size: 0.8125rem;
  font-weight: 500;
  user-select: none;
}

/* === CmsBlock::Marquee ============================================= */
/* Horizontally-scrolling marquee — logo wall / news ticker / press
   mentions. Items are duplicated in the DOM (substrate-emitted) so the
   CSS keyframe loops seamlessly. The second copy is aria-hidden so
   screen readers don't announce duplicates.

   Honours prefers-reduced-motion: animation disabled, first group
   shown statically. No JS layer. */
.loom-block-marquee {
  overflow: hidden;
  width: 100%;
  position: relative;
  mask-image: linear-gradient(
    to right,
    transparent,
    black 8%,
    black 92%,
    transparent
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent,
    black 8%,
    black 92%,
    transparent
  );
}
.loom-block-marquee__track {
  display: flex;
  width: max-content;
  gap: 3rem;
  animation: loom-marquee-scroll var(--loom-marquee-duration, 40s) linear infinite;
}
.loom-block-marquee__group {
  display: flex;
  align-items: center;
  gap: 3rem;
  flex-shrink: 0;
}
.loom-block-marquee[data-speed="slow"]   .loom-block-marquee__track { --loom-marquee-duration: 60s; }
.loom-block-marquee[data-speed="medium"] .loom-block-marquee__track { --loom-marquee-duration: 40s; }
.loom-block-marquee[data-speed="fast"]   .loom-block-marquee__track { --loom-marquee-duration: 24s; }
.loom-block-marquee[data-direction="right"] .loom-block-marquee__track {
  animation-direction: reverse;
}
@keyframes loom-marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .loom-block-marquee__track {
    animation: none;
  }
  /* Hide the cloned group so static rendering doesn't show duplicates */
  .loom-block-marquee__group[aria-hidden="true"] {
    display: none;
  }
}

/* === CmsBlock::Carousel ============================================ */
/* Slide gallery via CSS scroll-snap. Native swipe + keyboard arrow
   navigation; no JS required. Screen readers navigate slide-by-slide
   via the per-slide aria-label="N of total". */
.loom-block-carousel {
  width: 100%;
  overflow: hidden;
  border-radius: var(--loom-radius-carousel, var(--loom-radius-md, 0.75rem));
}
.loom-block-carousel__track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  /* Track shows native scrollbar on hover for keyboard discoverability */
  scrollbar-width: thin;
}
.loom-block-carousel__slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  padding: 1rem;
  min-height: var(--loom-carousel-slide-min-height, 12rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
@media (prefers-reduced-motion: reduce) {
  .loom-block-carousel__track {
    scroll-behavior: auto;
  }
}

/* === NavStyle / FooterStyle variants (#331) ======================== */
/* Substrate ships four NavStyle + four FooterStyle variants. Cascade
   keys off the `data-loom-nav-style` / `data-loom-footer-style`
   attributes the renderer sets per the typed enum. Each variant
   delivers a meaningfully different visual register — not just
   color tweaks — so tenants composing identical CMS content render
   distinctly when they pick different styles. */

/* NavStyle::Centered — brand centred between two link halves. */
nav[data-loom-nav-style="centered"] > div {
  justify-content: space-between;
}
nav[data-loom-nav-style="centered"] > div > a:first-child {
  order: 2;
  margin: 0 auto;
}
nav[data-loom-nav-style="centered"] > div > div.hidden {
  order: 1;
  flex: 1;
}

/* NavStyle::Compact — tighter chrome. */
nav[data-loom-nav-style="compact"] {
  padding-top: 0.5rem !important;
  padding-bottom: 0.5rem !important;
}
nav[data-loom-nav-style="compact"] span.font-display {
  font-size: 1rem;
}
nav[data-loom-nav-style="compact"] > div {
  gap: 1rem !important;
}

/* FooterStyle::Minimal — hide column blocks; keep brand + bottom band. */
footer[data-loom-footer-style="minimal"] .grid {
  display: block;
}
footer[data-loom-footer-style="minimal"] .grid > div + div {
  display: none;
}
footer[data-loom-footer-style="minimal"] {
  padding-top: 2.5rem;
  padding-bottom: 2.5rem;
}

/* FooterStyle::Dense — tighter footer chrome. */
footer[data-loom-footer-style="dense"] {
  padding-top: 2rem;
  padding-bottom: 2rem;
  font-size: 0.875rem;
}
footer[data-loom-footer-style="dense"] .grid {
  gap: 1.5rem;
}
footer[data-loom-footer-style="dense"] h3 {
  font-size: 0.95rem;
}

/* === CmsSection::HeroSplit ========================================= */
/* Image-on-one-side, text-on-the-other hero. Cascade flips
   flex-direction based on data-image-side. */
.loom-section-hero-split {
  padding: 5rem 0;
  background: var(--loom-color-hero-bg, var(--loom-color-bg, transparent));
}
.loom-section-hero-split__grid {
  display: flex;
  align-items: center;
  gap: 3rem;
  max-width: 80rem;
  margin: 0 auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}
.loom-section-hero-split[data-image-side="left"] .loom-section-hero-split__grid {
  flex-direction: row-reverse;
}
.loom-section-hero-split__text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.loom-section-hero-split__title {
  margin: 0;
  font-size: var(--loom-font-hero, 3rem);
  font-weight: 700;
  line-height: 1.1;
  color: var(--loom-color-text, inherit);
}
/* #588: step the hero title down below the 768 md boundary so a long
 * title (e.g. "We Are PlausiDen LLC") wraps to 2 lines on phones like a
 * mainstream Tailwind text-4xl→5xl hero, not 3. Capped by min() so a
 * tenant whose --loom-font-hero is already <2.25rem is never enlarged.
 * Generic; desktop/tablet (≥768) stay byte-identical. */
@media (max-width: 767.98px) {
  .loom-section-hero-split__title {
    font-size: min(var(--loom-font-hero, 3rem), 2.25rem);
  }
}
.loom-section-hero-split__lede {
  margin: 0;
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
  max-width: 36rem;
}
.loom-section-hero-split__cta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  padding: 0.75rem 1.5rem;
  background: var(--loom-color-accent, var(--loom-color-primary, #3a7afe));
  color: var(--loom-color-on-accent, #fff);
  border-radius: var(--loom-radius-button, var(--loom-radius-md, 0.5rem));
  font-weight: 600;
  text-decoration: none;
}
.loom-section-hero-split__media {
  flex: 1;
}
.loom-section-hero-split__image {
  width: 100%;
  height: auto;
  border-radius: var(--loom-radius-image, var(--loom-radius-md, 0.75rem));
  /* #586: opt-in hero-image elevation + hairline. Both default to
     none so a HeroSplit without [style.image] hero_shadow / hero_border
     renders byte-identically to the pre-#586 markup. Scoped to the hero
     image (not the shared --loom-img-shadow) so an inline content figure
     keeps its own lighter shadow — the hero image is a distinct, larger
     elevation slot, matching how real marketing heroes lift the image. */
  box-shadow: var(--loom-img-hero-shadow, none);
  border: var(--loom-img-hero-border, none);
}
.loom-section-hero-split__placeholder {
  aspect-ratio: 4 / 3;
  background: var(--loom-color-surface-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 5%, transparent));
  border-radius: var(--loom-radius-image, var(--loom-radius-md, 0.75rem));
}
@media (max-width: 768px) {
  .loom-section-hero-split__grid {
    flex-direction: column !important;
  }
}
/* #582: HeroSplit eyebrow + extra body paragraphs + secondary CTA.
   All opt-in — a HeroSplit with none of these renders byte-identically
   to the pre-#582 markup. The eyebrow mirrors the ImageTextRow eyebrow
   (same uppercase/tracking/badge treatment) so a page eyebrow looks
   identical whichever primitive emits it. */
.loom-section-hero-split__eyebrow {
  margin: 0;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--loom-color-primary, var(--loom-primary));
}
.loom-section-hero-split__eyebrow--badge {
  display: inline-block;
  align-self: flex-start;
  padding: 0.4rem 0.95rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 32%, transparent);
  font-family: var(--loom-font-body);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  text-transform: none;
}
.loom-section-hero-split__eyebrow-icon {
  display: inline-flex;
  align-items: center;
  margin-right: 0.4rem;
  vertical-align: -0.15em;
}
.loom-section-hero-split__eyebrow-icon-svg {
  width: 1em;
  height: 1em;
}
.loom-section-hero-split__body {
  margin: 0;
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 70%, transparent));
  max-width: 36rem;
}
.loom-section-hero-split__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
  align-self: flex-start;
}
.loom-section-hero-split__cta.loom-section-hero-split__cta--secondary {
  background: transparent;
  color: var(--loom-color-text, inherit);
  border: 1px solid var(--loom-color-border, color-mix(in oklab, var(--loom-color-text, currentColor) 22%, transparent));
}

/* === CmsSection::HeroMinimal ======================================= */
/* Text-only hero. No decoration, no eyebrow, no background tint.
   Just title + optional lede + optional CTA, centered. */
.loom-section-hero-minimal {
  padding: 4rem 1.5rem 3rem;
  text-align: left;
  max-width: 56rem;
  margin: 0 auto;
}
.loom-section-hero-minimal__title {
  margin: 0;
  font-size: var(--loom-font-hero, 2.75rem);
  font-weight: 700;
  line-height: 1.15;
  color: var(--loom-color-text, inherit);
}
.loom-section-hero-minimal__lede {
  margin: 1rem 0 0;
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 65%, transparent));
}
.loom-section-hero-minimal__cta {
  display: inline-flex;
  margin-top: 1.5rem;
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 0.125rem;
  text-underline-offset: 0.25rem;
  color: var(--loom-color-accent, var(--loom-color-primary, #3a7afe));
}

/* === CmsBlock::Figure ============================================== */
/* Semantic <figure> with <img> + <figcaption>. Substrate asserts
   structure; visual register (radius, caption font, credit weight)
   flows from tenant [style] config. */
.loom-block-figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.loom-block-figure__image {
  width: 100%;
  height: auto;
  border-radius: var(--loom-img-radius, var(--loom-radius-figure, var(--loom-radius-md, 0.5rem)));
}
.loom-block-figure__caption {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--loom-color-muted, color-mix(in oklab, var(--loom-color-text, currentColor) 65%, transparent));
}
.loom-block-figure__caption-text {
  color: var(--loom-color-text, inherit);
}
.loom-block-figure__credit {
  font-size: 0.75rem;
  font-style: italic;
  opacity: 0.85;
}

/* ===================================================================
 * loom-hero-slideshow — pure-CSS auto-rotating hero with text overlay
 * Substrate-general; any tenant can use. No JS. Respects
 * prefers-reduced-motion (slides held statically when set).
 * ===================================================================
 */
.loom-hero-slideshow {
  position: relative;
  display: block;
  width: 100%;
  min-height: clamp(360px, 50vw, 640px);
  overflow: hidden;
  background: var(--loom-color-surface, #ffffff);
}
.loom-hero-slideshow__slide {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  transition: opacity 600ms ease-in-out;
}
.loom-hero-slideshow__slide[data-active="true"] {
  /* First slide gets a small head start so the page does not load
     blank for the duration of the first interval. */
  opacity: 1;
}
.loom-hero-slideshow__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--loom-hero-bg-position, 75% center);
  z-index: 0;
}
.loom-hero-slideshow__overlay {
  position: relative;
  z-index: 1;
  max-width: 56ch;
  padding: clamp(1.5rem, 5vw, 4rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  color: var(--loom-color-on-hero, var(--loom-color-ink, #111));
  text-shadow: 0 1px 2px rgba(255,255,255,0.6);
}
.loom-hero-slideshow--align-start  .loom-hero-slideshow__overlay { margin-right: auto; }
.loom-hero-slideshow--align-center .loom-hero-slideshow__overlay { margin: 0 auto; text-align: center; }
.loom-hero-slideshow--align-end    .loom-hero-slideshow__overlay { margin-left: auto; text-align: right; }
.loom-hero-slideshow__eyebrow {
  margin: 0 0 .5em;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--loom-color-primary);
}
.loom-hero-slideshow__title {
  margin: 0 0 .5em;
  /* Hero title defaults to the premium display face, but a tenant can
     opt it to another stack via [style.fonts] hero_title (→
     --loom-font-hero-title) without affecting other headings. */
  font-family: var(--loom-font-hero-title, var(--loom-font-display, var(--loom-font-body)));
  font-size: clamp(1.4rem, 2.8vw, 2.25rem);
  font-weight: 700;
  line-height: 1.1;
}
.loom-hero-slideshow__lede {
  margin: 0 0 1.25em;
  font-size: clamp(1rem, 1.5vw, 1.25rem);
  line-height: 1.55;
  max-width: 50ch;
}
.loom-hero-slideshow__lede--accent {
  font-style: italic;
  margin-top: -1.2em;
}
.loom-hero-slideshow__slide .loom-button {
  align-self: flex-start;
}
.loom-hero-slideshow__cta-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.25rem;
}
.loom-hero-slideshow__cta-row .loom-button {
  align-self: auto;
  padding: 0.9em 1.6em;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 0;
  /* Hero CTAs carry a thin accent-color outline over their fill (the
   * base button's border is transparent). Token-driven, so each tenant's
   * own accent supplies the edge; where accent == fill it reads as none. */
  border-color: var(--loom-color-accent);
}
.loom-hero-slideshow--align-center .loom-hero-slideshow__slide .loom-button { align-self: center; }
.loom-hero-slideshow--align-end    .loom-hero-slideshow__slide .loom-button { align-self: flex-end; }

/* Hero slideshow OVERLAY layout: text card sits over the full-bleed
   image. White card bg with shadow, bounded width, left-aligned by
   default (align-class controls horizontal placement). */
.loom-hero-slideshow--overlay .loom-hero-slideshow__overlay {
  background: color-mix(in oklab, var(--loom-color-bg, #fff) 92%, transparent);
  color: var(--loom-color-ink, #111);
  text-shadow: none;
  max-width: 36rem;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  margin: clamp(1rem, 4vw, 3rem);
  border-radius: var(--loom-radius-md, 6px);
  box-shadow: var(--loom-shadow-md, 0 4px 24px rgba(0,0,0,.15));
  height: auto;
  align-self: center;
}
.loom-hero-slideshow--overlay .loom-hero-slideshow__eyebrow,
.loom-hero-slideshow--overlay .loom-hero-slideshow__title,
.loom-hero-slideshow--overlay .loom-hero-slideshow__lede {
  text-shadow: none;
}
.loom-hero-slideshow--overlay .loom-hero-slideshow__cta-row .loom-button {
  padding: 0.7rem 1.1rem !important;
  font-size: 0.85rem !important;
}
.loom-hero-slideshow--overlay .loom-hero-slideshow__title {
  color: var(--loom-color-ink, #111);
}
.loom-hero-slideshow--overlay .loom-hero-slideshow__lede {
  color: var(--loom-color-ink, #111);
}
.loom-hero-slideshow--overlay {
  min-height: clamp(360px, 30vw, 480px);
}
.loom-hero-slideshow--split {
  min-height: clamp(320px, 28vw, 440px);
}
@media (max-width: 768px) {
  .loom-hero-slideshow--overlay .loom-hero-slideshow__overlay {
    margin: 1rem;
    max-width: none;
    padding: 1.25rem;
  }
}

/* Hero slideshow prev/next nav arrows — substrate-general primitive.
   Renders as round buttons stacked vertically on the right edge. */
.loom-hero-slideshow__nav {
  position: absolute;
  right: clamp(0.5rem, 2vw, 1.5rem);
  z-index: 10;
  width: 36px;
  height: 36px;
  border-radius: var(--loom-radius-sm, 4px);
  border: 1px solid var(--loom-color-border, rgba(255,255,255,0.4));
  background: rgba(255, 255, 255, 0.85);
  color: var(--loom-color-ink, #111);
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-hero-slideshow__nav:hover {
  background: var(--loom-color-primary, var(--loom-color-ink));
  color: var(--loom-color-on-primary, #fff);
}
.loom-hero-slideshow__nav::before {
  font-size: 18px;
  line-height: 1;
  font-weight: 700;
}
.loom-hero-slideshow__nav--prev {
  top: calc(50% - 40px);
}
.loom-hero-slideshow__nav--prev::before { content: "‹"; }
.loom-hero-slideshow__nav--next {
  top: calc(50% + 4px);
}
.loom-hero-slideshow__nav--next::before { content: "›"; }
/* Split layout: dock the prev/next controls at the text↔image seam
   (centered on 50%) and stack next-over-prev, matching the editorial
   split-hero pattern. Scoped to --split so the overlay mode keeps its
   right-edge controls. */
.loom-hero-slideshow--split .loom-hero-slideshow__nav {
  right: auto;
  left: calc(50% - 18px);
}
.loom-hero-slideshow--split .loom-hero-slideshow__nav--next { top: calc(50% - 40px); }
.loom-hero-slideshow--split .loom-hero-slideshow__nav--prev { top: calc(50% + 4px); }
/* Mobile: the split hero restacks vertically at <=768px (see the split
   layout media query further down), so the seam-centered controls lose
   the seam they dock to. Reflow them to the right edge — the primitive's
   default overlay position — so prev/next sit on the right on phones. */
@media (max-width: 768px) {
  .loom-hero-slideshow--split .loom-hero-slideshow__nav {
    left: auto;
    right: clamp(0.5rem, 3vw, 1rem);
  }
  .loom-hero-slideshow--split .loom-hero-slideshow__nav--prev { top: calc(50% - 40px); }
  .loom-hero-slideshow--split .loom-hero-slideshow__nav--next { top: calc(50% + 4px); }
}

@keyframes loom-hero-slideshow-cycle {
  /* Each slide is visible for ~1/N of the cycle with smooth fade.
     Working for N=2..=6 with --loom-slideshow-slide-count to
     parameterize. Calibrated tight enough that overlap is minimal. */
  0%   { opacity: 0; }
  3%   { opacity: 1; }
  22%  { opacity: 1; }
  26%  { opacity: 0; }
  100% { opacity: 0; }
}
/* Static-fallback for chromiumoxide / screenshots: only the
   data-active slide gets opacity:1, preventing all-stacked render
   issues during capture. Animation still drives interactive cycle. */
.loom-hero-slideshow__slide:not([data-active="true"]) {
  pointer-events: none;
}

/* Per-slide stagger driven by static rules, NOT inline style. The
   page-shell CSP hash-pins only the critical-inline <style> block, so
   inline `style=` attributes on slides are silently dropped — which
   left every slide at --loom-slideshow-slide-index:0, so all slides
   animated in lockstep and stacked on top of each other. Slides are
   always the first N children (the prev/next buttons follow), so
   :nth-child maps 1:1 to the slide index. Range matches the keyframe's
   validated N=2..6. */
.loom-hero-slideshow__slide:nth-child(1) { --loom-slideshow-slide-index: 0; }
.loom-hero-slideshow__slide:nth-child(2) { --loom-slideshow-slide-index: 1; }
.loom-hero-slideshow__slide:nth-child(3) { --loom-slideshow-slide-index: 2; }
.loom-hero-slideshow__slide:nth-child(4) { --loom-slideshow-slide-index: 3; }
.loom-hero-slideshow__slide:nth-child(5) { --loom-slideshow-slide-index: 4; }
.loom-hero-slideshow__slide:nth-child(6) { --loom-slideshow-slide-index: 5; }
/* Slide count + cycle duration via container class (custom properties
   inherit to the slides). Duration assumes the substrate-default ~5s
   dwell; a tenant with a custom interval_ms still gets exact rotation
   timing through the data-interval-ms JS path. */
.loom-hero-slideshow--count-2 { --loom-slideshow-slide-count: 2; --loom-slideshow-total-ms: 10000ms; }
.loom-hero-slideshow--count-3 { --loom-slideshow-slide-count: 3; --loom-slideshow-total-ms: 15000ms; }
.loom-hero-slideshow--count-4 { --loom-slideshow-slide-count: 4; --loom-slideshow-total-ms: 20000ms; }
.loom-hero-slideshow--count-5 { --loom-slideshow-slide-count: 5; --loom-slideshow-total-ms: 25000ms; }
.loom-hero-slideshow--count-6 { --loom-slideshow-slide-count: 6; --loom-slideshow-total-ms: 30000ms; }

/* CSS-only auto-rotation as primary driver (JS is now redundant
   sync for the prev/next arrow buttons, not required for rotation).
   Scoped to counted (multi-slide) shows so a single-slide hero keeps
   its static data-active opacity and never fades out. Explicit
   longhand because some headless renderers parse the shorthand
   inconsistently. */
:where(.loom-hero-slideshow[class*="loom-hero-slideshow--count-"]) .loom-hero-slideshow__slide {
  animation-name: loom-hero-slideshow-cycle;
  animation-duration: var(--loom-slideshow-total-ms, 20000ms);
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  animation-fill-mode: backwards;
  animation-delay: calc(
    var(--loom-slideshow-slide-index, 0) *
    (var(--loom-slideshow-total-ms, 20000ms) / var(--loom-slideshow-slide-count, 4))
  );
}

@media (prefers-reduced-motion: reduce) {
  .loom-hero-slideshow__slide {
    animation: none;
    opacity: 0;
  }
  .loom-hero-slideshow__slide[data-active="true"] { opacity: 1; }
}

/* ===================================================================
 * loom-utility-strip — narrow top-of-page bar (#414)
 * Substrate-general; any tenant opts in via CmsPage.utility_strip.
 * =================================================================== */
.loom-utility-strip {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1.5rem;
  font-size: 0.875rem;
  background: var(--loom-color-muted, #f3f4f6);
  color: var(--loom-color-ink, #111);
}
div.loom-utility-strip[data-bg-role="primary"]   { background: var(--loom-color-primary) !important;   color: var(--loom-color-on-primary, #fff) !important; }
div.loom-utility-strip[data-bg-role="secondary"] { background: var(--loom-color-secondary) !important; color: var(--loom-color-on-secondary, #fff) !important; }
.loom-utility-strip[data-bg-role="accent"]    { background: var(--loom-color-accent);    color: var(--loom-color-on-accent, #fff); }
.loom-utility-strip[data-bg-role="surface"]   { background: var(--loom-color-bg);        color: var(--loom-color-ink); }
.loom-utility-strip__left  { margin-right: auto; }
.loom-utility-strip__right { margin-left: auto; }
.loom-utility-strip a {
  color: inherit;
  text-decoration: none;
}

/* Color-banded nav: when CmsPage.nav_bar_color_role is set, the
   header gets a colored band background + matching foreground. */
.loom-page-header[data-nav-bg-role] {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: none;
}
header.loom-page-header[data-nav-bg-role="primary"] {
  background: var(--loom-color-primary) !important;
  color: var(--loom-color-on-primary, #fff) !important;
}
.loom-page-header[data-nav-bg-role="primary"] .loom-page-nav a,
.loom-page-header[data-nav-bg-role="primary"] .loom-page-brand {
  color: var(--loom-color-on-primary, #fff);
}
.loom-page-header[data-nav-bg-role="primary"] .loom-page-nav a:hover {
  background: rgba(255, 255, 255, 0.12);
  color: var(--loom-color-on-primary, #fff);
}
/* nav_border_role variant — white nav with thin colored accent line
   under the entire header. Used INSTEAD of nav-bg-role; never both. */
header.loom-page-header[data-nav-border-role="primary"] {
  border-bottom: 3px solid var(--loom-color-primary) !important;
}
header.loom-page-header[data-nav-border-role="secondary"] {
  border-bottom: 3px solid var(--loom-color-secondary) !important;
}
header.loom-page-header[data-nav-border-role="accent"] {
  border-bottom: 3px solid var(--loom-color-accent) !important;
}
header.loom-page-header[data-nav-bg-role="secondary"] {
  background: var(--loom-color-secondary) !important;
  color: var(--loom-color-on-secondary, #fff) !important;
}
.loom-page-header[data-nav-bg-role="secondary"] .loom-page-nav a,
.loom-page-header[data-nav-bg-role="secondary"] .loom-page-brand {
  color: var(--loom-color-on-secondary, #fff);
}
.loom-page-header[data-nav-bg-role="accent"] {
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, #111);
}
.loom-page-header[data-nav-bg-role="accent"] .loom-page-nav a,
.loom-page-header[data-nav-bg-role="accent"] .loom-page-brand {
  color: var(--loom-color-on-accent, #111);
}

/* ===================================================================
 * loom-image-text-row — two-column image+text marketing row (#415)
 * Substrate-general; any tenant authors. Image-left or image-right
 * variants. Collapses to stacked single-column on narrow viewports.
 * =================================================================== */
.loom-image-text-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
  /* #572: vertical padding tenant-tunable via --loom-size-row-py. On a
   * [data-surface=secondary] row the navy band is painted by ::before, so the
   * row's py is exactly how much navy shows above/below the content — a tenant
   * matching plausiden.com's dark "Why Industry Leaders" band (py-24 = 96px)
   * dials this up. Default = current clamp (byte-identical for existing sites). */
  padding: var(--loom-size-row-py, clamp(.5rem, .75vw, .75rem)) clamp(1rem, 3vw, 2rem);
  max-width: 80rem;
  margin: 0 auto;
}
.loom-image-text-row--image-right {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.loom-image-text-row--image-right .loom-image-text-row__media { order: 2; }
.loom-image-text-row--image-right .loom-image-text-row__body  { order: 1; }
.loom-image-text-row__media {
  margin: 0;
  border-radius: var(--loom-img-radius, var(--loom-radius-lg, 14px));
  overflow: hidden;
  box-shadow: var(--loom-img-shadow, 0 8px 30px rgba(0,0,0,.08));
}
.loom-image-text-row__media img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto;
}
.loom-image-text-row__body {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.loom-image-text-row__eyebrow {
  margin: 0;
  font-size: .875rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--loom-color-primary, var(--loom-primary));
}
/* #550: solid brand-tinted pill-badge eyebrow — mirrors the ImageHero
 * eyebrow badge (#535). Opt-in via ImageTextRow.eyebrow_badge = true;
 * the bare uppercase label above is unchanged. The body is a flex
 * column, so align-self: flex-start keeps the pill hugging its text
 * instead of stretching full width. Generic. */
.loom-image-text-row__eyebrow--badge {
  display: inline-block;
  align-self: flex-start;
  padding: 0.4rem 0.95rem;
  border-radius: 999px;
  background: color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 12%, transparent);
  border: 1px solid color-mix(in oklab, var(--loom-color-primary, var(--loom-accent)) 32%, transparent);
  font-family: var(--loom-font-body);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  text-transform: none;
}
/* #559: optional leading glyph inside the image-text-row eyebrow. The
 * badge stays inline-block; the icon is an inline-flex box so the svg
 * sits on the label baseline with a small gap before the text. Sized
 * in em so it tracks the eyebrow font-size; inherits currentColor so
 * it tints with the eyebrow/surface text. Substrate-general. */
.loom-image-text-row__eyebrow-icon {
  display: inline-flex;
  align-items: center;
  margin-right: 0.4rem;
  vertical-align: -0.15em;
}
.loom-image-text-row__eyebrow-icon-svg {
  width: 1em;
  height: 1em;
}
.loom-image-text-row__heading {
  margin: 0;
  font-family: var(--loom-font-display, var(--loom-font-body));
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: var(--loom-weight-heading, 700);
  line-height: 1.25;
  color: var(--loom-color-ink, var(--loom-fg));
  letter-spacing: -0.01em;
}
.loom-image-text-row__prose {
  margin: 0;
  font-size: .95rem;
  line-height: 1.55;
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-image-text-row__mid-heading {
  margin: 0;
  font-family: var(--loom-font-display, var(--loom-font-body));
  font-size: clamp(1.05rem, 1.6vw, 1.35rem);
  font-weight: var(--loom-weight-heading, 700);
  line-height: 1.25;
  color: var(--loom-color-primary, var(--loom-primary));
  text-transform: uppercase;
  letter-spacing: 0.01em;
}
.loom-image-text-row__heading-accent {
  color: var(--loom-color-accent, var(--loom-accent));
}
.loom-image-text-row__bullets {
  margin: 0;
  padding-left: 1.4rem;
  list-style: disc outside;
  color: var(--loom-color-ink, var(--loom-fg));
  font-size: 1rem;
  line-height: 1.65;
}
.loom-image-text-row__bullets li {
  margin-bottom: .2rem;
}
/* #542: icon-led bullet list — drop the disc marker and lay each
 * item out as a leading glyph + label row. Opt-in via bullets_icon;
 * the icon inherits --loom-color-primary (which inverts to a light
 * tint inside a dark surface remap, staying visible). Generic. */
.loom-image-text-row__bullets--iconed {
  list-style: none;
  padding-left: 0;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}
.loom-image-text-row__bullets--iconed li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0;
}
.loom-image-text-row__bullet-icon {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--loom-color-primary, var(--loom-accent));
}
.loom-image-text-row__bullet-label {
  flex: 1;
}
.loom-image-text-row__body .loom-button {
  align-self: flex-start;
  margin-top: .75rem;
}
@media (max-width: 768px) {
  .loom-image-text-row {
    grid-template-columns: 1fr;
  }
  /* #556: stacked rows are text-first regardless of desktop image side.
   * The base image-right rule keeps body order:1 / media order:2, so we
   * intentionally do NOT re-flip media above body on mobile — leading
   * with the heading reads better on a phone than an orphaned image. */
}

/* Surface tones (#532): paint a full-bleed tinted band behind the row.
 * The ::before breaks out of the max-width container to span the
 * viewport; --loom-color-ink/primary/accent are locally remapped so
 * every child (eyebrow / heading / prose / mid-heading / bullets /
 * links) inverts to on-color text with no per-child edits — the same
 * technique as the rich footer (#531). Absent attr → transparent. */
.loom-image-text-row[data-surface] {
  position: relative;
  isolation: isolate;
  align-items: center;
  padding-block: clamp(2.5rem, 5vw, 4.5rem);
}
.loom-image-text-row[data-surface]::before {
  content: "";
  position: absolute;
  inset: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  z-index: -1;
}
.loom-image-text-row[data-surface="secondary"]::before {
  background: var(--loom-color-secondary);
}
.loom-image-text-row[data-surface="primary"]::before {
  background: var(--loom-color-primary);
}
.loom-image-text-row[data-surface="accent"]::before {
  background: var(--loom-color-accent);
}
.loom-image-text-row[data-surface="muted"]::before {
  background: color-mix(in oklab, var(--loom-color-ink, #1d2530) 6%, var(--loom-color-bg, #fff));
}
.loom-image-text-row[data-surface="secondary"] {
  --loom-color-ink: var(--loom-color-on-secondary, #fff);
  --loom-color-primary: var(--loom-color-on-secondary, #fff);
  --loom-color-accent: color-mix(in oklab, var(--loom-color-on-secondary, #fff) 72%, var(--loom-color-accent));
}
.loom-image-text-row[data-surface="primary"] {
  --loom-color-ink: var(--loom-color-on-primary, #fff);
  --loom-color-primary: var(--loom-color-on-primary, #fff);
  --loom-color-accent: color-mix(in oklab, var(--loom-color-on-primary, #fff) 72%, var(--loom-color-accent));
}
.loom-image-text-row[data-surface="accent"] {
  --loom-color-ink: var(--loom-color-on-accent, #fff);
  --loom-color-primary: var(--loom-color-on-accent, #fff);
}
/* #539: primary button on a dark image-text surface. The surface
 * remap above re-points --loom-color-primary at the light on-* color
 * so that primary-colored TEXT inverts to light. But a primary
 * BUTTON fills with --loom-color-primary and draws text in
 * --loom-color-on-primary, so the remap renders it light-on-light
 * (invisible). Restore a visible inverted button: light fill
 * (--loom-color-ink is remapped to the surface's on-* color, so this
 * picks the right light per surface) + dark text (--loom-color-
 * secondary is never remapped on these surfaces and is the canonical
 * dark section color). Generic; applies to any tenant using a dark
 * image-text surface. */
.loom-image-text-row[data-surface="secondary"] .loom-button[data-variant="primary"],
.loom-image-text-row[data-surface="primary"] .loom-button[data-variant="primary"],
.loom-image-text-row[data-surface="accent"] .loom-button[data-variant="primary"] {
  background: var(--loom-color-ink);
  color: var(--loom-color-secondary, #0f172a);
  border-color: transparent;
}
.loom-image-text-row[data-surface="secondary"] .loom-button[data-variant="primary"]:hover,
.loom-image-text-row[data-surface="primary"] .loom-button[data-variant="primary"]:hover,
.loom-image-text-row[data-surface="accent"] .loom-button[data-variant="primary"]:hover {
  background: color-mix(in oklab, var(--loom-color-ink) 88%, var(--loom-color-secondary));
}

/* ===================================================================
 * loom-feature-spotlight.deco-bordered — WP-style bordered cards (#416)
 * Each card has a 4px colored top border + bold uppercase heading
 * in the same color + light card chrome. Border color cycles
 * primary → secondary → accent → primary across cards. Generic;
 * any tenant can opt in via decoration="bordered".
 * =================================================================== */
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__grid {
  gap: 1.25rem;
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__item {
  background: var(--loom-color-bg, #fff);
  border-radius: 4px;
  border-top: 4px solid var(--loom-color-primary);
  padding: 1rem 1.25rem;
  box-shadow: 0 1px 3px rgba(0,0,0,.08);
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__item:nth-child(3n+2) {
  border-top-color: var(--loom-color-secondary);
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__item:nth-child(3n+3) {
  border-top-color: var(--loom-color-accent);
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__title {
  color: var(--loom-color-primary);
  font-size: 1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .028em;
  line-height: 1.3;
  margin: 0 0 .5rem;
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__title::after {
  content: "  \2192";
  display: inline;
  font-weight: 700;
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__item:nth-child(3n+2) .loom-feature-spotlight__title {
  color: var(--loom-color-secondary);
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__item:nth-child(3n+3) .loom-feature-spotlight__title {
  color: var(--loom-color-accent);
}
/* Uniform heading color (SpotlightHeadingColor != Cycle): when the
 * section carries .heading-fixed + a .heading-<role> class, every card
 * heading uses one palette role instead of the per-card rotation above.
 * The nth-child selectors here are specificity (0,6,0) — one class
 * higher than the rotation rules (0,5,0) — so they override without
 * :not() or !important and without editing the rotation rules. */
.loom-feature-spotlight.deco-bordered.heading-fixed.heading-primary {
  --loom-spotlight-heading: var(--loom-color-primary);
}
.loom-feature-spotlight.deco-bordered.heading-fixed.heading-secondary {
  --loom-spotlight-heading: var(--loom-color-secondary);
}
.loom-feature-spotlight.deco-bordered.heading-fixed.heading-accent {
  --loom-spotlight-heading: var(--loom-color-accent);
}
.loom-feature-spotlight.deco-bordered.heading-fixed .loom-feature-spotlight__title,
.loom-feature-spotlight.deco-bordered.heading-fixed .loom-feature-spotlight__item:nth-child(3n+2) .loom-feature-spotlight__title,
.loom-feature-spotlight.deco-bordered.heading-fixed .loom-feature-spotlight__item:nth-child(3n+3) .loom-feature-spotlight__title {
  color: var(--loom-spotlight-heading);
}
/* Uniform top-border color (SpotlightBorderColor != Cycle): mirrors the
 * heading-fixed mechanism above. .border-fixed + .border-<role> makes
 * every card's 4px top border one palette role instead of the per-card
 * rotation (12160-12172). The override selectors carry one extra class
 * (.border-fixed) over the rotation rules, so the base item is (0,4,0)
 * and the nth-child items are (0,5,0) — each one specificity step above
 * its rotation counterpart, winning without :not() or !important. */
.loom-feature-spotlight.deco-bordered.border-fixed.border-primary {
  --loom-spotlight-border: var(--loom-color-primary);
}
.loom-feature-spotlight.deco-bordered.border-fixed.border-secondary {
  --loom-spotlight-border: var(--loom-color-secondary);
}
.loom-feature-spotlight.deco-bordered.border-fixed.border-accent {
  --loom-spotlight-border: var(--loom-color-accent);
}
.loom-feature-spotlight.deco-bordered.border-fixed .loom-feature-spotlight__item,
.loom-feature-spotlight.deco-bordered.border-fixed .loom-feature-spotlight__item:nth-child(3n+2),
.loom-feature-spotlight.deco-bordered.border-fixed .loom-feature-spotlight__item:nth-child(3n+3) {
  border-top-color: var(--loom-spotlight-border);
}
.loom-feature-spotlight.deco-bordered .loom-feature-spotlight__body {
  color: var(--loom-color-ink, var(--loom-fg));
  font-size: .88rem;
  line-height: 1.5;
  margin: 0;
}
/* Full-box border variant (border_style="box"): a thin 2px border on
 * ALL four sides instead of the default 4px top rule, squared corners,
 * no shadow, color cycling primary → accent → secondary per card.
 * Opt-in and additive — leaves the default top-only treatment (above)
 * untouched. The extra .border-box class lifts specificity one step
 * over the base/nth-child rules so the `border` shorthand cleanly
 * resets the top-only rule and the per-card border-color wins. */
.loom-feature-spotlight.deco-bordered.border-box .loom-feature-spotlight__item {
  border: 2px solid var(--loom-color-primary);
  border-radius: 0;
  box-shadow: none;
}
.loom-feature-spotlight.deco-bordered.border-box .loom-feature-spotlight__item:nth-child(3n+2) {
  border-color: var(--loom-color-accent);
}
.loom-feature-spotlight.deco-bordered.border-box .loom-feature-spotlight__item:nth-child(3n+3) {
  border-color: var(--loom-color-secondary);
}

/* ===================================================================
 * loom-hero-slideshow--split — text-left + image-right layout (#418)
 * Default mode is overlay (text on top of full-bleed image). Split
 * puts text in a left column and image in a right column, both
 * 50% width on desktop, stacked on mobile.
 * =================================================================== */
.loom-hero-slideshow--split {
  min-height: clamp(420px, 36vw, 520px);
  position: relative;
}
/* Slides STAY position:absolute (inherited from base) so the
   opacity-cycle keeps working. Inside each slide, content is
   laid out in a 2-col grid via the existing position:absolute
   inset:0 + display:grid combination. */
.loom-hero-slideshow--split .loom-hero-slideshow__slide {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: stretch;
  padding: clamp(1.5rem, 4vw, 3rem);
  height: 100%;
  inset: 0;
}
.loom-hero-slideshow--split .loom-hero-slideshow__bg {
  position: relative;
  inset: auto;
  grid-column: 2;
  grid-row: 1;
  z-index: 1;
  width: 100%;
  height: 100%;
  min-height: 340px;
  max-height: 480px;
  object-fit: cover;
  object-position: var(--loom-hero-bg-position, right center);
  border-radius: 0;
  align-self: stretch;
}
.loom-hero-slideshow--split .loom-hero-slideshow__overlay {
  grid-column: 1;
  position: relative;
  padding: 0;
  height: auto;
  margin: 0;
  max-width: none;
  text-shadow: none;
  z-index: 2;
}
.loom-hero-slideshow--split .loom-hero-slideshow__title {
  color: var(--loom-color-ink, var(--loom-fg));
}
.loom-hero-slideshow--split .loom-hero-slideshow__lede {
  color: var(--loom-color-ink, var(--loom-fg));
}
@media (max-width: 768px) {
  /* Mobile: slides remain position:absolute so all N slides overlap
     in the same area (data-active toggles which one is visible).
     Parent gets explicit height that fits stacked image + overlay. */
  .loom-hero-slideshow--split {
    min-height: 640px;
    height: 640px;
  }
  .loom-hero-slideshow--split .loom-hero-slideshow__slide {
    grid-template-columns: 1fr;
    grid-template-rows: 280px 1fr;
    gap: 1rem;
    padding: 1rem;
  }
  .loom-hero-slideshow--split .loom-hero-slideshow__bg {
    grid-column: 1;
    grid-row: 1;
    width: 100%;
    height: 100%;
    max-height: none;
    min-height: 0;
  }
  .loom-hero-slideshow--split .loom-hero-slideshow__overlay {
    grid-column: 1;
    grid-row: 2;
    width: 100%;
    max-width: none;
    padding: 0;
  }
}

/* ===================================================================
 * loom-page-footer extensions — buttons + decoration + back-to-top (#417)
 * Substrate-general additions to the existing rich footer.
 * =================================================================== */
.loom-page-footer[data-bg-role="surface"]   { background: var(--loom-color-surface, var(--loom-color-bg, #fff)); }
.loom-page-footer[data-bg-role="muted"]     { background: color-mix(in oklab, var(--loom-color-muted, #f3f4f6) 50%, var(--loom-color-bg, #fff)); }
/* Dark footer surfaces (#531): full palette bg + on-* text. Locally
   remap the ink/border tokens so every percentage-mixed child
   (headings, links, col-body, legal, colophon, dividers) inverts to
   light-on-dark with no per-child edits — mirrors the utility-strip
   (data-bg-role) and colophon-band treatment. The brand button color
   (--loom-color-primary) is left intact so footer CTA buttons keep
   their accent; only text-link hovers brighten to the full on-color. */
.loom-page-footer[data-bg-role="primary"] {
  background: var(--loom-color-primary);
  color: var(--loom-color-on-primary, #fff);
  --loom-color-ink: var(--loom-color-on-primary, #fff);
  --loom-color-border: color-mix(in oklab, var(--loom-color-on-primary, #fff) 28%, transparent);
}
.loom-page-footer[data-bg-role="secondary"] {
  background: var(--loom-color-secondary);
  color: var(--loom-color-on-secondary, #fff);
  --loom-color-ink: var(--loom-color-on-secondary, #fff);
  --loom-color-border: color-mix(in oklab, var(--loom-color-on-secondary, #fff) 28%, transparent);
}
.loom-page-footer[data-bg-role="accent"] {
  background: var(--loom-color-accent);
  color: var(--loom-color-on-accent, #fff);
  --loom-color-ink: var(--loom-color-on-accent, #fff);
  --loom-color-border: color-mix(in oklab, var(--loom-color-on-accent, #fff) 28%, transparent);
}
.loom-page-footer[data-bg-role="primary"] a:hover,
.loom-page-footer[data-bg-role="secondary"] a:hover,
.loom-page-footer[data-bg-role="accent"] a:hover {
  color: var(--loom-color-ink);
}
.loom-page-footer__buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1.5rem 1.75rem 0.5rem;
  justify-content: flex-start;
}
.loom-page-footer__decoration {
  display: flex;
  align-items: center;
  justify-content: center;
}
.loom-page-footer__decoration-img {
  max-width: 100%;
  max-height: 240px;
  height: auto;
  object-fit: contain;
  opacity: .9;
}
.loom-page-footer__back-to-top {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: .15rem;
  padding: 0;
  margin: 1.25rem 1.75rem 0;
  font-size: .8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--loom-color-ink, var(--loom-fg));
  text-decoration: none;
  border: 0;
  border-radius: 0;
  background: none;
  transition: color var(--loom-motion-fast) var(--loom-ease-out);
}
.loom-page-footer__back-to-top::before {
  content: "\25B2";
  font-size: .7rem;
  line-height: 1;
  color: var(--loom-color-secondary, var(--loom-color-primary));
}
.loom-page-footer__back-to-top:hover {
  background: none;
  color: var(--loom-color-secondary, var(--loom-color-primary));
}

/* Brand logo image — substrate-general sizing. Auto-fits within
   the page-header height; tenants can override via tenant_style or
   forge.toml [identity.logo_max_height]. */
.loom-page-brand__logo {
  display: block;
  /* clamp floor is the minimum rendered logo height; on narrow
     viewports 10vw falls below it so the floor governs the mobile
     size. Tenant-overridable via --loom-nav-logo-min so a brand
     whose mark should stay large on mobile (rather than shrink to
     the 80px default) can opt in without upscaling past natural. */
  max-height: clamp(var(--loom-nav-logo-min, 80px), 10vw, 160px);
  width: auto;
  height: auto;
  object-fit: contain;
}
/* On the dark theme a raster logo with baked-in dark ink would
   vanish against the near-black canvas. Give it a light chip so
   fixed-color marks stay legible. Light theme is untouched (logo
   sits transparent on the page, matching a light brand bar). A
   tenant whose logo is already dark-theme-ready can override via
   tenant-style. */
:root[data-theme="dark"] .loom-page-brand__logo {
  background: #fff;
  padding: 0.3rem 0.55rem;
  border-radius: var(--loom-radius-md, 8px);
}
.loom-page-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
/* Brand icon-mark (#536): inline loom-icons glyph before the wordmark.
 * currentColor on the SVG stroke would inherit the ink brand text; pin
 * it to the primary accent so the mark reads as a logo, not text. */
.loom-page-brand__icon {
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 auto;
  color: var(--loom-color-primary, var(--loom-accent));
}
/* Boxed brand mark (#553): opt-in (brand_icon_boxed) filled rounded
 * container so the icon + wordmark read as a logo lockup rather than a
 * bare glyph. content-box keeps the glyph at its 1.6rem size and adds
 * the pad + fill around it; the glyph inverts to the on-primary ink. */
.loom-page-brand__icon--boxed {
  box-sizing: content-box;
  padding: 0.4rem;
  border-radius: var(--loom-radius-md, 0.5rem);
  background: var(--loom-color-primary, var(--loom-accent));
  color: var(--loom-color-on-primary, #fff);
}
/* Two-tone wordmark (#543): the trailing accent portion (e.g. "LLC")
 * picks up the primary brand color while the head stays ink. Generic;
 * emitted only when the tenant sets brand_accent_tail. */
.loom-page-brand__name-accent {
  color: var(--loom-color-primary, var(--loom-accent));
  /* #565: the parent .loom-page-brand is a flex row with gap:0.5rem, so
   * that 0.5rem also separates the wordmark head from this accent tail —
   * REAL renders "PlausiDen LLC" as one wordmark with a single word
   * space. A tunable margin (default 0 = flex gap unchanged for demos)
   * lets a tenant pull the tail back toward a normal word space WITHOUT
   * collapsing the icon↔wordmark gap that the same flex gap also sets. */
  margin-left: var(--loom-size-brand-accent-gap, 0);
}

/* Chrome content aligns to the page content column (same width as
   main#content). Without this the header + utility strip are full-bleed
   while the body is centered, so the brand/nav sit flush-left and read
   as "in the wrong spot" against the indented body. Backgrounds and
   borders still span full width; only the inner content is inset via
   auto gutters. The colored 2-row [data-nav-bg-role] header keeps its
   own full-bleed layout (its padding:0 !important wins). */
body { --loom-page-content-max: 64rem; }
body[data-content-width="narrow"]      { --loom-page-content-max: 42rem; }
body[data-content-width="comfortable"] { --loom-page-content-max: 64rem; }
body[data-content-width="roomy"]       { --loom-page-content-max: 70rem; }
body[data-content-width="wide"]        { --loom-page-content-max: 90rem; }
body[data-content-width="full"]        { --loom-page-content-max: 100%; }
header.loom-page-header {
  padding-inline: max(1.25rem, calc((100% - var(--loom-page-content-max, 64rem)) / 2));
}

/* When the header has a colored nav-bg, force a 2-row layout:
   brand-row on white above, colored nav-row below. Achieves her
   real-site layout via flex-wrap without changing the substrate HTML. */
.loom-page-header[data-nav-bg-role] {
  padding: 0 !important;
}
.loom-page-header[data-nav-bg-role] .loom-page-nav {
  flex-wrap: wrap;
  padding: 0;
}
.loom-page-header[data-nav-bg-role] .loom-page-brand {
  flex: 0 0 100%;
  background: var(--loom-color-bg, #fff) !important;
  color: var(--loom-color-ink, #111) !important;
  padding: 1rem 1.75rem !important;
  border-bottom: 1px solid var(--loom-color-border, #eee);
  margin: 0 !important;
}
.loom-page-header[data-nav-bg-role] .loom-page-brand img {
  max-height: 70px;
}
.loom-page-header[data-nav-bg-role] .loom-page-nav__link,
.loom-page-header[data-nav-bg-role] .loom-theme-toggle {
  padding: 0.75rem 1rem;
}
.loom-page-header[data-nav-bg-role="primary"] .loom-page-nav > a:not(.loom-page-brand),
.loom-page-header[data-nav-bg-role="primary"] .loom-page-nav > button {
  margin-left: 0 !important;
}
/* Utility strip: full-width bar, content inset to the page content
   column so the email/phone align with the header + body below. */
.loom-utility-strip {
  padding-block: 0.6rem !important;
  padding-inline: max(1.5rem, calc((100% - var(--loom-page-content-max, 64rem)) / 2)) !important;
  font-size: 0.875rem !important;
  font-weight: 500;
}

/* Logo on its own row — bigger when in 2-row header. */
.loom-page-header[data-nav-bg-role] .loom-page-brand img,
.loom-page-header[data-nav-bg-role] .loom-page-brand__logo {
  max-height: 130px !important;
  height: auto;
}

/* Standard a11y visually-hidden — content readable by screen readers
   but invisible to sighted users. WCAG 2.1 AA pattern. */
.loom-visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* ===================================================================
 * Brand-row extras: social links + lang selector on header right side.
 * Substrate-general; any tenant can opt in via CmsPage.social_links
 * + CmsPage.lang_selector. Positioned at the brand row's right edge.
 * =================================================================== */
.loom-page-brand-extras {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1rem;
}
/* 2-row header: brand row on top (brand + extras), links row below.
   Emitted automatically when CmsPage carries brand_extras (social_links
   or lang_selector). Substrate-general. */
.loom-page-nav--brand-row {
  display: flex;
  align-items: center;
  width: 100%;
  margin-bottom: 0.5rem;
}
.loom-page-nav--brand-row .loom-page-brand-extras {
  margin-left: auto;
}
.loom-page-nav--links-row {
  display: flex;
  align-items: var(--loom-nav-links-row-align, center);
  gap: 0.5rem;
  flex-wrap: wrap;
}
header.loom-page-header:has(.loom-page-nav--brand-row) {
  padding-bottom: 0.5rem;
}
/* Right-aligned action-button cluster in the PageShell header (#533).
 * margin-left:auto pushes it to the far edge of the flex nav so brand +
 * links stay left, CTA buttons sit right. Substrate-general; emitted
 * only when CmsPage carries nav_actions. */
.loom-page-nav__actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: 0 0 auto;
}
/* ===================================================================
 * Tenant-overridable primary-nav link styling. A tenant opts in via
 * forge.toml [style.nav] (link_color / link_weight / link_padding /
 * link_hover_color), which emits --loom-nav-* CSS vars in
 * tenant-style.css. With NO override these fall back to the premium
 * muted-nav baseline from BASE_THEME_CSS, so other tenants are
 * unaffected. Selector carries higher specificity (0,2,2) than the
 * baseline `nav.loom-page-nav a` (0,1,2) via the .loom-page-nav__link
 * class, so it wins regardless of source order without touching the
 * CSP-hashed inline critical block. Padding applies to every
 * top-level link (incl. active) for uniform item width; color +
 * weight use :not([aria-current="page"]) so the active-page
 * highlight stays on the baseline accent rule.
 * Substrate-general — no per-site CSS.
 * =================================================================== */
nav.loom-page-nav a.loom-page-nav__link {
  padding: var(--loom-nav-link-padding, 0.5rem 0.9rem);
}
nav.loom-page-nav a.loom-page-nav__link:not([aria-current="page"]) {
  color: var(--loom-nav-link-color, var(--loom-muted));
  font-weight: var(--loom-nav-link-weight, 500);
}
nav.loom-page-nav a.loom-page-nav__link:hover,
nav.loom-page-nav a.loom-page-nav__link:focus-visible {
  color: var(--loom-nav-link-hover-color, var(--loom-fg));
}
/* #554: the active-page link carries a brand-accent underline so the
 * current section is visibly marked (matches real marketing nav). Uses
 * text-decoration rather than a border so it adds no layout shift, and
 * an offset so it clears the descenders. `background: none` overrides
 * the lower-specificity `.loom-navlinks a[aria-current="page"]` overlay
 * pill (0,3,2 beats 0,2,1, so it wins regardless of source order and
 * without touching the CSP-hashed inline block) — real marketing navs
 * mark the active link with an underline alone, not a filled chip.
 * Substrate-general. */
nav.loom-page-nav a.loom-page-nav__link[aria-current="page"] {
  background: none;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.45em;
  text-decoration-color: var(--loom-color-primary, var(--loom-accent));
}
/* Home-icon glyph tracks the tenant nav-link color so it doesn't read
   as a lone muted item among colored links. Higher specificity (0,2,2)
   than the baseline `nav.loom-page-nav a` (0,1,2) that otherwise forces
   --loom-muted onto the icon anchor's `color: inherit`. Padding stays
   on the icon's own fixed 36×36 box — only color is overridden here. */
nav.loom-page-nav a.loom-page-nav__home-icon {
  color: var(--loom-nav-link-color, var(--loom-muted));
  align-self: var(--loom-nav-home-align, auto);
}
/* ===================================================================
 * Nav dropdown submenus. A CmsNavLink carrying `children` renders as
 * a parent: an inline-flex item wrapper holding the parent anchor
 * (with a disclosure caret) plus an absolutely-positioned submenu.
 * The submenu is hidden by default and revealed on :hover OR
 * :focus-within, so it works for mouse, keyboard, and touch with no
 * JavaScript. Nested children reuse the same machinery one level
 * deeper. Substrate-general: any tenant gets dropdowns purely from
 * cms/*.json — no per-site CSS.
 * =================================================================== */
.loom-page-nav__item {
  position: relative;
  display: inline-flex;
  align-items: center;
}
/* Caret placement knob. By default the disclosure caret sits inline to
   the right of the label (flex-direction:row, inherited from the
   .loom-page-nav__link inline-flex base). A tenant can stack the caret
   centered BELOW the label by setting [style.nav] caret_flow=column
   (pair with caret_margin_left=0, caret_margin_top=<x>, and
   links_row_align=baseline so every label stays on one line while only
   the dropdown carets hang beneath). CSS-only; the row default leaves
   every other tenant's nav unchanged. */
nav.loom-page-nav a.loom-page-nav__link--parent {
  flex-direction: var(--loom-nav-caret-flow, row);
}
.loom-page-nav__caret {
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: var(--loom-nav-caret-margin-left, 0.35em);
  margin-top: var(--loom-nav-caret-margin-top, 0);
  border-left: 0.3em solid transparent;
  border-right: 0.3em solid transparent;
  border-top: 0.32em solid currentColor;
  transition: transform 0.15s ease;
}
.loom-page-nav__item:hover > .loom-page-nav__link--parent .loom-page-nav__caret,
.loom-page-nav__item:focus-within > .loom-page-nav__link--parent .loom-page-nav__caret {
  transform: rotate(180deg);
}
.loom-page-nav__submenu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  min-width: 15rem;
  padding: 0.4rem 0;
  margin-top: 0.25rem;
  background: var(--loom-surface, #fff);
  border: var(--loom-stroke-thin, 1px) solid var(--loom-border, #e5e5e5);
  border-radius: var(--loom-radius-sm, 6px);
  box-shadow: var(--loom-shadow-md, 0 8px 24px rgba(0, 0, 0, 0.12));
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.25rem);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}
.loom-page-nav__item:hover > .loom-page-nav__submenu,
.loom-page-nav__item:focus-within > .loom-page-nav__submenu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* Submenu entries: full-width vertical rows, not inline nav pills. */
.loom-page-nav__sublink {
  display: block;
  padding: 0.5rem 1rem;
  white-space: nowrap;
  color: var(--loom-fg, #222);
  text-decoration: none;
  font-size: 0.95rem;
  line-height: 1.4;
}
.loom-page-nav__sublink:hover,
.loom-page-nav__sublink:focus {
  background: color-mix(in oklab, var(--loom-accent) 12%, transparent);
  color: var(--loom-accent);
}
/* Nested submenu (a child that itself has children): flies out to the
   right of its row rather than stacking below. */
.loom-page-nav__subitem {
  position: relative;
}
.loom-page-nav__subitem .loom-page-nav__submenu {
  top: 0;
  left: 100%;
  margin-top: 0;
  margin-left: 0.25rem;
}
/* Mobile hamburger via JS-free checkbox-hack. Hidden checkbox holds
   the open state; <label for> flips it on tap. On desktop the toggle
   is hidden + nav is always visible. On <=768px the nav collapses by
   default; tapping MENU toggles it open. Tenants get the toggle
   automatically once their CmsPage carries brand_extras (the trigger
   for 2-row layout). */
.loom-page-nav-collapse {
  width: 100%;
}
.loom-page-nav-toggle-cb {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
.loom-page-nav-toggle {
  display: none;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  border: none;
  border-radius: var(--loom-radius-sm, 6px);
  background: var(--loom-color-accent, var(--loom-color-primary));
  color: var(--loom-color-on-accent, #111);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  user-select: none;
  cursor: pointer;
}
.loom-page-nav-toggle__icon {
  font-size: 1.1rem;
  line-height: 1;
}
@media (max-width: 768px) {
  .loom-page-nav-toggle {
    display: inline-flex;
  }
  /* Mobile header reflow for the brand-extras layout: keep the logo and
     the MENU toggle together on row 1 (toggle pushed to the right edge),
     and wrap the brand-extras (social links / language selector) onto a
     centered row 2 — the standard premium mobile-header arrangement.
     The MENU <label> now lives inside the brand row (its `for`/`id` still
     toggles the checkbox in the sibling collapse container), so pure
     flex order + wrap places it without absolute positioning.
     Substrate-general: applies to any tenant carrying brand_extras. */
  .loom-page-nav--brand-row {
    flex-wrap: wrap;
    row-gap: 0.6rem;
  }
  .loom-page-nav--brand-row .loom-page-nav-toggle {
    order: 1;
    margin-left: auto;
  }
  .loom-page-nav--brand-row .loom-page-brand-extras {
    order: 2;
    flex-basis: 100%;
    margin-left: 0;
    flex-direction: column;
    align-items: center;
    row-gap: 0.5rem;
    justify-content: center;
  }
  .loom-page-nav-toggle-cb:not(:checked) ~ .loom-page-nav--links-row {
    display: none;
  }
  .loom-page-nav-toggle-cb:checked ~ .loom-page-nav--links-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
    margin-top: 0.5rem;
  }
}
/* nav_collapse_always — hamburger pattern at EVERY viewport. Used by
   tenants whose nav is long enough that inline desktop nav wraps or
   visually crowds the brand row. Substrate-general opt-in. */
.loom-page-nav-toggle--always {
  display: inline-flex !important;
}
.loom-page-nav-collapse--always .loom-page-nav-toggle-cb:not(:checked) ~ .loom-page-nav--links-row {
  display: none !important;
}
.loom-page-nav-collapse--always .loom-page-nav-toggle-cb:checked ~ .loom-page-nav--links-row {
  display: flex !important;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  margin-top: 0.5rem;
}

/* Path A inline-collapse wrapper. At desktop it is layout-transparent
   (display:contents) so brand / links / actions / theme-toggle flow in the
   nav flex exactly as before — no desktop change. At <=768px it becomes a
   real box: hidden until the shared toggle checkbox is checked, then a
   full-width stacked column. Generic across page_shell tenants without
   brand-extras. */
.loom-page-nav__collapse-inline {
  display: contents;
}
@media (max-width: 768px) {
  .loom-page-nav > .loom-page-nav-toggle {
    margin-left: auto;
  }
  .loom-page-nav__collapse-inline {
    display: none;
    flex-basis: 100%;
  }
  .loom-page-nav-toggle-cb:checked ~ .loom-page-nav__collapse-inline {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
    gap: 0.25rem;
    margin-top: 0.5rem;
  }
  .loom-page-nav-toggle-cb:checked ~ .loom-page-nav__collapse-inline .loom-page-nav__actions {
    margin-left: 0;
  }
}

/* Opt-in right-packed desktop nav (nav_links_align_end). At >=769px the
   first child of the collapse-inline wrapper takes margin-left:auto, so
   the brand stays far-left and the link+action cluster groups on the
   right; the actions container drops its own auto so the cluster stays
   tight. No effect at <=768px — the hamburger column owns that range. */
@media (min-width: 769px) {
  .loom-page-nav--links-end .loom-page-nav__collapse-inline > :first-child {
    margin-left: auto;
  }
  .loom-page-nav--links-end .loom-page-nav__actions {
    margin-left: 0.5rem;
  }
}

/* Opt-in lower collapse breakpoint (nav_collapse_sm). The default
   hamburger fires at <=768px; tenants whose links fit one row down to
   tablet width opt into collapsing only at <=640px, matching sites that
   keep the full horizontal nav at 768. In the 641-768 band we override
   the default md-collapse back to the expanded inline row and re-apply
   the #560 align-end cluster (whose own min-width:769 rule does not
   reach this band). At <=640 the default collapse rules already apply,
   so the hamburger returns with no extra CSS. No effect on navs without
   this class -> ProsperityClub + demos keep the md collapse. */
@media (min-width: 641px) and (max-width: 768px) {
  .loom-page-nav--collapse-sm > .loom-page-nav-toggle {
    display: none;
  }
  .loom-page-nav--collapse-sm .loom-page-nav__collapse-inline {
    display: contents;
  }
  .loom-page-nav--collapse-sm.loom-page-nav--links-end .loom-page-nav__collapse-inline > :first-child {
    margin-left: auto;
  }
  .loom-page-nav--collapse-sm.loom-page-nav--links-end .loom-page-nav__actions {
    margin-left: 0.5rem;
  }
  /* Tablet density: a nav that stays expanded down to 641px keeps a
     single row. The trap is plain `flex-wrap:nowrap` — flex items
     default to `flex-shrink:1`, so a too-wide row is compressed item
     by item until the brand wordmark and each link wrap their text
     vertically ("Pl/au/si"). Reference marketing navs avoid this by
     pinning every item to its natural width (`flex:0 0 auto`) +
     `white-space:nowrap`, then letting the action cluster run past the
     right edge (clipped by the nav's own `overflow:hidden`, so no
     page-wide horizontal scrollbar). Trim gap + per-link padding so as
     much as possible fits before the clip. nav-level specificity
     (0,1,1) beats the inline base `.loom-page-nav` (0,1,0); the link
     rule matches the base (0,2,2) and wins on later source order.
     NOTE: `.loom-page-nav__collapse-inline` is `display:contents`
     here, so its children (links + actions) are nav flex items and
     the `flex`/`white-space` rules below reach them directly. */
  nav.loom-page-nav--collapse-sm {
    flex-wrap: nowrap;
    gap: 0.45rem;
    overflow: hidden;
  }
  .loom-page-nav--collapse-sm .loom-page-brand,
  .loom-page-nav--collapse-sm .loom-page-nav__link,
  .loom-page-nav--collapse-sm .loom-page-nav__actions {
    flex: 0 0 auto;
    white-space: nowrap;
  }
  nav.loom-page-nav--collapse-sm a.loom-page-nav__link {
    padding: 0.4rem 0.5rem;
  }
  .loom-page-nav--collapse-sm .loom-page-nav__actions {
    gap: 0.35rem;
  }
}

/* Opt-in plain/borderless toggle glyph (nav_toggle_plain). Drops the
   accent fill + box so the hamburger reads as a bare menu icon at the
   inherited text color. Same specificity as the default filled button
   rule but later in source, so it wins per-property for tenants that opt
   in; every other tenant keeps the filled button. */
.loom-page-nav-toggle--plain {
  background: none;
  color: inherit;
  padding: 0.35rem;
  border-radius: 0;
  font-weight: 400;
}
.loom-page-nav-toggle--plain .loom-page-nav-toggle__icon {
  font-size: 1.6rem;
}

.loom-social-links {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.loom-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--loom-radius-full, 9999px);
  color: var(--loom-color-ink, #111);
  background: transparent;
  text-decoration: none;
  transition: color var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease),
              background var(--loom-motion-fast, 120ms) var(--loom-ease-out, ease);
}
.loom-social-link:hover {
  color: var(--loom-color-primary);
  background: color-mix(in oklab, var(--loom-color-primary) 10%, transparent);
}
.loom-social-link::before {
  font-family: ui-monospace, monospace;
  font-size: 18px;
  line-height: 1;
  white-space: nowrap;
  letter-spacing: 0;
}
.loom-social-link--facebook::before    { content: "f"; font-weight: 900; font-style: italic; }
.loom-social-link--linkedin::before    { content: "in"; font-weight: 800; font-size: 14px; }
.loom-social-link--twitter::before,
.loom-social-link--x::before           { content: "X"; font-weight: 900; }
.loom-social-link--instagram::before   { content: "◉"; font-size: 22px; }
.loom-social-link--youtube::before     { content: "▶"; font-size: 16px; }
.loom-social-link--github::before      { content: "GH"; font-weight: 800; font-size: 13px; }
.loom-social-link--mastodon::before    { content: "M"; font-weight: 800; }

/* Utility-strip leading icons — same pure-CSS glyph mechanism as
   social links, keyed by slug. Built-ins below; tenants extend by
   adding more --slug rules. Trailing U+FE0E forces text (non-emoji)
   presentation so the glyph stays monochrome and inherits color. */
.loom-utility-strip__left, .loom-utility-strip__right {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.loom-utility-strip__icon {
  display: inline-flex;
  align-items: center;
}
.loom-utility-strip__icon::before {
  font-style: normal;
  line-height: 1;
  font-size: 1em;
}
.loom-utility-strip__icon--envelope::before { content: "\2709\FE0E"; }
.loom-utility-strip__icon--phone::before    { content: "\260E\FE0E"; }

/* Lang selector — JS-free <details> disclosure with chevron. */
.loom-lang-selector {
  position: relative;
  display: inline-block;
  font-size: 0.875rem;
}
.loom-lang-selector > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--loom-color-border, #e5e5e5);
  border-radius: var(--loom-radius-md, 6px);
  background: var(--loom-color-surface, #fff);
  color: var(--loom-color-ink, #111);
  user-select: none;
}
.loom-lang-selector > summary::-webkit-details-marker { display: none; }
.loom-lang-selector > summary::after {
  content: "▾";
  font-size: 0.7em;
  margin-left: 0.25rem;
  color: var(--loom-color-ink-muted, var(--loom-color-ink, #111));
}
.loom-lang-selector__marker {
  display: inline-block;
  font-size: 1.1em;
}
/* Image flag marker (LangSelector::current_marker_image / option
 * marker_image). 2:1 flag at ~1em tall; robust where emoji fonts are
 * absent. margin-right spaces it from the label inside menu <a> rows
 * (the summary uses flex `gap`, so a little extra there is harmless). */
.loom-lang-selector__flag {
  display: inline-block;
  height: 0.95em;
  width: auto;
  margin-right: 0.35rem;
  border-radius: 2px;
  vertical-align: -0.12em;
  object-fit: contain;
}
.loom-lang-selector__menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 60;
  list-style: none;
  margin: 0;
  padding: 0.4rem 0;
  min-width: 9rem;
  background: var(--loom-color-surface, #fff);
  border: 1px solid var(--loom-color-border, #e5e5e5);
  border-radius: var(--loom-radius-md, 6px);
  box-shadow: var(--loom-shadow-md, 0 4px 12px rgba(0,0,0,.1));
}
.loom-lang-selector__menu a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.85rem;
  color: var(--loom-color-ink, #111);
  text-decoration: none;
}
.loom-lang-selector__menu a:hover {
  background: color-mix(in oklab, var(--loom-color-primary) 8%, transparent);
}

/* Home icon — CSS-rendered house glyph, no external font/SVG file.
   The mark is an SVG-mask filled house (color inherited via
   background-color: currentColor) rather than the U+2302 ⌂ text
   glyph: the mask renders identically across fonts and reads as a
   solid house, the conventional home affordance. CSP-clean under the
   page-shell's `img-src 'self' data:`. */
.loom-page-nav__home-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--loom-radius-full, 9999px);
  color: inherit;
  text-decoration: none;
  font-size: 18px;
}
.loom-page-nav__home-icon::before {
  content: "";
  display: inline-block;
  width: 1.18em;
  height: 1.18em;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='black' d='M12 2 L1 12 h3 v10 h6 v-6 h4 v6 h6 V12 h3 Z'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='black' d='M12 2 L1 12 h3 v10 h6 v-6 h4 v6 h6 V12 h3 Z'/></svg>") center / contain no-repeat;
}
/* Optional vertical divider between the home icon and the first nav
   link. Off by default (width/gap 0, transparent) so other tenants are
   unchanged; a tenant opts in via forge.toml [style.nav]
   home_divider_width / home_divider_color / home_divider_gap. Absolute
   so enabling it never shifts the house glyph. */
.loom-page-nav__home-icon::after {
  content: "";
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-left: var(--loom-nav-home-divider-gap, 0px);
  width: var(--loom-nav-home-divider-width, 0px);
  height: var(--loom-nav-home-divider-height, 1.4em);
  background: var(--loom-nav-home-divider-color, transparent);
}
.loom-page-nav__home-icon:hover {
  background: color-mix(in oklab, currentColor 12%, transparent);
}

/* Inside colored-band header, brand-extras sit at top-right corner
   of the white brand row. Constrained to the brand-row height so they
   don't overlap the colored nav band below. */
.loom-page-header[data-nav-bg-role] {
  position: relative;
}
.loom-page-header[data-nav-bg-role] .loom-page-brand-extras {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.75rem;
  background: transparent;
  color: var(--loom-color-ink, #111);
  z-index: 5;
}

/* Hide brand-extras from doc flow when empty; the wrapper div is only
   emitted when at least one extra is present, so this is belt-and-suspenders. */
.loom-page-brand-extras:empty {
  display: none !important;
}

/* Mobile nav: tighter padding + smaller font so links fit better.
   Substrate-general — applies to any tenant with colored-band header. */
@media (max-width: 768px) {
  .loom-page-header[data-nav-bg-role] .loom-page-nav {
    justify-content: flex-start;
    gap: 0;
  }
  .loom-page-header[data-nav-bg-role] .loom-page-nav__link,
  .loom-page-header[data-nav-bg-role] .loom-page-nav a:not(.loom-page-brand) {
    padding: 0.6rem 0.75rem !important;
    font-size: 0.85rem !important;
  }
  .loom-page-header[data-nav-bg-role] .loom-page-brand img,
  .loom-page-header[data-nav-bg-role] .loom-page-brand__logo {
    max-height: 80px !important;
  }
  .loom-utility-strip {
    font-size: 0.75rem !important;
    padding: 0.5rem 0.75rem !important;
  }
}

/* Hero buttons — make them larger + flat-square per real-site shape. */
.loom-hero-slideshow .loom-button,
.loom-hero-slideshow__cta-row .loom-button {
  padding: 0.85rem 1.5rem !important;
  font-size: 0.95rem !important;
  font-weight: 600 !important;
  text-transform: uppercase !important;
  letter-spacing: .03em !important;
  border-radius: 4px !important;
}
.loom-hero-slideshow .loom-button[data-variant="secondary"] {
  background: var(--loom-color-primary) !important;
  color: var(--loom-color-on-primary, #fff) !important;
}

/* Footer bottom row — contact-cta + find-us + back-to-top.
   Substrate-general; emitted when at least one of those is set. */
.loom-page-footer__bottomrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1rem 0;
  border-top: 1px solid var(--loom-color-border, #eee);
  margin-top: 1.5rem;
}
.loom-page-footer__contact-cta {
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--loom-color-primary, var(--loom-color-ink, #111));
  text-decoration: none;
}
.loom-page-footer__contact-cta:hover {
  color: var(--loom-color-link-hover, var(--loom-color-primary));
}
.loom-page-footer__find-us {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
}
.loom-page-footer__find-us-label {
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--loom-color-ink-muted, var(--loom-color-ink, #111));
}
.loom-page-footer__bottomrow .loom-page-footer__back-to-top {
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--loom-color-ink, #111);
  text-decoration: none;
}

/* Footer yellow accent bar at very bottom. */
.loom-page-footer--rich {
  border-bottom: 4px solid var(--loom-color-accent, #C9921A);
}
/* Generic colophon-band primitive — opt-in via footer.colophon_bg_role.
   Tenants set the role; substrate maps it to the palette CSS var. */
.loom-page-footer__colophon-band {
  margin: 0 -1.75rem -2.5rem;
  padding: .75rem 1.75rem;
  text-align: center;
}
.loom-page-footer__colophon-band[data-bg-role="primary"]   { background: var(--loom-color-primary);   color: var(--loom-color-on-primary, #fff); }
.loom-page-footer__colophon-band[data-bg-role="secondary"] { background: var(--loom-color-secondary); color: var(--loom-color-on-secondary, #fff); }
.loom-page-footer__colophon-band[data-bg-role="accent"]    { background: var(--loom-color-accent);    color: var(--loom-color-on-accent, #111); }
.loom-page-footer__colophon-band[data-bg-role="muted"]     { background: color-mix(in oklab, var(--loom-color-ink, var(--loom-fg)) 6%, var(--loom-color-bg, var(--loom-bg))); }
.loom-page-footer__colophon-band .loom-page-footer__colophon {
  margin: 0;
  color: inherit;
}
