/* ==========================================================================
   Home Church, components
   Reusable pieces. Screen specific layout lives in screens.css.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Top bar. Minimal and mostly empty. The title only appears after scroll.
   -------------------------------------------------------------------------- */

.hc-topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--hc-space-md);
  height: calc(var(--hc-header-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) var(--hc-screen-pad) 0;
  background: var(--hc-paper);
  transition: border-color var(--hc-dur-fast) var(--hc-ease);
  border-bottom: var(--hc-hairline) solid transparent;
}

/* Once the screen scrolls, the bar earns its hairline and shows the title. */
.hc-topbar[data-scrolled="true"] { border-bottom-color: var(--hc-rule); }

/* With the date rail out, the rail is the bottom of the chrome and carries
   the hairline. Two of them stacked read as a seam. */
.hc-topbar[data-rail="true"] { border-bottom-color: transparent; }

/* The logo and the screen title share one slot. At rest on a tab the logo
   shows, past the scroll threshold (or on any pushed view, which forces
   data-scrolled true right away) the title fades in over it. */
.hc-topbar__center {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
}

.hc-topbar__logo {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(0, -50%);
  height: 20px;
  width: auto;
  opacity: 0;
  /* Height is in here with the rest so the lockup shrinks on the way across
     rather than snapping a size the moment the scroll threshold is passed.
     See the note on the scrolled rule below for why it shrinks at all. */
  transition: left var(--hc-dur-fast) var(--hc-ease),
              transform var(--hc-dur-fast) var(--hc-ease),
              height var(--hc-dur-fast) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease);
}

/* At rest on a tab, the logo sits where the title will later appear. */
.hc-topbar[data-is-tab="true"] .hc-topbar__logo { opacity: 1; }

/* Once the screen scrolls, the title claims the left edge, so the logo gets
   out of its way rather than disappearing.

   IT GOES TO THE FAR END OF THE SLOT RATHER THAN TO THE MIDDLE OF IT, and
   the difference is the two discs to the right of it. The middle was the
   right destination while the bar ended in one avatar: with light-or-dark and
   search beside the initials the slot is 201pt wide on the narrowest phone
   this design targets, and a 127pt lockup centred in it starts at 94, which
   is under the tail of a title like "Practices" that runs to 102. Ending at
   the slot's right edge instead is the same gesture doing the same job, and
   it is measured rather than eyeballed: it is exactly as far out of the
   title's way as the header allows.

   AND IT STEPS DOWN THREE PIXELS ON THE WAY. At 20px the lockup is 127 wide
   and right-aligned it still starts at 94; at 17px it is about 108 and starts
   at 113, which clears the longest tab title by 11pt on a 375pt phone and by
   26 on a 390. Three pixels of height is what buys that, and a lockup that is
   a size smaller once it is no longer the thing being read is right anyway.
   Nothing here is a compromise on the discs: all three stay a full 44pt tap
   target.

   left and transform are both percentages, of the slot and of the lockup's
   own width, which is what makes this animate the way the old centring rule
   did rather than jumping. The arithmetic above is asserted on a real page,
   at three widths and against every tab title, in tests/e2e/search.js. */
.hc-topbar[data-is-tab="true"][data-scrolled="true"] .hc-topbar__logo {
  left: 100%;
  transform: translate(-100%, -50%);
  height: 17px;
}

/* A pushed view has no logo at all, the back arrow and title fill that role. */
.hc-topbar[data-is-tab="false"] .hc-topbar__logo {
  opacity: 0;
  pointer-events: none;
}

:root[data-theme="dark"] .hc-topbar__logo--light,
:root:not([data-theme="dark"]) .hc-topbar__logo--dark {
  display: none;
}

.hc-topbar__title {
  position: absolute;
  left: 0;
  top: 50%;
  min-width: 0;
  max-width: 100%;
  transform: translateY(-50%);
  /* Matches the section header it replaces mid-scroll, same screen name in
     two different weights would read as a glitch, not a transition. */
  font-family: var(--hc-sans-display);
  font-weight: 800;
  font-size: 17px;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--hc-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transition: opacity var(--hc-dur-fast) var(--hc-ease);
}

.hc-topbar[data-scrolled="true"] .hc-topbar__title {
  opacity: 1;
}

.hc-topbar__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  margin-left: -12px;
  color: var(--hc-ink);
  flex: 0 0 auto;
}

.hc-topbar__spacer { flex: 1 1 auto; }

/* The three circles at the right end of the bar: light or dark, search, you.

   One flex child of the header rather than three, so the header still has
   exactly two things to space at --hc-space-md and the logo keeps its room.
   The gap inside is zero: each button is a 44pt tap target with a smaller
   circle drawn inside it, so the discs already sit apart by the padding
   around them and a gap on top of that pushes the row into the logo. */
.hc-topbar__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0;
}

/* The two icon buttons. A 44pt target, a 36pt circle, matching the avatar
   beside them at one step down: the initials are who you are and outrank a
   pair of controls, so they stay the largest of the three. */
.hc-topbar__disc {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  padding: 0;
  color: var(--hc-ink);
}

.hc-topbar__disc-icon {
  width: 36px;
  height: 36px;
  padding: 7px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-cream);
  border: var(--hc-hairline) solid var(--hc-rule);
  transition: background var(--hc-dur-fast) var(--hc-ease);
}

.hc-topbar__disc:active .hc-topbar__disc-icon { background: var(--hc-paper-sunk); }

/* Avatar button, the only way into Profile. No bell, no chat. */
.hc-avatar {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  margin-right: -10px;
}

.hc-avatar__disc {
  /* 30% up from the original 32px, still clears the 44px tap target. */
  width: 42px;
  height: 42px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-cream);
  border: var(--hc-hairline) solid var(--hc-rule);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.04em;
  color: var(--hc-accent-deep);
  transition: background var(--hc-dur-fast) var(--hc-ease);
}

.hc-avatar:active .hc-avatar__disc { background: var(--hc-paper-sunk); }

/* --------------------------------------------------------------------------
   Date rail. A second strip of chrome, directly under the top bar and only on
   the Listen archive: the month each message was preached, running in the
   order the page draws them, with the one you are reading filled in.

   Hidden it is not just moved but made invisible, which takes the chips out of
   the tab order and out of the accessibility tree without needing the
   attribute plumbing that would fight the slide.
   -------------------------------------------------------------------------- */

.hc-date-rail {
  position: fixed;
  z-index: 39;   /* under the top bar it slides out from */
  /* Below the pinned strip when there is one, which is what --hc-pin-h is:
     0px unless js/app.js has drawn one. See .hc-pinbar further down. */
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + env(safe-area-inset-top));
  left: 0; right: 0;
  height: 42px;
  background: var(--hc-paper);
  border-bottom: var(--hc-hairline) solid var(--hc-rule);
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
  transition: transform var(--hc-dur-base) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease),
              visibility 0s linear var(--hc-dur-base);
}

.hc-date-rail[data-show="true"] {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition: transform var(--hc-dur-base) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease),
              visibility 0s;
}

/* The paper behind it is solid, so the blur is doing tone rather than
   legibility: the list reads as passing under the rail instead of ending at
   it. Where it is unsupported the opaque paper above stands in. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-date-rail {
    background: color-mix(in srgb, var(--hc-paper) 88%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hc-date-rail,
  .hc-date-rail[data-show="true"] { transform: none; }
}

/* Centered on the same column the screen uses, so on a tablet the first month
   starts where the episode titles start. */
.hc-date-rail__track {
  display: flex;
  align-items: center;
  gap: var(--hc-space-xs);
  height: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 0 var(--hc-screen-pad);
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.hc-date-rail__track::-webkit-scrollbar { display: none; }

/* Tablet gives the column more air, and the months move with it. */
@media (min-width: 768px) {
  .hc-date-rail__track { padding: 0 var(--hc-space-xl); }
}

.hc-date-rail__chip {
  flex: 0 0 auto;
  /* The chip is 30px of ink in a 42px strip. The rail is the target, not the
     pill, which is why the padding is vertical as well as horizontal. */
  padding: 6px var(--hc-space-md);
  border-radius: var(--hc-radius-pill);
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--hc-mid);
  transition: background var(--hc-dur-fast) var(--hc-ease),
              color var(--hc-dur-fast) var(--hc-ease);
}

.hc-date-rail__chip[aria-current="true"] {
  background: var(--hc-accent-deep);
  color: var(--hc-on-dark);
  font-weight: 700;
}

.hc-date-rail__chip:focus-visible { outline-offset: 1px; }

/* --------------------------------------------------------------------------
   Pull to sync. The disc that comes down out from under the header when a
   thumb drags the top of a screen, and the ring inside it that closes as it
   travels and turns while the app is fetching. See js/pull.js.

   THE LAYER IS FULL WIDTH AND EMPTY. It is a fixed strip pinned to the
   bottom of the chrome with one disc centred in it, so the disc lands on the
   same axis on every screen and on a tablet without anything measuring
   anything. Nothing in it takes a tap: the gesture is read off .hc-scroll,
   the way the index rail's is, which is what leaves the glass under the disc
   working normally the whole time.

   NOTHING HERE TRANSITIONS BY DEFAULT. Every number while a thumb is down is
   written frame by frame from js/pull.js, and a transition on top of that is
   a second opinion about the same number. [data-settling="true"] is the one
   exception, and it is on only for the length of a release.
   -------------------------------------------------------------------------- */

.hc-pull {
  position: fixed;
  /* Under the top bar, which is 40, and under the month strip at 39. Both
     are opaque, and being under them is what lets the disc come out from
     beneath the chrome rather than appear on top of it. */
  z-index: 38;
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + env(safe-area-inset-top));
  left: 0; right: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  /* Half a disc above its own strip, so at rest it is behind the header
     rather than sitting on the first line of the page. */
  margin-top: -17px;
}

/* Listen's month strip is a second band of chrome under the header, and the
   disc comes out from under that instead while it is out. 42px is the
   strip's own height, written here for the same reason .hc-index writes it:
   the two are only ever equal by agreement. */
.hc-date-rail[data-show="true"] ~ .hc-pull {
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + 42px + env(safe-area-inset-top));
}

.hc-pull[data-settling="true"],
.hc-pull[data-settling="true"] .hc-pull__disc {
  transition: transform var(--hc-dur-base) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease);
}

/* The same gold the discs by the thumb are made of, at three quarters the
   size: this one is read, not pressed, and a full 44px of it under the
   header is a lozenge rather than a marker. */
.hc-pull__disc {
  position: relative;   /* the sheen below is absolute against it */
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: var(--hc-radius-pill);
  border: var(--hc-hairline) solid var(--hc-gold-edge);
  background: var(--hc-gold-skin);
  box-shadow: var(--hc-gold-shadow);
  color: var(--hc-gold-ink);
  isolation: isolate;

  /* Down by default, and every state that puts it up is below, so there is
     one place to look when it is in the wrong one. */
  opacity: 0;
  transform: scale(0.62);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-pull__disc {
    background: var(--hc-gold-skin-lens);
    backdrop-filter: blur(20px) saturate(1.7);
    -webkit-backdrop-filter: blur(20px) saturate(1.7);
  }
}

.hc-pull__disc::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--hc-gold-sheen);
  pointer-events: none;
}

.hc-pull__ring {
  position: relative;
  z-index: 1;
  width: 19px;
  height: 19px;
  /* Twelve o'clock, so the ring closes the way a clock hand would rather
     than starting from three. */
  transform: rotate(-90deg);
  filter: drop-shadow(0 0 2px var(--hc-gold-halo));
}

.hc-pull__track,
.hc-pull__arc {
  fill: none;
  stroke: currentColor;
  stroke-width: 3.4;
  stroke-linecap: round;
}

.hc-pull__track { opacity: 0.26; }

/* 81.68 is the circumference at r=13 in the 36 unit box, and js/pull.js
   writes the offset against the same number. Closed until the finger opens
   it. */
.hc-pull__arc {
  stroke-dasharray: 81.68;
  stroke-dashoffset: 81.68;
}

/* Working. The inline numbers the drag wrote are cleared at this point, so
   these are what is left holding the disc out. */
.hc-pull[data-state="syncing"] .hc-pull__disc {
  opacity: 1;
  transform: scale(1);
}

.hc-pull[data-state="syncing"] .hc-pull__arc {
  /* A quarter of the ring, turning. Long enough to read as a hand and short
     enough that the gap says it is moving. */
  stroke-dasharray: 20 62;
  stroke-dashoffset: 0;
}

.hc-pull[data-state="syncing"] .hc-pull__ring {
  animation: hc-pull-turn 900ms linear infinite;
}

@keyframes hc-pull-turn {
  from { transform: rotate(-90deg); }
  to   { transform: rotate(270deg); }
}

/* Reduced motion still gets the disc and still gets an answer, it just does
   not spin. The ring closes whole and holds while the fetch is out. */
@media (prefers-reduced-motion: reduce) {
  .hc-pull[data-state="syncing"] .hc-pull__ring { animation: none; }
  .hc-pull[data-state="syncing"] .hc-pull__arc {
    stroke-dasharray: 81.68;
    stroke-dashoffset: 0;
    opacity: 0.6;
  }
}

/* Presentation mode has no chrome at all, and js/pull.js refuses the gesture
   there. This is the same answer said in CSS, for the frame where a sync
   started somewhere else is still settling as the slide goes up. */
#app[data-view="present"] .hc-pull { display: none; }

/* ==========================================================================
   TWO NAVIGATIONS, AND THE ONE ATTRIBUTE THAT PICKS ONE.

   #app carries data-nav, written by applyNavStyle in js/app.js from the
   navStyle line on the profile: "bar" is the six tile plinth this app had
   for its whole life, "button" is the round one in the corner with the full
   screen list behind it. Both sets of markup are in the shell from boot and
   both sets of rules are in this file; the attribute decides which one is on
   the glass, and the switch on Your account writes it.

   WHY BOTH ARE BUILT RATHER THAN ONE SWAPPED IN. The shell is rendered once,
   at boot, and everything hanging off it, the discs, the rails, the pull, the
   swipe, holds a reference to what it found. Rebuilding the bottom of it on a
   switch would mean re-finding all of that and being wrong about one of them.
   Two hidden buttons cost nothing; a re-render costs a bug somebody finds on
   a Sunday.

   Everything below this line to the end of the navigation section is drawn
   for one mode or the other, never for both.
   ========================================================================== */

/* Whichever one is not on is not on the glass, not reachable, and not in the
   accessibility tree. display:none rather than visibility or opacity, so a
   screen reader is not reading out a navigation nobody can see and a tap
   cannot land on it. */
#app[data-nav="button"] .hc-tabbar,
#app[data-nav="button"] .hc-oversheet,
#app[data-nav="button"] .hc-oversheet__scrim,
#app[data-nav="bar"] .hc-navfab,
#app[data-nav="bar"] .hc-navmenu { display: none; }

/* The plinth's footprint, in place of the button's. See the note on
   --hc-chrome-space in css/tokens.css: this is the one place the difference
   between the two shapes is written down, and every rule that clears the
   bottom of the screen reads it without knowing which one it got. */
#app[data-nav="bar"] {
  --hc-chrome-space: calc(var(--hc-tabbar-h)
                          + (var(--hc-tabbar-pad) * 2)
                          + (var(--hc-chrome-inset) * 2));
}


/* --------------------------------------------------------------------------
   Tab bar. A glass plinth floating clear of the screen edges, with the
   active tab raised out of it as a tile.

   Two pseudo elements carry the whole treatment, so the markup stays five
   buttons and nothing else:
     ::before  the tile, one element that slides between tabs. JS sets
               --hc-tab-index; the tile does the travelling.
     ::after   the sheen, one soft diagonal pass across the top-left.
   The tile sits under the buttons and the sheen sits over everything,
   which is the order light actually arrives in.
   -------------------------------------------------------------------------- */

.hc-tabbar {
  position: fixed;
  left: var(--hc-chrome-inset);
  right: var(--hc-chrome-inset);
  bottom: calc(var(--hc-chrome-inset) + env(safe-area-inset-bottom));
  z-index: 40;
  display: flex;
  padding: var(--hc-tabbar-pad);
  /* Concentric with the tile: 26 outside, 6 of frame, 20 inside. Corners
     stay parallel, which is the difference between this looking made and
     looking assembled. */
  border-radius: 26px;
  background: var(--hc-glass-skin);
  border: var(--hc-hairline) solid var(--hc-glass-edge);
  box-shadow: var(--hc-glass-shadow);
  isolation: isolate;
}

/* Glass only where the browser can actually blur. Everywhere else the
   opaque skin above stands in, and the bar simply reads as a solid plinth. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-tabbar {
    background: var(--hc-glass-skin-lens);
    -webkit-backdrop-filter: blur(22px) saturate(150%);
    backdrop-filter: blur(22px) saturate(150%);
  }
}

/* The raised tile. Width is a fifth of the frame, driven by --hc-tab-count
   so adding a sixth tab needs no CSS. It hides on pushed views, where no
   tab is current, and keeps its place for the return. */
.hc-tabbar::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: var(--hc-tabbar-pad);
  bottom: var(--hc-tabbar-pad);
  left: var(--hc-tabbar-pad);
  width: calc((100% - (var(--hc-tabbar-pad) * 2)) / var(--hc-tab-count, 5));
  border-radius: 20px;
  background: var(--hc-tabbar-tile);
  border: var(--hc-hairline) solid var(--hc-tabbar-tile-edge);
  box-shadow: var(--hc-tabbar-tile-shadow);
  opacity: var(--hc-tab-tile, 1);
  transform: translateX(calc(var(--hc-tab-index, 0) * 100%));
  /* --hc-tab-tween is the swipe settling: the tile is given the same duration
     as the screens so it arrives with them rather than on its own schedule.
     Unset the rest of the time, which is the ordinary tap. */
  transition: transform var(--hc-tab-tween, var(--hc-dur-base)) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease);
}

/* Mid drag the tile is not travelling toward anything, it is being placed
   every frame at a fraction of a tab, so it must not also be easing. */
.hc-tabbar[data-swiping="true"]::before {
  transition: opacity var(--hc-dur-fast) var(--hc-ease);
}

.hc-tabbar::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  background: var(--hc-glass-sheen);
}

@media (prefers-reduced-motion: reduce) {
  .hc-tabbar::before { transition: opacity var(--hc-dur-fast) var(--hc-ease); }
}

.hc-tab {
  position: relative;
  z-index: 1;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: var(--hc-tabbar-h);
  min-height: var(--hc-tap-min);
  padding: 6px 2px 8px;
  color: var(--hc-mid);
  transition: color var(--hc-dur-fast) var(--hc-ease);
}

.hc-tab__icon {
  width: 24px;
  height: 24px;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.hc-tab__label {
  font-family: var(--hc-sans);
  font-weight: 500;
  font-size: var(--hc-tab);
  line-height: var(--hc-tab-line);
  letter-spacing: 0.01em;
}

/* The tile lightens the ground under the active tab, so its label lifts a
   step past ink to hold the same separation it had on paper. */
.hc-tab[aria-current="page"] { color: var(--hc-ink); }
.hc-tab[aria-current="page"] .hc-tab__icon { color: var(--hc-accent-deep); }

/* The focus ring has to stay inside the plinth, or it draws a rectangle
   across the rounded corner. */
.hc-tab:focus-visible {
  outline-offset: -4px;
  border-radius: 20px;
}

/* --------------------------------------------------------------------------
   The overflow sheet. What ••• lifts.

   It is the plinth again, and that is the whole design: the same skin, the
   same hairline, the same sheen, the same 26px corner, the same 12px inset on
   both edges, so it is exactly as wide as the bar and its edges line up with
   the bar's rather than nearly lining up. One 8px gap between them is what
   keeps it reading as a second object instead of the bar having grown a lump.

   The modules sit on the glass the way the five tabs sit on it, and the
   raised tile is spent on the one you are in. The bar already has a rule
   about what a tile means and it means you are here; the sheet keeps it
   rather than inventing a second one.
   -------------------------------------------------------------------------- */

.hc-oversheet {
  position: fixed;
  left: var(--hc-chrome-inset);
  right: var(--hc-chrome-inset);
  /* The bar's own height, its frame, its two hairlines and the inset it
     floats on, and then the gap. Written out rather than measured, so moving
     any one of those tokens moves this with it. */
  bottom: calc(var(--hc-tabbar-h)
               + (var(--hc-tabbar-pad) * 2)
               + (var(--hc-hairline) * 2)
               + var(--hc-chrome-inset)
               + env(safe-area-inset-bottom)
               + 8px);
  z-index: 46;                  /* over the discs at 45, under the toast at 60 */
  padding: 8px var(--hc-tabbar-pad) var(--hc-tabbar-pad);
  border-radius: 26px;
  background: var(--hc-glass-skin);
  border: var(--hc-hairline) solid var(--hc-glass-edge);
  box-shadow: var(--hc-glass-shadow);
  isolation: isolate;

  /* Parked below the screen edge, far enough down that the shadow clears too. */
  transform: translateY(calc(100% + 120px));
  opacity: 1;
  transition: transform var(--hc-dur-sheet) var(--hc-ease-enter);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-oversheet {
    background: var(--hc-glass-skin-lens);
    -webkit-backdrop-filter: blur(22px) saturate(150%);
    backdrop-filter: blur(22px) saturate(150%);
  }
}

.hc-oversheet::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  background: var(--hc-glass-sheen);
}

.hc-oversheet[data-state="open"],
.hc-oversheet[data-state="peek"] { transform: translateY(0); }

/* The peek leaves on a fade rather than the slide it arrived on. Sliding back
   down is what a dismissal looks like, and nobody opened this one. */
.hc-oversheet[data-state="fade"] {
  transform: translateY(0);
  opacity: 0;
  transition: opacity 320ms var(--hc-ease);
}

/* Mid drag it is under a finger, not travelling anywhere, so it must not also
   be easing. And putting it back to parked once it is invisible must not be
   animated at all, or it travels down through the screen on the way. */
.hc-oversheet[data-dragging="true"],
.hc-oversheet[data-reset="true"] { transition: none; }

/* The handle. A grab rail with a real target around it: the drag has to start
   somewhere a thumb can hit without aiming, and the modules below are buttons,
   so it cannot be the whole panel. */
.hc-oversheet__grab {
  display: grid;
  place-items: center;
  width: 100%;
  height: 22px;
  margin-bottom: 2px;
  padding: 0;
  cursor: pointer;
  touch-action: none;
}

.hc-oversheet__handle {
  width: 38px;
  height: 4px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-rule);
}

.hc-oversheet__grab:focus-visible { outline-offset: -2px; border-radius: 12px; }

/* Slots only for what is there. Two modules is one row of two, filling the
   width the way the bar fills the screen, and nothing is reserved for a third.
   --hc-mod-count is set in js/app.js and capped at four, so a fifth module
   starts a second row rather than a fifth narrower column. */
.hc-oversheet__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(var(--hc-mod-count, 2), minmax(0, 1fr));
  gap: 4px;
}

.hc-oversheet__mod {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 72px;
  padding: 10px 4px;
  border-radius: 20px;
  border: var(--hc-hairline) solid transparent;
  color: var(--hc-ink);
  transition: transform var(--hc-dur-fast) var(--hc-ease);
}

.hc-oversheet__mod:active { transform: scale(0.96); }

.hc-oversheet__icon {
  width: 24px;
  height: 24px;
  color: var(--hc-accent-deep);
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.hc-oversheet__label {
  font-family: var(--hc-sans);
  font-weight: 500;
  font-size: var(--hc-tab);
  line-height: var(--hc-tab-line);
  letter-spacing: 0.01em;
}

/* The one you are in, raised out of the glass exactly as the active tab is. */
.hc-oversheet__mod[aria-current="page"] {
  background: var(--hc-tabbar-tile);
  border-color: var(--hc-tabbar-tile-edge);
  box-shadow: var(--hc-tabbar-tile-shadow);
}

.hc-oversheet__mod:focus-visible { outline-offset: -3px; }

/* The paper behind it. It stops at the tab bar rather than covering it, so
   the bar stays lit and usable with the sheet up. */
.hc-oversheet__scrim {
  position: fixed;
  inset: 0;
  z-index: 30;
  width: 100%;
  padding: 0;
  background: var(--hc-overlay);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--hc-dur-base) var(--hc-ease);
}

#app[data-oversheet="open"] .hc-oversheet__scrim {
  opacity: 1;
  pointer-events: auto;
}

/* Both discs sit exactly where the sheet lands. They go down with it up, and
   come back when it goes, rather than floating over a scrim or poking out
   beside a panel. */
#app[data-oversheet="open"] .hc-disc,
#app[data-oversheet="peek"] .hc-disc,
#app[data-oversheet="fade"] .hc-disc {
  opacity: 0;
  transform: translateY(10px) scale(0.86);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .hc-oversheet,
  .hc-oversheet[data-state="fade"],
  .hc-oversheet__scrim { transition: none; }
}

/* --------------------------------------------------------------------------
   The navigation button, and the screen it lifts.

   WHAT WAS HERE. Six tiles on a glass plinth across the bottom of every
   screen, and a second plinth above it holding everything that would not fit
   in the six. The geometry was measured rather than guessed and it worked,
   right up to the point where the answer to "where does the next thing go" was
   always the same answer: behind the •••. A bar that is full is a bar that has
   stopped being a design and started being a queue.

   WHAT IS HERE NOW. One round button in the bottom right corner, and behind it
   the whole navigation, set as words rather than as pictures of words. Nothing
   about the app moved: the same routes, in the same order, reachable by the
   same sideways drag. What changed is that the row is now read top to bottom
   instead of being squeezed left to right, which is the one direction a phone
   has plenty of.

   THE ORDER IS THE LANE, UPSIDE DOWN. The overlay lists the last screen in the
   row at the top and the first at the bottom, so the thing most often reached
   for lands nearest the thumb that opened it and Home sits directly above the
   button. Read from the button upward it is exactly the order a drag runs: the
   five tabs, then the modules, then Settings. See sheetTiles() and syncModules
   in js/app.js, which are still the one list both halves come out of.
   -------------------------------------------------------------------------- */

.hc-navfab {
  position: fixed;
  right: var(--hc-chrome-inset);
  bottom: calc(var(--hc-chrome-inset) + env(safe-area-inset-bottom));
  /* Over the overlay it opens, because it is also the way to close it. */
  z-index: 56;
  width: var(--hc-navfab-size);
  height: var(--hc-navfab-size);
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: var(--hc-radius-pill);

  /* THE PLINTH'S MATERIAL, ON A ROUND PIECE OF IT. Identical to what the tab
     bar is made of, down to the token: the same thin skin over a blur, the
     same hairline, the same shadow, the same diagonal sheen. That is the
     point rather than a coincidence. This button replaced a bar made of this
     glass, and for one build it was a solid dark disc with a 2px gold ring,
     which read as something bolted onto the app rather than a piece of it.
     The app's chrome is frosted paper and this is now frosted paper.

     THE HAIRLINE IS NOT AN OUTLINE, it is the same 0.5px edge every floating
     surface in this app carries, and it is what keeps a transparent disc from
     dissolving into a photograph underneath it. The ring that was asked to go
     was the 2px gold one.

     The icon is --hc-accent-deep, which is exactly what an active tab's icon
     is drawn in. It is the taupe that is safe to set marks in, 5.05 on paper
     and 4.54 on cream, so it holds up on glass in both themes where --hc-gold
     at 1.9 on frosted white did not. */
  border: var(--hc-hairline) solid var(--hc-glass-edge);
  background: var(--hc-glass-skin);
  box-shadow: var(--hc-glass-shadow);
  color: var(--hc-accent-deep);
  isolation: isolate;
  transition: transform var(--hc-dur-fast) var(--hc-ease);
}

/* Glass only where the browser can actually blur, exactly as the tab bar and
   the discs do it. Everywhere else the denser skin above stands in and the
   button simply reads as a solid one. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-navfab {
    background: var(--hc-glass-skin-lens);
    -webkit-backdrop-filter: blur(22px) saturate(150%);
    backdrop-filter: blur(22px) saturate(150%);
  }
}

/* One soft diagonal pass across the top left, the plinth's again. Under the
   icon rather than over it, which is the order light actually arrives in. */
.hc-navfab::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--hc-glass-sheen);
  pointer-events: none;
}

.hc-navfab__icon { position: relative; z-index: 1; }

.hc-navfab:active { transform: scale(0.94); }

.hc-navfab__icon {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  /* 1.8, the discs' weight, not the 2 it was drawn at on a solid ground. The
     rules are thinner now for the same reason they are taupe now: this is a
     piece of the app's chrome rather than a badge sitting on top of it. */
  stroke-width: 1.8;
  stroke-linecap: round;

  /* AND THE HALO THE DISCS ALREADY WEAR, for the reason given beside them:
     glass this thin sits over whatever is scrolling underneath it, and on the
     frame where that is a dark button or a photograph, neither a taupe rule
     nor a paper one is safe on its own. The halo is what carries it. It is
     invisible against the ordinary case, which is the disc over paper, and it
     is the whole of the legibility in the case that is not. */
  filter: drop-shadow(0 0 2px var(--hc-gold-halo))
          drop-shadow(0 1px 1px var(--hc-gold-halo));
}

/* The three rules become an x. Both glyphs are the same three lines in the
   same 24 box, so the swap is a transform on each line rather than one icon
   being taken away and another put in its place: the middle rule fades, and
   the outer two travel to the centre and cross. A person watching sees the
   menu fold, which is the honest picture of what the tap did. */
.hc-navfab__bar {
  transform-origin: 12px 12px;
  transition: transform var(--hc-dur-base) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease);
}

#app[data-navmenu="open"] .hc-navfab__bar--top {
  transform: translateY(6px) rotate(45deg);
}
#app[data-navmenu="open"] .hc-navfab__bar--mid { opacity: 0; }
#app[data-navmenu="open"] .hc-navfab__bar--bot {
  transform: translateY(-6px) rotate(-45deg);
}

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

/* --------------------------------------------------------------------------
   The overlay. Everything the app has, on one screen.
   -------------------------------------------------------------------------- */

.hc-navmenu {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: flex;
  flex-direction: column;
  /* The top group is the one that can outgrow a short phone, so it is the one
     allowed to scroll. The bottom group and the rule above it hold still. */
  overflow: hidden;
  opacity: 0;

  /* CLOSED MEANS GONE, NOT JUST INVISIBLE, and this is the whole of the bug
     that shipped without it.

     An opacity of 0 paints nothing and still takes every touch that lands on
     it. pointer-events:none here is not enough on its own either, because the
     links and the scrolling group inside set pointer-events:auto so that they
     can be tapped and dragged while the overlay is up, and a child can turn
     that back on whatever its parent said. So on a phone that had opened the
     navigation once, which is every phone, the panel stayed in the document
     with eleven invisible link boxes and one invisible scroll container lying
     across the middle of every screen in the app. A thumb dragging on one of
     them scrolled a container with nothing in it rather than the page, and a
     tap on one of them navigated. The page looked frozen.

     visibility:hidden is the fix, because it is the one that descendants
     inherit and cannot take back by accident: nothing inside a hidden subtree
     is hit-tested, and none of it is in the accessibility tree either, which
     is the other half of what "closed" should mean. The zero-second
     transition, delayed by the length of the fade, is what lets the overlay
     finish fading out before it stops existing; without the delay it would
     vanish on the first frame of its own dismissal. */
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--hc-dur-base) var(--hc-ease),
              visibility 0s linear var(--hc-dur-base);
}

#app[data-navmenu="open"] .hc-navmenu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  /* Visible immediately on the way in; only the leaving is delayed. */
  transition: opacity var(--hc-dur-base) var(--hc-ease), visibility 0s;
}

@media (prefers-reduced-motion: reduce) {
  /* No fade to wait for, so no delay to hold the layer open through. */
  .hc-navmenu { transition: none; }
}

/* Two groups and a rule, laid out from the two ends rather than from the
   middle: the secondary list is pinned near the top, the primary list sits
   directly over the button, and whatever is left over is the gap between
   them. On a tall phone that gap is generous; on a short one the rule is
   pushed up against the top group and the top group starts to scroll, which
   is the right thing to give up first. */
/* THE WHOLE SCREEN IS THE MENU'S. The top group starts just under the
   status bar rather than under the header, which the overlay blurs anyway,
   and the bottom group comes down until Home sits level with the button
   rather than a button's height above it: Home is centred and the button is
   in the corner, so they never meet. Those two bands were about 180pt of
   empty glass on a tall phone, and using them is what lets every line fit
   without the top group scrolling.

   CENTRED, AS ONE BLOCK. The two groups stay a short rule apart and the
   lines keep their spacing; the whole block sits in the middle of the
   screen, with any spare room split evenly above Settings and below Home,
   never between the groups and never stretched through the lines. The
   padding is the same at both ends, the larger of the two safe areas, so
   the middle of the panel is the middle of the screen. */
.hc-navmenu__panel {
  display: flex;
  flex-direction: column;
  justify-content: center;
  flex: 1 1 auto;
  min-height: 0;
  padding: calc(max(env(safe-area-inset-top), env(safe-area-inset-bottom)) + var(--hc-space-base))
           var(--hc-screen-pad);
}

.hc-navmenu__group {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Least used at the top, most used at the bottom, so the item nearest the
   rule is the nearest thing in this group to the thumb. */
.hc-navmenu__group--more {
  flex: 0 1 auto;
  min-height: 0;
  /* A last resort only. fitMenu in js/app.js tightens the lines until they
     fit, so on any phone this app runs on the top group does not scroll. */
  overflow-y: auto;
  gap: 2px;
  -webkit-overflow-scrolling: touch;
}

.hc-navmenu__group--tabs {
  flex: 0 0 auto;
  gap: 2px;
}

/* The space between the two groups. It is a flex spacer with the rule drawn
   across the middle of it, so the rule floats between the lists rather than
   being welded to the bottom of one. */
.hc-navmenu__split {
  flex: 0 0 auto;
  height: calc(var(--hc-space-lg) + 8px);
  display: grid;
  place-items: center;
}

.hc-navmenu__rule {
  width: 120px;
  height: 1px;
  background: var(--hc-navmenu-rule);
}

/* One link. The app's display face, all caps and tracked out, which is the
   same treatment the eyebrow above every section header gets and for the same
   reason: a word set this way is read as a label rather than as prose, and a
   list of them reads as a list at a glance.

   Compact in the top group and a real step larger in the bottom one, because
   the difference in size is the only thing saying which list is which once
   the icons are gone. */
/* THE BOX HUGS THE WORD, which is the whole of what makes "tap anywhere else
   to close" true. A link laid out as a full width row is a row of empty glass
   on both sides of a short word that navigates, so tapping the space beside
   HOME went home. The padding is what keeps the target comfortably wider than
   the letters without reaching the edges of the screen. */
.hc-navmenu__link {
  position: relative;
  display: block;
  max-width: 100%;
  padding: 0 16px;
  font-family: var(--hc-sans-display);
  text-align: center;
  text-transform: uppercase;
  color: var(--hc-navmenu-dim);
  transition: color var(--hc-dur-fast) var(--hc-ease);
}

/* The sizes come from two numbers fitMenu sets: --hc-navmenu-fit shrinks
   the leading, and --hc-navmenu-type the letters, and only once the leading
   has given all it can. Both are 1 on a phone with room. */
.hc-navmenu {
  --hc-navmenu-fit: 1;
  --hc-navmenu-type: 1;
  --hc-navmenu-tabs-lh: calc(44px * var(--hc-navmenu-fit));
}

.hc-navmenu__group--more .hc-navmenu__link {
  font-weight: 600;
  font-size: calc(13px * var(--hc-navmenu-type));
  line-height: calc(32px * var(--hc-navmenu-fit));
  letter-spacing: 0.16em;
}

.hc-navmenu__group--tabs .hc-navmenu__link {
  font-weight: 800;
  font-size: calc(20px * var(--hc-navmenu-type));
  line-height: var(--hc-navmenu-tabs-lh);
  letter-spacing: 0.10em;
  color: var(--hc-navmenu-ink);
}

.hc-navmenu__link:active { color: var(--hc-gold); }

/* Where you already are. Gold, and a short rule under it, because colour
   alone is not a thing everybody can see. */
.hc-navmenu__link[aria-current="page"] { color: var(--hc-gold); }

.hc-navmenu__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 4px;
  width: 22px;
  height: 2px;
  margin-left: -11px;
  border-radius: 1px;
  background: var(--hc-gold);
}

.hc-navmenu__group--tabs .hc-navmenu__link[aria-current="page"]::after {
  bottom: 6px;
  width: 30px;
  margin-left: -15px;
}

.hc-navmenu__link:focus-visible {
  outline-offset: -2px;
  border-radius: var(--hc-radius-sm);
}

/* A SHORT PHONE, WITH EVERY LINE THERE IS. Not a media query any more: the
   number of lines changes with who is signed in and which pages the church
   has switched on, and one more is coming, so fitMenu in js/app.js measures
   the real list on the real screen and brings the leading in until it fits.
   See there. */

/* Anywhere that is not a word. Tapping it closes the overlay without going
   anywhere, which is the same intention the x is, so it is the same handler.
   It sits under the panel rather than over it, and the panel's own empty
   space lets taps through to it.

   AND IT IS ALSO THE DARK ROOM: the dim is painted here rather than on
   .hc-navmenu above, so the words sit over it rather than in it. */
.hc-navmenu__scrim {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--hc-navmenu-scrim);
}

/* And the blur, which is put on the screen underneath rather than taken out
   of it with a backdrop-filter.

   WHY NOT backdrop-filter, WHICH IS WHAT THE REST OF THIS FILE USES. Because
   this layer, unlike the plinth and the discs, has a scrolling box inside it:
   on a short phone with every module switched on the top group runs past the
   room it has and becomes a scroller. Chromium promotes a scroller to its own
   compositing layer, and a backdrop-filter anywhere in the same stacking
   context then samples a different backdrop inside those bounds, which drew
   the top group as a pale rectangle floating over the dim. It is a real
   artifact and it was on the glass, not a theory.

   Blurring the thing itself has no such trap, and it is also the more honest
   description of what is being asked for: the screen you are still on, out of
   focus, with a dark room in front of it. The four named here are the whole of
   what is behind the overlay. They are listed rather than reached with a
   wildcard because the overlay and the button are siblings of all four, and
   blurring the navigation through itself is not a thing anybody wants. */
#app[data-navmenu="open"] .hc-scroll,
#app[data-navmenu="open"] .hc-topbar,
#app[data-navmenu="open"] .hc-pinbar,
#app[data-navmenu="open"] .hc-date-rail {
  filter: blur(7px);
}

@media (prefers-reduced-motion: reduce) {
  /* The blur stays. It is not motion, and the scrim alone does not separate
     small white capitals from a screen full of headings underneath them. */
  .hc-navmenu__scrim { transition: none; }
}

/* The panel lets taps through everywhere it is not holding something, so the
   scrim underneath it gets them. The links take their own, and so does the
   top group, which has to be able to take a finger and scroll on a phone
   short enough to need it. Both close the overlay either way: see wireMenu in
   js/app.js, where one listener on the whole layer says so.

   ONLY WHILE IT IS OPEN. Scoping these two to the open state is the second
   half of the fix described on .hc-navmenu above, and it is deliberately a
   second half rather than a tidier alternative: visibility:hidden already
   stops every one of these being hit, and this makes sure that a future rule
   which makes some part of a closed overlay visible again, a peeking edge, a
   handle, cannot quietly hand it the whole screen's touches back. */
.hc-navmenu__panel {
  position: relative;
  z-index: 1;
  pointer-events: none;
}

#app[data-navmenu="open"] .hc-navmenu__link,
#app[data-navmenu="open"] .hc-navmenu__group--more { pointer-events: auto; }

/* The discs and the pill go down with the overlay up. They belong to the
   screen underneath, and the screen underneath is not what anybody is reading
   at that moment. */
#app[data-navmenu="open"] .hc-disc,
#app[data-navmenu="open"] .hc-jlink {
  opacity: 0;
  transform: translateY(10px) scale(0.86);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Section header. The signature pattern: eyebrow, serif title, short rule.
   -------------------------------------------------------------------------- */

.hc-section-header { margin-top: var(--hc-section-top); }
.hc-section-header--flush { margin-top: 0; }

.hc-section-header__eyebrow { display: block; }

.hc-section-header__title {
  margin-top: 8px;  /* The one deliberate departure from the reading serif: a heavy geometric
     sans, so a section break reads instantly while scrolling fast. */
  font-family: var(--hc-sans-display);
  font-weight: 800;
  font-size: calc(25px * var(--hc-text-scale));
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--hc-ink);
}

/* An eyebrow-less header, which is how Home names its blocks. Nothing above
   the title means nothing for it to sit under. */
.hc-section-header__title:first-child { margin-top: 0; }

.hc-section-header__rule {
  margin-top: 12px;
  width: 22%;
  min-width: 56px;
  height: 0.75px;
  background: var(--hc-accent);
  border: 0;
}

.hc-section-header + * { margin-top: var(--hc-space-base); }

/* Two headers in a row still get the full breath between them. */
.hc-section-header + .hc-section-header { margin-top: var(--hc-section-top); }

/* --------------------------------------------------------------------------
   Section header, the variant set from the right edge.

   THE PROBLEM IT SOLVES. A collapsible section draws the same three marks a
   section header does, eyebrow over title over a short taupe rule, because
   they are the same brand move at two levels. On Connect the two meet: "LEND A
   HAND / Serve teams" is followed immediately by seven of "SERVE TEAM / Home
   Kids", identical in face, size, weight and rule, and the header stops
   reading as the name of the list and starts reading as the first thing in it.

   WHY THE RIGHT EDGE. Every other line on these screens starts at the same
   left margin, so crossing to the other one is the loudest thing a header can
   do while still being made of exactly the same parts. The eyebrow is still
   over the title and the rule is still under it, at the same sizes and in the
   same colors and in the same order; the block is simply set from the far
   side, where nothing else is.
   -------------------------------------------------------------------------- */

.hc-section-header--right { text-align: right; }

/* The rule is a box with a width, not a line of text, so the alignment above
   does not reach it. Pushing all of the free space to its left is what hangs
   its right end on the same margin the title's last letter sits on. */
.hc-section-header--right .hc-section-header__rule { margin-left: auto; }

/* A STEP OF SIZE AND A LITTLE TRACKING, WHICH IS WHERE THE EXTRA WEIGHT COMES
   FROM. Poppins is bundled at 800 and at 800 only, see css/fonts.css, so
   font-weight: 900 here would not load a heavier cut, it would ask the browser
   to smear the one that is loaded. That synthetic bold would sit directly
   above seven titles drawing the real 800 and would read as a rendering fault
   rather than as emphasis. Three more pixels and a tighter fit buy the same
   impression out of the type that actually exists. */
.hc-section-header--right .hc-section-header__title {
  font-size: calc(28px * var(--hc-text-scale));
  letter-spacing: -0.02em;
}

/* Edit mode wraps the eyebrow in a tap target, and that target is a block
   with its own text-align: left, which parks a right hand eyebrow at the far
   left of a pill running the width of the screen while the title stays over
   on the right. Shrinking it to its contents lets the header's own alignment
   carry it across with everything else. Only admins in edit mode ever see
   this, which is exactly why it is worth saying here rather than leaving to be
   found. */
.hc-section-header--right > .hc-editable {
  display: inline-block;
  text-align: right;
}

/* --------------------------------------------------------------------------
   Cards. Color and hairlines separate surfaces, never shadows.
   -------------------------------------------------------------------------- */

.hc-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--hc-cream);
  border-radius: var(--hc-radius-lg);
  padding: var(--hc-card-pad);
  color: var(--hc-ink);
  transition: background var(--hc-dur-fast) var(--hc-ease);
}

button.hc-card:active,
a.hc-card:active { background: var(--hc-paper-sunk); }

.hc-card + .hc-card { margin-top: var(--hc-card-gap); }

/* The left edge rule, carried straight from the print guides. */
.hc-card--edge {
  border-left: var(--hc-edge-rule) solid var(--hc-accent);
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
}

.hc-card--quiet {
  background: transparent;
  border: var(--hc-hairline) solid var(--hc-rule);
}

.hc-card__title {
  font-family: var(--hc-serif);
  font-weight: 500;          /* was 300, see the note in base.css */
  font-size: calc(22px * var(--hc-text-scale));
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--hc-ink);
}

.hc-card__meta { margin-top: var(--hc-space-sm); }

/* --------------------------------------------------------------------------
   Quote card. Used for the one-liners from the pulpit.
   -------------------------------------------------------------------------- */

.hc-quote-card {
  background: var(--hc-cream);
  border-left: var(--hc-edge-rule) solid var(--hc-accent);
  border-radius: 4px var(--hc-radius-lg) var(--hc-radius-lg) 4px;
  padding: var(--hc-card-pad);
}

.hc-quote-card + .hc-quote-card { margin-top: var(--hc-card-gap); }

.hc-quote-card__text { color: var(--hc-ink); }

.hc-quote-card__foot {
  margin-top: var(--hc-space-md);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--hc-space-md);
}

.hc-quote-card__attr {
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: 10px;
  line-height: 14px;
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  color: var(--hc-mid);
}

.hc-share {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: var(--hc-tap-min);
  min-height: var(--hc-tap-min);
  margin: -12px -8px -12px 0;
  padding: 0 8px;
  color: var(--hc-accent-deep);
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-caption);
}

.hc-share__icon {
  width: 16px; height: 16px;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --------------------------------------------------------------------------
   Numbered question row. The most distinctive component carried from print.
   The numeral is ornament and is hidden from screen readers in the markup.
   -------------------------------------------------------------------------- */

.hc-numbered {
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: var(--hc-space-base);
  align-items: start;
  padding-top: var(--hc-space-base);
  border-top: var(--hc-hairline) solid var(--hc-rule);
}

.hc-numbered + .hc-numbered { margin-top: var(--hc-space-base); }

.hc-numeral {
  font-family: var(--hc-serif);
  font-weight: 500;          /* was 300, see the note in base.css */
  font-size: 20px;
  line-height: 27px;
  text-align: right;
  color: var(--hc-accent);
  font-variant-numeric: tabular-nums;
}

.hc-numbered__body { min-width: 0; }

/* --------------------------------------------------------------------------
   Checkable question row, so a leader can track coverage during group.
   -------------------------------------------------------------------------- */

.hc-check {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: var(--hc-space-md);
  align-items: start;
  width: 100%;
  text-align: left;
  padding: var(--hc-space-md) 0;
  min-height: var(--hc-tap-min);
  border-top: var(--hc-hairline) solid var(--hc-rule);
}

.hc-check__box {
  width: 22px;
  height: 22px;
  margin-top: 3px;
  border-radius: 6px;
  border: 1px solid var(--hc-rule);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--hc-dur-fast) var(--hc-ease),
              border-color var(--hc-dur-fast) var(--hc-ease);
}

.hc-check__tick {
  width: 13px; height: 13px;
  stroke: var(--hc-on-dark);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity var(--hc-dur-fast) var(--hc-ease);
}

.hc-check[aria-pressed="true"] .hc-check__box {
  background: var(--hc-accent-deep);
  border-color: var(--hc-accent-deep);
}

.hc-check[aria-pressed="true"] .hc-check__tick { opacity: 1; }

.hc-check[aria-pressed="true"] .hc-check__text { color: var(--hc-mid); }

.hc-check__text { transition: color var(--hc-dur-fast) var(--hc-ease); }

/* --------------------------------------------------------------------------
   Collapsible section, the six part guide structure.
   -------------------------------------------------------------------------- */

.hc-section {
  border-top: var(--hc-hairline) solid var(--hc-rule);
  padding-top: var(--hc-space-lg);
  margin-top: var(--hc-space-lg);
}

/* The header is a row of two things now: the toggle, which is still a full
   width button, and the narration controls, which only the guide reader ever
   passes. A section with no audio has an empty row and behaves exactly as it
   did when the <h2> held the button directly. */
.hc-section__bar {
  display: flex;
  align-items: flex-start;
  gap: var(--hc-space-sm);

  /* THE CHEVRON IS ON THE LEFT AND THE RIGHT EDGE BELONGS TO THE RAIL.
     The index rail is a 34px ruler fixed to right:0, and a chevron sitting
     against it read as one of the notches: you went to fold a part and put
     your thumb on the page index instead. So the mark that folds the section
     now leads the header from the left, where nothing else is, and the right
     of the bar is left to the rail.

     BOTH PADDINGS ARE HERE ON PURPOSE. The left one is the chevron's own
     column. The right one is not left over: the play button is a 34px disc
     and the rail is a 34px ruler, and without this gap the two share an edge
     and the notches read as part of the control. Keeping it means moving the
     chevron moved the chevron and nothing else — the play button is still
     exactly where it was.

     This is padding and a position rather than a move in the markup, and the
     reason is that the chevron has to stay inside the toggle. A tap resolves
     through evt.target.closest('[data-action]') in js/app.js, which reads DOM
     ancestry and not where a box was painted, so a chevron drawn at the front
     of its button still folds the section from the end of the markup. Moving
     it in the markup would have cost that, and cost the [aria-expanded="true"]
     selector below that turns it over, and put a second aria-expanded on the
     page to keep in step. */
  position: relative;
  padding-left: calc(20px + var(--hc-space-md));
  padding-right: calc(20px + var(--hc-space-md));
}

.hc-section__h {
  flex: 1 1 auto;
  min-width: 0;
}

.hc-section__toggle {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--hc-space-md);
  width: 100%;
  text-align: left;
  min-height: var(--hc-tap-min);
  padding: 0;
}

.hc-section__heading {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
}

.hc-section__heading .hc-eyebrow { display: block; }

.hc-section__rule {
  display: block;
  margin-top: 12px;
  width: 22%;
  min-width: 56px;
  height: 0.75px;
  background: var(--hc-accent);
}

.hc-section__title {
  display: block;
  margin-top: 8px;
  /* Matches .hc-section-header__title, same signature pattern. */
  font-family: var(--hc-sans-display);
  font-weight: 800;
  font-size: calc(25px * var(--hc-text-scale));
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--hc-ink);
}

.hc-section__chevron {
  /* Placed against .hc-section__bar's left padding, not laid out in the row.
     The 10px that was margin-top is now top: it is the same offset, still
     setting the mark against the title's cap rather than the middle of a
     heading that may run to two lines. */
  position: absolute;
  left: 0;
  top: 10px;
  width: 20px; height: 20px;
  stroke: var(--hc-accent-deep);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform var(--hc-dur-base) var(--hc-ease);
}

.hc-section__toggle[aria-expanded="true"] .hc-section__chevron { transform: rotate(180deg); }

/* --------------------------------------------------------------------------
   Narration, the play button on an open section

   ONLY ON AN OPEN SECTION, which is the point. Six play buttons stacked down
   a closed guide is a remote control. One play button on the part you just
   opened is an offer. js/app.js unhides these in the toggle-section handler,
   using the `hidden` attribute so a screen reader is told the same thing the
   eye is.
   -------------------------------------------------------------------------- */

.hc-section__controls {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--hc-space-xs);
  /* Sits on the title's line rather than the eyebrow's. The eyebrow is 11px
     over a 25px title, so the optical centre of the heading is well below the
     top of the box. */
  margin-top: 18px;
}

.hc-section__controls[hidden] { display: none; }

.hc-narrate {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-dark);
  color: var(--hc-on-dark);
  display: grid;
  place-items: center;
  padding: 0;
  transition: transform var(--hc-dur-fast) var(--hc-ease);
}

.hc-narrate:active { transform: scale(0.94); }

.hc-narrate__glyph {
  width: 15px;
  height: 15px;
  grid-area: 1 / 1;
}

/* The play triangle's mass sits left of its bounding box, so centring the box
   leaves the glyph looking shoved back. One pixel forward fixes it. */
.hc-narrate__glyph--play { transform: translateX(1px); }
.hc-narrate__glyph--pause { display: none; }

.hc-narrate[aria-pressed="true"] .hc-narrate__glyph--play { display: none; }
.hc-narrate[aria-pressed="true"] .hc-narrate__glyph--pause { display: block; }

/* Loading. A phone on one bar can sit here for a few seconds after the tap,
   and a button that does nothing visible is a button somebody presses again. */
.hc-narrate[data-state="loading"] .hc-narrate__glyph { opacity: 0.45; }

/* The row under the header while a section is talking: how far in, and how
   fast. js/narration.js builds it on play and removes it on stop, so a guide
   sitting still has none of these.

   THE SPEED PILL IS DOWN HERE AND NOT IN THE HEADER, and the reason is the
   heading. At 390px the header has room for a 27px title, a chevron and one
   34px button, and not for a 70px pill as well: putting it up there wrapped
   "Sermon Summary" onto two lines, and because the pill appears on play, the
   title reflowed under the thumb that pressed it. */
.hc-section__playbar {
  display: flex;
  align-items: center;
  gap: var(--hc-space-md);
  margin-top: var(--hc-space-md);
}

.hc-narrate-speed {
  flex: 0 0 auto;
  min-width: 52px;
  height: 28px;
  padding: 0 var(--hc-space-sm);
  border-radius: var(--hc-radius-pill);
  border: var(--hc-hairline) solid var(--hc-rule);
  color: var(--hc-accent-deep);
  font-family: var(--hc-sans);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--hc-track-button);
}

/* The same 1.5px as the app's signature left edge, laid flat. */
.hc-section__progress {
  flex: 1 1 auto;
  height: var(--hc-edge-rule);
  border-radius: var(--hc-edge-rule);
  background: var(--hc-rule);
  overflow: hidden;
}

.hc-section__progress > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--hc-accent-deep);
  transition: width 250ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .hc-narrate { transition: none; }
  .hc-section__progress > span { transition: none; }
}

.hc-section__panel {
  overflow: hidden;
  /* Animating grid rows keeps the collapse smooth without a measured height. */
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--hc-dur-base) var(--hc-ease);
}

.hc-section__panel[data-open="true"] { grid-template-rows: 1fr; }

.hc-section__panel > div { min-height: 0; }

.hc-section__panel[data-open="true"] > div { padding-top: var(--hc-space-base); }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.hc-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--hc-space-sm);
  width: 100%;
  min-height: 52px;
  padding: 0 var(--hc-space-lg);
  border-radius: var(--hc-radius-md);
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-button);
  line-height: var(--hc-button-line);
  letter-spacing: var(--hc-track-button);
  transition: opacity var(--hc-dur-fast) var(--hc-ease),
              background var(--hc-dur-fast) var(--hc-ease);
}

.hc-btn--primary {
  background: var(--hc-dark);
  color: var(--hc-on-dark);
}

.hc-btn--primary:active { opacity: 0.86; }

.hc-btn--secondary {
  background: transparent;
  border: 1px solid var(--hc-rule);
  color: var(--hc-ink);
}

.hc-btn--secondary:active { background: var(--hc-paper-sunk); }

.hc-btn--tertiary {
  width: auto;
  min-height: var(--hc-tap-min);
  padding: 0;
  color: var(--hc-accent-deep);
  background: none;
}

.hc-btn--tertiary:active { opacity: 0.7; }

/* A button that sits in a row of other buttons rather than alone under a
   card. Narrower and shorter, but never below --hc-tap-min, which is the one
   number in this file that is not a matter of taste. */
.hc-btn--small {
  width: auto;
  min-height: var(--hc-tap-min);
  padding: var(--hc-space-sm) var(--hc-space-md);
  font-size: 14px;
}

/* Tertiary and small together is the Group tab's Report and Block, sitting
   under somebody's answer. Tertiary alone has no padding at all, which is
   right for a lone link and wrong here: two of them side by side ran together
   and read as one control called "Report Block". */
.hc-btn--tertiary.hc-btn--small { padding: var(--hc-space-xs) var(--hc-space-sm); }

.hc-btn[disabled] {
  opacity: 0.5;
  pointer-events: none;
}

.hc-btn__icon {
  width: 18px; height: 18px;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: 0 0 auto;
}

/* A quiet inline link, used to cross into a guide from a sermon. */
.hc-inline-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--hc-tap-min);
  color: var(--hc-accent-deep);
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-caption);
}

/* An inline link that is reporting rather than offering: Get notified once a
   reminder is set, which reads "Reminding you Sep 11, 6:30 PM".

   Quieter than the offer beside it, and deliberately so. It has already been
   accepted, so it should not go on competing for the tap that Add to calendar
   is still asking for, and it stays a link because it is still the way back in
   to change the time or turn it off. --hc-mid is the same 4.5:1 secondary text
   colour every caption in the app uses, so quieter here does not mean fainter
   than legible. */
.hc-inline-link--on { color: var(--hc-mid); }

/* --------------------------------------------------------------------------
   Rows and lists
   -------------------------------------------------------------------------- */

.hc-row {
  display: flex;
  align-items: center;
  gap: var(--hc-space-base);
  width: 100%;
  text-align: left;
  min-height: var(--hc-tap-min);
  padding: var(--hc-space-base) 0;
  border-top: var(--hc-hairline) solid var(--hc-rule);
  color: var(--hc-ink);
}

.hc-row:last-child { border-bottom: var(--hc-hairline) solid var(--hc-rule); }

.hc-row__body { flex: 1 1 auto; min-width: 0; }

.hc-row__title {
  font-family: var(--hc-serif);
  font-weight: 400;
  font-size: calc(19px * var(--hc-text-scale));
  line-height: 1.3;
  color: var(--hc-ink);
}

.hc-row__label {
  font-family: var(--hc-sans);
  font-size: calc(var(--hc-body-sans) * var(--hc-text-scale));
  line-height: 1.4;
  color: var(--hc-ink);
}

.hc-row__value {
  flex: 0 0 auto;
  font-family: var(--hc-sans);
  font-size: var(--hc-body-sans);
  color: var(--hc-mid);
}

.hc-row__chevron {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  stroke: var(--hc-rule);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --------------------------------------------------------------------------
   Form fields. Inert in v1, styled for when they are not.
   -------------------------------------------------------------------------- */

.hc-field { display: block; }

.hc-field__label {
  display: block;
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-eyebrow);
  line-height: var(--hc-eyebrow-line);
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  color: var(--hc-accent-deep);
  margin-bottom: var(--hc-space-sm);
}

.hc-input,
.hc-textarea {
  width: 100%;
  background: var(--hc-paper-sunk);
  border: var(--hc-hairline) solid var(--hc-rule);
  border-radius: var(--hc-radius-sm);
  padding: 14px var(--hc-space-base);
  min-height: var(--hc-tap-min);
  font-family: var(--hc-sans);
  font-size: 16px;   /* 16px or larger, or iOS zooms the field on focus */
  line-height: 1.45;
  color: var(--hc-ink);
  transition: border-color var(--hc-dur-fast) var(--hc-ease);
}

.hc-textarea {
  font-family: var(--hc-serif);
  font-size: calc(var(--hc-body-serif) * var(--hc-text-scale));
  min-height: 88px;
  resize: vertical;
}

.hc-input::placeholder,
.hc-textarea::placeholder { color: var(--hc-mid); opacity: 1; }

.hc-input:focus,
.hc-textarea:focus { outline: none; border-color: var(--hc-accent-deep); }

/* Journal field under a reflection question. */
.hc-journal { margin-top: var(--hc-space-md); }

.hc-journal__status {
  margin-top: 6px;
  min-height: 18px;
  font-family: var(--hc-sans);
  font-size: 12px;
  line-height: 18px;
  color: var(--hc-mid);
  opacity: 0;
  transition: opacity var(--hc-dur-fast) var(--hc-ease);
}

.hc-journal__status[data-visible="true"] { opacity: 1; }

/* --------------------------------------------------------------------------
   Toggle switch
   -------------------------------------------------------------------------- */

.hc-switch {
  flex: 0 0 auto;
  width: 50px;
  height: 30px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-paper-sunk);
  border: var(--hc-hairline) solid var(--hc-rule);
  padding: 2px;
  display: flex;
  align-items: center;
  transition: background var(--hc-dur-fast) var(--hc-ease),
              border-color var(--hc-dur-fast) var(--hc-ease);
}

.hc-switch__knob {
  width: 24px;
  height: 24px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-paper);
  border: var(--hc-hairline) solid var(--hc-rule);
  transform: translateX(0);
  transition: transform var(--hc-dur-fast) var(--hc-ease);
}

.hc-switch[aria-checked="true"] {
  background: var(--hc-accent-deep);
  border-color: var(--hc-accent-deep);
}

.hc-switch[aria-checked="true"] .hc-switch__knob { transform: translateX(20px); }

/* The whole row is the target, so the switch itself never needs to be 44pt. */
.hc-switch-row {
  display: flex;
  align-items: center;
  gap: var(--hc-space-base);
  width: 100%;
  text-align: left;
  min-height: var(--hc-tap-min);
  padding: var(--hc-space-md) 0;
  border-top: var(--hc-hairline) solid var(--hc-rule);
}

.hc-switch-row:last-child { border-bottom: var(--hc-hairline) solid var(--hc-rule); }

/* --------------------------------------------------------------------------
   Pills, used for filters only. Never as a bright primary action.
   -------------------------------------------------------------------------- */

.hc-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hc-space-sm);
}

.hc-pill {
  min-height: var(--hc-tap-min);
  padding: 0 var(--hc-space-base);
  border-radius: var(--hc-radius-pill);
  border: var(--hc-hairline) solid var(--hc-rule);
  background: transparent;
  color: var(--hc-mid);
  font-family: var(--hc-sans);
  font-weight: 500;
  font-size: var(--hc-caption);
  transition: color var(--hc-dur-fast) var(--hc-ease),
              border-color var(--hc-dur-fast) var(--hc-ease),
              background var(--hc-dur-fast) var(--hc-ease);
}

.hc-pill[aria-pressed="true"] {
  color: var(--hc-ink);
  border-color: var(--hc-accent-deep);
  background: var(--hc-cream);
}

/* --------------------------------------------------------------------------
   Placeholder imagery. Solid cream blocks where photographs will go.
   Never stock photography.
   -------------------------------------------------------------------------- */

.hc-media {
  position: relative;
  width: 100%;
  background: var(--hc-cream);
  border-radius: var(--hc-radius-lg);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hc-media--16x9 { aspect-ratio: 16 / 9; }
.hc-media--4x3  { aspect-ratio: 4 / 3; }
.hc-media--1x1  { aspect-ratio: 1 / 1; }

.hc-media__label {
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  color: var(--hc-accent);
  padding: 0 var(--hc-space-base);
  text-align: center;
}

/* With a play affordance in the middle, the label steps aside to the corner. */
.hc-media--play .hc-media__label {
  position: absolute;
  left: 0; right: 0; bottom: var(--hc-space-md);
  text-align: left;
  padding: 0 var(--hc-space-base);
}

/* --------------------------------------------------------------------------
   Podcast cover art. Drawn, not fetched. The lockup on a dark panel, which is
   the show's own artwork, so a 64px row thumb and a full width hero agree.
   -------------------------------------------------------------------------- */

.hc-cover {
  position: relative;
  display: block;
  width: 100%;
  /* Literals, not tokens. Artwork is artwork, it looks the same in both
     themes, so the tile carries a hairline to hold its edge against a dark
     page rather than lightening to find one. */
  background: #1A1918;
  box-shadow: inset 0 0 0 var(--hc-hairline) var(--hc-rule);
  border-radius: var(--hc-radius-lg);
  overflow: hidden;
}

/* The play disc reads against the art, so it stays light in both themes. */
.hc-cover .hc-play__disc {
  background: #F7F4EF;
  border-color: rgba(247, 244, 239, 0.24);
}

.hc-cover .hc-play__icon { fill: #111111; }

.hc-cover__logo {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 62%;
  max-width: 260px;
  height: auto;
  opacity: 0.92;
}

/* The play disc owns the middle, so the wordmark sits above it. */
.hc-cover--play .hc-cover__logo {
  top: 26%;
  width: 48%;
}

/* The house mark alone. Capped so it stays a mark at any tile size. */
.hc-cover--compact .hc-cover__logo { width: 46%; max-width: 96px; }

/* Real artwork, over the drawn tile. The tile is already clipped to the
   radius and hides its overflow, so the picture needs neither of its own: it
   simply fills the frame it was handed, whatever shape that frame is, which
   is how one series graphic serves a 16:9 hero, a 4:3 rail tile and a 56px
   square thumb without three files. */
.hc-cover__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Art that never arrived, marked by the image error listener in js/app.js.
   Taking the picture away is the whole fix, because the drawn cover it was
   laid over is still underneath it. */
.hc-cover[data-failed="true"] .hc-cover__art { display: none; }

.hc-cover__label {
  position: absolute;
  left: 0; right: 0; bottom: var(--hc-space-md);
  padding: 0 var(--hc-space-base);
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  color: #F7F4EF;
  opacity: 0.7;
  text-align: left;
}

.hc-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hc-play__disc {
  width: 56px;
  height: 56px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-paper);
  border: var(--hc-hairline) solid var(--hc-rule);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--hc-dur-fast) var(--hc-ease);
}

.hc-play:active .hc-play__disc { transform: scale(0.96); }

.hc-play__icon {
  width: 20px; height: 20px;
  margin-left: 3px;
  fill: var(--hc-ink);
  stroke: none;
}

/* --------------------------------------------------------------------------
   Progress rule. Position, not pressure. No percentage, no streak.
   -------------------------------------------------------------------------- */

.hc-progress {
  height: 2px;
  width: 100%;
  background: var(--hc-rule);
  border-radius: var(--hc-radius-pill);
  overflow: hidden;
}

.hc-progress__fill {
  height: 100%;
  background: var(--hc-accent-deep);
  border-radius: var(--hc-radius-pill);
  transition: width var(--hc-dur-base) var(--hc-ease);
}

/* --------------------------------------------------------------------------
   Empty state. A hospitality moment, never a shrug.
   -------------------------------------------------------------------------- */

.hc-empty {
  padding: var(--hc-space-xl) var(--hc-space-base);
  text-align: center;
}

.hc-empty__icon {
  width: 32px; height: 32px;
  margin: 0 auto var(--hc-space-base);
  stroke: var(--hc-rule);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.hc-empty__text {
  font-family: var(--hc-serif);
  font-style: italic;
  font-size: calc(18px * var(--hc-text-scale));
  line-height: 1.5;
  color: var(--hc-mid);
}

/* --------------------------------------------------------------------------
   Banner. The announcement card, on Home and in the archive behind it.

   It used to be at most one of these and it is a list now, for the reasons
   announcementCard() in js/screens/home.js sets out. Always archivable rather
   than always dismissible: the corner puts the card in a list of its own
   rather than ending it, and the same card is what that list is made of.
   -------------------------------------------------------------------------- */

.hc-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--hc-space-md);
  background: var(--hc-cream);
  border-left: var(--hc-edge-rule) solid var(--hc-accent);
  border-radius: 4px var(--hc-radius-lg) var(--hc-radius-lg) 4px;
  padding: var(--hc-space-base) var(--hc-space-base) var(--hc-space-base) var(--hc-card-pad);
}

/* The card is a door. Everything but the x is inside one button, which is
   what makes the whole surface open the announcement's own page, and the x is
   its sibling rather than its child because a button inside a button is
   invalid markup and an ambiguous tap. See announcementCard() in
   js/screens/home.js. */
.hc-banner__open {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  color: inherit;
}

/* No `> * + *` stack here on purpose: the title and the picture frame already
   carry their own margins from before the card became a button, and a second
   rule on top of them would double every gap. */
.hc-banner__label,
.hc-banner__snippet { display: block; }

/* Three lines of the announcement and then it stops, the same rule an
   Instagram caption is held to on Connect and for the same reason: the front
   door shows enough to decide with, and the whole of it is one tap away on a
   screen where it reads properly. */
.hc-banner__snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--hc-mid);
}

/* What else is behind the card, and the chevron that says there is a behind.
   Same shape as the cue on the guide card above it, so two tappable cards on
   one screen say "there is more of this" the same way. */
.hc-banner__cue {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: var(--hc-space-md);
  color: var(--hc-accent-deep);
}

.hc-banner__chev {
  width: 14px; height: 14px;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.hc-banner__open:active { opacity: 0.85; }

/* The corner. Three of them, one shape.

   --archive is what an announcement card on Home carries, where the x used to
   be, and it puts the card in the archive rather than ending it.
   --select is what the same card carries in that archive, where it is a tick
   box instead: the thing you do to a card in there is choose it, and the
   restore link above the list is what acts on what you chose.
   --dismiss is the original x, and it is still here because it is still the
   right drawing somewhere else: js/screens/leader.js borrows it for removing
   a prayer request, which is a removal and not a filing.

   One set of box rules for all three, because they are the same corner of the
   same kind of card, and a corner that moved four pixels between two screens
   would read as the card having been redrawn. What differs is only what is
   inside: a 16px glyph, or the 22px tick box every other checkable thing in
   this app already uses. See js/screens/announcement-archive.js. */
.hc-banner__archive,
.hc-banner__select,
.hc-banner__dismiss {
  flex: 0 0 auto;
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  margin: -10px -10px -10px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--hc-mid);
}

/* stroke-linejoin is new beside the two rules the x needed. An x is two
   straight lines and has no joins; the archive box has corners, and without
   this they come to a point at 16px. */
.hc-banner__archive svg,
.hc-banner__dismiss svg {
  width: 16px; height: 16px;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The tick box is borrowed whole from .hc-check above, which is what a guide's
   coverage boxes are drawn with, so a tick in this app is one drawing. The
   aria-pressed selector is the one thing that has to be repeated: the rule up
   there is written against .hc-check, and this button is not one. */
.hc-banner__select[aria-pressed="true"] .hc-check__box {
  background: var(--hc-accent-deep);
  border-color: var(--hc-accent-deep);
}

.hc-banner__select[aria-pressed="true"] .hc-check__tick { opacity: 1; }


/* --------------------------------------------------------------------------
   The discs. Two of them, one material, one set of rules.

   --top   sits on the right and turns up once the screen has been scrolled
           past a screenful. One tap and you are back at the start of it.
   --back  sits on the left, and is there for the whole time you are on a
           screen that is not a tab. The tab bar cannot take you back out of a
           pushed view and the header's arrow is a stretch away from a thumb,
           so this is the way back that is always under the hand.

   They are the same material as the plinth below them, drawn the same way: a
   thin skin over a blur, a hairline edge, one diagonal sheen. Concentric with
   the bar too, sharing its 12px inset on both edges so the three line up on
   the same edges rather than nearly lining up.

   ON THE HEIGHT. --hc-chrome-space is the bar's whole footprint plus one
   inset, so these land exactly one inset above the plinth and the gap above
   the bar matches the gap beside it.

   ON THE SIZE. 44px is --hc-tap-min, not a number picked for looks. A disc
   cannot get smaller without the target getting smaller with it; if one ever
   needs to look smaller, keep this box and shrink what is painted inside it.
   -------------------------------------------------------------------------- */

.hc-disc {
  position: fixed;
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom));
  z-index: 45;                    /* over the content, under the toast */
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  padding: 0;
  border-radius: var(--hc-radius-pill);
  border: var(--hc-hairline) solid var(--hc-gold-edge);
  background: var(--hc-gold-skin);
  box-shadow: var(--hc-gold-shadow);
  color: var(--hc-gold-ink);
  display: grid;
  place-items: center;
  isolation: isolate;

  /* Down by default. Everything that puts one up is in the state below, so
     there is one place to look when it is in the wrong one. */
  opacity: 0;
  transform: translateY(10px) scale(0.86);
  pointer-events: none;
  transition: opacity var(--hc-dur-fast) var(--hc-ease),
              transform var(--hc-dur-fast) var(--hc-ease-enter);
}

.hc-disc--top  { right: var(--hc-chrome-inset); }
.hc-disc--back { left: var(--hc-chrome-inset); }

/* BESIDE THE BUTTON, NOT ABOVE IT.

   The rule above puts both discs one inset clear of whatever is at the bottom
   of the screen, which is right for the tab bar: the plinth is the full width
   of the glass and there is nowhere to be except above it. The button is not
   the full width. It is a 64pt circle in one corner, and stacking the way up
   directly on top of it made a totem of two round things in the same column
   and left the rest of the row empty.

   So on a phone set to 'button' the row comes down and sits beside it: the
   way up immediately to its left, the way back at the far end of the same
   row, and the two of them centred on the button rather than on a line of
   their own. Read left to right it is back, up, menu, which is also the order
   of how much each one is reached for.

   THE ARITHMETIC, so that resizing the button moves all of it. The right
   offset clears the button and one gap. The bottom offset is the button's own
   inset plus half the difference between the two diameters, which is what
   puts a 44 and a 64 on the same centre line.

   The journal pill comes down with them. It is the third thing on this row,
   not a fourth thing hovering above it: a link floating half a button's
   height over two arrows reads as something left behind by an older layout,
   and the row is only a row when all three of its objects are on one line. */
#app[data-nav="button"] .hc-disc {
  bottom: calc(var(--hc-chrome-inset) + env(safe-area-inset-bottom)
               + ((var(--hc-navfab-size) - var(--hc-tap-min)) / 2));
}

#app[data-nav="button"] .hc-disc--top {
  right: calc(var(--hc-chrome-inset) + var(--hc-navfab-size) + var(--hc-space-md));
}

/* THE PILL, ON THE SAME LINE AND CENTRED ON THE GAP RATHER THAN ON THE GLASS.

   Down to the discs' own bottom, which is the same offset they use, so the
   three of them share a centre line. Across, 50% is the middle of the screen
   and the middle of the screen is not the middle of this row: the right hand
   end carries the button and its gap as well as the disc, so a pill centred
   at 50% sits left of where the eye puts the middle of the three objects.

   THE ARITHMETIC. The gap runs from the back disc's right edge, at
   inset + 44, to the way up's left edge, at 100% - (inset + 64 + gap + 44).
   Its middle is 50% - (64 + gap) / 2, which is what the offset below says,
   and every term in it is the token the button and the discs are drawn from,
   so moving either moves the pill with them.

   IT DOES NOT READ THE ARROWS. The discs hold their slots whether they are up
   or down, because a disc that is down is an opacity and a transform over its
   slot rather than a gap that closes. So this lands in the one place on a
   guide with both arrows, on the journal with neither, and mid fade between
   the two. A pill that re-centred on whichever of them happened to be up
   would be a link that moves out from under the thumb reaching for it.

   AND THE WIDTH COMES OFF THE SAME SUM. What the row above gave it, less the
   button and its gap, which is the one thing this move costs. Two words in
   ten pixel caps still fit a 320pt phone; the ellipsis on .hc-jlink__text is
   what catches anything longer, and caps sliding under a disc is the failure
   it exists to prevent. */
#app[data-nav="button"] .hc-jlink {
  bottom: calc(var(--hc-chrome-inset) + env(safe-area-inset-bottom)
               + ((var(--hc-navfab-size) - var(--hc-tap-min)) / 2));
  left: calc(50% - ((var(--hc-navfab-size) + var(--hc-space-md)) / 2));
  max-width: calc(100% - ((var(--hc-chrome-inset) + var(--hc-tap-min) + var(--hc-space-sm)) * 2)
                       - var(--hc-navfab-size) - var(--hc-space-md));
}

/* Glass only where the browser can actually blur, exactly as the tab bar
   does it. Everywhere else the denser skin above stands in. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-disc {
    background: var(--hc-gold-skin-lens);
    backdrop-filter: blur(20px) saturate(1.7);
    -webkit-backdrop-filter: blur(20px) saturate(1.7);
  }
}

.hc-disc::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--hc-gold-sheen);
  pointer-events: none;
}

.hc-disc__icon {
  position: relative;
  z-index: 1;
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* A disc is thin enough to sit over a photograph, where neither an ink
     arrow nor a paper one is safe on its own. The halo is what carries it. */
  filter: drop-shadow(0 0 2px var(--hc-gold-halo))
          drop-shadow(0 1px 1px var(--hc-gold-halo));
}

.hc-disc[data-show="true"] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.hc-disc:active { transform: scale(0.93); }

.hc-disc:focus-visible {
  outline: 2px solid var(--hc-ink);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .hc-disc {
    transform: none;
    transition: opacity 1ms linear;
  }
  .hc-disc[data-show="true"] { transform: none; }
  .hc-disc:active { transform: none; }
}


/* --------------------------------------------------------------------------
   The journal pill. The third thing on the disc row, and the only one of the
   three that says what it does in words.

   In a guide it says MY JOURNAL and goes to what you have written about that
   guide. In the journal you got to that way it says BACK TO GUIDE and takes
   you back, arrow the other way round. One element, painted by
   paintJournalLink() in js/app.js, because two would eventually disagree
   about where they sit.

   THE SAME MATERIAL AS THE DISCS, DELIBERATELY. The gold skin, the hairline
   edge, the sheen and the blur, so the row above the plinth reads as one set
   of three objects rather than as two arrows with a caption between them.
   The ink is --hc-gold-ink, which is what the arrows in the discs are drawn
   in: gold is the surface here, never the type. Type set in --hc-gold on
   this app's paper measures 1.70:1 and fails at any size, which is why the
   words are dark on gold rather than gold on paper.

   ON THE HEIGHT, AND WHY IT IS TWO RECTANGLES. The capsule is 34px because
   that is what looks right beside a 44px disc. The button is 44px, which is
   --hc-tap-min, so the paint is drawn on ::before and the target is the
   element. Shrinking the button instead loses ten pixels of target with
   nothing on screen looking wrong.

   CENTRED ON ITS OWN, NOT IN A BAND. left: 50% and a translate rather than a
   full width strip with the pill inside it: a strip spanning the gap between
   the discs is a rectangle of dead pointer events over live guide prose, and
   this app has been bitten by an invisible layer eating taps before.
   -------------------------------------------------------------------------- */

.hc-jlink {
  position: fixed;
  left: 50%;
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom));
  z-index: 45;                    /* beside the discs, under the toast */

  /* Clear of both discs at every width: the inset each one floats on, its
     44px, and a space between. On the narrowest phone this leaves 172px. */
  max-width: calc(100% - ((var(--hc-chrome-inset) + var(--hc-tap-min) + var(--hc-space-sm)) * 2));

  height: var(--hc-tap-min);
  padding: 0 16px;
  margin: 0;
  border: 0;
  background: transparent;
  color: var(--hc-gold-ink);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  isolation: isolate;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;

  font-family: var(--hc-sans);
  font-weight: 650;
  font-size: 10px;
  line-height: 13px;
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  white-space: nowrap;

  /* Down by default, like the discs, and for the same reason: everything
     that puts it up is in the state below. */
  opacity: 0;
  transform: translate(-50%, 10px);
  pointer-events: none;
  transition: opacity var(--hc-dur-fast) var(--hc-ease),
              transform var(--hc-dur-fast) var(--hc-ease-enter);
}

/* The capsule, and the sheen over it. Both inset five pixels top and bottom
   off a 44px button, which is the 34px pill. */
.hc-jlink::before,
.hc-jlink::after {
  content: "";
  position: absolute;
  inset: 5px 0;
  border-radius: var(--hc-radius-pill);
  pointer-events: none;
  z-index: 0;
}

.hc-jlink::before {
  border: var(--hc-hairline) solid var(--hc-gold-edge);
  background: var(--hc-gold-skin);
  box-shadow: var(--hc-gold-shadow);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-jlink::before {
    background: var(--hc-gold-skin-lens);
    backdrop-filter: blur(20px) saturate(1.7);
    -webkit-backdrop-filter: blur(20px) saturate(1.7);
  }
}

.hc-jlink::after { background: var(--hc-gold-sheen); }

/* The words and the arrow ride over both. */
.hc-jlink__text,
.hc-jlink__icon {
  position: relative;
  z-index: 1;
}

/* A long guide title never gets in here, but a long label might, and caps
   sliding under a disc reads as a broken layout rather than as a label that
   does not fit. */
.hc-jlink__text {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.hc-jlink__icon {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* The same halo the disc puts behind its arrow, for the same reason: this
     one is drawn on gold rather than on whatever is scrolling past, but it
     keeps the two arrows looking like the same arrow. */
  filter: drop-shadow(0 0 2px var(--hc-gold-halo));
}

.hc-jlink[data-show="true"] {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}

.hc-jlink:active { transform: translate(-50%, 0) scale(0.96); }

.hc-jlink:focus-visible {
  outline: 2px solid var(--hc-ink);
  outline-offset: 3px;
  border-radius: var(--hc-radius-pill);
}

@media (prefers-reduced-motion: reduce) {
  .hc-jlink { transition: opacity 1ms linear; }
  .hc-jlink:active { transform: translate(-50%, 0); }
}

/* 320 wide, where the button's row is at its tightest. Coming down beside the
   button costs the pill the button's width and its gap, and on the smallest
   phone that is the difference between BACK TO GUIDE and BACK TO GUI...

   So the gutters come off rather than the words: the padding, the gap to the
   arrow, and the clearance to the discs, each down to what still reads as
   space between two objects. The tracking comes off with them, which is the
   move the navigation's two lists make one screen over and for the reason
   given there: twelve hundredths of an em is what makes a label at this size
   look considered, and the first thing worth spending when there is nothing
   else left to spend.

   The ellipsis on .hc-jlink__text stays as the floor under all of it. It is
   there for a label longer than these two, and caps sliding under a disc is
   still the failure it prevents. */
@media (max-width: 359px) {
  #app[data-nav="button"] .hc-jlink {
    padding: 0 8px;
    gap: 5px;
    letter-spacing: 0.06em;
    max-width: calc(100% - ((var(--hc-chrome-inset) + var(--hc-tap-min) + 4px) * 2)
                         - var(--hc-navfab-size) - var(--hc-space-md));
  }
}

/* --------------------------------------------------------------------------
   Toast. Quiet confirmation, bottom of the screen, fades out.
   -------------------------------------------------------------------------- */

.hc-toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom) + var(--hc-space-base));
  transform: translate(-50%, 8px);
  z-index: 60;
  max-width: calc(100% - 40px);
  background: var(--hc-dark);
  color: var(--hc-on-dark);
  border-radius: var(--hc-radius-md);
  padding: var(--hc-space-md) var(--hc-space-base);
  font-family: var(--hc-sans);
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--hc-dur-base) var(--hc-ease),
              transform var(--hc-dur-base) var(--hc-ease);
}

.hc-toast[data-visible="true"] {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Presentation mode has no tab bar, so the toast drops to the real bottom. */
#app[data-view="present"] .hc-toast {
  bottom: calc(env(safe-area-inset-bottom) + var(--hc-space-base));
}

/* --------------------------------------------------------------------------
   View transition. A soft fade, nothing more.
   -------------------------------------------------------------------------- */

.hc-view-enter {
  animation: hc-fade-in var(--hc-dur-base) var(--hc-ease-enter) both;
}

@keyframes hc-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Swiping between tabs. The layer the incoming screen rides in on, see
   js/swipe.js.

   It is fixed and full screen rather than sitting inside .hc-scroll, because
   the screen coming in has to be a whole screen: its own top padding under the
   header, its own bottom padding above the tab bar, clipped at the edges and
   scrolled nowhere. z-index puts it over the content it is arriving beside and
   under every piece of chrome, so the header, the date rail, the disc, and the
   tab bar hold still while the pages move underneath them.

   The screen on its way out is not in here. It stays in the mount and is moved
   by a transform, which .hc-scroll clips, so its scroll position is untouched
   and a cancelled gesture costs nothing at all.
   -------------------------------------------------------------------------- */

.hc-swipe {
  position: fixed;
  inset: 0;
  z-index: 30;
  overflow: hidden;
  /* Passing through: the finger that is dragging this belongs to the screen
     underneath, and nothing in here is tappable until it has landed. */
  pointer-events: none;
}

.hc-swipe__pane {
  position: absolute;
  inset: 0;
  overflow: hidden;
  /* Opaque, or the outgoing screen reads through the incoming one. */
  background: var(--hc-paper);
  /* The leading edge of a sheet of paper sliding over another one. Soft and
     narrow, so it is the seam that shows rather than a drop shadow. */
  box-shadow: 0 0 22px rgba(28, 24, 20, 0.10);
  will-change: transform;
}

/* The mount only carries this while a finger is on it. A permanent
   will-change on the one element every screen mounts into would keep a layer
   alive for the whole life of the app to serve a gesture lasting a second. */
.hc-view-dragging {
  will-change: transform;
}

/* --------------------------------------------------------------------------
   The rail. A horizontal strip of square photographs, used by the Instagram
   section at the top of Connect.

   FULL BLEED ON PURPOSE. .hc-screen carries 20px of side padding, and a rail
   that stopped at that padding would look like a list that had been cut off
   at both ends. The negative margins pull it out to the screen edges and the
   scroll padding puts the inset back on the content, so the first tile lines
   up with the text above it and the last one runs off the edge. That overrun
   is the affordance: it is what tells a thumb there is more to the right
   without needing an arrow or a row of dots.

   .hc-screen is capped at 720px and centered, so on a wide window the rail
   bleeds to the edges of the column rather than the edges of the display,
   which is the right answer for both.
   -------------------------------------------------------------------------- */

.hc-rail {
  margin-left: calc(var(--hc-screen-pad) * -1);
  margin-right: calc(var(--hc-screen-pad) * -1);
  overflow-x: auto;
  overflow-y: hidden;
  /* Momentum scrolling inside the Capacitor web view. */
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--hc-screen-pad);
  /* The scrollbar is chrome this design does not use, and on a phone it is
     not drawn at all. Hidden rather than styled. */
  scrollbar-width: none;
}

.hc-rail::-webkit-scrollbar { display: none; }

.hc-rail__track {
  display: flex;
  gap: var(--hc-card-gap);
  list-style: none;
  margin: 0;
  /* The trailing pad has to be padding and not a margin on the last child, or
     iOS drops it at the end of an overflow scroll and the last tile sits hard
     against the edge. */
  padding: 0 var(--hc-screen-pad);
}

.hc-rail__item {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

/* --- A single post -------------------------------------------------------- */

.hc-post {
  position: relative;
  display: block;
  width: 148px;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: none;
  /* Showing through while the photograph loads, and the whole tile if it
     never does. Same cream as every other piece of missing art in the app. */
  background: var(--hc-cream);
  border-radius: var(--hc-radius-lg);
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.hc-post__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* A photograph that fades in reads as arriving. One that snaps in reads as
     a flash, which is what nine of them landing at once looks like. */
  animation: hc-fade-in var(--hc-dur-base) var(--hc-ease-enter) both;
}

/* Set by the image error listener in app.js. The tile keeps its cream block
   rather than drawing a broken image glyph. */
.hc-post[data-failed="true"] .hc-post__img { display: none; }

.hc-post:active { opacity: 0.85; }

/* --- The way out, last tile in the rail ----------------------------------- */

.hc-post--more {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--hc-space-sm);
  background: var(--hc-paper-sunk);
  color: var(--hc-accent-deep);
}

.hc-post__more-icon {
  width: 22px;
  height: 22px;
}

.hc-post__more-label {
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
}

/* A rail is a horizontal scroll, which is a gesture, and a person who has
   asked for less motion still has to be able to reach the ninth tile. So the
   snapping goes and the scrolling stays. */
@media (prefers-reduced-motion: reduce) {
  .hc-rail { scroll-snap-type: none; }
  .hc-post__img { animation: none; }
}

/* --------------------------------------------------------------------------
   Social links, a centered row. Used on Home under the latest post.

   The glyphs are --hc-ink, which is near black on paper and near white in
   dark mode, so one token carries the whole inversion and these follow the
   theme exactly the way body text does. No disc behind them: a filled circle
   would have needed its own light and dark pair kept in step with this one,
   and five solid discs under a photograph read as a toolbar rather than as a
   quiet row of links.

   The button is 44 square while the glyph is 24. That gap is deliberate.
   --hc-tap-min is 44 and nothing here goes under it, but a 44pt logo is a
   billboard, so the target stays 44 and only the paint shrinks.
   -------------------------------------------------------------------------- */

/* Solid brand marks opt back out of the `svg { fill: none }` reset in
   base.css, which exists for this app's own stroke drawn icons and wins over
   a fill attribute. Set on the svg only: fill inherits, so children without
   one of their own pick it up, while Instagram's rect and circle keep the
   fill="none" they carry and stay an outline the way its real mark is. */
.hc-brand { fill: currentColor; }

/* The opt out of `svg { fill: none }` in base.css, for the app's own icons
   rather than somebody else's brand mark. Only `more` needs it so far: three
   dots drawn as 1.5 stroke rings are mostly hole at tab size. */
.hc-icon--solid { fill: currentColor; }

.hc-social-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--hc-space-sm);
  margin-bottom: var(--hc-space-lg);
}

.hc-social {
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  padding: 0;
  border: none;
  background: none;
  color: var(--hc-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.hc-social__icon {
  width: 24px;
  height: 24px;
  display: block;
  transition: transform var(--hc-dur-fast) var(--hc-ease),
              opacity var(--hc-dur-fast) var(--hc-ease);
}

.hc-social:active .hc-social__icon {
  opacity: 0.6;
  transform: scale(0.92);
}

@media (prefers-reduced-motion: reduce) {
  .hc-social__icon { transition: none; }
  .hc-social:active .hc-social__icon { transform: none; }
}

/* --------------------------------------------------------------------------
   The editor
   A contenteditable with five buttons over it. See js/editor.js.
   -------------------------------------------------------------------------- */

.hc-editor { margin-top: var(--hc-space-base); }

.hc-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--hc-space-xs);
  padding: var(--hc-space-xs);
  border: var(--hc-hairline) solid var(--hc-rule);
  border-bottom: 0;
  border-radius: var(--hc-radius-sm) var(--hc-radius-sm) 0 0;
  background: var(--hc-paper-sunk);
}

.hc-tool {
  min-width: var(--hc-tap-min);
  min-height: var(--hc-tap-min);
  padding: 0 var(--hc-space-sm);
  border-radius: var(--hc-radius-sm);
  background: transparent;
  color: var(--hc-mid);
  font-family: var(--hc-sans);
  font-size: calc(15px * var(--hc-text-scale));
  line-height: 1;
}

/* The three letters are the thing they do. Nobody needs a legend for a bold
   B, and an icon set for this would be five more glyphs to draw and keep. */
.hc-tool--b { font-weight: 700; }
.hc-tool--i { font-style: italic; font-family: var(--hc-serif); }
.hc-tool--u { text-decoration: underline; }

.hc-tool:active { background: var(--hc-cream); color: var(--hc-ink); }

/* No margin-left:auto pushing this to the right. At 375 across it does not
   fit on the same line as the five format buttons, and at a larger text scale
   it fits even less, so it wraps to a second line either way. Flowing left
   under the B reads as a deliberate second row; pinned right it reads as a
   button that fell off the end. */
.hc-tool--wide {
  display: inline-flex;
  align-items: center;
  gap: var(--hc-space-xs);
  padding: 0 var(--hc-space-md);
  font-size: var(--hc-caption);
  font-weight: 500;
  color: var(--hc-accent-deep);
}

.hc-tool__icon { width: 14px; height: 14px; }

.hc-rt {
  min-height: 34vh;
  padding: var(--hc-space-base);
  border: var(--hc-hairline) solid var(--hc-rule);
  border-radius: 0 0 var(--hc-radius-sm) var(--hc-radius-sm);
  background: var(--hc-paper-sunk);
  color: var(--hc-ink);
  font-family: var(--hc-sans);
  font-size: calc(var(--hc-body-sans) * var(--hc-text-scale));
  line-height: 1.6;
  /* Long words and pasted URLs must not push the screen sideways. */
  overflow-wrap: break-word;
}

.hc-rt:focus { outline: none; border-color: var(--hc-accent-deep); }

/* A contenteditable has no placeholder attribute. :empty is not enough on its
   own, because a browser that has ever had a caret in the box leaves a <br>
   behind, so this also matches a box whose only child is that break. */
.hc-rt:empty::before,
.hc-rt:has(> br:only-child)::before {
  content: attr(data-placeholder);
  color: var(--hc-mid);
  pointer-events: none;
}

.hc-rt p { margin: 0 0 var(--hc-space-md); }
.hc-rt p:last-child { margin-bottom: 0; }
.hc-rt ul, .hc-rt ol { margin: 0 0 var(--hc-space-md); padding-left: 1.4em; }
.hc-rt ul { list-style: disc; }
.hc-rt ol { list-style: decimal; }
.hc-rt li { margin-bottom: var(--hc-space-xs); }

/* A scripture reference inside somebody's writing. Not blue, not underlined:
   it is set in the serif the guides use for scripture, in the accent, so it
   reads as a citation rather than as a web link dropped into a paragraph. */
.hc-rt a,
.hc-jbody a {
  font-family: var(--hc-serif);
  color: var(--hc-accent-deep);
  text-decoration: none;
  border-bottom: var(--hc-hairline) solid var(--hc-accent);
}

/* --------------------------------------------------------------------------
   Scripture you can tap
   Every reference that opens the verse sheet says so the same way: a little
   heavier than the words around it, and every four and a half seconds a
   band of gold light crosses it, left to right, with a soft glow while it
   does, the way the light crosses the house on the splash screen. Then still
   again: it is a hint that the words do something, not a thing to watch.

   HOW. The text is painted by its own background, clipped to the letters.
   The background is the text's colour with one narrow band of the glint in
   the middle, three times wider than the words, and walking it from one end
   to the other carries the band across. currentColor rather than a token,
   so it is the same taupe or ink the words already were in either theme,
   and the hairline under a link keeps its own colour because a border is
   not text.

   WHERE. Anything that opens the sheet:
     a scripture link in a journal entry, a note or an announcement, found by
       its href, because those are markup somebody wrote and carry no class
     the scripture rows on a guide, cascading down the list, one row a beat
       after the one above (--hc-glint-at, set by scriptureRow())
     the passage under a verse highlight on its journal entry

   REDUCE MOTION keeps the weight and drops the light. The global rule in
   base.css shortens durations rather than removing animations, and an
   infinite animation shortened to nothing is a flicker, so it is switched
   off by name here instead.
   -------------------------------------------------------------------------- */

a[href^="https://www.bible.com/bible/"],
a[href^="https://www.biblegateway.com/"],
.hc-row[data-action="open-scripture"] .hc-row__title,
.hc-entry__ref {
  font-weight: 600;
  background-image: linear-gradient(100deg,
    currentColor 36%,
    var(--hc-scripture-glint) 44%,
    var(--hc-scripture-glint-hi) 50%,
    var(--hc-scripture-glint) 56%,
    currentColor 64%);
  background-size: 260% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: hc-scripture-glint 4.5s cubic-bezier(0.45, 0, 0.35, 1) infinite;
  animation-delay: var(--hc-glint-at, 0.8s);
}

/* Still for a little over half of the four and a half seconds, then one
   pass of about two, and while the light is on the words they glow a
   little, gold, the way the mark does on the splash screen.

   The glow is a filter rather than a text-shadow on purpose: the letters
   are transparent and painted by the background, and a text-shadow is
   drawn on top of that background, which would cover the gold with a
   blur. A drop-shadow filter is applied to what was painted, so it glows
   around the light instead of over it.

   The delay above staggers where in that cycle each reference starts, which
   is what makes a guide's list of rows light up one after another rather
   than all at once. */
@keyframes hc-scripture-glint {
  0%, 55% { background-position: 100% 0; filter: none; }
  70%     { filter: drop-shadow(0 0 5px var(--hc-scripture-glow)); }
  88%     { filter: drop-shadow(0 0 5px var(--hc-scripture-glow)); }
  100%    { background-position: 0% 0; filter: none; }
}

@media (prefers-reduced-motion: reduce) {
  a[href^="https://www.bible.com/bible/"],
  a[href^="https://www.biblegateway.com/"],
  .hc-row[data-action="open-scripture"] .hc-row__title,
  .hc-entry__ref { animation: none; }
}

/* A selection inside a reference has to be readable, and transparent letters
   over a selection highlight are not. While selected they are simply the
   colour they were. */
a[href^="https://www.bible.com/bible/"]::selection,
a[href^="https://www.biblegateway.com/"]::selection,
.hc-row[data-action="open-scripture"] .hc-row__title::selection {
  -webkit-text-fill-color: currentColor;
}

/* --------------------------------------------------------------------------
   The sheet
   One overlay, from the bottom, used by the scripture picker. The app's only
   modal, and it stays that way unless something else genuinely cannot be a
   pushed view.
   -------------------------------------------------------------------------- */

.hc-sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
}

.hc-sheet__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  background: rgba(26, 25, 24, 0.4);
  border: 0;
  animation: hc-fade-in var(--hc-dur-fast) var(--hc-ease);
}

.hc-sheet__panel {
  position: relative;
  width: 100%;
  max-height: 86vh;
  overflow-y: auto;
  padding: var(--hc-space-lg) var(--hc-space-base)
           calc(var(--hc-space-lg) + env(safe-area-inset-bottom));
  border-radius: var(--hc-radius-lg) var(--hc-radius-lg) 0 0;
  background: var(--hc-paper);
  border-top: var(--hc-hairline) solid var(--hc-rule);
  box-shadow: 0 -8px 32px rgba(26, 25, 24, 0.18);
  animation: hc-sheet-up var(--hc-dur-base) var(--hc-ease);
}

@keyframes hc-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes hc-sheet-up { from { transform: translateY(12%); } to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .hc-sheet__panel, .hc-sheet__scrim { animation: none; }
}

.hc-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--hc-space-base);
}

.hc-sheet__close {
  width: var(--hc-tap-min);
  height: var(--hc-tap-min);
  margin: calc(var(--hc-space-sm) * -1) calc(var(--hc-space-sm) * -1) 0 0;
  color: var(--hc-mid);
}

.hc-sheet__close svg { width: 18px; height: 18px; margin: 0 auto; }

/* Verse and Through side by side. They are two halves of one answer. */
.hc-sheet__verses {
  display: flex;
  gap: var(--hc-space-md);
  margin-top: var(--hc-space-md);
}

.hc-sheet__verses .hc-field { flex: 1 1 0; min-width: 0; }

.hc-sheet__foot { margin-top: var(--hc-space-lg); }

.hc-sheet__preview {
  margin-bottom: var(--hc-space-md);
  font-family: var(--hc-serif);
  font-size: calc(21px * var(--hc-text-scale));
  color: var(--hc-ink);
}

.hc-sheet__note { margin-top: var(--hc-space-md); }

/* A sheet redrawn in place, as the verse sheet is when its words arrive, has
   already made its entrance. Playing it again would bounce it on every
   answer. */
.hc-sheet[data-settled="true"] .hc-sheet__panel,
.hc-sheet[data-settled="true"] .hc-sheet__scrim { animation: none; }

/* The verse sheet, js/verse.js. The reference in the serif the picker uses
   for its preview, the words in the reading serif a guide uses, and the
   translation's copyright under them in caption size, because it has to be
   there and it does not have to be loud. */
.hc-verse__ref { margin-bottom: var(--hc-space-md); font-weight: 400; }

.hc-verse__body { min-height: 4.5em; }

.hc-verse__text + .hc-verse__text { margin-top: var(--hc-space-md); }

/* Line breaks in the Psalms are characters in the text rather than <br>s,
   because a highlight's offsets count characters. pre-line draws them and
   still folds runs of spaces. */
.hc-verse__text { white-space: pre-line; }

/* The passage under a highlight kept from the verse sheet, on its journal
   entry. Drawn the way a scripture link in the writing is, because it does
   the same thing: opens the passage. */
.hc-entry__ref {
  padding: 0;
  font: inherit;
  color: var(--hc-accent-deep);
  border-bottom: var(--hc-hairline) solid var(--hc-accent);
}

.hc-verse__status { color: var(--hc-mid); }

.hc-verse__credit {
  margin-top: var(--hc-space-lg);
  color: var(--hc-mid);
}

/* More than one passage in a reference. A row of pills that scrolls sideways
   rather than wraps, so a guide's long list does not push the words down. */
.hc-verse__pills {
  display: flex;
  gap: var(--hc-space-sm);
  margin: 0 calc(var(--hc-space-base) * -1) var(--hc-space-md);
  padding: 0 var(--hc-space-base);
  overflow-x: auto;
  scrollbar-width: none;
}

.hc-verse__pills::-webkit-scrollbar { display: none; }

.hc-verse__pill {
  flex: 0 0 auto;
  min-height: var(--hc-tap-min);
  padding: 0 var(--hc-space-md);
  border: var(--hc-hairline) solid var(--hc-rule);
  border-radius: 999px;
  color: var(--hc-mid);
  font-size: calc(14px * var(--hc-text-scale));
  white-space: nowrap;
}

.hc-verse__pill[aria-selected="true"] {
  border-color: var(--hc-accent);
  color: var(--hc-accent-deep);
}

/* The reminder sheet on the Cal tab. Three presets over the two boxes, which
   is the whole of the question it asks. It borrows .hc-sheet__verses for the
   day and the time, because those are two halves of one answer in exactly the
   way a verse and a through are. */
.hc-remind__presets { margin-top: var(--hc-space-lg); }

/* Turning one off, under the button that saves it rather than beside it. A
   destructive-ish action sharing a row with the one somebody came here to
   press is a mis-tap waiting to happen, and this one has no confirm behind it
   because it costs nothing to set again. */
.hc-remind__off { margin-top: var(--hc-space-md); }

/* --------------------------------------------------------------------------
   Highlighting
   A marker behind the words. One treatment, plus a heavier one for the ones
   that carry a note, because a rack of highlighter colours would be the
   loudest thing in a calm two-palette app.

   IT USED TO BE A WASH UNDER THE WORDS, a gradient filling the bottom 0.55em
   of the line. That is a real marker's shape on paper, where the ink soaks
   and the top of the stroke fades. On a phone, at a glance, it read as an
   underline: kept text looked like a link rather than like something somebody
   had gone back and marked. The block behind the words is louder and says the
   true thing, and it is the shape the hint in js/hints.js draws to teach it,
   so the two have to agree.

   The strength is a token per theme, --hc-hl-wash, because dark cannot carry
   what light can. See the note in css/tokens.css.
   -------------------------------------------------------------------------- */

.hc-hl {
  background: var(--hc-hl-wash);
  color: inherit;
  border-radius: 2px;
  cursor: pointer;
  /* The words are the tap target. No padding, or the marker starts creeping
     out past the sentence it belongs to. */
  padding: 0;
  /* A marker stroke overshoots the line a little. box-shadow rather than
     padding, because padding would move the text. */
  box-shadow: 0 1px 0 1px var(--hc-hl-wash), 0 -1px 0 1px var(--hc-hl-wash);
}

/* One that has been written about. Same hue, more of it, and a dog-ear so it
   is still distinguishable with no colour vision at all. */
.hc-hl--noted {
  background: var(--hc-hl-noted);
  box-shadow: 0 1px 0 1px var(--hc-hl-noted), 0 -1px 0 1px var(--hc-hl-noted),
              inset 2px 0 0 var(--hc-accent-deep);
}

.hc-hl:focus-visible {
  outline: 2px solid var(--hc-accent-deep);
  outline-offset: 2px;
}

@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  .hc-hl { background: var(--hc-cream); box-shadow: none; }
  .hc-hl--noted { background: var(--hc-cream); box-shadow: inset 2px 0 0 var(--hc-accent-deep); }
}

/* The bar for a selection. Docked in the band above the tab bar rather than
   floated over the words, because that band is the one place iOS never draws
   its own Copy / Look Up callout. The reasoning is at the top of
   js/highlight.js; all this file does is centre a pill down there.

   Centred by margin between two insets, not by translate, because the entry
   animation owns transform. */
.hc-hlbar {
  position: fixed;
  left: var(--hc-chrome-inset);
  right: var(--hc-chrome-inset);
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom));
  z-index: 55;
  width: -webkit-fit-content;
  width: fit-content;
  max-width: calc(100% - (var(--hc-chrome-inset) * 2));
  margin: 0 auto;
  display: flex;
  align-items: stretch;
  padding: 0 var(--hc-space-xs);
  border-radius: var(--hc-radius-pill);
  background: var(--hc-ink);
  box-shadow: 0 6px 20px rgba(26, 25, 24, 0.28);
  animation: hc-hlbar-in var(--hc-dur-fast) var(--hc-ease);
}

/* The discs stand down while it is up. The band is 44px tall and holds one
   thing: a back arrow the guide is not going anywhere without, and a way to
   the top that a scroll will offer again, both of which can wait for the
   answer to a question the person is being asked right now.

   The journal pill goes with them, and it is the one that would have been
   worst to leave: it sits dead centre of this band, which is exactly where
   Note this / Highlight lands, and the two would be drawn on top of each
   other over the one question in the app most likely to be asked. */
#app[data-hlbar="true"] .hc-disc {
  opacity: 0;
  transform: translateY(10px) scale(0.86);
  pointer-events: none;
}

/* Over the verse sheet. The sheet covers the tab bar and the band above it,
   so the bar comes up over the sheet instead, at the foot of it where Read
   the full chapter is, which is the one thing on the sheet that can wait
   while somebody decides what to do with the words they picked. */
#app[data-hlbar="sheet"] .hc-hlbar {
  z-index: 65;
  bottom: calc(var(--hc-space-lg) + env(safe-area-inset-bottom));
}

/* And the button it sits over steps aside, rather than showing its words
   round the edges of the bar. It is back the moment the selection ends. */
#app[data-hlbar="sheet"] [data-sheet="verse"] .hc-sheet__foot { visibility: hidden; }

#app[data-hlbar="true"] .hc-jlink {
  opacity: 0;
  transform: translate(-50%, 10px);
  pointer-events: none;
}

@keyframes hc-hlbar-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) { .hc-hlbar { animation: none; } }

.hc-hlbar__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--hc-space-xs);
  min-height: var(--hc-tap-min);
  padding: 0 var(--hc-space-base);
  background: transparent;
  color: var(--hc-paper);
  font-family: var(--hc-sans);
  font-weight: 500;
  font-size: var(--hc-caption);
  white-space: nowrap;
}

.hc-hlbar__icon { width: 14px; height: 14px; }

.hc-hlbar__rule {
  width: var(--hc-hairline);
  margin: var(--hc-space-sm) 0;
  background: var(--hc-paper);
  opacity: 0.3;
}

/* --------------------------------------------------------------------------
   The hint that teaches it
   Drawn by js/hints.js the first time somebody scrolls onto a section they
   just opened. A sentence marks itself, a card says what that means, and the
   bar it is about shows up where the bar really lives.

   IT NEVER TAKES A TAP. pointer-events: none on the layer and every child,
   and that line is the whole promise: there is no Got it, no x, and nothing
   to dismiss. Every tap during the two seconds it is up lands on whatever is
   under it, exactly as it would have if the hint had not been there. Any
   softening of this makes it a modal.

   IT IS ALSO NOT ENDED BY THAT TAP. It has a clock instead: it arrives, it
   sits for two seconds, it fades. See the note where the tap listener used to
   be in js/hints.js.

   EVERY MOVING PART IS transform OR opacity. HINTS.md §12 blames the stutter
   that got the last attempt at hints reverted on a shape that animated
   background-position over the blurred plinth. Nothing here animates a
   background, and nothing here is drawn over the plinth except the ghosted
   bar, which does not move.
   -------------------------------------------------------------------------- */

.hc-hint, .hc-hint *,
.hc-hint-marks, .hc-hint-marks * { pointer-events: none; }

.hc-hint {
  position: fixed;
  inset: 0;
  z-index: 48;   /* under the sheets at 55 and the toast at 60 */
}

/* THE MARKS ARE IN A LAYER OF THEIR OWN, AND IT HAS NO z-index ON PURPOSE.

   They blend with the page (see below), and mix-blend-mode only blends within
   the nearest stacking context. .hc-hint above is position: fixed with a
   z-index, which is a stacking context twice over, so marks parked in there
   blend against an empty transparent box and nothing happens: the words come
   out dimmed by a translucent taupe instead of highlighted by a marker. It
   looks close enough to right in a screenshot to survive a review and is
   wrong in the hand.

   position: absolute with z-index auto makes no stacking context, so these
   blend against #app, which is where the guide is drawn. That also means they
   sit under the sheets and the toast without asking, because a positioned
   element with no z-index still yields to one with a high one. */
.hc-hint-marks {
  position: absolute;
  inset: 0;
}

/* One box per line box of the sentence, grown from its left edge the way a
   thumb would drag a marker.

   THE BLEND MODE IS WHAT KEEPS THE WORDS READABLE. This layer is over the
   page rather than under it, so a solid wash would grey the text out.
   multiply leaves near black ink near black and turns the paper behind it
   taupe, which is what a real marker does; the dark theme screens instead,
   because multiplying charcoal only makes charcoal. */
.hc-hint__mark {
  position: absolute;
  border-radius: 3px;
  background: var(--hc-hl-wash);
  mix-blend-mode: multiply;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 420ms var(--hc-ease-enter), opacity 200ms var(--hc-ease);
}

:root[data-theme="dark"] .hc-hint__mark { mix-blend-mode: screen; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hc-hint__mark { mix-blend-mode: screen; }
}

.hc-hint__mark[data-in="true"] { transform: scaleX(1); }

/* The box the card rides in, so that following a scroll and arriving are two
   transforms on two elements rather than one fighting the other. */
.hc-hint__follow {
  position: absolute;
  inset: 0;
}

/* The words, and the bar. Both arrive the way the toast does: a short rise
   and a fade, nothing springy. */
.hc-hint__say {
  position: absolute;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 260ms var(--hc-ease-enter), transform 260ms var(--hc-ease-enter);
}

.hc-hint__say[data-in="true"] { opacity: 1; transform: none; }

/* Going away on its own clock is not the same as being put away by a tap. A
   tap is an answer, so the hint gets out of its way at once; a clock nobody
   set leaves slowly enough to be seen leaving. */
.hc-hint[data-going="soft"] .hc-hint__say,
.hc-hint-marks[data-going="soft"] .hc-hint__mark { transition: opacity 450ms var(--hc-ease); }

.hc-hint[data-going] .hc-hint__say,
.hc-hint-marks[data-going] .hc-hint__mark { opacity: 0; }

/* The card. The guides' signature left edge rule, so it reads as a piece of
   this app rather than as a tooltip. No nub and no caret: proximity and the
   marker do the work, and a nub is one more thing to get wrong against a
   line that wrapped. */
.hc-hint__card {
  display: flex;
  align-items: center;
  gap: 9px;
  max-width: 100%;
  padding: 11px 14px 11px 15px;
  border-radius: var(--hc-radius-lg);
  background: var(--hc-cream);
  box-shadow: inset var(--hc-edge-rule) 0 0 var(--hc-accent),
              0 6px 18px rgba(26, 25, 24, 0.10);
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  color: var(--hc-ink);
}

.hc-hint__icon {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  color: var(--hc-accent-deep);
}

/* The real bar, ghosted, in the band it really occupies. Not a picture of the
   bar somewhere convenient: the whole point of drawing it is that this is
   where the answer will appear. Kept in step with .hc-hlbar above. */
.hc-hint__bar {
  position: fixed;
  left: var(--hc-chrome-inset);
  right: var(--hc-chrome-inset);
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom));
  display: flex;
  justify-content: center;
}

.hc-hint__pill {
  display: flex;
  align-items: stretch;
  padding: 0 var(--hc-space-xs);
  border-radius: var(--hc-radius-pill);
  background: var(--hc-ink);
  box-shadow: 0 6px 20px rgba(26, 25, 24, 0.28);
  opacity: 0.92;
}

.hc-hint__pill span {
  display: inline-flex;
  align-items: center;
  gap: var(--hc-space-xs);
  min-height: var(--hc-tap-min);
  padding: 0 var(--hc-space-base);
  color: var(--hc-paper);
  font-family: var(--hc-sans);
  font-weight: 500;
  font-size: var(--hc-caption);
  white-space: nowrap;
}

/* NO DARK OVERRIDE HERE, AND THAT IS THE POINT. --hc-ink and --hc-paper both
   turn over with the theme, so the real bar in js/highlight.js is ink on
   paper in the light and a paper coloured pill with dark type in the dark.
   The ghost reads the same two tokens and turns over with it. A ghost that is
   not quite the bar teaches the wrong bar, and the first draft of this had a
   hand-picked near black here that would have been a dark pill in a theme
   where the real one is light. */

/* Scoped past .hc-hint__pill span above, which would otherwise give the
   divider that rule's 16px of padding and a 44px height and draw it as a grey
   block between the two labels rather than as a hairline. */
.hc-hint__pill .hc-hint__rule {
  width: var(--hc-hairline);
  min-height: 0;
  padding: 0;
  margin: var(--hc-space-sm) 0;
  background: var(--hc-paper);
  opacity: 0.3;
}

/* Reduce Motion degrades this rather than refusing it, which is the opposite
   of the call js/index-rail.js makes about its own hint. That one is
   decoration and this one is information: taking it away from somebody who
   asked for stillness would be taking away the only thing in the app that
   says the words can be kept. So nothing draws itself, and it is simply
   there, held a beat longer by js/hints.js because there is no movement to
   catch the eye. */
@media (prefers-reduced-motion: reduce) {
  .hc-hint__mark { transition: opacity 200ms linear; transform: scaleX(1); }
  .hc-hint__say { transition: opacity 200ms linear; transform: none; }
}

/* The note sheet. The quotation on top, the writing surface under it. */
.hc-hlsheet__quote {
  margin: 0 0 var(--hc-space-base);
  padding-left: var(--hc-space-base);
  border-left: var(--hc-edge-rule) solid var(--hc-accent);
  font-size: calc(18px * var(--hc-text-scale));
  line-height: 1.45;
}

/* Inside a sheet the writing surface has a keyboard under it, so it gives up
   the third of a screen it takes on the entry screen. */
.hc-sheet .hc-rt { min-height: 22vh; }

.hc-hlsheet__foot { display: flex; flex-direction: column; gap: var(--hc-space-sm); }

/* ==========================================================================
   The screen before Home

   Study A out of demo-splash, at the sizes it was drawn at. The greeting
   borrows the Home tab's own face and size exactly, so handing off reads as
   the words changing rather than the screen changing. See js/splash.js for
   how long it stays and what takes it away.
   ========================================================================== */

.hc-splash {
  position: fixed;
  inset: 0;
  /* Over everything, including the toast at 60 and the sheets at 55. Nothing
     in the app is meant to be reachable while this is up. */
  z-index: 95;
  background: var(--hc-paper);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.hc-splash--out {
  opacity: 0;
  transition: opacity 420ms ease-in;
  pointer-events: none;
}

.hc-splash__markwrap {
  position: relative;
  width: 92px;
  margin-bottom: var(--hc-space-lg);
}

.hc-splash__mark { display: block; width: 92px; height: auto; }

/* The glimmer. The mark's own silhouette is used as a mask, so a band of warm
   white with slightly deeper gold on either side of it slides across and is
   clipped to the house. The light never touches the paper around it, there is
   no second asset to draw, and nothing here has to be redone if the logo is.

   The dark shoulders are not decoration. White on gold on an off-white ground
   is very nearly nothing on its own, and the darker edges are what give the
   highlight something to be brighter than. */
.hc-splash__gleam {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask: url('../assets/icons/mark.png') center / contain no-repeat;
          mask: url('../assets/icons/mark.png') center / contain no-repeat;
  background-image: linear-gradient(105deg,
    rgba(138, 104, 44, 0) 24%,
    rgba(138, 104, 44, 0.32) 39%,
    rgba(255, 251, 242, 0.95) 50%,
    rgba(138, 104, 44, 0.32) 61%,
    rgba(138, 104, 44, 0) 76%);
  background-repeat: no-repeat;
  background-size: 300% 100%;
  background-position: 100% 0;
  opacity: 0;
}

.hc-splash__greeting {
  margin: 0;
  padding: 0 var(--hc-space-xl);
  text-align: center;
  /* Identical to .hc-home__greeting, deliberately. If that one ever moves,
     this one moves with it. */
  font-family: var(--hc-sans-display);
  font-weight: 800;
  font-size: calc(28px * var(--hc-text-scale));
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--hc-ink);
}

.hc-splash__loading {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--hc-space-lg) + env(safe-area-inset-bottom));
  margin: 0;
  text-align: center;
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  color: var(--hc-mid);
}

/* The entrance. The house settles on its own, the welcome comes up behind it,
   the loading line arrives last and breathes, and the light crosses the mark
   at 1.9s, still going as the layer starts to leave.

   base.css collapses all of this under Reduce Motion, which leaves the three
   pieces on screen at once and no light. That is the intended still version,
   and js/splash.js shortens the hold to match. */
.hc-splash__mark,
.hc-splash__greeting,
.hc-splash__loading { opacity: 0; }

.hc-splash__mark {
  animation: hc-splash-rise 900ms cubic-bezier(0.16, 0.84, 0.44, 1) 140ms forwards,
             hc-splash-bloom 1000ms ease-in-out 1900ms;
}

/* THE WELCOME IS TWO ANIMATIONS AND THAT IS THE WHOLE POINT OF IT. It used to
   be one: hc-splash-rise, opacity and travel together on the same hard ease
   out, starting at 460ms while the house was still climbing. Nobody ever saw
   it move. That curve is almost all front, 39% through at 90ms and 76% at
   270ms, so by the time the words were solid enough to read, under 2pt of
   their 8pt rise was left, and that last 2pt took another 600ms. What it
   read as was a fade in place.

   Split apart, the fade is short and the travel is long: solid in 260ms so
   the words can be seen at all, still climbing for 680 from 20pt on an ease
   out gentle enough to spread the move out. Over 8pt of it now happens in
   front of somebody who can read it.

   The delay waits for the house. Its rise ends at 1.04s, so 980ms is the
   welcome arriving after it rather than with it, and the whole thing is
   settled at 1.66s: clear of the light at 1.9s, and more than a second
   before the layer leaves at 2.75s. Nothing about the hold moved for this.
   See demo-splash/sequence.src.html for the drawing it was chosen from. */
.hc-splash__greeting {
  animation: hc-splash-lift 680ms cubic-bezier(0.39, 0.58, 0.57, 1) 980ms forwards,
             hc-splash-fade 260ms ease-out 980ms forwards;
}

/* Reduce Motion is the one place the wait costs something. base.css collapses
   every duration to nothing but leaves the delays alone, so the still version
   is the three pieces appearing in order, and js/splash.js holds it for
   1200ms. A welcome that waited until 980 would be readable for two tenths of
   a second. There is no house rising to follow in that version anyway, so it
   arrives with the house instead. */
@media (prefers-reduced-motion: reduce) {
  .hc-splash__greeting { animation-delay: 140ms; }
}

.hc-splash__loading {
  animation: hc-splash-fade 700ms ease 1000ms forwards,
             hc-splash-breathe 2800ms ease-in-out 1800ms infinite;
}

.hc-splash__gleam {
  animation: hc-splash-sweep 1000ms cubic-bezier(0.4, 0, 0.3, 1) 1900ms forwards;
}

@keyframes hc-splash-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Travel only. The welcome's opacity is hc-splash-fade, on its own shorter
   clock, which is what makes the movement visible. */
@keyframes hc-splash-lift {
  from { transform: translateY(20px); }
  to   { transform: translateY(0); }
}

@keyframes hc-splash-fade { to { opacity: 1; } }

@keyframes hc-splash-breathe {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 0.9; }
}

/* 100% to 0% and not the other way round. At 100% the gradient sits far
   enough left that the highlight is off the left edge of the house, so
   winding it down to 0 walks the light across to the right. */
@keyframes hc-splash-sweep {
  0%   { opacity: 0; background-position: 100% 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; background-position: 0% 0; }
}

@keyframes hc-splash-bloom {
  0%, 100% { filter: none; }
  50%      { filter: drop-shadow(0 0 7px rgba(215, 186, 133, 0.34)); }
}

/* ==========================================================================
   Maintenance mode

   The screen above, held. Same layer, same house, same face for the words,
   with "We'll be back soon." where the welcome would be. Drawn by
   js/maintenance.js over everything, the splash and its gate included, and
   none of it animates: this is the settled frame of the greeting, and it
   stays on the glass until an admin turns the switch off.
   ========================================================================== */

.hc-maintenance { z-index: 100; }

.hc-maintenance__mark { display: block; width: 92px; height: auto; }

.hc-maintenance .hc-splash__greeting {
  animation: none;
  opacity: 1;
  transform: none;
}

.hc-maintenance__foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--hc-space-lg) + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 var(--hc-space-xl);
}

/* Where Loading sits on the splash, and just as quiet. An admin needs to be
   able to find it; nobody else needs to notice it. */
.hc-maintenance__link {
  background: none;
  border: 0;
  padding: var(--hc-space-sm) var(--hc-space-base);
  min-height: var(--hc-tap-min);
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  color: var(--hc-mid);
  opacity: 0.7;
}

.hc-maintenance__form {
  width: 100%;
  max-width: 320px;
  display: flex;
  flex-direction: column;
  gap: var(--hc-space-sm);
}

.hc-maintenance__note {
  margin: 0;
  min-height: var(--hc-caption-line);
  text-align: center;
  color: var(--hc-mid);
}

/* ==========================================================================
   The way in

   What the screen above becomes on a phone that is not signed in: the same
   layer, the same house, the same welcome, lifted to make room for two
   buttons underneath them. See js/gate.js for the order of it.

   NOTHING HERE FADES BETWEEN STATES. The lockup climbs, the panels slide, and
   the light at the end comes out from behind the mark. Every one of those is
   a movement of something already on the glass, which is the difference
   between a greeting that turns into a door and two screens taking turns.
   ========================================================================== */

/* The house and the welcome, wrapped so one transform carries both. It has to
   be this element and not the two inside it: those are still holding the
   transform their entrance animation ended on, and a transition on a property
   an animation is filling forwards is a second opinion about the same number.
   This div has no animation, so it is free to move. */
.hc-splash__lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: transform 720ms cubic-bezier(0.16, 0.84, 0.44, 1);
}

.hc-splash--gate .hc-splash__lockup { transform: translateY(-104px); }

/* Loading is finished being true the moment there is something to tap. */
.hc-splash--gate .hc-splash__loading {
  animation: none;
  opacity: 0;
  transition: opacity 260ms ease;
}

/* THE DECK IS FULL BLEED AND THE PADDING IS ON THE PANELS. A panel is meant
   to arrive from the right edge of the screen, so the thing that clips it has
   to reach that edge. Anchored below the middle rather than to the bottom of
   the screen, because the middle is where the lockup is and the two of them
   move as one arrangement; the keyboard shrinking the viewport takes both up
   together instead of pushing this into it. */
.hc-splash__gate {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% + 24px);
  overflow: hidden;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 420ms ease,
              transform 520ms cubic-bezier(0.16, 0.84, 0.44, 1);
}

.hc-splash--gate .hc-splash__gate { opacity: 1; transform: translateY(0); }

/* Three panels side by side and the track is what moves. A carousel rather
   than three elements each sliding on their own: one transform means the
   panel leaving and the panel arriving can never disagree about where the
   middle of the screen is, and the one going back the other way is the same
   number in reverse rather than a second set of rules. */
.hc-gate__track {
  display: flex;
  width: 300%;
  transition: transform 560ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.hc-gate__track[data-step="1"] { transform: translateX(-33.3333%); }
.hc-gate__track[data-step="2"] { transform: translateX(-66.6667%); }

/* The password panel shares the third slot with the code panel and starts
   out of the flow, so the track is always exactly three panels wide. This
   rule is what makes that true: `hidden` alone loses to the `display: flex`
   directly below it, and a panel that is meant to be gone but is merely
   transparent still takes a third of the track. */
.hc-gate__panel[hidden] { display: none; }

.hc-gate__panel {
  flex: 0 0 33.3333%;
  width: 33.3333%;
  display: flex;
  flex-direction: column;
  gap: var(--hc-space-md);
  padding: 0 var(--hc-screen-pad);
  /* Every panel is as tall as the tallest one, which is what keeps the track
     from changing height under a slide. */
  align-self: stretch;
}

.hc-gate__label {
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  letter-spacing: var(--hc-track-eyebrow);
  text-transform: uppercase;
  color: var(--hc-accent-deep);
}

.hc-gate__note {
  margin: 0;
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  color: var(--hc-mid);
  text-align: center;
}

.hc-gate__error {
  margin: 0;
  font-family: var(--hc-sans);
  font-size: var(--hc-caption);
  line-height: var(--hc-caption-line);
  color: var(--hc-error);
}

/* Tertiary is a link with no width of its own, which sits left in a column.
   In here it is the way back and it belongs under the middle of the button
   above it. */
.hc-gate__back { align-self: center; }

/* --------------------------------------------------------------------------
   You're in

   The one moment in this app that is allowed to be loud. The panels drop, the
   house settles back toward the middle of the screen, and light comes out
   from behind it: a warm glow with no edge on it and nothing else. One shape,
   softly lit and softly gone.

   Under Reduce Motion base.css collapses all of it, and because the burst
   ends at opacity 0 it is simply never seen, which is the intended still
   version. The words still change, which is the part that carries the news.
   -------------------------------------------------------------------------- */

/* A round warm glow with no edge to it, brightest right behind the house and
   gone by the time it reaches the words. This is the whole effect now.

   TWO THINGS WERE HERE BEFORE IT AND BOTH CAME OUT. First a conic gradient
   masked to a ring, which drew a sunburst: a cartoon of light rather than
   light, when what this moment wants is the screen brightening from behind
   the mark. Then a hairline ring travelling out of the glow, which was closer
   but still something drawn on top of the light rather than the light itself.
   What is left is the part that was never in question. */
.hc-splash__burst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 340px;
  height: 340px;
  margin: -170px 0 0 -170px;
  pointer-events: none;
  border-radius: 50%;
  opacity: 0;
  /* THE ALPHAS ARE THE BRIGHTNESS AND THE ONLY BRIGHTNESS. Every stop is one
     quarter down from where this was first drawn, and the reach, the scale and
     the timing were left exactly where they were: dimming a glow by shrinking
     it makes it a smaller glow rather than a softer one. If this ever wants
     turning up or down again, it is these four numbers and nothing else. */
  background: radial-gradient(closest-side,
    rgba(255, 250, 238, 0.75) 0%,
    rgba(236, 212, 164, 0.47) 28%,
    rgba(219, 192, 143, 0.20) 46%,
    rgba(215, 186, 133, 0) 68%);
  /* Behind the house, which is the only place light coming from behind the
     house can be. */
  z-index: -1;
}

/* The gate stands down. */
.hc-splash--in .hc-splash__gate {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
}

/* And the house comes most of the way back to where it started. After this
   rule, not before it: both classes are on the layer at this point and they
   carry the same weight, so the order in this file is what decides. */
.hc-splash--in .hc-splash__lockup { transform: translateY(-32px); }

.hc-splash--in .hc-splash__burst {
  animation: hc-burst-core 1200ms cubic-bezier(0.12, 0.66, 0.28, 1) forwards;
}

.hc-splash--in .hc-splash__markwrap {
  animation: hc-gate-pop 760ms cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
}

/* Replaces both entrance animations still filling this element, which is why
   it holds opacity 1 at both ends: whatever this says is what the greeting is
   from here on. One name in the list is enough to do that, since a shorthand
   sets the whole list. */
.hc-splash--in .hc-splash__greeting {
  animation: hc-gate-say 520ms cubic-bezier(0.16, 0.84, 0.44, 1) forwards;
}

/* Up fast and gone slowly, which is what light does. It barely grows, because
   a glow that travels is a balloon: what changes over the twelve hundred
   milliseconds is how bright it is, not how big. */
@keyframes hc-burst-core {
  0%   { opacity: 0; transform: scale(0.5); }
  16%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.22); }
}

@keyframes hc-gate-pop {
  0%   { transform: scale(1); }
  36%  { transform: scale(1.13); }
  100% { transform: scale(1); }
}

@keyframes hc-gate-say {
  from { opacity: 0; transform: translateY(7px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* A shorter phone lifts less, so the welcome does not climb into the notch on
   the way to making room. */
@media (max-height: 700px) {
  .hc-splash--gate .hc-splash__lockup { transform: translateY(-84px); }
  .hc-splash--in .hc-splash__lockup { transform: translateY(-24px); }
  .hc-splash__gate { top: calc(50% + 12px); }
}

/* --------------------------------------------------------------------------
   The index rail. The right edge, in place of a scrollbar this design does
   not draw. See js/index-rail.js for what moves and why.

   Three layers and a veil, all of them absolutely placed against #app so a
   screen never has to know they exist: the notches, which are always on the
   glass; the page's headings written out, which are not; the card behind the
   focused one; and the paper that comes back over the page underneath the
   whole thing.

   NOTHING HERE TRANSITIONS. Every value that moves is written frame by frame
   from one gaussian in the JavaScript, and a transition on top of that is a
   second opinion about the same number. The one exception is the state below,
   which only takes the reading out of the tree once the swell is spent.
   -------------------------------------------------------------------------- */

.hc-index {
  position: fixed;
  z-index: 46;                    /* over the page, beside the discs */
  right: 0;
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + env(safe-area-inset-top));
  /* Clear of the back to top disc rather than merely behind it. The disc is
     44px of gold in that corner and it wins the tap either way, but a track
     that runs under it is a track with a dead end in it. */
  bottom: calc(var(--hc-chrome-space) + var(--hc-tap-min) + 8px
               + env(safe-area-inset-bottom));
  width: 34px;

  /* The notches are drawn, never touched. The gesture is read off .hc-scroll,
     which is what leaves the right edge available to the tab swipe as well.
     See the note at the top of js/index-rail.js.

     So this width is how wide the ruler looks, not how far in a thumb has to
     land to work it. That is HOT_DRAG in js/index-rail.js, and it is wider
     than this on purpose: few people scrub the actual glass edge. Widening
     this instead would only draw longer marks. */
  pointer-events: none;
}

/* Listen's month strip is a second band of chrome under the header, and the
   track starts below it while it is out. 42px is that strip's own height; it
   is written here rather than shared because the two are only ever equal by
   agreement, and a rail that quietly resized this one would be worse than a
   number that has to be changed twice. */
.hc-date-rail[data-show="true"] ~ .hc-index {
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + 42px + env(safe-area-inset-top));
}

.hc-index[hidden] { display: none !important; }

/* The page, while the rail has the finger. A touch that lands on a flying
   page cannot be refused from a touch listener on a phone — the scroll owns
   it before the listener runs — so the scroll is taken away instead: no
   overflow, no fling, no pan. js/index-rail.js still moves the page itself,
   because a script can scroll what a thumb cannot. See hold() there. */
.hc-scroll[data-index-held] {
  overflow-y: hidden;
  touch-action: none;
}

/* The written column, the card and the veil come and go together. */
.hc-index[data-state="off"] ~ .hc-index__titles,
.hc-index[data-state="off"] ~ .hc-index__card,
.hc-index[data-state="off"] ~ .hc-index__veil { visibility: hidden; }

.hc-index__track { position: absolute; inset: 0; }

.hc-index__notch {
  position: absolute;
  right: 0;
  width: 34px;
  display: block;
  /* A notch is reachable with a keyboard and drawn for a thumb. The thumb
     never lands on it: the whole strip is the target, and the strip is the
     scroller underneath. */
  pointer-events: none;
}

.hc-index__notch:focus-visible {
  outline: 2px solid var(--hc-accent-deep);
  outline-offset: -2px;
  border-radius: var(--hc-radius-sm);
  pointer-events: auto;
}

/* What is actually drawn: one hairline, pinned right, growing leftward.
   Length is scaleX off a fixed maximum rather than a width, so no frame of
   the swell touches layout. Every second one rests shorter than its
   neighbour, which is the whole claim the edge makes before anybody has
   touched it: a run of even ticks is a thing you slide along. */
.hc-index__mark {
  position: absolute;
  right: 8px;
  top: 50%;
  width: 30px;
  height: 2px;
  margin-top: -1px;
  border-radius: 1px;
  background: var(--hc-ink);
  transform-origin: 100% 50%;
  transform: scaleX(0.3);
  opacity: 0.2;
  will-change: transform, opacity;
}

/* Paper drawn back over the right of the screen while the column is up. The
   headings behind it are the same words in the same face as the ones in the
   rail, and without this the two sets sit on top of each other and neither
   reads. A gradient with no edge to it, so nothing about it is a panel. */
.hc-index__veil {
  position: fixed;
  inset: 0;
  z-index: 38;                    /* over the page, under every piece of chrome */
  pointer-events: none;
  background: linear-gradient(to left, var(--hc-paper) 30%, rgba(247, 244, 239, 0) 96%);
  opacity: 0;
  will-change: opacity;
}

:root[data-theme="dark"] .hc-index__veil {
  background: linear-gradient(to left, var(--hc-paper) 30%, rgba(26, 25, 24, 0) 96%);
}

/* The page's whole contents, held out to the left of the hand. Every heading
   at its own notch's height; the one under the finger at full size and full
   ink on the card below, the rest small and faint behind it, which is the
   point of them: you can see what else there is without letting go. */
.hc-index__titles {
  position: fixed;
  inset: 0;
  z-index: 47;
  pointer-events: none;
}

.hc-index__title {
  position: absolute;
  right: 46px;                    /* inside the screen, outside the thumb */
  top: 0;
  height: 22px;
  margin-top: -11px;
  max-width: 66%;
  font-family: var(--hc-sans-display);
  font-weight: 800;
  font-size: 17px;
  line-height: 22px;
  letter-spacing: -0.01em;
  color: var(--hc-ink);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transform-origin: 100% 50%;
  opacity: 0;
  will-change: transform, opacity;
}

/* The card behind the one you are on. The tab bar's glass at the tab bar's
   radius, sized to that heading in JavaScript and moved with it, so the
   focused heading stays legible over a photograph and the faint ones behind
   it are free to be faint. */
.hc-index__card {
  position: fixed;
  right: 32px;
  top: 0;
  z-index: 46;
  height: 34px;
  margin-top: -17px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-glass-skin);
  border: var(--hc-hairline) solid var(--hc-glass-edge);
  box-shadow: var(--hc-glass-shadow);
  pointer-events: none;
  opacity: 0;
  transform-origin: 100% 50%;
  will-change: transform, opacity;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .hc-index__card {
    background: var(--hc-glass-skin-lens);
    backdrop-filter: blur(24px) saturate(1.6);
    -webkit-backdrop-filter: blur(24px) saturate(1.6);
  }
}

/* Printing a guide prints the guide. */
@media print {
  .hc-index,
  .hc-index__titles,
  .hc-index__card,
  .hc-index__veil { display: none !important; }
}

/* --------------------------------------------------------------------------
   Announcements, continued

   The banner used to be one card at a time. It is a list now, written from
   Settings -> Admin, and each card is a door into the announcement's own
   page. See the long note in js/screens/home.js about why the one-card rule
   went, and the one above announcementCard() about why the card became a
   button.
   -------------------------------------------------------------------------- */

.hc-banner + .hc-banner { margin-top: var(--hc-space-md); }

/* The picture sits under the words rather than beside them. A photograph
   next to two lines of text on a 375pt screen is a thumbnail, and a
   thumbnail of a serve day is not worth the row it costs.

   It also runs out under the archive corner. The words share their row with
   that 44pt button, so a picture held to the words' width sat 62px from the
   card's right edge and 20px from its left, and read as squeezed to one
   side. The negative margin hands back the corner's column and the gap
   before it, less the difference between the card's two paddings, so the
   picture ends the same distance from each edge. The -10px is the corner's
   own pull into the padding, above. Width goes back to auto because
   .hc-latest__frame pins it at 100%, and 100% would ignore the margin; the
   doubled selector is what outranks that rule, which loads later from
   css/screens.css. */
.hc-banner .hc-banner__frame {
  display: block;
  width: auto;
  margin-top: var(--hc-space-md);
  margin-right: calc(var(--hc-card-pad) - var(--hc-space-base) - var(--hc-space-md) - var(--hc-tap-min) + 10px);
}

/* The video used to be a Watch button on the card, opening YouTube in the
   phone's browser. It plays in the app now, on the announcement's own page,
   so there is no button here any more. See video() in
   js/screens/announcement.js. */

/* --------------------------------------------------------------------------
   The pinned line

   Above the greeting, and the only thing in the app that outranks somebody's
   own name. Not dismissible on purpose: an announcement is news and a person
   is allowed to be done with it, and this is the building being shut.

   role="status" rather than role="alert", so a screen reader reads it when it
   reaches it instead of interrupting whatever is being read.

   STICKY, THE WAY THE STRIP BELOW IS FIXED. It starts where it always sat,
   above the greeting, and when Home scrolls it stops against the bottom of
   the top bar, or of the pinned strip when there is one, and stays there. It
   is sticky and not fixed because it belongs to Home and not to the shell:
   the other tabs never draw it, so it leaves when Home does. Nothing between
   it and .hc-scroll clips overflow, which is what sticky needs to work.
   -------------------------------------------------------------------------- */

.hc-pinned {
  position: sticky;
  z-index: 5;                  /* over Home's cards, under all of the chrome */
  top: calc(var(--hc-header-h) + var(--hc-pin-h, 0px) + env(safe-area-inset-top));
  background: var(--hc-accent-deep);
  border-radius: var(--hc-radius-sm);
  padding: var(--hc-space-md) var(--hc-space-base);
  margin-bottom: var(--hc-space-lg);
}

/* --------------------------------------------------------------------------
   The pinned strip

   The other pinned thing, and the difference is worth keeping straight. The
   block above is a sentence typed into App settings, it lives on Home, and it
   cannot be put away. This is an announcement an admin pinned: it is shell
   chrome, so it carries across every tab, it opens the announcement when it
   is tapped, and the x on the right takes it down on that phone for good.

   Same fill as the block above on purpose. Two pinned things in two different
   colours would read as two systems, and they are one idea at two volumes.

   FIXED, UNDER THE HEADER, and the screens below clear it by adding
   --hc-pin-h into their padding rather than by knowing anything about it.
   js/app.js measures the strip and publishes that number on #app.
   -------------------------------------------------------------------------- */

.hc-pinbar {
  position: fixed;
  z-index: 39;                 /* with the date rail, under the top bar */
  top: calc(var(--hc-header-h) + env(safe-area-inset-top));
  left: 0; right: 0;
  display: flex;
  align-items: stretch;
  gap: var(--hc-space-xs);
  padding: 0 var(--hc-screen-pad);
  background: var(--hc-accent-deep);
}

.hc-pinbar[hidden] { display: none; }

/* The tap. Everything but the x, so a thumb landing anywhere along the strip
   opens the announcement rather than having to find the title. */
.hc-pinbar__open {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--hc-space-sm);
  /* The 44pt minimum is the floor, not the height: a title that wraps to two
     lines makes the strip taller and the screens below follow it. */
  min-height: var(--hc-tap-min);
  padding: var(--hc-space-sm) 0;
  text-align: left;
  color: var(--hc-on-dark);
}

.hc-pinbar__pin {
  flex: 0 0 auto;
  width: 15px; height: 15px;
  stroke: currentColor;
  stroke-width: 1.6;
  opacity: 0.85;
}

.hc-pinbar__title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--hc-sans);
  font-weight: 600;
  /* Not scaled by --hc-text-scale, like the rest of the chrome. This has to
     stay one or two lines at every reading size, or the strip starts eating
     the screen it is announcing something above. */
  font-size: 14px;
  line-height: 1.35;
  color: var(--hc-on-dark);
}

.hc-pinbar__chev {
  flex: 0 0 auto;
  width: 15px; height: 15px;
  stroke: currentColor;
  stroke-width: 1.6;
  opacity: 0.7;
}

.hc-pinbar__open:active { opacity: 0.72; }

/* The way out. Its own button, beside the tap rather than inside it, and a
   full tap target of its own so putting the strip away never opens the
   announcement by accident. */
.hc-pinbar__dismiss {
  flex: 0 0 auto;
  width: var(--hc-tap-min);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: -10px;
  color: var(--hc-on-dark);
}

.hc-pinbar__dismiss svg {
  width: 16px; height: 16px;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  opacity: 0.85;
}

.hc-pinbar__dismiss:active svg { opacity: 0.5; }

/* The strip used to land on Home and flash the card gold, which was how a
   person told which of three cards it had meant. It opens the announcement's
   own page now, so there is nothing left to point at and the gold has gone
   with the scroll that needed it. See openPinned() in js/app.js. */

.hc-pinned__text {
  font-family: var(--hc-sans);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.45;
  /* --hc-on-dark and not --hc-ink. The pair above is the same one the primary
     button uses, and it is the only pair in the palette that inverts together:
     --hc-accent-deep is a dark taupe in light mode and a pale one in dark, so
     text on it has to flip with it. Hardcoding either half is how this ends up
     as pale text on a pale ground for everybody who reads at night. */
  color: var(--hc-on-dark);
}

/* ==========================================================================
   Edit mode
   The half hour in which an admin can fix a sentence where it is written.
   See js/edit-mode.js, which decides what is outlined, and migration 0030,
   which decides what may be written.

   THE OUTLINE HAS TO BE OBVIOUS AND HAS TO NOT MOVE ANYTHING. It is drawn
   with an inset box-shadow rather than a border, so turning edit mode on does
   not reflow every screen it touches by two pixels: text that shifts when the
   switch is flipped is text somebody misreads as already edited. The padding
   is negative-margined back out for the same reason.
   ========================================================================== */

.hc-editable {
  position: relative;
  display: block;
  margin: 0 calc(var(--hc-space-sm) * -1);
  padding: var(--hc-space-sm);
  padding-right: calc(var(--hc-space-base) + var(--hc-space-lg));
  border-radius: var(--hc-radius-sm);
  box-shadow: inset 0 0 0 1.5px var(--hc-accent);
  background: color-mix(in srgb, var(--hc-accent) 8%, transparent);
  cursor: pointer;
  text-align: left;
  transition: box-shadow var(--hc-dur-fast) var(--hc-ease),
              background var(--hc-dur-fast) var(--hc-ease);
}

/* Hover is a desktop nicety, the active state is the one that matters: it is
   what a thumb gets in the moment between the tap and the box appearing. */
.hc-editable:hover,
.hc-editable:active {
  box-shadow: inset 0 0 0 1.5px var(--hc-accent-deep);
  background: color-mix(in srgb, var(--hc-accent) 16%, transparent);
}

.hc-editable:focus-visible {
  outline: 2px solid var(--hc-accent-deep);
  outline-offset: 2px;
}

/* The pencil, top right of whatever is outlined. Absolute so it never joins
   the text flow, and aria-hidden in the markup because the wrapper already
   announces itself as "Edit the line under the Give button". */
.hc-editable__mark {
  position: absolute;
  top: var(--hc-space-xs);
  right: var(--hc-space-xs);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-accent-deep);
  color: var(--hc-on-dark);
}

.hc-editable__icon {
  width: 13px;
  height: 13px;
}

/* A sentence the church has cleared. Only ever drawn while edit mode is on,
   because the whole point of clearing it is that everybody else sees nothing.
   Italic so it cannot be mistaken for the church's own words. */
.hc-editable__empty {
  font-style: italic;
  opacity: 0.75;
}

/* ------------------------------------------------------------- the editor */

.hc-edit {
  display: block;
  margin: 0 calc(var(--hc-space-sm) * -1);
  padding: var(--hc-space-md);
  border-radius: var(--hc-radius-lg);
  box-shadow: inset 0 0 0 1.5px var(--hc-accent-deep);
  background: var(--hc-paper-sunk);
}

.hc-edit__label {
  display: block;
  margin-bottom: var(--hc-space-xs);
  font-family: var(--hc-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--hc-mid);
}

.hc-edit__box {
  width: 100%;
  /* 16px or iOS zooms the whole screen on focus, which on a screen that has
     just repainted under a thumb reads as the app losing its place. */
  font-size: 16px;
}

.hc-edit__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--hc-space-sm);
  margin-top: var(--hc-space-sm);
}

.hc-edit__note {
  margin-top: var(--hc-space-sm);
}

/* ---------------------------------------------------------------- the pill

   Edit mode can end on its own, after thirty minutes, so something has to say
   it is on, and it has to be somewhere no screen redraws. js/edit-mode.js
   appends this to the body once and takes it away again, which is also why it
   carries its own safe-area padding rather than inheriting a screen's. */

.hc-edit-pill {
  position: fixed;
  /* Over the tab bar and the page, under the toast at 60, and sitting a
     toast's height clear of it: this thing is on screen for half an hour and
     the toast that says "Saved" arrives right where it is standing. */
  z-index: 50;
  left: var(--hc-screen-pad);
  right: var(--hc-screen-pad);
  bottom: calc(var(--hc-chrome-space) + env(safe-area-inset-bottom) +
               var(--hc-space-base) + 52px);
  display: flex;
  align-items: center;
  gap: var(--hc-space-sm);
  padding: var(--hc-space-sm) var(--hc-space-sm) var(--hc-space-sm) var(--hc-space-base);
  border-radius: var(--hc-radius-pill);
  background: var(--hc-accent-deep);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}

.hc-edit-pill__text {
  flex: 1 1 auto;
  font-family: var(--hc-sans);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  /* The same inverting pair the pinned strip uses. See the note there. */
  color: var(--hc-on-dark);
}

.hc-edit-pill__done {
  flex: 0 0 auto;
  min-height: 34px;
  padding: 0 var(--hc-space-base);
  border: none;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-on-dark);
  color: var(--hc-accent-deep);
  font-family: var(--hc-sans);
  font-size: 13px;
  font-weight: 600;
}

/* Edit mode's second shape: the element drawn exactly as it always is, with a
   pencil beside it. Used where the text is already inside a tap target, which
   a button's label is.

   The wrapper is a block with the chip positioned off it rather than a flex
   row, so it changes nothing about how the element inside lays out: every
   place this is used sits in a plain block container, and a button that is
   inline-flex keeps its own width inside it. */
.hc-edit-inline {
  position: relative;
  display: block;
}

.hc-edit-chip {
  position: absolute;
  top: -6px;
  right: -6px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  border-radius: var(--hc-radius-pill);
  background: var(--hc-accent-deep);
  color: var(--hc-on-dark);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}

.hc-edit-chip__icon {
  width: 14px;
  height: 14px;
}

.hc-edit-chip:focus-visible {
  outline: 2px solid var(--hc-accent-deep);
  outline-offset: 2px;
}
