/**
 * Blog feed — the dense list layout.
 *
 * Built to docs/设计定调-博客页.md. Two rules from that card drive almost
 * everything here:
 *
 *   zero shadows, zero cards — rows are separated by a 1px rule so the eye
 *   travels down one straight line, which is what a scanning page needs;
 *
 *   one square thumbnail size — a fixed 96px keeps every row the same height,
 *   and equal row height is the thing that actually makes a list scannable.
 *
 * Motion is tier A (CSS transitions only): a reader here is scanning for one
 * article, and entrance animation makes them wait for content they are trying
 * to skim.
 */

/* ------------------------------------------------------------- layout -- */

.ch-feed-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--spacing-3xl);
  align-items: start;
  padding-block: var(--spacing-xl);
}

/* --------------------------------------------------------------- rows -- */

.ch-feed {
  min-width: 0;
  /* The list owns its own rule between rows; no card, no shadow. */
  border-top: 1px solid var(--color-border);
}

.ch-feed-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px;
  gap: var(--spacing-lg);
  align-items: start;
  padding-block: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}
.ch-feed-row.has-no-art { grid-template-columns: minmax(0, 1fr); }

.ch-feed-row__body { min-width: 0; }

.ch-feed-row__byline {
  display: flex;
  align-items: center;
  gap: .45rem;
  flex-wrap: wrap;
  font-size: var(--font-size-xs);
  color: var(--ch-feed-muted, #5f6874);
}
.ch-feed-row__author {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  text-decoration: none;
  color: var(--color-text-secondary);
  font-weight: 600;
}
.ch-feed-row__author:hover { color: var(--color-text-primary); }
.ch-feed-row__byline time::before { content: "·"; margin-right: .45rem; opacity: .5; }

.ch-feed-row__title {
  margin: .3rem 0 0;
  font-size: var(--font-size-xl);
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -.005em;
}
.ch-feed-row__title a {
  text-decoration: none;
  color: var(--color-text-primary);
  transition: color .15s;
}
.ch-feed-row__title a:hover { color: var(--ex-orange, #FF5A1F); }

/* The rubric is set into the headline rather than sitting above it as a pill.
   A row already carries a category rail, tag chips and status badges; a
   fourth pill means none of them reads as emphasis any more. */
.ch-feed-row__rubric {
  /* Same ink as the headline, not the accent colour.
   *
   * The first pass made this orange and it failed the squint test in the
   * design card: the rubric jumped out harder than the title it was supposed
   * to introduce. Hodinkee's version — which this is taken from — sets the
   * rubric in the *same* colour as the headline and separates it by weight,
   * size and letterspacing alone. That is what makes it read as the opening
   * words of the sentence rather than as a label stuck to the front. */
  font-variant: small-caps;
  font-size: .82em;
  font-weight: 800;
  letter-spacing: .07em;
  /* Measured, not eyeballed: `opacity: .55` on the inherited headline colour
     came out at 3.80:1, under the 4.5:1 AA floor for 15px text. Separation
     from the headline is carried by weight, size and letterspacing instead,
     with a real colour that passes. */
  color: var(--ch-feed-rubric, #5a6472);
  margin-right: .5em;
  transition: color .15s;
}
.ch-feed-row__title a:hover .ch-feed-row__rubric { color: var(--ex-orange, #FF5A1F); }

.ch-feed-row__excerpt {
  margin: .35rem 0 0;
  font-size: var(--font-size-base);
  line-height: 1.6;
  color: var(--color-text-secondary);
  /* Two lines, hard. Rows must stay the same height to stay scannable. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
  overflow: hidden;
}

/* The theme's global --color-text-muted is #999, which measures 3.47:1 on the
   light page background — under AA for 11px text. That token is used site-wide
   so it is not changed here; these components take a compliant override and the
   global token is reported as a separate finding. */
.ch-feed-row__meta {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
  flex-wrap: wrap;
  margin-top: .55rem;
  font-size: var(--font-size-xs);
  color: var(--ch-feed-muted, #5f6874);
  font-variant-numeric: tabular-nums;
}
.ch-feed-row__meta > span {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
}
/* The report button only appears on hover/focus — it is an exception action
   and should not compete with the engagement figures on every row. */
.ch-feed-row__meta .ch-report-btn {
  opacity: 0;
  transition: opacity .15s;
}
.ch-feed-row:hover .ch-report-btn,
.ch-feed-row:focus-within .ch-report-btn { opacity: 1; }

/* Keyboard focus has to reveal AND outline it. Hiding a control behind hover
   and giving it no focus ring means a keyboard user tabs onto something they
   cannot see — the audit caught this on all 10 rows. */
.ch-feed-row__meta .ch-report-btn:focus-visible {
  opacity: 1;
  outline: 2px solid var(--ex-orange, #FF5A1F);
  outline-offset: 2px;
}

.ch-feed-row__art {
  display: block;
  width: 96px;
  height: 96px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--color-border-light);
}
.ch-feed-row__art img {
  width: 100%;
  height: 100%;
  /* 1:1 crop: the articles are about where the difference is — stitching, a
     tongue tag, a bezel font — and a 16:9 cover only has room for the whole
     shoe, which is the one scale at which nothing is distinguishable. */
  object-fit: cover;
  display: block;
  transition: opacity .15s;
}
.ch-feed-row:hover .ch-feed-row__art img { opacity: .85; }

/* ------------------------------------------------- rhythm interruption -- */

.ch-feed-break {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: var(--spacing-xl) var(--spacing-lg);
  margin-block: var(--spacing-md);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-border-light);
}
.ch-feed-break__kicker {
  font-size: var(--font-size-xs);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  /* #FF5A1F on the light panel measured 2.71:1. The accent is kept as the
     identity but darkened until it passes AA on this background. */
  color: var(--ch-feed-kicker, #B33C0E);
}
.ch-feed-break__title {
  font-size: var(--font-size-2xl);
  font-weight: 800;
  line-height: 1.3;
  text-decoration: none;
  color: var(--color-text-primary);
}
.ch-feed-break__title:hover { color: var(--ex-orange, #FF5A1F); }
.ch-feed-break__meta { font-size: var(--font-size-xs); color: var(--ch-feed-muted, #5f6874); }

/* --------------------------------------------------------- empty state -- */

.ch-feed-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-3xl) var(--spacing-lg);
  text-align: center;
  color: var(--ch-feed-muted, #5f6874);
}

/* ------------------------------------------------------------ sidebar -- */

.ch-feed-side {
  display: grid;
  gap: var(--spacing-lg);
  position: sticky;
  top: calc(var(--ch-header-height, 64px) + var(--spacing-lg));
}

.ch-side-mod {
  padding: var(--spacing-lg);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-card);
}

.ch-side-mod__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-md);
}
.ch-side-mod__head h2 {
  margin: 0;
  font-size: var(--font-size-md);
  font-weight: 700;
}
.ch-side-mod__more,
.ch-side-mod__link {
  font-size: var(--font-size-xs);
  font-weight: 600;
  text-decoration: none;
  color: var(--color-link);
}
.ch-side-mod__link {
  display: inline-flex;
  align-items: center;
  gap: .2rem;
  margin-top: var(--spacing-md);
}
.ch-side-mod__streak {
  font-size: var(--font-size-xs);
  font-weight: 700;
  color: var(--ex-orange, #FF5A1F);
}
.ch-side-mod__lede {
  margin: 0 0 var(--spacing-md);
  font-size: var(--font-size-sm);
  line-height: 1.6;
  color: var(--color-text-secondary);
}
.ch-side-mod__rate {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  margin: 0 0 var(--spacing-md);
  font-size: var(--font-size-xs);
  color: var(--ch-feed-muted, #5f6874);
}
.ch-side-mod__rate strong { font-size: var(--font-size-sm); color: var(--color-success); }
.ch-side-mod__done {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin: 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-success);
}

.ch-side-rank { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.ch-side-rank li {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto;
  align-items: center;
  gap: .5rem;
  font-size: var(--font-size-sm);
}
.ch-side-rank__n {
  font-size: var(--font-size-xs);
  font-weight: 800;
  color: var(--ch-feed-muted, #5f6874);
  font-variant-numeric: tabular-nums;
}
.ch-side-rank__who {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.ch-side-rank__who span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ch-side-rank__score { font-size: var(--font-size-xs); color: var(--ch-feed-muted, #5f6874); }

.ch-side-picks { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--spacing-md); }
.ch-side-pick {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: .6rem;
  align-items: start;
}
.ch-side-pick.has-no-art { grid-template-columns: minmax(0, 1fr); }
.ch-side-pick__art {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--color-border-light);
}
.ch-side-pick__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch-side-pick__body { min-width: 0; }
.ch-side-pick__body a {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 600;
  line-height: 1.4;
  text-decoration: none;
  color: var(--color-text-primary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  line-clamp: 2;
  overflow: hidden;
}
.ch-side-pick__body a:hover { color: var(--ex-orange, #FF5A1F); }
.ch-side-pick__body span {
  font-size: var(--font-size-xs);
  color: var(--ch-feed-muted, #5f6874);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------- responsive -- */

@media (max-width: 1024px) {
  .ch-feed-layout { grid-template-columns: minmax(0, 1fr); }
  .ch-feed-side { position: static; }
}

@media (max-width: 560px) {
  /* The thumbnail shrinks but stays on the row. Dropping it below the text
     would break the constant row height the whole layout depends on, and
     hiding it would delete the article's actual argument. */
  .ch-feed-row { grid-template-columns: minmax(0, 1fr) 64px; gap: var(--spacing-md); }
  .ch-feed-row__art { width: 64px; height: 64px; }
  .ch-feed-row__title { font-size: var(--font-size-lg); }
  .ch-feed-row__excerpt { -webkit-line-clamp: 2; line-clamp: 2; }
  .ch-feed-row__meta { gap: var(--spacing-md); }
  /* Touch has no hover, so the report action must not be hover-gated. */
  .ch-feed-row__meta .ch-report-btn { opacity: 1; }
}

@media (pointer: coarse) {
  .ch-feed-row__meta .ch-report-btn { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .ch-feed-row__title a,
  .ch-feed-row__art img,
  .ch-feed-row__meta .ch-report-btn { transition: none; }
}


/* ----------------------------------------------- masthead on the feed -- */

/* The magazine masthead was sized for a hero-led page. On a list whose whole
   job is to get the reader to a row, a 72px wordmark pushes the first article
   below the fold. Halved here rather than removed: it still carries the
   category rail and the search field. */
.ch-blog-page .ch-blog-mag-head-inner { padding-block: var(--spacing-xl); }
.ch-blog-page .ch-blog-mag-copy h1 {
  font-size: clamp(1.6rem, 3.2vw, 2.4rem);
  line-height: 1.1;
}
.ch-blog-page .ch-blog-mag-copy p {
  max-width: 54ch;
  font-size: var(--font-size-base);
}

/* ------------------------------------------------- article lead image -- */

/* Capped, not removed. The client asked that opening an article should not
   put a wall of picture between the reader and the first sentence, but the
   photograph is still the article's own and still the LCP element. 320px lets
   the headline, the byline and the opening paragraph share the first screen. */
.ch-single-lead {
  margin: var(--spacing-lg) 0 0;
}
.ch-single-lead img {
  display: block;
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 4px;
}
.ch-single-lead figcaption {
  margin-top: .5rem;
  font-size: var(--font-size-xs);
  line-height: 1.6;
  color: var(--ch-feed-muted, #5f6874);
}

@media (max-width: 560px) {
  .ch-single-lead img { max-height: 200px; }
}

@media (max-width: 720px) {
  /* On a phone the masthead was pushing the first article a full screen down.
     Compacted rather than hidden: the standard is explicit that difficult
     desktop content should be made to work, not removed on mobile. The
     description clamps to two lines and the stats become one inline row. */
  .ch-blog-page .ch-blog-mag-head-inner { padding-block: var(--spacing-lg); }

  .ch-blog-page .ch-blog-mag-copy h1 { font-size: 1.5rem; }

  .ch-blog-page .ch-blog-mag-copy p {
    font-size: var(--font-size-sm);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    overflow: hidden;
  }

  .ch-blog-page .ch-blog-landing-stats {
    display: flex;
    gap: var(--spacing-lg);
    flex-wrap: wrap;
  }
  .ch-blog-page .ch-blog-landing-stats span {
    display: inline-flex;
    align-items: baseline;
    gap: .3rem;
    font-size: var(--font-size-xs);
  }
  .ch-blog-page .ch-blog-landing-stats strong { font-size: var(--font-size-md); }

  /* The category rail scrolls sideways instead of wrapping to three rows. */
  .ch-blog-page .ch-blog-category-rail {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: .5rem;
    padding-bottom: .35rem;
    scrollbar-width: none;
  }
  .ch-blog-page .ch-blog-category-rail::-webkit-scrollbar { display: none; }
  .ch-blog-page .ch-blog-rail-link { flex: 0 0 auto; }
}


/* ------------------------------------------------- dark-mode ink levels -- */

/* The three scoped values above are tuned for the light ground. On the dark
   ground they have to move the other way or they sink into the panel. */
[data-theme="dark"] .ch-feed,
[data-theme="dark"] .ch-feed-side {
  --ch-feed-rubric: #9aa6b6;
  --ch-feed-kicker: #FF8A5C;
  --ch-feed-muted:  #9aa6b6;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ch-feed,
  :root:not([data-theme="light"]) .ch-feed-side {
    --ch-feed-rubric: #9aa6b6;
    --ch-feed-kicker: #FF8A5C;
    --ch-feed-muted:  #9aa6b6;
  }
}


/* ------------------------------------------------------- focus visibility -- */

/* The theme resets `outline: none` widely (208 elements page-wide). These are
   the feed's own interactive elements, so they get an explicit ring back. */
.ch-feed a:focus-visible,
.ch-feed button:focus-visible,
.ch-feed-side a:focus-visible,
.ch-feed-side button:focus-visible {
  outline: 2px solid var(--ex-orange, #FF5A1F);
  outline-offset: 2px;
  border-radius: 2px;
}
