/* ==========================================================================
   AxoGrid — page-specific components

   Loaded only by axogrid.html. Base, navigation, sections, the layer stack,
   cards, and footer come from styles.css; all values come from tokens.css.
   ========================================================================== */

/* ==========================================================================
   The link — you on one side of the network, the Grid on the other

   The graphic this replaced was a six by four grid of identical modules, which
   reads as a table, and showed neither the network nor you while its caption
   claimed both. Everything below exists to say "you are here, the biology is
   there", which is the one thing the word cloud has to earn.
   ========================================================================== */

/* Held to the same optical column the rest of the page sits in. A little wider
   than the layer stack beside it because the composition is horizontal and needs
   the room, but nowhere near the full container, which strands the two sides
   at opposite edges. */
.link {
  max-width: calc(var(--unit) * 52);
  margin: var(--space-16) auto 0;
}

.link__scene {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(calc(var(--unit) * 2.5), 0.8fr) minmax(0, 2.15fr);
  grid-template-rows: auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-4);
  row-gap: var(--space-3);
}

.link__tag {
  align-self: end;
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
  text-align: center;
}

.link__tag--you {
  grid-area: 1 / 1;
}

.link__tag--net {
  grid-area: 1 / 2;
}

.link__tag--them {
  grid-area: 1 / 3;
}

/* ---- your side: a laptop ---- */

/* A prompt and a cursor rather than fake code, which nobody can read at this
   size and which invites the reader to squint at something that says nothing. */
.link__you {
  grid-area: 2 / 1;
}

.laptop {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  filter: drop-shadow(var(--shadow-md));
}

.laptop__lid,
.laptop__base {
  fill: var(--surface-raised);
  stroke: var(--border-strong);
  stroke-width: 1px;
  vector-effect: non-scaling-stroke;
}

.laptop__screen {
  fill: var(--surface-overlay);
  stroke: var(--border-subtle);
  stroke-width: 1px;
  vector-effect: non-scaling-stroke;
}

.laptop__notch {
  fill: var(--border-strong);
}

.laptop__prompt {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.laptop__cursor {
  fill: var(--accent);
  animation: cursor-blink 1.2s steps(1) infinite;
}

@keyframes cursor-blink {
  50% {
    opacity: 0;
  }
}

/* ---- the link across the network ---- */

.link__wire {
  grid-area: 2 / 2;
  position: relative;
  align-self: stretch;
}

/* Pulled out past the column gaps on both sides, so the link visibly lands on
   the code panel and on the population rather than stopping short of each. */
.link__wire::before {
  content: "";
  position: absolute;
  inset: 50% calc(var(--space-4) * -1) auto;
  height: 1px;
  background: var(--border-strong);
}

.link__boundary {
  position: absolute;
  left: 50%;
  top: calc(var(--space-3) * -1);
  bottom: calc(var(--space-3) * -1);
  border-left: 1px dashed var(--border-strong);
}

.link__packet {
  position: absolute;
  top: 50%;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--accent);
  /* Linear, not the house easing. Everything else on the site eases because it
     is a thing arriving; this is a thing in transit, and easing makes it cover
     half the wire immediately and then crawl. */
  animation: packet-out 3.6s linear infinite;
}

/* The answer comes back in the partner hue, so the two directions read as
   different things rather than as one dot bouncing. */
.link__packet--back {
  background: var(--accent-secondary);
  animation-name: packet-back;
}

@keyframes packet-out {
  0%,
  6% {
    left: 0%;
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  44% {
    left: 100%;
    opacity: 1;
  }
  50%,
  100% {
    left: 100%;
    opacity: 0;
  }
}

@keyframes packet-back {
  0%,
  56% {
    left: 100%;
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  94% {
    left: 0%;
    opacity: 1;
  }
  100% {
    left: 0%;
    opacity: 0;
  }
}

/* ---- our side: a population in the cloud, not a rack ---- */

/* Positions are a jittered grid with cells dropped at random, kept only where a
   whole module fits inside the cloud, worked out once and written into the
   markup. Jitter alone only hides the rows and dropouts alone only hide the
   columns; a table needs both to read as a table. The fixed ratio, the same as
   the cloud's viewBox, keeps the modules inside it as the column narrows. */
.pool {
  grid-area: 2 / 3;
  position: relative;
  aspect-ratio: 400 / 260;
}

/* The cloud is circles over a rounded base. The edge layer strokes every shape
   at twice the line width, and the fill layer then covers the inner half of
   each stroke, so only the outline of the union is left. */
.pool__cloud {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(var(--shadow-md));
}

/* Opaque, because where two shapes' strokes cross a translucent colour would
   darken into knots along the outline. */
.pool__cloud-edge {
  fill: none;
  stroke: color-mix(in srgb, var(--accent) 30%, var(--surface-raised));
  stroke-width: 2px;
}

.pool__cloud-fill {
  fill: color-mix(in srgb, var(--accent) 6%, var(--surface-raised));
}

.pool__unit {
  position: absolute;
  width: 10.6%;
  aspect-ratio: 40 / 26;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  box-shadow: var(--shadow-sm);
}

/* The culture window inside each module. */
.pool__unit::before {
  content: "";
  position: absolute;
  inset: 24% 16%;
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--accent) 60%, transparent) 0.9px,
    transparent 1.1px
  );
  background-size: 5px 5px;
  opacity: 0.32;
}

.pool__unit.is-live {
  border-color: var(--accent-border);
}

/* Live modules brighten as the request lands, so the link and the population
   are visibly the same event. */
.pool__unit.is-live::before {
  animation: unit-wake 3.6s var(--ease) infinite;
}

.pool__unit.is-live:nth-of-type(3n)::before {
  animation-delay: 0.3s;
}

@keyframes unit-wake {
  0%,
  44% {
    opacity: 0.32;
  }
  50%,
  58% {
    opacity: 1;
  }
  66%,
  100% {
    opacity: 0.32;
  }
}

.link__caption {
  max-width: 34em;
  margin: var(--space-8) auto 0;
  color: var(--text-tertiary);
  font-size: var(--text-base);
  text-align: center;
}

/* Side by side stops working once the pool is narrower than the modules can
   survive, so the link stands up and the same journey runs top to bottom. */
@media (max-width: 760px) {
  .link__scene {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: repeat(6, auto);
    row-gap: var(--space-3);
  }

  .link__tag--you {
    grid-area: 1 / 1;
  }

  .link__you {
    grid-area: 2 / 1;
    justify-self: center;
    width: min(100%, calc(var(--unit) * 13));
  }

  .link__tag--net {
    grid-area: 3 / 1;
    align-self: center;
  }

  .link__wire {
    grid-area: 4 / 1;
    align-self: center;
    height: var(--space-12);
  }

  .link__tag--them {
    grid-area: 5 / 1;
    align-self: center;
  }

  .pool {
    grid-area: 6 / 1;
  }

  .link__wire::before {
    inset: 0 auto 0 50%;
    width: 1px;
    height: auto;
  }

  /* Spans the scene rather than overhanging it. The gutter at 320px is
     narrower than the overhang the wide layout uses, so anything negative
     here pushes the page sideways. */
  .link__boundary {
    left: 0;
    right: 0;
    top: 50%;
    bottom: auto;
    border-left: 0;
    border-top: 1px dashed var(--border-strong);
  }

  .link__packet {
    top: 0;
    left: 50%;
    animation-name: packet-down;
  }

  .link__packet--back {
    animation-name: packet-up;
  }

  @keyframes packet-down {
    0%,
    6% {
      top: 0%;
      opacity: 0;
    }
    10% {
      opacity: 1;
    }
    44% {
      top: 100%;
      opacity: 1;
    }
    50%,
    100% {
      top: 100%;
      opacity: 0;
    }
  }

  @keyframes packet-up {
    0%,
    56% {
      top: 100%;
      opacity: 0;
    }
    60% {
      opacity: 1;
    }
    94% {
      top: 0%;
      opacity: 1;
    }
    100% {
      top: 0%;
      opacity: 0;
    }
  }
}

/* ==========================================================================
   Division of labour
   ========================================================================== */

.handoff {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: var(--space-4);
}

.handoff__side {
  padding: var(--space-8);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-base);
}

.handoff__side--you {
  border-color: var(--accent-border);
  background: color-mix(in srgb, var(--accent) 5%, var(--surface-base));
}

.handoff__who {
  margin-bottom: var(--space-6);
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.handoff__side--you .handoff__who {
  color: var(--accent);
}

.handoff__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  font-family: var(--font-display);
  font-size: var(--text-h3);
  line-height: var(--leading-snug);
}

.negations {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4) var(--space-8);
  margin-top: var(--space-12);
  font-family: var(--font-display);
  font-size: var(--text-h3);
  color: var(--text-tertiary);
  text-align: center;
}

/* ==========================================================================
   Layer stack additions
   ========================================================================== */

/* The platform statement is longer than the heading measure allows, but must
   hold to one line wherever the container can fit it, so only the heading is
   released to the container width. */
.block--platform > :not(.headline-lg) {
  max-width: var(--measure);
}

/* The heading is about 22.6 times its size wide, so from tablet width up it
   takes whatever size fills the container, never more than a standard
   heading. The 20px leaves room for a desktop scrollbar. */
@media (min-width: 768px) {
  .block--platform .headline-lg {
    font-size: min(
      var(--text-h2),
      (min(100vw, var(--container)) - 2 * var(--gutter) - 20px) / 22.7
    );
  }
}

.stack--wide {
  max-width: calc(var(--unit) * 46);
}

.stack--wide .stack__layer {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
}

.stack__owner {
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* ==========================================================================
   Consistency diagram
   ========================================================================== */

.band {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-16);
}

.band__panel {
  padding: var(--space-8);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-base);
}

.band__panel--core {
  border-color: var(--accent-border);
}

.band__label {
  margin-bottom: var(--space-8);
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.band__panel--core .band__label {
  color: var(--accent);
}

/* An unlabelled axis on purpose: this illustrates the design goal rather than
   reporting measurements. */
.band__plot {
  position: relative;
  height: calc(var(--unit) * 6.5);
  border-bottom: 1px solid var(--border-strong);
}

.band__dot {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: 0 0 -3px -3px;
  border-radius: 50%;
  background: var(--text-tertiary);
  opacity: 0.8;
}

.band__plot--tight .band__dot {
  background: var(--accent);
}

.band__range {
  position: absolute;
  left: 41%;
  right: 41%;
  top: 0;
  bottom: 0;
  border-inline: 1px dashed var(--accent-border);
  background: color-mix(in srgb, var(--accent) 4%, transparent);
}

.band__axis {
  margin-top: var(--space-4);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}

.band__caption {
  grid-column: 1 / -1;
  max-width: 47em;
  margin: var(--space-6) auto 0;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  text-align: center;
}

/* ==========================================================================
   BPU instances
   ========================================================================== */

.instances {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-16);
}

.instance {
  display: flex;
  flex-direction: column;
  padding: var(--space-8);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.instance--core {
  border-color: var(--accent-border);
}

/* Side by side, the two cards share their rows, so the visuals and the spec
   tables line up even when a summary wraps in one card and not the other. */
@media (min-width: 901px) {
  .instances {
    row-gap: 0;
  }

  .instance {
    display: grid;
    grid-row: span 5;
    grid-template-rows: subgrid;
    row-gap: 0;
  }
}

/* Centred, not baseline aligned: the pill is a different size to the name, so
   sharing a baseline leaves it sitting visibly low. */
.instance__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-4);
}

.instance__name {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-heading);
  background-image: var(--gradient-accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.instance__for {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--accent-border);
  border-radius: var(--radius-pill);
  background: var(--accent-tint);
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* Centring the boxes is not enough: each line box carries room for
   descenders, so the pill text sat lower than the capitals of the name.
   Trimming both boxes to cap height centres the letters themselves. The name
   gets equal padding back so its clipped gradient still covers the dot of
   the i. */
@supports (text-box: trim-both cap alphabetic) {
  .instance__name,
  .instance__for {
    text-box: trim-both cap alphabetic;
  }

  .instance__name {
    padding-block: 0.2em;
  }

  .instance__for {
    padding-block: 0.8em;
  }
}

/* Side by side, the name and its pill share one line, so both scale with the
   card. The pill bottoms out at the smallest eyebrow size; below that the head
   stacks instead. The head is the container, not the card: size containment
   on the card would switch off its subgrid. */
@media (min-width: 901px) {
  .instance__head {
    container-type: inline-size;
  }

  .instance__name {
    font-size: min(var(--text-xl), 7.2cqi);
  }

  .instance__for {
    font-size: clamp(0.78125rem, 3cqi, var(--text-eyebrow));
    white-space: nowrap;
  }
}

@media (min-width: 901px) and (max-width: 1365px) {
  .instance__head {
    column-gap: var(--space-3);
  }

  .instance__for {
    padding-inline: var(--space-2);
    letter-spacing: 0.1em;
  }
}

/* Too narrow for one line: stack the pill under the name in both cards, rather
   than letting the longer pill wrap in one card only. */
@media (min-width: 901px) and (max-width: 1060px) {
  .instance__head {
    flex-direction: column;
    align-items: flex-start;
  }

  .instance__name {
    font-size: var(--text-xl);
  }

  .instance__for {
    font-size: var(--text-eyebrow);
  }
}

.instance__summary {
  margin-top: var(--space-4);
  color: var(--text-secondary);
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
}

/* ---- channel density ---- */

.instance__visual {
  margin-top: var(--space-8);
}

.density {
  position: relative;
  height: calc(var(--unit) * 7);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background-color: var(--surface-base);
  background-repeat: repeat;
  background-position: center;
}

/* Two things carry the comparison, and neither is dot count: spacing, and how
   much of the culture the array reaches at all. The 60 channels sit in one
   small array over the middle of the substrate, so nano paints into a centred
   box rather than tiling the whole panel. */
.density--nano::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--unit) * 5.5);
  height: calc(var(--unit) * 5.5);
  transform: translate(-50%, -50%);
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--accent) 70%, transparent) 1.8px,
    transparent 2.1px
  );
  background-size: 16px 16px;
  background-position: center;
}

.density--mini {
  background-image: radial-gradient(
    circle,
    color-mix(in srgb, var(--accent) 70%, transparent) 1.1px,
    transparent 1.4px
  );
  background-size: 6px 6px;
}

.density__note {
  margin-top: var(--space-3);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  text-align: center;
}

/* ---- specifications ---- */

.specs {
  container: specs / inline-size;
  margin-top: var(--space-8);
  border-top: 1px solid var(--border-subtle);
}

/* The longest label is about 8.6 times its own size wide. */
.specs__row {
  display: grid;
  grid-template-columns: calc(var(--text-eyebrow) * 9) 1fr;
  gap: var(--space-4);
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
}

/* Below this the values would wrap to three lines beside the labels, so each
   label sits above its value instead. */
@container specs (max-width: 460px) {
  .specs__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-1);
  }
}

.specs__row dt {
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.specs__row dd {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.throughput {
  margin-top: auto;
  padding-top: var(--space-8);
}

.throughput__bar {
  display: block;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-overlay);
  overflow: hidden;
}

.throughput__bar > span {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background-image: var(--gradient-accent);
}

.throughput__value {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

/* ==========================================================================
   What the numbers mean
   ========================================================================== */

.glossary {
  margin-top: var(--space-12);
  padding: var(--space-8);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
}

.glossary__title {
  margin-bottom: var(--space-6);
  font-family: var(--font-display);
  font-size: var(--text-eyebrow);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--accent);
}

.glossary__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-8);
}

.glossary__list dt {
  margin-bottom: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
}

.glossary__list dd {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* ==========================================================================
   Access
   ========================================================================== */

/* The closing block runs wider than the standard measure so the sign off line
   holds on one line; the paragraphs above it keep a readable column. The
   line is about 28 times its own size wide. */
#access .block {
  max-width: calc(var(--text-xl) * 29);
}

#access .body-lg {
  max-width: var(--measure-body);
}

.access__line {
  margin-top: var(--space-6);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  color: var(--text-primary);
}

.access__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 900px) {
  .handoff,
  .band,
  .instances {
    grid-template-columns: minmax(0, 1fr);
  }

  .glossary__list {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
  }
}

@media (max-width: 620px) {
  .handoff__side,
  .band__panel,
  .instance,
  .glossary {
    padding: var(--space-6);
  }

  .negations {
    flex-direction: column;
    gap: var(--space-3);
  }
}
