/* The Daily Puzzle. Loaded from index.html alongside rush.css, so the service
 * worker's index scan precaches it without anyone editing sw.js.
 *
 * TWO SURFACES, ONE STYLESHEET. `.dailycard` is the strip at the top of the
 * Learn tab -- the thing that has to be unmissable on a screen nobody came to
 * look at it on -- and `.daily` is the full-screen view it opens. They share
 * the week strip (`.dw`) because it is the same seven days meaning the same
 * seven things in both places, and a reader must not have to learn the marks
 * twice.
 *
 * IT USES THE HOUSE TOKENS AND ADDS NONE. Every colour here is a var() out of
 * styles.css, which is why the daily follows the theme toggle and every board
 * skin for free, and why it cannot drift when those change.
 */

/* ---------- the card on the Learn tab ----------
 *
 * FIRST THING UNDER THE HEADER, above the greeting and the tree. It is the one
 * element on the home screen with an expiry date on it, and anything with an
 * expiry date that sits below the fold has none.
 *
 * A LINK, NOT A BUTTON. It goes to a hash route, so an anchor gets middle-click,
 * long-press-to-copy and the browser's own focus ring at no cost.
 */

.dailycard {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin: .9rem 0 .2rem;
  padding: .8rem .9rem;
  border-radius: var(--radius);
  background: var(--card);
  color: inherit;
  text-decoration: none;
  box-shadow: 0 var(--shelf) 0 var(--rule);
  /* The accent is the state. An unplayed daily wears brand indigo down its left
     edge and a played one goes quiet, so the card answers "do I still need to
     do this" from peripheral vision, before any of the words are read. */
  border-left: 4px solid var(--brand);
  transition: transform .1s ease, box-shadow .1s ease;
}
.dailycard:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--rule); }
.dailycard.solved { border-left-color: var(--good); }
.dailycard.missed { border-left-color: var(--bad); }
.dailycard.solved, .dailycard.missed { background: var(--surface); }

.dc-left { display: flex; flex-direction: column; gap: .3rem; flex: 1 1 auto; min-width: 0; }

.dc-kicker {
  font-size: .72rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--brand-lt);
}
.dailycard.solved .dc-kicker { color: var(--good); }
.dailycard.missed .dc-kicker { color: var(--bad); }

.dc-line {
  /* Stated, not inherited. styles.css colours anchors with
     `:root:not([data-theme="light"]) a`, which outranks anything this file can
     say on `.dailycard` itself -- so the card's headline came out link-blue.
     Setting it on the child element wins, because inheritance never beats a
     rule that names the element. */
  color: var(--ink);
  font-size: .95rem; font-weight: 600; line-height: 1.25;
  /* One line, always. Two of the three state strings are longer than a phone is
     wide, and a card that changes height when yesterday's result changes makes
     the whole tree jump on load. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.dc-week { display: flex; gap: 3px; margin-top: .1rem; }
.dc-week .dw { width: 13px; height: 6px; border-radius: 3px; }

.dc-streak {
  flex: none;
  display: flex; flex-direction: column; align-items: center;
  min-width: 2.6rem;
  color: var(--dim);
}
.dc-streak b { font-size: 1.35rem; font-weight: 700; line-height: 1; }
.dc-streak span { font-size: .68rem; font-weight: 600; }
.dc-streak.on { color: var(--streak); }

/* ---------- the full-screen view ---------- */

.daily {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.daily-top {
  display: flex; align-items: center; gap: .6rem;
  padding: max(.6rem, env(safe-area-inset-top)) var(--pad) .7rem;
}
.daily-title {
  flex: 1 1 auto; text-align: center;
  font-weight: 700; font-size: 1.05rem; letter-spacing: -.02em;
}
/* Balances the close button so the title is optically centred without a grid. */
.daily-spacer { width: var(--tap); flex: none; }

.daily-body { padding-bottom: 0; }

/* ---------- the intro ---------- */

.daily-card {
  background: var(--card);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.1rem 1.2rem;
  box-shadow: var(--lift);
}
.daily-card h1 { margin: .1rem 0 .3rem; font-size: 1.6rem; }

.daily-date {
  margin: 0;
  font-size: .74rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--brand-lt);
}

.daily-rules {
  list-style: none;
  margin: 1rem 0 1.2rem;
  padding: 0;
  display: flex; flex-direction: column; gap: .65rem;
}
.daily-rules li {
  font-size: .9rem; line-height: 1.45; color: var(--dim);
  padding-left: 1.1rem; position: relative;
}
.daily-rules li::before {
  content: ""; position: absolute; left: 0; top: .5em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand);
}
.daily-rules b { color: var(--ink); font-weight: 700; }

.daily-err {
  margin: 0 0 .8rem; font-size: .88rem; font-weight: 600; color: var(--bad);
}

/* ---------- the streak block ----------
 * Shown on the intro, on the result, and on the already-played screen. It is
 * the product, so it is never more than one scroll away from wherever the
 * reader is standing.
 */

.daily-streak {
  display: flex; align-items: baseline; justify-content: center; gap: .5rem;
  margin: 1.4rem 0 .5rem;
  color: var(--dim);
}
.daily-streak .ds-n {
  font-size: 2.4rem; font-weight: 700; line-height: 1; letter-spacing: -.03em;
}
.daily-streak.on { color: var(--streak); }
.daily-streak .ds-n { display: inline-flex; align-items: center; gap: .1rem; }
.ds-flame { display: inline-flex; }
.ds-flame svg { width: 1.7rem; height: 1.7rem; }
.ds-label { font-size: .9rem; font-weight: 600; }

.daily-week {
  display: flex; justify-content: center; gap: .45rem;
  margin: .2rem 0 .6rem;
}
.daily-week .dw {
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  width: 2rem;
}
.daily-week .dw b {
  font-size: .68rem; font-weight: 700; color: var(--dim);
}
.daily-week .dw i { width: 100%; height: .55rem; border-radius: .3rem; }

/* THE FOUR MARKS. A skipped day is a hollow slot rather than a missing one:
   an absent element would close the gap and make a broken week look unbroken,
   which is the one thing the strip exists to prevent. */
.dw i, .dc-week .dw { background: var(--rule); }
.dw.solved i, .dc-week .dw.solved { background: var(--good); }
.dw.missed i, .dc-week .dw.missed { background: var(--bad); opacity: .8; }
.dw.skipped i {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--rule);
}
/* Today, still open. It pulses so that the one cell the reader can still do
   something about is the one that moves. */
.dw.today i, .dc-week .dw.today {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--brand);
  animation: dailyPulse 2s ease-in-out infinite;
}
/* On the card the marks are 13x6px, where a 2px ring fills the shape and a
   hollow cell is indistinguishable from a full one. So the card states the
   three past outcomes as solid colour and keeps the ring for today only, which
   is the one cell that has to look different from all of them. */
.dc-week .dw.skipped { background: var(--rule); opacity: .55; }
@keyframes dailyPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .dw.today i, .dc-week .dw.today { animation: none; }
}

.daily-note {
  text-align: center;
  font-size: .82rem; line-height: 1.45; color: var(--dim);
  margin: 0 auto .4rem;
  max-width: 22rem;
}

/* ---------- playing ---------- */

.daily-play {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;
  gap: .8rem;
  padding: 0 var(--pad);
}

.daily-prompt {
  margin: 0;
  font-size: 1.05rem; font-weight: 700; letter-spacing: -.015em;
  text-align: center;
  /* Reserved rather than grown. The prompt swaps between four different
     strings while the reader is looking at the board directly beneath it, and a
     line that changes height would slide the position they are calculating. */
  min-height: 1.5rem;
}
.daily-prompt.dim  { color: var(--dim); font-weight: 600; }
.daily-prompt.good { color: var(--good); }
.daily-prompt.bad  { color: var(--bad); }
.daily-prompt.arm  { color: var(--streak); }

/* The commit row. See daily.js: with one attempt, a move is armed rather than
   played, and this is where it is confirmed by name.

   FIXED HEIGHT, because it holds either one line of small print or two stacked
   buttons, and the board must not move between those two states -- the reader
   is arming a move they are still looking at. */
.daily-commit {
  width: 100%;
  max-width: min(30rem, 100% - 1rem);
  min-height: 7.2rem;
  display: flex; flex-direction: column; gap: .5rem; justify-content: center;
}
.daily-tip {
  margin: 0; text-align: center;
  font-size: .85rem; color: var(--dim);
}
.daily-commit .btn.big { padding: .95rem; }
.daily-commit .btn.ghost { padding: .6rem; min-height: 2.4rem; font-size: .9rem; }

/* ---------- the result ---------- */

.daily-verdict { text-align: center; margin-bottom: .6rem; }
.daily-verdict h1 { margin: .4rem 0 .2rem; }
.daily-verdict.win h1  { color: var(--good); }
.daily-verdict.loss h1 { color: var(--bad); }
.daily-verdict .sub { margin: 0; }

/* The solution, as chips. Clicking one jumps the board to that ply, which makes
   the row a stepper and a summary at the same time -- and means the reader who
   missed it can sit on the move they did not see for as long as they want. */
.daily-line {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .35rem;
  margin: .8rem 0 .4rem;
}
.sanchip {
  font: inherit; font-weight: 700; font-size: .85rem;
  font-family: var(--mono);
  background: var(--card); color: var(--dim);
  border: 0; border-radius: .6rem;
  padding: .35rem .55rem;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
/* The solver's own moves carry the accent; the opponent's replies stay quiet.
   Half the line is not the answer and colouring it the same would suggest it
   was. */
.sanchip.mine { color: var(--ink); box-shadow: inset 0 0 0 1px var(--rule); }
.sanchip.now { background: var(--brand); color: #fff; box-shadow: none; }
/* Quiet, and deliberately not the amber .btn.hint it started as: amber means
   "help" everywhere else in this app, and replaying a solution the reader has
   already been shown is not help. It sits in the row as another chip. */
.sanchip.replay {
  font-family: var(--sans);
  margin-left: .35rem;
  color: var(--brand-lt);
  box-shadow: inset 0 0 0 1px var(--rule);
}
:root[data-theme="light"] .sanchip.replay { color: var(--brand-dk); }

/* The teaching block, which is what makes one attempt defensible. */
.daily-teach {
  background: var(--card);
  border-radius: var(--radius);
  padding: 1rem;
  margin: 1rem 0 .2rem;
}
.dt-kicker {
  margin: 0;
  font-size: .72rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--dim);
}
.daily-teach h2 { margin: .25rem 0 .5rem; font-size: 1.2rem; }
.daily-teach p { font-size: .92rem; }
/* The subject changes here: everything above is about THIS position, everything
   from here down is about the skill the reader is being sent to. A hairline is
   enough to stop the two reading as one paragraph. */
.dt-explain {
  color: var(--dim); font-size: .88rem; margin-bottom: 0;
  border-top: 1px solid var(--rule);
  padding-top: .8rem;
}

.dt-drill {
  display: flex; align-items: center; gap: .4rem;
  margin-top: .9rem;
  padding: .7rem .85rem;
  border-radius: .9rem;
  background: var(--card-2);
  color: var(--brand-lt);
  font-weight: 700; font-size: .92rem;
  text-decoration: none;
  min-height: var(--tap); box-sizing: border-box;
}
.dt-drill svg { width: 1.1rem; height: 1.1rem; margin-left: auto; flex: none; }
:root[data-theme="light"] .dt-drill { color: var(--brand-dk); }

.daily-cta { margin-top: 1.2rem; }
.daily-next {
  text-align: center; margin: .7rem 0 0;
  font-size: .85rem; font-weight: 600; color: var(--dim);
}

/* ---------- light mode ----------
   Only the two accents that are tuned for a dark field need restating; every
   other colour above is already a token that flips with the theme. */
:root[data-theme="light"] .dc-kicker,
:root[data-theme="light"] .daily-date { color: var(--brand-dk); }
