/* Twote — cast / web feed page (T2.5). Design width 1280px (cast/TV target);
   see docs/design/handoff-t2/README.md §9 for the contract this recreates.
   Mobile-first per web-structure: base styles are the small-viewport styles,
   media queries only ADD width back for the cast/TV target. */

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--fontUI);
  overflow-x: hidden; /* belt-and-braces; layout below must never need this */
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--text);
  padding: 12px 20px;
  border-radius: 8px;
  z-index: 100;
  border: 1px solid var(--outline);
}
.skip-link:focus {
  left: 12px;
  top: 12px;
}

:focus-visible {
  outline: 2px solid var(--swiftGreenBright);
  outline-offset: 2px;
}

a {
  color: var(--link);
  text-decoration: none;
}
a:hover,
a:focus-visible {
  text-decoration: underline;
}

.page {
  max-width: 1280px;
  margin: 0 auto;
  padding: 20px 16px 28px;
}

/* ---------- Header ---------- */
.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 24px;
  padding-bottom: 20px;
}

.brand {
  display: inline-flex;
  align-items: center;
  flex: none;
}
/* The logo is the canonical Claude Design lockup exported as an image (design/ds/Twote
   Handoff-selection2.png), whose background is exactly --canopy (#0F1814) = the page bg, so it sits
   seamlessly. Using the exact asset (not a CSS/font recreation) is deliberate: the O and the
   "talk is cheep" proportion are locked to the design, not re-derived. The tagline is part of the image. */
.brand-logo {
  display: block;
  height: 52px;
  width: auto;
}

.header-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
  color: var(--fog);
  font-size: 14px;
  flex: 1 1 240px;
  min-width: 0;
}
.header-meta .dot-sep::before {
  content: '·';
  margin: 0 6px;
  color: var(--moss);
}

.header-right {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-left: auto;
  flex-wrap: wrap;
}

.tally {
  color: var(--fog);
  font-size: 14px;
  white-space: nowrap;
}

.live-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--swiftCobalt);
  color: var(--cobaltMist);
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--swiftGreenBright);
  position: relative;
  flex: none;
}
.live-dot::after {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1.5px solid var(--swiftGreenBright);
  animation: live-pulse 2.4s ease-out infinite;
}
@keyframes live-pulse {
  0% { opacity: 0.9; transform: scale(0.4); }
  100% { opacity: 0; transform: scale(1.6); }
}
@media (prefers-reduced-motion: reduce) {
  .live-dot::after { animation: none; opacity: 0.35; }
}
.live-pill a {
  color: inherit;
}
/* Offline state: the pill only pulses green when the sensor is genuinely live (fresh heartbeat). */
.live-pill.is-offline {
  border-color: var(--leaf);
  color: var(--fog);
}
.live-pill.is-offline .live-dot {
  background: var(--moss);
}
.live-pill.is-offline .live-dot::after {
  animation: none;
  opacity: 0;
}

/* Sensor status (device identity + battery/charging), driven by the live biosys heartbeat.
   Inline row to sit level with the tally + pill (everything in the header-right is one row). */
.sensor-status {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  line-height: 1.2;
  white-space: nowrap;
}
.sensor-device {
  font-size: 13px;
  color: var(--fog);
  white-space: nowrap;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sensor-batt {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 12px;
  font-weight: 600;
  color: var(--swiftGreenBright);
}
.sensor-status.is-offline .sensor-device,
.sensor-status.is-offline .sensor-batt {
  color: var(--moss);
}

/* ---------- Layout: three columns ---------- */
main {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

@media (min-width: 768px) {
  main {
    grid-template-columns: 1fr 1fr;
  }
  .col-session {
    grid-column: 1 / -1;
  }
}

@media (min-width: 1024px) {
  /* DESKTOP cast surface (deliberately NOT the mobile-first stacking above). The interface is a
     compact, bounded rectangle, header on top, three equal-height columns below, sized to ONE screen
     but never stretched to fill a tall monitor with empty gaps. All three columns share one bounded
     height, so the card bottom aligns with the Recent + Featured bottoms and the whole thing reads as
     a single clean rectangle from the logo (top-left) to the card (bottom-right). */
  .page {
    display: flex;
    flex-direction: column;
    /* Top-aligned: the interface is a rectangle anchored at the top-left (logo) down to the card,
       not floated in the middle of a tall monitor. */
  }
  .site-header { flex: none; }
  .brand-logo { height: 78px; } /* desktop: 50% larger than the 52px base */
  main {
    flex: 0 1 auto;
    min-height: 0;
    height: min(calc(100dvh - 158px), 660px);
    align-items: stretch;
    grid-template-columns: minmax(320px, 1.6fr) minmax(280px, 1fr) minmax(232px, 0.66fr);
  }
  .col-featured,
  .col-recent,
  .col-session {
    grid-column: auto;
    min-height: 0;
  }
  /* Featured art fills the column (cover-cropped, mascot centred) rather than forcing a square that
     leaves a gap. */
  #featured { height: 100%; }
  .featured-art { flex: 1; min-height: 0; width: 100%; aspect-ratio: auto; }
  /* Recent list scrolls internally past what fits; This-session list scrolls and fades into the card. */
  #recent-list { flex: 1; min-height: 0; overflow-y: auto; padding-right: 6px; }
  #session-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding-right: 6px;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
  }
}

/* Thin scrollbars styled to the page furniture (leaf on transparent), for the internal-scroll lists. */
#recent-list,
#session-list {
  scrollbar-width: thin;
  scrollbar-color: var(--leaf) transparent;
}
#recent-list::-webkit-scrollbar,
#session-list::-webkit-scrollbar { width: 6px; }
#recent-list::-webkit-scrollbar-thumb,
#session-list::-webkit-scrollbar-thumb { background: var(--leaf); border-radius: 3px; }
#recent-list::-webkit-scrollbar-thumb:hover,
#session-list::-webkit-scrollbar-thumb:hover { background: var(--moss); }
#recent-list::-webkit-scrollbar-track,
#session-list::-webkit-scrollbar-track { background: transparent; }

.column-heading {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 12px;
  font-weight: 600;
  color: var(--moss);
  margin: 0 0 14px;
}

.empty-note {
  color: var(--fog);
  font-size: 14px;
  margin: 0;
}

/* Scientific (Latin) names are set in Fraunces italic, per the handoff. */
.species-name {
  font-family: var(--fontSpecies);
  font-style: italic;
}

/* ---------- (a) Featured latest detection ---------- */
.col-featured {
  display: flex;
  flex-direction: column;
}
#featured {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.featured-art {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 16px;
  overflow: hidden;
  background-color: var(--surface);
  background-size: cover;
  background-position: center;
  border: 1px solid var(--outline);
}
.featured-lockup {
  padding-top: 16px;
}
.featured-name {
  font-family: var(--fontUI);
  font-weight: 700;
  font-size: 32px;
  line-height: 1.1;
  margin: 0 0 6px;
  color: var(--text);
}
@media (min-width: 1024px) {
  .featured-name { font-size: 46px; }
}
.featured-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
}
.featured-sci {
  font-size: 22px;
  margin: 0;
}
.latest-label {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 12px;
  color: var(--fog);
  font-weight: 600;
}

/* ---------- Tier chips (shared) ---------- */
.tier-chip {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  font-weight: 600;
  font-size: 12px;
  padding: 4px 12px;
  white-space: nowrap;
}
.tier-chip--compact {
  padding: 3px 10px;
}

/* ---------- (b) Recent detections rail ---------- */
.col-recent {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
#recent-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.recent-card {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--outline);
  border-radius: 12px;
  padding: 8px 12px;
}
.recent-art {
  width: 56px;
  height: 56px;
  border-radius: 10px;
  flex: none;
  background-color: var(--bg);
  background-size: cover;
  background-position: center;
}
@media (min-width: 640px) {
  .recent-art { width: 80px; height: 80px; }
}
.recent-body {
  flex: 1;
  min-width: 0;
}
.recent-name {
  font-weight: 600;
  font-size: 15px;
  margin: 0 0 2px;
  color: var(--text);
  overflow-wrap: break-word;
}
.recent-sci {
  font-size: 12px;
  margin: 0;
}
.recent-ago {
  color: var(--fog);
  font-size: 13px;
  flex: none;
  white-space: nowrap;
}

/* Portrait mascot share card, pinned to the bottom of the This-session column. Ported from the
   design spec docs/design/qr-mockups/compose_mascot_card.py: full-bleed mascot, a footer scrim,
   green pulse echoes anchored to the wordmark O (drawn behind the QR), the frosted styled QR
   (window.TwoteQR.renderStyledSvg, same as the app), and the twOte.org/<code> wordmark. Rounded
   corners per the hero image. Sizes are in cqw (fractions of the card width) to mirror the spec's
   fractions-of-W layout at any column width. */
.mascot-card {
  margin-top: auto;
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;   /* shorter than the old 9:16 at the same (column-driven) width */
  border-radius: 20px;
  overflow: hidden;
  background: var(--canopy);
  flex: none;
  container-type: inline-size;
  text-decoration: none;
}
.mascot-card:hover,
.mascot-card:focus-visible { text-decoration: none; }
.mascot-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%; /* permanent crop: bias slightly up so the bird's head is never clipped */
}
.mascot-card-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    rgba(15, 24, 20, 0) 52%,
    rgba(15, 24, 20, 0.55) 72%,
    rgba(15, 24, 20, 0.95) 100%);
}
.mascot-card-echoes {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.mascot-card-qr {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 15cqw;
  /* 51.2cqw = 0.512 of card width, matching the app card's qrFrac (scaled down from 0.64 so a small
     mascot is not hidden behind the QR). Only the live-composite fallback uses this; baked hides it. */
  width: 51.2cqw;
  height: 51.2cqw;
}
.mascot-card-qr svg { display: block; width: 100%; height: 100%; }
.mascot-card-wordmark {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 5cqw;
  text-align: center;
  color: #fff;
  font-family: var(--fontUI);
  font-weight: 600;
  font-size: 6cqw;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.mascot-card-wordmark .o { color: var(--swiftGreenBright); }

/* Baked mode (group E, Path B): the app-generated card PNG IS the whole card (mascot + QR + wordmark
   + echoes flattened at the canonical 3:4), so it drops straight into the existing responsive 3:4
   .mascot-card frame (no aspect override, no width cap). We only reset the crop bias to centre (the
   baked card is already composed, not a mascot photo to re-crop) and hide the live DOM overlays that
   would otherwise double up on the baked-in ones. */
.mascot-card.baked .mascot-card-img {
  object-fit: cover;
  object-position: center;
}
.mascot-card.baked .mascot-card-scrim,
.mascot-card.baked .mascot-card-echoes,
.mascot-card.baked .mascot-card-qr,
.mascot-card.baked .mascot-card-wordmark { display: none; }

/* ---------- (c) This session tally ---------- */
.col-session {
  display: flex;
  flex-direction: column;
}
#session-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.session-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.session-name {
  font-weight: 500;
  font-size: 17px;
  color: var(--text);
}
.count-pill {
  background: var(--surface);
  border: 1px solid var(--outline);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fog);
  flex: none;
}

/* ---------- Detection-arrival animation (A2) ---------- */
@media (prefers-reduced-motion: no-preference) {
  .recent-card,
  #featured {
    animation: feed-arrive 180ms ease-out;
  }
}
@keyframes feed-arrive {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
