/* ============================================================
   ADOL — Technology hub / blog
   Loads AFTER style.css + static.css and reuses their tokens.

   Two surfaces, one token vocabulary:
     body.tech-hub    → dark charcoal + gold (the brand grade, for browsing)
     body.tech-reader → warm cream + charcoal (for reading 1500 words)

   The reader does NOT restyle .topbar/.btn/.site-footer component by
   component — it REDEFINES the :root custom properties on <body>, and the
   shared components follow automatically. Anything added here that hardcodes
   a colour instead of using a token will break that inheritance.

   Per-card accent: each card sets --cat-hue from its category, and every
   accent below derives from it. Adding a category needs no CSS change.
   ============================================================ */

/* ============================================================
   1 · SHARED PRIMITIVES
   ============================================================ */

/* Label text that only screen readers need — visible labels would crowd the
   filter bar, but an unlabelled search input is unusable without sight. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.32em;
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.32em 0.7em;
  border-radius: 100px;
  white-space: nowrap;
}
.badge--staff {
  color: var(--gold-bright);
  background: rgba(240, 198, 116, 0.11);
  border: 1px solid rgba(240, 198, 116, 0.34);
}
.badge--guest {
  color: var(--cream-soft);
  background: color-mix(in srgb, var(--cream) 7%, transparent);
  border: 1px solid var(--line);
}

.avatar {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
  font-size: 0.58rem;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.avatar--initials {
  background: var(--gold-sheen);
  color: #14100a;
}
.avatar--lg { width: 72px; height: 72px; font-size: 1.35rem; }

/* Numeric metadata (views, ratings) uses tabular figures so counts don't
   jitter horizontally when they tick up. */
.tnum { font-variant-numeric: tabular-nums; }

.icon { width: 1em; height: 1em; flex: none; stroke-width: 1.6; }

/* ============================================================
   2 · HUB (dark)
   ============================================================ */

body.tech-hub {
  background:
    radial-gradient(80% 50% at 80% -8%, rgba(217, 164, 65, 0.09), transparent 62%),
    var(--ink);
}

/* Bottom padding is deliberately tight: the flow carousel sits directly under
   this block and supplies the breathing room. */
.hub-hero {
  padding: clamp(4.6rem, 10vh, 6.6rem) clamp(1.4rem, 6vw, 6rem) clamp(1rem, 2.4vh, 1.5rem);
  max-width: 1400px;
  margin: 0 auto;
}
.hub-hero .s-title { max-width: 16ch; }
.hub-hero .s-body { margin-bottom: 2.4rem; }

.hub-stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.6rem, 5vw, 4rem);
  border-top: 1px solid var(--line);
  padding-top: 1.6rem;
}
.hub-stat__value {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.5rem, 3vw, 2.3rem);
  color: var(--gold-bright);
  line-height: 1;
}
.hub-stat__label {
  font-size: 0.6rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--cream-soft);
  margin-top: 0.5rem;
}

/* Short laptop screens: the hero copy and the carousel together overflow a
   768px-tall viewport at the default rhythm, and the point of this section is
   that it greets you on arrival. Space comes out of the hero, not the cards. */
@media (max-height: 820px) {
  .hub-hero { padding-top: clamp(3.8rem, 8vh, 5rem); }
  .hub-hero .s-body { margin-bottom: 1.5rem; }
  .hub-stats { padding-top: 1.1rem; }
  .hub-stat__label { margin-top: 0.35rem; }
}

/* ---- sticky filter bar ---- */
.hub-filters {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: 0.9rem clamp(1.4rem, 6vw, 6rem);
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-block: 1px solid var(--line);
}
.hub-filters__inner {
  max-width: 1400px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.chips {
  display: flex;
  gap: 0.45rem;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1 1 auto;
  /* Fade the right edge so a clipped chip row reads as scrollable. */
  mask-image: linear-gradient(90deg, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent);
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--cream-soft);
  font: inherit;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.52rem 0.95rem;
  border-radius: 100px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.25s var(--ease-out), border-color 0.25s var(--ease-out),
    background 0.25s var(--ease-out);
}
.chip:hover { color: var(--cream); border-color: rgba(240, 198, 116, 0.5); }
.chip[aria-pressed='true'] {
  color: #14100a;
  background: var(--gold-sheen);
  border-color: transparent;
  font-weight: 500;
}
.chip__count { opacity: 0.55; margin-left: 0.4em; font-variant-numeric: tabular-nums; }

.hub-search {
  position: relative;
  flex: none;
  width: min(240px, 34vw);
}
.hub-search input {
  width: 100%;
  background: color-mix(in srgb, var(--cream) 5%, transparent);
  border: 1px solid var(--line);
  border-radius: 100px;
  color: var(--cream);
  font: inherit;
  font-size: 0.78rem;
  padding: 0.55rem 0.9rem 0.55rem 2.2rem;
  transition: border-color 0.25s var(--ease-out);
}
.hub-search input::placeholder { color: color-mix(in srgb, var(--cream) 38%, transparent); }
.hub-search input:focus { outline: none; border-color: var(--gold-bright); }
.hub-search .icon {
  position: absolute;
  left: 0.82rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--cream-soft);
  pointer-events: none;
}

.hub-sort {
  flex: none;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 100px;
  color: var(--cream-soft);
  font: inherit;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.52rem 0.8rem;
  cursor: pointer;
}
.hub-sort:focus { outline: none; border-color: var(--gold-bright); }
.hub-sort option { background: var(--ink-2); color: var(--cream); }

/* ---- grid ---- */
.hub-body {
  max-width: 1400px;
  margin: 0 auto;
  padding: clamp(2rem, 5vh, 3.4rem) clamp(1.4rem, 6vw, 6rem) 2rem;
}
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: clamp(1rem, 2vw, 1.7rem);
}

.hub-empty {
  text-align: center;
  padding: clamp(3rem, 12vh, 6rem) 1rem;
  color: var(--cream-soft);
}
.hub-empty__title {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--cream);
  margin-bottom: 0.8rem;
}

/* ---- post card ---- */
.pcard {
  --accent: hsl(var(--cat-hue, 42) 62% 60%);
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  background: linear-gradient(
    170deg,
    color-mix(in srgb, var(--accent) 9%, transparent),
    transparent 42%
  );
  overflow: hidden;
  transition: transform 0.4s var(--ease-out), border-color 0.4s var(--ease-out),
    box-shadow 0.4s var(--ease-out);
}
.pcard:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--accent) 52%, transparent);
  box-shadow: 0 18px 44px -22px color-mix(in srgb, var(--accent) 60%, transparent);
}
.pcard__link {
  display: flex;
  flex-direction: column;
  flex: 1;
  text-decoration: none;
  color: inherit;
}
.pcard__link:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: -3px; }

.pcard__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ink-2);
}
.pcard__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.7s var(--ease-out);
}
.pcard:hover .pcard__media img { transform: scale(1.05); }

.pcard__cat {
  position: absolute;
  /* Top-left, because the generated thumbnails print the article title across
     the BOTTOM-left of the image — the two collided when both sat low. */
  left: 0.75rem;
  top: 0.75rem;
  font-size: 0.56rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.36em 0.75em;
  border-radius: 100px;
  /* Dark glass tinted by the category rather than filled with it — a
     saturated block here fought the brand grade and read as a sticker. */
  color: hsl(var(--cat-hue, 42) 70% 82%);
  background: rgba(8, 7, 5, 0.62);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  backdrop-filter: blur(10px) saturate(1.3);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
}

.pcard__body { padding: 1.05rem 1.1rem 0.7rem; flex: 1; }
.pcard__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.08rem;
  line-height: 1.3;
  color: var(--cream);
  margin-bottom: 0.5rem;
  /* Two lines max keeps every card in a row the same height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color 0.3s var(--ease-out);
}
.pcard:hover .pcard__title { color: var(--gold-bright); }
.pcard__excerpt {
  font-size: 0.79rem;
  line-height: 1.62;
  color: var(--cream-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.75rem 1.1rem 1rem;
  border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  margin-top: 0.6rem;
}
.pcard__author {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.68rem;
  color: var(--cream-soft);
}
.pcard__author-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pcard__stats {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: none;
  font-size: 0.66rem;
  color: var(--cream-soft);
}
.pcard__stat { display: inline-flex; align-items: center; gap: 0.28em; }
.pcard__stat--rating { color: var(--gold-bright); }
.pcard__stat--none { opacity: 0.42; }

/* ---- featured card (spans two columns on wide screens) ---- */
.pcard--featured { grid-column: span 2; }
.pcard--featured .pcard__link { flex-direction: row; }
/* Fills the full card height. Safe to crop now that the card art is abstract —
   the generated thumbnail only sets the headline into the ?title=1 share
   variant, so there is no text here for object-fit: cover to slice off. */
.pcard--featured .pcard__media { flex: 0 0 52%; aspect-ratio: auto; }
.pcard--featured .pcard__wrap {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
/* The featured card is as tall as the media column beside it, which is taller
   than its own text. Centring the text inside the expanded body — rather than
   centring the whole wrap — keeps the footer pinned to the bottom edge instead
   of leaving a band of dead space under the excerpt. */
.pcard--featured .pcard__body {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.pcard--featured .pcard__body { padding: 1.5rem 1.5rem 0.8rem; }
.pcard--featured .pcard__title { font-size: clamp(1.25rem, 2vw, 1.7rem); -webkit-line-clamp: 3; line-clamp: 3; }
.pcard--featured .pcard__excerpt { -webkit-line-clamp: 3; line-clamp: 3; font-size: 0.85rem; }
.pcard--featured .pcard__foot { padding: 0.85rem 1.5rem 1.3rem; }
.pcard__flag {
  position: absolute;
  top: 0.8rem;
  right: 0.8rem; /* the category chip owns top-left */
  z-index: 2;
  font-size: 0.56rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #14100a;
  background: var(--gold-sheen);
  padding: 0.36em 0.8em;
  border-radius: 100px;
  font-weight: 600;
}

/* ---- skeletons ---- */
.pcard--skeleton { pointer-events: none; border-color: color-mix(in srgb, var(--line) 55%, transparent); }
.sk {
  background: linear-gradient(
    100deg,
    color-mix(in srgb, var(--cream) 5%, transparent) 30%,
    color-mix(in srgb, var(--cream) 11%, transparent) 50%,
    color-mix(in srgb, var(--cream) 5%, transparent) 70%
  );
  background-size: 220% 100%;
  animation: sk-slide 1.5s var(--ease-out) infinite;
  border-radius: 3px;
}
.sk--media { aspect-ratio: 16 / 9; border-radius: 0; }
.sk--line { height: 0.7rem; margin: 0.55rem 1.1rem; }
.sk--line.is-short { width: 45%; }
@keyframes sk-slide {
  from { background-position: 140% 0; }
  to { background-position: -40% 0; }
}

/* ---- contribute banner ---- */
.contrib {
  max-width: 1400px;
  margin: clamp(2rem, 6vh, 4rem) auto 0;
  padding: clamp(2rem, 5vw, 3.4rem) clamp(1.6rem, 6vw, 4rem);
  border: 1px solid var(--line);
  background:
    radial-gradient(70% 130% at 88% 20%, rgba(240, 198, 116, 0.1), transparent 70%),
    var(--ink-2);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.4rem, 4vw, 3rem);
  flex-wrap: wrap;
}
.contrib__copy { max-width: 54ch; }
.contrib__copy .s-title { font-size: clamp(1.4rem, 2.6vw, 2.1rem); margin-bottom: 0.7rem; }
.contrib__copy p { font-size: 0.86rem; line-height: 1.7; color: var(--cream-soft); }
.contrib__actions { display: flex; gap: 0.8rem; flex-wrap: wrap; }

@media (max-width: 900px) {
  .pcard--featured { grid-column: span 1; }
  .pcard--featured .pcard__link { flex-direction: column; }
  .pcard--featured .pcard__media { aspect-ratio: 16 / 9; flex: none; }
  .pcard--featured .pcard__body { padding: 1.05rem 1.1rem 0.7rem; }
  .pcard--featured .pcard__foot { padding: 0.75rem 1.1rem 1rem; }
}
@media (max-width: 720px) {
  .hub-grid { grid-template-columns: 1fr; }
  .hub-filters__inner { flex-wrap: wrap; }
  .chips { order: 3; width: 100%; flex-basis: 100%; }
  .hub-search { flex: 1 1 auto; width: auto; }
}

/* Matches style.css's own `@media (max-width: 820px) { .topbar__nav {
   display: none } }`, which hides the nav site-wide for the cinematic pages.
   That is right for the hub (its filter bar carries navigation) but leaves an
   ARTICLE with no route back to the index except the browser Back button — so
   the reader gets its nav back, trimmed to the one link that matters.
   The breakpoint has to be 820px, not 720px: at 780px the site rule was still
   hiding the whole <nav> while this block wasn't active yet. */
@media (max-width: 820px) {
  body.tech-reader .topbar__nav { display: flex; }
  body.tech-reader .topbar__nav a:not(:first-child) { display: none; }
  body.tech-reader .topbar__nav a:first-child { font-size: 0.68rem; }
}

/* ============================================================
   3 · READER (light) — token overrides drive the shared components
   ============================================================ */

body.tech-reader {
  --ink: #f7f2e8;
  --ink-2: #efe6d4;
  --cream: #191713;
  --cream-soft: rgba(25, 23, 19, 0.74);
  --gold: #9a6a1c;
  --gold-bright: #7d5514;
  /* The reader NEEDS its own sheen. .btn--gold paints `--gold-sheen` and writes
     `var(--ink)` on top — and on this page --ink is cream, so the dark theme's
     bright #f7dc9e highlight stop would have put cream text on a near-cream
     band and swallowed the label. These stops stay dark enough for cream. */
  --gold-deep: #5f3b0d;
  --gold-sheen: linear-gradient(
    118deg,
    #5f3b0d 0%,
    #8a5f18 34%,
    #96661f 52%,
    #8a5f18 68%,
    #5f3b0d 100%
  );
  --line: rgba(25, 23, 19, 0.15);
  background:
    radial-gradient(70% 40% at 78% -6%, rgba(154, 106, 28, 0.09), transparent 62%),
    var(--ink);
  color: var(--cream);
}
/* Film grain is a dark-surface device; over cream it just reads as dirt. */
body.tech-reader .page-grain { display: none; }
/* .topbar hardcodes a dark scrim in style.css — the light page needs its own. */
body.tech-reader .topbar {
  background: linear-gradient(180deg, rgba(247, 242, 232, 0.94), rgba(247, 242, 232, 0));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
body.tech-reader .btn--gold { color: #fdf8ee; }
body.tech-reader .site-footer { border-top-color: var(--line); }

/* ---- arrival ----
   The hub's flip ends on a full-screen cream wipe, and this page's surface is
   that same cream. Rising the header in behind it makes the two documents read
   as one movement instead of a page swap. It is `backwards`-filled so nothing
   flashes at full opacity before its delay elapses. */
@media (prefers-reduced-motion: no-preference) {
  body.tech-reader .art-head__cat,
  body.tech-reader .art-title,
  body.tech-reader .art-standfirst,
  body.tech-reader .art-meta,
  body.tech-reader .art-hero {
    animation: reader-in 0.75s var(--ease-out) backwards;
  }
  body.tech-reader .art-head__cat { animation-delay: 0.02s; }
  body.tech-reader .art-title { animation-delay: 0.08s; }
  body.tech-reader .art-standfirst { animation-delay: 0.15s; }
  body.tech-reader .art-meta { animation-delay: 0.21s; }
  body.tech-reader .art-hero { animation-delay: 0.27s; }
}
@keyframes reader-in {
  from { opacity: 0; transform: translateY(18px); }
}

/* ---- reading progress ---- */
.progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 50;
  background: transparent;
}
.progress__fill {
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  background: linear-gradient(90deg, var(--gold), var(--gold-bright));
  transform-origin: 0 50%;
}

/* ---- article header ---- */
.art-head {
  max-width: 820px;
  margin: 0 auto;
  padding: clamp(6.5rem, 15vh, 9rem) clamp(1.4rem, 6vw, 2rem) 0;
  text-align: center;
}
.art-head__cat {
  display: inline-block;
  font-size: 0.6rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 0.42em 0.95em;
  border-radius: 100px;
  color: hsl(var(--cat-hue, 42) 70% 26%);
  background: hsl(var(--cat-hue, 42) 60% 92%);
  border: 1px solid hsl(var(--cat-hue, 42) 45% 78%);
  margin-bottom: 1.5rem;
}
.art-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 4.4vw, 3.2rem);
  line-height: 1.14;
  letter-spacing: -0.012em;
  text-wrap: balance;
  margin-bottom: 1.1rem;
}
.art-standfirst {
  font-size: clamp(1rem, 1.5vw, 1.14rem);
  line-height: 1.65;
  color: var(--cream-soft);
  max-width: 60ch;
  margin: 0 auto 2rem;
  text-wrap: pretty;
}
.art-meta {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.55rem 1.1rem;
  font-size: 0.76rem;
  color: var(--cream-soft);
  padding-bottom: 2rem;
  border-bottom: 1px solid var(--line);
}
.art-meta__author { display: inline-flex; align-items: center; gap: 0.5rem; }
.art-meta__dot { opacity: 0.4; }

.art-hero {
  max-width: 1080px;
  margin: clamp(2rem, 5vh, 3.2rem) auto 0;
  padding-inline: clamp(1.4rem, 6vw, 2rem);
}
.art-hero img {
  width: 100%;
  /* height:auto is REQUIRED, not cosmetic. The tag carries width/height
     attributes (for layout stability), and those act as presentation hints —
     leaving height at 720px made both dimensions definite, which makes the
     browser ignore aspect-ratio entirely and gave a 1016×720 box that
     object-fit: cover filled by slicing the sides off the artwork. */
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
  border-radius: 2px;
}

/* ---- article layout: sticky ToC beside the column ---- */
.art-wrap {
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(2.4rem, 6vh, 4rem) clamp(1.4rem, 6vw, 2rem) 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 232px;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
.art-col { min-width: 0; max-width: 68ch; margin-inline: auto; }

.toc {
  position: sticky;
  top: 6rem;
  border-left: 1px solid var(--line);
  padding-left: 1.1rem;
}
.toc__label {
  font-size: 0.58rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.9rem;
}
.toc__list { list-style: none; display: grid; gap: 0.55rem; padding: 0; margin: 0; }
.toc__item--h3 { padding-left: 0.85rem; }
.toc__item a {
  color: var(--cream-soft);
  text-decoration: none;
  font-size: 0.78rem;
  line-height: 1.45;
  transition: color 0.25s var(--ease-out);
}
.toc__item a:hover { color: var(--gold-bright); }
.toc__item.is-active a { color: var(--gold-bright); font-weight: 500; }

/* ---- article prose ----
   Everything here targets .prose descendants because the body HTML is
   machine-generated from Word: it carries no classes of its own. */
.prose {
  font-size: clamp(1.02rem, 1.25vw, 1.115rem);
  line-height: 1.82;
  color: var(--cream);
  text-wrap: pretty;
}
.prose > * + * { margin-top: 1.35em; }
.prose h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.35rem, 2.4vw, 1.72rem);
  line-height: 1.28;
  letter-spacing: -0.008em;
  margin-top: 2.4em;
  /* Anchor jumps from the ToC must clear the fixed topbar. */
  scroll-margin-top: 5.5rem;
}
.prose h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.1rem, 1.8vw, 1.28rem);
  margin-top: 2em;
  scroll-margin-top: 5.5rem;
}
.prose p { color: var(--cream); }
.prose strong { font-weight: 600; color: #0f0d0a; }
.prose em { font-style: italic; }
.prose a {
  color: var(--gold-bright);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--gold-bright) 42%, transparent);
}
.prose a:hover { text-decoration-color: currentColor; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li + li { margin-top: 0.55em; }
.prose li::marker { color: var(--gold); }
.prose blockquote {
  border-left: 2px solid var(--gold);
  padding: 0.2em 0 0.2em 1.35em;
  font-family: var(--font-display);
  font-size: 1.12em;
  line-height: 1.6;
  color: color-mix(in srgb, var(--cream) 88%, transparent);
}
.prose img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 2px;
  margin-block: 2em;
}
.prose figcaption {
  font-size: 0.8rem;
  color: var(--cream-soft);
  text-align: center;
  margin-top: -1.2em;
}
.prose hr { border: 0; border-top: 1px solid var(--line); margin-block: 2.6em; }
.prose code {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 0.88em;
  background: color-mix(in srgb, var(--cream) 7%, transparent);
  padding: 0.15em 0.4em;
  border-radius: 3px;
}
/* Tables arrive from Word without <thead>; the first row is the header in
   practice, so it is styled positionally. Wide tables scroll inside the
   column rather than widening the page. */
.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.93em;
  display: block;
  overflow-x: auto;
}
.prose th, .prose td {
  border: 1px solid var(--line);
  padding: 0.62em 0.85em;
  text-align: left;
  vertical-align: top;
}
.prose tr:first-child td, .prose th {
  background: color-mix(in srgb, var(--gold) 11%, transparent);
  font-weight: 600;
}
/* Word wraps every cell's text in a <p>; its margin would double the padding. */
.prose td > p, .prose th > p { margin: 0; }
.prose td > p + p { margin-top: 0.6em; }

/* ---- rating ---- */
.rate {
  margin-top: clamp(2.6rem, 6vh, 4rem);
  padding: clamp(1.5rem, 3vw, 2.2rem);
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--gold) 5%, transparent);
  border-radius: 3px;
  text-align: center;
}
.rate__q {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2vw, 1.35rem);
  margin-bottom: 0.5rem;
}
.rate__hint { font-size: 0.79rem; color: var(--cream-soft); margin-bottom: 1.2rem; }
.rate__stars {
  display: inline-flex;
  gap: 0.28rem;
  /* One hover target that lights up every star to its left — the whole point
     is that rating costs a single click and no thought. */
  direction: ltr;
}
.rate__star {
  background: none;
  border: 0;
  padding: 0.15rem;
  cursor: pointer;
  font-size: 1.85rem;
  line-height: 1;
  color: color-mix(in srgb, var(--cream) 22%, transparent);
  transition: color 0.18s var(--ease-out), transform 0.18s var(--ease-out);
}
.rate__star:hover { transform: scale(1.16); }
.rate__star.is-on { color: var(--gold); }
.rate__star:focus-visible { outline: 2px solid var(--gold-bright); outline-offset: 2px; border-radius: 3px; }
.rate__result { margin-top: 1rem; font-size: 0.82rem; color: var(--cream-soft); min-height: 1.4em; }
.rate__result strong { color: var(--gold-bright); font-size: 1.05rem; }
.rate.is-done .rate__stars { pointer-events: none; opacity: 0.92; }

/* ---- author box ---- */
.author-box {
  margin-top: clamp(2.2rem, 5vh, 3.2rem);
  padding: clamp(1.4rem, 3vw, 2rem);
  border: 1px solid var(--line);
  border-radius: 3px;
  display: flex;
  gap: 1.3rem;
  align-items: flex-start;
  background: color-mix(in srgb, var(--ink-2) 55%, transparent);
}
.author-box__name {
  font-family: var(--font-display);
  font-size: 1.15rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.28rem;
}
.author-box__role {
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.7rem;
}
.author-box__bio { font-size: 0.87rem; line-height: 1.7; color: var(--cream-soft); }

/* ---- share + ask ---- */
.art-share {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 2rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--line);
}
.art-share__label {
  font-size: 0.6rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--cream-soft);
  margin-right: 0.3rem;
}
.share-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: transparent;
  color: var(--cream-soft);
  font: inherit;
  font-size: 0.74rem;
  padding: 0.45rem 0.95rem;
  cursor: pointer;
  text-decoration: none;
  transition: color 0.25s, border-color 0.25s;
}
.share-btn:hover { color: var(--gold-bright); border-color: var(--gold-bright); }

.ask-band {
  margin: clamp(3rem, 8vh, 5rem) auto 0;
  max-width: 1180px;
  padding: clamp(1.8rem, 4vw, 2.6rem) clamp(1.4rem, 6vw, 2rem);
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.4rem;
  flex-wrap: wrap;
}
.ask-band__copy { max-width: 52ch; }
.ask-band__title { font-family: var(--font-display); font-size: 1.28rem; margin-bottom: 0.4rem; }
.ask-band__copy p { font-size: 0.85rem; line-height: 1.65; color: var(--cream-soft); }

/* ---- related ---- */
.rel {
  max-width: 1180px;
  margin: clamp(2.4rem, 6vh, 4rem) auto 0;
  padding: 0 clamp(1.4rem, 6vw, 2rem) clamp(3rem, 8vh, 5rem);
}
.rel__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.2rem;
  margin-top: 1.4rem;
}
.rel-card {
  --accent: hsl(var(--cat-hue, 42) 55% 42%);
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;
  transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease-out);
}
.rel-card:hover { transform: translateY(-3px); border-color: var(--accent); }
.rel-card__media { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--ink-2); }
.rel-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rel-card__cat {
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 0.85rem 0.95rem 0.35rem;
}
.rel-card__title {
  font-family: var(--font-display);
  font-size: 0.98rem;
  line-height: 1.32;
  padding: 0 0.95rem 0.5rem;
  flex: 1;
}
.rel-card__meta { font-size: 0.68rem; color: var(--cream-soft); padding: 0 0.95rem 0.95rem; }

@media (max-width: 1000px) {
  .art-wrap { grid-template-columns: minmax(0, 1fr); }
  /* The sticky rail has nowhere to live in one column; the outline is a
     nice-to-have, not content, so it is dropped rather than stacked. */
  .toc { display: none; }
}

/* ============================================================
   4 · FORMS (contribute + admin login)
   ============================================================ */

.form-page { max-width: 860px; margin: 0 auto; padding: clamp(6.5rem, 15vh, 9rem) clamp(1.4rem, 6vw, 2rem) 4rem; }

.tfield { margin-bottom: 1.5rem; }
.tfield__label {
  display: block;
  font-size: 0.63rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.6rem;
}
.tfield__hint { font-size: 0.76rem; line-height: 1.55; color: var(--cream-soft); margin-top: 0.5rem; }
.tfield__req { color: var(--gold-bright); }

.input, .textarea, .select {
  width: 100%;
  background: color-mix(in srgb, var(--cream) 4%, transparent);
  border: 1px solid var(--line);
  color: var(--cream);
  font: inherit;
  font-size: 0.92rem;
  padding: 0.75rem 0.95rem;
  border-radius: 2px;
  transition: border-color 0.25s var(--ease-out);
}
.input:focus, .textarea:focus, .select:focus { outline: none; border-color: var(--gold-bright); }
.textarea { resize: vertical; min-height: 5.5rem; line-height: 1.6; }
.select option { background: var(--ink-2); color: var(--cream); }

.tfield-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.2rem;
}

/* ---- dropzone ---- */
.drop {
  border: 1.5px dashed color-mix(in srgb, var(--gold) 42%, transparent);
  border-radius: 4px;
  padding: clamp(1.8rem, 5vw, 3rem) 1.4rem;
  text-align: center;
  cursor: pointer;
  background: color-mix(in srgb, var(--gold) 4%, transparent);
  transition: border-color 0.25s, background 0.25s, transform 0.25s var(--ease-out);
}
.drop:hover, .drop.is-over {
  border-color: var(--gold-bright);
  background: color-mix(in srgb, var(--gold) 10%, transparent);
}
.drop.is-over { transform: scale(1.01); }
.drop__icon { font-size: 2rem; color: var(--gold); line-height: 1; margin-bottom: 0.8rem; }
.drop__title { font-family: var(--font-display); font-size: 1.1rem; margin-bottom: 0.4rem; }
.drop__hint { font-size: 0.78rem; color: var(--cream-soft); }
.drop.is-busy { pointer-events: none; opacity: 0.6; }
.drop.is-done { border-style: solid; border-color: color-mix(in srgb, var(--gold) 60%, transparent); }

.imgdrop {
  display: flex;
  align-items: center;
  gap: 1rem;
  border: 1px dashed var(--line);
  border-radius: 3px;
  padding: 0.9rem;
  cursor: pointer;
  transition: border-color 0.25s;
}
.imgdrop:hover { border-color: var(--gold-bright); }
.imgdrop__preview {
  width: 104px;
  height: 59px;
  flex: none;
  border-radius: 2px;
  object-fit: cover;
  background: var(--ink-2);
  display: grid;
  place-items: center;
  font-size: 0.62rem;
  color: var(--cream-soft);
  overflow: hidden;
}
.imgdrop__preview--avatar { width: 59px; height: 59px; border-radius: 50%; }
.imgdrop__preview img { width: 100%; height: 100%; object-fit: cover; }
.imgdrop__text { font-size: 0.79rem; color: var(--cream-soft); line-height: 1.5; }

/* ---- live preview of the converted document ---- */
.preview {
  border: 1px solid var(--line);
  border-radius: 3px;
  margin-top: 1rem;
  overflow: hidden;
}
.preview__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--cream) 4%, transparent);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-soft);
}
/* The preview shows the article on its real reading surface — cream, serif
   headings — so a contributor sees exactly what gets published. */
.preview__body {
  background: #f7f2e8;
  color: #191713;
  padding: clamp(1.2rem, 3vw, 2rem);
  max-height: 460px;
  overflow-y: auto;
}
.preview__body.prose { --cream: #191713; --cream-soft: rgba(25,23,19,.74); --gold: #9a6a1c; --gold-bright: #7d5514; --line: rgba(25,23,19,.15); }

.alert {
  padding: 0.85rem 1.1rem;
  border-radius: 3px;
  font-size: 0.82rem;
  line-height: 1.6;
  margin-bottom: 1.2rem;
  border: 1px solid;
}
.alert--err { color: #ffc9bd; background: rgba(150, 40, 20, 0.18); border-color: rgba(220, 90, 60, 0.45); }
.alert--ok { color: #bdf0d2; background: rgba(20, 110, 70, 0.18); border-color: rgba(60, 190, 130, 0.42); }
.alert--warn { color: #f2dfb0; background: rgba(150, 110, 20, 0.16); border-color: rgba(220, 175, 70, 0.42); }
.alert:empty { display: none; }

.form-actions {
  display: flex;
  gap: 0.9rem;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 2rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--line);
}
/* style.css's .btn never sets a background because every existing use is an
   <a>. On a real <button> the UA default (rgb(240,240,240)) shows through, so
   a ghost button rendered as a light grey slab with cream text on it —
   effectively invisible. .btn--gold sets its own background and is unaffected. */
.btn--ghost { background: transparent; }
button.btn { cursor: pointer; font-family: inherit; }
button.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; filter: none; }

.tsteps { display: flex; gap: 0.6rem; margin-bottom: 2.4rem; flex-wrap: wrap; }
.tstep {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-soft);
}
.tstep__n {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--line);
  font-size: 0.66rem;
}
.tstep.is-done .tstep__n { background: var(--gold-sheen); color: #14100a; border-color: transparent; }
.tstep.is-done { color: var(--cream); }
.tstep__sep { color: var(--line); }

/* ============================================================
   5 · ADMIN
   ============================================================ */

.admin { max-width: 1240px; margin: 0 auto; padding: clamp(6.5rem, 14vh, 8.5rem) clamp(1.4rem, 5vw, 3rem) 4rem; }
.admin-login { max-width: 380px; margin: 0 auto; }

.admin-tabs { display: flex; gap: 0.5rem; margin-bottom: 1.8rem; flex-wrap: wrap; }

.admin-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.2rem;
  flex-wrap: wrap;
  margin-bottom: 1.6rem;
}
.admin-head__actions { display: flex; gap: 0.6rem; flex-wrap: wrap; }

/* ---- editor ---- */
.ed-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: clamp(1.4rem, 3vw, 2.6rem);
  align-items: start;
}
.ed-side {
  border-left: 1px solid var(--line);
  padding-left: clamp(1rem, 2vw, 1.6rem);
}
.ed-side .tfield { margin-bottom: 1.2rem; }

.ed-toolbar {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex-wrap: wrap;
  padding: 0.45rem 0.55rem;
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--cream) 5%, transparent);
  /* Sticks to the top of the scroll so formatting stays reachable in a long
     article instead of being stranded above the fold. */
  position: sticky;
  top: 0;
  z-index: 5;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.ed-toolbar button {
  min-width: 32px;
  height: 30px;
  padding: 0 0.5rem;
  border: 1px solid transparent;
  border-radius: 2px;
  background: transparent;
  color: var(--cream-soft);
  font: inherit;
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}
.ed-toolbar button:hover { color: var(--gold-bright); background: color-mix(in srgb, var(--gold) 12%, transparent); }
.ed-toolbar button.is-on { color: #14100a; background: var(--gold-sheen); border-color: transparent; }
.ed-toolbar__sep { width: 1px; height: 18px; background: var(--line); margin: 0 0.3rem; }

/* The editing surface deliberately mirrors the published article: cream page,
   .prose type. Editing dark-on-dark and publishing dark-on-cream would mean
   never seeing what readers see. */
.ed-body {
  --ink: #f7f2e8;
  --ink-2: #efe6d4;
  --cream: #191713;
  --cream-soft: rgba(25, 23, 19, 0.74);
  --gold: #9a6a1c;
  --gold-bright: #7d5514;
  --line: rgba(25, 23, 19, 0.15);
  background: #f7f2e8;
  color: #191713;
  border: 1px solid rgba(240, 198, 116, 0.3);
  border-radius: 0 0 2px 2px;
  padding: clamp(1.2rem, 2.4vw, 2rem);
  min-height: 45vh;
  max-height: 62vh;
  overflow-y: auto;
  font-size: 1rem;
}
.ed-body:focus { outline: none; border-color: var(--gold-bright); }
.ed-body:empty::before {
  content: 'Nhập nội dung bài viết…';
  color: rgba(25, 23, 19, 0.4);
}
/* Inside the editor headings need no huge top margin — it makes the caret jump
   around and wastes the limited scroll box. */
.ed-body.prose h2 { margin-top: 1.4em; }
.ed-body.prose h3 { margin-top: 1.1em; }
.ed-body.prose > *:first-child { margin-top: 0; }

/* ---- revision history ---- */
.rev {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  font-size: 0.72rem;
  color: var(--cream-soft);
}
.rev:last-child { border-bottom: 0; }
.rev__when { font-variant-numeric: tabular-nums; }
.rev__note { color: var(--gold); font-size: 0.66rem; }
.rev button {
  flex: none;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--cream-soft);
  font: inherit;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  border-radius: 100px;
  cursor: pointer;
}
.rev button:hover { color: var(--gold-bright); border-color: var(--gold-bright); }
.rev--empty { color: color-mix(in srgb, var(--cream) 40%, transparent); border: 0; }

/* ---- curriculum order ---- */
.ord-row {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr) 150px;
  gap: 1rem;
  align-items: center;
  padding: 0.55rem 0;
  border-bottom: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
}
.ord-row input {
  width: 100%;
  background: color-mix(in srgb, var(--cream) 5%, transparent);
  border: 1px solid var(--line);
  color: var(--cream);
  font: inherit;
  font-size: 0.85rem;
  text-align: center;
  padding: 0.4rem;
  border-radius: 2px;
  font-variant-numeric: tabular-nums;
}
.ord-row input:focus { outline: none; border-color: var(--gold-bright); }
.ord-row__title { font-family: var(--font-display); font-size: 0.92rem; color: var(--cream); }
.ord-row__cat { font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--cream-soft); }

@media (max-width: 900px) {
  .ed-grid { grid-template-columns: minmax(0, 1fr); }
  .ed-side { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 1.4rem; }
  .ord-row { grid-template-columns: 60px minmax(0, 1fr); }
  .ord-row__cat { grid-column: 2; }
}

.qrow {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr) auto;
  gap: 1.2rem;
  align-items: center;
  border: 1px solid var(--line);
  padding: 1rem;
  margin-bottom: 0.9rem;
  background: linear-gradient(170deg, color-mix(in srgb, var(--gold) 4%, transparent), transparent 50%);
}
.qrow__thumb { width: 132px; aspect-ratio: 16/9; object-fit: cover; background: var(--ink-2); }
.qrow__title { font-family: var(--font-display); font-size: 1.02rem; margin-bottom: 0.35rem; }
.qrow__meta { font-size: 0.7rem; color: var(--cream-soft); display: flex; gap: 0.8rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.qrow__excerpt { font-size: 0.76rem; line-height: 1.55; color: var(--cream-soft); }
.qrow__actions { display: grid; gap: 0.5rem; }
.btn--sm { padding: 0.5rem 1rem; font-size: 0.63rem; letter-spacing: 0.16em; }
.btn--danger { color: #ff9b85; border-color: rgba(220, 90, 60, 0.45); }
.btn--danger:hover { border-color: #ff6b4a; color: #ff6b4a; }

.pill {
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.3em 0.7em;
  border-radius: 100px;
  border: 1px solid var(--line);
}
.pill--pending { color: #f2dfb0; border-color: rgba(220, 175, 70, 0.5); }
.pill--published { color: #bdf0d2; border-color: rgba(60, 190, 130, 0.45); }
.pill--hidden { color: var(--cream-soft); }

@media (max-width: 760px) {
  .qrow { grid-template-columns: 1fr; }
  .qrow__thumb { width: 100%; }
  .qrow__actions { grid-auto-flow: column; }
}

/* ============================================================
   6 · MOTION
   ============================================================ */

/* Cards fade up as they enter the viewport.
   CRITICAL: the hidden state is keyed on .will-reveal, which JS adds only
   after it has confirmed IntersectionObserver exists and has begun observing
   that card. CSS must never hide content on its own — when the hidden state
   was written against the markup attribute instead, every card below the fold
   stayed at opacity 0 in any situation where the observer did not fire. */
@media (prefers-reduced-motion: no-preference) {
  .pcard.will-reveal { opacity: 0; transform: translateY(14px); }
  .pcard.will-reveal.is-in {
    opacity: 1;
    transform: none;
    transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
  }
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .sk { animation: none; }
}
