/* Work Theory design system, v2.
 *
 * The contract is assets/brand-system/design-system.html on daye/brand-v2-linkedin.
 * Values here are copied from its build appendix, not retyped from prose.
 *
 * Five rules this file enforces:
 *   1. Paper is the ground. Nothing inverts.
 *   2. The accent is a ground, never a letter. Type on it is ink, always.
 *   3. Nothing rounded, nothing blurred, nothing floats.
 *   4. One mark per surface. One highlighted phrase per beat.
 *   5. Figures carry their source, in mono.
 *
 * Pages declare no colours of their own. If a page needs a hex, the system is
 * missing something and the system is what should change. That is what makes the
 * brand review a swap of the block below rather than an edit of every page.
 */

:root {
  /* colour. paper and ink never move. the accent is the only choice in here. */
  --paper: #FAFAF6;
  --ink: #1A1A18;
  --card: #FFFFFF;
  /* The accent, and the only colour in here that is a choice. The system's own
   * words: it takes any hue, and changing it changes which of two things the
   * brand is. A light accent takes an ink letter and reads as a highlighter; a
   * dark one takes a paper letter and reads as a printed colour. Sky is light,
   * so Rule 2 is unchanged and type on it stays ink.
   *
   * Named by role rather than by hue on purpose. These were --lime, --lime-dark
   * and --lime-tint, which meant the first accent change had to touch every use
   * site to stop the names lying. Swapping accents is now this block.
   *
   * Sky, from the design system's accent row. Previous default: lime #D2F065,
   * deep #5A7816, tint #ECF7C3.
   */
  --accent: #A8D8F0;
  --accent-deep: #1F5C99;
  --accent-tint: #D8EDF8;
  --grey: #6B6759;
  /* THE SAME GREY, FOR THE ONE GROUND ON THIS SITE THAT IS NOT PAPER.
   *
   * --grey is measured against paper, where it is 5.41:1 and comfortable. The
   * ledger rows are not paper: they are the accent ramp, drifting through all
   * seven stops over 48 seconds, and the captions on those rows are 11px, which
   * is not large text and gets no 3:1 allowance. Measured, --grey on the seven:
   * sand 4.18, acid 4.23, lime 4.42, mint 3.98, sky 3.71, lilac 3.46, blush
   * 3.64. Every one fails 4.5:1, and because the ramp drifts the same caption is
   * at 4.42 one moment and 3.46 a few seconds later, which is why looking at it
   * once tells you nothing.
   *
   * ⚠️ IT IS THE LEAST LEGIBLE TEXT ON THE PAGE AND IT IS THE PAGE'S EVIDENCE.
   * "Core, billed yearly", "A cut of each payment, 2.9% + 30 cents online",
   * "Their page would not load for us". The site's whole claim is that every
   * figure carries where it came from; the where-it-came-from is what was
   * failing.
   *
   * Darkened from --grey along its own hue until it clears the WORST stop,
   * lilac, which is what makes it safe everywhere on the loop rather than for
   * part of it. Measured on the seven: 5.58, 5.64, 5.90, 5.32, 4.95, 4.62,
   * 4.85, and 6.11 to 6.62 on the paper wash an unpriced row lays over them.
   * The target is 4.6 rather than 4.5 for the reason --mark-ramp-ink gives
   * below: rounding to a hex triplet must not be able to drop it under the bar.
   *
   * ⚠️ NOT --mark-ramp-ink, WHICH IS THE OBVIOUS WRONG ANSWER. Those seven are
   * darkened against PAPER. On the light ramp they measure 2.93 to 3.76, so
   * using them here would fail the thing it looks like it fixes.
   *
   * ⚠️ AND NOT A REASON TO DARKEN --grey ITSELF. It is on paper on every other
   * page in the repo, where it already passes, and moving it would restyle all
   * of them to fix a caption on one. */
  --grey-mark: #585449;
  --rule: #DCD9CE;

  /* The whole accent row, copied from the design system's swatches. The mark
   * moves through all seven rather than picking one, so the brand is the range
   * rather than a colour, and no single hue has to carry the whole company.
   *
   * Ordered by hue, not by taste: sand, acid, lime, mint, sky, lilac, blush is
   * one continuous turn around the wheel, which is why the drift reads as
   * breathing rather than as flicking between swatches.
   *
   * Every one of these takes ink type in the system's own table, so Rule 2 is
   * unbroken at every point in the loop. That is not a coincidence to rely on
   * quietly: a hue added here that needs paper type would silently make the
   * mark illegible for part of its cycle, which nothing would warn you about. */
  --mk-sand:  #F0DCA8;
  --mk-acid:  #F0E24A;
  --mk-lime:  #D2F065;
  --mk-mint:  #9FE8C0;
  --mk-sky:   #A8D8F0;
  --mk-lilac: #D6C2F0;
  --mk-blush: #F5C2CE;

  /* The ramp, written once because several components draw it and a stop list
   * repeated in four places is a stop list that will disagree with itself.
   *
   * Two things decide these percentages, and neither is taste. Both were
   * measured against the painted pixel, not reasoned about.
   *
   * FIRST, the pairs are not the same distance apart. In OKLab, lime to mint is
   * 0.118 and acid to lime is 0.047, which is two and a half times. Evenly
   * spaced stops give every pair the same slice of the loop, and equal time
   * over unequal distance is unequal speed, so the wide pairs lurched and the
   * narrow ones crawled. Each stop now sits at its cumulative share of the
   * total distance, which makes the rate of visible change constant.
   *
   * SECOND, and this is the one that is easy to get wrong: a pixel never
   * crosses the whole ramp. background-size is 700%, so the image is seven
   * element widths and sliding it from 0% to 100% moves it by six of them. Any
   * fixed point therefore travels six sevenths of the ramp, 85.71%, and then
   * the animation restarts. With the cycle spread over the full 100%, a pixel
   * ran from sand to blush and snapped back to sand: measured, that seam moved
   * twelve times as far in its final second as an average second.
   *
   * So the whole cycle lives in the first 85.71%, and the tail past it repeats
   * the start of the cycle, which makes the window at the end of the loop show
   * the same thing as the window at the beginning. The seam has nowhere to be.
   *
   * Changing a colour here means recomputing all of it. Hand-picked
   * percentages bring back both faults at once, and neither is visible in a
   * screenshot. */
  --mark-ramp:
    var(--mk-sand) 0%,
    var(--mk-acid) 14.21%,
    var(--mk-lime) 21.03%,
    var(--mk-mint) 38.02%,
    var(--mk-sky) 51.32%,
    var(--mk-lilac) 62.53%,
    var(--mk-blush) 71.73%,
    var(--mk-sand) 85.71%,
    var(--mk-acid) 100%;

  /* THE SAME SEVEN, DARK ENOUGH TO BE LETTERS.
   *
   * Rule 2 says the accent is a ground and never a letter, and the seven above
   * are why: as type on paper not one of them reaches 4.5:1, so a marked word
   * in any of them is unreadable for part of every loop. That rule stands.
   *
   * These are the exception it allows for, and they are a different palette
   * rather than the same one used wrongly. Each keeps its hue angle in OKLCH
   * exactly and is darkened until it hits 4.6:1 on paper, holding the most
   * chroma sRGB has at that lightness so it stays the brand's colour instead of
   * sliding to grey. Measured on #FAFAF6: 4.62, 4.59, 4.60, 4.59, 4.62, 4.59,
   * 4.62. The target is 4.6 rather than 4.5 so that rounding to a hex triplet
   * cannot drop one of them under the bar.
   *
   * ⚠️ They are for TYPE and only type. Used as a ground they are dark enough
   * that ink on them fails, which is Rule 2 broken from the other side.
   * Yellows converge when you darken them, so sand, acid and lime sit closer
   * together here than they do above. That is the palette telling the truth
   * about itself, not a mistake to hand-correct. */
  --mki-sand:  #886F26;
  --mki-acid:  #7D7400;
  --mki-lime:  #677A00;
  --mki-mint:  #008354;
  --mki-sky:   #377997;
  --mki-lilac: #81679F;
  --mki-blush: #9D6071;

  /* Stops recomputed from the DARK palette's own OKLab distances. The light
   * ramp's percentages are a measurement of the light palette and mean nothing
   * here: sand to acid is a sixth of that loop and a seventeenth of this one.
   *
   * The tail past the seam repeats the head, for the reason given above, and
   * here it takes four stops rather than two. The light ramp needed only two
   * because its first leg happens to be almost exactly a seventh of its loop;
   * this one's first leg is much shorter, so stopping at acid would leave the
   * last nine percent of the image a flat olive band and put the seam back.
   *
   * The final stop sits past 100% on purpose. A stop position outside the box
   * is legal: the gradient line extends beyond the paint area and only the part
   * inside it is drawn. That makes the tail an exact copy of the head instead
   * of an approximation that has to be eyeballed. */
  --mark-ramp-ink:
    var(--mki-sand) 0%,
    var(--mki-acid) 4.91%,
    var(--mki-lime) 10.32%,
    var(--mki-mint) 22.82%,
    var(--mki-sky) 40.56%,
    var(--mki-lilac) 55.82%,
    var(--mki-blush) 67.93%,
    var(--mki-sand) 85.71%,
    var(--mki-acid) 90.63%,
    var(--mki-lime) 96.03%,
    var(--mki-mint) 108.54%;

  /* type. two faces, never a third. (Paper Mono, in .brand below, is the
     wordmark: the logo, not a text face.) */
  --sans: 'Public Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* spacing. the whole scale. a value not on this list is one invented under deadline. */
  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 48px;
  --s6: 64px;
  --s7: 96px;
  --s8: 120px;
}

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

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

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 18px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; }

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

::selection { background: var(--accent); color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; animation: none !important; }
}

/* ---- type -------------------------------------------------------------- */

h1, h2, h3 {
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

/* capped so the highlighted phrase sets on one line inside the reading column:
   a marked phrase that wraps becomes a two-line slab of accent instead of a rule. */
h1 { font-size: clamp(36px, 6vw, 58px); }
h2 { font-size: clamp(29px, 4.4vw, 46px); }
h3 { font-size: clamp(21px, 2.5vw, 28px); line-height: 1.2; letter-spacing: -0.02em; }

/* ---- components, from the system --------------------------------------- */

/* The mark drifts through the accent row instead of standing on one colour.
 *
 * The gradient is seven times the width of whatever it marks, so at any moment
 * the phrase sits in what looks like a flat ground with a slight lean in it,
 * and the change is only visible if you stay on the page. That is the intent:
 * it should be felt on a second visit, not watched on the first.
 *
 * Where the loop closes is decided by the stop positions above, not here. The
 * comment that used to sit on this line said sand at both ends of the ramp made
 * the loop seamless. It did not, and it was wrong for a reason worth keeping:
 * the image slides by six sevenths of its width, so a pixel never reached the
 * closing sand at all. Measuring the painted pixel found it. Reading the CSS
 * did not, twice.
 *
 * background-position rather than a colour animation on purpose. Animating the
 * colour would step between seven flat grounds; moving one long ramp keeps every
 * intermediate blend, which is what makes it read as breathing.
 *
 * The flat colour under the image is not decoration. It is what shows if the
 * gradient fails to paint, and it is what reduced motion falls back to below. */
.hl,
.hl-in {
  background-color: var(--mk-sand);
  background-image: linear-gradient(90deg, var(--mark-ramp));
  background-size: 700% 100%;
  background-position: 0 0;
  animation: mark-drift 48s linear infinite;
}

@keyframes mark-drift {
  from { background-position: 0 0; }
  to { background-position: 100% 0; }
}

/* highlight, display. 40px type and up. inline-block so a marked phrase moves to
   the next line whole instead of splitting into two boxes. */
.hl {
  padding: 0 14px;
  display: inline-block;
}

/* highlight, in text. 30px and down. inline so it wraps mid-phrase the way a
   highlighter would; the negative margin keeps it from opening up the sentence. */
.hl-in {
  padding: 0 5px;
  margin: 0 -2px;
  display: inline;
}

/* The lighter state of the mark: a tool we recognised but could not price.
 *
 * It has to be the SAME colour, lighter, at every moment of the loop, or it
 * stops reading as a state of one thing and starts reading as a second colour
 * with its own meaning. A flat tint could not do that once the mark started
 * moving: parked on pale sky while the mark was at blush, the two looked
 * unrelated, which is the opposite of what the pairing is for.
 *
 * So it runs the same ramp, in step, under a wash of paper. The wash is 0.6
 * because that is the ratio the old flat pair already had, measured off the
 * tint against the accent it was derived from, so the two states sit the same
 * distance apart as they always did.
 *
 * Being paler, it can only have MORE contrast with ink than the mark does, so
 * Rule 2 needs no separate check here. */
.hl-in.soft {
  background-image:
    linear-gradient(rgba(250, 250, 246, 0.6), rgba(250, 250, 246, 0.6)),
    linear-gradient(90deg, var(--mark-ramp));
  background-size: 100% 100%, 700% 100%;
  background-position: 0 0, 0 0;
  animation: mark-drift-soft 48s linear infinite;
}

/* The wash never moves, so its position is pinned while the ramp underneath
 * travels. One keyframe list covers every layer, which is why it is spelled out
 * rather than reusing mark-drift. */
@keyframes mark-drift-soft {
  from { background-position: 0 0, 0 0; }
  to { background-position: 0 0, 100% 0; }
}

/* The block at the top of this file already stops the animation for anyone who
 * asked for less motion. Stopping it is not enough on its own: a halted drift
 * leaves the mark showing whichever slice of a seven-times-oversized ramp it
 * froze on, which is a lopsided wash nobody designed and which differs by
 * element width. So the mark becomes what it is a moving version of, one flat
 * ground, and sand is chosen because it is where the loop begins. */
@media (prefers-reduced-motion: reduce) {
  .hl,
  .hl-in {
    background-image: none;
    background-color: var(--mk-sand);
  }

  /* Still the same colour, lighter. The pair has to hold when it stops moving
   * as much as when it moves, so the wash is baked into a flat value rather
   * than left to a halted ramp. This is sand under the same 0.6 paper wash,
   * computed rather than eyeballed. */
  .hl-in.soft {
    background-image: none;
    background-color: #F6EED7;
  }
}

/* OUR ADDRESS, WHEREVER IT APPEARS.
 *
 * The one thing on any of these pages a reader might actually write to, so it
 * carries the brand's colour rather than sitting in grey underlined text. It
 * runs the same ramp as the mark, on the same 48s clock, so it drifts in step
 * with every accent surface on the page rather than keeping its own time.
 *
 * ⚠️ TYPE, NOT A GROUND, WHICH IS WHY IT USES --mark-ramp-ink. Rule 2 still
 * holds: the seven light marks are grounds and never letters. This is the
 * second palette, the same seven darkened until each clears 4.5:1 on paper,
 * and it exists for exactly this case.
 *
 * It lives here rather than in a page because it was a page rule twice, and
 * the second time it was on the wrong element: the class sat on the address
 * inside the claim note and not on the one in the footer, so the footer link
 * stayed grey and the claim note wore a treatment nothing else on the page had.
 * A rule in the system with a test behind it (test/mail-mark.test.js) is the
 * version of this that cannot be half-applied. */
.mail {
  background-image: linear-gradient(90deg, var(--mark-ramp-ink));
  background-size: 700% 100%;
  background-position: 0 0;
  animation: mark-drift 48s linear infinite;
  /* The underline stays, and it is set in a fixed colour rather than
   * currentColor: currentColor is transparent here, which would take the
   * underline with it. Colour alone is not a marker for a reader who cannot
   * see colour, and a mail link that looks like body text is one nobody
   * clicks. */
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--mki-sky);
}

/* Clipping the paint to the glyphs, and only where that actually works.
 * `color: transparent` is what makes the gradient visible and it is also what
 * would make the address INVISIBLE anywhere the clip is unsupported, so the two
 * are declared together: no clip, no transparent, and the link falls back to
 * ordinary underlined ink. */
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .mail {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* Reduced motion cannot simply drop the image the way the marks above do: here
 * the glyphs are painted BY the image, so removing it would leave nothing to
 * read. It parks on one of the seven and hands the colour back to the text, so
 * the underline follows it again. */
@media (prefers-reduced-motion: reduce) {
  .mail {
    background-image: none;
    color: var(--mki-sky);
    text-decoration-color: currentColor;
  }
}

.chip {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 5px 10px;
  background: var(--ink);
  color: var(--paper);
  display: inline-block;
  white-space: nowrap;
}

.chip-accent { background: var(--accent); }

.lbl {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--grey);
}

/* Rule 5 takes this form. Every figure, every time. */
.src {
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.7;
  color: var(--grey);
  border-top: 1px solid var(--rule);
  padding-top: var(--s2);
}

/* Rule 3. Nothing floats. A panel is how two things separate. */
.panel {
  border: 2px solid var(--ink);
  background: var(--card);
  padding: var(--s4);
}

/* Rule 2. Type on the accent is ink, always, on any ground, in any context.
 *
 * This block sits AFTER every component that sets a colour, on purpose. Written
 * above them it loses on source order and an accent chip silently renders paper-on-accent,
 * which is illegible and which nothing warns you about. The system's own notes record
 * this exact failure happening once already. Do not move it up the file. */
.hl,
.hl-in,
.chip.chip-accent { color: var(--ink); }

/* ---- the wordmark ------------------------------------------------------ */
/* The mark and "Work Theory" in Paper Mono, as on worktheory.ai (Daye,
   2026-10-22: "we want the book.worktheory.ai wordmark to be same as the new
   website"). Copied from the website's assets/css/landing.css .brand, .wm and
   .mark, values unchanged; the mark is assets/brand/wt-mark-128.png and the
   face assets/fonts/paper-mono-400.woff2, both the website's own files.
   Text baseline on the mark's bottom edge: with line-height 1, Paper Mono's
   baseline sits .15em above its box, so the box is pulled down .15em
   (measured here too: 0px between baseline and the mark's bottom).
   Until 2026-10-22 this was ten letters in ten families, one span each. */

.brand {
  --mark: 32px;
  display: flex;
  align-items: flex-end;
  gap: 12px;
  font-size: 20px;
  font-weight: 400;
  color: var(--ink);
  text-decoration: none;
}
.brand .wm {
  font-family: 'Paper Mono', ui-monospace, monospace;
  line-height: 1;
  letter-spacing: -0.01em;
  margin-bottom: -0.15em;
  white-space: nowrap;
}
.brand .mark { width: var(--mark); height: var(--mark); border-radius: 7px; }

/* ---- one control ------------------------------------------------------- */

.btn {
  display: inline-block;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 14px var(--s3);
  border: 2px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  text-decoration: none;
  cursor: pointer;
}

/* Taking the accent as a ground now means taking the whole ramp, in step.
 *
 * The animation is the same name and the same duration as the mark's, so a
 * hovered button and every mark on the page are showing the same colour at the
 * same moment rather than two unrelated accents. That is the point: the accent
 * is one moving thing the page shares, not a swatch each component keeps.
 *
 * The letter turns to ink here. It was paper on a dark blue, and the ramp is
 * seven light grounds, so leaving it would be Rule 2 broken for the whole hover
 * and unreadable at every point in the loop rather than some of them. */
.btn:hover {
  background-color: var(--mk-sand);
  background-image: linear-gradient(90deg, var(--mark-ramp));
  background-size: 700% 100%;
  animation: mark-drift 48s linear infinite;
  color: var(--ink);
  border-color: var(--ink);
}

/* A disabled button must not light up. Without this it inherits the hover above
   and reads as pressable while refusing to be pressed, which is the same lie
   the old dark-blue hover told at 0.55 opacity. */
.btn:disabled { opacity: 0.55; cursor: default; }
.btn:disabled:hover {
  background-image: none;
  background-color: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
  animation: none;
}
