/* ==========================================================================
   FRONTLINE21 — TWENTY ONE DAYS
   Movement 07, the calendar, and the Wall of Truth it opens.

   The calendar is the manual's own structure: four weeks of five disciplines
   and one day standing alone. Every day is a link to its wall, so it works as
   a link with scripts off and as the way into the dialog with them.

   The wall is printed material. One uninterrupted charcoal field, the
   statement in worn Frontline red, clean light type for everything a man has
   to read or press. Ink and ground are separate layers: the ground is a static, locally
   hosted tile of the owned Frontline21 texture at its own scale; the statement
   is live, selectable text; and the wear is the site's printed ink (ink.css),
   which since 8 September evening is a density field MULTIPLIED over the core
   red rather than an alpha mask cut through it. The words are never made
   transparent, so nothing here can hide them, and the ground never shows
   through a letter.
   ========================================================================== */

/* --- Tokens ----------------------------------------------------------------
   The spacing tiers are the site's, in tokens.css. Here, only the wall's
   own material and edges. The tier is selected by rendered type size and
   written on the statement by wall.js as data-tier. */
:root {
  --wall-ink: #e8e1d4;            /* light ink: context, controls, source */
  --wall-ink-dim: #b9b3a7;        /* the same ink, quieter */
  --wall-red: #dc2227;            /* the statement: Frontline red on the black */
  --wall-ground: #171b1b;         /* deeper charcoal, under the texture */
  --wall-edge: clamp(24px, 5vw, 96px);
  --wall-word-space: 0.015em;     /* added to the font's own 0.26 em space */
}

/* --- 07 THE CALENDAR ---------------------------------------------------------- */

.calendar__head {
  row-gap: var(--stack-lg);
  align-items: end;
  margin-bottom: var(--stack-lg);
}

.calendar__aside .body {
  margin-top: var(--stack-md);
}

.calendar {
  display: grid;
  gap: var(--stack-lg);
}

.calendar__label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 18px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ink-line);
  margin-bottom: 12px;
  font-family: var(--font-text);
  font-size: var(--t-small);
  color: var(--paper-dim);
}

.calendar__label b {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: var(--track-label);
  line-height: var(--lead-label);
  text-transform: uppercase;
  color: var(--paper);
}

.calendar__span {
  margin-left: auto;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-faint);
  font-variant-numeric: tabular-nums;
}

.calendar__row {
  display: grid;
  gap: 1px;
  background: var(--ink-line-soft);
  border: 1px solid var(--ink-line-soft);
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .calendar__row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .calendar__row--single { grid-template-columns: 1fr; }
}

.calendar__cell {
  min-width: 0;
  background: var(--ink);
}

/* The day cell itself is in day.css, shared with the homepage teaser. */

/* Below the rank, one column: a list a thumb can work down. */
@media (max-width: 619px) {
  .day {
    grid-template-columns: 3.2em minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    align-items: baseline;
    padding: 15px 14px 14px;
  }
  .day__n { grid-column: 1; grid-row: 1 / span 2; align-self: start; font-size: 22px; }
  .day__title { grid-column: 2; grid-row: 1; margin-top: 0; }
  .day__value { grid-column: 2; grid-row: 2; margin-top: 5px; }
  .day__marks { grid-column: 3; grid-row: 1; padding-top: 1px; }
  .day--wide { grid-template-columns: 3.2em minmax(0, 1fr) auto; }
  .day--wide .day__n { grid-row: 1 / span 2; }
  .day--wide .day__title { grid-column: 2; grid-row: 1; }
  .day--wide .day__value { grid-column: 2; grid-row: 2; }
  .day--wide .day__marks { grid-column: 3; grid-row: 1; }
}

.calendar__foot {
  margin-top: var(--stack-lg);
  padding-top: var(--stack-sm);
  border-top: 1px solid var(--ink-line);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px var(--stack-md);
}

.calendar__soon {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--f21-red);
}

.calendar__dot {
  width: 6px;
  height: 6px;
  flex: none;
  background: var(--f21-red);
}

@media (prefers-reduced-motion: reduce) {
  .day::before { transition: none; }
}

/* --- THE WALL OF TRUTH ------------------------------------------------------ */

.wall {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--wall-ground);
  color: var(--wall-ink);
  overflow: hidden;
  z-index: 100;
}

.wall::backdrop {
  background: var(--wall-ground);
}

/* The ground. The owned texture, tiled at 1024 CSS px: native on a 2x screen,
   downsampled on a 1x one, never stretched. A vignette settles the edges and
   the same fine grain the rest of the site carries sits over it, because the
   dialog stands above the document's own grain layer. */
.wall__ground {
  position: absolute;
  inset: 0;
  background: var(--wall-ground) url('../images/texture/f21-wall-ground-2048.jpg') center / 1024px 1024px repeat;
}

.wall__ground::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 50% 45%, rgba(23, 27, 27, 0) 40%, rgba(12, 15, 15, 0.55) 100%);
}

.wall__frame {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  height: 100%;
  padding: 0 var(--wall-edge) env(safe-area-inset-bottom, 0px);
}

/* --- The chrome: quiet elements on the same field --------------------------- */

.wall__top,
.wall__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--stack-md);
  min-height: 64px;
}

.wall__top {
  border-bottom: 1px solid rgba(232, 225, 212, 0.14);
}

.wall__foot {
  border-top: 1px solid rgba(232, 225, 212, 0.14);
  padding-bottom: 4px;
}

.wall__name {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: var(--track-label);
  line-height: var(--lead-label);
  text-transform: uppercase;
  color: var(--wall-ink);
}

.wall__name-mark {
  width: 22px;
  height: 2px;
  background: var(--f21-red);
}

.wall__position {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--wall-ink-dim);
  font-variant-numeric: tabular-nums;
}

.wall__position b {
  color: var(--wall-ink);
  font-weight: 700;
}

.wall__position span {
  margin-left: 6px;
}

/* Controls: clean type, 44 px targets, hairline plates, visible focus. */
.wall__ctl {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  min-width: 44px;
  padding: 0 14px;
  border: 1px solid rgba(232, 225, 212, 0.26);
  color: var(--wall-ink);
  font-family: var(--font-text);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.35;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.wall__ctl svg {
  width: 16px;
  height: 16px;
  flex: none;
}

.wall__ctl:hover,
.wall__ctl:focus-visible {
  border-color: var(--f21-red-hi);
}

.wall__ctl:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  box-shadow: 0 0 0 5px rgba(23, 27, 27, 0.92);
}

.wall__ctl[disabled] {
  opacity: 0.32;
  cursor: default;
}

.wall__ctl[disabled]:hover {
  border-color: rgba(232, 225, 212, 0.26);
}

.wall__nav {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.wall__manual-link {
  --btn-fg: var(--wall-ink);
  --btn-bd: rgba(232, 225, 212, 0.34);
  min-height: 44px;
  padding-top: 0;
  padding-bottom: 0;
}

.wall__manual-link:hover,
.wall__manual-link:focus-visible {
  --btn-bd: var(--f21-red-hi);
}

@media (max-width: 599px) {
  .wall__ctl--prev span,
  .wall__ctl--next span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap;
  }
  .wall__ctl--prev,
  .wall__ctl--next { padding: 0 13px; }
  .wall__close span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap;
  }
  .wall__close { padding: 0 13px; }
}

/* --- The days ---------------------------------------------------------------- */

.wall__days {
  position: relative;
  display: grid;
  align-content: center;
  min-height: 0;
  padding: clamp(18px, 3vh, 40px) 0;
  overflow: hidden auto;
  overscroll-behavior: contain;
}

.wall__day {
  display: grid;
  grid-template-rows: auto auto auto;
  row-gap: clamp(18px, 3vh, 34px);
  align-content: center;
  justify-items: center;
  text-align: center;
  min-height: 0;
}

.wall__day[hidden] {
  display: none;
}

/* The day receives focus on opening so it is announced once; the ring is for
   the controls, not for the printed field. */
.wall__day:focus,
.wall__day:focus-visible {
  outline: none;
  box-shadow: none;
}

/* The day's context, above the statement. Clean Poppins, tracked, quiet. */
.wall__index {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 4px 0;
  font-family: var(--font-text);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.16em;
  line-height: 1.35;
  text-transform: uppercase;
  color: var(--wall-ink-dim);
}

@media (min-width: 721px) {
  .wall__index { font-size: 13px; }
}

.wall__day-n {
  color: var(--wall-ink);
}

.wall__index > span + span::before {
  content: '';
  display: inline-block;
  width: 1px;
  height: 0.9em;
  margin: 0 14px;
  vertical-align: -0.1em;
  background: rgba(232, 225, 212, 0.3);
}

.wall__day-title {
  text-transform: none;
  letter-spacing: 0.02em;
  font-weight: 400;
  font-size: 13px;
}

/* On a narrow screen the title and the manual line take their own line, with
   no separator standing at the start of it. */
@media (max-width: 599px) {
  .wall__day-title,
  .wall__manual { flex-basis: 100%; }
  .wall__index > .wall__day-title::before,
  .wall__source > .wall__manual::before { display: none; }
}

/* --- The statement ------------------------------------------------------------
   Live, selectable, semantic text in the shipped Quantico Bold with kerning
   on and nothing synthesised. The size is written by wall.js from a
   measurement of the actual lines; the tier follows the size. Until it has
   been measured the type wraps at an ordinary size, so nothing is ever cut
   off. */
.wall__truth {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-synthesis: none;
  font-kerning: normal;
  font-feature-settings: 'kern' 1;
  text-rendering: optimizeLegibility;
  text-transform: uppercase;
  color: var(--wall-red);
  font-size: clamp(36px, 8vw, 96px);
  line-height: var(--lead-compact);
  letter-spacing: var(--track-compact);
  word-spacing: var(--wall-word-space);
  text-wrap: initial;
  text-align: center;
  max-width: 100%;
  /* A little room either side, so a period or an overshooting round never
     touches the edge of the room. */
  padding-inline: 0.04em;
}

.wall__truth.is-fitted {
  white-space: nowrap;
}

.wall__truth[data-tier='1'] {
  letter-spacing: var(--track-monumental);
  line-height: var(--lead-monumental);
}

.wall__truth[data-tier='2'] {
  letter-spacing: var(--track-intermediate);
  line-height: var(--lead-intermediate);
}

.wall__truth[data-tier='3'] {
  letter-spacing: var(--track-compact);
  line-height: var(--lead-compact);
}

.wall__truth[data-tier='4'] {
  letter-spacing: var(--track-reflow);
  line-height: var(--lead-reflow);
  white-space: normal;
}

/* The two compositions. Only one set of breaks is shown; the other is a
   space. Which set is decided by fit, in wall.js, not by a device width. */
.wall__truth br { display: none; }
.wall__truth[data-composition='desktop'] br[data-b='d'],
.wall__truth[data-composition='desktop'] br[data-b='dm'],
.wall__truth[data-composition='phone'] br[data-b='m'],
.wall__truth[data-composition='phone'] br[data-b='dm'] { display: inline; }
/* Unmeasured, the phone set breaks: it is the safer composition at any size. */
.wall__truth:not([data-composition]) br[data-b='m'],
.wall__truth:not([data-composition]) br[data-b='dm'] { display: inline; }

.wall__w {
  display: inline;
}

/* Each word carries its first glyph's side bearing as --lsb and wall.js marks
   the first word of every rendered line. A centred setting needs no pull to
   the edge, so the mark is kept for a left aligned variant and not used here. */

/* The reviewed, font specific optical corrections for this copy: the terminal
   period and the apostrophe sit a fraction closer to the letter before them.
   Trial values from the type study, kept because they read better at final
   size in the browser; not a rule for the wider site. */
.wall__p { margin-left: -0.025em; font-weight: inherit; }
.wall__q { margin-left: -0.015em; font-weight: inherit; }

/* --- The printed surface ------------------------------------------------------
   The statement is printed by the site's ink system (ink.css): the print
   layer is .wall__print, whose data-ink setting wall.js writes from the
   fitted size, and the surface is the statement itself, which carries the
   .ink class. Everything a man has to read or press stays clean and
   unfiltered. The light runs while the wall is open. */

.wall__print {
  display: block;
  max-width: 100%;
  --light-state: running;
  /* The containing block for the entrance's decorative duplicates. */
  position: relative;
}

/* --- The entrance ---------------------------------------------------------------
   A misregistration and then a registration, about 270 ms, on every day and on
   every intentional day change. wall-glitch.js builds it and takes it away.

   Each duplicate is the statement itself, cloned, tinted and cut into strips.
   It sits exactly over the real statement, which never moves, so the glyph
   positions, the authored line breaks and the wear are the same before,
   during and after. The duplicates are out of the accessibility tree and
   nothing can be pressed through them. */

.wall__glitch {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

.wall__ghost {
  position: absolute;
  inset: 0;
  margin: 0;
  pointer-events: none;
  /* No material of its own: a duplicate is a flat tint, which is what a
     single plate of a misregistered impression is. The wear it prints
     through is .wall__print's, the same impression the real statement uses,
     so the mask never moves during the event.

     Opaque, and never blended. The slices stand in for the statement rather
     than sitting on it: red over blue, or blue over red ink, is the purple
     the brief rules out, and the only way to be sure of avoiding it is for no
     two coloured things ever to share a pixel. */
  background: none;
  mix-blend-mode: normal;
  will-change: transform;
  transition: transform 70ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .wall__glitch { display: none !important; }
}

/* The same fine grain the site carries, over the whole wall, so type and
   ground share one surface. Above the print, below nothing a man presses. */
.wall::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: url('../images/texture/grain-256.png') 0 0 / 256px 256px repeat;
  opacity: 0.055;
  mix-blend-mode: overlay;
}

/* Windows high contrast and the like: the ground, the vignette and the wear
   go, the words stay. */
@media (forced-colors: active) {
  .wall__ground,
  .wall::after { display: none; }
}

/* The source, under the statement: the scripture that grounds it, and where
   the day's full reading is. Clean type. */
.wall__source {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 6px 0;
  font-family: var(--font-text);
  font-size: 13px;
  letter-spacing: 0.02em;
  line-height: 1.35;
  color: var(--wall-ink-dim);
}

@media (min-width: 721px) {
  .wall__source { font-size: 14px; }
}

.wall__scripture {
  font-weight: 500;
  color: var(--wall-ink);
}

.wall__source > span + span::before {
  content: '';
  display: inline-block;
  width: 1px;
  height: 0.9em;
  margin: 0 14px;
  vertical-align: -0.1em;
  background: rgba(232, 225, 212, 0.3);
}

/* --- Motion -------------------------------------------------------------------
   Arrival is brief and the type resolves into stillness. Opening about 240 ms,
   closing about 150 ms, a day change 120 out and 180 in with no movement, so
   the spatial anchor holds. Reduced motion shows the complete state at once. */
.wall[open] {
  animation: wall-in 240ms var(--ease-out);
}

.wall.is-closing {
  animation: wall-out 150ms var(--ease-out) forwards;
}

@keyframes wall-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes wall-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

.wall__day.is-entering {
  animation: wall-day-in 180ms var(--ease-out);
}

@keyframes wall-day-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.wall[open] .wall__frame {
  animation: wall-frame-in 280ms var(--ease-out);
}

@keyframes wall-frame-in {
  from { transform: translateY(6px); }
  to { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .wall[open],
  .wall.is-closing,
  .wall__day.is-entering,
  .wall[open] .wall__frame { animation: none; }
}

/* --- Scripts off ---------------------------------------------------------------
   The dialog is a plain section at the end of the page, every day in order,
   and the calendar's links land on them. Nothing is hidden. */
.no-js .wall {
  display: block;
  position: static;
  height: auto;
  overflow: visible;
}

.no-js .wall__frame {
  height: auto;
  padding-top: var(--stack-lg);
  padding-bottom: var(--stack-lg);
}

.no-js .wall__days {
  overflow: visible;
  padding: var(--stack-lg) 0;
  row-gap: var(--stack-xl);
}

.no-js .wall__day[hidden] {
  display: grid;
}

.no-js .wall__truth {
  font-size: clamp(30px, 6vw, 72px);
  white-space: normal;
}

.no-js .wall__top .wall__close,
.no-js .wall__nav {
  display: none;
}

/* The page behind an open wall does not scroll; the wall keeps its own
   scroll for the reflow case. */
html.has-wall-open {
  overflow: hidden;
}
