/* THE EDITORIAL COLUMN v2 DETAIL TREATMENT, IN PRODUCTION.
   ==========================================================================
   Rolled out 2026-08-30 from layouts/detail/01-editorial-column-v2/, which is
   a set of hand-written prototypes. Nothing here is copied from them: the
   prototypes carry their own .ec-/.sv- markup and hardcoded words, and the
   three real pages share one family, .sd-. So this is the same TREATMENT
   written against the classes production actually ships.

   THE THREE PAGES, and they are drawn three different ways:
     song   templates/song-detail.html, hero baked by api/song-page.js and
            then redrawn by assets/js/kc-song-detail.js
     poster templates/poster-detail.html, head baked by api/poster-page.js,
            body drawn by assets/js/kc-poster-detail.js
     game   games/<slug>/index.html, written by scripts/build-game-pages.mjs
            and said in 35 more languages by api/game-page.js

   ONE STYLESHEET rather than three copies, because the whole point of the
   change is that the three page types stop disagreeing with each other. It is
   linked LAST on each of them: several rules here have to outrank
   assets/kc-rail-toggle.css and music-versions/v17/v17.css at equal
   specificity, and source order is what settles that.

   Every rule is presentation only. No markup was deleted from a generator, so
   a crawler and a reader with no JavaScript still receive the eyebrow, the
   sentence and the by-line in the bytes; this decides what a reader SEES. */

/* ---------------------------------------------------------------- 1. TRIM */
/* Kaʻo: "we need to minimize the elements at the top since it pushes the main
   content (cards) down."

   An eyebrow reading SONG tells you a song is a song, which you knew when you
   clicked it, and the rail beside it already says KEEP CALM Music. The by-line
   repeats the artist and the genre that the About panel states properly a
   screen below. The game's back-to-kind link goes for the same reason: the
   rail lists every kind already.

   Hidden, not cut. These pages are generated in three different places and one
   line here reverses any of it. */
html .am-main .sd-eyebrow,
html .am-main .sd-by { display: none; }
/* The game's dek is its one-line hint, and only the copy that sits under the
   title. The retired notice is also a .gd-hint inside .sd-head and must keep
   speaking, so this is anchored to the title rather than to the class. */
html .am-main .sd-title + .gd-hint { display: none; }
/* THE BACK LINK GOES ONLY WHERE THE RAIL IS. The prototype drops it outright
   because "the rail already lists every kind with counts", and that is true
   from 901px up. Below that, game-detail.css sets `.gd-app .am-side {
   display: none }` on purpose, and its own note says nothing is lost because
   the back link still reaches the library. Hiding both would strand a phone
   reader on a game with no way back to games. */
@media (min-width: 901px) {
  html .am-main .gd-back { display: none; }
}

/* ------------------------------------------------------- 2. ONE TOP LINE */
/* Kaʻo: "have all templates have the same exact top starting spot."

   Measured at 1440x900 before this: the song and poster titles began at y=109
   under an eyebrow, and the game title at y=151 under a back link. .am-main
   already gives all three the same 20px of padding, so the only thing left
   between them was each hero's own top padding: .sd-hero sets .5rem and
   .gd-hero 6.4px. Both become 8px, so the first line a reader can read lands
   at y=90 on all three, which is where the prototypes put it. */
/* Neither class is written by the three generators any more (section 9), but
   scripts/build-intention-layout-options.mjs still writes .sd-hero, so the
   rule is not dead and stays. */
html .am-main .sd-hero,
html .am-main .gd-hero { padding-top: 8px; }
/* AND SINCE THE STRUCTURE LANDED (section 9), the one top line is the content
   area's own: .am-main.ec takes v17's shared 20px of padding on all three page
   types and the headline block starts flush inside it, so the first readable
   line is at the same y whichever kind you opened, AND at the same y as the
   article, the product page and every index. .ec-head therefore adds nothing
   of its own; this states that rather than leaving the two rules to argue. */
html .am-main .ec-head { padding-top: 0; margin-top: 0; }
html .am-main > :first-child,
html .am-main > * > :first-child { margin-top: 0; }

/* -------------------------------------------------- 3. THE PANEL IS A CARD */
/* Kaʻo, pointing at the article rail: "That also isn't the padded rounded
   treatment I requested on the side bar content - make it look like this."

   That is .kc-quicktips / .kc-signup--rail in assets/cdo-watchtower.css, and
   these are their exact numbers. NOT .wt-panel, which is a transparent
   wrapper on that page (.wt-panel--calm sets background:none, border:none,
   padding:0); reaching for it was the earlier mistake.

   The game's About panel used to be a bare <aside> with no class of its own,
   matched here by its position inside .sd-mid. Before that it had no ground,
   no border, no radius and no padding at all: measured rgba(0,0,0,0) on 0px.

   SINCE THE STRUCTURE LANDED (section 9) all three panels carry .ec-note, so
   they are matched by NAME rather than by where they happen to sit, and the
   .sd-mid this used to reach through is no longer written by any of the three
   generators. .sd-side stays in the selector because the song and poster
   panels still carry it and kc-song-detail.css still styles it by that name. */
html .am-main .sd-side,
html .am-main .ec-note {
  background: var(--wt-panel-bg);
  border: 1px solid var(--wt-panel-border);
  border-radius: 12px;
  padding: 16px 18px;
  align-self: start;
}
/* 4. NO TRAILING RULE on the last fact row. The rows here are separated by a
   gap rather than a hairline, so there is nothing to remove; this holds the
   line if one is ever added, because a divider under the final row divides it
   from nothing and reads as a double border inside a card. */
html .am-main .sd-facts > .sd-fact:last-child { border-bottom: 0; }

/* ------------------------------------------- 5. THE RAIL REACHES THE FOOTER */
/* Kaʻo, after asking more than once: "Stop leaving this gap between the left
   nav and footer." Measured before this at 1440x900: 1532px of empty rail
   edge on the song page, 1412 on the poster, 1079 on the game.

   SCOPED TO THE DESKTOP LAYOUT ON PURPOSE, and this is the one thing the
   prototypes get wrong. Below 901px .am-side is `position: fixed` with
   `transform: translateX(-102%)`: an off-canvas drawer. An unscoped
   `html .am-side { position: relative }` outranks that media rule, and the
   drawer stops being a drawer: it takes flow space while parked off-screen
   and, when opened, pushes the page down instead of sliding over it. So every
   declaration that changes how the column is positioned lives inside the same
   min-width the rail's own desktop rules use.

   position: relative, not static. .kc-railtoggle is position:absolute and
   anchors to .am-side; a static column would take its containing block away
   and send the collapse button somewhere else entirely.

   top: auto is not cosmetic. The shared rule sets `top: var(--am-sticky-top)`
   for the STICKY behaviour, where top is the threshold it pins at. Under
   position:relative that same declaration becomes a literal 62px shove
   downward, so the rail keeps its height and hangs 62px past the footer.

   And the COLUMN stops being the thing that follows the reader; the NAV
   inside it does that. One element cannot both stand still to the footer and
   track the scroll. */
@media (min-width: 901px) {
  html .am-layout { align-items: stretch; }
  html .am-side,
  html.kc-has-musicbar .am-side {
    position: relative;
    top: auto;
    height: auto;
    min-height: 0;
    max-height: none;
    align-self: stretch;
    overflow: visible;
  }
  html .am-side__nav {
    position: sticky;
    top: var(--am-sticky-top, 62px);
    max-height: calc(100vh - var(--am-sticky-top, 62px) - 16px);
    overflow-y: auto;
    /* 6. NO overscroll-behavior: contain, here or on the panel. `contain`
       stops a scroll chaining out to the page, which is right on a scroll
       container that is full and wrong on one that is not: the wheel gets
       swallowed and the page refuses to move while the pointer is over the
       rail. Kaʻo reported exactly that. */
  }
  html.kc-has-musicbar .am-side__nav {
    max-height: calc(100vh - var(--am-sticky-top, 62px) - var(--mus-bar-h, 0px) - 16px);
  }
}
/* AND THE LAST 40px, which is why aiming at the rail alone could never finish
   the job. .am-app ends where the layout ends and .kc-footer starts lower
   because it carries margin-top:40px and sits outside the shell the app is
   inside. On a page with a rail that reaches the bottom, that margin reads as
   the rail giving up early. */
html .kc-footer { margin-top: 0; }
/* And the last 8px after that, which is the shell's own bottom padding sitting
   between the app and the footer. Measured on the song page: the rail ended at
   y=2384 and the footer began at 2392. This stylesheet is only ever loaded by
   the three detail pages, so the rule cannot reach a page that wants the gap. */
html .wt-shell { padding-bottom: 0; margin-bottom: 0; }

/* ----------------------------------------------- 7. THE BRAND HEADLINE */
/* Kaʻo: "For all the headers that say KEEP CALM, have the AND be smaller and
   bottom aligned. You can follow the paradigm on the footer and home hero."
   These are .kc-footer__and's own numbers, so the detail headline, the footer
   headline and the home hero now agree. vertical-align:baseline is the part
   that reads as bottom aligned: it sits the small word on the same baseline as
   the big caps instead of centring it against them.

   The span is written by assets/js/kc-title-and.js, which shrinks WHATEVER
   SINGLE WORD follows the brand rather than looking for "AND", because the
   connector is "E" in Italian and "Y" in Spanish. */
.sd-title .kc-title__and {
  display: inline-block;
  font-size: .552em;
  font-weight: 700;
  letter-spacing: .05em;
  vertical-align: baseline;
  text-transform: uppercase;
}

/* --------------------------------------------- 8. THE LANGUAGE PILL TIP */
/* A pill naming the language it leads to, in the page's own language, written
   by assets/js/kc-lang-tip.js. These pages carry no pill today; the rule is
   the presentation half, lifted from the prototype so that the day one
   appears it matches the rail's own hover label instead of inventing a second
   style. */
[data-kc-langpills] a[data-tip] { position: relative; }
[data-kc-langpills] a[data-tip]::after {
  content: attr(data-tip);
  position: absolute; left: 50%; bottom: calc(100% + 8px);
  transform: translate(-50%, 4px);
  padding: 7px 11px; border-radius: 8px;
  background: var(--mus-ink, #161616); color: var(--mus-surface, #fdfcf8);
  font: 500 13px/1.2 var(--wt-font-ui, inherit);
  letter-spacing: 0; text-transform: none; white-space: nowrap;
  pointer-events: none; opacity: 0; z-index: 6;
  transition: opacity 130ms ease, transform 220ms cubic-bezier(.32,.72,0,1);
}
[data-kc-langpills] a[data-tip]:hover::after,
[data-kc-langpills] a[data-tip]:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  [data-kc-langpills] a[data-tip]::after { transition: none; }
}

/* ================================================================ 9. THE
   EDITORIAL COLUMN, THE STRUCTURE ITSELF
   ==========================================================================
   WHAT WENT WRONG BEFORE THIS, so it is not repeated. Everything above this
   line is the v2 TREATMENT applied to the page shape production already had.
   The ask was the TEMPLATE. Measured on production 2026-08-30, the song page
   carried zero `ec-` layout classes: a 260px card to the LEFT of the title
   with the About panel off to the right, which is the old shape wearing new
   paint. This section is the shape.

   THE SHAPE, from layouts/detail/01-editorial-column-v2/, in Kaʻo's own
   description of it: ONE generous column, optically centred in the content
   area beside the rail, so it never reads as shoved right by the rail. The
   gutter beside the rail is closed by the measure GROWING, never by filler.
   Inside the column a margin-note track sits to the right of the words, the
   way a printed column carries its notes.

     .ec-col    the measure. A two track grid: the words, and the notes.
     .ec-head   the brand headline and the transport, and nothing else.
     .ec-stage  the artifact, BENEATH the headline, at the words' full width.
                This is the part the styling pass could not reach: the old
                shape put the card small and to the left of the title.
     .ec-note   the About panel, beside the words, sticky through the read.

   PRODUCTION KEEPS ITS OWN CLASSES. Every element still carries the `sd-`
   (or `gd-`) class it always did, so the treatment above, kc-song-detail.css,
   kc-poster-detail.css, game-detail.css and every querySelector in the three
   renderers go on working untouched. The `ec-` classes are added beside them
   and carry the STRUCTURE only. Nothing was deleted to make room.

   ONE STYLESHEET for song, poster and game: the three page types agreeing
   with each other is the whole point of the change. */

/* ---------- tokens, on the content area ----------
   Named on .am-main.ec so the three generators each say the word once, in
   their own markup, and inherit the rest. */
.am-main.ec {
  /* The scroll header's real height, v17's own --am-sticky-top. */
  --ec-top: var(--am-sticky-top, 100px);
  /* THE MARGIN NOTE TRACK. The prototype's bare track is 220px. Production's
     panel is a 12px card with 18px of side padding (rule 3 above), so 256px
     of track leaves the same 220px of readable note inside it. */
  --ec-note-w: 256px;
  --ec-gap: 56px;
  /* THE MEASURE. 86% of the content area, between 640 and 1180. At 1440 the
     content area is 1116px (1440 - 260 rail - 32 - 32 gutters), so the column
     is 960px with 78px either side; at 1920 it caps at 1180 with 208 each
     side, still equal. Under 640px of content it is simply the content, and
     the min() below keeps it from ever overflowing. */
  --ec-measure: clamp(640px, 86%, 1180px);
  /* THE ARTIFACT IS SIZED BY HEIGHT, whole, never cropped: the viewport minus
     the header and 24px of air above and below. A song card is square so that
     is its width too, capped by the words' track. */
  --ec-stage-h: clamp(440px, calc(100svh - var(--ec-top) - 48px), 900px);
}
html.kc-has-musicbar .am-main.ec {
  --ec-stage-h: clamp(440px, calc(100svh - var(--ec-top) - 48px - var(--mus-bar-h, 0px)), 900px);
}

/* ---------- the content area ----------
   v17 gives .am-main `padding: 20px 0 48px 32px` plus a right gutter of
   clamp(20px, 2.5vw, 44px) from 901px up. Both gutters become 32px here, so
   the centred column is centred in FACT and not just in intention. Two
   classes (0,2,0) outranks v17's lone .am-main (0,1,0) whatever the load
   order, and the child combinator matches the same element v17 aims at. */
@media (min-width: 901px) {
  html .am-layout > .am-main.ec { padding-left: 32px; padding-right: 32px; }
}
/* 20, NOT 28, AND THE 8px WAS MEASURED ACROSS THE SITE RATHER THAN ACROSS
   THESE THREE. 28 came in verbatim from the prototype, which is a standalone
   page with no other template to agree with. v17.css:159 gives .am-main
   `padding: 20px 0 48px 32px`, so on a layout that starts at 62 every other
   rail template lands its content box at y=82: the article, the product page,
   a category index, /news/, and the music, games and posters indexes. All of
   them measured on production 2026-08-30. 28 left song, poster and game alone
   at 90, which is the single thing the rule forbids. Kaʻo: "have all templates
   have the same exact top starting spot." The narrow rule below already agreed
   with v17's own narrow 18. */
html .am-main.ec { padding-top: 20px; padding-bottom: 72px; }

/* ---------- the column ----------
   ONE track by default, and this is the important half of the rule. The
   margin note is an ENRICHMENT of a wide page, not the shape of the page: it
   only appears where the words can afford to give up 312px and still read as
   the measure. See the note on the 1200px breakpoint below, which was written
   after measuring what the two-track grid actually does to a laptop.

   The `> *` rule is what makes the shelves, the merch block and anything a
   generator adds later span the measure without having to know this file
   exists. */
.ec-col {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--ec-gap);
  width: min(100%, var(--ec-measure));
  margin-inline: auto;
}
.ec-col > * { grid-column: 1 / -1; min-width: 0; }

/* ---------- the headline block ---------- */
.ec-head { padding-bottom: 24px; border-bottom: 1px solid var(--wt-rule); }
/* The measure is wide, so the headline may be too: this is the prototype's
   .ec-title, replacing the old column-width clamp that topped out at 3.4rem
   inside a 1fr beside a 260px card. */
html .am-main .ec-head .sd-title,
html .am-main .ec-head .gd-title {
  margin: 0;
  font-size: clamp(2rem, 3.6vw, 3.3rem);
  line-height: 1.02;
  letter-spacing: -0.025em;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
/* The transport row sits under the headline with air, not crowded onto it. */
html .am-main .ec-head .sd-actions,
html .am-main .ec-head .gd-actions { margin-top: 22px; }

/* ---------- the artifact, beneath the headline ----------
   IT IS THE REAL CARD (.kc-pcard, drawn by KCCards / KCCardSystem), so it has
   no typography, no colour and no ratio here: this only says how wide its
   track is. Do not reintroduce a ratio or a font size on .sd-art; if the card
   is wrong it is wrong everywhere and it gets fixed in the card system. */
.ec-stage { grid-column: 1; margin: 28px 0 0; }
.ec-mount { width: min(100%, var(--ec-stage-h)); min-width: 0; }
.ec-mount .kc-pcard,
.ec-mount .sd-art,
.ec-mount .pd-art { width: 100%; margin: 0; max-width: none; }
/* A POSTER CARD IS 5:7, a song card is square. .kc-flip carries the 5/7
   (assets/cdo-watchtower.css) and .kc-flip--square overrides it to 1/1 for
   songs (assets/kc-card-system.css), so the SAME height budget buys a
   different width. Without this the poster card is sized as if it were square
   and stands 1106px tall in a 900px window, which means the reader scrolls to
   see one artifact: the opposite of what "whole, never cropped" asks for. */
.ec-stage--tall .ec-mount { width: min(100%, calc(var(--ec-stage-h) * 5 / 7)); }
/* AN INTENTION CARD IS 9:16, taller again than the poster's 5:7. Same rule,
   same reason: the stage is sized by HEIGHT, so the ratio is what turns that
   height budget into the right width. Sized as if square it would stand about
   1600px tall in a 900px window. Added 2026-08-31 when the Intention detail
   page joined the column; it had been left on .sd-hero/.id-hero, the two
   column grid with a 300px card on the left that the song page was rebuilt
   off on 2026-08-30. */
.ec-stage--portrait .ec-mount { width: min(100%, calc(var(--ec-stage-h) * 9 / 16)); }
/* The Intention card caps itself at 420px for the OLD 300px track
   (kc-intention-layouts.css:423). Inside the stage the track is the measure,
   so the cap has to go or the artifact stays a thumbnail on a 1440 screen,
   which is the whole defect this rebuild exists to remove. Scoped to the
   stage, so the feed and every other surface keep their cap. */
.ec-stage .id-art .kci-card,
.ec-stage .id-mount > .kci-card { max-width: none; }
/* .id-wrap is the Intention page's own 1080px container. When it IS the
   column, the measure owns the width and the container must not also cap it. */
.id-wrap.ec-col { max-width: none; padding-left: 0; padding-right: 0; }
/* ---------- the artifact is a CARD, and a card flips in the open ----------
   Kaʻo, 2026-08-31: "it is a card that should flip like all other cards... not
   clipped or in a container. It should also be the same exact aspect ratio,
   front and back... back looks shorter."

   Both were true and both were measured. .kci-card is `overflow: hidden` with
   its own radius, and the flip happens INSIDE it, so every rotation clipped the
   card against a stationary box: the corners swung out and were cut off, which
   is what the screen recording shows. The faces already clip themselves
   (`.kci-flip .kc-flip__face { border-radius: inherit; overflow: hidden }`), so
   the wrapper's clip was never load bearing. It just had to stop.

   The radius and the shadow move onto the faces, which is where they belong on
   a card that turns: the shadow travels with the face instead of sitting under
   it as a rectangle the card rotates out of. */
.ec-stage .kci-card { overflow: visible; background: transparent; box-shadow: none; }
.ec-stage .kci-flip { border-radius: 18px; }
.ec-stage .kci-flip .kc-flip__face {
  box-shadow: 0 2px 6px rgba(0, 0, 0, .16), 0 24px 60px -28px rgba(0, 0, 0, .5);
}
/* THE 34px, AND IT IS A CLASS NAME COLLISION, not a layout choice.
   kc-intentions.css:183 styles a line of TEXT in the create flow as
   `.kci-back { margin: 34px 0 0 }`. The card's back FACE carries the same class,
   and it is `position: absolute; inset: 0`, so that stray top margin shortened
   it by exactly 34px: measured front 890, back 856 on /intentions/begin-again/.
   Front and back are now the same box, which is the only thing a card can be. */
.kci-flip .kc-flip__back { margin: 0; }

/* ---------- the artifact invites you to play it ----------
   Kaʻo: "the big image needs to have a hover state with the big play button to
   inspire playing the intention." Pointer and focus both, so it is not a
   mouse-only affordance, and it stands down once the piece is playing. */
.kci-playhint {
  position: absolute; inset: 0; z-index: 3;
  display: grid; place-items: center;
  border: 0; padding: 0; cursor: pointer;
  background: rgba(10, 14, 12, .28);
  opacity: 0; transition: opacity .18s ease;
  border-radius: inherit;
}
.kci-playhint__ring {
  width: 92px; height: 92px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 253, 249, .93);
  box-shadow: 0 10px 30px -8px rgba(0, 0, 0, .55);
  transform: scale(.94); transition: transform .18s ease;
}
.kci-playhint svg { width: 34px; height: 34px; margin-left: 4px; fill: #10140f; }
.ec-stage .kci-card:hover .kci-playhint,
.kci-playhint:focus-visible { opacity: 1; }
.ec-stage .kci-card:hover .kci-playhint__ring,
.kci-playhint:focus-visible .kci-playhint__ring { transform: scale(1); }
.kci-playhint:focus-visible { outline: 3px solid var(--wt-accent, #2c5f4e); outline-offset: -3px; }
/* Playing, or turned to the poster, and the invitation has nothing to say. */
.kci-card.is-playing .kci-playhint,
.kc-flip.is-flipped .kci-playhint { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .kci-playhint, .kci-playhint__ring { transition: none; }
}
/* Drag to turn it, so the card answers a hand as well as the button. */
.ec-stage .kci-card .kci-flip { cursor: grab; }
.ec-stage .kci-card .kci-flip.is-dragging { cursor: grabbing; }

/* ---------- the jump marks, beside the artifact ----------
   Kaʻo, 2026-08-31: "where are the jump time stamps like the template?
   implement those." Taken from layouts/detail/01-editorial-column-v2/intention,
   written against the classes the real page ships rather than copied with the
   prototype's own hardcoded words.

   The stage becomes a row: the card keeps its measured 9:16 width and the marks
   take the rest of the track, so nothing else in section 9 has to move. */
.ec-stage--chapters { display: flex; align-items: stretch; gap: 28px; min-width: 0; }
/* THE CARD GIVES THE MARKS THEIR ROOM, and the number is measured rather than
   guessed. The stage is 648px at 1440. Sized only by height the card came out
   506 wide and left the marks 114px, which is narrower than a 56px thumbnail
   plus two lines of words can use: they wrapped to one word a line. Capping the
   card at 62% of the row leaves about 200px for the marks, which is the
   prototype's own proportion (501 card against 151 of marks in a 684 row).
   The card is still sized by HEIGHT first; this only stops it taking the row. */
.ec-stage--chapters > .ec-mount { flex: 0 0 auto; max-width: 62%; }
.ec-chapters { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.ec-chapters__l {
  flex: 1 1 auto; display: flex; flex-direction: column;
  justify-content: space-between; gap: 8px; margin: 0; padding: 0; list-style: none;
}
.ec-ch {
  display: grid; grid-template-columns: 56px minmax(0, 1fr);
  grid-template-rows: auto auto; column-gap: 12px; row-gap: 2px;
  align-items: center; align-content: center;
  width: 100%; padding: 0; margin: 0; border: 0; background: transparent;
  cursor: pointer; text-align: start;
  color: var(--mus-ink-quiet, var(--wt-ink-quiet, #5f6a60));
  font: 700 13px/1 "Montserrat", system-ui, sans-serif;
  font-variant-numeric: tabular-nums; letter-spacing: .04em;
  transition: color 140ms ease;
}
.ec-ch img {
  grid-row: 1 / 3; display: block; width: 56px; height: auto;
  aspect-ratio: 9 / 16; object-fit: cover;
  /* the card's own crop: portrait crops to the BOTTOM, the house rule */
  object-position: center bottom;
  border-radius: 6px; background: var(--mus-surface-2, rgba(0, 0, 0, .12));
  box-shadow: 0 0 0 2px transparent; transition: box-shadow 140ms ease;
}
.ec-ch__t { align-self: end; }
.ec-ch__w {
  align-self: start; letter-spacing: 0;
  font: 400 13.5px/1.4 "Montserrat", system-ui, sans-serif;
  color: var(--mus-ink-soft, var(--wt-ink-soft, #4a5b55));
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; text-wrap: pretty;
}
.ec-ch:hover { color: var(--mus-ink, var(--wt-ink, #26302b)); }
.ec-ch:hover img { box-shadow: 0 0 0 2px var(--wt-accent, #2c5f4e); }
.ec-ch:focus-visible { outline: 3px solid var(--wt-accent, #2c5f4e); outline-offset: 3px; border-radius: 4px; }
.ec-ch.is-live { color: var(--wt-accent, #2c5f4e); }
.ec-ch.is-live .ec-ch__w { color: var(--mus-ink, var(--wt-ink, #26302b)); }
.ec-ch.is-live img { box-shadow: 0 0 0 3px var(--wt-accent, #2c5f4e); }
@media (prefers-reduced-motion: reduce) { .ec-ch, .ec-ch img { transition: none; } }
/* Under the two-track width the card is the whole measure, so the marks go
   below it and run as a wrapped row rather than a tall thin column. */
@media (max-width: 1199px) {
  .ec-stage--chapters { flex-direction: column; align-items: center; gap: 20px; }
  .ec-chapters { width: 100%; }
  .ec-chapters__l { flex-direction: row; flex-wrap: wrap; justify-content: flex-start; gap: 14px 18px; }
  .ec-ch { width: auto; max-width: 260px; }
}

/* ---------- a tag is a way in, not a label ----------
   The pill was an <li> of dead text while an article's keyword is a link. It is
   an <a> now, so it needs what kc-intentions.css:174 never had to give an <li>:
   no underline, a hit area, and states a keyboard can see. The colour and the
   chip shape still come from that rule; this only adds the link behaviour. */
.kci-taglist a.kci-tag { display: inline-block; text-decoration: none; transition: background-color 140ms ease, border-color 140ms ease; }
.kci-taglist a.kci-tag:hover { background: var(--wt-chip-hover, rgba(44, 95, 78, .14)); border-color: rgba(44, 95, 78, .34); }
.kci-taglist a.kci-tag:focus-visible { outline: 3px solid var(--wt-accent, #2c5f4e); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .kci-taglist a.kci-tag { transition: none; } }

/* ---------- the margin note reads as one family in BOTH modes ----------
   Kaʻo, 2026-08-31: "I like how dark mode has the same color range for the
   difference in tones within the side panel... the beige on green does not work
   as well in light mode. Try a subtle different green in the same color family."

   He is describing the token, exactly. Dark mode builds the tip box from
   `--wt-chip-bg: rgba(143, 208, 180, .10)`, the brand mint laid over the dark
   ground, so the panel and the box inside it are one hue at two depths. Light
   mode's `--wt-chip-bg` is `#ede7da`, a warm BEIGE, and the panel it sits in is
   `--wt-panel-bg: #eef3ec`, a soft sage. Beige on sage is two families, which
   is the thing he can see.

   So light mode gets the same CONSTRUCTION as dark: the brand green
   (--wt-accent #2c5f4e) at a low alpha over the panel, not a second hue. The
   border follows it. Scoped to this box so no chip elsewhere on the site
   changes. */
html:not([data-mode="batman"]) .ec-note .kci-tipbox {
  background: rgba(44, 95, 78, .07);
  border-color: rgba(44, 95, 78, .16);
}

/* AND THE PAGE ABOVE IT, which is the one that actually bit. kc-intentions.css
   caps `.kci-main > .kci-page` at 860px, so the column could never reach the
   measure however wide the window: measured 740px of column with 60/316 of
   margin, visibly shoved left, while the song page beside it sat at 960 with
   78/78. Only the column's own measure may decide the width, exactly as it
   does on song, poster and game. 0,3,0 beats kc-intentions.css's 0,2,0 and this
   file loads after it. */
.am-main.ec > .kci-page { max-width: none; }

/* ---------- the note, stacked ----------
   In one column it is simply the next block, in reading order: headline,
   artifact, note, words. `position: static` matters here rather than being a
   default worth stating: sticky in one column pins the panel over the words
   as they scroll past it, and `top` under static is inert, so the value
   cannot quietly come to mean something else at another width. That is trap 6
   in the handoff, in its other direction. */
.ec-note { position: static; top: auto; margin-top: 28px; }

/* ---------- the words ---------- */
.ec-words { padding-top: 40px; }
.ec-words > :first-child { margin-top: 0; }

/* ---------- narrow ---------- */
@media (max-width: 900px) {
  .ec-stage { margin-top: 22px; }
  .ec-words { padding-top: 30px; }
  html .am-main.ec { padding-top: 18px; padding-bottom: 56px; }
  .ec-col { width: 100%; }
}

/* ---------- TWO TRACKS, and only where they fit ----------
   1200px, NOT the rail's own 901px, and the number is measured rather than
   chosen. The content area beside a 260px rail with 32px gutters is
   (W - 324), the measure is 86% of it, and the note costs the words 256px of
   track plus a 56px gap:

     words = 0.86 x (W - 324) - 312

   At 1440 that is 648px of card and the design works. At 1024 it is 328px and
   at 901 it is 265px, which is SMALLER than the 260px thumbnail this whole
   rebuild exists to get rid of. Measured, both of them, before this rule was
   written. Splitting a page that has no room to be split does not give you an
   editorial column, it gives you the old page with extra steps.

   1200 is where the words clear 440px, which is the prototype's own floor for
   the artifact (--ec-stage-h's clamp minimum). Below it the column stays
   whole: at 1024 the card is 640px and the note reads under it. So the
   breakpoint costs a narrow laptop its margin note and hands back twice the
   artifact, which is the trade the design is asking for.

   grid-row: span 2 is placement, not decoration. .ec-note is written after
   .ec-stage and before the words, so auto placement puts it in track 2 of the
   artifact's row and the span carries it down past the words: a margin note
   for the whole read, never a 256px void beside seventeen stanzas. */
@media (min-width: 1200px) {
  /* BEHIND :has(), AND NOT AS A FLOURISH. The baked hero that a crawler and a
     reader with no JavaScript receive carries the headline and the whole song
     but NO note: the panel's facts are assembled client side. Splitting the
     grid unconditionally left those readers a 312px empty gutter down the
     right of every song. The two tracks now exist only when there is
     something to put in the second one. A browser with no :has() support
     simply keeps the single column, which is the correct fallback and not a
     broken one. */
  .ec-col:has(> .ec-note) { grid-template-columns: minmax(0, 1fr) var(--ec-note-w); }
  .ec-col:has(> .ec-note) > .ec-stage,
  .ec-col:has(> .ec-note) > .ec-words { grid-column: 1; }
  /* TOP ALIGNED, AND NOT STICKY. Kaʻo, 2026-08-31: "make sure that the content
     on the far right column stays top-aligned. There was one type of content
     that still had it scrolling down and went behind content that was full
     width towards the bottom."

     It was sticky, so it travelled with the reader, and on a page whose column
     ends in a FULL WIDTH block the note was still pinned when that block came
     up and sat on top of it. Measured at the foot of each page before this
     change, at 1440: the song page's note overlapped .sd-merch by 256x179, the
     game page's overlapped .gd-more by 256x142, and the poster page's
     overlapped its trailing block by 256x441. Only the Intention page was
     clean, because its column has nothing full width after the words.

     `align-self: start` alone is the whole fix: the note sits at the top of its
     track and stays there, so it can never arrive underneath something. The
     grid-row span stays, so the track still runs the height of the read and the
     note is still a margin note rather than a box wedged beside the artifact. */
  .ec-col > .ec-note {
    grid-column: 2; grid-row: span 2; align-self: start;
    position: static; top: auto;
  }
}
