/* Site footer — light plaster.
 *
 * Self-contained on purpose. This one stylesheet is loaded by fifteen pages
 * that do not agree on a token set (the app pages, the Journal and the
 * marketing pages each define their own :root), so the footer states its own
 * colours as literals rather than inheriting variables that may not exist.
 * Everything is scoped under .tsf so it cannot leak into a page's own styles.
 *
 * Paired with shared/site-footer.html, which is the single copy of the markup;
 * scripts/sync-footer.mjs injects it and `npm test` fails if a page drifts.
 */

.tsf {
  position: relative;
  isolation: isolate;
  contain: paint;
  background: #F5F5F5;
  overflow: hidden;
  padding: 0 20px 20px;
  font-family: 'Albert Sans', 'PingFang SC', system-ui, -apple-system, sans-serif;
  color: #262626;
}

/* The photograph and the veil that lifts it to near-white. Two layers rather
   than one pre-lightened image, so the same asset can be reused at other
   opacities without re-exporting it. */
.tsf-plaster {
  position: absolute;
  inset: 0;
  background-image: url('/assets/footer/footer-plaster.jpg');
  background-size: cover;
  background-position: center 68%;
}
.tsf-veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, .34) 0%,
    rgba(255, 255, 255, .10) 38%,
    rgba(245, 245, 245, .46) 100%);
}

/* Breathing room above the plate, so the photograph reads as a room the card
   is sitting in rather than as a texture behind it. */
.tsf-air { position: relative; height: clamp(84px, 10vw, 168px); }

.tsf-plate {
  position: relative;
  max-width: 1520px;
  margin: 0 auto;
  border-radius: 24px;
  overflow: hidden;
  background: rgba(255, 255, 255, .6);
  -webkit-backdrop-filter: blur(24px) saturate(1.08);
  backdrop-filter: blur(24px) saturate(1.08);
  border: 1px solid rgba(255, 255, 255, .92);
  box-shadow: inset 0 1px 0 #fff, 0 44px 90px -58px rgba(38, 38, 38, .42);
}
/* Without backdrop-filter the plate would be a flat 60% white wash over a
   photograph, which is unreadable. Fall back to near-opaque instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tsf-plate { background: rgba(255, 255, 255, .93); }
}

.tsf-cols {
  display: flex;
  flex-wrap: wrap;
  gap: 44px 40px;
  padding: clamp(30px, 3.2vw, 46px) clamp(26px, 3vw, 44px);
}

/* — brand + the pass — */
.tsf-brand { flex: 1 1 220px; max-width: 300px; display: flex; flex-direction: column; gap: 16px; }
.tsf-brand-mark { display: inline-flex; align-items: center; text-decoration: none; }
.tsf-brand-mark img { height: 26px; width: auto; display: block; }
.tsf-pass { display: flex; flex-direction: column; gap: 9px; max-width: 250px; }
.tsf-pass-kicker {
  font-weight: 700; font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: #3F5334;
}
.tsf-sub {
  display: flex; align-items: stretch; border-radius: 7px; overflow: hidden;
  background: #fff; border: 1.5px solid #D9D9D9;
}
.tsf-sub-input {
  flex: 1; min-width: 0; font-family: inherit; font-size: 12.5px; color: #262626;
  background: transparent; border: 0; padding: 9px 12px; outline: none;
}
.tsf-sub-input:focus { box-shadow: inset 0 0 0 1.5px #566F46; border-radius: 5px; }
.tsf-sub-go {
  flex: none; width: 38px; border: 0; background: #71B357; color: #1C1C1A;
  font-size: 13px; font-weight: 800; cursor: pointer; font-family: inherit;
}
.tsf-sub-go:hover { filter: brightness(.92); }
.tsf-sub-go:active { transform: translateY(1px); }
.tsf-sub-go[disabled] { opacity: .6; cursor: default; }
/* The honeypot is off-screen rather than display:none — a bot that reads
   computed styles skips hidden fields, but follows the ones it can "see". */
.tsf-hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}
.tsf-sub-note { margin: 0; font-size: 11px; line-height: 1.45; color: #595959; min-height: 0; }
.tsf-start {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 7px;
  padding: 2px 0 3px; border-bottom: 1px solid #D9D9D9;
  font-weight: 600; font-size: 12px; color: #262626; text-decoration: none;
  transition: border-color .18s cubic-bezier(.4, 0, .2, 1);
}
.tsf-start:hover { border-color: #262626; color: #262626; }
.tsf-start span { font-size: 10.5px; color: #566F46; }

/* — link columns — */
.tsf-nav {
  flex: 1 1 168px; min-width: 150px; max-width: 220px;
  display: flex; flex-direction: column; gap: 11px;
}
.tsf-nav-h {
  font-weight: 700; font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: #595959; padding-bottom: 5px;
}
.tsf-nav a { font-size: 14.5px; font-weight: 500; color: #434343; text-decoration: none; }
.tsf-nav a:hover { color: #262626; }

/* — "featured & listed" marquee — */
.tsf-listed { border-top: 1px solid rgba(38, 38, 38, .12); overflow: hidden; }
.tsf-listed-row { display: flex; align-items: center; min-height: 70px; }
.tsf-listed-h {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 0 clamp(20px, 2.4vw, 34px);
  font-weight: 700; font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase;
  color: #595959; white-space: nowrap;
  border-right: 1px solid rgba(38, 38, 38, .12); align-self: stretch;
}
.tsf-listed-dot { width: 6px; height: 6px; border-radius: 50%; background: #71B357; }
.tsf-listed-view {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.tsf-listed-track {
  display: flex; width: max-content; min-width: 100%;
  animation: tsf-travel 46s linear infinite;
}
.tsf-listed-view:hover .tsf-listed-track { animation-play-state: paused; }
.tsf-listed-set { display: flex; align-items: center; flex: 0 0 auto; min-width: max-content; }
/* line-height:0 collapses the box to the image, so the pointer target is stated
   outright rather than left to the padding to imply (WCAG 2.5.8). */
.tsf-listed-set a {
  display: flex; align-items: center; justify-content: center;
  min-width: 162px; min-height: 24px;
  padding: 16px 26px; line-height: 0; text-decoration: none;
}
.tsf-listed-set img {
  display: block; width: auto; height: var(--tsf-logo-h, 24px);
  max-width: 128px; object-fit: contain;
  filter: grayscale(1); opacity: .5;
  transition: opacity .2s cubic-bezier(.4, 0, .2, 1), filter .2s cubic-bezier(.4, 0, .2, 1);
}
.tsf-listed-set a:hover img { opacity: 1; filter: none; }
/* One badge is authored white-on-transparent. On a light plate it has to be
   inverted back to dark, not dimmed, or it disappears entirely. */
.tsf-listed-set img.tsf-reversed { filter: grayscale(1) invert(1) brightness(.35); opacity: .55; }
.tsf-listed-set a:hover img.tsf-reversed { filter: grayscale(1) invert(1) brightness(.35); opacity: .85; }

/* The track is two identical sets; travelling exactly -50% lands the second set
   where the first began, so the loop has no seam. */
@keyframes tsf-travel {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* — bottom bar — */
.tsf-base {
  border-top: 1px solid rgba(38, 38, 38, .12);
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px 28px; flex-wrap: wrap;
  padding: 18px clamp(26px, 3vw, 44px);
}
/* The design draws this line at #8C8C8C, which is ~3.5:1 on the plate — under
   AA for 10px text. Darkened to the nearest tone that passes; it still reads as
   the quietest thing in the footer. */
.tsf-copy {
  font-weight: 600; font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: #6E6E6E;
}
.tsf-social { display: flex; align-items: center; gap: 18px; }
/* The glyph is 17px, but the target is not: WCAG 2.5.8 wants at least 24x24,
   and these are the smallest links on the page. */
.tsf-social a {
  display: grid; place-items: center; color: #595959; text-decoration: none;
  min-width: 24px; min-height: 24px;
}
.tsf-social a:hover { color: #262626; }
.tsf-social svg { width: 17px; height: 17px; display: block; fill: currentColor; }

/* — phones —
   The desktop layout wraps its five flex columns one per row on a narrow
   screen, which turns the footer into a 1000px tower: four full-width lists of
   three or four short links each, every one of them wasting most of its line.
   A phone footer is not a scaled-down desktop footer, so this is a different
   arrangement rather than the same one with smaller gaps. */
@media (max-width: 720px) {
  .tsf { padding: 0 12px 12px; }
  .tsf-air { height: clamp(44px, 9vw, 84px); }
  .tsf-plate { border-radius: 18px; }

  /* Two columns of two. The longest label here is "How it works", so nothing
     wraps at half of a 390px screen, and the block halves in height. */
  .tsf-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 26px 18px;
    padding: 26px 20px 28px;
  }
  .tsf-brand { grid-column: 1 / -1; max-width: none; }
  .tsf-pass { max-width: none; }
  .tsf-nav { max-width: none; min-width: 0; gap: 9px; }
  .tsf-nav-h { padding-bottom: 1px; }
  /* 14.5px → 14px keeps four rows of links under a thumb without dropping
     below the size the rest of the site sets body text at. */
  .tsf-nav a { font-size: 14px; }

  /* The label is a fifth of the width on a phone and the logos say the same
     thing, so the strip keeps the space and loses the caption. */
  .tsf-listed-h { display: none; }
  .tsf-listed-row { min-height: 60px; }
  .tsf-listed-set a { min-width: 128px; padding: 14px 18px; }

  .tsf-base { padding: 14px 20px; gap: 10px 16px; }
  .tsf-copy { font-size: 9.5px; letter-spacing: .12em; }
}

/* Very narrow phones: the second column of links starts to crowd, so the two
   halves become one again — but by then the brand block and the strip have
   already given up their extra height, so the total stays well under the
   original. */
@media (max-width: 359px) {
  .tsf-cols { grid-template-columns: 1fr; gap: 22px; }
}
@media (prefers-reduced-motion: reduce) {
  .tsf-listed-track { animation: none; }
  .tsf-listed-view { overflow-x: auto; }
}
