/* ---------------------------------------------------------------------
   Sioux Falls SD Stake site -- "Evergreen" theme.

   A quiet deep green on warm white. Nothing here imitates Church
   branding, uses its logo or symbols, or borrows its photography.

   Colors live in these tokens and nowhere else, so changing the theme
   is this block and the hero gradient below -- which is why they are
   named --brand rather than after any one color.

   Mobile first on purpose: nearly everyone opens this on a phone, often
   minutes before a meeting starts, sometimes on ward-building wifi. Big
   tap targets, no layout that needs a mouse, nothing that blocks paint.
   --------------------------------------------------------------------- */

:root {
  --paper: #ffffff;
  --sink: #eef3ef;
  --ink: #1c2620;
  --muted: #5a6b60;
  --rule: #dbe3dd;

  /* Shadows as tokens so the dark theme can soften them. Black on
     near-black reads as dirt, not depth. */
  --lift: 0 1px 2px rgba(20, 45, 32, .05), 0 6px 20px rgba(20, 45, 32, .08);
  --lift-sm: 0 1px 2px rgba(20, 45, 32, .07);

  /* The brand bar. Its own pair, defined once and deliberately NOT
     redefined for dark mode, because the bar is meant to look the same in
     both. It used to be `background: var(--ink); color: #fdfbf9`, and
     --ink is the *text* color -- it goes near-white in dark mode, so the
     bar turned near-white while the hardcoded text stayed near-white.
     Measured on a phone: 1.13:1, which is invisible.

     The rule that keeps this from recurring: a token named for ink is for
     ink. If a background needs to stay put across themes, it needs its
     own token, not a borrowed one. */
  --masthead: #1c2620;
  --masthead-ink: #fdfbf9;  /* 15.1:1 on the above, in both themes */

  --brand: #2b5f46;         /* deep evergreen; 7.4:1 with white text */
  --brand-ink: #ffffff;
  --brand-soft: #e6efe9;
  --sky: #45705a;           /* secondary buttons; 5.9:1 */
  --sky-soft: #e9f0e9;

  --live: #8a5e15;          /* amber, NOT green: a green "Live now" pill on
                               green buttons stops being a signal. 5.7:1 */
  --warn: #8a4a15;          /* "the webcast is down". Rust, not the amber of
                               --live: one says something IS happening and the
                               other says it is not, so they must not match.
                               7.3:1 on --warn-soft */
  --warn-soft: #fdf1e3;
  --focus: #1a5fa8;         /* distinct from both brand and live */

  --radius: 12px;
  --sans: "Source Sans 3", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: "EB Garamond", Georgia, "Times New Roman", serif;
}
/* ---------- Schedule ----------
   A conference day. Three columns on a wide screen -- when, what, and a
   way in if there is one -- collapsing to a stack on a phone, where the
   attendee note is often longer than everything else on the row. */
/* A container, not the viewport, decides the row layout. The schedule
   sits in the stake column on a wide screen -- about 450px -- while the
   viewport is 1150px, so a media query put three columns in a space that
   fits one and wrapped "Saturday Evening Adult Session" a word per line.
   Measured, then fixed. Browsers without container queries keep the
   stacked rows, which is what a phone gets and reads perfectly well. */
.schedule { margin: 0 0 1.6rem; container-type: inline-size; }
.sched { list-style: none; margin: 0; padding: 0; }
.sched li {
  display: grid; gap: .3rem 1rem; align-items: start;
  grid-template-columns: 1fr; padding: .85rem 0;
  border-top: 1px solid var(--rule);
}
.sched li:last-child { border-bottom: 1px solid var(--rule); }
.sched-time { font-weight: 600; font-size: .92rem; white-space: nowrap; }
.sched-what { display: block; min-width: 0; }
.sched-title { display: block; font-family: var(--display); font-size: 1.12rem; line-height: 1.25; }
.sched-who { display: block; color: var(--muted); font-size: .85rem; line-height: 1.45; margin-top: .15rem; }
.sched-pass { display: block; color: var(--muted); font-size: .8rem; margin-top: .2rem; }
.sched-go:empty { display: none; }
.sched-go { display: flex; flex-direction: column; gap: .4rem; }
.sched-go .btn-link { margin: 0; }
@container (min-width: 38rem) { .sched-go { min-width: 10.5rem; } }
@container (min-width: 38rem) {
  .sched li { grid-template-columns: 9rem minmax(0, 1fr) auto; align-items: baseline; }
  .sched-go { justify-self: end; align-self: center; }
}
@media print {
  .sched-go { display: none; }
  .sched li { break-inside: avoid; }
}

/* The reader's own light/dark control. It sits in the masthead, which
   keeps its own near-white ink in both themes, so the button is given
   those colors rather than the page's. */
.theme-btn {
  font: inherit; font-size: .82rem; line-height: 1; cursor: pointer;
  padding: .4rem .8rem; margin-left: .6rem; flex: none;
  color: var(--masthead-ink, #fdfbf9); background: transparent;
  border: 1px solid rgba(253, 251, 249, .38); border-radius: 999px;
}
.theme-btn:hover { border-color: rgba(253, 251, 249, .75); }
.theme-btn:focus-visible { outline: 2px solid var(--masthead-ink, #fdfbf9); outline-offset: 2px; }
@media print { .theme-btn { display: none !important; } }

/* Dark is opt-in, never automatic. There is deliberately no
   `prefers-color-scheme` block: light is the default for everyone, and a
   reader who wants dark presses the button, which stores the choice. */
:root[data-theme="dark"] {
  --lift: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .34);
  --lift-sm: 0 1px 2px rgba(0, 0, 0, .3);
  --paper: #141a16; --sink: #0e120f; --ink: #e8efe9; --muted: #9aa89f;
  --rule: #2a352e; --brand: #7db894; --brand-ink: #10180f; --brand-soft: #1d2a22;
  --sky: #8fb59c; --sky-soft: #1b2620; --live: #e0a95c; --focus: #7fb0e8;
  --warn: #e8b887; --warn-soft: #2a1f14;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-padding-top: 4rem; }
body {
  margin: 0; background: var(--sink); color: var(--ink);
  font-family: var(--sans); font-size: 17px; line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}
a { color: var(--brand); }
:where(a, button, input, select, textarea):focus-visible {
  outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 4px;
}

/* ---------- Masthead ---------- */
.masthead { background: var(--masthead); color: var(--masthead-ink); padding: env(safe-area-inset-top, 0px) 0 0; }
.masthead-inner {
  /* Matches the widest the content below ever gets, so the stake name
     lines up with the left edge of the card rather than floating inside
     it. The narrower pages (privacy, terms) keep the same bar; a header
     wider than its page is an ordinary pattern, a header misaligned with
     its page is not. */
  max-width: 80rem; margin: 0 auto;
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .7rem 1rem;
}
.masthead a { color: inherit; text-decoration: none; }
.brandmark { font-family: var(--display); font-size: 1.12rem; font-weight: 500; margin-right: auto; letter-spacing: .01em; }
.nav { display: flex; gap: .3rem; flex-wrap: wrap; }
.nav a { padding: .35rem .7rem; border-radius: 999px; font-size: .9rem; opacity: .8; }
.nav a[aria-current="page"] { background: rgba(255,255,255,.15); opacity: 1; }
.nav a:hover { opacity: 1; }

/* ---------- Now / next banner ---------- */
.banner { background: var(--sky-soft); border-bottom: 1px solid var(--rule); }
.banner[data-state="live"] { background: var(--brand-soft); }
.banner-inner {
  max-width: 42rem; margin: 0 auto; padding: .7rem 1rem;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  font-size: .95rem;
}
.banner .dot {
  width: .6rem; height: .6rem; border-radius: 50%; flex: none;
  background: var(--sky);
}
.banner[data-state="live"] .dot { background: var(--live); animation: pulse 2s ease-in-out infinite; }
/* Webcast off. A pulsing dot and a green band both say "a stream is
   running"; nothing is, so both are turned off here -- after the live
   rules above, so they win on order rather than on a specificity race. */
.banner[data-down] { background: var(--warn-soft); border-bottom-color: var(--warn); }
.banner[data-down] .dot { background: var(--warn); animation: none; }
.banner-off { color: var(--warn); font-weight: 600; white-space: nowrap; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .banner .dot { animation: none; } }
.banner-text { margin-right: auto; }
.banner-go {
  background: var(--brand); color: var(--brand-ink); text-decoration: none;
  padding: .4rem 1rem; border-radius: 999px; font-weight: 600; font-size: .9rem;
  min-height: 2.2rem; display: grid; place-items: center;
}

/* ---------- Hero ---------- */
.hero {
  position: relative; display: grid; place-items: center; text-align: center;
  min-height: clamp(12rem, 32vw, 19rem);
  padding: 2.5rem 1.25rem; color: #fdfbf9;
  background:
    linear-gradient(180deg, rgba(14,24,18,.32), rgba(14,24,18,.58)),
    linear-gradient(160deg, #4e7f62 0%, #2b5f46 42%, #24443a 72%, #1b3529 100%);
  background-size: cover; background-position: center;
}
.hero h1 {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(2rem, 7vw, 3.2rem); line-height: 1.1;
  margin: 0; max-width: 16ch; letter-spacing: .005em;
  text-shadow: 0 1px 16px rgba(10,20,14,.45);
}

/* ---------- Page body ---------- */
/* Named rather than `main` so the editor's preview can reuse it. */
.site-body { max-width: 42rem; margin: 0 auto; padding: 0 1rem 4rem; }
.card-surface {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 1.5rem 1.25rem;
  margin: -2rem 0 0; position: relative; box-shadow: 0 8px 30px rgba(20, 45, 32, .10);
}
@media (min-width: 40rem) { .card-surface { padding: 2rem 2.25rem; } }

h2.block-heading {
  font-family: var(--display); font-weight: 500; text-align: center;
  font-size: clamp(1.45rem, 4.6vw, 1.85rem); line-height: 1.2;
  margin: 2.4rem 0 1rem; padding-bottom: .5rem; position: relative;
}
h2.block-heading::after {
  content: ""; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 3rem; height: 2px; background: var(--brand); opacity: .5;
}
.card-surface > :first-child { margin-top: 0; }
h3.block-subheading { font-weight: 600; text-align: center; color: var(--muted); font-size: 1.02rem; margin: .1rem 0 1.1rem; }
p.block-text { margin: 0 0 1rem; }

/* ---------- Event ---------- */
.events > .event + .event {
  margin-top: 1.6rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule, rgba(0, 0, 0, .12));
}
.events > .event:last-child { margin-bottom: 0; }

/* An event kept for its recording should not read as something about to
   happen. Quieter, but its buttons stay exactly as usable. */
.event.is-past .block-heading,
.event.is-past .event-when,
.event.is-past .event-note { opacity: .72; }
.past-pill {
  display: inline-block; margin-left: .4rem; padding: .1em .55em;
  border: 1px solid currentColor; border-radius: 999px;
  font-size: .72em; letter-spacing: .04em; text-transform: uppercase;
  vertical-align: .12em; opacity: .75;
}

.event { text-align: center; margin: 0 0 1.6rem; }
.event.is-live { }
.event-title { font-family: var(--display); font-weight: 500; font-size: 1.3rem; margin: 0 0 .15rem; }
.event-when { color: var(--muted); font-size: 1rem; margin: 0 0 1rem; }
.event-note { color: var(--muted); font-size: .92rem; margin: -.6rem 0 1rem; }
.live-pill {
  display: inline-block; vertical-align: middle; margin-left: .35rem;
  background: var(--live); color: #fff; font-size: .7rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  padding: .12rem .5rem; border-radius: 999px;
}

/* ---------- Buttons ---------- */
.btn-stack { display: flex; flex-direction: column; gap: .65rem; margin: 0 0 1.5rem; }
.btn-link {
  display: grid; place-items: center; gap: .1rem;
  text-align: center; text-decoration: none;
  padding: .8rem 1rem; border-radius: var(--radius);
  min-height: 3rem; /* comfortable thumb target */
  border: 1px solid transparent;
}
.btn-label { font-weight: 600; font-size: 1rem; line-height: 1.25; }
.btn-helper { font-size: .82rem; opacity: .85; font-weight: 400; line-height: 1.3; }
.btn-link.primary { background: var(--brand); color: var(--brand-ink); }
.btn-link.secondary { background: var(--sky-soft); color: var(--sky); border-color: var(--rule); }
.btn-link:hover { filter: brightness(1.06); }
.btn-link:active { transform: translateY(1px); }

/* ---------- Meeting cards ---------- */
.cards { display: grid; gap: .75rem; margin: 0 0 1rem; }
@media (min-width: 34rem) { .cards { grid-template-columns: 1fr 1fr; } }
.ward-card {
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 1rem; background: var(--paper);
  display: flex; flex-direction: column; gap: .35rem;
}
.ward-card.is-live { border-color: var(--live); box-shadow: 0 0 0 2px rgba(31,111,74,.14); }
.ward-card .name { font-family: var(--display); font-size: 1.16rem; font-weight: 500; }
.ward-card .time { color: var(--muted); font-size: .92rem; }
.ward-card .passcode { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums; }
.ward-card .btn-link { margin-top: .5rem; padding: .65rem .8rem; min-height: 2.6rem; }
.ward-card .btn-label { font-size: .92rem; }

/* ---------- When the webcast is down ---------- */
/* The notice sits above the cards and is the first thing read, because
   the question somebody opens this page with is "can I watch", and the
   answer today is no. */
.webcast-down {
  margin: 0 0 .85rem; padding: .8rem 1rem;
  border: 1px solid var(--warn); border-left-width: 4px;
  border-radius: var(--radius);
  background: var(--warn-soft); color: var(--warn);
  font-weight: 600; line-height: 1.45;
}
/* Not `opacity`, which would fade the ward name and the meeting time --
   the two things still worth reading. Only the dead control recedes. */
.ward-card.is-down { border-style: dashed; }
.btn-link.disabled {
  background: none; border-color: var(--rule); color: var(--muted);
  cursor: not-allowed;
}
.btn-link.disabled:hover { filter: none; }
.btn-link.disabled:active { transform: none; }

/* ---------- Wide screens: the stake's things sit beside the ward's ----------
   `.events` is the stake column: events *and* schedules go in it, which
   is what keeps stake conference and the devotional on the left while
   the ward meetings stay on the right. The class name predates
   schedules; it is kept because six selectors below depend on it.

   Keyed off :has() rather than a class somebody has to remember to set.
   An event removes itself once it is over, so a page with nothing left
   in the stake column falls back to one column on its own.
   Browsers without :has() keep the stacked layout, which is the same
   thing everyone gets on a phone -- nothing is lost, it is just taller. */
/* How the two columns divide, chosen per page in the editor and written
   onto this element by StakeSite.render as data-split.

   The default is weighted towards the stake, not 1fr 1fr: during
   conference the left column carries dense text -- a schedule row's
   "who it is for" runs to forty words -- while the right carries cards
   of a ward name, a time and a button. An even split gave the wordy
   column the same room as the sparse one, and kept it under the width
   its own three-across row layout needs.

   The rest of the year that is backwards, which is what the other three
   settings are for: outside conference the stake column may hold one
   devotional while the ward meetings are the page.

   Declared outside the media query so the ratio lives in one place, and
   consumed only by the grid below, which only exists above 64rem. */
.card-surface                      { --stake-col: 1.5fr; --ward-col: 1fr; }
.card-surface[data-split="even"]   { --stake-col: 1fr;   --ward-col: 1fr; }
.card-surface[data-split="narrow"] { --stake-col: 1fr;   --ward-col: 1.5fr; }
.card-surface[data-split="slim"]   { --stake-col: 1fr;   --ward-col: 2fr; }

@media (min-width: 64rem) {
  /* 66rem left most of a wide screen empty while the schedule's longest
     rows wrapped five deep. The cap is the measure of the prose, and the
     prose lives in the left column, so the page may be wider than that
     as long as the column is not. */
  .site-body:has(.card-surface > .events) { max-width: 80rem; }

  .card-surface:has(> .events) {
    display: grid;
    grid-template-columns: minmax(0, var(--stake-col)) minmax(0, var(--ward-col));
    column-gap: 3rem;
    align-items: start;
  }
  /* The event SPANS the rows rather than occupying one. Sharing row 1 with
     the "Sacrament Meetings" heading made that row as tall as the whole
     event block, leaving ~200px of dead space above the first ward card.
     A spanning item distributes its height instead of forcing one row.
     The span is deliberately larger than any plausible number of blocks;
     the extra rows collapse to zero. Note `column-gap`, not `gap` -- a row
     gap here would multiply across every one of those empty rows. */
  .card-surface:has(> .events) > .events { grid-column: 1; grid-row: 1 / span 50; margin-bottom: 0; }
  .card-surface:has(> .events) > :not(.events) { grid-column: 2; }

  /* Start both columns level with each other. */
  .card-surface:has(> .events) > .events .block-heading,
  .card-surface:has(> .events) > .events + * { margin-top: 0; }

  /* One ward per row here: the column is half the width, and the button
     labels need the room more than the page needs to be compact. */
  .card-surface:has(> .events) .cards { grid-template-columns: 1fr; }
}

/* ---------- Footer ---------- */
footer { max-width: 42rem; margin: 0 auto; padding: 1.5rem 1rem 3rem; color: var(--muted); font-size: .85rem; text-align: center; }
footer .updated { font-style: italic; }
/* The help links. A little more present than the privacy line beneath
   them -- somebody who needs them is looking for them -- and still well
   short of competing with the webcast buttons the page is actually for. */
footer .helpful { margin: .9rem 0 .2rem; }
footer .helpful a { color: var(--brand); text-decoration: none; }
footer .helpful a:hover, footer .helpful a:focus-visible { text-decoration: underline; }

/* ---------- States ---------- */
.notice { text-align: center; color: var(--muted); padding: 2rem 1rem; }
.notice.bad { color: #a3312a; }

@media print {
  .masthead, .nav, .banner, footer { display: none; }
  body { background: #fff; }
  .hero { min-height: auto; color: #000; background: none; padding: 1rem 0; }
  .hero h1 { text-shadow: none; }
  .card-surface { margin: 0; box-shadow: none; border: 0; }
  .btn-link { border: 1px solid #999; color: #000 !important; background: none !important; }
  .btn-link::after { content: " (" attr(href) ")"; font-weight: 400; font-size: .8em; word-break: break-all; }
}

/* Version marker, on screen only. */
.version { color: var(--muted); font-size: .72rem; margin-top: .6rem; }
@media print { .version { display: none; } }

/* ---------- The DEV build marker ---------- */
/* Set by the one-line script every page carries in its head, which keys
   off the hostname: dev.* and *-dev.pages.dev, nothing else. Deliberately
   NOT localhost -- this says "you are on the dev deployment", and the
   offline harness already announces itself.

   In normal flow, as body's own ::before, so it pushes the page down
   instead of sitting on top of it. A fixed strip would have covered the
   sticky toolbar, and overlaying the one thing a moderator reaches for
   is how a marker becomes a nuisance somebody asks to remove.

   `--devbar` is 0 everywhere else, so the one layout that is exactly one
   screen tall can subtract it unconditionally. */
:root { --devbar: 0rem; }
html[data-env="dev"] { --devbar: 1.75rem; }
html[data-env="dev"] body::before {
  content: "DEV BUILD — LIVE DATA";
  display: block; height: var(--devbar);
  background: #b3261e; color: #fff;
  font: 700 .72rem/var(--devbar) var(--sans, system-ui, sans-serif);
  letter-spacing: .14em; text-align: center;
  white-space: nowrap; overflow: hidden;
}
/* Red across the top of a printed sacrament program, forever, in every
   ward that ever prints from the dev site. No. */
@media print { html[data-env="dev"] body::before { display: none; } }
