/* Shared component layer for the Eazdrop web reader.
 *
 * Every class here maps to a named layer in the Figma file. Card templates
 * compose these; they should not need bespoke CSS beyond a card-specific
 * block at the bottom of this file. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg-page);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

button {
  font: inherit;
  color: inherit;
}

/* ===================================================================== */
/* App chrome                                                             */
/* ===================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  min-height: 56px;
  border-bottom: 1px solid var(--accent-32);
  background: rgba(13, 0, 16, 0.72);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  padding: 10px 24px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: var(--text);
  flex-shrink: 0;
}

.brand__mark {
  width: 30px;
  height: 30px;
  object-fit: contain;
}

.brand__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  line-height: 28px;
  letter-spacing: -0.5px;
}

.btn-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 0;
  border-radius: var(--radius-cta);
  background: var(--gradient-cta);
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
}

.btn-cta--sm {
  height: 36px;
  padding: 8px 16px;
  font-size: 14px;
  line-height: 20px;
}

.btn-cta--md {
  height: 44px;
  padding: 12px 28px;
  font-size: 14px;
  line-height: 20px;
}

.btn-cta--lg {
  height: 54px;
  width: 100%;
  padding: 14px;
  font-size: 16px;
  line-height: 24px;
}

.btn-cta--pill {
  height: 40px;
  padding: 10px 20px;
  font-size: 14px;
  line-height: 20px;
}

.btn-cta__mark {
  flex-shrink: 0;
  object-fit: contain;
}

/* ===================================================================== */
/* Preview switcher (development only)                                    */
/* ===================================================================== */

.switcher {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.switcher__label {
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-50);
  font-family: var(--font-display);
  font-weight: 600;
}

.switcher__select {
  appearance: none;
  border: 1px solid var(--accent-32);
  background-color: rgba(31, 0, 36, 0.9);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-70) 50%),
    linear-gradient(135deg, var(--text-70) 50%, transparent 50%);
  background-position:
    calc(100% - 16px) 52%,
    calc(100% - 11px) 52%;
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
  border-radius: var(--radius-pill);
  color: var(--text);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 32px 7px 14px;
  cursor: pointer;
}

.switcher__select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.switcher__select option {
  background: #1a0020;
  color: var(--text);
}

/* Shown only when JS is unavailable — the form still works, it just needs a
 * click to submit. */
.switcher__go {
  border: 1px solid var(--accent-55);
  background: var(--accent-20);
  color: var(--text);
  border-radius: var(--radius-pill);
  padding: 7px 14px;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.js .switcher__go {
  display: none;
}

/* ===================================================================== */
/* Page shell                                                             */
/* ===================================================================== */

.page {
  /* --content-width is the width of the CARD, as drawn in Figma. Because
   * box-sizing is border-box, max-width includes the side padding, so the
   * gutter has to be added back or every card renders 24px narrow. */
  max-width: calc(var(--content-width) + (var(--page-gutter) * 2));
  margin: 0 auto;
  padding: 28px var(--page-gutter) 64px;
}

.pov-tag {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 4px 8px;
}

.pov-tag__state {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.pov-tag__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

.pov-tag__text {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  line-height: 16px;
  white-space: nowrap;
}

.pov-tag--live .pov-tag__dot {
  background: var(--state-live);
}
.pov-tag--live .pov-tag__text {
  color: var(--state-live);
}
.pov-tag--locked .pov-tag__dot {
  background: var(--state-locked);
}
.pov-tag--locked .pov-tag__text {
  color: var(--state-locked);
}
.pov-tag--paid .pov-tag__dot {
  background: var(--state-paid);
}
.pov-tag--paid .pov-tag__text {
  color: var(--state-paid);
}

.pov-tag__rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--hairline);
}

/* ===================================================================== */
/* Card primitives                                                        */
/* ===================================================================== */

.card {
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--accent-32);
  border-radius: var(--radius-card);
  background: var(--surface-card);
}

.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 16px 16px 12px;
}

.card__head-left {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.card__body {
  display: flex;
  flex-direction: column;
  padding: var(--card-pad);
}

.card__media {
  padding: 0 12px;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 27px;
  padding: 5px 12px;
  border: 0.5px solid var(--accent-55);
  border-radius: var(--radius-pill);
  background: var(--accent-20);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  line-height: 16px;
  color: var(--text);
  white-space: nowrap;
}

.badge__icon {
  width: 11px;
  height: 11px;
  flex-shrink: 0;
}

.meta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  line-height: 16px;
  color: var(--text-70);
  white-space: nowrap;
}

.meta__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--state-live-dot);
  opacity: 0.71;
  flex-shrink: 0;
}

.author {
  display: flex;
  align-items: center;
  gap: 8px;
}

.author__avatar {
  width: 26px;
  height: 26px;
  min-width: 26px;
  border-radius: var(--radius-pill);
  object-fit: cover;
}

.author__text {
  font-size: 14px;
  line-height: 20px;
  color: var(--text-70);
}

.card__title {
  margin: 12px 0 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  line-height: 28px;
  color: var(--text);
}

.card__text {
  margin: 12px 0 0;
  font-size: 14px;
  line-height: 22.75px;
  color: var(--text-70);
}

.card__note {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 16px;
  color: var(--text-72);
}

.card__cta {
  margin-top: 12px;
}

/* ---- Cover ----------------------------------------------------------- */

.cover {
  position: relative;
  height: 260px;
  border-radius: var(--radius-panel);
  overflow: hidden;
}

/* A container that sizes to its gate instead of clipping it.

   Gate copy has no fixed length: a community name can run to a hundred
   characters and a service can supply the wording outright, so a designed
   height is a floor rather than a ceiling. The trick is inverting the two
   layers — the media becomes the backdrop and the gate is what has height.
   Each card keeps its own floor and names its own backdrop; this is the half
   they share. */
.fits-gate {
  display: flex;
  height: auto;
}

/* Keeps .gate's own `padding: 0 24px`. Vertical padding here would be added
   to the gate's height and so to the container's, which puts every card whose
   gate already fills its frame above its floor — scheduled-past grew 43px on
   ordinary copy that way. The floor exists to leave short copy untouched, so
   the gate has to measure as its content alone. */
.fits-gate > .gate {
  position: relative;
  inset: auto;
  flex: 1;
}

.cover.fits-gate {
  min-height: 260px;
}

.cover.fits-gate .cover__img,
.cover.fits-gate .cover__fill {
  position: absolute;
  inset: 0;
}

.cover__fill {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
  background: var(--cover-blue);
}

.cover__initial {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 187px;
  line-height: 1.5;
  color: var(--text-20);
  user-select: none;
}

.cover__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A cover holding a REAL image sizes to it, same rule as every other piece of
 * media on a card. The gradient placeholder (.cover__fill) keeps the designed
 * height instead: it has no natural size to take, and its oversized initial
 * needs the room. That is why this is a modifier the template opts into on
 * the image branch rather than a change to .cover itself — the same container
 * renders both. */
.cover--media {
  height: auto;
  min-height: 120px;
}

.cover--media .cover__img {
  height: auto;
  max-height: 85vh;
  object-fit: contain;
}

.is-blurred {
  filter: blur(6px);
}

/* ---- Gate overlay ---------------------------------------------------- */

.gate {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 0 24px;
  text-align: center;
  background: var(--overlay-strong);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.gate__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin: 0 auto 4px;
  border: 1px solid var(--surface-raised-border);
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
}

.gate__icon img {
  width: 20px;
  height: 20px;
}

.gate__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  line-height: 24px;
  color: var(--text);
}

.gate__text {
  margin: 0;
  font-size: 14px;
  line-height: 22.75px;
  color: var(--text-70);
}

/* ---- Panel (message / detail previews) ------------------------------- */

.panel {
  position: relative;
  margin-top: 12px;
  padding: 12px;
  border: 1px solid var(--surface-panel-border);
  border-radius: var(--radius-panel);
  background: var(--surface-panel);
}

.panel__label {
  margin: 0 0 2px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-50);
}

.panel-wrap {
  position: relative;
  margin-top: 12px;
  border-radius: var(--radius-panel);
  overflow: hidden;
}

.panel-wrap .panel {
  margin-top: 0;
}

.panel-wrap__veil {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay-soft);
}

.msg {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding-top: 10px;
}

.msg__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12px;
  line-height: 16px;
  color: var(--text);
}

.msg__body {
  min-width: 0;
  font-size: 12px;
  line-height: 19.5px;
  color: var(--text-65);
}

.msg__name {
  font-weight: 600;
  color: var(--text-60);
  line-height: 16px;
}

/* Skeleton bars stand in for message text behind a gate. */
.skel {
  display: block;
  border-radius: 6px;
  background: var(--skeleton-weak);
}

.skel--name {
  width: 132.5px;
  height: 10px;
  background: var(--skeleton-strong);
}

.skel--line {
  width: 397.5px;
  max-width: 100%;
  height: 8px;
  margin-top: 4px;
}

/* ===================================================================== */
/* Footer                                                                 */
/* ===================================================================== */

.footer {
  border-top: 1px solid var(--hairline);
  padding: 28px 24px;
  text-align: center;
  color: var(--text-50);
  font-size: 12px;
}

.footer a {
  color: var(--text-70);
}

/* ===================================================================== */
/* Responsive                                                             */
/* ===================================================================== */

@media (max-width: 680px) {
  .header__inner {
    padding: 10px 14px;
    /* Brand + CTA share the first row; the switcher drops to its own row
     * below. Without this the three children overflow the viewport and the
     * whole page scrolls sideways. */
    flex-wrap: wrap;
    row-gap: 8px;
  }

  .switcher {
    order: 3;
    width: 100%;
    gap: 6px;
  }

  .switcher__select {
    flex: 1 1 0;
    min-width: 0;
  }

  .switcher__label {
    display: none;
  }

  .cover {
    height: 210px;
  }

  /* The floor follows the designed height at this breakpoint, so a gate with
     short copy is not forced taller than the cover it replaces. height must be
     restated: the .cover rule above is later in the file and equally specific,
     so without it the fixed height wins right back and the gate clips again. */
  .cover.fits-gate {
    height: auto;
    min-height: 210px;
  }

  .cover__initial {
    font-size: 132px;
  }
}

/* ===================================================================== */
/* Open-in-app affordance (Android)                                       */
/* ===================================================================== */

/* Hidden by default and revealed only for Android user agents by the
 * inline script in the content layout. iOS gets the native Smart App
 * Banner instead, and desktop needs neither. */
.open-in-app {
  display: none;
  margin: 0 0 12px;
  padding: 0 4px;
  font-size: 13px;
}

.open-in-app a {
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 600;
  text-decoration: none;
}

.is-android .open-in-app {
  display: block;
}

/* ===================================================================== */
/* Share                                                                  */
/* ===================================================================== */

/* Sits below the card with a clear gap, so it reads as an action ON the
 * content rather than part of it. The button itself wears the shared
 * .btn-cta classes, so there is nothing to style here beyond that spacing.
 *
 * The .js rule is load-bearing rather than decorative: the flex parent is
 * what stretches the button to the card's width. Without scripting the
 * button renders at content width and does nothing when pressed, since both
 * behaviours behind it — the native share sheet and the clipboard fallback —
 * need JS. */
.share {
  margin-top: 48px !important;
}

.js .share {
  display: flex;
}

/* ---- Clamped copy ------------------------------------------------------
 *
 * Collapses long descriptions and captions to a fixed line count, with the
 * more/less toggle inline at the end of the last visible line.
 *
 * -webkit-line-clamp cannot do that: it hides the overflow, and anything
 * placed after the text is part of that overflow. So clamp.js cuts the text
 * itself, to a length that leaves room for the toggle on the last line, and
 * the clamp below is only the pre-JS floor that stops a long paragraph
 * flashing at full height before the script runs.
 *
 * All of it is gated on .js: without scripting the toggle cannot work, and a
 * permanently cut description would hide content outright, so a no-JS reader
 * gets the whole thing.
 *
 * The line count arrives as a data attribute rather than an inline style —
 * html/template refuses to interpolate into a style attribute. */

.clamp {
  display: block;
}

.clamp[data-clamp-lines="2"] { --clamp-lines: 2; }
.clamp[data-clamp-lines="3"] { --clamp-lines: 3; }
.clamp[data-clamp-lines="4"] { --clamp-lines: 4; }
.clamp[data-clamp-lines="5"] { --clamp-lines: 5; }
.clamp[data-clamp-lines="6"] { --clamp-lines: 6; }

/* Pre-JS floor only. Once clamp.js has trimmed the text the element goes back
   to normal flow — a clamped box would clip the very toggle we just made room
   for. */
.js .clamp:not(.is-measured) .clamp__text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--clamp-lines, 2);
  line-clamp: var(--clamp-lines, 2);
  overflow: hidden;
}

/* Inherit the type so the toggle shares a baseline with the words it follows;
   only the weight and colour set it apart. Never let it wrap on its own. */
.clamp__toggle {
  display: inline;
  margin: 0 0 0 4px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--text-72);
  white-space: nowrap;
  cursor: pointer;
}

.clamp__toggle:hover {
  color: var(--text);
}

.clamp__toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* An explicit rule is required: the display above would otherwise beat the
   hidden attribute's default styling. */
.clamp__toggle[hidden] {
  display: none;
}


/* ---- Media carousel ----------------------------------------------------
 *
 * A drop can carry several images or videos. The track is a scroll-snap list,
 * which means swiping and trackpad scrolling work with no JavaScript at all;
 * carousel.js only adds the arrows, dots and counter on top, and they ship
 * hidden so a no-JS reader never sees a dead control. */

.carousel {
  position: relative;
}

.carousel__track {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Firefox and the standard property; the WebKit rule below hides the bar in
     Safari and Chrome. The track stays keyboard-scrollable either way. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.carousel__track::-webkit-scrollbar {
  display: none;
}

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

/* Every slide shares one row height, so without a definite box the tallest
   image sets it: a single portrait shot stretched the frame and left every
   landscape slide stranded at the top above a band of dead space, and the
   scrim then washed the bottom of the tall ones. One bounded viewport with the
   media centred inside it letterboxes mixed aspect ratios symmetrically
   instead, and nothing is cropped.

   Only the carousel does this. A single-media drop keeps its natural height. */
.carousel__track {
  aspect-ratio: 1 / 1;
  max-height: 70vh;
}

.carousel__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.carousel__slide > img,
.carousel__slide > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.carousel__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--surface-raised-border);
  border-radius: var(--radius-pill);
  background: var(--overlay-strong);
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.carousel__nav--prev { left: 8px; }
.carousel__nav--next { right: 8px; }

.carousel__nav:disabled {
  opacity: 0.35;
  cursor: default;
}

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

.carousel__count {
  position: absolute;
  top: 12px;
  right: 12px;
  margin: 0;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--overlay-strong);
  font-size: 12px;
  line-height: 16px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.carousel__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 10px;
  display: flex;
  gap: 6px;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.carousel__dot {
  display: block;
  width: 6px;
  height: 6px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--text-50);
  cursor: pointer;
}

.carousel__dot[aria-current="true"] {
  background: var(--text);
}

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

/* Explicit rules: the displays above would otherwise beat the hidden
   attribute's default styling. */
.carousel__nav[hidden],
.carousel__count[hidden],
.carousel__dots[hidden] {
  display: none;
}

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

/* ---- Community attribution ---------------------------------------------
 *
 * Content that lives in a community names it above the author, with the
 * community's picture stacked behind the author's — the same treatment the
 * app's feed cards use. Personal content renders its usual author row and
 * never reaches these rules. */

.author-community {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

/* The community sits behind and above-left of the author, the way
   EAccountAvatar stacks them in the app. The box is one avatar plus the
   offset, so the text beside it starts in the same place either way. */
.author-community__stack {
  position: relative;
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
}

.author-community__stack--solo {
  width: 34px;
  height: 34px;
}

.author-community__behind,
.author-community__front {
  position: absolute;
  object-fit: cover;
}

/* A rounded square, not a circle: EAccountAvatar draws a community as
   BoxShape.rectangle, which is what tells the two apart at a glance. */
/* Roughly half of it stays uncovered, which is what makes it read as a second
   thing behind the avatar rather than a fringe on it. */
.author-community__behind {
  top: 0;
  left: 0;
  width: 30px;
  height: 30px;
  border-radius: 8px;
}

/* Offset down-right so the community peeks out at the top left. The ring is
   what reads as "in front", so it takes the card's own background rather than
   a fixed colour. */
.author-community__front {
  top: 14px;
  left: 14px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--bg-page);
  z-index: 1;
}

/* With no community picture there is nothing to offset from. */
.author-community__stack--solo .author-community__front {
  top: 0;
  left: 0;
}

.author-community__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The community line sits UNDER the person and carries the sub-line with it,
   separated by a middot — one line, not two. */
.author-community__meta {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  line-height: 16px;
  color: var(--text-72);
  min-width: 0;
}

.author-community__name {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.author-community__name > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.author-community__name > img {
  flex: 0 0 auto;
}

.author-community__person {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  line-height: 20px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.author-community__sub {
  flex: 0 0 auto;
  color: var(--text-60);
}

.author-community__dot {
  flex: 0 0 auto;
  color: var(--text-60);
}
