/* =====================================================================
   Clyr. Labs — the landing page's own stylesheet
   ---------------------------------------------------------------------
   Linked from clyr.html and nowhere else. Clyr reads cooler and more
   technical than the rest of JMK: monochrome, geometric, precise. Keeping
   that in its own file means the page can hold a voice of its own without
   every other page carrying the weight of it.

   Two rules run through everything here:

   1. MONOCHROME IS THE DISCIPLINE. The supplied artwork is black on
      white and nothing else. So there is no accent colour on this page —
      contrast, scale and depth do the work a colour would normally do.
      A gradient would be the first thing that made it look like every
      other technology site.

   2. NOTHING ANIMATES THAT IS NOT SAYING SOMETHING. Motion here is
      scroll-linked and slow, because the page is an argument delivered in
      order, not a set of things that happen to move. Everything is
      transform or opacity; nothing triggers layout.

   All of it stops under prefers-reduced-motion, at the foot of this file,
   where every scroll-driven section falls back to being simply legible.
   ===================================================================== */

.clyr {
  --ink: #08090A;
  --paper: #F6F6F4;
  --ink-soft: rgba(255,255,255,.62);
  --ink-faint: rgba(255,255,255,.34);
  --hair: rgba(255,255,255,.14);
  --paper-soft: rgba(8,9,10,.62);
  --paper-faint: rgba(8,9,10,.36);
  --hair-dark: rgba(8,9,10,.14);

  --type: var(--font-clyr, "Poppins", "Century Gothic", sans-serif);

  background: var(--ink);
  color: #fff;
}

/* Sections alternate ground. The rhythm IS the pacing: a light section
   between two dark ones is a breath, and the page needs three of them. */
.clyr__screen {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: clamp(72px, 10vh, 128px) clamp(20px, 5vw, 72px);
  overflow: hidden;
}
.clyr__screen--light { background: var(--paper); color: var(--ink); }
.clyr__screen--tall { min-height: auto; }

.clyr__inner { width: 100%; max-width: 1240px; margin: 0 auto; }

/* --- type ------------------------------------------------------------
   One family, three sizes, two weights. The restraint is the point: a
   technology launch page that reaches for a fourth weight has stopped
   trusting its own hierarchy. */
.clyr h1, .clyr h2, .clyr h3, .clyr p, .clyr li { font-family: var(--type); }

.clyr__display {
  font-weight: 300;
  font-size: clamp(44px, 8.4vw, 148px);
  line-height: .94;
  letter-spacing: -.035em;
  margin: 0;
  text-wrap: balance;
}
.clyr__head {
  font-weight: 300;
  font-size: clamp(34px, 5.4vw, 88px);
  line-height: 1;
  letter-spacing: -.03em;
  margin: 0;
  text-wrap: balance;
}
.clyr__lead {
  font-weight: 400;
  font-size: clamp(17px, 1.55vw, 22px);
  line-height: 1.5;
  letter-spacing: -.008em;
  max-width: 38ch;
  color: var(--ink-soft);
  margin: 0;
}
.clyr__screen--light .clyr__lead { color: var(--paper-soft); }

.clyr__label {
  font-family: var(--type);
  font-weight: 500;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 clamp(22px, 3vh, 40px);
  display: block;
}
.clyr__screen--light .clyr__label { color: var(--paper-faint); }

/* --- controls --------------------------------------------------------
   A pill and a bare link. Two, never more: a hero with three equal
   choices has made none of them. */
.clyr__btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 15px 28px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: #fff; color: var(--ink);
  font-family: var(--type); font-size: 15px; font-weight: 500;
  letter-spacing: -.01em; text-decoration: none;
  transition: transform 520ms cubic-bezier(.16,.84,.28,1), opacity 320ms ease;
}
.clyr__btn:hover { transform: translateY(-2px); }
.clyr__btn--ghost { background: transparent; color: inherit; border-color: var(--hair); }
.clyr__screen--light .clyr__btn { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.clyr__screen--light .clyr__btn--ghost { background: transparent; color: var(--ink); border-color: var(--hair-dark); }

.clyr__quiet {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--type); font-size: 14px; font-weight: 400;
  color: var(--ink-soft); text-decoration: none;
  transition: color 300ms ease;
}
.clyr__quiet:hover { color: #fff; }
.clyr__actions { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(16px, 2.4vw, 32px); }

.clyr :is(a, button):focus-visible {
  outline: 2px solid currentColor; outline-offset: 4px; border-radius: 6px;
}

/* --- hero ------------------------------------------------------------ */
.clyr__hero { text-align: left; }

/* The hero line as it is being typed.
   Every character is in the layout from the first frame — see
   useTypedOnce in ClyrLabs.jsx — so this only ever changes whether one
   is painted. The caret hangs off the character due next, drawn
   absolutely so it cannot push the line along as it moves. */
.clyr__ch { position: relative; }
.clyr__ch.is-caret::before {
  content: "";
  position: absolute;
  left: 0; top: .12em;
  width: .045em; height: .74em;
  background: currentColor;
  animation: jmkTypeCaret 900ms steps(1, end) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .clyr__ch.is-caret::before { display: none; }
}
.clyr__lockup { display: block; margin-bottom: clamp(40px, 7vh, 88px); }
.clyr__heroGrid {
  display: grid; gap: clamp(30px, 5vh, 56px);
  position: relative; z-index: 2;
}

/* The board behind the hero.
   Masked to a soft ellipse so it never reaches a hard edge, and sat
   under the copy at a fraction of its own brightness — the canvas is
   already drawing at five per cent, and this takes it lower still.
   It has to survive being read over. */
.clyr__board {
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  opacity: .85;
  -webkit-mask-image: radial-gradient(120% 88% at 58% 50%, #000 24%, rgba(0,0,0,.45) 62%, transparent 100%);
  mask-image: radial-gradient(120% 88% at 58% 50%, #000 24%, rgba(0,0,0,.45) 62%, transparent 100%);
}
.clyr__board canvas { display: block; width: 100%; height: 100%; }

/* --- pinned narrative ------------------------------------------------
   The tall parent gives the sequence room to happen in; the sticky child
   is the one screen it happens on. Overflow lives on the CHILD — an
   ancestor with overflow becomes the scrolling context and sticky
   silently stops working. */
.clyr__stage { position: relative; }
.clyr__pin {
  position: sticky; top: 0;
  height: 100svh;
  overflow: hidden;
  display: flex; align-items: center;
  padding: clamp(56px, 8vh, 104px) clamp(20px, 5vw, 72px);
}

/* Statements that replace one another in place. All of them are laid on
   top of each other in a grid cell so nothing reflows as they swap. */
.clyr__swap { display: grid; width: 100%; max-width: 1240px; margin: 0 auto; }
.clyr__swap > * { grid-area: 1 / 1; }
.clyr__line {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 640ms cubic-bezier(.2,.7,.2,1), transform 780ms cubic-bezier(.16,.84,.28,1);
  will-change: opacity, transform;
}
.clyr__line[data-on="true"] { opacity: 1; transform: none; }
.clyr__line[data-past="true"] { opacity: 0; transform: translateY(-28px); }

/* --- the morph: rigid box to Clyr form ------------------------------- */
.clyr__morph { display: grid; place-items: center; }
.clyr__morphCell { grid-area: 1 / 1; }

/* --- the system graph ------------------------------------------------ */
.clyr__node {
  fill: none; stroke: currentColor; stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}
.clyr__nodeLabel {
  font-family: var(--type); font-size: 9.5px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  fill: currentColor;
}
.clyr__wire {
  fill: none; stroke: currentColor; stroke-width: 1;
  vector-effect: non-scaling-stroke;
  opacity: .45;
  transition: opacity 340ms ease, stroke-width 340ms ease;
}
/* With something chosen, everything else steps back. Dimming the rest is
   what makes one node readable; brightening the node alone just makes a
   bright dot in an equally busy diagram. */
.clyr__graph.is-lit .clyr__wire { opacity: .12; }
.clyr__graph.is-lit .clyr__wire.is-on { opacity: .85; stroke-width: 1.6; }

.clyr__dot { cursor: pointer; }
.clyr__hit { fill: transparent; }
.clyr__halo {
  fill: none; stroke: currentColor; stroke-width: 1;
  vector-effect: non-scaling-stroke;
  opacity: 0;
  transform-box: fill-box; transform-origin: 50% 50%;
  transform: scale(.55);
  transition: opacity 380ms ease, transform 520ms cubic-bezier(.16,.84,.28,1);
}
.clyr__dot .clyr__node,
.clyr__dot .clyr__nodeLabel {
  transition: opacity 340ms ease, stroke-width 340ms ease, fill 340ms ease;
}
.clyr__graph.is-lit .clyr__dot:not(.is-on) .clyr__node,
.clyr__graph.is-lit .clyr__dot:not(.is-on) .clyr__nodeLabel { opacity: .3; }
.clyr__dot.is-on .clyr__halo { opacity: .5; transform: scale(1); }
.clyr__dot.is-on .clyr__node { stroke-width: 2.2; }
.clyr__dot:focus { outline: none; }
.clyr__dot:focus-visible .clyr__halo { opacity: .9; transform: scale(1.14); }

.clyr__tip rect {
  fill: rgba(255,255,255,.94);
  stroke: none;
}
.clyr__tipText {
  font-family: var(--type); font-size: 12.5px; font-weight: 400;
  fill: #14161A;
}
.clyr__tip { animation: clyrTipIn 220ms cubic-bezier(.2,.7,.2,1) both; }
@keyframes clyrTipIn { from { opacity: 0; } }

/* The caption swaps rather than jumps. Keyed on the chosen node in the
   component, so React remounts it and the animation runs each time. */
.clyr__caption { animation: clyrTipIn 380ms ease both; }
.clyr__hint {
  font-family: var(--type); font-size: 12.5px; letter-spacing: .04em;
  color: var(--ink-faint); margin: clamp(18px,2.4vh,28px) 0 0;
}

/* --- process --------------------------------------------------------- */
.clyr__steps { display: grid; gap: 0; }
.clyr__step {
  display: grid;
  grid-template-columns: clamp(58px, 7vw, 92px) 1fr;
  gap: clamp(20px, 3vw, 44px);
  padding: clamp(13px, 2.3vh, 28px) 0;
  border-top: 1px solid var(--hair-dark);
  align-items: start;
  opacity: .28;
  transition: opacity 620ms ease;
}
.clyr__step[data-on="true"] { opacity: 1; }
/* The step mark. Sized for a drawn glyph now rather than for "01" —
   the number fell back to the same slot if Illos is not on the page,
   which is why the type rules are still here. */
.clyr__stepN {
  font-family: var(--type); font-size: clamp(13px, 1.2vw, 16px); font-weight: 500;
  letter-spacing: .1em; font-variant-numeric: tabular-nums;
  display: block; width: clamp(30px, 2.6vw, 40px); line-height: 1;
}
.clyr__stepN .jmk-glyph { width: 100%; height: auto; display: block; }

/* The mark at the middle of the ClyrCustom diagram, inside a
   foreignObject. The box is the one the circle sits in, so the mark is
   centred on the node rather than on the viewBox. */
.clyr__hubMark {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; color: var(--paper);
}
.clyr__stepT {
  font-family: var(--type); font-weight: 300;
  font-size: clamp(24px, 2.9vw, 44px); line-height: 1.02;
  letter-spacing: -.028em; margin: 0 0 7px;
}
.clyr__stepD {
  font-family: var(--type); font-size: clamp(14px, 1.05vw, 17px);
  line-height: 1.5; color: var(--paper-soft); margin: 0; max-width: 58ch;
}

/* --- why: large type, not an icon grid -------------------------------- */
.clyr__pillars { display: grid; gap: 0; }
.clyr__pillar {
  display: grid; grid-template-columns: 1fr; gap: 6px;
  padding: clamp(24px, 3.6vh, 42px) 0;
  border-top: 1px solid var(--hair);
}
.clyr__pillarT {
  font-family: var(--type); font-weight: 300;
  font-size: clamp(28px, 4vw, 62px); line-height: 1;
  letter-spacing: -.03em; margin: 0;
}
.clyr__pillarD {
  font-family: var(--type); font-size: clamp(15px, 1.25vw, 18px);
  line-height: 1.5; color: var(--ink-soft); margin: 0; max-width: 46ch;
}
@media (min-width: 860px) {
  .clyr__pillar { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); align-items: baseline; }
}

/* A pillar that goes somewhere. Same row, plus an arrow that moves. */
.clyr__pillar--link {
  text-decoration: none; color: inherit;
  transition: opacity 320ms ease;
}
.clyr__pillar--link .clyr__pillarT { display: flex; align-items: baseline; gap: .4em; }
.clyr__pillarGo {
  font-size: .42em; opacity: .35;
  transition: transform 460ms cubic-bezier(.16,.84,.28,1), opacity 320ms ease;
}
.clyr__pillar--link:hover .clyr__pillarGo,
.clyr__pillar--link:focus-visible .clyr__pillarGo { opacity: 1; transform: translateX(.5em); }
@media (prefers-reduced-motion: reduce) {
  .clyr__wire, .clyr__halo, .clyr__node, .clyr__nodeLabel { transition: none; }
  .clyr__tip, .clyr__caption { animation: none; }
  .clyr__pillarGo { transition: none; }
  .clyr__pillar--link:hover .clyr__pillarGo { transform: none; }
}

/* --- footer ---------------------------------------------------------- */
.clyr__foot {
  background: var(--ink); color: #fff;
  padding: clamp(56px, 8vw, 88px) clamp(20px, 5vw, 72px) clamp(40px, 5vw, 56px);
  border-top: 1px solid var(--hair);
}
.clyr__footGrid {
  max-width: 1240px; margin: 0 auto;
  display: grid; gap: clamp(32px, 5vw, 64px);
}
@media (min-width: 760px) { .clyr__footGrid { grid-template-columns: 1fr auto; align-items: start; } }
.clyr__footLinks { display: flex; flex-wrap: wrap; gap: 10px clamp(20px, 3vw, 36px); }
.clyr__footLinks a {
  font-family: var(--type); font-size: 14px; color: var(--ink-soft);
  text-decoration: none; transition: color 300ms ease;
}
.clyr__footLinks a:hover { color: #fff; }
.clyr__footNote {
  font-family: var(--type); font-size: 12.5px; color: var(--ink-faint);
  margin: clamp(28px, 4vw, 44px) auto 0; max-width: 1240px;
}

/* --- mobile ----------------------------------------------------------
   Not a shrunken desktop. The pinned sequences still pin — they are the
   argument — but the field behind the hero is dropped (it is a lot of
   painting for something a phone shows a sliver of), the graph loses its
   scroll-linked reflow in favour of appearing whole, and the type scale
   is reset rather than merely allowed to clamp down. */
@media (max-width: 760px) {
  /* Lower again on a phone: the copy is over more of the screen, and the
     board has less room to be atmosphere in. */
  .clyr__board { opacity: .6; }
  .clyr__pin { padding-left: 20px; padding-right: 20px; }
  .clyr__lead { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .clyr__line { transition: none; opacity: 1 !important; transform: none !important; }
  .clyr__step { opacity: 1; transition: none; }
  .clyr__btn { transition: none; }
  /* A pinned section whose whole content is a sequence has nothing to
     show when the sequence cannot run, so it stops pinning and lets the
     statements stack as ordinary text. */
  .clyr__pin { position: static; height: auto; padding-top: 0; padding-bottom: 0; }
  .clyr__swap { display: block; }
  .clyr__swap > * { margin-bottom: clamp(28px, 5vh, 56px); }
}

/* --- copy beside a figure --------------------------------------------
   Two columns on a wide screen, stacked on a narrow one. The figure is
   capped against the viewport's HEIGHT as well as its width, because a
   pinned section is exactly one screen tall and a diagram sized only
   against width runs straight off the bottom of it. */
.clyr__split {
  display: grid;
  gap: clamp(28px, 4vw, 72px);
  align-items: center;
}
.clyr__split > :first-child { max-width: 36ch; }
.clyr__split .clyr__head { font-size: clamp(30px, 4.4vw, 68px); }
.clyr__fig {
  width: 100%;
  max-width: min(52vw, 620px);
  max-height: 62svh;
  justify-self: center;
  color: rgba(255,255,255,.66);
}
.clyr__screen--light .clyr__fig,
.clyr__pin.clyr__screen--light .clyr__fig { color: rgba(8,9,10,.62); }

@media (min-width: 861px) {
  .clyr__split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
}
@media (max-width: 860px) {
  /* The figure still earns its place on a phone, but it stops being half
     the argument and becomes the illustration under it. */
  .clyr__fig { max-width: 82vw; max-height: 40svh; }
  .clyr__split > :first-child { max-width: none; }
}

/* =====================================================================
   What we build with — the stack wall
   ---------------------------------------------------------------------
   On the paper ground, because brand marks are overwhelmingly dark on
   transparent: on the near-black they would need inverting, and
   inverting somebody else's logo is worse than not showing it.

   Greyscale until pointed at. Rule one of this page is that it is
   monochrome, and a row of full-colour marks is the single thing that
   would make it look like every other technology site. Colour on hover
   is both a better object and a fairer treatment of a trademark than
   permanently desaturating it.

   The layout does not depend on the logos existing. Each item is a
   column of optional logo, name, line — so with no logo files at all it
   is a typographic wall, which is the state it ships in.
   ===================================================================== */
/* Padding, not margin. Each group is wrapped in a Reveal, so a top
   margin collapses out through that wrapper and the groups end up
   sharing one gap instead of each having its own — which put every
   group heading hard against the cards above it. Padding cannot
   collapse. */
.clyr__stackGroup { padding-top: clamp(30px, 4.5vh, 54px); }
/* NO :first-of-type reset, and that is not an omission.
   Every group sits in a Reveal wrapper of its own, so each one is the
   first — and only — .clyr__stackGroup inside its own parent. The
   selector matched all four and zeroed the padding on every one of
   them, which is the same collapsed-gap bug wearing a different hat.
   The first group's extra space is taken back off the standfirst above
   it instead, where it is a fixed number rather than a guess about the
   DOM. */

.clyr__stackHead {
  font-family: var(--type);
  font-size: var(--text-2xs, 12px);
  letter-spacing: .16em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--paper-faint);
  margin: 0 0 clamp(14px, 2vh, 22px);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hair-dark);
}

.clyr__stackRow {
  display: grid;
  gap: clamp(18px, 2.4vw, 34px);
  /* auto-FILL, not auto-fit. A group with one tool in it — and there is
     one — stretches that card the full width of the page under auto-fit,
     so Salesforce came out four times the size of Shopify for no reason
     anybody could see. auto-fill leaves the empty tracks empty and every
     card is the same object. */
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

.clyr__stackItem {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  text-decoration: none;
  color: inherit;
  padding: clamp(16px, 1.8vw, 22px);
  border: 1px solid var(--hair-dark);
  border-radius: 14px;
  background: rgba(255, 255, 255, .5);
  transition: border-color 260ms var(--ease, ease), transform 300ms var(--ease, ease);
}
a.clyr__stackItem:hover { border-color: var(--ink); transform: translateY(-3px); }
a.clyr__stackItem:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.clyr__stackLogo { display: block; height: 26px; margin-bottom: 4px; }
.clyr__stackLogo img {
  height: 100%; width: auto; max-width: 160px; object-fit: contain; display: block;
  filter: grayscale(1);
  opacity: .78;
  transition: filter 300ms var(--ease, ease), opacity 300ms var(--ease, ease);
}
a.clyr__stackItem:hover .clyr__stackLogo img,
a.clyr__stackItem:focus-visible .clyr__stackLogo img { filter: none; opacity: 1; }

.clyr__stackName {
  font-family: var(--type);
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 500;
  letter-spacing: -.015em;
  color: var(--ink);
}

.clyr__stackNote {
  font-family: var(--type);
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--paper-soft);
}

@media (prefers-reduced-motion: reduce) {
  .clyr__stackItem,
  .clyr__stackLogo img { transition: none; }
  a.clyr__stackItem:hover { transform: none; }
}

/* The managed section's heading beside its illustration. Stacks below
   900, where there is no room to put anything beside anything. */
.clyr__managed {
  display: grid;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  margin: 0 0 clamp(40px, 6vh, 72px);
}
@media (min-width: 901px) {
  .clyr__managed { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); }
}

/* The mark at the foot: monogram and wordmark on one baseline. The
   lockup alone was the name set in type, which is what every other line
   down here already is. */
.clyr__footMark { display: flex; align-items: center; gap: clamp(10px, 1.2vw, 16px); }

/* The site's own closing notice, right at the bottom, below the routing
   note. Clyr has a footer of its own because it is a product with its
   own navigation — it is not a separate company, and a page ending
   without the line every other page ends with reads as one published
   from somewhere else. */
.clyr__footLegal {
  font-family: var(--type); font-size: 12px; color: var(--ink-faint);
  margin: clamp(24px, 3.4vw, 36px) auto 0; max-width: 1240px;
  padding-top: clamp(20px, 2.6vw, 28px);
  border-top: 1px solid var(--hair);
}
