/* ============================================================================
   Orris Bench: the site.

   EVERY VALUE IN THIS FILE IS A TOKEN. There is no hex code, no rgba(), no px
   font size and no invented duration anywhere below. If a rule needs a color,
   a size, a space, a radius or a duration that is not in tokens.css, the answer
   is to decide what role it plays and add the role there; writing it here is a
   bug, and it is the specific bug that makes a website's amber drift half a
   step from the application's amber.

   The few bare numbers that do appear are geometry rather than design values,
   and there are four kinds of them:

     - proportions: a 1fr in a grid, a 100% width, an aspect-ratio;
     - the screen-reader-text clip, which is a 1 px box by definition;
     - the mark, drawn at 32 and therefore placed at 32, because a PNG drawn at
       one size and shown at another is soft, and the mark may be scaled only
       uniformly;
     - a 2 px pipe under a selected tab or navigation item. It is the second
       signal beside the weight change, it is not a border (--ob-border-width is
       the hairline between rows and is a different job), and the palette has no
       role for it because the application draws the same pipe from its own
       control template rather than from a brush.

   BREAKPOINTS are in em and are not tokens either. 34em, 48em and 62em are
   where this content wraps: one column to two on a card grid, a stacked header
   to a horizontal one, two product columns to three. They are properties of the
   layout rather than of the brand, they change if a template changes, and
   putting them in the token file would mean the application's palette file had
   a website's column count in it. em rather than px so that a reader who has
   raised the base text size gets the narrower layout at the width where the
   text actually needs it.

   ORDER OF THE FILE. Base, then type, then layout, then the chrome that wraps
   every page, then the components, then the front page. Later sections may rely
   on earlier ones; nothing relies on anything later.

   CONTRAST. Every pair used below is one of the pairs measured in
   docs/brand/identity.md section 4. The four light-mode near-misses are avoided
   by rule rather than by luck, and each avoidance carries the reason where it
   happens:
     1. an accent button darkens on hover instead of lightening
     2. pill and badge text on --ob-accent-soft is --ob-accent-pressed
     3. accent text sits only on --ob-surface or --ob-surface-raised
     4. every filled series mark carries a --ob-series-stroke hairline
   ============================================================================ */

/* ==========================================================================
   1. BASE
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* No font-size here on purpose. --ob-text-scale is 1rem, and the root font
     size belongs to the reader: a site that sets a pixel body size overrides a
     setting somebody changed deliberately, often because they cannot read the
     default. */
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    /* Smooth scrolling is motion that answers "where did this come from?", so
       it stays by default; a reader who has asked for less motion gets the jump
       instead. */
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background-color: var(--ob-surface);
  color: var(--ob-text);
  font-family: var(--ob-font-sans);
  font-size: var(--ob-text-body);
  line-height: var(--ob-leading-body);
  font-weight: var(--ob-weight-regular);
  /* Inter's own numerals are proportional by default. Every figure on this site
     is compared with another figure, so tabular is the resting state and the
     mono face is reserved for columns and identifiers. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

/* A product photograph or a journal image that arrives without width and height
   attributes still holds its aspect ratio rather than collapsing to nothing and
   then pushing the page down when it loads. */
img {
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

/* ==========================================================================
   2. FOCUS

   One ring, everywhere, and it is never removed. The application draws the same
   one. A site that hides the focus ring is unusable with a keyboard, and the
   reader who most needs it is the one who cannot use a pointer.

   :focus-visible rather than :focus, so a pointer click on a button does not
   leave a ring behind it; a keyboard press does.
   ========================================================================== */

:focus-visible {
  outline: var(--ob-focus-ring-width) solid var(--ob-focus-ring);
  /* The offset is what makes the ring read on an amber button as well as on the
     page: the gap between the element and the ring shows the ground through,
     which is why --ob-focus-ring-offset-color exists in the token file as the
     documented answer to what that gap is standing on. No border-radius is set
     here; an outline already follows the element's own corners, and forcing one
     would round a square element the moment it took focus. */
  outline-offset: var(--ob-focus-ring-offset);
}

/* A <main> is focused programmatically by the skip link and must not draw a
   ring for it: the reader asked to move, not to focus a region. The link has
   already told him where he is going. */
main:focus,
main:focus-visible {
  outline: none;
}

/* ==========================================================================
   3. TYPE

   Six sizes, six line heights, three weights, and nothing else. Every heading
   below sets both its size and its own line height, because a line height is a
   ratio of its own size: a heading that borrowed the title line height came out
   with a line box shorter than its glyphs, and the bottom of every p, g and y
   in a page title was cut off. That is a recorded bug in this codebase, not a
   hypothetical.
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 var(--ob-space-3);
  font-weight: var(--ob-weight-semibold);
  /* Sentence case is set in the copy, not by text-transform. A CSS transform
     changes what is drawn and not what is read out, so a screen reader gets a
     different sentence from the one on the screen. */
  text-wrap: balance;
}

h1 {
  font-size: var(--ob-text-heading);
  line-height: var(--ob-leading-heading);
  font-weight: var(--ob-weight-bold);
}

h2 {
  font-size: var(--ob-text-title);
  line-height: var(--ob-leading-title);
}

h3 {
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

h4,
h5,
h6 {
  font-size: var(--ob-text-body);
  line-height: var(--ob-leading-body);
}

p {
  margin: 0 0 var(--ob-space-4);
  max-width: var(--ob-measure);
}

p:last-child {
  margin-bottom: 0;
}

a {
  /* Accent text is only ever on --ob-surface or --ob-surface-raised (4.99 and
     5.59). On the sunken tone and on a hover wash it is 4.41 and misses AA, so
     every context below that uses one of those grounds overrides this to
     --ob-accent-pressed. Search this file for accent-pressed to see all of
     them. */
  color: var(--ob-accent);
  text-decoration-thickness: from-font;
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--ob-accent-pressed);
}

strong,
b {
  font-weight: var(--ob-weight-semibold);
}

small {
  font-size: var(--ob-text-small);
  line-height: var(--ob-leading-small);
}

code,
kbd,
samp,
pre {
  font-family: var(--ob-font-mono);
  font-size: var(--ob-text-caption);
}

hr {
  border: 0;
  border-top: var(--ob-border-width) solid var(--ob-border);
  margin: var(--ob-space-6) 0;
}

blockquote {
  margin: var(--ob-space-5) 0;
  padding-left: var(--ob-space-4);
  border-left: 2px solid var(--ob-border-strong);
  color: var(--ob-text);
}

/* THE ONE ITALIC ROLE. A value that is an estimate rather than a measurement.
   It carries an opacity as well as an angle because the angle alone is not
   noticeable on a figure, and 0.7 is the application's own .estimated role.
   Nothing else on this site is italic. */
.ob-estimated {
  font-style: italic;
  opacity: 0.7;
}

/* An identifier: a lot code, a batch number, a CAS number. Mono wherever it
   appears, column or not, because it is read character by character. */
.ob-code {
  font-family: var(--ob-font-mono);
  font-size: var(--ob-text-caption);
  letter-spacing: 0.01em;
}

.ob-code--secondary {
  display: block;
  color: var(--ob-text-muted);
}

/* A figure inline in a sentence stays in the sans face with tabular figures, so
   it does not change face mid-sentence. Columns of figures use the mono stack;
   see the table rules further down. */
.ob-figure {
  font-variant-numeric: tabular-nums;
  font-weight: var(--ob-weight-bold);
}

.ob-figure__unit {
  margin-left: var(--ob-space-1);
  font-size: var(--ob-text-caption);
  font-weight: var(--ob-weight-regular);
  color: var(--ob-text-muted);
}

.ob-note {
  max-width: var(--ob-measure);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-note--tight {
  margin-top: var(--ob-space-2);
}

/* A value the bench has not stated. Not styled as an error, because it is not
   one: it is a figure nobody has provided yet. */
.ob-unstated {
  color: var(--ob-text-muted);
  font-style: italic;
}

/* ==========================================================================
   4. UTILITIES
   ========================================================================== */

/* WordPress and WooCommerce both emit this class and expect it to work. The
   clip-path pair is the version that survives a screen reader announcing the
   text while keeping it out of the layout; display:none would remove it from
   the accessibility tree as well, which is the opposite of the intent. */
.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Anything wider than the page scrolls inside its own box. The body never
   scrolls sideways: a page that does on a phone is a page where the reader
   loses the left edge of every line. */
.ob-table-scroll {
  overflow-x: auto;
  /* A visible scroll boundary rather than an invisible one, so a table that is
     wider than the screen looks like it scrolls instead of looking cut off. */
  border-radius: var(--ob-radius-panel);
}

.ob-icon {
  width: var(--ob-icon-size);
  height: var(--ob-icon-size);
  stroke-width: var(--ob-icon-stroke-width);
  flex: none;
}

/* ==========================================================================
   5. THE SKIP LINK

   Off screen until focused, then a real control on a raised surface with the
   ring around it. It is the first focusable element on every page.
   ========================================================================== */

.ob-skip-link {
  position: absolute;
  top: var(--ob-space-2);
  left: var(--ob-space-2);
  z-index: 100;
  padding: var(--ob-button-padding);
  background-color: var(--ob-surface-raised);
  color: var(--ob-accent);
  border: var(--ob-border-width) solid var(--ob-border-field);
  border-radius: var(--ob-radius-control);
  box-shadow: var(--ob-shadow-card);
  /* Moved rather than hidden: a display:none link is not focusable, and a link
     that cannot be focused is not a skip link. */
  transform: translateY(calc(-100% - var(--ob-space-4)));
  transition: transform var(--ob-motion-feedback) var(--ob-ease-out);
}

.ob-skip-link:focus {
  transform: translateY(0);
}

/* ==========================================================================
   6. LAYOUT
   ========================================================================== */

.ob-main {
  display: block;
  max-width: var(--ob-page-max);
  margin: 0 auto;
  padding: var(--ob-space-6) var(--ob-space-4) var(--ob-space-7);
}

@media (min-width: 48em) {
  .ob-main {
    padding-left: var(--ob-space-5);
    padding-right: var(--ob-space-5);
  }
}

.ob-page-header {
  margin-bottom: var(--ob-space-6);
}

.ob-page-title {
  margin-bottom: var(--ob-space-2);
}

.ob-page-lede {
  max-width: var(--ob-measure);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-page-count {
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* ==========================================================================
   7. THE BANNER

   The mark, the name, the navigation, and a cart link when the cart has
   something in it. Not sticky: a header that follows the reader down a product
   page takes a strip of a phone screen away from the ingredient list.
   ========================================================================== */

.ob-banner {
  background-color: var(--ob-surface-raised);
  border-bottom: var(--ob-border-width) solid var(--ob-border);
}

.ob-banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ob-gap);
  max-width: var(--ob-page-max);
  margin: 0 auto;
  padding: var(--ob-space-3) var(--ob-space-4);
}

@media (min-width: 48em) {
  .ob-banner__inner {
    padding-left: var(--ob-space-5);
    padding-right: var(--ob-space-5);
  }
}

/* --------------------------------------------------------------------------
   THE LOCKUP.

   The mark, then the name in Inter SemiBold, with the gap equal to the mark's
   clear space and the name's cap height matched to the ring's outer diameter.

   The arithmetic, because it is the one place on this site where a size is
   derived rather than chosen. The ring's outer diameter is 0.48 of the tile, so
   at a 32 px mark it is 15.4 px. Inter's cap height is about 0.727 of its em,
   so the name needs a font size of about 21 px to match it. --ob-text-title at
   a 16 px base is 20.6 px, which is that size to within half a pixel, so the
   token is used rather than a new number being invented. The clear space is a
   quarter of the tile, which at 32 px is 8 px, which is --ob-space-2.
   -------------------------------------------------------------------------- */

.ob-lockup {
  display: inline-flex;
  align-items: center;
  gap: var(--ob-space-2);
  margin-right: auto;
  color: var(--ob-text);
  text-decoration: none;
  font-size: var(--ob-text-title);
  line-height: var(--ob-leading-title);
  font-weight: var(--ob-weight-semibold);
}

.ob-lockup:hover {
  color: var(--ob-text);
}

.ob-lockup__mark {
  /* Fixed at the size it was drawn at. The mark may be scaled uniformly and
     never stretched, and a PNG drawn at 32 shown at 33 is soft. */
  width: 32px;
  height: 32px;
  border-radius: 0;
}

.ob-lockup__name {
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   THE NAVIGATION.

   Open at every width by default. It collapses behind the toggle only while a
   visible toggle exists, which is what makes a page with no JavaScript a page
   with an open navigation rather than a hidden one. navigation.js removes the
   button's hidden attribute; until it does, the :not([hidden]) selector below
   does not match and nothing is collapsed.
   -------------------------------------------------------------------------- */

.ob-nav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ob-space-1) var(--ob-space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ob-nav__list a {
  display: inline-block;
  padding: var(--ob-space-2) 0;
  /* A tap target on a phone is 44 by 44; the padding above plus the line box
     clears that, and the min-height states it rather than leaving it to
     arithmetic that a font change could break. */
  min-height: var(--ob-touch-min);
  color: var(--ob-text);
  text-decoration: none;
  font-size: var(--ob-text-body);
}

.ob-nav__list a:hover {
  color: var(--ob-accent-pressed);
  text-decoration: underline;
}

/* The page the reader is on. Weight and a rule, not color alone: color is never
   the only signal. */
.ob-nav__list .current-menu-item > a,
.ob-nav__list .current_page_item > a {
  font-weight: var(--ob-weight-semibold);
  box-shadow: inset 0 -2px 0 0 var(--ob-accent);
}

.ob-nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--ob-space-2);
  min-height: var(--ob-touch-min);
  padding: var(--ob-button-padding);
  background-color: transparent;
  color: var(--ob-text);
  border: var(--ob-border-width) solid var(--ob-border-field);
  border-radius: var(--ob-radius-control);
  cursor: pointer;
}

.ob-nav-toggle:hover {
  background-color: var(--ob-hover);
}

.ob-nav-toggle[aria-expanded="false"] .ob-nav-toggle__close,
.ob-nav-toggle[aria-expanded="true"] .ob-nav-toggle__open {
  display: none;
}

@media (min-width: 48em) {
  /* Wide enough for the whole navigation to sit on one line beside the lockup,
     so the toggle is not needed at all. */
  .ob-nav-toggle {
    display: none;
  }
}

@media (max-width: 47.99em) {
  .ob-nav-toggle:not([hidden]) + .ob-nav {
    display: none;
  }

  .ob-nav-toggle:not([hidden])[aria-expanded="true"] + .ob-nav {
    display: block;
    /* Full width on its own row under the header. flex-basis 100% is what makes
       the wrapped flex row break here. */
    flex-basis: 100%;
  }

  .ob-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .ob-nav__list li {
    border-top: var(--ob-border-width) solid var(--ob-border);
  }

  .ob-nav__list a {
    display: flex;
    align-items: center;
  }
}

.ob-cart-link {
  padding: var(--ob-space-2) var(--ob-space-3);
  min-height: var(--ob-touch-min);
  display: inline-flex;
  align-items: center;
  background-color: var(--ob-accent-soft);
  /* Rule 2 of the near-misses: text on --ob-accent-soft is --ob-accent-pressed
     (6.09) and never --ob-accent (4.45). */
  color: var(--ob-accent-pressed);
  border-radius: var(--ob-radius-pill);
  text-decoration: none;
  font-size: var(--ob-text-caption);
  font-weight: var(--ob-weight-semibold);
}

.ob-cart-link:hover {
  color: var(--ob-accent-pressed);
  text-decoration: underline;
}

/* ==========================================================================
   8. THE FOOTER

   On the sunken tone, which is why every link in it is --ob-accent-pressed:
   --ob-accent on --ob-surface-sunken is 4.41 and misses AA.
   ========================================================================== */

.ob-contentinfo {
  background-color: var(--ob-surface-sunken);
  border-top: var(--ob-border-width) solid var(--ob-border);
}

.ob-contentinfo__inner {
  display: grid;
  gap: var(--ob-space-5);
  max-width: var(--ob-page-max);
  margin: 0 auto;
  padding: var(--ob-space-6) var(--ob-space-4);
}

@media (min-width: 48em) {
  .ob-contentinfo__inner {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    padding-left: var(--ob-space-5);
    padding-right: var(--ob-space-5);
  }

  .ob-contentinfo__legal {
    grid-column: 1 / -1;
  }
}

.ob-contentinfo a {
  color: var(--ob-accent-pressed);
}

.ob-contentinfo__line {
  margin-top: var(--ob-space-3);
  margin-bottom: 0;
  max-width: var(--ob-measure-narrow);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-footer-nav__list {
  display: flex;
  flex-direction: column;
  gap: var(--ob-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--ob-text-caption);
}

.ob-footer-nav__list a {
  display: inline-flex;
  align-items: center;
  min-height: var(--ob-hit-min);
}

.ob-contentinfo__legal {
  margin: 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-small);
  line-height: var(--ob-leading-small);
}

/* ==========================================================================
   9. BUTTONS

   One primary action per screen, filled with the accent. Everything else is
   quiet: an outline on the page ground.
   ========================================================================== */

.ob-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ob-space-2);
  min-height: var(--ob-touch-min);
  padding: var(--ob-button-padding);
  border: var(--ob-border-width) solid transparent;
  border-radius: var(--ob-radius-control);
  font-size: var(--ob-text-body);
  font-weight: var(--ob-weight-semibold);
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--ob-motion-feedback) var(--ob-ease-out),
    color var(--ob-motion-feedback) var(--ob-ease-out),
    border-color var(--ob-motion-feedback) var(--ob-ease-out);
}

.ob-button--primary {
  background-color: var(--ob-accent);
  color: var(--ob-on-accent);
}

/* THE HOVER DARKENS. --ob-on-accent on --ob-accent-hover is 4.04:1 and fails
   AA, and the application has the same gap in its own pointer-over brush. On
   this site an accent button reaches for --ob-accent-pressed instead, which is
   7.48:1 with cream on it. --ob-accent-hover is for borders and icon strokes,
   never for a surface with text on it. */
.ob-button--primary:hover,
.ob-button--primary:focus-visible {
  background-color: var(--ob-accent-pressed);
  color: var(--ob-on-accent);
}

.ob-button--primary:active {
  background-color: var(--ob-accent-pressed);
}

.ob-button--quiet {
  background-color: transparent;
  color: var(--ob-text);
  border-color: var(--ob-border-field);
}

.ob-button--quiet:hover {
  background-color: var(--ob-hover);
  /* The row's ground changed, so the text on it changes too: accent text on a
     hover wash is 4.41. A link that only changes its background is the third
     near-miss in the palette, and it is the easiest one to walk into. */
  color: var(--ob-accent-pressed);
  border-color: var(--ob-accent-hover);
}

.ob-button[disabled],
.ob-button[aria-disabled="true"] {
  background-color: var(--ob-disabled);
  color: var(--ob-disabled-text);
  border-color: var(--ob-border);
  cursor: not-allowed;
  /* At 2.45:1 this is below AA on purpose: WCAG 1.4.3 exempts a disabled
     control, and the house rule is that a disabled control explains itself in a
     tooltip. A disabled control with no title attribute is a bug, not a style. */
}

/* ==========================================================================
   10. FORMS

   A control's boundary is --ob-border-field, which is the only tone in the
   palette that clears the 3:1 WCAG 1.4.11 wants of the thing that identifies a
   control against every surface in both themes. --ob-border at 1.38:1 is a
   hairline between rows and is not enough to say "this is a field".
   ========================================================================== */

input[type="text"],
input[type="email"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="tel"],
input[type="date"],
textarea,
select {
  width: 100%;
  min-height: var(--ob-control-min-height);
  padding: var(--ob-field-padding);
  background-color: var(--ob-surface-raised);
  color: var(--ob-text);
  border: var(--ob-border-width) solid var(--ob-border-field);
  border-radius: var(--ob-radius-control);
}

textarea {
  min-height: calc(var(--ob-control-min-height) * 3);
  line-height: var(--ob-leading-body);
  resize: vertical;
}

input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  /* The bottom edge thickens and the other three do not, so the text inside
     does not shift by a pixel when the field is focused. That is the reason the
     application's focused border thickness is 1,1,1,2. */
  border-width: var(--ob-border-width-focus);
  border-color: var(--ob-accent);
}

label {
  display: block;
  margin-bottom: var(--ob-space-1);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* The search form, used by the 404 page, the search results page and anywhere
   else get_search_form() is called. */
.ob-search {
  max-width: var(--ob-measure-narrow);
}

.ob-search__row {
  display: flex;
  gap: var(--ob-space-2);
}

.ob-search__submit {
  flex: none;
}

/* ==========================================================================
   11. TABLES

   Deliberately quiet: no vertical rules, a hairline between rows, and a header
   that recedes. That is the ledger grid from the application, and the reason is
   that this is where the reader spends time rather than a thing to be admired.
   ========================================================================== */

.ob-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ob-text-body);
}

.ob-table__caption {
  caption-side: top;
  text-align: left;
  padding-bottom: var(--ob-space-2);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
  max-width: var(--ob-measure);
}

.ob-table thead th {
  padding: var(--ob-table-header-padding);
  background-color: var(--ob-surface-sunken);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  font-weight: var(--ob-weight-semibold);
  text-align: left;
  /* The one border that has to be seen, so it is the strong one rather than the
     hairline. */
  border-bottom: var(--ob-border-width) solid var(--ob-border-strong);
}

.ob-table tbody th,
.ob-table tbody td {
  padding: var(--ob-cell-padding);
  border-bottom: var(--ob-border-width) solid var(--ob-border);
  vertical-align: baseline;
  text-align: left;
}

.ob-table tbody th {
  font-weight: var(--ob-weight-regular);
}

.ob-table tbody tr:last-child th,
.ob-table tbody tr:last-child td {
  border-bottom: 0;
}

/* A column of figures. Mono, right aligned, so 0.008 and 0.080 are not the same
   length and the decimal points line up down the page. */
.ob-table__figure {
  font-family: var(--ob-font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.ob-table thead .ob-table__figure {
  font-family: var(--ob-font-sans);
}

/* ==========================================================================
   12. STATUS: VERDICTS AND BADGES

   Always a color, a glyph and a word together. --ob-caution and --ob-accent
   differ by a hair, so amber alone cannot tell "this is the primary action"
   apart from "this needs attention", and the three glyphs are three different
   outlines rather than one shape in three colors.
   ========================================================================== */

.ob-verdict {
  display: inline-flex;
  align-items: center;
  gap: var(--ob-space-2);
  padding: var(--ob-pill-padding);
  border-radius: var(--ob-radius-pill);
  font-size: var(--ob-text-caption);
  font-weight: var(--ob-weight-semibold);
  line-height: var(--ob-leading-caption);
}

.ob-verdict--good {
  background-color: var(--ob-good-soft);
  color: var(--ob-good);
}

.ob-verdict--caution {
  background-color: var(--ob-caution-soft);
  color: var(--ob-caution);
}

.ob-verdict--critical {
  background-color: var(--ob-critical-soft);
  color: var(--ob-critical);
}

.ob-verdict-line {
  margin-bottom: var(--ob-space-3);
}

.ob-badge {
  display: inline-flex;
  align-items: center;
  padding: var(--ob-pill-padding);
  border-radius: var(--ob-radius-pill);
  font-size: var(--ob-text-small);
  font-weight: var(--ob-weight-semibold);
  line-height: var(--ob-leading-small);
  background-color: var(--ob-accent-soft);
  color: var(--ob-accent-pressed);
}

/* The coverage line under a verdict. It is muted but it is not small print:
   this is the sentence that says what the verdict rests on, and a reader who
   skips it has read half the claim. */
.ob-coverage {
  margin-top: var(--ob-space-2);
  max-width: var(--ob-measure);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* ==========================================================================
   13. PROSE

   Editor content: a journal entry, a page, a product description. Capped at the
   measure, which is the whole job.
   ========================================================================== */

.ob-prose {
  max-width: var(--ob-measure);
}

.ob-prose--narrow {
  max-width: var(--ob-measure-narrow);
}

.ob-prose > * + * {
  margin-top: var(--ob-space-4);
}

.ob-prose h2,
.ob-prose h3 {
  margin-top: var(--ob-space-6);
}

.ob-prose ul,
.ob-prose ol {
  padding-left: var(--ob-space-5);
}

.ob-prose li + li {
  margin-top: var(--ob-space-2);
}

.ob-prose img,
.ob-prose figure {
  margin-left: 0;
  margin-right: 0;
  border-radius: var(--ob-radius-panel);
}

.ob-figcaption,
.ob-prose figcaption {
  margin-top: var(--ob-space-2);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-prose table {
  width: 100%;
  border-collapse: collapse;
}

.ob-prose th,
.ob-prose td {
  padding: var(--ob-cell-padding);
  border-bottom: var(--ob-border-width) solid var(--ob-border);
  text-align: left;
}

/* ==========================================================================
   14. ENTRIES
   ========================================================================== */

.ob-entry-list {
  display: grid;
  gap: var(--ob-space-6);
}

.ob-entry--summary {
  padding-bottom: var(--ob-space-6);
  border-bottom: var(--ob-border-width) solid var(--ob-border);
}

.ob-entry-list > .ob-entry--summary:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.ob-entry__title {
  margin-bottom: var(--ob-space-1);
}

.ob-entry--full .ob-entry__title {
  font-size: var(--ob-text-heading);
  line-height: var(--ob-leading-heading);
  font-weight: var(--ob-weight-bold);
}

.ob-entry__title a {
  color: var(--ob-text);
  text-decoration: none;
}

.ob-entry__title a:hover {
  color: var(--ob-accent-pressed);
  text-decoration: underline;
}

.ob-entry__meta {
  margin-bottom: var(--ob-space-3);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-entry__excerpt {
  max-width: var(--ob-measure);
}

.ob-entry__more a {
  display: inline-flex;
  align-items: center;
  gap: var(--ob-space-1);
  font-weight: var(--ob-weight-semibold);
  text-decoration: none;
}

.ob-entry__more a:hover {
  text-decoration: underline;
}

.ob-entry__figure {
  margin: var(--ob-space-5) 0;
}

.ob-entry__figure img {
  border-radius: var(--ob-radius-panel);
}

/* Pagination and post navigation, both of which WordPress marks up as a nav
   with a screen-reader heading inside it. */
.ob-pagination,
.nav-links {
  margin-top: var(--ob-space-6);
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ob-space-2);
}

.page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--ob-touch-min);
  min-height: var(--ob-touch-min);
  padding: 0 var(--ob-space-3);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-control);
  text-decoration: none;
}

.page-numbers:hover {
  background-color: var(--ob-hover);
  color: var(--ob-accent-pressed);
}

.page-numbers.current {
  background-color: var(--ob-selected);
  border-color: var(--ob-border-strong);
  /* Selected carries weight as well as color, because color is never a state on
     its own. */
  font-weight: var(--ob-weight-semibold);
  color: var(--ob-text);
}

.ob-postnav {
  margin-top: var(--ob-space-7);
  padding-top: var(--ob-space-5);
  border-top: var(--ob-border-width) solid var(--ob-border);
}

.ob-postnav .nav-links {
  display: grid;
  gap: var(--ob-space-4);
}

@media (min-width: 48em) {
  .ob-postnav .nav-links {
    grid-template-columns: 1fr 1fr;
  }
}

.ob-postnav a {
  display: block;
  padding: var(--ob-space-4);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  text-decoration: none;
  color: var(--ob-text);
}

.ob-postnav a:hover {
  border-color: var(--ob-accent-hover);
}

.ob-postnav__label {
  display: block;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-postnav__title {
  display: block;
  margin-top: var(--ob-space-1);
  font-weight: var(--ob-weight-semibold);
}

/* ==========================================================================
   15. EMPTY STATES

   An empty state on this site is a card with a sentence that says what is
   empty, what will make something appear, and where it will appear. It is
   deliberately not centered in a large space with an illustration.
   ========================================================================== */

.ob-empty {
  max-width: var(--ob-measure);
  padding: var(--ob-card-padding);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  box-shadow: var(--ob-shadow-card);
}

.ob-empty > * + * {
  margin-top: var(--ob-space-4);
}

.ob-empty__lede {
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-empty__subheading {
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-empty__routes {
  margin: 0;
  padding-left: var(--ob-space-5);
}

.ob-empty__routes li + li {
  margin-top: var(--ob-space-2);
}

/* ==========================================================================
   16. COMMENTS
   ========================================================================== */

.ob-comments {
  max-width: var(--ob-measure);
  margin-top: var(--ob-space-7);
  padding-top: var(--ob-space-5);
  border-top: var(--ob-border-width) solid var(--ob-border);
}

.ob-comments__list,
.ob-comments__list .children {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ob-comments__list .children {
  margin-top: var(--ob-space-4);
  padding-left: var(--ob-space-4);
  border-left: var(--ob-border-width) solid var(--ob-border);
}

.ob-comments__list .comment-body {
  padding: var(--ob-space-4) 0;
  border-bottom: var(--ob-border-width) solid var(--ob-border);
}

.ob-comments__list .comment-meta {
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

.ob-comments__subheading {
  margin-top: var(--ob-space-5);
}

.comment-form-comment textarea,
.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"] {
  max-width: var(--ob-measure-narrow);
}

.comment-form p + p {
  margin-top: var(--ob-space-4);
}

/* ==========================================================================
   17. THE FRONT PAGE
   ========================================================================== */

.ob-section {
  padding-top: var(--ob-space-7);
}

.ob-section:first-child {
  padding-top: 0;
}

.ob-section + .ob-section {
  border-top: var(--ob-border-width) solid var(--ob-border);
}

.ob-section__heading {
  margin-bottom: var(--ob-space-3);
}

.ob-section__lede {
  max-width: var(--ob-measure);
  margin-bottom: var(--ob-space-5);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-section__more {
  margin-top: var(--ob-space-5);
}

/* --------------------------------------------------------------------------
   THE HERO.

   The only thing this class does to type is raise --ob-text-scale. Every size
   inside it is still --ob-text-heading, --ob-text-body and the rest, so the
   whole scale moves together and the site does not grow a seventh ratio that
   nothing else uses. That is the rule in identity.md section 5, and it is
   TextScale's own argument applied to the web: one knob moves the system and
   preserves every relationship.
   -------------------------------------------------------------------------- */

.ob-hero {
  --ob-text-scale: 1.15rem;
  padding-bottom: var(--ob-space-6);
}

@media (min-width: 48em) {
  .ob-hero {
    --ob-text-scale: 1.35rem;
  }
}

.ob-hero__title {
  max-width: var(--ob-measure-narrow);
}

.ob-hero__lede {
  max-width: var(--ob-measure);
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-hero__body {
  /* Back to the page's own scale: the hero's larger setting is for the title
     and the lede, and a whole paragraph at 1.35rem is a paragraph nobody
     finishes. */
  --ob-text-scale: 1rem;
  max-width: var(--ob-measure);
  color: var(--ob-text-muted);
}

.ob-hero__actions {
  --ob-text-scale: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: var(--ob-space-3);
  margin-top: var(--ob-space-5);
}

/* --------------------------------------------------------------------------
   DEFINITION LISTS: what is on every product page.
   -------------------------------------------------------------------------- */

.ob-defs {
  display: grid;
  gap: var(--ob-space-5);
  margin: 0;
}

@media (min-width: 48em) {
  .ob-defs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ob-space-5) var(--ob-space-6);
  }
}

.ob-defs__row {
  padding: var(--ob-card-padding);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  box-shadow: var(--ob-shadow-card);
}

.ob-defs__term {
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
  font-weight: var(--ob-weight-semibold);
}

.ob-defs__value {
  margin: var(--ob-space-2) 0 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* --------------------------------------------------------------------------
   REFUSALS.

   A list with a rule between the items rather than bullets. Each one is a
   sentence, and a bullet in front of a sentence that long is noise.
   -------------------------------------------------------------------------- */

.ob-refusals {
  max-width: var(--ob-measure);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ob-refusals__item {
  padding: var(--ob-space-4) 0;
  border-bottom: var(--ob-border-width) solid var(--ob-border);
}

.ob-refusals__item:first-child {
  padding-top: 0;
}

.ob-refusals__item:last-child {
  border-bottom: 0;
}

.ob-entry-list--compact {
  gap: var(--ob-space-5);
}

/* --------------------------------------------------------------------------
   THE SOFTWARE FRONT PAGE.

   Ported from web/mockup/index.html, which is the settled design and the only
   description of what this page is meant to look like. The theme's markup was
   written first and its class names are not the mockup's, so everything below
   is a translation rather than a copy. Where the two disagree, the mockup wins
   on appearance and the markup wins on structure: no rule here asks for an
   element that the seven template parts do not print.

   WHAT THIS FIXES. The page rendered as a wall of text, because the rules it
   had were written from the class names alone. The mockup holds no photograph
   either; all of its visual interest comes from six things, and all six are
   below:

     1. sections separated above AND below, so the rule between two of them
        reads as a boundary rather than as an underline on the last paragraph;
     2. multi-column card grids that collapse to one column on a phone;
     3. cards with a hairline, a raised ground and the one shadow;
     4. body copy capped at the measure, never running the full 76rem;
     5. the inline SVG icons given a tinted well and a real size, which is the
        only place on the page where the accent appears as a shape;
     6. a type hierarchy in which a section heading is visibly a section
        heading and not a slightly heavier paragraph.

   ORDER. The page shell, then the sections in the order front-page.php calls
   them, then the breakpoints.
   -------------------------------------------------------------------------- */

/* The mockup's page wrapper carries horizontal padding and no vertical padding
   at all: every section states the space above and below itself, so the rhythm
   of the page is one number rather than two that have to be added together.
   .ob-main sets a top padding for every other template on the site, so the
   front page hands it back and lets its first section say it instead. */
.ob-main--front {
  padding-top: 0;
}

.ob-main--front > .ob-section:first-child {
  padding-top: var(--ob-space-7);
}

/* BOTH EDGES, AT THE SECTIONING STEP. The section had padding above and none
   below, so the divider sat hard against the bottom of the last paragraph and
   the page came out as one column of prose with occasional lines drawn through
   it. This single declaration is most of the difference between that and a page
   of sections. */
.ob-section {
  padding-bottom: var(--ob-space-7);
}

/* A section heading was arriving at the title step in SemiBold, which is one
   step above the subhead a card's own name uses: there was no visible level
   between "What Orris Bench does" and "IFRA compliance". The mockup puts it at
   the heading step in Bold. Its own line height rather than the title's; see
   section 3 for the recorded bug that rule exists to prevent. */
.ob-section__heading {
  font-size: var(--ob-text-heading);
  line-height: var(--ob-leading-heading);
  font-weight: var(--ob-weight-bold);
  /* Tracking is a function of size: type this large needs less of it than body
     copy set from the same face. The eyebrow below is the only other place on
     the page that touches it. */
  letter-spacing: -0.01em;
}

/* --------------------------------------------------------------------------
   THE HERO.

   .ob-hero raises --ob-text-scale in section 17, and the intent stated there is
   that every size inside it follows. It did not, and the headline was arriving
   at exactly the size of every section heading on the page: 1.71rem against the
   1.71rem of an h2, which is a hero with no hierarchy at all.

   WHY IT DID NOT WORK, because it is worth knowing before anybody tries the
   same trick again. A custom property is substituted where it is DECLARED, not
   where it is used. tokens.css declares

       --ob-text-heading: calc(var(--ob-text-scale) * 1.71)

   on :root, so it computes there, against the root's 1rem, to calc(1rem * 1.71)
   and inherits down as that. Raising --ob-text-scale further down the tree
   arrives too late: the heading step has already been resolved. The mockup does
   not hit this because its hero title is written as calc() against the scale
   directly rather than through the derived step.

   The fix is to re-derive the one step the hero actually consumes, at the hero,
   where --ob-text-scale is the raised one. THE 1.71 IS COPIED FROM tokens.css
   SECTION 8 AND THE TWO HAVE TO CHANGE TOGETHER. It is the only ratio repeated
   in this file, and it is repeated because the alternative is a literal font
   size on the largest piece of type on the site. Every other step inside the
   hero is consumed by an element that puts the scale back to 1rem first, so
   this is the only one that has to be re-derived.
   -------------------------------------------------------------------------- */

.ob-hero {
  --ob-text-heading: calc(var(--ob-text-scale) * 1.71);
}

/* text-transform, deliberately, and this is the one place it is allowed. The
   house rule in section 3 is that sentence case is set in the copy so a screen
   reader is read the same sentence that is on the screen. That rule is about
   headings. This is a four-word label above the headline, the copy in hero.php
   stays in sentence case, and the transform changes only what is drawn.

   The scale drops back to the page's own, as .ob-hero__body and
   .ob-hero__actions already do: the small step of a 1.35rem scale is the size
   of body text, which is not what a label above a headline is for. */
.ob-hero__eyebrow {
  --ob-text-scale: 1rem;
  margin-bottom: var(--ob-space-3);
  /* Accent text on the page ground clears AA at 4.99, but this is set at the
     small step, and --ob-accent-pressed is the palette's answer to accent
     colored text that has to work small. */
  color: var(--ob-accent-pressed);
  font-size: var(--ob-text-small);
  line-height: var(--ob-leading-small);
  font-weight: var(--ob-weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* The mockup gives the headline the left column of a two-column hero, which
   lands at a little over half the page and wraps the sentence onto two lines.
   There is no second column in this markup (see the report's note about the
   application window), so the measure is derived from the page width rather
   than from a ch count: 68ch at the hero's own text size is wider than the page
   is, and a headline that runs the full 76rem arrives as one thin line. */
.ob-hero__title {
  max-width: calc(var(--ob-page-max) / 2);
  letter-spacing: -0.02em;
}

/* Section 17 already drops this back to the page's own text scale, because a
   whole paragraph at the hero size is a paragraph nobody finishes. It is still
   one step above body copy, which is what makes it read as the hero's sentence
   rather than as the first paragraph of the page. */
.ob-hero__body {
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
}

.ob-hero__meta {
  --ob-text-scale: 1rem;
  max-width: var(--ob-measure);
  margin-top: var(--ob-space-4);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* THE QUALIFICATION, ON AN ID RATHER THAN A CLASS. hero.php gives this span no
   class of its own; the id is a contract with the primary button's
   aria-describedby, so it is what there is to hook. It is the sentence that
   stops a reader clicking a download that does not exist, so it carries weight
   and the accent while the rest of the line stays muted. */
#ob-hero-soon {
  color: var(--ob-accent-pressed);
  font-weight: var(--ob-weight-semibold);
}

/* The other two operating systems, written into this span by the download
   script. Nothing but the wrap: an operating system name broken across two
   lines reads as two words. */
[data-os-alt] a {
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   WHAT IT DOES: the nine capability cards.

   The grid was auto-fit with a 16rem floor, which at the page maximum resolves
   to four columns and leaves nine cards as four, four and one. Explicit column
   counts instead, so the last row is never a single stranded card and the
   collapse happens where the content needs it rather than where the arithmetic
   lands.
   -------------------------------------------------------------------------- */

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

.ob-feature {
  padding: var(--ob-card-padding);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  box-shadow: var(--ob-shadow-card);
}

/* THE ICON WELL, WHICH IS THE PICTURE. The glyphs were being drawn at 18 px in
   the accent on the card's own ground, which is to say they were invisible. In
   the mockup each one sits in a tinted disc, and nine of those down a grid are
   the only color on the page below the hero. --ob-icon-well-size and
   --ob-radius-icon-well are in the token file for exactly this shape.

   The stroke is --ob-accent-pressed and not --ob-accent: rule 2 of the four
   near-misses is that anything sitting on --ob-accent-soft takes the pressed
   tone, 6.09 against 4.45. The svg carries stroke="currentColor", so setting
   color on the well is what colors the glyph. */
.ob-feature__icon {
  display: grid;
  place-items: center;
  width: var(--ob-icon-well-size);
  height: var(--ob-icon-well-size);
  margin-bottom: var(--ob-space-3);
  background-color: var(--ob-accent-soft);
  border-radius: var(--ob-radius-icon-well);
  color: var(--ob-accent-pressed);
}

.ob-feature__name {
  margin-bottom: var(--ob-space-2);
  font-size: var(--ob-text-subhead);
  line-height: var(--ob-leading-subhead);
  font-weight: var(--ob-weight-bold);
}

/* The card is already the measure, so the paragraph does not need one, and it
   is set at the caption step: nine descriptions at body size is the wall of
   text the owner was reading. Muted, because the card's name is the thing being
   scanned and the sentence is what is read once a name has caught. */
.ob-feature__desc {
  max-width: none;
  margin: 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* The trailing aside about bringing existing data across. The mockup sets its
   one closing aside on the sunken tone inside a bounded box, and that treatment
   is what stops this reading as a tenth card that lost its border. */
.ob-section__more {
  max-width: var(--ob-measure-wide);
  margin-top: var(--ob-space-6);
  padding: var(--ob-space-5);
  background-color: var(--ob-surface-sunken);
  border-radius: var(--ob-radius-panel);
}

/* Rule 3 of the near-misses: --ob-accent on --ob-surface-sunken is 4.41 and
   misses AA, so a link standing on the sunken tone takes the pressed tone. The
   footer does the same thing for the same reason. */
.ob-section__more a {
  color: var(--ob-accent-pressed);
}

/* --------------------------------------------------------------------------
   HOW IT WORKS: the six steps.

   One column at the measure, as the mockup has it, with the number in a disc to
   the left of the text. It was a three-column grid of bordered blocks, which
   broke the one thing this section exists to say: that the steps happen in an
   order and each one reads from the one above it. An order is read down, not
   across.

   The numbers come from a counter rather than from the markup, so a step can be
   added, removed or moved in how-it-works.php without anything being
   renumbered by hand. The <ol> is what tells a screen reader this is a
   sequence; the counter is only what draws it.
   -------------------------------------------------------------------------- */

.ob-steps {
  counter-reset: ob-step;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ob-space-4);
  max-width: var(--ob-measure);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ob-step {
  counter-increment: ob-step;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--ob-space-4);
  align-items: start;
}

/* The same disc as the feature icon well, in the same accent pair, because it
   is the same object doing the same job: a small tinted shape that gives the
   eye somewhere to land between six paragraphs. Round rather than 9 px, so that
   a number reads as a step and a glyph reads as a capability. Mono, because the
   figure is an ordinal and every figure in the application is set in mono. */
.ob-step__num {
  display: grid;
  place-items: center;
  width: var(--ob-icon-well-size);
  height: var(--ob-icon-well-size);
  background-color: var(--ob-accent-soft);
  color: var(--ob-accent-pressed);
  border-radius: 50%;
  font-family: var(--ob-font-mono);
  font-size: var(--ob-text-body);
  font-weight: var(--ob-weight-bold);
}

/* Plain, not decimal-leading-zero. Six steps is not a list that needs its
   figures padded to a common width, and "01" reads as a version rather than as
   a first step. */
.ob-step__num::before {
  content: counter(ob-step);
}

/* The 4 px above is optical: it drops the cap height of the title onto the
   middle of the disc beside it, which is where the eye expects the pair to line
   up. */
.ob-step__title {
  max-width: none;
  margin: var(--ob-space-1) 0;
  font-size: var(--ob-text-body);
  line-height: var(--ob-leading-body);
  font-weight: var(--ob-weight-bold);
}

.ob-step__desc {
  max-width: none;
  margin: 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* --------------------------------------------------------------------------
   WHO IT IS FOR, and WHY THE NAME.

   Neither section takes a rule of its own, and that is a decision rather than
   an omission. who-its-for.php renders .ob-defs, which section 17 already draws
   as the mockup's card: raised ground, hairline, card radius, the one shadow,
   two columns from 48em. name-story.php renders nothing but .ob-prose, which is
   already capped at the measure, and the mockup's own version of that section
   is prose at a measure too. .ob-section--name carries nothing because the
   two-column pairing it was drawn for needs a second part that front-page.php
   does not call; see the report.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   BEFORE YOU SPEND A EURO: the three answers, then the ten questions.
   -------------------------------------------------------------------------- */

/* The three short answers were one sunken panel with three untreated columns
   inside it, and the key and the value had been swapped: the answer was set as
   a muted caption and the sentence explaining it as a semibold subhead, so each
   one read upside down. Three cards, each the same card as a capability, with
   the answer stated first. */
.ob-highlight {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ob-space-4);
  margin-bottom: var(--ob-space-6);
}

.ob-highlight__item {
  min-width: 0;
  padding: var(--ob-card-padding);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  box-shadow: var(--ob-shadow-card);
}

.ob-highlight__k {
  max-width: none;
  margin: 0 0 var(--ob-space-1);
  color: var(--ob-text);
  font-size: var(--ob-text-body);
  line-height: var(--ob-leading-body);
  font-weight: var(--ob-weight-bold);
}

.ob-highlight__v {
  max-width: none;
  margin: 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
  font-weight: var(--ob-weight-regular);
}

/* Ten disclosures separated by nothing but a hairline is a list that looks like
   a table of contents. Each one is its own card, and the gap between them is
   what makes a closed question look like something to open. */
.ob-faq {
  display: grid;
  gap: var(--ob-space-3);
  max-width: var(--ob-measure);
}

.ob-faq details {
  padding: var(--ob-space-3) var(--ob-space-4);
  background-color: var(--ob-surface-raised);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
  box-shadow: var(--ob-shadow-card);
}

.ob-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ob-space-3);
  min-height: var(--ob-hit-min);
  cursor: pointer;
  /* Two ways of taking the browser's own triangle away, because they are not
     the same mechanism: the standard property here, and the pseudo-element
     below for the WebKit versions that ignore it. */
  list-style: none;
  font-size: var(--ob-text-body);
  line-height: var(--ob-leading-body);
  font-weight: var(--ob-weight-semibold);
}

.ob-faq summary::-webkit-details-marker {
  display: none;
}

/* The marker is drawn after the question rather than before it, so ten
   questions of different lengths still line up on their first word. It changes
   shape as well as color when the answer opens, because the open state has to
   be readable without the color being the only signal. */
.ob-faq summary::after {
  content: "+";
  color: var(--ob-accent-pressed);
  font-family: var(--ob-font-mono);
  font-weight: var(--ob-weight-bold);
}

.ob-faq details[open] summary::after {
  content: "\2212";
}

.ob-faq__a {
  max-width: none;
  margin: var(--ob-space-2) 0 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* --------------------------------------------------------------------------
   COMING SOON: the closing call to action.

   The mockup ends the page on a bounded card on the sunken tone rather than on
   another section, because it is the one place the reader is asked to do
   something and the last thing on a page should look like an ending. The
   compound selector is not decoration: .ob-section + .ob-section draws a rule
   across the top of every section, and that rule has to lose to the card's own
   border rather than sit above it.
   -------------------------------------------------------------------------- */

.ob-section.ob-section--get {
  padding: var(--ob-space-6) var(--ob-space-4);
  background-color: var(--ob-surface-sunken);
  border: var(--ob-border-width) solid var(--ob-border);
  border-radius: var(--ob-radius-card);
}

/* Rule 3 again: the card is on the sunken tone, so the link that reaches a
   person takes the pressed accent. */
.ob-section--get a {
  color: var(--ob-accent-pressed);
}

.ob-section--get .ob-section__heading,
.ob-section--get .ob-section__lede {
  text-align: center;
}

.ob-section--get .ob-section__lede {
  margin-left: auto;
  margin-right: auto;
}

/* The mockup centers the whole call to action, but its version is one short
   paragraph and this one is two of about fifty words each. Prose that long is
   read down its left edge, and centering ragged both edges takes that edge
   away, so the column is centered and the sentences inside it are not. */
.ob-section--get .ob-prose {
  margin-left: auto;
  margin-right: auto;
}

/* --------------------------------------------------------------------------
   THE BREAKPOINTS.

   The mockup collapses everything at one width, 820 px. This file's ladder is
   48em and 62em, stated at the top as the widths where this content wraps, and
   in em so that a reader who has raised his text size gets the narrower layout
   at the width where the text actually needs it. 48em is where a card grid goes
   from one column to two; 62em is where it goes to three. The mockup's third
   query is prefers-color-scheme, which lives in tokens.css and is not repeated
   here.
   -------------------------------------------------------------------------- */

@media (min-width: 48em) {

  .ob-features {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Three, and not two along with everything else. There are exactly three of
     these, two columns would strand the third on a row of its own, and the
     mockup takes them straight from one column to three for the same reason. */
  .ob-highlight {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* The closing card can afford the sectioning step once there is room for it.
     Below this width the page's own edge padding is already doing that work,
     and doubling it would leave the text in a channel. */
  .ob-section.ob-section--get {
    padding: var(--ob-space-7) var(--ob-space-5);
  }
}

@media (min-width: 62em) {

  /* Nine cards, three across, which is the mockup's grid and the only count
     that divides them evenly. */
  .ob-features {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* ---------------------------------------------------------------------------------------------
   The hero's drawing of the application.

   Ported from the mockup. The markup for it went into hero.php before these rules existed, and with
   no rules a fake window is not a picture of anything: it is a nav list and a table rendered as a run
   of bare text under the buttons, which is worse than leaving the space empty. Every literal from the
   mockup is mapped onto a token here, except the few pixel values that are drawing a window chrome
   rather than laying out a page.
   --------------------------------------------------------------------------------------------- */

/* Two columns once there is room, the text and the picture. Below that the picture goes under the
   text rather than shrinking to the point where the table is unreadable. */
.ob-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ob-space-6);
  align-items: center;
}
@media (min-width: 62em) {
  .ob-hero {
    grid-template-columns: 1.05fr 1fr;
  }
}

.ob-window {
  overflow: hidden;
  border: var(--ob-border-width) solid var(--ob-border-strong);
  border-radius: var(--ob-radius-card);
  background: var(--ob-surface-raised);
  box-shadow: var(--ob-shadow-card);
}

.ob-window__bar {
  display: flex;
  align-items: center;
  gap: var(--ob-space-2);
  padding: var(--ob-space-2) var(--ob-space-3);
  border-bottom: var(--ob-border-width) solid var(--ob-border);
  background: var(--ob-surface-sunken);
}
/* Fixed pixels on purpose: these are three dots drawn at the size a title bar draws them, not a
   quantity that should grow with the reader's type size. */
.ob-window__dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--ob-border-strong);
}
.ob-window__title {
  margin-left: var(--ob-space-2);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
}

.ob-window__body {
  display: grid;
  grid-template-columns: 8rem 1fr;
  min-height: 18rem;
}
.ob-window__side {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--ob-space-3);
  border-right: var(--ob-border-width) solid var(--ob-border);
  background: var(--ob-surface-sunken);
}
.ob-window__navitem {
  padding: 5px var(--ob-space-2);
  border-radius: var(--ob-radius-control);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
}
.ob-window__navitem.is-on {
  background: var(--ob-selected);
  color: var(--ob-text);
  font-weight: var(--ob-weight-semibold);
}

.ob-window__main {
  padding: var(--ob-space-4);
}
.ob-window__verdict {
  display: flex;
  align-items: center;
  gap: var(--ob-space-2);
  margin-bottom: var(--ob-space-3);
  flex-wrap: wrap;
}
.ob-window__note {
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
}

/* The four columns are substance, what is in the blend, the cap, and the verdict. */
.ob-window__row {
  display: grid;
  grid-template-columns: 1.4fr 0.8fr 0.7fr auto;
  gap: var(--ob-space-2);
  align-items: center;
  padding: 7px 4px;
  border-bottom: var(--ob-border-width) solid var(--ob-border);
  font-size: var(--ob-text-caption);
}
.ob-window__row--head {
  border-bottom: var(--ob-border-width) solid var(--ob-border-strong);
  color: var(--ob-text-muted);
  font-weight: var(--ob-weight-semibold);
}
/* Tabular figures so the percentages line up on the decimal point down the column. */
.ob-window__num {
  font-family: var(--ob-font-mono);
  font-variant-numeric: tabular-nums;
}

/* The verdict pills, used in the window and available to any section that needs one. */
.ob-pill {
  display: inline-block;
  padding: var(--ob-pill-padding);
  border-radius: var(--ob-radius-pill);
  font-size: var(--ob-text-small);
  font-weight: var(--ob-weight-semibold);
}
.ob-pill--good {
  background: var(--ob-good-soft);
  color: var(--ob-good);
}
.ob-pill--caution {
  background: var(--ob-caution-soft);
  color: var(--ob-caution);
}

/* ---------------------------------------------------------------------------------------------
   The name story and its picture.

   One column on a phone, with the picture under the prose rather than squeezed beside it. Two from
   the same breakpoint the rest of the page widens at, so the section does not change shape on its
   own at a width nothing else reacts to.
   --------------------------------------------------------------------------------------------- */

.ob-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ob-space-6);
  align-items: start;
}
@media (min-width: 62em) {
  .ob-split {
    grid-template-columns: 1.15fr 0.85fr;
  }
}

.ob-figure {
  margin: 0;
}
/* Width and height are on the img so the space is reserved before the file arrives and the text
   beside it does not jump when it does. */
.ob-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--ob-radius-card);
}
.ob-figure__caption {
  margin-top: var(--ob-space-2);
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  line-height: var(--ob-leading-caption);
}

/* The two halves of the plant, shown together. Side by side even on a phone: the pair only works as
   a comparison, and stacking them turns it into two unrelated pictures. */
.ob-plate {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ob-space-3);
}
.ob-plate__item img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--ob-radius-card);
}
.ob-plate__label {
  margin: var(--ob-space-2) 0 0;
  color: var(--ob-text-muted);
  font-size: var(--ob-text-caption);
  font-weight: var(--ob-weight-semibold);
  line-height: var(--ob-leading-caption);
}
