/* =========================================================
   HOME WIDGETS — shared chrome for the small collapsible blocks that sit on
   the page background above the Home feed (Top story, Today's French sentence).

   Everything here is widget-agnostic. Anything specific to one widget lives in
   its own file (topstory.css / french.css). Extracted from french.css when the
   second widget arrived, so the two can't drift apart.
   ========================================================= */

/* ---- the row ----
   Flex rather than a 2-col grid on purpose: either widget can be absent (the
   French one is per-user, Top story only appears when a story is well covered)
   and flex lets the survivor take the full width instead of leaving a hole.
   Wrapping is automatic from the basis, so no breakpoint is needed — the pair
   stack once the column is too narrow to seat both. */
.mn-home-widgets {
  display: flex;
  flex-wrap: wrap;
  column-gap: 1.4rem;
}

.mn-home-widgets > * {
  flex: 1 1 300px;
  min-width: 0;
}

.mn-home-widgets > *[hidden] { display: none; }

@media (max-width: 640px) {
  /* Pull the row up towards the admin bar and leave a slightly wider gap before
     the first card, so the widget block is evenly spaced top and bottom. The
     top gap is .content's 1.1rem padding; the bottom is the last host's 1.1rem
     margin, which this adds to.

     :has() is what makes the negative margin safe — without it, a Home page
     with both widgets hidden would have the feed yanked upwards by an empty
     row. Tune these two numbers together; they are meant to look equal. */
  .mn-home-widgets:has(> *:not([hidden])) {
    margin-top: -0.55rem;
    margin-bottom: 0.45rem;
  }
}

/* ---- shared timings ---- */
.mn-w {
  --w-slide: 300ms;
  --w-fade: 190ms;
  --w-ease: cubic-bezier(0.4, 0, 0.2, 1);

  /* Size of each widget's lead line — the Top story headline and the French
     sentence. Shared deliberately: the two sit side by side, so any mismatch
     makes one widget noticeably taller than the other. Change it here, not in
     topstory.css / french.css. */
  --w-lead: clamp(0.95rem, 0.7vw + 0.75rem, 1.1rem);
}

/* Must come after the base .mn-w rule above: a media query adds no
   specificity, so placed earlier in the file this would simply be overridden. */
@media (max-width: 640px) {
  .mn-w { --w-lead: 0.98rem; }
}

/* ---- collapse / expand ----
   Expand-only content goes in `.mn-w-reveal`, whose track animates its grid row
   0fr -> 1fr. Growing rows are what make the persistent parts SLIDE to their
   new positions; display:none cannot be transitioned, so it is never used.

   THE THREE LEVELS ARE LOAD-BEARING. The animating grid must not itself be a
   grid item of a parent grid: in an auto-sized row a `1fr` row has no definite
   height to resolve against and computes to 0px, so the reveal silently never
   opens. Measured 0px as a grid item vs 17.2px on a normal-flow child. */
.mn-w-reveal { display: block; }

.mn-w-reveal-track {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--w-slide) var(--w-ease);
}

.mn-w.is-expanded .mn-w-reveal-track { grid-template-rows: 1fr; }

/* min-height:0 lets the 0fr row actually collapse; overflow:hidden clips the
   content and contains its margins so a closed row is truly 0 high.
   The fade sits on this wrapper rather than its children on purpose: revealed
   content often carries its own opacity at higher specificity (the French
   kicker at .6, the phonetic at .72), which a `> *` rule would lose to.
   Nesting compounds instead — a child at .6 runs 0 -> 0.6 as this goes 0 -> 1. */
.mn-w-reveal-inner {
  overflow: hidden;
  min-height: 0;
  opacity: 0;
  transition: opacity var(--w-fade) ease;
}

/* Fades in behind the motion so content arrives rather than snapping on.
   Collapsing hides it at once — the slide carries the eye. */
.mn-w.is-expanded .mn-w-reveal-inner {
  opacity: 1;
  transition-delay: calc(var(--w-slide) * 0.45);
}

/* ---- comment hint ----
   There is no +/- control any more — the widget itself is the control. What
   sits in that slot is a passive indicator: a comment count while collapsed, so
   a folded widget never hides the fact that somebody has replied. The slot is
   always in the DOM and holds a minimum width, so the line it trails doesn't
   reflow when the first comment arrives.

   Hidden while expanded, where the real like/comment row shows the count
   anyway. Delayed past the slide so it fades out after the motion settles,
   the same way the old crossfade did. */
.mn-w-hint {
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  min-width: 1.6em;
  margin-left: 0.5rem;
  vertical-align: baseline;
  line-height: 1;
  opacity: 0.5;
  transition: opacity var(--w-fade) ease var(--w-slide);
}

.mn-w.is-expanded .mn-w-hint { opacity: 0; }

/* No comments: collapse the slot entirely. Holding width for an empty element
   would leave a permanent trailing gap after the translation, and on a narrow
   column could push it to wrap onto a line of its own. */
.mn-w-hint:empty {
  min-width: 0;
  margin-left: 0;
}

.mn-w-hint i { font-size: 0.9em; }

.mn-w-hint-count {
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
}

/* ---- interactivity ----
   With the +/- gone, hover is the only desktop cue that these do something.
   Kept off touch (`hover: hover`) so it can't stick after a tap. The tint is
   painted on the widget box itself — no padding is added, so nothing shifts.

   Collapsed, the whole widget is the target. Expanded, only the header is:
   `.mn-w-tail` holds the like/comment row and thread (French) or the excerpt
   and link (Top story), and clicking there must not fold the widget away. */
.mn-w { cursor: pointer; }
.mn-w.is-expanded .mn-w-tail { cursor: auto; }

/* Breathing room inside the tint, cancelled by an equal negative margin so the
   widget still occupies exactly the space it did — only the tint grows, the
   text does not move. Vertical only: horizontally the widget box already runs
   the full column width, well past the text.

   Outside the hover query on purpose, so the geometry is identical on touch and
   pointer devices; only the colour is hover-gated. Safe from margin collapsing
   because the hosts are flex items, which establish their own formatting
   context. */
.mn-w {
  padding-block: 0.6rem;
  margin-block: -0.6rem;
  /* Horizontal too, or the tint hugs the text — most obvious on a French
     sentence with no icon, where there is no icon column to provide inset.
     0.7rem eats half the 1.4rem column gap, so a hovered tint stops exactly
     0.7rem short of its neighbour's text and the two can never collide. */
  padding-inline: 0.7rem;
  margin-inline: -0.7rem;
  border-radius: 12px;
}

@media (hover: hover) and (pointer: fine) {
  .mn-w {
    transition: background-color 160ms ease;
  }
  .mn-w.is-collapsed:hover,
  .mn-w.is-expanded:hover:not(:has(.mn-w-tail:hover)) {
    background-color: color-mix(in srgb, currentColor 7%, transparent);
  }
  /* The French widget is a .mn-linkcard, whose skin sets `background: none` at
     (1,2,0) — this has to outrank it. */
  #home .mn-linkcard.mn-french.is-collapsed:hover,
  #home .mn-linkcard.mn-french.is-expanded:hover:not(:has(.mn-w-tail:hover)) {
    background-color: color-mix(in srgb, currentColor 7%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .mn-w-reveal-track,
  .mn-w-reveal-inner,
  .mn-w-hint,
  .mn-w {
    transition: none;
  }
}
