---
import type { RoadmapItemWithAttribution, RoadmapStatus } from '../lib/db';
import { computeRouteLayout, travelledStopFor } from '../lib/roadmap-layout';

interface Props {
  items: RoadmapItemWithAttribution[];
  viewportWidth?: number;   // SSR fallback for the layout math
}

const { items, viewportWidth = 1100 } = Astro.props;

// Align the first milestone with the left edge of the page's content column
// (matches the LatestDispatchBanner below). --content-max is 72rem = 1152px.
const CONTENT_MAX = 1152;
const DEFAULT_PADDING = 60;
const paddingLeft = Math.max(DEFAULT_PADDING, (viewportWidth - CONTENT_MAX) / 2);

const layout = computeRouteLayout({
  itemCount: items.length,
  viewportWidth,
  paddingLeft,
});
const travelledStop = travelledStopFor(items.map(i => i.status));

const STATUS_LABEL: Record<RoadmapStatus, string> = {
  shipping:    'SHIPPING',
  in_beta:     'IN BETA',
  exploring:   'EXPLORING',
  considering: 'CONSIDERING',
};
const STATUS_LABEL_COLOR: Record<RoadmapStatus, string> = {
  shipping:    '#6d8c7c',
  in_beta:     '#b96b58',
  exploring:   '#b4b2a9',
  considering: '#b4b2a9',
};
const STATUS_DOT_COLOR: Record<RoadmapStatus, string> = {
  shipping:    '#6d8c7c',
  in_beta:     '#b96b58',
  exploring:   '#b4b2a9',
  considering: '#d4d2c8',
};

// "You are here" — the most recent shipping item. -1 if nothing has shipped yet.
let lastShippingIndex = -1;
items.forEach((it, i) => { if (it.status === 'shipping') lastShippingIndex = i; });

function trailingLine(item: RoadmapItemWithAttribution): string | null {
  if (item.metadata_text && item.metadata_text.trim().length > 0) return item.metadata_text;
  if (item.attributed.length > 0) {
    const names = item.attributed.map(a => a.name.split(' ')[0]);
    if (names.length === 1) return `Shaped by ${names[0]}`;
    if (names.length === 2) return `Shaped by ${names[0]} and ${names[1]}`;
    return `Shaped by ${names.slice(0, -1).join(', ')} and ${names.at(-1)}`;
  }
  return null;
}

// Stringified x position of the 'you are here' milestone for the
// initial-scroll logic in the nav script. -1 → 0 (no scroll offset).
const initialShippingX = lastShippingIndex >= 0 ? layout.itemX[lastShippingIndex] : 0;
---
<section class="route" aria-label="Roadmap route" data-initial-x={initialShippingX}>

  <!-- The route — desktop horizontal. .rr-fullbleed escapes the parent
       .page max-width so the route can span the actual viewport while
       the header above and legend below stay centred in the content
       column. -->
  <div class="rr-wrap rr-fullbleed rr-desktop" data-item-count={items.length}>
    <div class="rr-scroll" id="rr-scroll">
      <div class="rr-scroll-inner">
        <div class="rr-track" id="rr-track" style={`width: ${layout.trackWidth}px; height: 420px;`}>
        <svg class="rr-path" id="rr-path-svg" width={layout.trackWidth} height="420" aria-hidden="true">
          <defs>
            <linearGradient id="rr-path-gradient" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" stop-color="#2a2520" stop-opacity="0.55"/>
              <stop offset={String(travelledStop)} stop-color="#2a2520" stop-opacity="0.55"/>
              <stop offset={String(Math.min(1, travelledStop + 0.06))} stop-color="#2a2520" stop-opacity="0.15"/>
              <stop offset="1" stop-color="#2a2520" stop-opacity="0.15"/>
            </linearGradient>
          </defs>
          {layout.pathD && (
            <path id="rr-path-d" d={layout.pathD} fill="none" stroke="url(#rr-path-gradient)" stroke-width="1.25" stroke-linecap="round"/>
          )}
        </svg>

        {items.map((item, i) => (
          <div
            class="rr-milestone"
            data-y={layout.itemY[i]}
            style={`left: ${layout.itemX[i]}px; top: ${layout.itemY[i]}px;`}
          >
            <div
              class:list={['rr-dot', { 'rr-current': i === lastShippingIndex }]}
              style={`background:${STATUS_DOT_COLOR[item.status]};`}
              aria-hidden="true"
            ></div>

            <div class:list={['rr-attach', `rr-attach-${layout.cardSide[i]}`]}>
              <div class="rr-connector" aria-hidden="true"></div>
              <a class="rr-card" tabindex="0" href={`#item-${item.id}`} id={`item-${item.id}`}>
                <p class="rr-eyebrow" style={`color:${STATUS_LABEL_COLOR[item.status]};`}>
                  {item.target ? `${item.target.toUpperCase()} · ` : ''}{STATUS_LABEL[item.status]}
                </p>
                <p class="rr-card-title">{item.title}</p>
                <div class="rr-more">
                  {item.description && <p class="rr-desc">{item.description}</p>}
                  {trailingLine(item) && <p class="rr-trail">{trailingLine(item)}</p>}
                </div>
              </a>
            </div>
          </div>
        ))}
        </div>
      </div>
    </div>

    <div class="rr-fade-left" id="rr-fade-l" aria-hidden="true"></div>
    <div class="rr-fade-right" id="rr-fade-r" aria-hidden="true"></div>

    <!-- Single forward-only advance affordance anchored to the right
         viewport edge. There's no left arrow on purpose — the path
         reads left-to-right and the user's instinct after looking at
         a milestone is 'what's next?', not 'what came before?'. -->
    <button
      type="button"
      class="rr-advance"
      id="rr-advance"
      aria-label="Further along the route"
    >
      <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
        <path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </button>
  </div>

  <!-- Legend lives in /roadmap.astro now so it returns to centred
       content-column width below the full-bleed route. -->

  <!-- Mobile vertical timeline -->
  <ol class="rr-mobile" aria-label="Roadmap timeline">
    {items.map((item, i) => (
      <li class="rrm-row">
        <div class="rrm-track-col" aria-hidden="true">
          <span class="rrm-dot" style={`background:${STATUS_DOT_COLOR[item.status]};`}></span>
          {i < items.length - 1 && <span class="rrm-line"></span>}
        </div>
        <div class="rrm-body">
          <p class="rrm-eyebrow" style={`color:${STATUS_LABEL_COLOR[item.status]};`}>
            {item.target ? `${item.target.toUpperCase()} · ` : ''}{STATUS_LABEL[item.status]}
          </p>
          <p class="rrm-title">{item.title}</p>
          {item.description && <p class="rrm-desc">{item.description}</p>}
          {trailingLine(item) && <p class="rrm-trail">{trailingLine(item)}</p>}
        </div>
      </li>
    ))}
  </ol>
</section>

<script>
  // Vanilla route runtime. Two concerns:
  //  1. Nav: arrow buttons, edge fades, initial-scroll into shipping
  //  2. Viewport-aware layout — SSR uses a 1100px fallback for the math;
  //     on the client we know the real viewport, so we recompute itemX
  //     positions + SVG path d + track width on mount and on (debounced)
  //     resize. itemY values come from data-y on each milestone (path
  //     amplitude doesn't change with viewport, only the horizontal spread).
  const MIN_SPACING = 320;
  const PADDING_X   = 60;
  const CONTENT_MAX = 1152;  // matches --content-max (72rem)

  document.querySelectorAll<HTMLElement>('.route').forEach((section) => {
    const scroll = section.querySelector<HTMLElement>('#rr-scroll');
    const wrap   = section.querySelector<HTMLElement>('.rr-wrap');
    const track  = section.querySelector<HTMLElement>('#rr-track');
    const svg    = section.querySelector<SVGSVGElement>('#rr-path-svg');
    const pathD  = section.querySelector<SVGPathElement>('#rr-path-d');
    const milestones = Array.from(section.querySelectorAll<HTMLElement>('.rr-milestone'));
    const fadeL  = section.querySelector<HTMLElement>('#rr-fade-l');
    const fadeR  = section.querySelector<HTMLElement>('#rr-fade-r');
    const advance = section.querySelector<HTMLButtonElement>('#rr-advance');
    if (!scroll || !track || !svg) return;

    const itemCount = milestones.length;
    const itemY: number[] = milestones.map(m => Number(m.dataset.y ?? 0));

    /** Recompute trackWidth + itemX[] + pathD using the live viewport. */
    function recompute() {
      const vw = window.innerWidth;
      const targetUsableWidth = vw * 0.80;
      const dataDrivenWidth   = (itemCount - 1) * MIN_SPACING;
      const usableWidth       = Math.max(targetUsableWidth, dataDrivenWidth);
      // Match the SSR offset — first item aligns with the content-column
      // left edge so the route lines up with the dispatch banner below.
      const paddingLeft       = Math.max(PADDING_X, (vw - CONTENT_MAX) / 2);
      const trackWidth        = paddingLeft + usableWidth + PADDING_X;

      const itemX: number[] = [];
      for (let i = 0; i < itemCount; i += 1) {
        itemX.push(
          itemCount === 1
            ? paddingLeft + usableWidth / 2
            : paddingLeft + (i / (itemCount - 1)) * usableWidth,
        );
      }

      // Bezier path: control points at the segment midpoint x with control
      // y values matching the prior and next milestone (keeps the tangent
      // flat at each dot — the "river" feel from the layout helper).
      let d = '';
      if (itemCount > 0) {
        d = `M ${itemX[0]} ${itemY[0]}`;
        for (let i = 1; i < itemCount; i += 1) {
          const cx = (itemX[i - 1] + itemX[i]) / 2;
          d += ` C ${cx} ${itemY[i - 1]}, ${cx} ${itemY[i]}, ${itemX[i]} ${itemY[i]}`;
        }
      }

      // Apply.
      track!.style.width = `${trackWidth}px`;
      svg!.setAttribute('width', String(trackWidth));
      if (pathD && d) pathD.setAttribute('d', d);
      milestones.forEach((m, i) => { m.style.left = `${itemX[i]}px`; });
    }

    /* Edge state — fades + advance disable. */
    function updateNav() {
      const max = scroll!.scrollWidth - scroll!.clientWidth;
      const atStart = scroll!.scrollLeft <= 2;
      const atEnd   = scroll!.scrollLeft >= max - 2;
      if (fadeL) fadeL.style.opacity = atStart ? '0' : '1';
      if (fadeR) fadeR.style.opacity = atEnd ? '0' : '1';
      if (advance) advance.classList.toggle('rr-at-end', atEnd);
    }

    /* ── Unified scroll handling: wheel, drag, animated glide. ──
       No CSS scroll-snap and no scroll-behavior: smooth — both fight
       the JS-driven smooth motion. Drag has momentum; wheel translates
       vertical to horizontal; arrow click runs a cubic-ease animation. */

    let isDragging = false;
    let dragStartX = 0;
    let dragStartScrollLeft = 0;
    let dragTotalMovement = 0;
    let lastMoveX = 0;
    let lastMoveTime = 0;
    let velocity = 0;          // px/ms, signed (positive = pointer moving right)
    let momentumRAF: number | null = null;
    let animateRAF: number | null = null;

    function cancelAnims() {
      if (momentumRAF !== null) { cancelAnimationFrame(momentumRAF); momentumRAF = null; }
      if (animateRAF !== null)  { cancelAnimationFrame(animateRAF);  animateRAF  = null; }
    }

    function animateScrollTo(target: number, durationMs: number) {
      cancelAnims();
      const start     = scroll!.scrollLeft;
      const delta     = target - start;
      const startTime = performance.now();
      const easeOut   = (t: number) => 1 - Math.pow(1 - t, 3);
      const step = () => {
        const t = Math.min(1, (performance.now() - startTime) / durationMs);
        scroll!.scrollLeft = start + delta * easeOut(t);
        updateNav();
        if (t < 1) animateRAF = requestAnimationFrame(step);
        else       animateRAF = null;
      };
      animateRAF = requestAnimationFrame(step);
    }

    // Wheel — vertical wheel becomes horizontal scroll on this element.
    // Trackpads sending horizontal deltaX go through unchanged (1:1, no scaling).
    scroll.addEventListener('wheel', (e) => {
      const dx = Math.abs(e.deltaX) > Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
      if (dx === 0) return;
      e.preventDefault();
      cancelAnims();
      scroll!.scrollLeft += dx;
      updateNav();
    }, { passive: false });

    // Drag — pointer events; momentum on release.
    scroll.addEventListener('pointerdown', (e) => {
      if (e.button !== undefined && e.button !== 0) return;
      // Don't start a drag when the click target is the advance button.
      if (advance && advance.contains(e.target as Node)) return;

      isDragging          = true;
      dragStartX          = e.pageX;
      dragStartScrollLeft = scroll!.scrollLeft;
      dragTotalMovement   = 0;
      lastMoveX           = e.pageX;
      lastMoveTime        = performance.now();
      velocity            = 0;

      cancelAnims();
      try { scroll!.setPointerCapture(e.pointerId); } catch { /* not all envs */ }
      scroll!.classList.add('rr-dragging');
    });

    scroll.addEventListener('pointermove', (e) => {
      if (!isDragging) return;
      const dx = e.pageX - dragStartX;
      scroll!.scrollLeft = dragStartScrollLeft - dx;
      dragTotalMovement = Math.max(dragTotalMovement, Math.abs(dx));

      const now = performance.now();
      const dt = now - lastMoveTime;
      if (dt > 0) velocity = (e.pageX - lastMoveX) / dt;
      lastMoveX = e.pageX;
      lastMoveTime = now;

      updateNav();
    });

    function endDrag() {
      if (!isDragging) return;
      isDragging = false;
      scroll!.classList.remove('rr-dragging');

      // Click vs drag: anything under 5px total movement is a click —
      // skip momentum and let the underlying card's <a> handle the click.
      if (dragTotalMovement < 5) return;

      // Otherwise it's a real drag — suppress the synthetic click that
      // follows so a drag-then-release-over-a-card doesn't navigate.
      const suppressClick = (ev: Event) => {
        ev.stopPropagation();
        ev.preventDefault();
        scroll!.removeEventListener('click', suppressClick, true);
      };
      scroll!.addEventListener('click', suppressClick, true);

      // Momentum: signed velocity, decay 0.93 per frame, stop under 0.4 px/frame.
      // Direction inverted because dragging right moves scrollLeft left.
      let v = -velocity * 16;
      const step = () => {
        if (Math.abs(v) < 0.4) { momentumRAF = null; return; }
        scroll!.scrollLeft += v;
        v *= 0.93;
        updateNav();
        momentumRAF = requestAnimationFrame(step);
      };
      momentumRAF = requestAnimationFrame(step);
    }

    scroll.addEventListener('pointerup', endDrag);
    scroll.addEventListener('pointercancel', endDrag);

    // Advance arrow — animated glide of 60% viewport width.
    advance?.addEventListener('click', () => {
      const target = Math.min(
        scroll!.scrollLeft + scroll!.clientWidth * 0.6,
        scroll!.scrollWidth - scroll!.clientWidth,
      );
      animateScrollTo(target, 480);
    });

    scroll.addEventListener('scroll', updateNav, { passive: true });

    // Debounced resize → recompute layout + refresh nav state. 120ms is
    // long enough to coalesce drag-resize events without feeling laggy.
    let resizeTimer: number | undefined;
    window.addEventListener('resize', () => {
      if (resizeTimer !== undefined) window.clearTimeout(resizeTimer);
      resizeTimer = window.setTimeout(() => { recompute(); updateNav(); }, 120);
    });

    // Initial mount: recompute with the real viewport, then anchor at the
    // start so the first milestone aligns with the content-column left edge.
    // (The "you are here" highlight on the most-recent shipping milestone is
    // still visible — but it's no longer the scroll anchor.)
    recompute();
    scroll.scrollLeft = 0;
    setTimeout(updateNav, 50);
    updateNav();

    // Three-pulse hint on the advance arrow ~100ms after layout settles
    // so the user notices the affordance once and then it sits quietly.
    setTimeout(() => advance?.classList.add('rr-hint'), 100);
  });
</script>

<style>
  /* ── Desktop route ──────────────────────────────────────────────── */
  .rr-wrap { position: relative; }

  /* Escape the parent .page max-width so the route can use the actual
     viewport width. The headline, dispatch banner, section header, and
     legend all stay centred at content width — only the route widens. */
  .rr-fullbleed {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
  }
  .rr-scroll {
    /* overflow-x: auto + overflow-y: visible lets hovered cards expand
       above/below the track without being clipped. .rr-scroll-inner is
       the spec-recommended belt-and-braces wrapper in case a browser
       misbehaves on the combination.
       NO scroll-snap-type and NO scroll-behavior: smooth — both fight
       the JS drag-momentum + animated-glide implementation below. The
       path is meant to glide continuously, not click into fixed
       positions. */
    overflow-x: auto;
    overflow-y: visible;
    scrollbar-width: none;
    padding: 60px 80px 80px;

    /* Drag affordance: cursor + suppress native horizontal swipe so
       horizontal drag triggers our handler while vertical drag still
       scrolls the page. user-select stops drag from selecting text. */
    cursor: grab;
    touch-action: pan-y;
    user-select: none;
  }
  .rr-scroll::-webkit-scrollbar { display: none; }
  .rr-scroll.rr-dragging { cursor: grabbing; }
  /* Pointer-events off the cards mid-drag — prevents accidental hover
     reveal while the track is being dragged past. */
  .rr-scroll.rr-dragging .rr-card { pointer-events: none; }
  .rr-scroll-inner { /* structural — keeps the track on its own layer */ }
  .rr-track { position: relative; }
  .rr-path { position: absolute; top: 0; left: 0; pointer-events: none; }

  .rr-milestone {
    position: absolute;
    transform: translate(-50%, -50%);
  }

  .rr-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    box-shadow: 0 0 0 5px var(--background);   /* halo cuts the path under the dot */
    transition: transform .25s ease, box-shadow .25s ease;
  }
  .rr-dot.rr-current {
    transform: scale(1.3);
    box-shadow:
      0 0 0 5px var(--background),              /* cream halo */
      0 0 0 6px rgba(185, 107, 88, 0.45);       /* terracotta ring outside */
  }

  /* Hover-on-card animates the sibling dot too. :has() is fine on every
     evergreen browser we target; older Firefox just doesn't grow the dot. */
  .rr-milestone:has(.rr-card:hover) .rr-dot,
  .rr-milestone:has(.rr-card:focus-visible) .rr-dot {
    transform: scale(1.15);
  }
  .rr-milestone:has(.rr-card:hover) .rr-dot.rr-current,
  .rr-milestone:has(.rr-card:focus-visible) .rr-dot.rr-current {
    transform: scale(1.4);
  }

  .rr-attach {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .rr-attach-below { top: 7px; }    /* hangs down from the dot */
  .rr-attach-above { bottom: 7px; flex-direction: column-reverse; }

  .rr-connector {
    width: 1px;
    height: 30px;
    background: rgba(0, 0, 0, 0.18);
  }

  .rr-card {
    display: block;
    width: 220px;
    padding: 12px 14px;
    border-radius: 10px;
    background: transparent;
    color: inherit;
    text-decoration: none;
    border-bottom: none;
    transition:
      transform .35s cubic-bezier(.2,.7,.3,1),
      box-shadow .35s ease,
      background .25s ease;
    cursor: pointer;
  }
  .rr-card:hover,
  .rr-card:focus-visible {
    background: var(--surface-card);
    box-shadow:
      0 12px 32px -16px rgba(42, 37, 32, 0.25),
      0 0 0 0.5px var(--surface-card-border);
    transform: translateY(-2px);
    z-index: 10;
    border-bottom: none;
    outline: none;
  }

  .rr-eyebrow {
    font-family: var(--font-sans);
    font-size: 9px;
    letter-spacing: 1.4px;
    text-transform: uppercase;
    margin: 0 0 6px;
    font-weight: 600;
  }
  .rr-card-title {
    font-family: var(--font-serif);
    font-size: 16px;
    line-height: 1.2;
    color: var(--on-surface);
    margin: 0;
  }
  .rr-more {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transition:
      max-height .35s ease,
      opacity .25s ease,
      margin-top .35s ease;
    margin-top: 0;
  }
  .rr-card:hover .rr-more,
  .rr-card:focus-visible .rr-more {
    max-height: 280px;
    opacity: 1;
    margin-top: 10px;
  }
  .rr-desc {
    font-family: var(--font-sans);
    font-size: 12px;
    line-height: 1.55;
    color: var(--on-surface-variant);
    margin: 0 0 10px;
  }
  .rr-trail {
    font-family: var(--font-sans);
    font-size: 9px;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--on-surface-muted);
    margin: 0;
  }

  /* ── Advance arrow ─────────────────────────────────────────────── */
  .rr-advance {
    position: absolute;
    right: 32px;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1px solid var(--pigment-terracotta);
    background: var(--background);
    color: var(--pigment-terracotta);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    z-index: 5;
    transition: background .2s ease,
                color      .2s ease,
                opacity    .25s ease,
                transform  .25s ease;
  }
  .rr-advance:hover,
  .rr-advance:focus-visible {
    background: var(--pigment-terracotta);
    color: var(--background);
    outline: none;
    transform: translateY(-50%) scale(1.06);
  }
  .rr-advance[disabled],
  .rr-advance.rr-at-end {
    opacity: 0.25;
    pointer-events: none;
  }
  /* Three-pulse hint on first load — fires once, then stops. */
  @keyframes rr-advance-pulse {
    0%, 100% { box-shadow: 0 0 0 0   rgba(185, 107, 88, 0); }
    50%      { box-shadow: 0 0 0 8px rgba(185, 107, 88, 0.15); }
  }
  .rr-advance.rr-hint {
    animation: rr-advance-pulse 1.4s ease-in-out 3;
  }

  /* Edge fades cover only the track itself — the top/bottom padding
     zones (60/80) on .rr-scroll exist so hover cards can overflow there
     without clipping, so the fades shouldn't paint over them. */
  .rr-fade-left, .rr-fade-right {
    position: absolute;
    top: 60px;
    bottom: 80px;
    pointer-events: none;
    transition: opacity .25s ease;
  }
  .rr-fade-left {
    left: 0; width: 60px;
    background: linear-gradient(to left, transparent, var(--background));
    opacity: 0;
  }
  .rr-fade-right {
    right: 0; width: 90px;
    background: linear-gradient(to right, transparent, var(--background));
  }

  /* ── Mobile vertical timeline ──────────────────────────────────── */
  .rr-mobile { display: none; }

  @media (max-width: 767px) {
    .rr-desktop { display: none; }
    .rr-mobile {
      display: block;
      list-style: none;
      padding: 0;
      margin: 0;
    }

    .rrm-row {
      display: grid;
      grid-template-columns: 32px 1fr;
      gap: 16px;
      align-items: start;
    }
    .rrm-track-col {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0;
      min-height: 100%;
      padding-top: 6px;
    }
    .rrm-dot {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .rrm-line {
      width: 1px;
      flex: 1;
      min-height: 28px;
      background: rgba(0, 0, 0, 0.18);
      margin-top: 4px;
    }
    .rrm-body {
      display: flex;
      flex-direction: column;
      gap: 6px;
      padding-bottom: 28px;
    }
    .rrm-eyebrow {
      font-family: var(--font-sans);
      font-size: 9px;
      letter-spacing: 1.4px;
      text-transform: uppercase;
      margin: 0;
      font-weight: 600;
    }
    .rrm-title {
      font-family: var(--font-serif);
      font-size: 18px;
      line-height: 1.2;
      color: var(--on-surface);
      margin: 0;
    }
    .rrm-desc {
      font-family: var(--font-sans);
      font-size: 13px;
      line-height: 1.55;
      color: var(--on-surface-variant);
      margin: 0;
    }
    .rrm-trail {
      font-family: var(--font-sans);
      font-size: 9px;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: var(--on-surface-muted);
      margin: 0;
    }
  }
</style>
