/* Generated by build/build.mjs — edit build/lib/*.css, not this file. */
/*
 * Self-hosted, WOFF2. No Google Fonts request: a product whose whole argument
 * is "no cloud" should not phone one home to render its own headline.
 *
 * Plus Jakarta Sans is the family unimsg.io uses, taken as the variable font
 * so one 27 KB file covers every weight from 200 to 800.
 */
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/fonts/plus_jakarta_sans.woff2") format("woff2-variations"),
       url("/assets/fonts/plus_jakarta_sans.woff2") format("woff2");
  font-weight: 200 800; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/fonts/plus_jakarta_sans_ext.woff2") format("woff2-variations"),
       url("/assets/fonts/plus_jakarta_sans_ext.woff2") format("woff2");
  font-weight: 200 800; font-style: normal; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Machine text only: version strings, hashes, code, ticket numbers. */
@font-face {
  font-family: "JetBrains Mono";
  src: url("/assets/fonts/jetbrains_mono_regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/assets/fonts/jetbrains_mono_bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/*
 * UniKiosk — design tokens, aligned to unimsg.io.
 *
 * Both products belong to UniCore, so this site now speaks the sister site's
 * visual language: Plus Jakarta Sans, a white ground with slate greys, mint
 * (#00FFA3) as the fill accent, violet (#6D28D9) as the second, pill shapes,
 * generous radii and soft shadows. Values were read off unimsg.io's own
 * Elementor globals and computed styles rather than guessed.
 *
 *   --e-global-color-primary  #00FFA3   mint
 *   --e-global-color-accent   #6D28D9   violet
 *   --e-global-color-47fb81e  #1B2340   navy
 *   --e-global-color-text     #0B0F19   ink
 *
 * Paper is the default, because unimsg.io is a light site. Ink is the dark
 * counterpart and is declared in full — every token has a value in both, since
 * a token that exists in only one is a bug waiting for the toggle.
 *
 * One rule about the mint: at #00FFA3 it is a *fill*, never text on white
 * (1.4:1). Text uses --accent-ink-safe, which is emerald-600 on Paper and the
 * mint itself on Ink.
 */

/* -------------------------------------------------------------- paper --- */
:root,
:root[data-theme="paper"] {
  --bg:            #ffffff;
  --bg-1:          #f8fafc;   /* section tint      (slate-50)  */
  --bg-2:          #f1f5f9;   /* raised twice      (slate-100) */
  --bg-inset:      #eef2f7;   /* recessed: wells, previews     */
  --bg-panel:      #ffffff;   /* cards sit on the tint         */

  --line:          #e2e8f0;   /* slate-200 */
  --line-2:        #cbd5e1;   /* slate-300 */
  --line-3:        #94a3b8;   /* slate-400 */

  --ink:           #0b0f19;   /* headings */
  --ink-1:         #334155;   /* body     (slate-700) */
  --ink-2:         #64748b;   /* secondary(slate-500) */
  --ink-3:         #94a3b8;   /* captions (slate-400) */

  --accent:        #00ffa3;   /* the fill: buttons, marks, rails */
  --accent-2:      #00e693;
  --accent-on:     #0b0f19;   /* text on the mint */
  --accent-ink-safe: #059669; /* mint's readable stand-in for text */
  --accent-soft:   #d1fae5;   /* tinted badge ground */
  --accent-line:   #6ee7b7;

  --violet:        #6d28d9;
  --violet-soft:   #ede9fe;
  --violet-line:   #c4b5fd;
  --violet-on:     #ffffff;

  --grad:          linear-gradient(100deg, #6d28d9 0%, #4f46e5 38%, #059669 72%, #00c98a 100%);
  --grad-flat:     linear-gradient(100deg, #6d28d9, #00ffa3);
  --wash:          radial-gradient(900px 520px at 12% -8%, #ede9fe 0%, transparent 62%),
                   radial-gradient(880px 560px at 92% 6%, #d1fae5 0%, transparent 60%);

  --ok:            #059669;  --ok-soft:   #d1fae5;
  --warn:          #b45309;  --warn-soft: #fef3c7;
  --bad:           #be123c;  --bad-soft:  #ffe4e6;

  --sh-1: 0 1px 2px rgba(11, 15, 25, .05);
  --sh-2: 0 4px 6px -4px rgba(11, 15, 25, .07), 0 10px 15px -3px rgba(11, 15, 25, .07);
  --sh-3: 0 8px 10px -6px rgba(11, 15, 25, .08), 0 20px 25px -5px rgba(11, 15, 25, .10);
  --sh-4: 0 25px 50px -12px rgba(11, 15, 25, .22);
  --sh-mint: 0 10px 30px -10px rgba(0, 200, 138, .55);

  --dots: rgba(11, 15, 25, .07);   /* the mockup stage grid */
  color-scheme: light;
}

/* ---------------------------------------------------------------- ink --- */
:root[data-theme="ink"] {
  --bg:            #0b0f19;
  --bg-1:          #111726;
  --bg-2:          #1b2340;
  --bg-inset:      #080c14;
  --bg-panel:      #131a2b;

  --line:          #1f2942;
  --line-2:        #2c3757;
  --line-3:        #475069;

  --ink:           #f5f7fb;
  --ink-1:         #c3cbdb;
  --ink-2:         #8e99b3;
  --ink-3:         #6b768f;

  --accent:        #00ffa3;
  --accent-2:      #4dffc0;
  --accent-on:     #06231a;
  --accent-ink-safe: #00ffa3;
  --accent-soft:   #08301f;
  --accent-line:   #0d6b4a;

  --violet:        #a78bfa;
  --violet-soft:   #221a3d;
  --violet-line:   #4c3a86;
  --violet-on:     #14092b;

  --grad:          linear-gradient(100deg, #a78bfa 0%, #818cf8 38%, #34d399 72%, #00ffa3 100%);
  --grad-flat:     linear-gradient(100deg, #a78bfa, #00ffa3);
  --wash:          radial-gradient(900px 520px at 12% -8%, rgba(109, 40, 217, .22) 0%, transparent 62%),
                   radial-gradient(880px 560px at 92% 6%, rgba(0, 255, 163, .12) 0%, transparent 60%);

  --ok:            #34d399;  --ok-soft:   #08301f;
  --warn:          #fbbf24;  --warn-soft: #2e2410;
  --bad:           #fb7185;  --bad-soft:  #340f1b;

  --sh-1: 0 1px 2px rgba(0, 0, 0, .4);
  --sh-2: 0 4px 6px -4px rgba(0, 0, 0, .5), 0 10px 15px -3px rgba(0, 0, 0, .45);
  --sh-3: 0 8px 10px -6px rgba(0, 0, 0, .5), 0 20px 25px -5px rgba(0, 0, 0, .5);
  --sh-4: 0 25px 50px -12px rgba(0, 0, 0, .7);
  --sh-mint: 0 10px 30px -10px rgba(0, 255, 163, .35);

  --dots: rgba(255, 255, 255, .07);
  color-scheme: dark;
}

/* No stored choice: follow the system. Paper is the default above, so only
   the dark case is restated here. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --bg: #0b0f19; --bg-1: #111726; --bg-2: #1b2340; --bg-inset: #080c14; --bg-panel: #131a2b;
    --line: #1f2942; --line-2: #2c3757; --line-3: #475069;
    --ink: #f5f7fb; --ink-1: #c3cbdb; --ink-2: #8e99b3; --ink-3: #6b768f;
    --accent: #00ffa3; --accent-2: #4dffc0; --accent-on: #06231a;
    --accent-ink-safe: #00ffa3; --accent-soft: #08301f; --accent-line: #0d6b4a;
    --violet: #a78bfa; --violet-soft: #221a3d; --violet-line: #4c3a86; --violet-on: #14092b;
    --grad: linear-gradient(100deg, #a78bfa 0%, #818cf8 38%, #34d399 72%, #00ffa3 100%);
    --grad-flat: linear-gradient(100deg, #a78bfa, #00ffa3);
    --wash: radial-gradient(900px 520px at 12% -8%, rgba(109, 40, 217, .22) 0%, transparent 62%),
            radial-gradient(880px 560px at 92% 6%, rgba(0, 255, 163, .12) 0%, transparent 60%);
    --ok: #34d399; --ok-soft: #08301f; --warn: #fbbf24; --warn-soft: #2e2410;
    --bad: #fb7185; --bad-soft: #340f1b;
    --sh-1: 0 1px 2px rgba(0,0,0,.4);
    --sh-2: 0 4px 6px -4px rgba(0,0,0,.5), 0 10px 15px -3px rgba(0,0,0,.45);
    --sh-3: 0 8px 10px -6px rgba(0,0,0,.5), 0 20px 25px -5px rgba(0,0,0,.5);
    --sh-4: 0 25px 50px -12px rgba(0,0,0,.7);
    --sh-mint: 0 10px 30px -10px rgba(0, 255, 163, .35);
    --dots: rgba(255,255,255,.07);
    color-scheme: dark;
  }
}

/* ------------------------------------------------------------- system --- */
:root {
  --disp: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --body: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 1rem;
  --t-md:   clamp(1.0625rem, 0.99rem + 0.31vw, 1.1875rem);
  --t-lg:   clamp(1.25rem,  1.14rem + 0.47vw, 1.5rem);
  --t-xl:   clamp(1.5rem,   1.28rem + 0.94vw, 2rem);
  --t-2xl:  clamp(2rem,     1.56rem + 1.87vw, 3rem);
  --t-3xl:  clamp(2.5rem,   1.85rem + 2.7vw,  3.9rem);
  --t-4xl:  clamp(2.75rem,  1.8rem + 4.2vw,   5rem);

  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;  --s-7: 48px;  --s-8: 64px;
  --s-9: 96px;  --s-10: 128px; --s-11: 160px;

  --wide: 1200px;
  --text: 68ch;
  --pad:  clamp(20px, 5vw, 40px);

  /* Rounded, following unimsg.io: 8 for chips, 12 for cards, 20/28 for
     panels, and a full pill for every control. */
  /* Nothing in the design rounds by more than 10px. The scale still steps, so
     a chip and a panel are not identical, but the top of it is a soft corner
     rather than a capsule. --r-pill keeps its name because it is used in a
     hundred places and still means "the roundest thing here"; it is simply no
     longer a semicircle. */
  --r-sm:   6px;
  --r:      8px;
  --r-lg:   10px;
  --r-xl:   10px;
  --r-pill: 10px;
  --hair:   1px;

  --ease: cubic-bezier(.2, .7, .3, 1);
  --fast: 150ms;
  --slow: 480ms;

  /* Three glyphs used as backgrounds rather than masks, so they keep their
     own colour. Inlined as data URIs: a strict no-cloud site should not make
     three extra requests to draw a tick. */
  --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23059669' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
  --i-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 6 18 18M18 6 6 18'/%3E%3C/svg%3E");
  --i-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230b0f19' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15M13 6l6 6-6 6'/%3E%3C/svg%3E");
}

/* The tick and arrow are drawn in fixed colours, so the dark theme needs its
   own pair rather than a filter that would wash them out. */
:root[data-theme="ink"] {
  --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2300ffa3' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
  --i-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f5f7fb' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15M13 6l6 6-6 6'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2300ffa3' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
    --i-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f5f7fb' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15M13 6l6 6-6 6'/%3E%3C/svg%3E");
  }
}

/*
 * UniKiosk — the whole visual system, in unimsg.io's language.
 *
 * Rounded cards on a white ground, pill controls, soft layered shadows, mint
 * as the fill and violet as the second accent, Plus Jakarta Sans throughout.
 * The mockup components at the bottom (.stage, .frame, .screen, .codewin,
 * .float) are the part the landing page is built out of — unimsg.io leans on
 * exactly this device and it is why their sections read as product rather
 * than as marketing copy with a screenshot dropped in.
 */

/* ============================================================== reset === */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body {
  background: var(--bg);
  color: var(--ink-1);
  font-family: var(--body);
  font-size: var(--t-base);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* The `hidden` attribute is a user-agent `display: none`, so any class that
   sets `display` silently outranks it and script that sets `el.hidden = true`
   does nothing. That is how "Show -24 more" stayed on screen with no results.
   One rule, rather than a `[hidden]` variant beside every component. */
[hidden] { display: none !important; }

img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
a { color: inherit; }

:focus-visible { outline: 2px solid var(--violet); outline-offset: 3px; border-radius: var(--r-sm); }
::selection { background: var(--accent); color: var(--accent-on); }

/* ============================================================== type === */
h1, h2, h3, h4 {
  font-family: var(--disp);
  color: var(--ink);
  line-height: 1.12;
  letter-spacing: -.03em;
  text-wrap: balance;
}
h1 { font-size: var(--t-3xl); font-weight: 800; letter-spacing: -.04em; line-height: 1.03; }
h2 { font-size: var(--t-2xl); font-weight: 800; letter-spacing: -.035em; line-height: 1.08; }
h3 { font-size: var(--t-lg); font-weight: 700; }
h4 { font-size: var(--t-base); font-weight: 700; }
p  { text-wrap: pretty; }

/* The one gradient headline line, exactly as unimsg.io uses it: never the
   whole headline, only the clause that carries the claim. */
.grad {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* An eyebrow is a pill badge here, not a rule and a word. */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-ink-safe);
  background: var(--accent-soft);
  border: var(--hair) solid var(--accent-line);
  border-radius: var(--r-pill);
  padding: 6px 14px;
}
.eyebrow--violet { color: var(--violet); background: var(--violet-soft); border-color: var(--violet-line); }
.eyebrow--plain { background: none; border: 0; padding: 0; color: var(--ink-3); }

.lede { font-size: var(--t-md); color: var(--ink-2); max-width: 62ch; }
.mono { font-family: var(--mono); font-size: var(--t-sm); color: var(--ink-2); }
.dim  { color: var(--ink-2); }

/* ============================================================= layout === */
.wrap { width: 100%; max-width: var(--wide); margin-inline: auto; padding-inline: var(--pad); }
.section { padding-block: clamp(64px, 8vw, 112px); }
.section--tight { padding-block: clamp(40px, 5vw, 64px); }
/* A tinted band, the way unimsg.io alternates white and slate-50. */
.section--tint { background: var(--bg-1); }
.section--tint + .section--tint { padding-top: 0; }

.head { max-width: 66ch; margin-bottom: var(--s-8); display: grid; gap: var(--s-4); justify-items: start; }
.head--mid { max-width: 62ch; margin-inline: auto; text-align: center; justify-items: center; }
.head--mid .lede { margin-inline: auto; }
.head h2 + p { color: var(--ink-2); font-size: var(--t-md); }

.grid { display: grid; gap: var(--s-5); }
.g-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid > * { min-width: 0; }
@media (max-width: 900px) { .g-3, .g-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .g-2, .g-3, .g-4 { grid-template-columns: minmax(0, 1fr); } }

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}
.split--flip > :first-child { order: 2; }
@media (max-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .split--flip > :first-child { order: 0; }
}

/* ============================================================ surface === */
.cell {
  background: var(--bg-panel);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  box-shadow: var(--sh-1);
  position: relative;
}
.cell--link {
  text-decoration: none; display: block;
  transition: box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease),
              border-color var(--fast) var(--ease);
}
.cell--link:hover {
  box-shadow: var(--sh-3);
  transform: translateY(-3px);
  border-color: var(--line-2);
}
/* A colour-coded card: the rail became a tinted icon chip and a hover glow,
   because a hard top border does not belong on a 20px radius. */
.cell--rail:hover { border-color: color-mix(in srgb, var(--rail, var(--accent)) 55%, var(--line)); }

/* The icon chip unimsg.io puts at the top of every feature card.
   `flex: none` because a chip is a square: as a flex item beside a label that
   wraps to two lines it would otherwise shrink on the horizontal axis only,
   and the megamenu's icons came out as 23x34 rectangles. */
.ic {
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px;
  border-radius: var(--r);
  background: var(--accent-soft);
  border: var(--hair) solid var(--accent-line);
  margin-bottom: var(--s-4);
}
.ic::before {
  content: ""; width: 22px; height: 22px;
  background: var(--accent-ink-safe);
  -webkit-mask: var(--i) center/contain no-repeat;
          mask: var(--i) center/contain no-repeat;
}
.ic--violet { background: var(--violet-soft); border-color: var(--violet-line); }
.ic--violet::before { background: var(--violet); }
.ic--dim { background: var(--bg-2); border-color: var(--line); }
.ic--dim::before { background: var(--ink-3); }
.ic--sm { width: 34px; height: 34px; border-radius: var(--r-sm); margin-bottom: 0; }
.ic--sm::before { width: 17px; height: 17px; }
/* Rail-coloured chip, so an industry grid reads as forty-four things. */
.cell--rail .ic {
  background: color-mix(in srgb, var(--rail, var(--accent)) 14%, var(--bg-panel));
  border-color: color-mix(in srgb, var(--rail, var(--accent)) 34%, var(--line));
}
.cell--rail .ic::before { background: var(--rail, var(--accent-ink-safe)); }

/* =========================================================== controls === */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bd: var(--line-2);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  font-family: var(--disp); font-weight: 700; font-size: .95rem; letter-spacing: -.01em;
  text-decoration: none;
  padding: 13px 26px;
  border-radius: var(--r-pill);
  background: var(--btn-bg); color: var(--btn-fg);
  border: var(--hair) solid var(--btn-bd);
  cursor: pointer; white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              border-color var(--fast) var(--ease), transform var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn--go {
  --btn-bg: var(--accent); --btn-fg: var(--accent-on); --btn-bd: var(--accent);
  font-weight: 800; box-shadow: var(--sh-mint);
}
.btn--go:hover { --btn-bg: var(--accent-2); --btn-bd: var(--accent-2); }
.btn--violet { --btn-bg: var(--violet); --btn-fg: var(--violet-on); --btn-bd: var(--violet); font-weight: 800; }
.btn--ghost { --btn-bg: var(--bg-panel); box-shadow: var(--sh-1); }
.btn--ghost:hover { --btn-bd: var(--line-3); box-shadow: var(--sh-2); }
.btn--sm { padding: 9px 18px; font-size: .875rem; }
.btn--lg { padding: 17px 34px; font-size: 1.0625rem; }
/* The circular arrow chip on unimsg.io's secondary CTA. */
.btn--arrow::before {
  content: ""; width: 26px; height: 26px; flex: none; border-radius: var(--r-pill);
  background: var(--bg-2) var(--i-arrow) center/13px no-repeat;
}

.tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em;
  padding: 4px 12px; border-radius: var(--r-pill);
  border: var(--hair) solid var(--line); background: var(--bg-2); color: var(--ink-2);
}
.tag--go { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent-ink-safe); }
.tag--soon { border-color: var(--violet-line); background: var(--violet-soft); color: var(--violet); }
.is-soon { opacity: .78; }

/* ============================================================== nav ==== */
/* unimsg.io floats a rounded bar with a blur over the page. */
.nav { position: sticky; top: 0; z-index: 60; padding-top: 10px; background: transparent; }
.nav__in {
  max-width: var(--wide); margin-inline: auto;
  padding: 8px 10px 8px 18px;
  display: flex; align-items: center; gap: var(--s-4);
  background: color-mix(in srgb, var(--bg-panel) 78%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
          backdrop-filter: blur(18px) saturate(1.6);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-pill);
  box-shadow: var(--sh-2);
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
/* Once the page has moved under it the bar has to hold its own against
   whatever is scrolling past, so it firms up rather than staying a ghost. */
.nav.is-stuck .nav__in {
  background: color-mix(in srgb, var(--bg-panel) 94%, transparent);
  border-color: var(--line-2);
  box-shadow: var(--sh-3);
}
@media (min-width: 1281px) { .nav__in { margin-inline: auto; } }
@media (max-width: 1280px) { .nav { padding-inline: var(--pad); } }

.brand {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--disp); font-weight: 800; font-size: 1.12rem;
  letter-spacing: -.035em; color: var(--ink); text-decoration: none; white-space: nowrap;
}
.brand img { width: 26px; height: 26px; }
/* Tight: the caret sits against its label, so the space between two items has
   to read as bigger than the space inside one. */
.nav__links { display: flex; align-items: center; gap: 2px; margin-left: auto; }
/* Direct children and the megamenu's own top link only. A bare
   `.nav__links a` also reaches every link inside an open panel, where it wins
   on specificity over .btn and .mega__promo and turns a promo card into a
   pill and a mint button into unreadable grey-on-mint. */
.nav__links > a, .nav__top {
  font-size: .92rem; font-weight: 600; color: var(--ink-2); text-decoration: none;
  padding: 8px 10px; border-radius: var(--r-pill);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.nav__item + .nav__item { margin-left: 2px; }
.nav__item > .nav__top { padding-right: 4px; }
.nav__links > a:hover, .nav__top:hover { color: var(--ink); background: var(--bg-2); }
.nav__links > a[aria-current="page"], .nav__top[aria-current="page"] { color: var(--ink); background: var(--bg-2); }
.nav__act { display: flex; align-items: center; gap: var(--s-2); }

.iconbtn {
  width: 38px; height: 38px; display: grid; place-items: center; flex: none;
  background: var(--bg-2); border: var(--hair) solid var(--line);
  border-radius: var(--r-pill); color: var(--ink-2); cursor: pointer;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.iconbtn:hover { color: var(--ink); border-color: var(--line-3); }
.iconbtn svg { width: 17px; height: 17px; }

.burger { display: none; }
@media (max-width: 1040px) {
  .nav__in { gap: var(--s-3); position: relative; }
  .nav__links {
    position: absolute; top: calc(100% + 10px); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--bg-panel); border: var(--hair) solid var(--line);
    border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s-2);
    display: none;
  }
  .nav__links.open { display: flex; }
  .nav__links > a, .nav__top { padding: 12px 14px; border-radius: var(--r); }
  .burger { display: grid; order: 9; }
  .nav__act { margin-left: auto; }
  .nav__act .btn--ghost { display: none; }
  /* The burger is the primary navigation control on a phone; 38px is under
     the 44px touch minimum and it was the smallest target on the page. */
  .iconbtn { width: 44px; height: 44px; }
}
@media (max-width: 560px) {
  .nav__in { padding-left: var(--s-4); }
  .nav__act .btn { padding: 11px 14px; font-size: .8rem; }
}
/* At 390px the bar needs 354px inside a 350px pill, and "UniKiosk" — which is
   nowrap and cannot shrink — ends up printing underneath the search button.
   The search icon is the one thing here that is also a row in the burger
   menu, so it is what gives way rather than the download CTA. */
@media (max-width: 420px) {
  .nav__act > .iconbtn[href="/search"] { display: none; }
}

/* ============================================================== hero === */
.hero { position: relative; padding-block: clamp(56px, 7vw, 96px) clamp(40px, 5vw, 72px); }
/* The lavender-to-mint wash unimsg.io lays behind the fold. */
.hero::before {
  content: ""; position: absolute; inset: -120px 0 auto; height: 880px; z-index: -2;
  background: var(--wash);
}
/* A faint ruled ground — the same grid the product draws layouts on. */
.hero::after {
  content: ""; position: absolute; inset: 0 0 auto; height: 760px; z-index: -1;
  background-image:
    linear-gradient(to right, var(--dots) 1px, transparent 1px),
    linear-gradient(to bottom, var(--dots) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 70% 62% at 50% 12%, #000 4%, transparent 72%);
          mask-image: radial-gradient(ellipse 70% 62% at 50% 12%, #000 4%, transparent 72%);
}
.hero--mid { text-align: center; }
.hero--mid .lede { margin-inline: auto; }
.hero--mid .hero__cta, .hero--mid .claims { justify-content: center; }
.hero h1 { margin-block: var(--s-5) var(--s-5); }
.hero--mid h1 { font-size: var(--t-4xl); max-width: 17ch; margin-inline: auto; }
.hero .lede { font-size: var(--t-lg); max-width: 56ch; }
.hero__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-7); }

/* Claim chips, doing the job review badges do for the incumbents — but
   proving our own differentiators rather than borrowing someone's stars. */
.claims { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-7); }
.claim {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 18px 9px 10px;
  background: var(--bg-panel); border: var(--hair) solid var(--line);
  border-radius: var(--r-pill); box-shadow: var(--sh-1);
}
.claim b { font-size: .875rem; font-weight: 700; color: var(--ink); }

/* ============================================================= strip === */
.strip {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  background: var(--bg-panel); border: var(--hair) solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--sh-2); overflow: hidden;
}
.strip > div { padding: var(--s-6) var(--s-5); text-align: center; border-right: var(--hair) solid var(--line); }
.strip > div:last-child { border-right: 0; }
.strip b {
  display: block; font-family: var(--disp); font-weight: 800;
  font-size: var(--t-xl); letter-spacing: -.04em; color: var(--ink); line-height: 1;
}
.strip span { display: block; margin-top: 8px; font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); }
@media (max-width: 700px) {
  .strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .strip > div:nth-child(2n) { border-right: 0; }
  .strip > div:nth-child(-n+2) { border-bottom: var(--hair) solid var(--line); }
}

/* ====================================================== mockup chrome === */
/* A stage: the rounded, grid-ruled panel unimsg.io floats its product
   diagrams on. Everything below is meant to sit inside one. */
.stage {
  position: relative;
  border-radius: var(--r-xl);
  border: var(--hair) solid var(--line);
  background:
    linear-gradient(to right, var(--dots) 1px, transparent 1px) 0 0 / 34px 34px,
    linear-gradient(to bottom, var(--dots) 1px, transparent 1px) 0 0 / 34px 34px,
    var(--bg-1);
  padding: clamp(24px, 4vw, 56px);
  overflow: hidden;
  box-shadow: var(--sh-1);
}
.stage::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 0%, transparent 55%, var(--bg-1) 100%);
}
.stage > * { position: relative; z-index: 1; }
.stage--tall { padding-block: clamp(32px, 5vw, 72px); }

/* A browser window: traffic lights, an address pill, then the shot. */
.frame {
  border-radius: var(--r-lg);
  border: var(--hair) solid var(--line-2);
  background: var(--bg-panel);
  box-shadow: var(--sh-4);
  overflow: hidden;
}
.frame__bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 10px 14px;
  background: var(--bg-2);
  border-bottom: var(--hair) solid var(--line);
}
.frame__dots { display: flex; gap: 6px; flex: none; }
.frame__dots i { width: 10px; height: 10px; border-radius: var(--r-pill); background: var(--line-3); }
.frame__dots i:nth-child(1) { background: #ff5f57; }
.frame__dots i:nth-child(2) { background: #febc2e; }
.frame__dots i:nth-child(3) { background: #28c840; }
.frame__url {
  flex: 1; min-width: 0;
  font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3);
  background: var(--bg-panel); border: var(--hair) solid var(--line);
  border-radius: var(--r-pill); padding: 5px 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.frame > img { width: 100%; display: block; }

/* A screen: a physical panel, bezel and all. Landscape by default. */
.screen {
  position: relative;
  border-radius: 10px;
  padding: 10px;
  background: linear-gradient(160deg, #2b3245, #12161f);
  box-shadow: var(--sh-4), inset 0 0 0 1px rgba(255, 255, 255, .07);
}
.screen > img { width: 100%; border-radius: 5px; display: block; background: #000; }
.screen--tall { border-radius: 10px; padding: 9px; }
.screen--pad  { border-radius: 10px; padding: 12px; }
/* The stand under a totem, so it reads as hardware and not a floating card. */
.screen--stand::after {
  content: ""; position: absolute; left: 50%; top: 100%; translate: -50% 0;
  width: 34%; height: 10px; border-radius: 0 0 6px 6px;
  background: linear-gradient(180deg, #232a3a, #151a25);
}

/* A little floating card, the way unimsg.io scatters labelled chips around a
   diagram. Positioned by the page with inline top/left. */
.float {
  position: absolute;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px 10px 10px;
  background: var(--bg-panel);
  border: var(--hair) solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh-3);
  white-space: nowrap;
}
.float small { display: block; font-size: 11px; font-weight: 600; color: var(--ink-3); line-height: 1.3; }
.float b { display: block; font-size: 13px; font-weight: 700; color: var(--ink); line-height: 1.3; }
/* The floating chips, and the totem the home stage hangs off its own right
   edge: below 900px the stage no longer has the slack those negative offsets
   assume, and `overflow: hidden` cuts the bezel and its stand off. */
@media (max-width: 900px) { .float, .stage .screen--stand { display: none; } }

/* A code window. unimsg.io uses one for its API section; ours holds the
   layout JSON, which is the thing a visitor actually downloads. */
.codewin {
  border-radius: var(--r-lg); overflow: hidden;
  border: var(--hair) solid #263049;
  background: #0d1220;
  box-shadow: var(--sh-4);
}
.codewin .frame__bar { background: #151c2e; border-bottom-color: #263049; }
.codewin .frame__url { background: #0d1220; border-color: #263049; color: #7c88a6; }
.codewin pre {
  margin: 0; padding: var(--s-5);
  font-family: var(--mono); font-size: 13px; line-height: 1.75;
  color: #c8d3ea; overflow-x: auto;
}
.codewin .k { color: #a78bfa; }   /* key      */
.codewin .s { color: #00ffa3; }   /* string   */
.codewin .n { color: #7dd3fc; }   /* number   */
.codewin .c { color: #5b6784; }   /* comment  */

/* ============================================================== shot === */
figure.shot {
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-panel);
  box-shadow: var(--sh-2);
  overflow: hidden;
}
figure.shot img { width: 100%; }
figure.shot figcaption {
  padding: var(--s-3) var(--s-5);
  border-top: var(--hair) solid var(--line);
  font-size: var(--t-sm); color: var(--ink-2);
}

/* ============================================================= lists === */
/* A ticked list, the shape unimsg.io uses for its benefit bullets. */
.ticks { list-style: none; padding: 0; display: grid; gap: var(--s-3); }
.ticks li { display: flex; gap: var(--s-3); align-items: flex-start; color: var(--ink-2); }
.ticks li::before {
  content: ""; flex: none; width: 20px; height: 20px; margin-top: 3px;
  border-radius: var(--r-pill);
  background: var(--accent-soft) var(--i-check) center/11px no-repeat;
  border: var(--hair) solid var(--accent-line);
}
.ticks--no li::before { background: var(--bg-2) var(--i-cross) center/10px no-repeat; border-color: var(--line); }

/* ============================================================= steps === */
.steps { counter-reset: n; }
.step { position: relative; padding-top: var(--s-7); }
.step::before {
  counter-increment: n; content: counter(n);
  position: absolute; left: 0; top: 0;
  width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-on);
  font-weight: 800; font-size: .95rem;
  box-shadow: var(--sh-mint);
}

/* ============================================================= table === */
.tablewrap {
  overflow-x: auto;
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-panel);
  box-shadow: var(--sh-1);
}
table { border-collapse: collapse; width: 100%; min-width: 760px; font-size: .92rem; }
th, td { padding: var(--s-4) var(--s-5); text-align: left; border-bottom: var(--hair) solid var(--line); }
thead th {
  position: sticky; top: 0;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); background: var(--bg-1);
}
tbody th { font-weight: 600; color: var(--ink); }
td { color: var(--ink-1); text-align: center; }
thead th { text-align: center; }
thead th:first-child, tbody th { text-align: left; }
tbody tr:last-child > * { border-bottom: 0; }
.col-us { background: var(--accent-soft); color: var(--ink); }
thead .col-us { background: var(--accent); color: var(--accent-on); }
.y { color: var(--ok); font-weight: 700; }
.n { color: var(--ink-3); }
.p { color: var(--warn); font-weight: 600; }

/* ============================================================== note === */
.note {
  border: var(--hair) solid var(--accent-line);
  border-left: 4px solid var(--accent-ink-safe);
  background: var(--accent-soft);
  border-radius: var(--r);
  padding: var(--s-4) var(--s-5);
  color: var(--ink-1);
}
.note--warn { border-color: var(--warn); border-left-color: var(--warn); background: var(--warn-soft); }
.note--bad  { border-color: var(--bad);  border-left-color: var(--bad);  background: var(--bad-soft); }
.note > :last-child { margin-bottom: 0; }

/* ============================================================== band === */
.band {
  position: relative; overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--bg-1);
  border: var(--hair) solid var(--line);
  padding: clamp(48px, 7vw, 88px) var(--s-6);
  text-align: center;
  box-shadow: var(--sh-2);
}
.band::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: var(--wash);
}
.band > * { position: relative; z-index: 1; }
.band .lede { margin-inline: auto; }
.band .hero__cta { justify-content: center; }

/* ============================================================ footer === */
.foot { border-top: var(--hair) solid var(--line); padding-block: var(--s-8) var(--s-6); background: var(--bg-1); }
.foot__grid { display: grid; grid-template-columns: 1.6fr repeat(4, 1fr); gap: var(--s-6); }
.foot h4 { font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); margin-bottom: var(--s-3); }
.foot a { display: block; padding: 10px 0; font-size: .9rem; color: var(--ink-2); text-decoration: none; }
.foot a:hover { color: var(--accent-ink-safe); }
.foot__legal {
  margin-top: var(--s-8); padding-top: var(--s-5);
  border-top: var(--hair) solid var(--line);
  display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: space-between;
  font-size: var(--t-sm); color: var(--ink-3);
}
@media (max-width: 900px) { .foot__grid { grid-template-columns: repeat(2, 1fr); } }

/* ============================================================ motion === */
.rise { opacity: 0; transform: translateY(16px); }
.rise.in { opacity: 1; transform: none; transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease); }
@media (prefers-reduced-motion: reduce) { .rise { opacity: 1; transform: none; } }

/* ============================================================== misc === */
.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip {
  position: absolute; left: var(--s-4); top: -60px; z-index: 100;
  background: var(--accent); color: var(--accent-on);
  padding: 10px 18px; border-radius: var(--r-pill); font-weight: 700;
  transition: top var(--fast) var(--ease);
}
.skip:focus { top: var(--s-4); }
.linkish { background: none; border: 0; padding: 0; color: var(--accent-ink-safe); cursor: pointer; text-decoration: underline; font: inherit; }

/* ============================================================ gallery === */
.filters {
  display: grid; gap: var(--s-4);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--bg-panel) 88%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  box-shadow: var(--sh-2);
  padding: var(--s-4);
  position: sticky; top: 76px; z-index: 40;
}
.chips { display: flex; gap: var(--s-2); overflow-x: auto; scrollbar-width: thin; padding-bottom: 2px; }
.chip {
  flex: none; cursor: pointer; white-space: nowrap;
  padding: 10px 16px; border-radius: var(--r-pill);
  font-size: .84rem; font-weight: 600;
  color: var(--ink-2); background: var(--bg-2); border: var(--hair) solid var(--line);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.chip:hover { color: var(--ink); border-color: var(--line-3); }
.chip[aria-pressed="true"] { background: var(--accent); color: var(--accent-on); border-color: var(--accent); font-weight: 700; }
.filters__row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }
.field {
  display: flex; align-items: center; gap: var(--s-2);
  border: var(--hair) solid var(--line); background: var(--bg-2);
  border-radius: var(--r-pill); padding: 0 var(--s-4);
}
.field > label { font-size: var(--t-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
.field input, .field select { background: none; border: 0; padding: 10px 2px; font-size: .9rem; color: var(--ink); min-width: 0; }
.field input { width: 210px; }
.filters__count { margin-left: auto; font-size: .85rem; font-weight: 700; color: var(--accent-ink-safe); }
@media (max-width: 620px) {
  .filters { position: static; }
  .field { flex: 1 1 100%; }
  .field input { width: 100%; }
  .filters__count { margin-left: 0; }
}

.tpls { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); margin-top: var(--s-6); }
@media (max-width: 980px) { .tpls { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .tpls { grid-template-columns: minmax(0, 1fr); } }

.tpl {
  border: var(--hair) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-panel);
  box-shadow: var(--sh-1);
  display: flex; flex-direction: column; overflow: hidden;
  transition: box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.tpl:hover { box-shadow: var(--sh-3); transform: translateY(-3px); }
/* A stage, not a thumbnail. Every board is shown on the hardware it is drawn
   for — a 16:9 layout on a wall panel, a 9:16 one on a totem, a square one on
   a tablet — so a portrait template is not letterboxed into a landscape box
   with two grey bars where the point of it used to be.
   The bottom padding is where the stand goes; the bezel overflows into it. */
.tpl__shot {
  position: relative;
  background: var(--bg-inset); border-bottom: var(--hair) solid var(--line);
  aspect-ratio: 16 / 10;
}
/* Absolutely positioned, so its height is a definite number rather than
   whatever its contents ask for. A portrait device sized with `height: 100%`
   inside an auto-sized box resolves to auto and grows a 9:16 board to twice
   the height of the card; from a fixed inset it resolves to the stage.
   The bottom inset is where the stand goes — the bezel overflows into it. */
/* Flex, not grid: a grid item's `height: 100%` resolves against its track,
   and an auto track is sized by the very content being constrained, so it
   comes out as auto and a 9:16 board grows to 537px inside a 169px stage. A
   flex item's percentage resolves against the container, which the insets
   have already made definite. */
.mock {
  position: absolute; inset: var(--s-5) var(--s-5) 28px;
  display: flex; align-items: center; justify-content: center;
}
.mock .screen { padding: 6px; border-radius: 10px; max-width: 100%; max-height: 100%; min-width: 0; }
.mock .screen > img { border-radius: 4px; display: block; }
.mock .screen--stand::after { height: 8px; }

/* Every orientation is driven by the height of the stage, including the wall
   panel. Sizing a 16:9 board by width instead looks right until you do the
   arithmetic: the stage is 16:10, so a full-width board is taller than the
   box that holds it, `max-height` clamps the bezel but not the picture inside
   it, and the board hangs 9px out of the bottom of its own frame. Driving
   from the height means the bezel always contains the board, whatever the
   card is doing. */
.mock .screen { height: 100%; }
.mock .screen > img { height: 100%; width: auto; object-fit: cover; }
.mock--landscape .screen > img { aspect-ratio: 16 / 9; }
.mock--portrait .screen > img { aspect-ratio: 9 / 16; }
.mock--square .screen { padding: 8px; border-radius: 10px; }
.mock--square .screen > img { aspect-ratio: 1; }
.tpl__body { padding: var(--s-5) var(--s-5) var(--s-3); flex: 1; }
.tpl__meta { margin-top: 6px; font-size: .84rem; color: var(--ink-2); }
.tpl__w { margin-top: var(--s-3); display: flex; flex-wrap: wrap; gap: 6px; }
.tpl__w span {
  font-size: 11px; font-weight: 600; color: var(--ink-2);
  background: var(--bg-2); border: var(--hair) solid var(--line);
  border-radius: var(--r-pill); padding: 2px 9px;
}
.tpl__act { display: flex; gap: var(--s-2); padding: 0 var(--s-5) var(--s-5); }
.tpl__act .btn { flex: 1; padding-inline: 10px; }

.more { display: flex; justify-content: center; margin-top: var(--s-7); }

/* ============================================================ prose === */
.doc { display: grid; grid-template-columns: 230px minmax(0, 1fr) 210px; gap: clamp(24px, 4vw, 56px); align-items: start; }
/* A post has no left sidebar, only the contents rail. Declared as a class and
   not as an inline style on the element: an inline declaration outranks every
   media query below it, and the two-column track survived down to 390px, where
   it left the article 126px wide beside a 200px column holding a rail that had
   already been display:none since 1080px. */
.doc--post { grid-template-columns: minmax(0, 1fr) 200px; }
@media (max-width: 1080px) {
  .doc { grid-template-columns: 230px minmax(0, 1fr); }
  .doc--post { grid-template-columns: minmax(0, 1fr); }
  .doc__toc { display: none; }
}
@media (max-width: 820px)  {
  .doc { grid-template-columns: minmax(0, 1fr); }
  .doc__side { position: static; max-height: none; }
  /* Stacked, the index would otherwise push the article a screen and a half
     down the page on every doc. */
  .doc__side { order: 2; margin-top: var(--s-7); }
  .doc > div, .doc > article { order: 1; }
}

.doc__side, .doc__toc { position: sticky; top: 100px; align-self: start; max-height: calc(100vh - 130px); overflow-y: auto; }
.doc__side {
  background: var(--bg-1); border: var(--hair) solid var(--line);
  border-radius: var(--r-lg); padding: var(--s-5);
}
.doc__side h4, .doc__toc h4 {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-3); margin: var(--s-5) 0 var(--s-2);
}
.doc__side h4:first-child, .doc__toc h4:first-child { margin-top: 0; }
.doc__side a, .doc__toc a {
  display: block; padding: 7px 12px; font-size: .9rem; font-weight: 500;
  color: var(--ink-2); text-decoration: none; border-radius: var(--r-sm);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.doc__side a:hover, .doc__toc a:hover { color: var(--ink); background: var(--bg-2); }
.doc__side a[aria-current], .doc__toc a[aria-current] {
  color: var(--accent-ink-safe); background: var(--accent-soft); font-weight: 700;
}
.doc__toc a { padding-left: 12px; border-left: 2px solid var(--line); border-radius: 0; }
.doc__toc a.lvl3 { padding-left: 24px; font-size: .84rem; }
.doc__toc a[aria-current] { border-left-color: var(--accent-ink-safe); background: none; }

.prose { max-width: var(--text); font-size: var(--t-md); }
.prose > * + * { margin-top: var(--s-5); }
.prose h2 { font-size: var(--t-xl); margin-top: var(--s-8); scroll-margin-top: 110px; }
.prose h3 { font-size: var(--t-lg); margin-top: var(--s-7); scroll-margin-top: 110px; }
.prose h2 + p, .prose h3 + p { margin-top: var(--s-4); }
.prose a { color: var(--accent-ink-safe); font-weight: 600; text-underline-offset: 3px; }
.prose ul, .prose ol { padding-left: 1.35em; display: grid; gap: var(--s-2); }
.prose li::marker { color: var(--accent-ink-safe); font-weight: 700; }
.prose code {
  font-family: var(--mono); font-size: .86em;
  background: var(--bg-2); border: var(--hair) solid var(--line);
  border-radius: var(--r-sm); padding: 2px 6px;
}
.prose pre {
  font-family: var(--mono); font-size: var(--t-sm); line-height: 1.7;
  background: #0d1220; color: #c8d3ea; border: var(--hair) solid #263049;
  border-radius: var(--r); padding: var(--s-5); overflow-x: auto;
}
.prose pre code { background: none; border: 0; padding: 0; font-size: inherit; color: inherit; }
.prose blockquote { border-left: 4px solid var(--violet); background: var(--violet-soft); border-radius: var(--r); padding: var(--s-4) var(--s-5); color: var(--ink-1); }
.prose td, .prose thead th { text-align: left; }
.prose hr { border: 0; border-top: var(--hair) solid var(--line); margin-block: var(--s-7); }

.crumbs {
  display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center;
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); margin-bottom: var(--s-5);
}
.crumbs a { color: var(--ink-2); text-decoration: none; }
.crumbs a:hover { color: var(--accent-ink-safe); }
.crumbs span::before { content: "/"; margin-right: var(--s-2); color: var(--line-3); }

.meta { display: flex; flex-wrap: wrap; gap: var(--s-4); font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); }

.pager { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); margin-top: var(--s-8); }
@media (max-width: 620px) { .pager { grid-template-columns: minmax(0, 1fr); } }
.pager a { text-decoration: none; font-weight: 700; color: var(--ink); }
.pager a:last-child { text-align: right; }
.pager small { display: block; font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px; }

/* ========================================================== preview === */
/*
 * A board shown on the fitting it would really be installed in. The size is
 * driven by the image with viewport-unit caps rather than percentage heights:
 * a percentage chain through a shrink-wrapping bezel has no definite ancestor
 * to resolve against, which is the bug that had the gallery boards hanging out
 * of their own frames.
 */
.pv {
  /* Restated because the reset's `* { margin: 0 }` beats the user-agent's
     `dialog { margin: auto }`, and a modal with inset:0 and no auto margin
     pins itself to the top-left corner instead of centring. */
  margin: auto;
  width: min(1040px, calc(100vw - 2 * var(--s-4)));
  max-width: none; max-height: calc(100dvh - 2 * var(--s-4));
  padding: 0; border: var(--hair) solid var(--line);
  border-radius: var(--r-xl); background: var(--bg-panel); color: var(--ink-1);
  box-shadow: var(--sh-4);
  overflow: hidden;
}
.pv::backdrop { background: rgb(4 7 14 / .62); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.pv[open] { display: flex; flex-direction: column; }

.pv__head {
  display: flex; align-items: flex-start; gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  border-bottom: var(--hair) solid var(--line);
}
.pv__head > div { min-width: 0; }
.pv__head .iconbtn { margin-left: auto; }

.pv__stage {
  position: relative;
  flex: 1; min-height: 0;
  background: var(--bg-inset);
  /* The faint ruled ground the product draws layouts on, so the board reads as
     mounted in a space rather than floating on a panel. */
  background-image:
    linear-gradient(to right, var(--dots) 1px, transparent 1px),
    linear-gradient(to bottom, var(--dots) 1px, transparent 1px);
  background-size: 32px 32px;
}

/* ---------------------------------------------------------- carousel -- */
/* Scroll-snap rather than a transform track: the swipe, the momentum, the
   snap and the keyboard are all the platform's, the buttons only call
   scrollTo, and with script off it is still a scrollable row of all three
   fittings rather than one fitting and two that never appear. */
.pv__rail {
  height: 100%;
  display: flex;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.pv__rail::-webkit-scrollbar { display: none; }
.pv__rail:focus-visible { outline-offset: -3px; }

/* Each slide is narrower than the rail, and the rail is padded by exactly the
   remainder, so a centre-snapped slide sits in the middle with the fittings
   either side of it showing at the edges. That peek is the whole point: you
   can see at a glance that the board has been put in three places, instead of
   having to discover it from a row of chips. */
/* `flex-basis: 100%` with the rail padded by 12% either side, NOT `76%`: a
   percentage basis resolves against the flex container's *content* box, so
   `76%` measured 76% of the already-padded 76% and every board overflowed its
   own slide into the neighbour. 100% of the content box is exactly the 76%
   wanted, and the padding is what lets the first and last slides centre. */
.pv__rail { padding-inline: 12%; }
.pv__slide {
  flex: 0 0 100%; scroll-snap-align: center;
  display: grid; grid-template-rows: 1fr auto; justify-items: center; align-items: center;
  gap: var(--s-4);
  padding: var(--s-7) var(--s-5) var(--s-5);
  min-width: 0;
  /* The two you are not looking at recede rather than compete. */
  opacity: .38;
  transition: opacity var(--fast) var(--ease);
}
.pv__slide.is-on { opacity: 1; }
.pv__slide figcaption {
  font-family: var(--mono); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
}
.pv__slide.is-on figcaption { color: var(--accent-ink-safe); }

.pv__arrow {
  position: absolute; top: 50%; translate: 0 -50%; z-index: 2;
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--r-pill); cursor: pointer;
  background: color-mix(in srgb, var(--bg-panel) 88%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: var(--hair) solid var(--line-2); color: var(--ink-2);
  box-shadow: var(--sh-2);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease),
              opacity var(--fast) var(--ease);
}
.pv__arrow:hover { color: var(--ink); border-color: var(--line-3); }
.pv__arrow svg { width: 18px; height: 18px; }
.pv__arrow--prev { left: var(--s-4); }
.pv__arrow--next { right: var(--s-4); }
/* Disabled rather than removed: a control that vanishes at the ends makes the
   row jump, and `hidden` here would fight the global [hidden] rule. */
.pv__arrow[disabled] { opacity: .3; cursor: default; box-shadow: none; }

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

.pv__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4);
  padding: var(--s-4) var(--s-6);
  border-top: var(--hair) solid var(--line); background: var(--bg-1);
}
/* Wraps rather than scrolls: there are five mounts and in a modal they should
   all be visible, not two of them parked off the right edge. */
.pv__mounts { flex: 1 1 320px; flex-wrap: wrap; overflow: visible; }
.pv__act { display: flex; gap: var(--s-2); margin-left: auto; }

/* ------------------------------------------------------------ mounts -- */
/* One screen, four fittings. The mount is independent of the board's shape —
   a portrait board on a wall bracket is an ordinary install. */
.dev { display: flex; flex-direction: column; align-items: center; }
.dev__body {
  display: block; position: relative;
  padding: var(--bz, 12px);
  border-radius: var(--rad, 8px);
  background: linear-gradient(160deg, #2b3245, #12161f);
  box-shadow: var(--sh-4), inset 0 0 0 1px rgb(255 255 255 / .07);
}
.dev__body img {
  display: block; width: auto; height: auto;
  /* --side is whatever is fitted alongside the screen and therefore competes
     for the slide's width. Only the lift has any: without subtracting its
     operating panel the board keeps its full cap and the whole fitting spills
     out of its slide into the neighbouring mockup. */
  max-width: calc(min(58vw, 600px) - var(--side, 0px));
  max-height: min(52dvh, 520px);
  border-radius: 4px; background: #000;
}
.dev__base { display: none; }

/* The hardware around the screen is sized in pixels, not percentages: a stand
   is the same casting whatever is bolted to it, and a percentage neck grows
   into a plank behind a wide landscape board. */
.dev__top { display: none; }

/* Standing kiosk: a slim column on a weighted plinth, the free-standing totem
   you walk past in a foyer. */
.dev--kiosk { --bz: 14px; --rad: 10px; }
.dev--kiosk .dev__base { display: block; position: relative; width: 100%; height: 56px; }
.dev--kiosk .dev__base::before {
  content: ""; position: absolute; left: 50%; top: 0; translate: -50% 0;
  width: 46px; height: 42px;
  background: linear-gradient(180deg, #232a3a, #1a2030);
}
.dev--kiosk .dev__base::after {
  content: ""; position: absolute; left: 50%; bottom: 0; translate: -50% 0;
  width: 190px; max-width: 100%; height: 14px; border-radius: 4px;
  background: linear-gradient(180deg, #2b3245, #141924);
  box-shadow: 0 12px 24px rgb(4 7 14 / .35);
}

/* On the wall: no foot at all — it is on a bracket — and a deep offset shadow
   so it reads as standing proud of the wall rather than lying on it. */
.dev--wall { --bz: 10px; --rad: 10px; }
.dev--wall .dev__body {
  box-shadow: 0 26px 50px -12px rgb(4 7 14 / .45), 0 8px 16px rgb(4 7 14 / .2),
              inset 0 0 0 1px rgb(255 255 255 / .07);
}

/* Hung from a ceiling: the mall and concourse install — two drop rods from a
   ceiling track down to a screen at head height. The rods are drawn above the
   screen, so this is the one fitting whose hardware is in .dev__top rather
   than .dev__base. */
.dev--ceiling { --bz: 12px; --rad: 8px; }
.dev--ceiling .dev__top {
  display: block; position: relative; width: 100%; height: 58px;
}
/* The track it is suspended from, running off both edges the way a real one
   runs the length of the concourse. */
.dev--ceiling .dev__top::before {
  content: ""; position: absolute; left: 50%; top: 0; translate: -50% 0;
  width: calc(100% + 72px); max-width: 92vw; height: 9px;
  border-radius: 2px;
  background: linear-gradient(180deg, #cfd6e0, #97a1af 60%, #6d7684);
  box-shadow: 0 3px 8px rgb(4 7 14 / .28);
}
/* Two rods, inset from the edges, hanging the screen off the track. */
.dev--ceiling .dev__top::after {
  content: ""; position: absolute; left: 50%; top: 9px; translate: -50% 0;
  width: 62%; height: 49px;
  background:
    linear-gradient(180deg, #b9c1cd, #7c8593) left  top / 5px 100% no-repeat,
    linear-gradient(180deg, #b9c1cd, #7c8593) right top / 5px 100% no-repeat;
}
/* Nothing under it — it is in mid-air — so the shadow does the work of saying
   the floor is a long way down. */
.dev--ceiling .dev__body {
  box-shadow: 0 34px 54px -18px rgb(4 7 14 / .5), 0 10px 20px rgb(4 7 14 / .22),
              inset 0 0 0 1px rgb(255 255 255 / .07);
}

/* In a lift: the screen recessed into the car's brushed-steel panelling, with
   the car operating panel — floor indicator and call buttons — beside it and
   the handrail under both. The steel is a repeating gradient rather than an
   image, so it costs nothing and scales to any board.
   Laid out as a grid so the panel is a sibling of the screen rather than
   something absolutely positioned over it; it then cannot land on top of the
   board when the board is wide. */
.dev--lift {
  --bz: 26px; --rad: 6px;
  --side: 84px;              /* the panel, plus the gap to it */
  display: grid; align-items: stretch;
  grid-template-columns: auto auto;
  grid-template-areas: "top top" "body cop" "base base";
  column-gap: 14px;
}
.dev--lift .dev__top { grid-area: top; }
.dev--lift .dev__body { grid-area: body; }
.dev--lift .cop { grid-area: cop; }
.dev--lift .dev__base { grid-area: base; }

/* ---- the car operating panel ---- */
.cop {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  padding: 12px 9px;
  border-radius: 5px;
  background:
    linear-gradient(90deg, rgb(255 255 255 / .06) 1px, transparent 1px) 0 0 / 3px 100%,
    linear-gradient(165deg, #aab3c1, #78818f 46%, #99a2b0 62%, #6b7481);
  box-shadow: 0 12px 26px -10px rgb(4 7 14 / .45), inset 0 1px 0 rgb(255 255 255 / .4);
}
/* The floor readout: a dot-matrix number and the direction arrow beside it. */
.cop__floor {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  width: 100%; padding: 5px 6px;
  border-radius: 3px; background: #0a0d14;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .7), inset 0 2px 5px rgb(0 0 0 / .6);
}
.cop__floor b {
  font-family: var(--mono); font-size: 15px; font-weight: 700; line-height: 1;
  color: #ff9b3d; text-shadow: 0 0 7px rgb(255 155 61 / .75);
}
/* The up arrow, drawn rather than typed so it needs no glyph. */
.cop__floor i {
  width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-bottom: 7px solid #ff9b3d;
  filter: drop-shadow(0 0 4px rgb(255 155 61 / .7));
}
.cop__btns { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.cop__btn {
  width: 22px; height: 22px; display: grid; place-items: center;
  border-radius: var(--r-pill);
  font-family: var(--mono); font-size: 9px; font-weight: 700; color: #2b3140;
  background: radial-gradient(circle at 34% 30%, #f2f5f9, #c3cad4 70%, #98a1ae);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .5), 0 1px 2px rgb(4 7 14 / .4);
}
/* The floor the car is on is lit, and it is the one the readout shows. */
.cop__btn.is-lit {
  color: #22160a;
  background: radial-gradient(circle at 34% 30%, #ffd9a8, #ffab4d 68%, #e08a24);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / .5), 0 0 9px rgb(255 155 61 / .8);
}
.cop__pair { display: flex; gap: 6px; }
.cop__btn--sm { width: 20px; height: 20px; font-size: 7px; letter-spacing: -.5px; }

/* The lift's own panel: a 7–10" screen built into the top of the car
   operating panel, above the floor readout and the call buttons, on one
   continuous plate. The board is tiny here — that is the point of showing it,
   because a layout that reads across a foyer often does not read at 8 inches
   in a corner of a lift car. */
.dev--panel {
  --bz: 0px; --rad: 0px;
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  padding: 14px 13px 16px;
  border-radius: 9px;
  background:
    linear-gradient(90deg, rgb(255 255 255 / .06) 1px, transparent 1px) 0 0 / 3px 100%,
    linear-gradient(165deg, #aab3c1, #78818f 46%, #99a2b0 62%, #6b7481);
  box-shadow: 0 22px 40px -14px rgb(4 7 14 / .45), inset 0 1px 0 rgb(255 255 255 / .4);
}
/* No second bezel: the plate is the bezel, and the screen is routed into it. */
.dev--panel .dev__body { background: none; box-shadow: none; padding: 0; }
.dev--panel .dev__body img {
  max-width: min(38vw, 190px);
  max-height: min(30dvh, 300px);
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .6), 0 0 0 3px rgb(20 25 36 / .92),
              0 2px 6px rgb(0 0 0 / .45);
}
/* The buttons are already on the plate, so they bring no plate of their own. */
.cop--bare {
  background: none; box-shadow: none; padding: 0;
  border-top: 1px solid rgb(255 255 255 / .18); padding-top: 11px;
}

@media (max-width: 620px) {
  .dev--lift { --side: 68px; }
  .dev--panel .dev__body img { max-width: min(46vw, 170px); }
  .cop { padding: 8px 6px; gap: 7px; }
  .cop__btn { width: 17px; height: 17px; font-size: 7px; }
  .cop__btn--sm { width: 15px; height: 15px; font-size: 5px; }
  .cop__floor b { font-size: 12px; }
}
.dev--lift .dev__body {
  background:
    linear-gradient(90deg, rgb(255 255 255 / .07) 1px, transparent 1px) 0 0 / 3px 100%,
    linear-gradient(160deg, #9aa4b4, #6c7686 42%, #8d97a7 60%, #5f6875);
  box-shadow: 0 24px 44px -14px rgb(4 7 14 / .4), inset 0 1px 0 rgb(255 255 255 / .35);
}
/* The screen sits in a routed recess, so it gets its own inner shadow. */
.dev--lift .dev__body img {
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .6), 0 0 0 3px rgb(20 25 36 / .9),
              0 2px 6px rgb(0 0 0 / .45);
  max-height: min(46dvh, 460px);
}
/* The car's ceiling line above, and the handrail below. */
.dev--lift .dev__top {
  display: block; width: 100%; height: 10px;
  background: linear-gradient(180deg, #b6bfcc, #8b95a5);
  border-radius: 3px 3px 0 0;
}
.dev--lift .dev__base { display: block; position: relative; width: 100%; height: 46px; }
.dev--lift .dev__base::before {
  content: ""; position: absolute; left: 50%; top: 20px; translate: -50% 0;
  width: calc(100% + 56px); max-width: 92vw; height: 11px;
  border-radius: var(--r-pill);
  background: linear-gradient(180deg, #dfe5ee, #98a2b1 55%, #6f7887);
  box-shadow: 0 6px 14px rgb(4 7 14 / .3);
}
/* The two brackets the rail is bolted to the car wall with. */
.dev--lift .dev__base::after {
  content: ""; position: absolute; left: 50%; top: 0; translate: -50% 0;
  width: calc(100% - 40px); height: 22px;
  background:
    linear-gradient(180deg, #aab3c1, #7d8695) left  center / 10px 22px no-repeat,
    linear-gradient(180deg, #aab3c1, #7d8695) right center / 10px 22px no-repeat;
}

@media (max-width: 620px) {
  .pv__head, .pv__foot { padding-inline: var(--s-4); }
  /* A wider slide and a thinner peek: on a phone the neighbour only has to
     hint that it is there. */
  .pv__rail { padding-inline: 8%; }
  .pv__slide { padding: var(--s-5) var(--s-4) var(--s-4); }
  .pv__act { width: 100%; }
  .pv__act .btn { flex: 1; }
  /* Shorter, because on a phone the header, the wrapped mount chips and the
     buttons take most of the height — at 56dvh a totem's plinth was cut off
     by the fold. */
  .dev__body img {
    /* 84% slide, minus its padding, minus the bezel either side. */
    max-width: calc((100vw - 2 * var(--s-4)) * .84 - 2 * var(--s-4) - 2 * var(--bz, 12px) - var(--side, 0px));
    max-height: min(38dvh, 400px);
  }
  .dev--lift .dev__body img { max-height: min(34dvh, 340px); }
  .dev--lift { --bz: 18px; }
  .dev--lift .dev__base::before { width: calc(100% + 28px); }
}

/* ============================================================== faq === */
details.q {
  border: var(--hair) solid var(--line); border-radius: var(--r);
  background: var(--bg-panel); margin-bottom: var(--s-3); box-shadow: var(--sh-1);
}
details.q > summary {
  cursor: pointer; list-style: none; padding: var(--s-4) var(--s-5);
  display: flex; align-items: center; gap: var(--s-4);
  font-family: var(--disp); font-weight: 700; font-size: 1.02rem;
  letter-spacing: -.015em; color: var(--ink);
}
details.q > summary::-webkit-details-marker { display: none; }
details.q > summary::after {
  content: "+"; margin-left: auto; flex: none;
  width: 26px; height: 26px; display: grid; place-items: center;
  border-radius: var(--r-pill); background: var(--bg-2);
  font-size: 1.1rem; color: var(--ink-2); line-height: 1;
}
details.q[open] > summary::after { content: "\2212"; background: var(--accent); color: var(--accent-on); }
details.q > div { padding: 0 var(--s-5) var(--s-5); color: var(--ink-2); }
details.q > div > * + * { margin-top: var(--s-4); }

/* ========================================================== pricing === */
.plans { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); align-items: start; }
@media (max-width: 900px) { .plans { grid-template-columns: minmax(0, 1fr); } }
.plan {
  padding: var(--s-6);
  background: var(--bg-panel); border: var(--hair) solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--sh-1);
  display: flex; flex-direction: column; gap: var(--s-4);
}
/* The live plan is the one that is lifted, ringed and glowing. */
.plan--now {
  border-color: var(--accent-line);
  box-shadow: var(--sh-3), 0 0 0 4px var(--accent-soft);
}
.plan .price { font-family: var(--disp); font-weight: 800; font-size: var(--t-2xl); letter-spacing: -.04em; color: var(--ink); line-height: 1; }
.plan .price small { display: block; margin-top: var(--s-2); font-size: var(--t-sm); font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink-3); }
.plan ul { margin-top: auto; }

/* ============================================================= cards == */
.post {
  display: grid; gap: var(--s-3); text-decoration: none;
  padding: var(--s-5); border: var(--hair) solid var(--line);
  border-radius: var(--r-lg); background: var(--bg-panel); box-shadow: var(--sh-1);
  transition: box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.post:hover { box-shadow: var(--sh-3); transform: translateY(-3px); }
.post h3 { font-size: var(--t-md); }
.post p { color: var(--ink-2); font-size: .93rem; }

/* A two-column "us vs them" card, the shape unimsg.io uses for
   "Why choose unimsg?". */
.versus { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-radius: var(--r-xl); overflow: hidden; border: var(--hair) solid var(--line); box-shadow: var(--sh-3); }
@media (max-width: 760px) { .versus { grid-template-columns: minmax(0, 1fr); } }
.versus > div { padding: clamp(24px, 3.5vw, 40px); }
.versus__us { background: var(--bg-panel); border-top: 4px solid var(--accent); }
.versus__them { background: var(--bg-1); }
.versus h3 { font-size: var(--t-lg); margin-bottom: var(--s-2); }
.versus .mono { margin-bottom: var(--s-5); }

/* ============================================================ narrow === */
@media (max-width: 620px) {
  .btn { white-space: normal; text-align: center; max-width: 100%; }
}
code, .mono, pre { overflow-wrap: anywhere; }

/* The single-column hero every interior page uses: no device beside it, and
   less air underneath, because the section that follows brings its own. */
.hero--solo { padding-bottom: clamp(24px, 3vw, 40px); }
.hero--solo::after { height: 520px; }

/* A row of hardware, ends aligned. Deliberately flex with a definite basis
   rather than a grid with `margin-inline:auto` on the items: auto inline
   margins switch a grid item off `stretch`, so its width becomes fit-content
   and a not-yet-loaded lazy image collapses the whole bezel to its padding. */
.rack {
  display: flex; flex-wrap: wrap;
  align-items: flex-end; justify-content: center;
  gap: clamp(20px, 3vw, 48px);
}
.rack > .screen { min-width: 0; }

/* An interior hero already ends with air under its lede, so the first section
   after it does not need a full band of padding on top as well. */
.hero--solo + .section { padding-top: clamp(24px, 3vw, 44px); }

/* The toggle shows the theme you would switch *to*, so only one glyph is ever
   visible. Driven by the theme rather than by script, so it is already correct
   on the first paint. */
.iconbtn .i-sun { display: none; }
:root[data-theme="ink"] .iconbtn .i-sun { display: block; }
:root[data-theme="ink"] .iconbtn .i-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .iconbtn .i-sun { display: block; }
  :root:not([data-theme]) .iconbtn .i-moon { display: none; }
}

/* ========================================================= fittings == */
/*
 * The five fittings on the home page. They borrow the device chrome the
 * template preview uses, so there is one definition of what a totem looks
 * like, and only override the sizing: in the preview a board is the subject
 * and fills the dialog, here five of them share a row and the caption under
 * each is doing as much work as the picture.
 */
.fits {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--s-5);
  /* start, not end: the fixed stage already stands the hardware on one floor,
     and bottom-aligning boxes whose captions run to different lengths lifts
     the shorter ones and staggers every title. */
  align-items: start;
}
.fit { display: grid; grid-template-rows: auto auto; gap: var(--s-4); }
/* A fixed stage so five different shapes of hardware stand on one floor and
   the captions under them line up. */
.fit__stage { height: 190px; display: flex; align-items: flex-end; justify-content: center; }
.fit figcaption { text-align: center; }
.fit figcaption b {
  display: block; font-family: var(--disp); font-size: .92rem; font-weight: 700;
  letter-spacing: -.01em; color: var(--ink);
}
.fit figcaption small {
  display: block; margin-top: 6px; font-size: .8rem; line-height: 1.5; color: var(--ink-2);
}

/* Sizing only — the chrome itself is inherited. */
.fit .dev { max-width: 100%; }
.fit .dev__body img { width: auto; height: auto; max-width: 100%; }
.fit .dev--wide .dev__body img { max-height: 96px; }
.fit .dev--tall .dev__body img { max-height: 132px; }
.fit .dev--kiosk { --bz: 7px; }
.fit .dev--kiosk .dev__base { height: 34px; }
.fit .dev--kiosk .dev__base::before { width: 22px; height: 26px; }
.fit .dev--kiosk .dev__base::after { width: 86px; height: 8px; }
.fit .dev--wall { --bz: 6px; }
.fit .dev--ceiling { --bz: 6px; }
.fit .dev--ceiling .dev__top { height: 30px; }
.fit .dev--ceiling .dev__top::before { width: calc(100% + 24px); height: 5px; }
.fit .dev--ceiling .dev__top::after { height: 25px; width: 58%; background-size: 3px 100%, 3px 100%; }
.fit .dev--panel { padding: 7px 6px 8px; gap: 6px; }
.fit .dev--panel .dev__body img { max-width: 104px; max-height: 74px; }
.fit .cop--bare { padding-top: 6px; gap: 5px; }
.fit .cop__floor { padding: 3px 4px; }
.fit .cop__floor b { font-size: 10px; }
.fit .cop__btn { width: 14px; height: 14px; font-size: 6px; }
.fit .cop__btn--sm { width: 12px; height: 12px; font-size: 4px; }
.fit .cop__btns { gap: 4px; }

/* The meeting-room door pad: a small tablet on a wall plate, the one fitting
   somebody stands right in front of. */
.dev--door { --bz: 9px; --rad: 8px; }
.dev--door .dev__body { box-shadow: var(--sh-3), inset 0 0 0 1px rgb(255 255 255 / .07); }
/* The plate it is screwed to, showing as a hairline margin around the slab. */
.dev--door .dev__base {
  display: block; width: 62%; height: 7px; margin-inline: auto;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(180deg, #2b3245, #171c28);
}
.fit .dev--door { --bz: 7px; }
.fit .dev--door .dev__body img { max-height: 86px; }

@media (max-width: 1080px) {
  .fits { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6); }
}
@media (max-width: 700px) {
  .fits { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-5); }
  .fit__stage { height: 150px; }
  .fit .dev--wide .dev__body img { max-height: 74px; }
  .fit .dev--tall .dev__body img { max-height: 104px; }
}
@media (max-width: 420px) {
  .fits { grid-template-columns: minmax(0, 1fr); }
  .fit__stage { height: 176px; }
}

/* ============================================================ wall ==== */
/*
 * The drifting template wall on the home page.
 *
 * Two lanes, opposite directions. Each lane contains its list twice and
 * animates by exactly -50% of its own height, so the reset is invisible: the
 * duplicate is sitting precisely where the original started. Nothing here
 * needs script, and the whole thing is inert under prefers-reduced-motion,
 * where a wall of moving images beside a paragraph is the worst case.
 *
 * The mask is what makes it read as a window onto something larger rather
 * than a box of clipped pictures.
 */
.wall {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4);
  height: clamp(380px, 46vw, 560px);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}
.wall__lane { display: grid; gap: var(--s-4); align-content: start; will-change: transform; }
.wall__lane--0 { animation: wallUp 46s linear infinite; }
/* The second lane starts already half travelled, so the two are never in step. */
.wall__lane--1 { animation: wallDown 52s linear infinite; }
.wall__run { display: grid; gap: var(--s-4); }
.wall img {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block;
  border-radius: var(--r-sm);
  border: var(--hair) solid var(--line);
  background: var(--bg-inset);
  box-shadow: var(--sh-2);
}
@keyframes wallUp   { from { transform: translateY(0); }      to { transform: translateY(-50%); } }
@keyframes wallDown { from { transform: translateY(-50%); }   to { transform: translateY(0); } }

/* Paused on hover, so a board that catches the eye can actually be looked at. */
.wall:hover .wall__lane { animation-play-state: paused; }

@media (max-width: 900px) {
  /* Two rows travelling left and right instead of two columns up and down:
     a phone has width to spare and no height to give. */
  .wall {
    grid-template-columns: minmax(0, 1fr); grid-auto-rows: min-content;
    height: auto; gap: var(--s-3);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
            mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  }
  /* The lane holds two runs and must let each size to its own content: given
     a fixed track the runs themselves were squeezed to one image's width and
     the row showed the same crop over and over. Only the runs lay out in
     fixed-width image tracks. */
  .wall__lane { grid-auto-flow: column; grid-auto-columns: max-content; gap: var(--s-3); }
  .wall__run { grid-auto-flow: column; grid-auto-columns: 236px; gap: var(--s-3); }
  .wall__lane--0 { animation: wallLeft 40s linear infinite; }
  .wall__lane--1 { animation: wallRight 46s linear infinite; }
  @keyframes wallLeft  { from { transform: translateX(0); }    to { transform: translateX(-50%); } }
  @keyframes wallRight { from { transform: translateX(-50%); } to { transform: translateX(0); } }
}

@media (prefers-reduced-motion: reduce) {
  .wall__lane { animation: none !important; }
  /* Without the drift the second copy is just the same six boards again. */
  .wall__run + .wall__run { display: none; }
}

/* ========================================================= megamenu === */
/*
 * Five nav items open a panel. The panel is anchored to the bar rather than to
 * the item it hangs from, and is `min(1060px, viewport - padding)` wide, so it
 * is centred on wide screens and can never be the thing that puts a horizontal
 * scrollbar on the page.
 */
.nav__in { position: relative; }
/* The label and its caret are two elements doing one job, so the hover and
   open states belong to the pair rather than to either — otherwise the row
   lights up as two adjacent pills with a seam down the middle. */
.nav__item {
  position: static; display: flex; align-items: center;
  border-radius: var(--r-pill);
  transition: background var(--fast) var(--ease);
}
.nav__item:hover,
.nav__item:has(.nav__caretbtn[aria-expanded="true"]) { background: var(--bg-2); }
.nav__item:hover > .nav__top,
.nav__item:has(.nav__caretbtn[aria-expanded="true"]) > .nav__top { background: none; color: var(--ink); }
.nav__item:has(.nav__caretbtn[aria-expanded="true"]) > .nav__caretbtn { color: var(--ink); }
.nav__top { white-space: nowrap; }

/* The page you are on is marked in the brand colour rather than in another
   shade of grey, so it reads as a state and not as a hover left behind. */
.nav__item[data-on],
.nav__links > a[aria-current="page"] { background: var(--accent-soft); }
.nav__item[data-on] > .nav__top,
.nav__links > a[aria-current="page"] { color: var(--accent-ink-safe); }
.nav__item[data-on] > .nav__caretbtn { color: var(--accent-ink-safe); }
.nav__item[data-on]:hover { background: var(--bg-2); }

.nav__caretbtn {
  flex: none; display: grid; place-items: center;
  width: 22px; height: 30px; margin-left: -6px; padding: 0;
  background: none; border: 0; border-radius: var(--r-pill);
  color: var(--ink-3); cursor: pointer;
}
.nav__caret { width: 14px; height: 14px; transition: transform var(--fast) var(--ease); }
.nav__caretbtn[aria-expanded="true"] .nav__caret { transform: rotate(180deg); }

.mega {
  position: absolute; top: calc(100% + 12px); left: 50%;
  transform: translateX(-50%);
  width: min(1060px, calc(100vw - 2 * var(--pad)));
  background: var(--bg-panel);
  border: var(--hair) solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-4);
  /* Not hidden: the notch below sits outside the box. The footer rounds its
     own bottom corners instead. */
  overflow: visible;
  animation: megaIn var(--fast) var(--ease) both;
}
@keyframes megaIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
/* A notch under the item that opened it. The panel is shared and centred, so
   without this it is a slab that appears near the bar with no indication of
   which of five things it belongs to. --arrow is the trigger's centre, set on
   open by the script and measured from the panel's own left edge. */
.mega::before {
  content: ""; position: absolute; top: -7px; left: var(--arrow, 50%);
  width: 13px; height: 13px; margin-left: -7px;
  background: var(--bg-panel);
  border-left: var(--hair) solid var(--line);
  border-top: var(--hair) solid var(--line);
  border-radius: 3px 0 0 0;
  rotate: 45deg;
}
.mega__in {
  display: grid; grid-template-columns: minmax(0, 1fr) 288px;
  gap: var(--s-6); padding: var(--s-6) var(--s-6) var(--s-5);
}
.mega__cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-4); }
.mega__cols:has(.mega__col:nth-child(2):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A heading with a rule under it, so a column reads as a section rather than
   as a pale word floating above some links. */
.mega__col h4 {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-ink-safe); margin-bottom: var(--s-3);
  padding-bottom: 9px; border-bottom: var(--hair) solid var(--line);
}
.mega__col a {
  position: relative;
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: 9px 20px 9px 10px; margin-inline: -10px;
  border-radius: var(--r); text-decoration: none;
  transition: background var(--fast) var(--ease);
}
.mega__col a:hover { background: var(--bg-2); }
.mega__col a b { display: block; font-size: .92rem; font-weight: 700; color: var(--ink); }
.mega__col a small { display: block; margin-top: 2px; font-size: .8rem; color: var(--ink-2); line-height: 1.4; }
.mega__col .ic { margin-top: 1px; transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease); }
.mega__col a:hover .ic { background: var(--accent-soft); border-color: var(--accent-line); }
.mega__col a:hover .ic::before { background: var(--accent-ink-safe); }
/* A chevron that arrives on hover: the row is a link, and nothing else in the
   panel says so. */
.mega__col a::after {
  content: ""; position: absolute; right: 8px; top: 50%;
  width: 6px; height: 6px; margin-top: -4px;
  border-right: 2px solid var(--accent-ink-safe);
  border-top: 2px solid var(--accent-ink-safe);
  rotate: 45deg;
  opacity: 0; translate: -4px 0;
  transition: opacity var(--fast) var(--ease), translate var(--fast) var(--ease);
}
.mega__col a:hover::after { opacity: 1; translate: 0 0; }

.mega__tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-4); }
.mega__tiles a {
  display: block; text-decoration: none; border-radius: var(--r-sm);
  overflow: hidden; border: var(--hair) solid var(--line); background: var(--bg-1);
  transition: box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.mega__tiles a:hover { box-shadow: var(--sh-2); transform: translateY(-2px); border-color: var(--accent-line); }
.mega__tiles .tile__shot { display: block; padding: 10px 10px 0; background: var(--bg-inset); }
.mega__tiles .screen { display: block; padding: 5px; border-radius: 8px; }
.mega__tiles .screen > img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; border-radius: 3px; }
.mega__tiles .tile__txt { display: block; padding: 10px var(--s-4) var(--s-4); }
.mega__tiles b { display: block; font-size: .9rem; font-weight: 700; color: var(--ink); }
.mega__tiles small { display: block; margin-top: 2px; font-size: .78rem; color: var(--ink-2); }

.mega__promo {
  position: relative;
  display: block; text-decoration: none; border-radius: var(--r-sm);
  overflow: hidden; border: var(--hair) solid var(--line); background: var(--bg-1);
  transition: box-shadow var(--fast) var(--ease), border-color var(--fast) var(--ease),
              transform var(--fast) var(--ease);
}
.mega__promo:hover { box-shadow: var(--sh-3); border-color: var(--accent-line); transform: translateY(-2px); }
.mega__promo img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; display: block; background: var(--bg-inset); }
.mega__promo__txt { display: block; padding: var(--s-4); }
/* The arrow on the card, which is the affordance the whole panel is missing. */
.mega__promo::after {
  content: ""; position: absolute; right: 14px; bottom: 14px;
  width: 26px; height: 26px; border-radius: var(--r-pill);
  background: var(--accent) var(--i-arrow) center/13px no-repeat;
  opacity: 0; translate: 0 6px;
  transition: opacity var(--fast) var(--ease), translate var(--fast) var(--ease);
}
.mega__promo:hover::after { opacity: 1; translate: 0 0; }
.mega__promo em {
  display: block; font-style: normal;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-ink-safe);
}
.mega__promo b { display: block; margin-top: 6px; font-family: var(--disp); font-size: 1rem; font-weight: 800; letter-spacing: -.02em; color: var(--ink); }
.mega__promo small { display: block; margin-top: 6px; font-size: .82rem; color: var(--ink-2); line-height: 1.45; }

.mega__foot {
  display: flex; justify-content: flex-end;
  padding: var(--s-4) var(--s-6);
  border-top: var(--hair) solid var(--line); background: var(--bg-1);
  /* The panel no longer clips, so the corners are rounded here instead. */
  border-radius: 0 0 calc(var(--r-xl) - 1px) calc(var(--r-xl) - 1px);
}

/* The bar's own buttons duplicated for the burger panel, where the bar's
   copies are hidden for width. */
.nav__extra { display: none; }

@media (prefers-reduced-motion: reduce) {
  .mega { animation: none; }
  .nav__caret { transition: none; }
}

/* -------------------------------------------------- megamenu, narrow -- */
/* Under the burger the panels stop floating and become accordions inside the
   menu, which is itself scrollable — five open sections are taller than a
   phone. */
@media (max-width: 1040px) {
  .nav__links {
    max-height: calc(100dvh - 96px); overflow-y: auto;
    overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  }
  .nav__item {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; border-bottom: var(--hair) solid var(--line);
  }
  .nav__links > .nav__item:last-of-type { border-bottom: 0; }
  .nav__caretbtn { width: 44px; height: 44px; margin-left: 0; }
  .nav__caret { width: 18px; height: 18px; }

  .mega {
    position: static; transform: none; width: auto; grid-column: 1 / -1;
    border: 0; border-radius: 0; box-shadow: none; background: var(--bg-1);
    margin-bottom: var(--s-3); animation: none;
  }
  .mega__in { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); padding: var(--s-4); }
  .mega__cols,
  .mega__cols:has(.mega__col:nth-child(2):last-child) { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .mega__tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mega__promo img { aspect-ratio: 16 / 7; }
  .mega__foot { padding: var(--s-3) var(--s-4); justify-content: stretch; }
  .mega__foot .btn { width: 100%; }

  .nav__extra {
    display: grid; gap: var(--s-2); padding: var(--s-3) var(--s-2) var(--s-2);
    border-top: var(--hair) solid var(--line); margin-top: var(--s-2);
  }
  .nav__extra .btn { width: 100%; }
  /* Docs, Blog, FAQ and Search are not in the bar; on a phone this row is the
     only place they can be reached without scrolling to the footer. */
  .nav__extra__links { display: flex; flex-wrap: wrap; gap: var(--s-2); }
  .nav__extra__links a {
    flex: 1 1 auto; text-align: center;
    padding: 10px 12px; border-radius: var(--r);
    background: var(--bg-2); border: var(--hair) solid var(--line);
    font-size: .88rem; font-weight: 700; color: var(--ink-2); text-decoration: none;
  }
  .nav__extra__links a[aria-current="page"] { color: var(--ink); border-color: var(--line-3); }
}
/* 390px is the base width of every current iPhone, so a 380px breakpoint
   never fired on the most common phone and left two 108px-wide previews side
   by side. One column from 560px down. */
@media (max-width: 560px) {
  .mega__tiles { grid-template-columns: minmax(0, 1fr); }
}

/* =========================================================== search === */
.search { display: grid; gap: var(--s-5); }
.search__bar {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 6px 6px 6px var(--s-4);
  background: var(--bg-panel); border: var(--hair) solid var(--line-2);
  border-radius: var(--r-pill); box-shadow: var(--sh-1);
}
.search__bar:focus-within { border-color: var(--accent-line); box-shadow: var(--sh-2), 0 0 0 4px var(--accent-soft); }
.search__bar .ic { flex: none; }
.search__bar input {
  flex: 1; min-width: 0; background: none; border: 0; outline: 0;
  padding: 11px 0; font: inherit; font-size: 1rem; color: var(--ink);
}
.search__bar input::placeholder { color: var(--ink-3); }
.search__bar input::-webkit-search-cancel-button { -webkit-appearance: none; }
.search__bar .btn { flex: none; }

.search__scope { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.search__note { font-size: var(--t-sm); font-weight: 600; color: var(--ink-3); }

.hits { display: grid; gap: var(--s-3); }
.hit {
  display: block; text-decoration: none;
  padding: var(--s-5); border: var(--hair) solid var(--line);
  border-radius: var(--r-lg); background: var(--bg-panel); box-shadow: var(--sh-1);
  transition: box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.hit:hover { box-shadow: var(--sh-3); transform: translateY(-2px); }
.hit__top { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
.hit__kind {
  font-family: var(--mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 3px 9px; border-radius: var(--r-pill);
  background: var(--bg-2); color: var(--ink-2); border: var(--hair) solid var(--line);
}
.hit__kind--doc  { background: var(--accent-soft); color: var(--accent-ink-safe); border-color: var(--accent-line); }
.hit__kind--post { background: var(--violet-soft); color: var(--violet); border-color: var(--violet-line); }
.hit__path { font-family: var(--mono); font-size: .78rem; color: var(--ink-3); overflow-wrap: anywhere; }
.hit h3 { margin-top: var(--s-3); font-size: var(--t-md); }
.hit p { margin-top: var(--s-2); font-size: .92rem; color: var(--ink-2); line-height: 1.6; }
/* No horizontal padding: a match inside a word — "kiosk" in "UniKiosk" — would
   otherwise be pushed apart and read as two words. */
.hit mark { background: var(--accent-soft); color: var(--ink); border-radius: 3px; padding: 1px 0; }

.search__empty { padding: var(--s-7) 0; }
