/* What's New — the creators' board.
 *
 * Colour is declared as a role, never as a literal, per DESIGN.md: every value here resolves
 * through --ks-market-* or --ks-accent*, so the page follows both the theme and the resident's
 * chosen accent. The one exception is the label on a filled accent button, which is #fff for
 * the same reason .ks-info-action's is: it sits on the accent, not on the page, so a themed ink
 * would go cream at night on a still-blue pill. Both are covered by accent-contrast.test.mjs.
 *
 * Type is the site's three families and no others: Bricolage Grotesque for headings and titles,
 * Literata for anything a person reads in sentences, Cabin for controls and small print.
 *
 * **There is one card, and it does not know what a platform is.** The page used to have a hero
 * for the article, a shelf for the video and a grid for the clips, each with its own rules, and
 * the CSS said plainly which format mattered most. What is left is a single `.ks-wn__card` with
 * two modifiers, and neither of them is a brand: `--lead` is "this is the newest thing on the
 * board", and `--upright` is "this was filmed in portrait". The first is decided by a timestamp
 * and the second by physics.
 */

body { margin: 0; background: var(--ks-market-paper); color: var(--ks-market-ink); }

.ks-wn-header { position: sticky; top: 0; z-index: 2; background: color-mix(in srgb, var(--ks-market-paper) 96%, transparent); border-bottom: 1px solid var(--ks-market-line); }
.ks-wn-header div { display: flex; align-items: center; gap: 12px; margin: auto; max-width: 1120px; padding: 12px clamp(16px, 4vw, 28px); }
.ks-wn-header img { width: 40px; height: 40px; border-radius: 50%; }
.ks-wn-header a.account { margin-left: auto; color: var(--ks-accent-ink); font: 700 14px Cabin, sans-serif; text-decoration: none; }

.ks-wn { max-width: 1120px; margin: auto; padding: clamp(30px, 5vw, 64px) clamp(16px, 4vw, 28px) 96px; }
.ks-wn h1 { margin: 0 0 12px; max-width: 14ch; font: 800 clamp(32px, 5.4vw, 56px)/1.02 'Bricolage Grotesque', sans-serif; letter-spacing: -.03em; text-wrap: balance; }
.ks-wn__lede { margin: 0 0 10px; max-width: 62ch; color: var(--ks-market-muted); font: 400 clamp(15px, 1.7vw, 19px)/1.55 Literata, serif; }
/* A credit line for the whole page, under the lede: the board says out loud whose work this is. */
.ks-wn__credit { margin: 0 0 10px; color: var(--ks-market-muted); font: 400 14px/1.55 Literata, serif; }
/* The earning rule, stated once at the top rather than repeated on every card. */
.ks-wn__rule { margin: 0 0 clamp(26px, 4vw, 46px); color: var(--ks-market-muted); font: 600 14px/1.5 Cabin, sans-serif; }

.ks-wn section + section { margin-top: clamp(38px, 5.5vw, 72px); }
.ks-wn h2 { margin: 0 0 8px; font: 800 clamp(21px, 2.8vw, 30px)/1.05 'Bricolage Grotesque', sans-serif; letter-spacing: -.02em; }
.ks-wn__section-body { margin: 0 0 clamp(18px, 2.4vw, 26px); max-width: 58ch; color: var(--ks-market-muted); font: 400 clamp(14px, 1.5vw, 16px)/1.55 Literata, serif; }

/* ---- The lead section ---------------------------------------------------
 *
 * The newest post across its own full width, then the rest in a row beneath it. Which post leads
 * is decided by `featuredAt` and nothing else - there is no rule here that could prefer one
 * format, which is the entire reason the old hero/shelf/grid split had to go.
 *
 * The lead spanned two columns *and two rows* for about an hour, which measured 1252px tall on a
 * 721px viewport: a card stretched to the height of two stacked ones beside it, so the single
 * most important post on the board could not be seen without scrolling past it. A full-width row
 * of its own has no such coupling - its height is its own content and nothing else's.
 */
/* `align-items: start` is load-bearing. A grid stretches every cell to its row height, so one
 * upright TikTok beside three articles made all four 863px tall - the articles carrying 400px of
 * empty card below their button. Each card is now its own height and the row ends ragged, which
 * is the honest shape for a board mixing portrait and landscape. */
.ks-wn__today { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); align-items: start; gap: clamp(12px, 1.8vw, 20px); }
.ks-wn__today .ks-wn__card--lead { grid-column: 1 / -1; }

/* ---- Everything else ----------------------------------------------------
 *
 * One stream, in one order, with the ways in sitting above it. Drawn at archive scale: a fixed
 * thumbnail beside two lines, so an older post is plainly a smaller thing than the newest ones
 * without being a different kind of object. Two per row on a wide screen, so a season's posts are
 * a short scroll rather than a second board.
 *
 * A fixed square thumbnail also settles the portrait/landscape problem the big cards have to live
 * with: every row is the same height whatever the post was filmed on. It crops, and at 96px that
 * is what a thumbnail is for - the full frame is one tap away on the creator's own page.
 */
.ks-wn__stream { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); align-items: start; gap: clamp(10px, 1.4vw, 16px); }


.ks-wn__controls { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 14px; margin-bottom: clamp(16px, 2.2vw, 24px); }
.ks-wn__filter { min-width: 0; }
.ks-wn__filter-label { display: block; margin-bottom: 8px; color: var(--ks-market-muted); font: 700 11px/1 Cabin, sans-serif; letter-spacing: .08em; text-transform: uppercase; }
.ks-wn__chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* A creator chip. The accent marks the *chosen* one and nothing else on this row, so the
 * selected state is legible without reading the labels. `aria-pressed` is the single source of
 * that state: the CSS selects on the same attribute a screen reader announces, so the paint and
 * the announcement cannot drift apart - which is exactly how a toggle ends up looking on while
 * sending off. */
.ks-wn__chip { min-height: 38px; padding: 8px 15px; border: 1px solid var(--ks-market-line-strong); border-radius: 999px; background: var(--ks-market-paper); color: var(--ks-market-ink); font: 600 14px Cabin, sans-serif; cursor: pointer; }
.ks-wn__chip:hover { border-color: var(--ks-accent); }
.ks-wn__chip:focus-visible { outline: 3px solid var(--ks-accent-tint); outline-offset: 2px; }
.ks-wn__chip[aria-pressed="true"] { border-color: var(--ks-accent); background: var(--ks-accent-tint); color: var(--ks-accent-ink); font-weight: 700; }

/* Deliberately not a filled accent pill. The accent has one job on this page and it is the
 * button that takes a reader to a creator; a second filled pill beside it would compete with
 * every card below. */
.ks-wn__surprise { min-height: 44px; padding: 11px 20px; border: 1px solid var(--ks-accent); border-radius: 999px; background: var(--ks-market-paper); color: var(--ks-accent-ink); font: 700 15px Cabin, sans-serif; cursor: pointer; }
.ks-wn__surprise:hover { background: var(--ks-accent-tint); }
.ks-wn__surprise:focus-visible { outline: 3px solid var(--ks-accent-tint); outline-offset: 2px; }
/* :not([hidden]), because a class display beats the UA's [hidden] rule and the button would never go. */
.ks-wn__more:not([hidden]) { display: block; margin: clamp(14px, 2vw, 22px) auto 0; }
.ks-wn__surprise[disabled] { opacity: .5; cursor: default; border-color: var(--ks-market-line-strong); color: var(--ks-market-muted); }

/* ---- The card ----------------------------------------------------------- */

.ks-wn__card { display: flex; flex-direction: column; border: 1px solid var(--ks-market-line); border-radius: 20px; background: var(--ks-market-paper); overflow: hidden; }
.ks-wn__card:hover { box-shadow: var(--ks-market-shadow); }
.ks-wn__card-media { aspect-ratio: 16/10; background-color: var(--ks-market-cream); background-size: cover; background-position: center; }
/* Shot in portrait, shown in portrait. Forcing a 16:9 crop onto a TikTok cuts the caption off
 * the top of every one of them - and the reverse was live until 30 August 2026, when YouTube was
 * cropped to 9:16 beside the clips and lost the sides of every frame. A long-form video is as
 * wide as an article's photograph, so it is drawn that way. */
.ks-wn__card--upright .ks-wn__card-media { aspect-ratio: 9/16; }
.ks-wn__card-body { display: flex; flex-direction: column; gap: 7px; padding: 15px; flex: 1; }
.ks-wn__card-body strong { font: 700 16px/1.28 'Bricolage Grotesque', sans-serif; }
.ks-wn__card-body p { margin: 0; color: var(--ks-market-muted); font: 400 13px/1.5 Literata, serif; }
.ks-wn__card-body .ks-wn__byline { margin: auto 0 0; }
.ks-wn__card .ks-wn__open { align-self: flex-start; min-height: 40px; padding: 9px 16px; font-size: 14px; }

/* The lead card. Bigger picture, bigger headline, and on a wide screen the words sit beside the
 * image rather than under it - at two columns wide a stacked 16:9 photograph would push the
 * headline most of a screen down. It is bigger because it is *newest*, never because of what it
 * is: an article, a TikTok and a video all look like this when they lead. */
.ks-wn__card--lead .ks-wn__card-media { aspect-ratio: 16/9; }
.ks-wn__card--lead.ks-wn__card--upright .ks-wn__card-media { aspect-ratio: 4/5; }
.ks-wn__card--lead .ks-wn__card-body { gap: 9px; padding: clamp(16px, 2vw, 24px); }
.ks-wn__card--lead .ks-wn__card-body strong { font-size: clamp(20px, 2.4vw, 27px); line-height: 1.14; letter-spacing: -.02em; }
.ks-wn__card--lead .ks-wn__card-body p { font-size: clamp(14px, 1.4vw, 16px); }
.ks-wn__card--lead .ks-wn__open { min-height: 46px; padding: 12px 22px; font-size: 15px; }

@media (min-width: 861px) {
  .ks-wn__card--lead { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: stretch; }
  /* A capped height, not a stretched one. `max-height` is what stops a portrait TikTok in the
   * lead slot drawing a 9:16 frame across half the page; `cover` crops the *frame*, which is the
   * one place cropping is right, because the alternative is a lead card two screens tall. */
  .ks-wn__card--lead .ks-wn__card-media { aspect-ratio: auto; height: 100%; min-height: 300px; max-height: 420px; }
  .ks-wn__card--lead .ks-wn__card-body { justify-content: center; }
}

/* Secondary cards share a row, so their text is clamped and they end up the same height whatever
 * the excerpt happens to be. Without this a long excerpt in one column set the row height for
 * every card beside it. `-webkit-line-clamp` is the only cross-browser line clamp there is. */
.ks-wn__today .ks-wn__card:not(.ks-wn__card--lead) .ks-wn__card-body strong,
.ks-wn__stream .ks-wn__card-body strong { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.ks-wn__today .ks-wn__card:not(.ks-wn__card--lead) .ks-wn__card-body > p,
.ks-wn__stream .ks-wn__card-body > p { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }

/* The kind, and it is information rather than rank: a reader deserves to know whether a tap
 * costs them nine minutes or thirty seconds. Neutral on purpose - it takes the page's own ink on
 * its own cream, not the accent and not a brand colour, because a row of platform-coloured
 * badges would rebuild the hierarchy this page was rewritten to remove. */
.ks-wn__kind { align-self: flex-start; padding: 4px 10px; border-radius: 999px; background: var(--ks-market-cream); color: var(--ks-market-ink); font: 700 11px/1.45 Cabin, sans-serif; letter-spacing: .07em; text-transform: uppercase; }

/* The creator's credit, and it is deliberately not small print.
 *
 * These posts are somebody else's work, carried here with their permission, and the board is
 * asking creators to take part. A 13px muted line under the excerpt - which is what this was -
 * gives their brand less weight than the publication date beside it. The credit sits *above*
 * the headline, at the size of a name rather than a footnote, so the card reads as "Daniel Food
 * Diary's piece, on Kampung Spirit" instead of an uncredited excerpt.
 *
 * The name takes --ks-market-ink, the page's full-strength text colour, not --ks-market-muted.
 * That is the whole point: muted is for metadata, and the source is not metadata here. */
.ks-wn__source { display: flex; align-items: center; gap: 11px; margin: 0; }
.ks-wn__monogram { display: grid; place-items: center; flex: none; width: 42px; height: 42px; border-radius: 50%; background: var(--ks-accent-tint); color: var(--ks-accent-ink); font: 800 18px/1 'Bricolage Grotesque', sans-serif; }
.ks-wn__source-lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ks-wn__source-label { color: var(--ks-market-muted); font: 700 11px/1 Cabin, sans-serif; letter-spacing: .08em; text-transform: uppercase; }
.ks-wn__source-name { color: var(--ks-market-ink); font: 700 17px/1.2 'Bricolage Grotesque', sans-serif; }
.ks-wn__source-handle { color: var(--ks-market-muted); font: 600 13px/1.3 Cabin, sans-serif; }

.ks-wn__card .ks-wn__source { gap: 9px; }
.ks-wn__card .ks-wn__monogram { width: 34px; height: 34px; font-size: 15px; }
.ks-wn__card .ks-wn__source-name { font-size: 15px; }
.ks-wn__card .ks-wn__source-handle { font-size: 12px; }
.ks-wn__card--lead .ks-wn__monogram { width: 42px; height: 42px; font-size: 18px; }
.ks-wn__card--lead .ks-wn__source-name { font-size: 17px; }

/* Where the click goes, said plainly. A reader who can see the destination is likelier to follow
 * it, and the creator is likelier to be credited for the visit in their own analytics. */
.ks-wn__domain { display: inline-flex; align-items: center; gap: 6px; margin-top: 2px; color: var(--ks-market-muted); font: 600 12px/1.4 Cabin, sans-serif; }
.ks-wn__domain strong { color: var(--ks-market-ink); font-weight: 700; }
.ks-wn__card--lead .ks-wn__domain { font-size: 13px; }

/* The headline is a real link to the creator's page, so the biggest text on the card is also a
 * way to reach them - keyboard reachable, middle-clickable, and copyable as a normal link. It
 * inherits its colour rather than taking the link accent: it is a headline first. */
.ks-wn__title-link { color: inherit; text-decoration: none; }
.ks-wn__title-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.ks-wn__title-link:focus-visible { outline: 3px solid var(--ks-accent-tint); outline-offset: 3px; border-radius: 4px; }

/* The picture is a third way through. It carries aria-hidden and tabindex="-1" so it is not a
 * second tab stop announcing the same destination as the headline above it. */
.ks-wn__media-link { display: block; cursor: pointer; }

.ks-wn__byline { display: block; color: var(--ks-market-muted); font: 600 13px/1.4 Cabin, sans-serif; }

.ks-wn__open { display: inline-flex; align-items: center; gap: 8px; min-height: 46px; padding: 12px 22px; border: 0; border-radius: 999px; background: var(--ks-accent); color: #fff; font: 700 15px Cabin, sans-serif; text-decoration: none; cursor: pointer; }
.ks-wn__open:hover { background: var(--ks-accent-press); }
.ks-wn__open:focus-visible { outline: 3px solid var(--ks-accent-tint); outline-offset: 2px; }
.ks-wn__open[disabled] { opacity: .6; cursor: default; }
/* Live answer from the API — awarded, already earned, or sign in. Assertive is wrong here: it
 * would interrupt a screen reader mid-sentence for a two-point confirmation. */
.ks-wn__note { display: block; margin-top: 10px; min-height: 1.4em; color: var(--ks-market-muted); font: 600 13px/1.4 Cabin, sans-serif; }
.ks-wn__note[data-tone="good"] { color: var(--ks-market-positive); }

/* A section with no posts yet says so, rather than collapsing to nothing and reading as a bug. */
.ks-wn__empty { margin: 0; padding: clamp(20px, 3vw, 30px); border: 1px dashed var(--ks-market-line-strong); border-radius: 20px; color: var(--ks-market-muted); font: 400 15px/1.55 Literata, serif; }

/* ---- The archive card -------------------------------------------------
 *
 * Everything in `More from around Punggol`, drawn smaller than the newest posts on purpose:
 * a fixed thumbnail beside two lines, so an older post is plainly a lesser thing without
 * being a different kind of object. It overrides the base card and is written after it.
 */
.ks-wn__card--compact { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: stretch; border-radius: 16px; }
.ks-wn__card--compact .ks-wn__card-media { aspect-ratio: auto; height: 100%; min-height: 96px; }
/* The one place the upright rule is deliberately not applied: a 9:16 frame in a 96px column is
 * 170px tall and would set the height of every archive row. */
.ks-wn__card--compact.ks-wn__card--upright .ks-wn__card-media { aspect-ratio: auto; }
.ks-wn__card--compact .ks-wn__card-body { gap: 5px; padding: 11px 13px; justify-content: center; }
.ks-wn__card--compact .ks-wn__card-body strong { font-size: 15px; line-height: 1.25; -webkit-line-clamp: 2; }
/* The excerpt and the destination are the first things to go at this size. The credit is not:
 * whose work it is stays on every card, at every scale, which is the whole point of the board. */
.ks-wn__card--compact .ks-wn__card-body > p { display: none; }
.ks-wn__card--compact .ks-wn__domain { display: none; }
.ks-wn__card--compact .ks-wn__monogram { width: 26px; height: 26px; font-size: 12px; }
.ks-wn__card--compact .ks-wn__source { gap: 8px; }
.ks-wn__card--compact .ks-wn__source-name { font-size: 14px; }
.ks-wn__card--compact .ks-wn__source-label,
.ks-wn__card--compact .ks-wn__source-handle { display: none; }
.ks-wn__card--compact .ks-wn__byline { margin: 0; font-size: 12px; }
.ks-wn__card--compact .ks-wn__kind { padding: 3px 8px; font-size: 10px; }
/* Smaller, but not below the touch minimum where it matters. This was written as 34px with a
 * comment claiming it still cleared 44 - it measured 34 on a phone, which is the claim being
 * wrong rather than the number being wrong. It stays compact on a pointer device and goes to 44
 * at the mobile breakpoint, where a finger is what reaches it. */
.ks-wn__card--compact .ks-wn__open { min-height: 36px; padding: 8px 14px; font-size: 13px; }
.ks-wn__card--compact .ks-wn__note:empty { display: none; margin: 0; }

@media (max-width: 1080px) {
  .ks-wn__today { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  .ks-wn__today { grid-template-columns: minmax(0, 1fr); }
  .ks-wn__stream { grid-template-columns: minmax(0, 1fr); }
  .ks-wn__controls { align-items: stretch; }
  .ks-wn__surprise { width: 100%; }
  .ks-wn__card--compact .ks-wn__open { min-height: 44px; padding: 11px 16px; }
}
.ks-wn__monogram--logo { background: var(--ks-market-paper) center / cover no-repeat; box-shadow: inset 0 0 0 1px var(--ks-market-line); }
