/* ==========================================================================
   CYGM components

   Depends on tokens.css and base.css. Reads semantic tokens only. No hex value
   appears in this file and none may be added.

   These are the original cygm.me treatments, restored from the <style> block in
   ../../index.html after an achromatic pass was rejected. The three that carry
   the look: 16px surfaces, 100px pill buttons, and an accent glow on hover.

   Saturated red, amber and green still mean glucose state and nothing else. No
   button, border or hover uses them as decoration, which is what makes a
   reading turning amber actually land.
   ========================================================================== */

@layer components {

/* ==========================================================================
   Sticky header
   ========================================================================== */

.masthead {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  background: color-mix(in srgb, var(--color-page) 85%, transparent);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: var(--hairline) solid var(--color-border);
}

.masthead-inner {
  display: flex;
  align-items: center;
  gap: var(--step-5);
  width: 100%;
}

.wordmark {
  display: flex;
  align-items: center;
  gap: var(--step-2);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--size-lg);
  letter-spacing: -0.03em;
  color: var(--color-text);
  margin-right: auto;
}

.wordmark:hover { color: var(--color-text); text-decoration: none; }

.wordmark-mark {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--color-accent);
  box-shadow: 0 0 12px var(--color-accent);
  flex: none;
  animation: pulse-dot 2s ease-in-out infinite;
}

.nav-list {
  display: flex;
  align-items: center;
  gap: var(--step-5);
}

.nav-list a {
  font-size: var(--size-sm);
  color: var(--color-text-muted);
}

.nav-list a:hover,
.nav-list a[aria-current='page'] { color: var(--color-text); text-decoration: none; }

/* The underline grows from the left on hover rather than appearing whole. On
   the current page it is already drawn. */
@media (min-width: 66rem) {
  .nav-list a { position: relative; }
  .nav-list a::after {
    content: '';
    position: absolute;
    bottom: -4px;
    left: 0;
    width: 0;
    height: 1.5px;
    background: var(--color-accent);
    transition: width var(--dur) var(--ease-out);
  }
  .nav-list a:hover::after,
  .nav-list a[aria-current='page']::after { width: 100%; }
}

@media (max-width: 65.999rem) {
  .nav-list a[aria-current='page'] { color: var(--color-accent); }
}

.nav-toggle {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-right: calc(var(--step-3) * -1);
  color: var(--color-text);
}

@media (min-width: 66rem) { .nav-toggle { display: none; } }

@media (max-width: 65.999rem) {
  .nav-list {
    position: fixed;
    inset: var(--nav-height) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--step-2) var(--space-gutter) var(--step-5);
    background: var(--color-surface);
    border-bottom: var(--hairline) solid var(--color-border);
    border-radius: 0 0 var(--radius-card) var(--radius-card);
    display: none;
  }
  .nav-list[data-open='true'] { display: flex; }
  .nav-list a {
    display: block;
    padding-block: var(--step-3);
    font-size: var(--size-base);
    border-bottom: var(--hairline) solid var(--color-border);
  }
}

/* ==========================================================================
   Buttons

   Two variants and no more. A page with five button treatments has no primary
   action.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--step-2);
  /* 2.75rem is the 44px floor for a touch target. Nothing tappable goes
     under it. */
  min-height: 2.75rem;
  padding: var(--step-3) var(--step-6);
  font-family: var(--font-body);
  font-size: var(--size-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  border-radius: var(--radius-button);
  border: var(--hairline) solid transparent;
  /* The spring overshoots on the way in, which is what makes the lift read as
     a physical press rather than as a fade. */
  transition: background-color var(--dur) var(--ease-spring),
              border-color var(--dur) var(--ease-spring),
              box-shadow var(--dur) var(--ease-spring),
              transform var(--dur) var(--ease-spring),
              color var(--dur) var(--ease-spring);
}

.btn:hover { text-decoration: none; }

.btn-primary {
  background: var(--color-accent);
  color: var(--color-page);
}
.btn-primary:hover {
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 8px 30px var(--color-glow-strong);
  color: var(--color-page);
}

.btn-secondary {
  background: transparent;
  color: var(--color-text);
  border-color: var(--color-border-strong);
}
.btn-secondary:hover {
  transform: translateY(-2px) scale(1.02);
  background: var(--color-hover-veil);
  border-color: var(--color-accent);
  color: var(--color-text);
}

.btn-block { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .btn:hover { transform: none; }
}

/* ==========================================================================
   Cards

   One card. Its internals reformat on its OWN width via a container query, so
   the same component is correct in a three-up rail, in a sidebar and on a
   phone without a single width media query.
   ========================================================================== */

.card {
  container-type: inline-size;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--step-3);
  padding: var(--space-card);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-card);
  transition: border-color var(--dur-slow) var(--ease-out),
              background-color var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}

/* The accent hairline that fades in across the top edge on hover. It is drawn
   as a gradient so it fades out at both ends instead of butting into the
   corners. */
.card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--color-accent), transparent);
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
  pointer-events: none;
}

a.card:hover,
.card-interactive:hover {
  background: var(--color-surface-hover);
  border-color: var(--color-border-strong);
  transform: translateY(-3px);
  text-decoration: none;
}

a.card:hover::before,
.card-interactive:hover::before { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  a.card:hover, .card-interactive:hover { transform: none; }
}

.card-title {
  font-family: var(--font-display);
  font-size: var(--size-base);
  font-weight: 600;
  color: var(--color-text);
}

.card-body { font-size: var(--size-sm); color: var(--color-text-muted); }

.card-foot {
  margin-top: auto;
  padding-top: var(--step-3);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
  border-top: var(--hairline) solid var(--color-border);
}

@container (min-width: 26rem) {
  .card { padding: var(--step-6); }
  .card-title { font-size: var(--size-lg); }
}

/* The icon sits in its own washed tile rather than floating loose beside the
   title, which is what gives a card row its rhythm. */
.card-icon {
  width: 2.625rem;
  height: 2.625rem;
  min-width: 2.625rem;
  padding: 0.6rem;
  display: grid;
  place-items: center;
  background: var(--color-glow);
  border-radius: var(--radius-sm);
  color: var(--color-accent);
  stroke-width: 1.75;
}

/* ==========================================================================
   Hero

   Left-aligned and asymmetric on a visible grid. Centring everything is the
   single fastest way for a page to read as generated.
   ========================================================================== */

.hero {
  position: relative;
  /* The bloom and the grid are both larger than the hero, so this is what stops
     them from widening the document. */
  overflow: hidden;
  padding-block: var(--step-9) var(--space-section);
}

/* The privacy disclosure earns a little weight of its own: a wide, weak bloom
   behind it and a hairline accent edge along the top. Same treatment as the hero
   bloom, at a fraction of the strength, so it reads as emphasis rather than as a
   banner. The bloom is wider than the section, so overflow is what stops it
   widening the document. */
.section-privacy {
  position: relative;
  overflow: hidden;
  border-top: var(--hairline) solid var(--color-accent);
}

.section-privacy::before {
  content: '';
  position: absolute;
  top: -30%;
  left: 50%;
  transform: translateX(-50%);
  width: min(900px, 120%);
  aspect-ratio: 1;
  background: radial-gradient(ellipse at center, var(--color-glow-strong) 0%, var(--color-glow) 35%, transparent 70%);
  opacity: 0.7;
  pointer-events: none;
  z-index: 0;
}

.section-privacy > .container { position: relative; z-index: 1; }

/* The bloom drifting behind the top right corner, and the faint measuring grid
   under the whole hero. Both are pointer-transparent decoration. */
.hero::before {
  content: '';
  position: absolute;
  top: -20%;
  right: -10%;
  width: min(600px, 90%);
  aspect-ratio: 1;
  background: radial-gradient(circle, var(--color-glow) 0%, transparent 70%);
  pointer-events: none;
  animation: float-glow 8s ease-in-out infinite;
}

.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--color-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--color-grid) 1px, transparent 1px);
  background-size: 60px 60px;
  pointer-events: none;
}

.hero > * { position: relative; }

@keyframes float-glow {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(-30px, 20px); }
}

.hero-copy { grid-column: 1 / -1; }
.hero-media { grid-column: 1 / -1; }

/* --------------------------------------------------------------------------
   Mobile hero order

   On a phone the first screen was a headline, a long paragraph and two stacked
   buttons, with the device itself not appearing until you scrolled. A product
   you cannot see is a poor first impression, so the photograph is lifted to sit
   directly under the headline.

   display: contents promotes the copy block's children into the grid so they
   can be ordered against the image, which is otherwise their sibling and cannot
   be moved in between them. Applied only here, and only on small screens.
   -------------------------------------------------------------------------- */
@media (max-width: 59.999rem) {
  .hero { padding-block: var(--step-6) var(--step-8); }

  .hero-copy { display: contents; }

  .hero-copy > *,
  .hero-media { grid-column: 1 / -1; }

  .hero-copy > .eyebrow { order: 1; }
  .hero-copy > h1       { order: 2; }
  .hero-copy > .lede    { order: 3; }
  .hero-media           { order: 4; }
  .hero-copy > .cluster { order: 5; }
  .hero-copy > .facts   { order: 6; margin-top: var(--step-5) !important; }

  .hero .grid { gap: var(--step-4); }

  /* One step down from the display size it uses on a wide screen. Each line
     saved here is a line of the photograph gained on the first screen. */
  .hero .lede { font-size: var(--size-base); line-height: 1.5; }

  /* Capped so the photograph introduces the product rather than filling the
     screen and pushing the buttons back out of reach. */
  /* Capped by width, not height. A max-height on a full-width image crops it
     rather than shrinking it, which turned the 4:3 hero into 2:1 at tablet
     widths. Limiting the box's width to what 40vh allows at 4:3 keeps the
     shape and scales the whole photograph down. */
  .hero-media {
    max-width: min(100%, calc(40vh * 4 / 3));
    margin-inline: auto;
  }
}

@media (min-width: 60rem) {
  .hero-copy { grid-column: 1 / 7; }
  .hero-media { grid-column: 8 / -1; align-self: center; }
}

/* Every hero photograph is framed to the same box whatever the source file's
   own proportions are, so a 1:1 or a 2:1 capture does not change the shape of
   the first screen from one page to the next. The crop is centred; a portrait
   source therefore keeps its middle and loses top and bottom. */
.hero-media img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  /* The radius used to be an inline style on some pages and absent on others.
     One shared value here, so every hero photograph has the same corners. */
  border-radius: var(--radius-card);
}

/* --------------------------------------------------------------------------
   A hero with no photograph.

   Stories and the thank-you page have no image to lead with, and the two-column
   hero with an empty right half reads as a missing asset. This is one column at
   the reading measure instead. A facts strip is still allowed inside it.
   -------------------------------------------------------------------------- */
.hero-solo .hero-copy { grid-column: 1 / -1; }

.hero-solo :is(h1, .lede, p) { max-width: var(--measure); }

@media (min-width: 60rem) {
  .hero-solo .hero-copy { grid-column: 1 / 9; }
}

.hero h1 { margin-bottom: var(--step-5); }

.hero .lede { margin-bottom: var(--step-6); }

/* ==========================================================================
   Fact strip

   Hard numbers, in mono, divided by rules. This is the instrument-panel
   element that does the most work per line of CSS on the whole site.
   ========================================================================== */

.facts {
  display: grid;
  /* Two or four, never three. auto-fit was fitting three across at tablet
     widths and dropping the fourth onto its own row, which reads as a mistake
     rather than as a grid. A fixed count keeps the strip balanced at every
     width, and four facts divide evenly into both. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--step-5) var(--step-4);
  padding-block: var(--step-5);
  border-block: var(--hairline) solid var(--color-border);
}

/* 34rem is the width at which four facts each still hold a two-word label.
   Below it they stack two-by-two rather than compressing. */
@media (min-width: 34rem) {
  .facts { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Inside a two-column hero the copy column is half the page, and four facts
   across it are too narrow for a value like 320x240 until the page is wide. */
@media (min-width: 60rem) and (max-width: 74.999rem) {
  .hero .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.fact-value {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  /* Scales down between a phone and a laptop, because in a hero's half-width
     column four columns of 1fr are narrow and a value like 320x240 at the full
     size ran into its neighbour. */
  font-size: clamp(var(--size-lg), 1rem + 1.6vw, 1.75rem);
  white-space: nowrap;
  color: var(--color-text);
  line-height: 1.1;
  min-width: 0;
}

.fact-label {
  display: block;
  margin-top: var(--step-2);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* ==========================================================================
   Device screenshots

   The panel is 320x240. Scaling that up with a smooth filter turns crisp
   firmware type into mush, so these render with nearest-neighbour and sit in a
   bezel that reads as hardware rather than as a browser window.
   ========================================================================== */

.screenshot {
  padding: var(--step-2);
  background: var(--color-surface-alt);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-card);
  box-shadow: 0 20px 60px var(--color-glow);
}

.screenshot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  /* 320x240 device captures. Smooth scaling turns firmware type to mush. */
  image-rendering: pixelated;
  border-radius: var(--radius-sm);
  background: var(--color-page);
}

/* Set by site.js on any figure that opens the lightbox, so the affordance only
   appears where the dialog can actually be opened. */
/* The lightbox shows both device captures and photographs, so the dialog itself
   must not impose either one's treatment. The 4:3 box and the nearest-neighbour
   scaling belong to an in-page .screenshot, not to whatever the dialog happens
   to be displaying: a portrait photo through the 4:3 box is stretched by nearly
   eighty per cent. */
#shot-dialog img {
  aspect-ratio: auto;
  image-rendering: auto;
  object-fit: contain;
  max-height: 82vh;
  width: auto;
  max-width: 100%;
}

.screenshot[role='button'],
.photo[role='button'] { cursor: zoom-in; }

.screenshot[role='button']:focus-visible,
.photo[role='button']:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.screenshot figcaption {
  margin-top: var(--step-3);
  font-size: var(--size-xs);
  color: var(--color-text-faint);
}

/* --------------------------------------------------------------------------
   Photographs

   Separate from .screenshot on purpose, and the distinction is load-bearing.
   .screenshot exists for 320x240 device captures: it forces a 4:3 box and turns
   off smoothing so firmware type stays crisp. A photograph put through it is
   squashed into 4:3 and looks terrible, which is exactly what happened to the
   portrait build photos. A photograph keeps its own shape and is scaled
   smoothly.
   -------------------------------------------------------------------------- */
.photo { margin: 0; }

.photo img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-card);
  border: var(--hairline) solid var(--color-border);
  background: var(--color-surface-alt);
}

.photo figcaption {
  margin-top: var(--step-3);
  font-size: var(--size-xs);
  color: var(--color-text-faint);
}

/* A landscape UI screenshot is read, not admired. Past this width the type in
   it is already larger than the page's own body copy. */
.photo-wide { max-width: 60rem; }

/* A tall phone capture, kept in its own scroll box rather than allowed to run
   for two screens. The frame matches .photo img so the box still reads as one
   photograph. */
.photo-scroll {
  max-height: 26rem;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  border-radius: var(--radius-card);
  border: var(--hairline) solid var(--color-border);
  background: var(--color-surface-alt);
}

/* The frame belongs to the scroll box, so the image inside must not draw a
   second one. */
.photo-scroll img { border: 0; border-radius: 0; }

/* A single phone capture is far narrower than the others, and letting it take a
   full column would blow the ribbon up past its own resolution. */
.photo-scroll-narrow { max-width: 15rem; }

/* A row of photographs. Unlike .shots this never becomes a snapping rail,
   because a build sequence is read in order rather than browsed. */
/* Columns are capped rather than stretched to 1fr. These are portrait
   photographs, so a column that grows to fill half a wide page produces an
   image roughly 750px tall and a build sequence turns into a very long scroll.
   Capping the column keeps the sequence scannable; justify-content keeps the
   capped columns together instead of drifting apart. */
.photos {
  display: grid;
  gap: var(--step-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 20rem));
  justify-content: start;
  align-items: start;
}

/* Every frame here is a 320x240 device capture, so a column that grows to fill a
   third of a wide page scales a small bitmap far past its own resolution. From
   60rem up the block below replaces this grid with a centred flex row at a fixed
   half-native width, and below 40rem it becomes a swiping rail, so this grid is
   what the 40 to 60rem band gets: capped columns rather than stretched ones,
   with justify-content keeping them together instead of drifting apart. */
.shots {
  display: grid;
  gap: var(--step-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 22rem));
  justify-content: start;
}

/* A rail that is exactly three across on a wide screen, for a set whose count
   divides by three. Six cards then read as two matched rows rather than four
   and two. */
@media (min-width: 60rem) {
  .rail-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Symmetry, on a wide screen. A screenshot row holds three at most, and a
   short row is centred under a full one: two sit centred beneath three, one
   sits dead centre. Grid cannot centre a partial last row, so from 60rem up
   the rail is a wrapping flex row with a fixed item width and a capped total
   width that is itself centred. */
@media (min-width: 60rem) {
  .shots {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    /* Half the panel's native 320px, everywhere on the site. The captures are
       sharp at that size and the lightbox is one click away for full size, so
       the page keeps its length for prose rather than for screenshots. */
    gap: var(--step-3);
    --shot-w: 10rem;
    max-width: calc(3 * 22rem + 2 * var(--step-3));
    margin-inline: auto;
  }
  /* The figures take the column they are given rather than a fixed half-native
     width: three 160px frames inside a 1150px measure left the captures
     unreadable and the row three-fifths empty. Capped so a two-column layout
     does not blow one frame up to the full measure. */
  .shots > * { flex: 1 1 0; width: auto; max-width: 22rem; }
  .shots .screenshot { padding: var(--step-1); }
  .shots figcaption { font-size: var(--size-2xs); margin-top: var(--step-2); }
}

/* .shots-compact is retained on the gallery for compatibility; the half-size
   treatment above is now the site-wide default. */

/* On a phone the screenshot grid becomes a swipeable rail rather than a very
   long column. Scroll snapping is what makes that feel deliberate. */
@media (max-width: 39.999rem) {
  .shots {
    grid-auto-flow: column;
    grid-auto-columns: 10rem;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: var(--step-3);
    scrollbar-width: none;
  }
  .shots::-webkit-scrollbar { display: none; }
  .shots > * { scroll-snap-align: start; }
}

/* The rail hides its scrollbar, so at those widths a half-visible second frame
   is the only affordance saying the row scrolls. This line says so in words. It
   exists only inside the same breakpoint the rail does; above it the shots are
   a plain grid and there is nothing to swipe. */
/* --------------------------------------------------------------------------
   The closing line under a section: a source, a caveat, a date. Mono, uppercase
   and faint, so it reads as a footnote on an instrument panel rather than as
   another sentence of body copy. Written inline on six pages before this.
   -------------------------------------------------------------------------- */
.section-note {
  margin-top: var(--step-6);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.shots-swipe { display: none; }

@media (max-width: 39.999rem) {
  .shots-swipe {
    display: block;
    margin-top: var(--step-3);
    text-align: right;
    font-family: var(--font-mono);
    font-size: var(--size-2xs);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--color-text-faint);
  }
}

/* ==========================================================================
   Data tables - the compact density tier

   A parts list or a spec table wants to be read as a table, not as a stack of
   cards. Mono, tabular figures, tight cells, hairline rules.
   ========================================================================== */

.table-wrap {
  container-type: inline-size;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.data-table {
  min-width: 34rem;
  font-size: var(--size-sm);
}


.data-table th,
.data-table td {
  padding: var(--step-3) var(--step-4);
  text-align: left;
  border-bottom: var(--hairline) solid var(--color-border);
  vertical-align: top;
}

.data-table thead th {
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent);
  background: var(--color-surface-tertiary);
  border-bottom-color: var(--color-border-strong);
}

.data-table td:first-child { color: var(--color-text); }

.data-table .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* Below the width where five columns can hold readable prose, a horizontal
   scrollbar is the wrong answer: the columns get so narrow that sentences wrap
   one word per line, and the reader has to scroll sideways to find out what
   they are looking at. Each row becomes its own block instead, with the column
   name carried by a data-label attribute on the cell.
   A cell without a data-label simply loses its label rather than breaking, so
   this degrades safely on a table that has not been marked up for it. */
@container (max-width: 34rem) {
  .data-table { min-width: 0; }
  .data-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .data-table tr {
    display: block;
    padding-block: var(--step-4);
    border-bottom: var(--hairline) solid var(--color-border);
  }
  /* Block, not grid. A grid cell makes every child of the cell a grid item,
     which turned an inline <em> or <strong> inside a value into its own row:
     the emphasised words fell onto lines of their own mid-sentence. The label
     is a block above the value instead, which is where it wanted to be at this
     width anyway. */
  .data-table td {
    display: block;
    padding: var(--step-1) 0;
    border: 0;
  }
  .data-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: var(--step-1);
    font-family: var(--font-mono);
    font-size: var(--size-2xs);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--color-text-faint);
  }
  /* The first cell is the row's own name, so it reads as a heading rather than
     as one more labelled field. */
  .data-table td:first-child {
    display: block;
    font-weight: 600;
    font-size: var(--size-base);
    margin-bottom: var(--step-2);
  }
  .data-table td:first-child::before { content: none; }
  .data-table .num { text-align: left; }
}

/* ==========================================================================
   Callouts

   The medical disclaimer is the most important text on the site and it must
   never read as a dismissible banner. It gets a left rule and the page's
   surface, not a tinted pastel box.
   ========================================================================== */

.callout {
  padding: var(--step-4) var(--step-5);
  background: var(--color-surface);
  border: 1px solid var(--color-accent-2);
  border-inline-width: 4px;
  border-radius: var(--radius-sm);
  font-size: var(--size-sm);
}

.callout-title {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text);
  margin-bottom: var(--step-2);
}

/* The safety variant is the one place a signal colour is allowed outside a
   glucose reading, because the meaning is the same: pay attention now. */
.callout-safety { border-color: var(--glucose-high); }

/* The danger variant is for the one warning on the site that describes damage
   with no way back: a full dark-orange edge rather than a left rule, and a glow
   outside it that runs red, then dark red, then away to nothing. */
.callout-danger {
  border: 2px solid var(--signal-orange);
  border-radius: var(--radius-sm);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--signal-red) 70%, transparent),
    0 0 0 7px color-mix(in srgb, var(--signal-red-deep) 60%, transparent),
    0 0 18px 10px color-mix(in srgb, var(--signal-red-deep) 40%, transparent),
    0 0 40px 18px color-mix(in srgb, var(--signal-red-deep) 15%, transparent);
}

/* ==========================================================================
   Glucose state

   The only saturated colour on the site.
   ========================================================================== */

.state-low  { color: var(--glucose-low); }
.state-ok   { color: var(--glucose-ok); }
.state-high { color: var(--glucose-high); }

.reading {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* ==========================================================================
   Breadcrumb

   Visible, not only in markup. Google's doorway policy names the absence of a
   browseable hierarchy as a marker, so this element is doing real work beyond
   navigation.
   ========================================================================== */

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--step-2);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
  padding-block: var(--step-5) 0;
}

.crumbs a { color: var(--color-text-faint); }
.crumbs a:hover { color: var(--color-text); }
.crumbs li:not(:last-child)::after { content: '/'; margin-left: var(--step-2); opacity: 0.5; }
.crumbs ol { display: contents; }
.crumbs li { display: inline-flex; align-items: center; gap: var(--step-2); }

/* ==========================================================================
   Question and answer blocks

   FAQ rich results were withdrawn from Search, so there is no snippet to win
   and no markup worth shipping. What remains is the reason to write this way at
   all: people ask questions in these words, and an answer-first paragraph is
   what gets lifted into an AI overview. Answers stay in the DOM, always.
   ========================================================================== */

.qa { border-top: var(--hairline) solid var(--color-border); }

.qa > * { padding-block: var(--step-5); }
.qa > * + * { border-top: var(--hairline) solid var(--color-border); }

.qa h3 { margin-bottom: var(--step-3); }

.qa p { font-size: var(--size-sm); max-width: var(--measure); }

/* ==========================================================================
   Stories
   ========================================================================== */

.story {
  display: flex;
  flex-direction: column;
  gap: var(--step-4);
  padding: var(--space-card);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-card);
}

.story blockquote { font-size: var(--size-base); color: var(--color-text-muted); }

.story-attrib {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--step-2) var(--step-4);
  padding-top: var(--step-3);
  border-top: var(--hairline) solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.story-name { color: var(--color-text); }

/* ==========================================================================
   Forms
   ========================================================================== */

.field { display: flex; flex-direction: column; gap: var(--step-2); }

/* A field the browser refuses is scrolled to and focused by the browser itself,
   and the sticky header can sit on top of the message it shows there. */
.field input,
.field select,
.field textarea { scroll-margin-top: calc(var(--nav-height) + var(--step-6)); }

/* The second input in a field is a secondary value for the same thing, such as
   the label for a link. */
.field input + input { margin-top: var(--step-2); }

.field label { font-size: var(--size-sm); font-weight: 600; color: var(--color-text); }

.field-help { font-size: var(--size-xs); color: var(--color-text-faint); }

.field input:not([type='checkbox']):not([type='radio']):not([type='file']),
.field select,
.field textarea {
  min-height: 2.75rem;
  padding: var(--step-3);
  background: var(--color-page);
  color: var(--color-text);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-input);
  transition: border-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
  /* 16px is the threshold below which iOS Safari zooms the viewport on focus,
     which then leaves the page zoomed after the field is dismissed. */
  font-size: 1rem;
}

.field textarea { min-height: 9rem; resize: vertical; line-height: var(--leading-body); }

.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-glow);
}

/* Consent checkboxes are large, separately labelled and never pre-ticked. The
   generous hit area is not only usability: consent has to be an affirmative
   act, and a checkbox that is hard to hit deliberately is a bad one. */
.consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--step-3);
  align-items: start;
  padding: var(--step-4);
  background: var(--color-surface);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--size-sm);
}

.consent input[type='checkbox'] {
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.15rem;
  accent-color: var(--color-accent);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.footer {
  padding-block: var(--space-section) var(--step-8);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--size-sm);
}

.footer-cols {
  display: grid;
  gap: var(--step-7);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

.footer h2 {
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
  margin-bottom: var(--step-4);
}

.footer li + li { margin-top: var(--step-3); }

.footer-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--step-4);
  margin-top: var(--step-8);
  padding-top: var(--step-5);
  border-top: var(--hairline) solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* ==========================================================================
   Preview toolbar

   Present only on the private preview. It exists so the look can be judged by
   switching a theme on a real phone at real size, which is the only way a look
   is ever actually judged. It is removed when the site is promoted.
   ========================================================================== */

.preview-bar {
  position: fixed;
  left: auto;
  right: max(var(--step-4), env(safe-area-inset-right));
  bottom: max(var(--step-4), env(safe-area-inset-bottom));
  transform: none;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: var(--step-1);
  padding: var(--step-1) var(--step-2);
  white-space: nowrap;
  background: color-mix(in srgb, var(--color-surface-high) 94%, transparent);
  backdrop-filter: blur(12px);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.preview-bar button {
  min-height: 1.75rem;
  padding: var(--step-1) var(--step-2);
  color: var(--color-text-faint);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-button);
}

.preview-bar button[aria-pressed='true'] {
  color: var(--color-page);
  background: var(--color-accent);
}

.preview-bar button:hover { color: var(--color-text); }
.preview-bar button[aria-pressed='true']:hover { color: var(--color-page); }


/* ==========================================================================
   Sequence pager and "where next"

   Rendered from the ordered page map, so the two links always name real
   neighbours. Layout only; the instrument skin sits in the unlayered sheet.
   ========================================================================== */
.pager {
  display: grid;
  gap: var(--step-4);
  margin-top: var(--space-section);
}

@media (min-width: 40rem) {
  .pager { grid-template-columns: 1fr 1fr; }
}

.pager a {
  display: block;
  min-height: 3.5rem;
  padding: var(--step-4) var(--step-5);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-card);
  color: var(--color-text);
  text-decoration: none;
}

/* With only a next link, it stays in the right-hand column so the direction is
   still read off the position and not only off the word. */
.pager-next { grid-column: 2; text-align: right; }
.pager-prev { grid-column: 1; }

@media (max-width: 39.999rem) {
  .pager-prev,
  .pager-next { grid-column: 1; text-align: left; }
}

.pager-dir {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.pager-title {
  display: block;
  margin-top: var(--step-1);
  font-size: var(--size-sm);
  font-weight: 600;
}

.related { margin-top: var(--step-6); }

.related h2 {
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.related ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--step-3);
  margin-top: var(--step-3);
  padding-left: 0;
  list-style: none;
}

.related li { max-width: none; }

.related a {
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem;
  padding: var(--step-2) var(--step-4);
  font-size: var(--size-sm);
  color: var(--color-text);
  border: var(--hairline) solid var(--color-border);
  border-radius: var(--radius-pill);
  text-decoration: none;
}

} /* @layer components */

/* On a phone the look switch keeps only the two letters, so a fixed bar never
   sits on a call to action while the page scrolls. */
@media (max-width: 30rem) {
  .preview-bar.skin-bar .skin-bar-label { display: none; }
  .preview-bar.skin-bar a { min-width: 2.25rem; text-align: center; }
}
