/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/* Music League self-hosted typefaces */
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/BricolageGrotesque-Regular-608ef152.ttf") format("truetype");
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/BricolageGrotesque-SemiBold-605513ab.ttf") format("truetype");
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/BricolageGrotesque-Bold-f1cf33b7.ttf") format("truetype");
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/assets/BricolageGrotesque-ExtraBold-970c4da6.ttf") format("truetype");
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/Figtree-Regular-bcf63794.ttf") format("truetype");
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/Figtree-Medium-0047481d.ttf") format("truetype");
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/Figtree-SemiBold-1a089dd7.ttf") format("truetype");
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/Figtree-Bold-7bfaea42.ttf") format("truetype");
}
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/assets/Figtree-ExtraBold-4c28ec64.ttf") format("truetype");
}

/* Music League Custom Styles */
body {
  font-family: 'Figtree', sans-serif;
  font-size: 0.8em;
  font-weight: 400;
}

/* Interactive controls show the pointer cursor (ML-378). Tailwind v4's Preflight
   dropped the v3 default that put `cursor: pointer` on buttons, so native buttons,
   submits, role=button elements, summaries, and clickable labels reverted to the
   text/arrow cursor. Restore it here in one place instead of sprinkling
   `cursor-pointer` utilities across views. Disabled controls are excluded so they
   keep the default arrow cursor. */
@layer base {
  button:not(:disabled),
  [type="button"]:not(:disabled),
  [type="submit"]:not(:disabled),
  [type="reset"]:not(:disabled),
  [role="button"]:not([aria-disabled="true"]),
  label[for]:has(+ input),
  summary {
    cursor: pointer;
  }
}

/* Screen reader only */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

input[type="search"].is-loading::-webkit-search-cancel-button {
  display: none;
}

/* Hide native number input spinners */
input[type="number"].no-spinner::-webkit-outer-spin-button,
input[type="number"].no-spinner::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"].no-spinner {
  -moz-appearance: textfield;
}

/* The 160x600 side rails are gone, and with them the 180px of padding that
   `.app-shell` reserved on each side at >= 992px (issue #491, ML-718) to keep
   them off the centred max-w-7xl content. Removing that reservation gives the
   page back 360px at every desktop width, which is what lets the league ad
   sidebar appear at xl (1280px) instead of needing ~1536px — the difference
   between reaching every 1366/1440 laptop and reaching almost none of them.
   The `body.has-nitro-ads` class those rules keyed off is no longer emitted. */

/* Chat composer safe-area clearance (ML-383). The composer is `sticky bottom-0`,
   so on an iPhone with a home indicator its input/Send button can sit under the
   safe-area inset (behind the indicator / the collapsing Safari toolbar). Reserve
   the home-indicator height at the bottom of the composer's own padding so the
   controls are always fully tappable above it. Zero on devices without an inset. */
.chat-composer {
  padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
}

/* No ad format overlays page content any more. The bottom anchor was the last
   one, and the footer / chat-composer / ballot-bar / Beacon offsets that existed
   to push content out from under it (ML-80, ML-199, ML-383, ML-574, ML-218) went
   with it. Every ad unit now sits in the normal flow. If a future placement needs
   a rule like these to stop it covering something, that is the signal it is the
   wrong format, not that it needs an offset. */

/* Help Scout launcher clearance on mobile (ML-218). The Beacon's own launcher
   (`.BeaconFabButtonFrame`) renders at the vendor default — bottom-right,
   ~20px inset, no offset — which is fine on desktop/tablet but on phones
   (<=575px) it can sit on top of
   whatever ends up at the bottom of the current viewport: the league tab row
   ("Rounds"/"Standings", ~48px tall — `py-3.5` (14px top+bottom) plus a
   text-sm line box) on short viewports or below a tall hero, or a primary
   CTA like "Join League" (~40px tall — `py-2` plus text-sm) that lands near
   the fold. 3.5rem (56px) lifts the launcher's bottom edge clear of a
   ~40-48px element sitting flush with the viewport bottom, with a little
   headroom to spare — the same reasoning the chat-page rule below applies to
   a taller target, just a smaller number since there's no composer to clear
   here.
   Both rules here are less specific than the chat-page rule below (which
   matches `body:has(.chat-page) .BeaconFabButtonFrame`, one compound
   selector level higher) — so on the chat tab the 5.5rem composer clearance
   always wins, exactly as before ML-218. */
@media (max-width: 575px) {
  .BeaconFabButtonFrame {
    bottom: 3.5rem !important;
  }
}

/* Chat-page-only layout overrides (ML-199). Scoped via :has(.chat-page) — the
   marker lives inside the swappable `league_tab` turbo-frame, so these apply
   only while the chat tab is in the DOM and drop the moment another tab swaps
   in; they never leak to other league tabs. Replaces an inline <style> block. */
body:has(.chat-page) footer {
  /* Footer sits flush under the composer (no sticky-footer gap on chat). */
  margin-top: 0;
}

/* ML-624: on mobile web, pin the chat composer to the viewport bottom (was
   `sticky bottom-0` in the markup, which un-pins at the end of the short message
   list and lets the site footer show beneath it). Mirrors the ML-574 ballot-bar
   pin. Desktop keeps `sticky` (this override is scoped to the same <=575px
   breakpoint the Beacon clearance uses), so AC6 (desktop unchanged)
   holds. The message list is already sized against 100svh minus the
   --chat-list-chrome token (ML-383), which reserves the composer's row, so the
   last message still clears the pinned composer (AC3). Keyboard clearance stays
   with the existing keyboard-inset controller (ML-616) — `transform` works the
   same on a fixed element as on a sticky one, so no JS change (AC2). */
@media (max-width: 575px) {
  .chat-page .chat-composer {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
  }
  /* No footer beneath the pinned composer on the chat tab (AC1). */
  body:has(.chat-page) footer {
    display: none;
  }
}

/* showModal() focuses the dialog itself; the UA ring reads as a stray border. */
dialog:focus {
  outline: none;
}

/* Message-list height (ML-383). Replaces the brittle `h-[calc(100dvh-10rem)]`
   arbitrary value on the list container. Two problems with the old value on
   iPhone Safari: (1) `100dvh` shrinks/grows as the Safari toolbar collapses, so
   the hardcoded `-10rem` header allowance drifted — either a dead gap opened
   below the last message or the sticky composer was pushed below the fold; and
   (2) it ignored the top safe-area inset on notched devices.
   Fix: size against `svh` (the *small* viewport — toolbar expanded — which is
   the stable floor that keeps the composer always on-screen even mid-collapse)
   and subtract the real chrome above the list via a single custom property plus
   the top inset. `--chat-list-chrome` is the combined navbar + league hero +
   tab row + page padding height; keeping it in one named token (vs. a bare
   magic number buried in a utility class) makes the one value that must track
   layout legible and adjustable in one place. min-height keeps a usable list on
   very short viewports; the composer stays reachable by scrolling. */
body:has(.chat-page) {
  --chat-list-chrome: 10rem;
}
.chat-page [data-chat-target="messages"] {
  height: calc(100svh - var(--chat-list-chrome) - env(safe-area-inset-top));
  min-height: 18rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* Centered on a phone: anchoring cannot see the native composer overlaying the
   webview, so a bottom-row menu opens straight into it. margin/padding/radius
   undo the UA popover defaults. */
.chat-actions-menu {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(20rem, 100vw - 2rem);
  height: max-content;
  max-height: 60dvh;
  padding: 0.25rem 0;
  overflow-y: auto;
  border-radius: 1rem;
}
/* ML-1319: the verified checkmark's note on the profile h1. Without anchor
   positioning it stays where the UA puts a popover (centered); with it, it
   opens under the checkmark that invoked it (its implicit anchor). */
.verified-note {
  max-width: min(16rem, 100vw - 2rem);
}
@supports (position-area: block-end) {
  .verified-note {
    inset: auto;
    margin: 0.5rem 0 0;
    position-area: block-end;
    position-try-fallbacks: flip-block;
  }
}
/* Editing takes the panel over: reactions and the action list step aside. */
.chat-actions-menu:has(.chat-actions-edit form) > *:not(.chat-actions-edit) {
  display: none;
}
.chat-replies:not(:has([data-chat-reply])) {
  display: none;
}
/* Thread line from the parent's avatar, curving into each reply's avatar. */
.chat-replies {
  --chat-thread-rail: 2px solid var(--color-gray-200);
  --chat-thread-line: var(--chat-thread-line-start, -15px);
}
.chat-replies summary,
.chat-replies [data-chat-reply] {
  position: relative;
}
.chat-replies[open] summary::before,
.chat-replies [data-chat-reply]:not(:last-child)::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--chat-thread-line);
  border-inline-start: var(--chat-thread-rail);
}
.chat-replies [data-chat-reply]::before {
  content: "";
  position: absolute;
  top: 0;
  height: 23px;
  inset-inline-start: var(--chat-thread-line);
  width: calc(var(--chat-thread-elbow-end, 14px) - var(--chat-thread-line));
  border-inline-start: var(--chat-thread-rail);
  border-block-end: var(--chat-thread-rail);
  border-end-start-radius: 12px;
}
/* Selected row dims with the rest; the panel carries its own copy of it. */
#messages:has(.chat-actions-menu:popover-open) > * {
  opacity: 0.25;
  transition: opacity 120ms ease-out;
}
@media (min-width: 640px) {
  @supports (position-area: block-end span-inline-start) {
    .chat-actions-menu {
      inset: auto;
      margin: 0.25rem 0;
      width: max-content;
      max-width: calc(100vw - 1rem);
      position-area: block-end span-inline-start;
      position-try-fallbacks: flip-block;
      max-height: 16rem;
      border-radius: 0.75rem;
    }
    /* The editor needs more room than the action list it replaces. */
    .chat-actions-menu:has(.chat-actions-edit form) {
      width: 22rem;
      max-height: 24rem;
    }
    /* Anchored beside its row, so the dim and the copy are redundant. */
    #messages:has(.chat-actions-menu:popover-open) > * {
      opacity: 1;
    }
    .chat-actions-preview {
      display: none;
    }
  }
}
/* Members tab and Year-in-Review menus: centered on a phone, anchored above 640px. */
.member-actions-menu,
.yir-share-menu {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(16rem, 100vw - 2rem);
  height: max-content;
  padding: 0.375rem 0;
  border-radius: 1rem;
}
.member-actions-menu::backdrop,
.yir-share-menu::backdrop {
  background: rgb(0 0 0 / 0.2);
}
@media (min-width: 640px) {
  @supports (position-area: block-end span-inline-start) {
    .member-actions-menu,
    .yir-share-menu {
      inset: auto;
      margin: 0.25rem 0;
      width: 13rem;
      position-area: block-end span-inline-start;
      position-try-fallbacks: flip-block;
      border-radius: 0.75rem;
    }
    .member-actions-menu::backdrop,
    .yir-share-menu::backdrop {
      background: transparent;
    }
  }
}
body:has(.chat-page) .BeaconFabButtonFrame {
  /* Lift the Help Scout launcher above the Send button (ML-199). More
     specific than — and declared after — the mobile clearance rule above
     (ML-218), so this always wins on the chat tab, including on the rare
     page that also has the Nitro anchor ad visible (the composer already
     reserves its own 4rem for the anchor via the rule above; stacking that
     further with the launcher is out of scope for ML-218). */
  bottom: 5.5rem !important;
}

/* Hide horizontal scrollbar while keeping scroll (tab rows on mobile) */
.no-scrollbar {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.no-scrollbar::-webkit-scrollbar {
  display: none;
}

/* Center when content fits, fall back to scrollable start when it overflows
   (plain justify-center clips and strands the leading tab on a scroll row) */
.justify-safe-center {
  justify-content: safe center;
}

input[type="datetime-local"] {
  max-width: 100%;
  min-width: 0;
}

/* ML-794 — Safari (WebKit) date/time picker alignment.
   Both mobile and desktop Safari match `@supports (-webkit-touch-callout: none)`.
   When we reset `-webkit-appearance` (needed so Safari honors the input's width
   and our own picker glyph), Safari also drops the native control's intrinsic
   vertical sizing of the internal `::-webkit-datetime-edit` segment container.
   Left alone, the segments then render against the input's own line box and the
   `py-2` padding on these fields clips/overlaps them (the reported bug). The
   fixes below are scoped to `datetime-local` and are WebKit-only, so Chrome and
   Firefox — which never match this `@supports` gate — are untouched. */
@supports (-webkit-touch-callout: none) {
  input[type="datetime-local"] {
    -webkit-appearance: none;
    appearance: none;
    padding-right: 2rem;
    /* Let the segment content size the field instead of clipping it. A fixed
       `height` would re-introduce clipping; `min-height` reserves room for the
       segments (1lh) plus the fields' `py-2` (0.5rem top+bottom) and their 1px
       border, and shrinks gracefully if a smaller font is applied. */
    min-height: calc(1lh + 1rem + 2px);
    /* Empty value renders no segments, collapsing the field to its padding (ML-792). */
    min-width: min(100%, 15rem);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%236b7280' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'><path d='M5.5 8L10 12.5L14.5 8'/></svg>");
    background-repeat: no-repeat;
    background-position: right 0.625rem center;
    background-size: 1rem 1rem;
  }
  /* Keep the segment container on the input's baseline with no extra box of its
     own, so the digits sit vertically centered rather than pinned to the top
     edge (Safari's default once appearance is reset). */
  input[type="datetime-local"]::-webkit-datetime-edit {
    padding: 0;
    line-height: normal;
  }
  input[type="datetime-local"]::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }
}

/* Non-Safari (Chrome/Firefox) keep the browser's native picker glyph, styled to
   match. This rule stays outside the `@supports` gate on purpose. */
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%236b7280' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'><path d='M5.5 8L10 12.5L14.5 8'/></svg>");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 1rem 1rem;
  width: 1rem;
  height: 1rem;
  opacity: 1;
  cursor: pointer;
}
/* Desktop + mobile Safari: our own glyph (drawn via background-image above) is
   the visible affordance, so make the native indicator an invisible click
   target on the right instead of a second glyph. Constrain it to a fixed 2rem
   width and center it vertically (rather than stretching `height: 100%`, which
   overlapped the segment text) so it never sits on top of the digits. */
@supports (-webkit-touch-callout: none) {
  input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    opacity: 0;
    width: 2rem;
    height: 1.25rem;
    margin-right: -0.625rem;
    align-self: center;
  }
}

/* Dotted border separator */
.border-dotted-bottom {
  border-bottom: 5px dotted var(--color-ml-border);
}

/* Card styles */
.guide-card {
  height: 100%;
  align-items: center;
}

/* Link hover states – exclude elements that set their own text color (e.g. buttons) */
a:not([class*="text-"]) {
  color: var(--color-brand-ink);
}

a:not([class*="text-"]):hover {
  color: var(--color-brand-ink-alt);
}

footer a {
  color: inherit;
  display: block;
  text-decoration: none;
}

footer a:hover {
  color: var(--color-brand-ink);
  text-decoration: underline;
}

/* Custom checkbox styles for gray background auth forms */
.checkbox-custom {
  appearance: none;
  width: 1rem;
  height: 1rem;
  border: 1px solid var(--color-gray-300);
  border-radius: 0.125rem;
  background-color: var(--color-surface);
  cursor: pointer;
}

.checkbox-custom:checked {
  background-color: var(--color-ml-500);
  border-color: var(--color-ml-500);
  background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

.checkbox-custom:focus-visible {
  outline: 2px solid var(--color-ml-500);
  outline-offset: 2px;
}

/* A box that failed server validation: red outline (the 1px border plus a 1px ring,
   so it reads as 2px without shifting the layout). The reason is always written out
   next to the box as well, never colour alone. */
.checkbox-custom[aria-invalid="true"] {
  border-color: var(--color-danger);
  box-shadow: 0 0 0 1px var(--color-danger);
}

/* Interstitial checkbox (the legal checkpoint): 20px, with a boundary at gray-500 so the
   one control on the screen clears 3:1 against the page, and a brand-ink focus ring pushed
   out 3px. Scoped to .checkbox-lg: sign-in, sign-up and the league join page keep the
   16px box above. */
.checkbox-custom.checkbox-lg {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.25rem;
  border-color: var(--color-gray-500);
}

.checkbox-custom.checkbox-lg:checked {
  border-color: var(--color-ml-500);
}

.checkbox-custom.checkbox-lg:focus-visible {
  outline-color: var(--color-brand-ink);
  outline-offset: 3px;
}

/* Invalid: a solid 2px red border instead of border + 1px ring, so the focus ring (which
   autofocus puts on the first invalid box) never hides it. */
.checkbox-custom.checkbox-lg[aria-invalid="true"] {
  border-width: 2px;
  border-color: var(--color-danger);
  box-shadow: none;
}

/* Auth input styles for white background forms */
.auth-input-white {
  display: block;
  width: 100%;
  border-radius: 0.625rem;
  background-color: var(--color-surface);
  padding: 0.75rem 0.875rem;
  font-size: 1rem;
  line-height: 1.5rem;
  color: var(--color-gray-900);
  border: 1px solid var(--color-gray-300);
}

.auth-input-white::placeholder {
  color: var(--color-gray-400);
}

.auth-input-white:focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--color-ml-500);
  border-color: transparent;
}

/* Auth primary + Spotify buttons (full pill, mirrors native) */
.auth-btn {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  border-radius: 9999px;
  padding: 0.875rem 1rem;
  font-size: 0.9375rem;
  font-weight: 800;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.auth-btn:hover {
  opacity: 0.9;
}

.auth-btn-primary {
  background-color: var(--color-ml-500);
  color: var(--color-white);
}

.auth-btn-spotify {
  background-color: #1db954;
  color: #ffffff;
}

/* Apple's guidelines: black on light, white on dark, never brand purple. */
.auth-btn-apple {
  background-color: var(--color-inverse);
  color: var(--color-on-inverse);
}

/* Apple Music is a service connect, not Sign in with Apple: outlined, with the Apple Music icon. */
.auth-btn-apple-music {
  background-color: var(--color-surface);
  color: var(--color-gray-900);
  border: 1px solid var(--color-gray-300);
}

/* Navigation link overrides */
nav a.text-white {
  color: white;
}

nav a.text-white:hover {
  color: rgba(255, 255, 255, 0.8);
}

a.no-underline {
  text-decoration: none;
}

/* Safe area padding for bottom nav on notched devices */
.pb-safe {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Invite toggle button states */
.invite-toggle-btn.invited {
  border-color: var(--color-success);
  color: var(--color-success);
  pointer-events: none;
}

/* Support article markdown typography */
.support-article {
  color: var(--color-gray-700);
  font-size: 0.95rem;
  line-height: 1.65;
}

.support-article > *:first-child { margin-top: 0; }
.support-article > *:last-child { margin-bottom: 0; }

.support-article h1,
.support-article h2,
.support-article h3,
.support-article h4 {
  color: var(--color-gray-900);
  font-weight: 600;
  line-height: 1.3;
}

.support-article h2 {
  font-size: 1.25rem;
  margin-top: 2rem;
  margin-bottom: 0.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-gray-100);
}
.support-article h2:first-child {
  padding-top: 0;
  border-top: 0;
  margin-top: 0;
}

.support-article h3 {
  font-size: 1.05rem;
  margin-top: 1.5rem;
  margin-bottom: 0.5rem;
}

.support-article p {
  margin: 0 0 1rem;
}

.support-article ul,
.support-article ol {
  margin: 0 0 1rem;
  padding-left: 1.5rem;
}
.support-article ul { list-style: disc; }
.support-article ol { list-style: decimal; }
.support-article li { margin-bottom: 0.35rem; }
.support-article li > ul,
.support-article li > ol { margin-top: 0.35rem; margin-bottom: 0.35rem; }

.support-article strong {
  color: var(--color-gray-900);
  font-weight: 600;
}

.support-article a {
  color: var(--color-brand-ink);
  text-decoration: none;
}
.support-article a:hover {
  color: var(--color-brand-ink-alt);
  text-decoration: underline;
}

.support-article code {
  background-color: var(--color-gray-100);
  color: var(--color-gray-800);
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  font-size: 0.875em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* Code blocks stay dark in both themes; .support-code__copy sits on them. */
.support-article pre {
  background-color: #111827;
  color: #f3f4f6;
  padding: 1rem;
  border-radius: 0.5rem;
  overflow-x: auto;
  font-size: 0.85rem;
  margin: 0 0 1rem;
}
.support-article pre code {
  background: transparent;
  color: inherit;
  padding: 0;
}

.support-article hr {
  border: 0;
  border-top: 1px solid var(--color-gray-200);
  margin: 2rem 0;
}

.support-article blockquote {
  border-left: 3px solid var(--color-ml-200);
  padding: 0.25rem 0 0.25rem 1rem;
  color: var(--color-gray-600);
  margin: 0 0 1rem;
}

.support-article table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  margin: 0 0 1rem;
}
.support-article th,
.support-article td {
  border-bottom: 1px solid var(--color-gray-200);
  padding: 0.5rem 0.75rem;
  text-align: left;
}
.support-article th {
  color: var(--color-gray-900);
  font-weight: 600;
  background-color: var(--color-gray-50);
}

/* Wide tables scroll horizontally within the article column instead of
   overflowing/clipping (long unbreakable <code> tokens force the width). */
.support-table-wrap {
  overflow-x: auto;
  margin: 0 0 1rem;
  -webkit-overflow-scrolling: touch;
}
.support-table-wrap > table {
  margin: 0;
}

/* News article markdown typography (/news/:slug). Long-form reading density —
   larger type, looser leading, no divider rule above each heading — distinct
   from .support-article's denser, scannable how-to styling. Shares the same
   fenced-code-block (.support-code) and table-wrap markup the renderer always
   emits (RendersArticleMarkdown), so pre/code/table below mirror .support-
   article's rules rather than relying on them. */
.news-article {
  color: var(--color-gray-700);
  font-size: 1.0625rem;
  line-height: 1.75;
}

.news-article > *:first-child { margin-top: 0; }
.news-article > *:last-child { margin-bottom: 0; }

.news-article h1,
.news-article h2,
.news-article h3,
.news-article h4 {
  font-family: 'Bricolage Grotesque', sans-serif;
  color: var(--color-gray-900);
  font-weight: 700;
  line-height: 1.3;
}

.news-article h2 {
  font-size: 1.5rem;
  margin-top: 2.5rem;
  margin-bottom: 1rem;
}

.news-article h3 {
  font-size: 1.1875rem;
  margin-top: 2rem;
  margin-bottom: 0.75rem;
}

.news-article p {
  margin: 0 0 1.5rem;
}

.news-article ul,
.news-article ol {
  margin: 0 0 1.5rem;
  padding-left: 1.5rem;
}
.news-article ul { list-style: disc; }
.news-article ol { list-style: decimal; }
.news-article li { margin-bottom: 0.5rem; }
.news-article li > ul,
.news-article li > ol { margin-top: 0.5rem; margin-bottom: 0.5rem; }

.news-article strong {
  color: var(--color-gray-900);
  font-weight: 700;
}

.news-article a {
  color: var(--color-brand-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.news-article a:hover {
  color: var(--color-brand-ink-alt);
}

.news-article code {
  background-color: var(--color-gray-100);
  color: var(--color-gray-800);
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  font-size: 0.875em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.news-article pre {
  background-color: #111827;
  color: #f3f4f6;
  padding: 1rem;
  border-radius: 0.5rem;
  overflow-x: auto;
  font-size: 0.9rem;
  margin: 0 0 1.5rem;
}
.news-article pre code {
  background: transparent;
  color: inherit;
  padding: 0;
}

.news-article hr {
  border: 0;
  border-top: 1px solid var(--color-gray-200);
  margin: 3rem 0;
}

.news-article blockquote {
  border-left: 3px solid var(--color-ml-200);
  padding: 0.25rem 0 0.25rem 1.25rem;
  color: var(--color-gray-600);
  font-size: 1.1875rem;
  font-style: italic;
  margin: 0 0 1.5rem;
}

.news-article table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
  margin: 0 0 1.5rem;
}
.news-article th,
.news-article td {
  border-bottom: 1px solid var(--color-gray-200);
  padding: 0.5rem 0.75rem;
  text-align: left;
}
.news-article th {
  color: var(--color-gray-900);
  font-weight: 600;
  background-color: var(--color-gray-50);
}

/* Fenced code blocks (URLs, commands, prompts) get a copy-to-clipboard
   button. Reuses clipboard_controller.js — the hidden textarea is its source. */
.support-code {
  position: relative;
  margin: 0 0 1rem;
}
.support-code pre {
  margin: 0;
  padding: 1rem 4.5rem 1rem 1rem; /* right room so the button never covers text */
}
.support-code__copy {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.55rem;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  color: #e5e7eb;
  background-color: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 0.375rem;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.support-code__copy:hover {
  background-color: rgba(255, 255, 255, 0.18);
  color: #ffffff;
}
.support-code__icon {
  width: 0.85rem;
  height: 0.85rem;
}
.support-code__source {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
}

/* Real-time member join animation */
@keyframes member-join {
  0%   { opacity: 0; transform: scale(0.8); background-color: var(--color-ml-50); }
  20%  { opacity: 1; transform: scale(1); background-color: var(--color-ml-100); }
  60%  { opacity: 1; background-color: var(--color-ml-100); }
  100% { opacity: 1; background-color: transparent; }
}
.member-new {
  animation: member-join 1.2s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) {
  .member-new { animation: none; opacity: 1; }
}

/* AdSense: when Google reports no fill, hide the whole labeled ad card
   rather than leaving an empty "Sponsored" box. */
.ad-card:has(ins[data-ad-status="unfilled"]) {
  display: none;
}


/* Photo-save spinner. Turbo sets aria-busy; the cropper's Save is a plain button. */
[data-busy-spinner] {
  display: none;
  width: 0.875rem;
  height: 0.875rem;
  border: 2px solid rgb(255 255 255 / 0.4);
  border-top-color: rgb(255 255 255);
  border-radius: 9999px;
  animation: ml-busy-spin 0.6s linear infinite;
}
form[aria-busy="true"] [data-busy-spinner],
[data-busy-spinner].is-busy {
  display: inline-block;
}
button[data-image-picker-target="saveButton"].is-busy {
  opacity: 0.75;
  pointer-events: none;
}
/* Opt-in only. Own rule so a WebView without :has() keeps the rule above. */
form[aria-busy="true"] [type="submit"]:has([data-busy-spinner]) {
  opacity: 0.75;
  pointer-events: none;
}
@keyframes ml-busy-spin {
  to { transform: rotate(360deg); }
}
