/*
 * Luminatti dashboard theme - layered over DaisyUI 5 (CDN).
 *
 * Brand tokens override the built-in `light` / `dark` DaisyUI themes
 * (this file loads after daisyui.css, so same-specificity rules win).
 * Plus the few structural rules Tailwind utilities can't express well:
 * widget grid spans, freshness states, command palette, slide-over panel.
 */

[data-theme="light"] {
  --color-base-100: oklch(99.1% 0.003 255);
  --color-base-200: oklch(96.8% 0.004 255);
  --color-base-300: oklch(92.5% 0.006 255);
  --color-base-content: oklch(24% 0.02 262);
  --color-primary: oklch(55% 0.19 264);
  --color-primary-content: oklch(98% 0.01 264);
  --radius-selector: 0.75rem;
  --radius-field: 0.625rem;
  --radius-box: 1rem;
  --depth: 0;
  --noise: 0;
}

[data-theme="dark"] {
  --color-base-100: oklch(20% 0.015 262);
  --color-base-200: oklch(16.5% 0.012 262);
  --color-base-300: oklch(25% 0.02 262);
  --color-base-content: oklch(90% 0.01 255);
  --color-primary: oklch(70% 0.16 264);
  --color-primary-content: oklch(16% 0.02 264);
  --radius-selector: 0.75rem;
  --radius-field: 0.625rem;
  --radius-box: 1rem;
  --depth: 0;
  --noise: 0;
}

/* `primary` isn't registered as a Tailwind color anywhere in this
   project's @theme (only `--color-primary` exists, as a DaisyUI runtime
   variable) - so Tailwind's build never generates ring-primary/
   bg-primary/text-primary/etc. utilities for it at all. Hand-defining
   just the one utility actually used (store.js's alert-highlight ring)
   here, where DaisyUI-variable-driven overrides already live, rather
   than restructuring the Tailwind theme config for every -primary
   variant that isn't otherwise needed. --tw-ring-color is the exact
   custom property Tailwind's own .ring-2 utility reads. */
.ring-primary {
  --tw-ring-color: var(--color-primary);
}

/* ------------------------------------------------------------------ */
/* Widget grid                                                         */
/* ------------------------------------------------------------------ */

.widget-grid {
  /* Density defaults - Comfortable, i.e. today's original literal values.
     Compact/Spacious override just these custom properties (see below)
     rather than duplicating the whole grid rule. --widget-title-scale
     applies only to .widget-title/.widget-device-name (device name and
     widget title) - it moves much less than --grid-cell's own shrink/
     grow between densities on purpose, so those two labels stay legible
     even at Compact instead of scaling down with everything else. */
  --grid-min-col: 15rem;
  --grid-cell: 9.5rem;
  --grid-gap: 1rem;
  --widget-title-scale: 1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-col), 1fr));
  grid-auto-rows: var(--grid-cell);
  grid-auto-flow: dense;
  gap: var(--grid-gap);
}

/* Arrange-mode magnet toggle (off): dense auto-flow backfills gaps by
   pulling later widgets forward on every drag, which can make an
   unrelated widget jump when a small one moves. Non-dense row flow still
   auto-packs, just without that backfill, so a drag's new DOM position
   more reliably predicts the final visual position. */
.widget-grid.no-pack {
  grid-auto-flow: row;
}

.density-compact .widget-grid {
  --grid-min-col: 12rem;
  --grid-cell: 7.5rem;
  --grid-gap: 0.5rem;
  --widget-title-scale: 0.93;
}
.density-spacious .widget-grid {
  --grid-min-col: 18rem;
  --grid-cell: 11.5rem;
  --grid-gap: 1.5rem;
  --widget-title-scale: 1.04;
}

/* Mobile: force exactly 2 columns regardless of density, rather than
   auto-fill collapsing to 1 on a narrow phone - same 40rem cutoff already
   used below for collapsing widget spans to 1, so both rules agree on
   what "mobile" means. Below 22rem (an unrealistically narrow phone
   today) falls back to 1 column instead of squeezing 2 unreadably. */
@media (max-width: 40rem) {
  .widget-grid {
    grid-template-columns: repeat(2, 1fr);
    --grid-cell: 8rem;
    --grid-gap: 0.5rem;
  }
}
@media (max-width: 22rem) {
  .widget-grid {
    grid-template-columns: 1fr;
  }
}

.widget-title {
  font-size: calc(0.75rem * var(--widget-title-scale, 1)); /* text-xs base */
}
.widget-device-name {
  font-size: calc(0.6875rem * var(--widget-title-scale, 1)); /* 11px base */
}

.widget-card {
  position: relative;
  transition: border-color 0.2s ease, opacity 0.3s ease;
}

/* Alert threshold indicator - a thin strip along the card's top edge.
   Warning: solid amber. Critical: solid red, softly blinking (reuses
   the same rt-pulse keyframe as the realtime status dot). Plain
   .widget-card::before with a transparent strip costs nothing when the
   widget is within normal range (thresholdState === 'normal').

   ::before spans the FULL card (inset: 0), not just the 3px strip
   height, and a hard-stop gradient paints color only for the top 3px -
   this is deliberate, not an oversight: a border-radius can't render
   correctly on a box only 3px tall (the browser auto-scales the
   requested var(--radius-box) down to ~3px to avoid a self-intersecting
   curve, which no longer matches the card's real corner curve once
   .widget-card lost its own overflow:hidden - that clipping used to cut
   off the widget's dropdown menu, position:absolute DaisyUI
   dropdown-content, whenever it overflowed a small card's height).
   Sizing ::before to the card's own full box lets border-radius render
   at its real value, so the gradient-painted strip is clipped to the
   exact same curve as the card's own corners. pointer-events: none is
   required now that this covers the whole card - it must never
   intercept clicks/drag on the card's real content underneath it. */
.widget-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius-box);
  background: linear-gradient(to bottom, var(--threshold-strip-color, transparent) 0 3px, transparent 3px 100%);
  pointer-events: none;
}
.widget-card.threshold-warning::before { --threshold-strip-color: var(--color-warning); }
.widget-card.threshold-critical::before {
  --threshold-strip-color: var(--color-error);
  animation: rt-pulse 1.2s ease-in-out infinite;
}

.span-w-2 { grid-column: span 2; }
.span-h-2 { grid-row: span 2; }
.span-w-3 { grid-column: span 3; }
.span-h-3 { grid-row: span 3; }
.span-w-4 { grid-column: span 4; }
.span-h-4 { grid-row: span 4; }

@media (max-width: 40rem) {
  .span-w-2, .span-w-3, .span-w-4 { grid-column: span 1; }

  /* Typographic widgets (a plain text value, no graph/gauge/control chrome)
     read poorly squeezed into a single mobile column - let them take both.
     A widget with its own small graph (value's sparkline) stays 1 column
     like every other widget; only the full chart widget spans both. */
  .widget-card.type-status_text, .widget-card.type-chart { grid-column: span 2; }
}

.widget-drag-handle {
  cursor: grab;
  opacity: 0.5;
}
.widget-drag-handle:active { cursor: grabbing; }

.sortable-ghost {
  opacity: 0.4;
  border: 1px dashed var(--color-primary);
}
.sortable-drag { opacity: 0.9; }

/* Freshness degradation: fresh (normal) → stale (dimmed) → offline (ghosted).
   Scoped to .widget-dim wrappers (dashboard.html), NOT the .widget-card root
   itself - the 3-dot menu lives outside those wrappers so opening it on a
   stale/offline widget never composites the menu's own solid background
   through a reduced ancestor opacity (opacity isn't something a descendant
   can opt back out of). */
.widget-dim.is-stale { opacity: 0.75; }
.widget-dim.is-offline { opacity: 0.45; }

.freshness-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
  flex: none;
}
.freshness-dot.fresh { background: var(--color-success); }
.freshness-dot.stale { background: var(--color-warning); }
.freshness-dot.offline { background: var(--color-base-300); }

/* Flash animation when a live value arrives */
@keyframes value-tick {
  0% { color: var(--color-primary); }
  100% { color: inherit; }
}
.value-tick { animation: value-tick 0.9s ease-out; }

/* ------------------------------------------------------------------ */
/* Command palette                                                     */
/* ------------------------------------------------------------------ */

.palette-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: color-mix(in oklab, var(--color-base-200) 55%, transparent);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

.palette-panel {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(24rem, 70vh);
}

.palette-item[aria-selected="true"] {
  background: var(--color-primary);
  color: var(--color-primary-content);
}
.palette-item[aria-selected="true"] .palette-hint,
.palette-item[aria-selected="true"] mark {
  color: inherit;
}
.palette-item mark {
  background: transparent;
  color: var(--color-primary);
  font-weight: 700;
}

/* ------------------------------------------------------------------ */
/* Slide-over device panel                                             */
/* ------------------------------------------------------------------ */

.slideover {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  width: min(34rem, 100vw);
  transform: translateX(100%);
  transition: transform 0.25s ease;
}
.slideover.open { transform: translateX(0); }

.slideover-backdrop {
  position: fixed;
  inset: 0;
  z-index: 49;
  background: color-mix(in oklab, black 30%, transparent);
}

/* Left-anchored variant, layered on top of .slideover (`class="slideover
   slideover-left"`) - the Trigger and Alerts drawers use this side, kept
   deliberately distinct from Device's right-anchored panel above so the
   two are never visually confused for one another. */
.slideover-left {
  right: auto;
  left: 0;
  transform: translateX(-100%);
}
.slideover-left.open {
  transform: translateX(0);
}

/* ------------------------------------------------------------------ */
/* Misc                                                                */
/* ------------------------------------------------------------------ */

/* Realtime status dot in the topbar */
.rt-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 9999px;
}
.rt-dot.connected { background: var(--color-success); }
.rt-dot.connecting, .rt-dot.reconnecting {
  background: var(--color-warning);
  animation: rt-pulse 1.2s ease-in-out infinite;
}
.rt-dot.disconnected { background: var(--color-error); }

@keyframes rt-pulse {
  50% { opacity: 0.35; }
}

/* Connection-status ring around the profile avatar - same state colors as
   .rt-dot above, just as a ring instead of a dot (the navbar's standalone
   dot was merged into the avatar). Hand-written rather than Tailwind's
   `ring-success`/`ring-warning`/`ring-error` utilities: Tailwind's local
   build only knows Tailwind's own palette, not DaisyUI's semantic color
   names, so those utility classes silently compile to nothing. */
.status-ring {
  box-shadow: 0 0 0 2px var(--color-base-100), 0 0 0 4px var(--status-ring-color, var(--color-base-300));
}
.status-ring.connected { --status-ring-color: var(--color-success); }
.status-ring.connecting, .status-ring.reconnecting {
  --status-ring-color: var(--color-warning);
  animation: rt-pulse 1.2s ease-in-out infinite;
}
.status-ring.disconnected { --status-ring-color: var(--color-error); }

/* Generic soft-blink utility, shared by the realtime dot/avatar ring and
   the urgent-alerts bell icon. */
.rt-pulse, .alert-pulse {
  animation: rt-pulse 1.2s ease-in-out infinite;
}

/* Keep chart containers from overflowing their cards, and scope their
   reflow/repaint to their own box - contain stops ancestor flex/grid
   recalculation (e.g. a sibling's threshold-state class flip) from
   cascading into the chart's own layout pass. Rounded + clipped to match
   the sibling loading .skeleton exactly (same rounded-box radius) - the
   two overlay the same absolute inset-* box, so without this the
   previously-mounted chart's square canvas corners showed past the
   skeleton's rounded ones while a new chart was loading. */
.chart-host {
  width: 100%;
  height: 100%;
  min-height: 0;
  contain: layout style;
  overflow: hidden;
  border-radius: var(--radius-box);
}

/* Absolutely-positioned instances (analytics.html, devices.html,
   dashboard.html's chart/gauge modals, the widget-chart modal) also pair
   .chart-host with Tailwind's `absolute` plus a full inset-0/inset-4 on
   all four sides. Specifying left+right+width (and top+bottom+height)
   together over-constrains CSS's absolute-positioning box model - the
   browser drops right/bottom and sizes from left/top + the explicit
   100% width/height instead, which is the FULL container's size, not
   "100% minus the insets" - so the box (and the ECharts canvas filling
   it) spilled past the container's right/bottom edge. `auto` lets the
   four inset offsets alone determine the box, as intended. */
.chart-host.absolute {
  width: auto;
  height: auto;
}

/* ECharts' dataZoom drag-to-pan reads mousedown/mousemove on the <canvas>
   itself - without these, the browser's own native drag-and-drop kicks in
   first (canvas content is draggable by default in some browsers), which
   shows its own ghost-image thumbnail (a solid-looking rectangle) instead
   of panning the chart, and leaves ECharts' pointer state to recover/
   redraw once the native drag ends. Disabling native drag/selection and
   touch gestures on the canvas hands the whole gesture to ECharts. */
.chart-host canvas {
  -webkit-user-drag: none;
  user-select: none;
  touch-action: none;
}

/* Mobile: page scroll must always win over a chart's own drag-to-pan/zoom -
   tapping the zoom buttons or the tap-to-reveal controls (dashboard.html)
   remains the only zoom path on mobile, but a vertical drag anywhere over a
   widget's chart/value/gauge canvas must not swallow the scroll gesture. */
@media (max-width: 40rem) {
  .chart-host canvas { touch-action: pan-y; }
}

/* ------------------------------------------------------------------ */
/* Critical-alert marquee                                             */
/* ------------------------------------------------------------------ */

.marquee-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 55;
  height: 2.25rem;
  overflow: hidden;
  color: #000000;
  white-space: nowrap;
  /* Scope style/layout/paint invalidation to this subtree - the slots
     inside mutate their inline transform every animation frame, and
     containment keeps that churn from ever being considered against the
     rest of the page's DOM. Safe here: the bar is a fixed, overflow-hidden
     strip whose contents never need to affect outside layout. */
  contain: layout style paint;
  /* Bicolor diagonal hazard-tape look: alternating bands of the error
     color and a slightly whiter tint of the same color, instead of a
     flat fill. */
  background: repeating-linear-gradient(
    135deg,
    var(--color-error) 0 24px,
    color-mix(in oklab, var(--color-error) 75%, white) 24px 48px
  );
}

/* Each active alert is its own independently-positioned slot (JS writes
   translateX per frame, per slot - see components/marquee.js), not a
   shared single-track offset - this is what lets a new alert queue in
   behind whatever's already scrolling instead of restarting the whole
   ribbon. position: absolute inside .marquee-bar's `position: fixed`
   containing block gives each slot a fully independent x position. */
.marquee-slot {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-right: 3rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  will-change: transform;
}

/* prefers-reduced-motion: static, in-flow layout, no JS animation loop
   (components/marquee.js skips starting it) - just a plain wrapped row. */
.marquee-bar.reduced-motion {
  display: flex;
  align-items: center;
  overflow-x: auto;
}
.marquee-bar.reduced-motion .marquee-slot {
  position: static;
}

.marquee-sep {
  margin-left: 0.75rem;
  opacity: 0.6;
}

/* ------------------------------------------------------------------ */
/* Masked contact email (partials/_contact_mask.html)                  */
/* ------------------------------------------------------------------ */

.contact-mask {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.contact-mask-value {
  font-variant-numeric: tabular-nums;
}

.contact-reveal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--color-primary);
  cursor: pointer;
  animation: contact-reveal-pulse 2.2s ease-in-out infinite;
}

.contact-reveal-btn:hover {
  background: color-mix(in oklch, var(--color-primary) 15%, transparent);
}

.contact-reveal-btn:disabled {
  animation: none;
  opacity: 0.5;
  cursor: default;
}

@keyframes contact-reveal-pulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .contact-reveal-btn { animation: none; }
}

/* Hide Google's floating reCAPTCHA badge. Allowed by Google's ToS only
   because the attribution notice is shown instead - see the public
   footer (partials/_public_footer.html). */
.grecaptcha-badge {
  visibility: hidden !important;
}

/* ------------------------------------------------------------------ */
/* Public marketing nav (header/footer, landing page sections)        */
/* ------------------------------------------------------------------ */

/* Anchor sections must clear the sticky navbar when scrolled to. */
section[id] {
  scroll-margin-top: 4.5rem;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* The navy Moai logo body disappears on dark surfaces; a soft light chip
   behind it keeps the mark legible in dark theme. */
.logo-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.5rem;
  padding: 0.2rem 0.3rem;
}
[data-theme="dark"] .logo-chip {
  background: oklch(96% 0.005 255);
}

/* Scroll-spy highlight for the active nav link (static/js/core/landing-nav.js). */
.nav-link.active {
  color: var(--color-primary);
  font-weight: 600;
}

/* Hero dashboard screenshot - swaps with the theme toggle, no JS needed. */
.dashboard-shot-dark {
  display: none;
}
[data-theme="dark"] .dashboard-shot-dark {
  display: block;
}
[data-theme="dark"] .dashboard-shot-light {
  display: none;
}

/* ------------------------------------------------------------------ */
/* Docs long-form content (docs_start/concepts/guides/samples/protocol) */
/* ------------------------------------------------------------------ */

/* Hand-written rather than a Tailwind Typography ("prose") plugin: this
   project has never installed @tailwindcss/typography (input.css only has
   `@import "tailwindcss";`), so `prose` classes silently style nothing.
   Scoped to raw h1-h3/p/ul/ol/pre/code inside `.docs-content` so the docs
   templates can write plain semantic HTML instead of hand-annotating every
   single tag with utility classes, same rationale as the widget-grid rules
   above. Uses the same --color-* design tokens as the rest of this file,
   so it tracks light/dark and any future brand-color change for free. */
.docs-content h1 {
  font-size: 1.875rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 0.75rem;
}
.docs-content h2 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-top: 2.5rem;
  margin-bottom: 0.75rem;
}
.docs-content h2:first-child {
  margin-top: 0;
}
.docs-content h3 {
  font-size: 1.125rem;
  font-weight: 600;
  margin-top: 1.75rem;
  margin-bottom: 0.5rem;
}
.docs-content p {
  font-size: 0.9375rem;
  line-height: 1.7;
  opacity: 0.85;
  margin-bottom: 1rem;
}
.docs-content p.lead {
  font-size: 1.0625rem;
  line-height: 1.6;
  opacity: 0.9;
}
.docs-content ul,
.docs-content ol {
  margin: 0 0 1rem 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.7;
  opacity: 0.85;
}
.docs-content ul {
  list-style: disc;
}
.docs-content ol {
  list-style: decimal;
}
.docs-content li {
  margin-bottom: 0.375rem;
}
.docs-content a {
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.docs-content a:hover {
  opacity: 0.8;
}
.docs-content strong {
  font-weight: 600;
}
.docs-content :not(pre) > code {
  background: var(--color-base-300);
  padding: 0.1rem 0.4rem;
  border-radius: 0.35rem;
  font-size: 0.8125rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Code colors are owned by highlight.js's github/github-dark themes
   (partials/_docs_code_assets.html), swapped to match html[data-theme].
   These rules only handle layout: framed blocks (.docs-code, built by
   partials/_code_macros.html with their own header bar and border) get a
   flush pre; a bare pre outside a frame still reads fine standalone. */
.docs-content pre {
  overflow-x: auto;
  font-size: 0.8125rem;
  line-height: 1.6;
}
.docs-content .docs-code pre {
  margin: 0;
}
.docs-content pre:not(.docs-code pre) {
  border: 1px solid var(--color-base-300);
  border-radius: var(--radius-box);
  margin-bottom: 1.25rem;
}
.docs-content pre code {
  display: block;
  padding: 1rem 1.25rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Before hljs classes land (or if its CDN fails), keep code readable on
   the app background instead of unstyled default colors. */
.docs-content pre code:not(.hljs) {
  background: var(--color-base-100);
}
/* :not(.table) - DaisyUI's own .table component (used for the rate-limit
   and roadmap tables) already fully styles itself; these plain rules only
   apply to a bare, unclassed <table>. */
.docs-content table:not(.table) {
  width: 100%;
  font-size: 0.875rem;
  border-collapse: collapse;
  margin-bottom: 1.25rem;
}
.docs-content table:not(.table) th,
.docs-content table:not(.table) td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--color-base-300);
  text-align: left;
}
.docs-content table:not(.table) th {
  font-weight: 600;
  opacity: 0.7;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* ------------------------------------------------------------------ */
/* Docs 3-column shell: nav | prose | code                            */
/* ------------------------------------------------------------------ */

/* Outer shell: sidebar + main content area. */
.docs-shell {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
  max-width: 72rem;
  margin-inline: auto;
}
@media (max-width: 64rem) {
  .docs-shell {
    grid-template-columns: 1fr;
  }
}

.docs-sidebar {
  position: sticky;
  top: 4.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  max-height: calc(100vh - 5.5rem);
  overflow-y: auto;
  padding-bottom: 1rem;
}
@media (max-width: 64rem) {
  .docs-sidebar {
    position: static;
    max-height: none;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.375rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0;
  }
  .docs-sidebar > div:first-child {
    width: 100%;
  }
}

.docs-nav-link {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.45rem 0.75rem;
  border-radius: var(--radius-field);
  font-size: 0.8125rem;
  opacity: 0.75;
  text-decoration: none;
  white-space: nowrap;
}
.docs-nav-link:hover {
  background: var(--color-base-200);
  opacity: 1;
}
.docs-nav-link.active {
  background: var(--color-base-200);
  color: var(--color-primary);
  opacity: 1;
  font-weight: 600;
}

.docs-main {
  min-width: 0;
}

/* Within .docs-main: each "section" pairs explanation (left) with its
   code (right) - code stays pinned (position: sticky) while its section's
   prose scrolls past, releasing once the section ends, the same pattern
   Stripe/Mintlify-style API docs use, achieved here with plain CSS (no
   scroll-spy JS needed since each section is its own sticky containing
   block). Falls back to simple top-to-bottom stacking on narrow screens. */
.docs-section {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  gap: 2rem;
  align-items: start;
  margin-bottom: 2.5rem;
}
@media (max-width: 64rem) {
  .docs-section {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
}

.docs-prose {
  min-width: 0;
}

.docs-code-col {
  min-width: 0;
  position: sticky;
  top: 4.5rem;
}
@media (max-width: 64rem) {
  .docs-code-col {
    position: static;
  }
}
