/* ============================================================
   omgfixmd shared components.
   Lives downstream of tokens.css. Every page loads this file, so
   rules here win against browser defaults but lose to any inline
   page CSS that follows — intended during migration. Once the
   inline duplicates are removed from a page, this file owns the
   component outright. New design for a shared pattern? Comes
   here first, never inline.
   ============================================================ */

/* ------------------------------------------------------------
   Post card — the one card that links to a post.
   One component, one variant. Used on:
     - /blog index (as .post-card)
     - in-post "Related reading" blocks (as .post-card .post-card--compact)
     - homepage Reading Cards (React) (as .post-card)
   ------------------------------------------------------------ */

.post-card {
  display: flex;
  flex-direction: column;
  padding: 28px 28px 24px;
  background: var(--bg);
  border: 1px solid var(--edge);
  border-radius: 14px;
  text-decoration: none;
  color: var(--ink);
  transition:
    border-color 200ms var(--ease-quiet),
    transform 260ms var(--ease-considered),
    box-shadow 260ms var(--ease-considered),
    background 200ms var(--ease-quiet);
}

.post-card:hover {
  border-color: var(--edge-strong);
  transform: translateY(-2px);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.03),
    0 14px 30px -20px rgba(55, 53, 47, 0.14);
}

.post-card:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 3px;
}

/* Eyebrow — .post-card__eyebrow is the preferred class; the short
   .eb alias is kept so the in-post related-reading markup can stay
   terse. Format is always "CATEGORY · N MIN READ", uppercased here
   so the markup can stay natural-case (better for JSON-LD / LLMs). */
.post-card__eyebrow,
.post-card .eb {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 14px;
}

/* Title — .post-card__title is preferred. Plain <h3> inside a card
   gets the same treatment, so markup can stay lean. */
.post-card__title,
.post-card h3 {
  font-family: var(--reading-font);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 0 0 10px;
  text-wrap: balance;
  /* The hover underline draws in the marker yellow — same --hl-fill
     as the hero's .mark band. Transparent at rest so nothing shifts
     when the color arrives. Thicker than a default underline because
     the yellow is pale by design. */
  text-decoration: underline 3px transparent;
  text-underline-offset: 3px;
  transition: text-decoration-color 260ms var(--ease-considered);
}

.post-card:hover .post-card__title,
.post-card:hover h3 {
  text-decoration-color: var(--hl-fill);
}

/* Description — .post-card__desc is preferred. Plain <p> inside a
   card inherits the same treatment. */
.post-card__desc,
.post-card p {
  font-family: var(--ui-font);
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink-2);
  margin: 0 0 22px;
  flex: 1;
  text-wrap: pretty;
}

.post-card__desc code,
.post-card p code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--bg-sub);
  border: 1px solid var(--edge);
  border-radius: 3px;
  padding: 1px 5px;
}

/* CTA — .post-card__cta is preferred. The historical .more span
   inside the in-post related-reading markup is aliased so existing
   markup still renders. Note: the ::after arrow is on the class
   variant only; .more markup has the arrow baked into its text node,
   so doubling it would look wrong. */
.post-card__cta,
.post-card .more {
  font-family: var(--ui-font);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
}

.post-card__cta::after {
  content: "→";
  color: var(--muted);
  transition:
    transform 200ms var(--ease-quiet),
    color 200ms var(--ease-quiet);
}

.post-card:hover .post-card__cta::after {
  transform: translateX(3px);
  color: var(--ink);
}

/* For legacy .more (arrow baked into the text), shift the whole span
   on hover instead — the character is part of the text node, we can't
   animate it independently. Subtle and still feels alive. */
.post-card:hover .more {
  color: var(--ink);
  transform: translateX(2px);
}
.post-card .more {
  transition:
    color 180ms var(--ease-quiet),
    transform 200ms var(--ease-quiet);
}

.post-card__meta {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: auto;
}

/* Compact — the in-post "Related reading" density. Same component,
   smaller breathing room and a slightly quieter title. */
.post-card--compact {
  padding: 20px 22px;
  border-radius: 12px;
}

.post-card--compact .post-card__title,
.post-card--compact h3 {
  font-size: 20px;
  line-height: 1.22;
  margin: 0 0 6px;
}

.post-card--compact .post-card__eyebrow,
.post-card--compact .eb {
  margin-bottom: 6px;
}

.post-card--compact .post-card__desc,
.post-card--compact p {
  font-size: 14px;
  line-height: 1.55;
  margin: 0 0 8px;
}

.post-card--compact:hover {
  transform: translateY(-1px);
}

/* Mobile — editorial cards get a hair less padding; compact cards
   stay tight. Title drops one step so two-line titles don't dominate
   a narrow viewport. */
@media (max-width: 640px) {
  .post-card {
    padding: 22px 22px 20px;
    border-radius: 12px;
  }
  .post-card__title,
  .post-card h3 {
    font-size: 20px;
  }
  .post-card--compact {
    padding: 18px 20px;
  }
  .post-card--compact .post-card__title,
  .post-card--compact h3 {
    font-size: 18px;
  }
}

/* ------------------------------------------------------------
   Body prose link — marker highlight.
   Design intent: a real highlighter ink-swipe, not a CSS underline
   pretending to be one. Three moves that separate this from the
   flat-band version:
     1. Translucent fill (color-mix with alpha) — paper shows
        through, so the mark reads as pigment absorbed into the
        page, not printed-plastic placed on top.
     2. Density variation — a two-stop gradient (slightly darker
        at the top "wet edge" where a marker tip lands, lightening
        as it bleeds down). This is how real marker ink looks when
        you swipe a highlighter across a word.
     3. The band sits *behind* the text (high on the line, covering
        the x-height), not below it. You read through the ink, the
        way a highlighter actually works.
   Animation: background-size / background-position DO animate
   (gradient-stop % don't). At rest the band is a narrow stripe
   sitting on the baseline; on hover it scales up to cover the
   x-height with a measured overshoot — the marker landing. One
   wild moment per surface, here.
   ------------------------------------------------------------ */

article a:not([class]),
.doc a:not([class]),
.tldr a:not([class]),
.post-body a:not([class]) {
  color: var(--ink);
  text-decoration: none;
  /* Translucent fill — same hue family as --hl-fill, but with alpha
     so ink absorbs into the paper. Top of the band is the "wet
     edge": ~3% darker, slightly more saturated. Bottom bleeds out
     to a softer wash. Not flat. */
  background-image: linear-gradient(
    175deg,
    color-mix(in oklab, var(--hl-fill) 82%, #b8860b) 0%,
    color-mix(in oklab, var(--hl-fill) 78%, transparent) 45%,
    color-mix(in oklab, var(--hl-fill) 64%, transparent) 100%
  );
  background-repeat: no-repeat;
  /* At rest: a thin stripe low on the baseline. The band is
     positioned at 100% (bottom), 28% tall. Animates to 78% tall
     on hover via background-size. */
  background-position: 0 100%;
  background-size: 100% 28%;
  padding: 0 1px;
  border-radius: 1px;
  /* Tiny -0.5° skew gives the mark a hand-drawn feel. Applied to
     the background via a CSS custom property so text stays upright.
     Easier route: let the stripe itself stay horizontal but add a
     micro-offset at one edge via background-position. We use
     size/position — both animate natively. */
  transition:
    background-size 320ms var(--ease-overshoot),
    background-position 320ms var(--ease-overshoot),
    color 200ms var(--ease-quiet);
}

article a:not([class]):hover,
.doc a:not([class]):hover,
.tldr a:not([class]):hover,
.post-body a:not([class]):hover {
  /* Band grows from 28% (baseline stripe) to 78% (covering the
     x-height and a hair of the ascenders). The overshoot easing
     makes it settle, not snap. Position stays at bottom — the mark
     grows upward from the baseline, the way a highlighter would
     flood the word. */
  background-size: 100% 82%;
  background-position: 0 98%;
}

article a:not([class]):focus-visible,
.doc a:not([class]):focus-visible,
.tldr a:not([class]):focus-visible,
.post-body a:not([class]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ------------------------------------------------------------
   Sticky-note — one component, three uses.
   .audience-card sits in the homepage marketing grid; .verdict and
   .pullquote sit mid-prose in long reads. Same paper paint, same
   paper-shadow, same hover gesture (un-tilt + lift). Variants
   override only what differs: paper color (--note-bg), rest tilt
   (--note-tilt), hover-lift amplitude (--note-lift). The legacy
   class names stay as the variant selectors so existing markup
   keeps working without touch.
   ------------------------------------------------------------ */

.audience-card,
.verdict,
.pullquote {
  position: relative;
  border: 0;
  border-radius: 6px;
  background: var(--note-bg, var(--note-butter));
  transform: rotate(var(--note-tilt, -0.4deg));
  transform-origin: 50% 60%;
  box-shadow:
    0 1px 2px rgba(15, 15, 15, 0.05),
    0 4px 10px -2px rgba(15, 15, 15, 0.08),
    0 18px 32px -18px rgba(15, 15, 15, 0.18);
  color: #1a1a1a;
  transition:
    transform 240ms cubic-bezier(.2, .9, .2, 1),
    box-shadow 240ms ease;
}

.audience-card:hover,
.verdict:hover,
.pullquote:hover {
  transform: rotate(0deg) translateY(var(--note-lift, -3px));
  box-shadow:
    0 2px 4px rgba(15, 15, 15, 0.06),
    0 10px 22px -4px rgba(15, 15, 15, 0.13),
    0 28px 48px -20px rgba(15, 15, 15, 0.25);
}

/* In-post .verdict — quieter sit, butter, mid-prose. */
.verdict {
  --note-bg: var(--note-butter);
  --note-tilt: -0.35deg;
  --note-lift: -2px;
  margin: 1.6em 0 1.8em;
  padding: 14px 16px 15px;
  font-family: var(--ui-font);
  font-size: 15px;
  line-height: 1.55;
}

.verdict strong {
  display: block;
  margin-bottom: 3px;
  font-family: var(--ui-font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(15, 15, 15, 0.62);
}

/* In-post .pullquote — editorial moment, blush, stronger tilt. */
.pullquote {
  --note-bg: var(--note-blush);
  --note-tilt: 0.6deg;
  --note-lift: -3px;
  margin: 2.6em 0 2.8em;
  padding: 26px 28px 28px;
  font-family: var(--reading-font);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: -0.005em;
}

@media (max-width: 720px) {
  .pullquote { font-size: 18px; padding: 22px 22px 24px; }
  .verdict { padding: 13px 15px 14px; }
}

/* ------------------------------------------------------------
   Page footer — single source. Two quiet rows: byline | nav,
   privacy tag | bug report. No pill, no underlined prose.
   <footer class="page-foot">
     <div class="page-foot-row">
       <span>Built by <a>Elad</a> in a fit of OMG.</span>
       <nav class="foot-nav">…links…</nav>
     </div>
     <div class="page-foot-row page-foot-row--quiet">
       <span>your document never leaves your browser</span>
       <a class="foot-nav-bug">Report a bug</a>
     </div>
   </footer>
   ------------------------------------------------------------ */

.page-foot {
  max-width: 880px;
  margin: 56px auto 0;
  padding: 22px 24px 40px;
  border-top: 1px solid var(--border);
  font-family: var(--ui-font);
  font-size: 13px;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.page-foot a {
  color: var(--ink-2);
  text-decoration: none;
  transition: color 140ms var(--ease-quiet);
}

.page-foot a:hover { color: var(--ink); }

.page-foot-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  align-items: center;
  justify-content: space-between;
}

.page-foot-row--quiet {
  font-size: 12.5px;
  opacity: 0.78;
}

.foot-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}

/* Hidden under the workspace — a marketing footer beneath the review surface
   is the wrong note. Only the homepage runs the React app today, but the
   rule lives here so any future React page inherits it. */
body:has(.app.mode-review) .page-foot { display: none; }

@media (max-width: 720px) {
  .page-foot {
    padding: 20px 20px 32px;
  }
  .page-foot-row {
    align-items: flex-start;
  }
  .foot-nav { gap: 14px; }
}

/* ------------------------------------------------------------
   Reduced motion — every transition in this file collapses to 0.
   Color changes still apply (they're the semantic feedback); only
   the time-based motion is cut.
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .post-card,
  .post-card__title,
  .post-card__cta::after,
  .audience-card,
  .verdict,
  .pullquote,
  article a:not([class]),
  .doc a:not([class]),
  .tldr a:not([class]),
  .post-body a:not([class]) {
    transition: none;
  }
  .audience-card,
  .verdict,
  .pullquote {
    transform: none;
  }
  .audience-card:hover,
  .verdict:hover,
  .pullquote:hover {
    transform: translateY(-1px);
  }
  .post-card:hover {
    transform: none;
  }
  .post-card:hover .post-card__cta::after {
    transform: none;
  }
}
