/* Revive Partners home page.
 *
 * The Memo: one left-aligned column at a comfortable measure, the capabilities as a plain list,
 * the name's origin as a quiet aside. Direction contract: .impeccable/surfaces/home.md.
 * Every colour comes from assets/tokens.css. No literal colour below this line.
 */

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

html { -webkit-text-size-adjust: 100%; }

/* The page is an object on a ground, not a column floating on a default.
 *
 * Two flat tones about 2% apart and two hairlines: paper on a desk. Deliberately NOT a
 * gradient, which is a lighting cue, and a memo is a printed thing lit flatly. Deliberately
 * no radius and no shadow, and the sheet runs the full height of the document, so it can
 * never close into a card and pick up the SaaS register this direction was chosen to avoid.
 */
body {
  margin: 0;
  background: var(--canvas-soft);
  color: var(--body);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--leading-body);
  padding-block-end: env(safe-area-inset-bottom, 0px);
}

.sheet {
  background: var(--canvas);
  /* The +2px is the sheet's own left and right borders. box-sizing is border-box, so without
     it the content column lands at 638 rather than --column, and the headline's authored break
     loses by under two pixels. */
  max-width: calc(var(--column) + 2 * var(--space-2xl) + 2px);
  margin-inline: auto;
  padding-inline: var(--space-2xl);
  min-height: 100vh;
  border-inline: 1px solid var(--hairline);
}

/* On a phone the screen IS the sheet. A 2% panel inside a 390px viewport is noise, and the
   side rules would eat the gutter. */
@media (max-width: 44rem) {
  body { background: var(--canvas); }
  .sheet {
    background: none;
    border-inline: 0;
    padding-inline: var(--space-m);
    min-height: 0;
  }
}

img { max-width: 100%; height: auto; }

h1, h2 {
  color: var(--ink);
  font-weight: 600;
  text-wrap: balance;
  margin: 0;
}

p { margin: 0; }

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

/* The skip link is an anchor sitting on an accent ground, so the generic a:hover rule would
   repaint its text to accent-strong on accent: 1.2:1 in dark mode, and the label disappears
   under the pointer of the one user who needed it. Pin the colour against every state. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-control);
  text-decoration: none;
  z-index: 10;
}
.skip,
.skip:hover,
.skip:focus,
.skip:active,
.skip:visited { color: var(--on-accent); }
.skip:focus { left: var(--space-m); top: var(--space-m); }

/* One shared column. The measure is what makes this read as correspondence.
   --column rather than --measure: `ch` resolves against each element's own font-size, so the
   footer (one step smaller) took a narrower column from the same token and sat 79px inside the
   edge every other block shares. */
.bar,
main > *,
.foot {
  max-width: var(--column);
  margin-inline: auto;
}

/* ---------------------------------------------------------------- header bar */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Sized for the 404's inline mark. The home bar carries the stacked brand, which is taller
     than this and sets its own height. */
  min-height: 34px;
  gap: var(--space-m);
  padding-block: var(--space-l);
  padding-block-start: calc(var(--space-l) + env(safe-area-inset-top, 0px));
}

.bar__logo {
  display: inline-flex;
  line-height: 0;
}
.bar__logo img { width: 94px; height: auto; }

/* The home bar carries the brand at full size next to the portal link. The 404 keeps the small
   inline mark instead: there it is a way back, not an introduction.

   Why the brand is IN the bar rather than below it. The mark first moved out of the bar so it
   could lead the page, which left the bar holding one small link in 90px of height: 3.6% of that
   strip was content, and everything below it started 64px lower than it needed to. The mark leads
   just as well from inside the bar, because at 72px against a 13px link it is the only thing in
   that row anyone sees, and the row stops being mostly empty. */
.bar--brand {
  /* The link is positioned by its own margin below, not by the flex box. align-items: center
     would centre it on the whole 72px block, which is level with "partners" and reads as a
     caption on the logo rather than as a separate destination. */
  align-items: flex-start;
  /* The gap below the brand belongs to .lede's padding, so it is declared in one place. */
  padding-block-end: 0;
  /* The mark's height is the one number this row is built from: the image takes it directly and
     the portal link's offset is derived from it, so the two cannot drift apart. */
  --brand-h: 72px;
}

/* Sit the link optically on the word "revive": half the mark's height lands in the middle of the
   cap-to-baseline band, and 0.55em lifts the link's own text centre up onto it. Aligning the box
   tops instead would hang the link off the tip of the "i" rays, which are the tallest thing in
   the artwork and the part of it nobody reads as the top of the word. */
.bar--brand .bar__portal { margin-block-start: calc(var(--brand-h) * 0.5 - 0.55em); }

/* The brand leads the page. The stacked full name is what the brand note asks for where
   somebody meets the practice, and 56px is its stated floor, which is the phone size below. */
.brand {
  display: block;
  line-height: 0;
}
.brand img { height: var(--brand-h); width: auto; }

.bar__portal {
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--muted);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 2px;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  white-space: nowrap;
}
.bar__portal:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ---------------------------------------------------------------- the lede */

/* --space-xl at the top, one step MORE than the headline's own bottom margin, on purpose. The
   mark is a separate object and wants air under it; the headline has to sit closer to the
   paragraph it owns than to the thing above it. At --space-l both gaps were 32px, which left the
   headline belonging to neither (an S2 failure a review caught twice). */
.lede { padding-block: var(--space-xl) var(--space-2xl); }

.lede h1 {
  font-size: var(--step-5);
  line-height: 1.06;
  letter-spacing: var(--tracking-display);
  margin-bottom: var(--space-l);
  text-wrap: pretty;
}

/* The breaks are authored above 44rem so the sentence's own phrase does not split across a
   line; below that the spans go back to inline and the headline wraps naturally.
   The word space is drawn by CSS rather than left in the markup: whitespace between two
   block-level spans generates its own anonymous line box, which silently added a fourth line
   to a three-line headline. At block level the generated space sits at the end of a line and
   collapses, so it costs nothing. */
.lede h1 .h1-a::after,
.lede h1 .h1-b::after { content: " "; }

@media (min-width: 44rem) {
  .lede h1 .h1-a,
  .lede h1 .h1-b { display: block; }
}

/* Only the closing phrase takes the accent, so the wordplay is visible and the blue has the
   same job here that it has on the mark below. */
.lede h1 .h1-c { color: var(--accent); }

.lede__p { font-size: var(--step-1); }

/* The signature. The claim is promoted out of the fine print and paired with the mark, which
   is the one decisive move on this page; everything else is quieted so it reads. */
.signature {
  margin-top: var(--space-xl);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-l);
}
/* space-between pins the mark to the column's right edge. Left-packed, it landed wherever the
   claim's string happened to end, which is an accident rather than an alignment. */
.signature__claim { max-width: 34rem; }

.signature__claim {
  font-size: var(--step-2);
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0;
}

.mark {
  flex: none;
  width: 80px;
  height: 80px;
  overflow: visible;
}
.mark__dot { fill: var(--accent); }
.mark__rays { stroke: var(--accent); }

/* The page's single authored moment: the rays come on once, after the text is already
   readable. The dot never moves. The default is fully visible, so a browser that runs no
   animation, or a reader who asked for less motion, sees the finished mark immediately. */
@media (prefers-reduced-motion: no-preference) {
  .mark__rays {
    animation: rays-on var(--dur-base) var(--ease-out) 420ms backwards;
  }
  @keyframes rays-on {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

/* ---------------------------------------------------------------- what we build
 *
 * A description list, not a row of cards. More space above each heading than below it, so a
 * term binds to the text it introduces rather than floating between two.
 */

.work { padding-block-end: var(--space-xl); }

.work h2,
.name h2,
.builder h2,
.contact h2 {
  font-size: var(--step-3);
  letter-spacing: -0.015em;
  margin-bottom: var(--space-l);
}

.work__list { margin: 0; }

.work__item {
  padding-top: var(--space-m);
  padding-bottom: var(--space-m);
  border-top: 1px solid var(--hairline);
}
.work__item:last-of-type { border-bottom: 1px solid var(--hairline); }

.work__item dt {
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: var(--space-2xs);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

/* The capability icons.
 *
 * These reverse an earlier rule in DESIGN.md that this list carries no icons. The rule existed to
 * keep the section off the icon-heading-text card grid every software homepage uses, and that part
 * still holds: these sit inside a description list's term, in the margin column, NOT on cards.
 *
 * They are drawn for this brand rather than taken from a library, and the tell is the dot. The
 * Revive mark is a filled dot with three rays, the "i" switched on, so every icon here carries
 * exactly ONE filled dot in the accent, and everything else is a monoline stroke. That is what
 * stops four icons reading as stock clipart: they share the brand's own vocabulary, and the eye
 * picks up the repeated dot before it reads any of the shapes.
 *
 * One stroke weight, round caps and joins, one 24 unit box, no fills except the dot. */
.wicon {
  width: 25px;
  height: 25px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.55;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.wicon__dot { fill: var(--accent); stroke: none; }

.work__item dd { margin: 0; }

/* Above 44rem the title moves into its own margin column and the descriptions line up, which
   gives the section a composition instead of the same block three times. No cards, no icons,
   no column rules: it stays a letter's organised detail. */
@media (min-width: 44rem) {
  .work__item {
    display: grid;
    /* 160px with the smaller gap, which leaves the descriptions 460px.
       This was 168px, then briefly 216px to keep every term on one line once the terms gained an
       icon and got longer. 216 broke T2: the descriptions fell to 392px and ran 39 to 44
       characters a line against a 45 minimum, which a reviewer measured and called. Worth
       recording that 168 ALSO failed it, at 44, so the original was already marginal and the
       widening only made a latent defect visible.
       A term wrapping to two lines beside its icon is a cosmetic cost. A description below the
       measure is a reading cost, so the descriptions win. Measured here: 48, 49 and 55
       characters on the non-final lines. */
    grid-template-columns: 160px 1fr;
    column-gap: var(--space-m);
    align-items: baseline;
  }
  .work__item { position: relative; }
  /* The label aligns LEFT here, against the icon, not right against the descriptions. Right
     aligned it looked tidier on the edge it shared with the description column, but it put a
     variable gap between each icon and the word it belongs to: 15px on the longest label and
     94px on "Clinical tools", so the shortest row's icon read as detached decoration. The icon
     is the left anchor now, and every label sits a constant 15px from its own icon. */
  .work__item dt { margin-bottom: 0; justify-content: flex-start; text-align: left; }

  /* The icon leaves the text flow here and becomes a left rail, so the four of them form one
     vertical column. Left in the flow they sit immediately before labels of four different
     widths, which lands them at four different x positions and reads as an accident.
     text-align: right is what keeps a label that wraps to two lines aligned on the edge it
     shares with the description, rather than only the cluster being flush.

     Absolute, and NOT a third grid column, because a third column needs display:contents on the
     dt to let its children take part in the grid, and that costs the description list its
     term-and-definition semantics in the accessibility tree. The layout is not worth the
     structure. top matches the row's own padding, so the icon centres on the label's first line.

     It sits at -2.5rem, in the SHEET'S GUTTER rather than in the text column. At left: 0 it
     landed on the same x as the label column and struck through the first word of the longest
     label. The gutter is --space-2xl (84px) and exists at every width where this rule applies,
     because the sheet drops its padding at max-width: 44rem and this rule starts at min-width:
     44rem, so the two breakpoints are exactly complementary. 45px clear of the sheet's border,
     19px clear of the text: nearer the thing it belongs to. */
  /* The icon stays IN the flow, at the start of its own term, so it sits on the page's shared
     left edge. It was briefly absolute at -2.5rem, hanging in the sheet's gutter, which made the
     four of them the only elements on the page breaking the edge that the logo, the headline, the
     paragraph, the claim, every section heading and the footer all share. The owner spotted it
     unprompted. The gutter version existed to stop the icons landing at four different x values
     behind right-aligned labels; the labels align left now, so the flow gives the same rail and
     keeps the edge. */
}

.work__soon {
  margin-top: var(--space-m);
  color: var(--muted);
  font-size: var(--step--1);
}

/* ---------------------------------------------------------------- the aside
 *
 * The one place the page changes texture. A single hairline on the inline-start edge, 1px, so
 * it reads as a margin note rather than as a callout box.
 */

/* The rule came off when the signature took over the page's graphic treatment. A secondary
   explanation should not carry more visual distinction than the opening. It stays a margin
   note through placement alone. */
.name {
  /* --space-2xl, not the --space-3xl this carried as the page's last section before the form.
     Moved above the capability list it became a 136px hole in the middle of the page. */
  margin-block-end: var(--space-2xl);
  padding-inline-start: var(--space-m);
}

.name h2 {
  font-size: var(--step-1);
  margin-bottom: var(--space-s);
}

/* ---------------------------------------------------------------- who builds it
 *
 * Running text, not a list. The capability list sits directly above and already gives the page
 * its one structured comparison; a second three-part structure here would read as a template
 * rather than as an answer. Paragraph margins are explicit because this stylesheet has no global
 * paragraph reset. */
.builder { padding-block-end: var(--space-2xl); }
.builder p { margin-block: 0; }
.builder p + p { margin-top: var(--space-m); }

/* ---------------------------------------------------------------- contact */

.contact { padding-block-end: var(--space-2xl); }

/* The page's destination, marked as a transition from explanation to response. One rule, and
   none beneath the heading, so the page does not fill up with separators. */
/* The rule crosses the whole sheet, so it reads as a fold in the paper rather than as another
   divider like the three above it: the point where the letter turns into a reply card. */
.contact h2 {
  padding-top: var(--space-l);
  border-top: 1px solid var(--accent);
  font-size: var(--step-3);
  line-height: 1.15;
  margin-inline: calc(-1 * var(--space-2xl));
  padding-inline: var(--space-2xl);
}
@media (max-width: 44rem) {
  .contact h2 {
    margin-inline: calc(-1 * var(--space-m));
    padding-inline: var(--space-m);
  }
}

.contact__intro { margin-bottom: var(--space-l); }

.field { margin-bottom: var(--space-m); }

.field label {
  display: block;
  font-size: var(--step--1);
  color: var(--ink);
  font-weight: 600;
  margin-bottom: var(--space-2xs);
}

.field input,
.field textarea {
  width: 100%;
  font: inherit;
  color: var(--ink);
  background: var(--canvas-soft);
  border: 1px solid var(--control-edge);
  border-radius: var(--radius-control);
  padding: 12px 14px;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}

.field textarea { display: block; resize: vertical; min-height: 7.5rem; }

.field input::placeholder,
.field textarea::placeholder { color: var(--placeholder); }

.field input:hover,
.field textarea:hover { border-color: var(--body); }

.field input:focus,
.field textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
  outline: none;
}

.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] { border-color: var(--danger-text); }

/* The honeypot. Never shown, never announced, never focusable. */
.hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.btn {
  font: inherit;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  padding: 14px 28px;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}
/* No lift and no shadow: a decorative hover on the submit is invisible to the phone audience
   this page is written for, and the page's one authored moment lives on the mark instead. The
   functional states stay. */
.btn:hover:not(:disabled) { background: var(--accent-strong); }
.btn:disabled { opacity: 0.55; cursor: default; }

.form__status {
  margin-top: var(--space-m);
  font-size: var(--step--1);
  min-height: 1.5em;
}
.form__status[data-state="error"] { color: var(--danger-text); }
.form__status[data-state="sent"] { color: var(--success-text); }

.form__alt {
  margin-top: var(--space-s);
  font-size: var(--step--1);
  color: var(--muted);
}

/* ---------------------------------------------------------------- footer */

.foot {
  border-top: 1px solid var(--hairline);
  padding-block: var(--space-l) var(--space-xl);
  font-size: var(--step--1);
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-m);
  align-items: baseline;
}

.foot__name { color: var(--ink); font-weight: 600; }
.foot__links { display: flex; gap: var(--space-xs); flex-wrap: wrap; align-items: baseline; }
.foot__links a { color: var(--muted); }
.foot__links a:hover { color: var(--accent); }
.foot__copy { margin-inline-start: auto; }

/* ---------------------------------------------------------------- narrow */

@media (max-width: 30rem) {
  /* .lede keeps its --space-xl top padding here. It used to be zeroed, because the brand sat
     inside .lede and its own margin carried the gap. The brand is in the header bar now, so
     zeroing this left the headline touching the logo. */
  /* The brand note's stated floor for the stacked full name. */
  .bar--brand { --brand-h: 56px; }
  /* At this width the space above the headline collapses to the header's own padding, 32px,
     which matched the 32px below it: the headline then belonged to the header as much as to
     the paragraph it introduces. One step down on the scale restores the grouping. */
  .lede h1 { margin-bottom: var(--space-m); }
  .foot__copy { margin-inline-start: 0; flex-basis: 100%; }
  .signature { gap: var(--space-m); }
  .mark { width: 48px; height: 48px; }
  .signature__claim { font-size: var(--step-0); }
  /* Both peers step down together. Stepping only the contact heading made the page's
     destination SMALLER than the section above it at exactly the width most visitors read. */
  .work h2,
  .builder h2,
  .contact h2 { font-size: var(--step-2); }
}

/* There is deliberately no entrance animation here.
 *
 * The page gets ONE authored motion moment and the surface brief puts it on the contact form,
 * which is the only thing on the page that actually changes state. A fade-and-rise on the
 * headline would be a second moment, it would animate from an invisible default, and it is the
 * single most common tell that a page was assembled rather than designed. Text that is already
 * correct does not need to arrive. */

/* ---------------------------------------------------------------- prose pages */

/* A page of running text: privacy today, terms or an about page later. The heading is --step-1,
   the quieter of the two heading sizes and the one the "Why revive" aside already uses. A page
   nobody reads for pleasure should not shout section by section.

   THE RHYTHM IS SET ON THE SECTION, NOT ON THE HEADING, and that is deliberate. A heading
   carrying its own top margin is exactly how the gap above it ends up equal to the gap below it,
   which is the S2 failure this project has now hit three separate times. Here the section gap is
   --space-xl (52px) and the heading-to-text gap is --space-s (12px), so every heading on the page
   belongs to the text underneath it by construction, not because somebody measured it. */
.prose { padding-block-end: var(--space-2xl); }
.prose section { margin-block: 0; }
.prose section + section { margin-top: var(--space-xl); }

/* Explicit, because there is no global paragraph reset in this stylesheet: leaving the browser
   default in place would put a collapsing 1em margin in the middle of every rule below. */
.prose h2 { font-size: var(--step-1); margin-block: 0 var(--space-s); }
.prose p { margin-block: 0; }
.prose p + p { margin-top: var(--space-m); }

.prose__updated { color: var(--muted); font-size: var(--step--1); }

/* ---------------------------------------------------------------- not found */

.notfound__back { margin-top: var(--space-l); }


/* Touch targets.
 *
 * The standalone navigation links are type-sized, so they measure about 24px tall: fine for a
 * mouse, under the 44px floor for a finger. Raise them only where the pointer is actually
 * coarse, so the desktop layout keeps its proportions. The email link inside a sentence is
 * exempt: it is an inline link in running text, not a standalone target.
 */
@media (pointer: coarse) {
  .bar__portal,
  .foot__links a,
  .notfound__back a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .bar__logo { min-height: 44px; align-items: center; }
}
