/* Reflex Chess -- stylesheet for the /learn content pages.
 *
 * WHAT THIS IS NOT. It is not styles.css and it must never import it. The app
 * is a fixed-viewport game shell: 100dvh, one scrolling pane, a pinned tab bar,
 * overscroll locked. A page that Google sends a stranger to is the opposite --
 * a document that scrolls, that prints, that can be read with the tab bar
 * nowhere in sight. Sharing a stylesheet between the two would mean every app
 * layout change silently reflowing sixty content pages, so they are separate
 * files that agree on tokens rather than one file doing both jobs.
 *
 * WHAT IT SHARES. The palette, the radii and the font are copied from
 * styles.css by hand, deliberately duplicated rather than @imported, because
 * a content page must render with exactly one stylesheet request. It reads the
 * SAME data-theme="light" attribute theme.js writes, so a reader who set light
 * mode in the app and then lands here from a search result keeps their choice.
 *
 * NO EXTERNAL ANYTHING. One self-hosted font, no CDN, no analytics, no icon
 * font, diagrams inline as SVG. These pages have to render under a strict CSP,
 * they have to render on a plane, and they have to render before the network
 * gets a second round trip -- which is most of what Core Web Vitals measures.
 */

@font-face {
  font-family: "Fredoka";
  src: url("/fonts/fredoka-var-latin.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: dark light;

  --brand: #5b6cff;
  --brand-dk: #4453e8;
  --brand-lt: #8b96ff;
  --good: #29c17e;
  --bad: #ff4d5e;

  --sec-basics: #22c55e;
  --sec-tactics-1: #5b6cff;
  --sec-mates: #ff4d5e;
  --sec-tactics-2: #ff8f3f;
  --sec-quiet: #b07cff;
  --sec-endgames: #16b8c9;
  --sec-attack: #ff5fa2;
  --sec-openings: #ffc531;

  /* Matched to styles.css, which dropped from #0f1117 for the field below.
     These two surfaces are one product and a reader arriving from a search
     result and then tapping "open the app" must not see the page get lighter. */
  --bg: #080a11;
  --surface: #171a29;
  --card: #1c2033;
  --card-2: #232841;
  --ink: #f2f3f8;
  --dim: #99a0bd;
  --rule: #2b3049;
  --shadow: 0 3px 0 rgb(0 0 0 / .45);

  /* ---------- the field, abridged ----------
     THE SAME LIGHT, DELIBERATELY NOT THE SAME AMOUNT OF IT. The app paints a
     fixed, full-viewport field (styles.css, "the field"). These pages get the
     same base, the same three light sources and the same token names -- so they
     read as the same product -- but the field is anchored to the TOP of the
     document and scrolls away after roughly one screen.

     That difference is the difference between the two surfaces. The app is a
     fixed viewport: content is in cards over a backdrop, and a backdrop that
     never moves is correct. A motif guide is three thousand words in one
     column, set directly on the background with no card under it, and a
     gradient that follows the reader down four screens of body copy is a
     gradient behind body copy. Lighting the masthead, the h1 and the lede and
     then handing over to a flat page is the honest version: it makes the
     arrival feel like the app and leaves the reading alone.

     AND IT IS A BACKGROUND ON <html>, NOT A LAYER. No pseudo-element, no
     compositing layer, no extra box in the tree: the root element's background
     propagates to the canvas, background-size caps it at one and a bit screens,
     and background-attachment:scroll is what makes it scroll away. On pages
     whose entire performance story is "one stylesheet and some text", that
     matters more than the two lines it saves. */
  --field-base: #080a11;
  --glow-key:    91 108 255;
  --glow-accent: 150 110 255;
  --glow-floor:  30 150 210;
  --field-lit:   #262d67;
  --field-veil:  rgb(8 10 17 / .42);

  --field-image:
    /* THE HANDOVER, AND IT HAS TO BE THE TOP LAYER.
       The field is a 125vh box and below it the canvas is flat --field-base. The
       stack does not reach zero at its own bottom edge -- the floor glow is
       centred below it -- so without this the page had a hard horizontal rule
       ruled across it wherever 125vh happened to land, straight through a
       paragraph. Fading to the base colour by 96% guarantees the two meet at
       the same value and the seam cannot exist. */
    linear-gradient(180deg, transparent 46%, var(--field-base) 96%),
    radial-gradient(105% 58% at 106% 44%, rgb(var(--glow-accent) / .22), transparent 100%),
    radial-gradient(150% 46% at 42% 116%, rgb(var(--glow-floor) / .26), transparent 100%),
    radial-gradient(120% 80% at 22% 6%, transparent 12%, rgb(0 0 0 / .72) 92%),
    radial-gradient(150% 95% at 14% -10%, rgb(var(--glow-key) / .37), transparent 100%),
    linear-gradient(180deg, rgb(var(--glow-key) / .05), transparent 70%);

  --sq-light: #eef0f7;
  --sq-dark: #7c86c4;

  --radius: 1.15rem;
  --radius-lg: 1.6rem;
  --shelf: 4px;

  --sans: "Fredoka", ui-rounded, "SF Pro Rounded", "Segoe UI Variable",
          system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  --sec: var(--brand);
  --measure: 39rem;
}

:root[data-theme="light"] {
  --bg: #fbfbff;
  --surface: #f1f2fb;
  --card: #ffffff;
  --card-2: #f5f6fd;
  --ink: #14161f;
  --dim: #5f6479;
  --rule: #e2e4f2;
  --shadow: 0 3px 0 rgb(20 22 31 / .13);
  --sq-light: #f0d9b5;
  --sq-dark: #b58863;

  /* A third of the dark alphas, for the reason given in styles.css: on white a
     tint darkens, so every point of light here costs contrast instead of buying
     it. --dim on these pages is already the darker #5f6479, which is where the
     headroom comes from. */
  --field-base: #fbfbff;
  --glow-key:    91 108 255;
  --glow-accent: 255 120 180;
  --glow-floor:  30 170 210;
  --field-lit:   #eceeff;
  --field-veil:  rgb(251 251 255 / .72);

  --field-image:
    linear-gradient(180deg, transparent 46%, var(--field-base) 96%),
    radial-gradient(105% 58% at 106% 44%, rgb(var(--glow-accent) / .06), transparent 100%),
    radial-gradient(150% 46% at 42% 116%, rgb(var(--glow-floor) / .06), transparent 100%),
    radial-gradient(150% 95% at 14% -10%, rgb(var(--glow-key) / .10), transparent 100%);
}

/* ---------- frame ---------- */

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  background-color: var(--field-base);
  background-image: var(--field-image);
  background-repeat: no-repeat;
  /* One and a bit screens tall, pinned to the top of the document, scrolling
     with it. Below that the canvas is --field-base and the reading column is
     flat, which is the point. */
  background-size: 100% 125vh;
  background-position: 0 0;
  background-attachment: scroll;
}

body {
  margin: 0;
  /* Transparent, so <html>'s background is what reaches the canvas. A colour
     here would paint straight over the field. */
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.68;
  /* Rendering the whole page in one column with generous leading is the entire
     mobile strategy. There is no responsive layout below to break. */
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 1.15rem 4rem;
}

/* ---------- masthead ---------- */

.top {
  border-bottom: 1px solid var(--rule);
  /* A veil fading out at its own bottom edge, not an opaque --surface bar. The
     masthead sits exactly where the field is brightest, so filling it hid the
     one part of the light a reader sees first -- and a flat veil drew a hard
     seam across it. Same treatment, same token, as .statsbar in the app. */
  background: linear-gradient(180deg, var(--field-veil), transparent);
  margin-bottom: 2rem;
}

/* flex-wrap and the min-width:0 below are load-bearing, not tidying. A flex row
   whose children refuse to shrink is wider than the viewport on a 390px phone,
   and because the row is inside .wrap the whole document then scrolls
   sideways -- every paragraph on the page clipped at the right edge by a nav
   with two links in it. */
.top .wrap {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  padding-top: .85rem;
  padding-bottom: .85rem;
  min-height: 3.5rem;
}

.brand {
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: -.015em;
  color: var(--ink);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  white-space: nowrap;
}

.brand .mark {
  width: 1.55rem;
  height: 1.55rem;
  border-radius: .5rem;
  background: var(--brand);
  display: grid;
  place-items: center;
  color: #fff;
  font-size: .95rem;
  line-height: 1;
}

.top nav {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: .3rem 1rem;
  min-width: 0;
  font-size: .93rem;
}

.top nav a { white-space: nowrap; }

.top nav a { color: var(--dim); text-decoration: none; }
.top nav a:hover { color: var(--ink); }

/* ---------- breadcrumb ---------- */

.crumbs {
  font-size: .84rem;
  color: var(--dim);
  margin: 0 0 1.1rem;
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  align-items: center;
}

.crumbs a { color: var(--dim); text-decoration: none; border-bottom: 1px solid var(--rule); }
.crumbs a:hover { color: var(--ink); }
.crumbs span[aria-hidden] { opacity: .5; }

/* ---------- type ---------- */

h1 {
  font-size: clamp(1.85rem, 5.6vw, 2.45rem);
  line-height: 1.15;
  letter-spacing: -.022em;
  font-weight: 700;
  margin: 0 0 .7rem;
  text-wrap: balance;
}

h2 {
  font-size: clamp(1.3rem, 3.6vw, 1.5rem);
  line-height: 1.25;
  letter-spacing: -.015em;
  font-weight: 600;
  margin: 2.6rem 0 .75rem;
  text-wrap: balance;
}

h3 {
  font-size: 1.08rem;
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 1.9rem 0 .5rem;
}

p { margin: 0 0 1.05rem; }

.lede {
  font-size: 1.16rem;
  line-height: 1.55;
  color: var(--dim);
  margin-bottom: 1.6rem;
}

a { color: var(--brand); }
:root[data-theme="dark"] a, :root:not([data-theme="light"]) a { color: var(--brand-lt); }

ul, ol { margin: 0 0 1.05rem; padding-left: 1.3rem; }
li { margin-bottom: .45rem; }
li::marker { color: var(--sec); }

strong { font-weight: 600; }

code, .san {
  font-family: var(--mono);
  font-size: .9em;
  background: var(--card-2);
  padding: .1em .34em;
  border-radius: .3rem;
}

hr { border: 0; border-top: 1px solid var(--rule); margin: 2.6rem 0; }

/* ---------- meta strip under the h1 ---------- */

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  margin: 0 0 1.6rem;
  padding: 0;
  list-style: none;
  font-size: .82rem;
}

.facts li {
  margin: 0;
  color: var(--dim);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: .22rem .7rem;
}

.facts li b { color: var(--ink); font-weight: 600; }

/* ---------- the call to action ----------
   Carries the app's shelf: a hard-edged bottom edge in a darker tint, never a
   blurred shadow, so the button reads as the same object the app uses. */

.cta {
  display: block;
  margin: 2rem 0;
  padding: 1.15rem 1.3rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
}

.cta b { display: block; font-size: 1.06rem; font-weight: 600; margin-bottom: .2rem; }
.cta span { display: block; color: var(--dim); font-size: .93rem; line-height: 1.5; }

.btn {
  display: inline-block;
  margin-top: .9rem;
  padding: .72rem 1.35rem;
  border-radius: 999px;
  background: var(--btn-fill);
  color: var(--btn-ink);
  font-weight: 600;
  font-size: 1rem;
  text-decoration: none;
  box-shadow: 0 var(--shelf) 0 rgb(0 0 0 / .28);
  transition: transform .06s, box-shadow .06s;
}

.btn:active {
  transform: translateY(var(--shelf));
  box-shadow: 0 0 0 rgb(0 0 0 / .28);
}

/* A .btn is usually its own element, but inside a .cta it is a nested span --
   and `.cta span` above scores 0-1-1 against `.btn`'s 0-1-0, so it won, and the
   pill's label rendered dim grey on a saturated fill at about 1.6:1. Restating
   it here at 0-2-1 is what fixes it; reordering would not, because the loss is
   on specificity and not on source order. The ink itself comes from the token
   set with the section colours below, so this rule never has to know which
   section it is in. */
.cta span.btn { color: var(--btn-ink); }

/* ---------- board diagrams ---------- */

.pos {
  margin: 1.9rem 0;
  padding: 1.1rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.pos > h3 { margin-top: 0; }

.board {
  display: block;
  width: min(100%, 21rem);
  height: auto;
  margin: .3rem auto 1rem;
  border-radius: .55rem;
  overflow: hidden;
}

.turn {
  display: block;
  text-align: center;
  font-size: .86rem;
  color: var(--dim);
  margin: 0 0 .9rem;
}

.turn b { color: var(--ink); font-weight: 600; }

/* The solution is a <details> so the page is a puzzle first and an answer key
   second. A reader who is handed the answer has not solved anything, and the
   time-on-page difference between the two is the whole ranking signal. */
.pos details {
  border-top: 1px solid var(--rule);
  padding-top: .85rem;
}

.pos summary {
  cursor: pointer;
  font-weight: 600;
  font-size: .95rem;
  color: var(--sec);
  list-style: none;
}

.pos summary::-webkit-details-marker { display: none; }
.pos summary::before { content: "\25B8\00a0"; display: inline-block; transition: transform .12s; }
.pos details[open] summary::before { content: "\25BE\00a0"; }
.pos details > *:not(summary) { margin-top: .8rem; }
.pos details p:last-child { margin-bottom: 0; }

.line {
  font-family: var(--mono);
  font-size: .95rem;
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--ink);
  background: var(--card-2);
  border-radius: .55rem;
  padding: .7rem .85rem;
  overflow-x: auto;
  white-space: nowrap;
}

.line .key { color: var(--good); font-weight: 600; }

/* ---------- sibling / hub link lists ---------- */

.tiles {
  list-style: none;
  padding: 0;
  margin: 1.2rem 0 0;
  display: grid;
  gap: .6rem;
  /* min() so the track can never be wider than the column it sits in; a bare
     minmax(15rem,...) overflows a 320px phone. */
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
}

.tiles li { margin: 0; }

.tiles a {
  display: block;
  height: 100%;
  padding: .8rem .95rem;
  background: var(--card);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--sec);
  border-radius: .75rem;
  text-decoration: none;
  color: var(--ink);
  font-weight: 500;
  font-size: .97rem;
  line-height: 1.4;
}

.tiles a:hover { background: var(--card-2); }
.tiles a em { display: block; font-style: normal; font-weight: 400; color: var(--dim); font-size: .85rem; margin-top: .15rem; }

/* Section colours. Set data-sec on <body> and everything inside picks it up. */
[data-sec="basics"]    { --sec: var(--sec-basics); }
[data-sec="tactics-1"] { --sec: var(--sec-tactics-1); }
[data-sec="mates"]     { --sec: var(--sec-mates); }
[data-sec="tactics-2"] { --sec: var(--sec-tactics-2); }
[data-sec="quiet"]     { --sec: var(--sec-quiet); }
[data-sec="endgames"]  { --sec: var(--sec-endgames); }
[data-sec="attack"]    { --sec: var(--sec-attack); }
[data-sec="openings"]  { --sec: var(--sec-openings); }

/* THE BUTTON LABEL IS MEASURED, NOT PICKED.
 *
 * Every accent above is a saturated mid-tone, and white text clears WCAG AA
 * (4.5:1 at this size) on NONE of them -- measured, in a browser, against the
 * computed fill: openings 1.58, basics 2.28, tactics-2 2.27, endgames 2.40,
 * attack 2.83, quiet 2.93, mates 3.24. Only openings and basics were ever given
 * a dark label, so the other six shipped failing. This applies the same decision
 * the house already made for those two, to all of them.
 *
 * The brand indigo is the one that cannot be fixed with a label: white gives
 * 4.17 and near-black gives 4.32, so BOTH fail and there is no choice of ink
 * that passes. It is the fill that has to move. Darkened 6%, which clears 4.61
 * with white and is not a colour anyone will notice has changed.
 *
 * Two tokens rather than a pile of per-section overrides, because the label also
 * has to win against `.cta span` further up -- and one rule reading a variable
 * beats restating eight selectors at ever-higher specificity. */
:root                  { --btn-fill: #5666f0;              --btn-ink: #fff; }
[data-sec="tactics-1"] { --btn-fill: #5666f0;              --btn-ink: #fff; }
[data-sec="basics"]    { --btn-fill: var(--sec-basics);    --btn-ink: #14161f; }
[data-sec="mates"]     { --btn-fill: var(--sec-mates);     --btn-ink: #14161f; }
[data-sec="tactics-2"] { --btn-fill: var(--sec-tactics-2); --btn-ink: #14161f; }
[data-sec="quiet"]     { --btn-fill: var(--sec-quiet);     --btn-ink: #14161f; }
[data-sec="endgames"]  { --btn-fill: var(--sec-endgames);  --btn-ink: #14161f; }
[data-sec="attack"]    { --btn-fill: var(--sec-attack);    --btn-ink: #14161f; }
[data-sec="openings"]  { --btn-fill: var(--sec-openings);  --btn-ink: #14161f; }

/* ---------- FAQ ---------- */

.faq { margin-top: 1rem; }
.faq h3 { margin-top: 1.7rem; }
.faq h3:first-child { margin-top: .4rem; }

/* ---------- footer ---------- */

.foot {
  border-top: 1px solid var(--rule);
  margin-top: 3.5rem;
  padding-top: 1.4rem;
  font-size: .85rem;
  color: var(--dim);
}

.foot a { color: var(--dim); }
.foot p { margin-bottom: .6rem; }

/* ---------- print ----------
   "chess skewer puzzles pdf" is a real query on every motif in this course.
   Until there is a PDF, a page that prints cleanly is the honest answer, and
   the answer key is forced open so a printed sheet is not useless. */

@media print {
  :root { --bg: #fff; --ink: #000; --dim: #444; --card: #fff; --card-2: #f4f4f4; --rule: #bbb; }
  /* Explicit, not left to the colour reset: the field is a background-IMAGE on
     the root, and setting --bg white would not remove it. A printer asked to
     lay down a full-page indigo gradient is a printer asked to waste ink. */
  html { background: #fff !important; }
  .top, .cta, .tiles, .crumbs { display: none; }
  .pos { break-inside: avoid; border-color: #999; }
  .pos details > *:not(summary) { display: block !important; }
  .board { width: 14rem; }
  a { color: #000; text-decoration: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}


/* ============================================================
   SPACING PASS
   ------------------------------------------------------------
   The content pages were laid out with the vertical rhythm of an article --
   tight leading between paragraphs, headings close to what follows them -- and
   then filled with CARDS. A stack of bordered cards needs roughly double the
   separation that a stack of paragraphs does, because each card already has a
   border and a padding of its own, so the gap between two of them has to beat
   the padding INSIDE them or the eye reads the whole run as one object.

   The rule applied throughout: space between two cards is larger than the
   padding within a card, and space above a heading is larger than the space
   below it. Everything here is that, applied per component.
   ============================================================ */

/* Card runs. These are the lists of downloads and patterns that read as one
   solid block on the worksheets hub. */
.tiles, .cards, .sheets, .downloads, .patterns {
  display: grid;
  gap: .85rem;
  margin: 1.4rem 0 2.2rem;
}
.tile, .card, .sheet, .download, .pattern {
  padding: 1rem 1.1rem;
  border-radius: var(--radius-lg, 1rem);
}

/* Any anchor styled as a block card, whatever the generator called it. Written
   as an attribute-agnostic rule because five generators wrote these pages and
   they do not agree on class names. */
main a[class*="card"],
main a[class*="tile"],
main a[class*="sheet"],
main a[class*="row"] {
  margin-bottom: .85rem;
}
main a[class*="card"]:last-child,
main a[class*="tile"]:last-child,
main a[class*="sheet"]:last-child { margin-bottom: 0; }

/* Headings own what follows them. The gap above a section heading is now
   comfortably larger than the gap below it, which is what makes a long page
   scan as sections rather than as one column of text. */
main h2 { margin: 3rem 0 .85rem; }
main h3 { margin: 2.2rem 0 .6rem; }
main h2 + p, main h3 + p { margin-top: 0; }
main h2:first-child, main h3:first-child { margin-top: 0; }

/* Body copy gets a little more air. 1.05rem between paragraphs is an article
   measure; these pages interleave prose with diagrams and cards. */
main p { margin-bottom: 1.15rem; line-height: 1.65; }
main ul, main ol { margin-bottom: 1.4rem; }
main li { margin-bottom: .6rem; line-height: 1.6; }

/* Worked positions. Each is a diagram plus an answer, so it is a card in all
   but name and needs the separation of one. */
.pos { margin: 2.4rem 0; }
.pos + .pos { margin-top: 2.8rem; }

/* The call to action should not butt against the paragraph above it. */
.cta { margin: 2.6rem 0; }

/* The fact pills under an h1 were sitting on the description. */
.facts { margin-bottom: 2.2rem; }

/* Section rules were doing the job of whitespace. Now they support it. */
hr { margin: 3.2rem 0; }

/* The footer had no separation from the last section at all. */
main + footer, footer { margin-top: 3.5rem; padding-top: 1.6rem; }

/* ---------- book list ----------
   Lives here rather than in styles.css: /books is a content page and links
   seo.css, so rules put in the app's stylesheet never reached it and the list
   rendered as plain bullets with the blurb running into the title. */
.books { list-style: none; margin: 1.4rem 0 2.2rem; padding: 0; display: grid; gap: .7rem; }
.book {
  background: var(--card); border: 1px solid var(--rule);
  border-radius: .9rem; padding: .9rem 1.05rem;
  display: flex; flex-direction: column; gap: .25rem;
}
.book a {
  font-weight: 700; font-size: 1rem; line-height: 1.3;
  text-decoration: none; color: var(--ink);
}
.book a:hover { color: var(--sec); text-decoration: underline; }
.book span { font-size: .85rem; color: var(--dim); line-height: 1.5; }
