/* ============================================================
   MUSIMEE · LANDING PATTERNS  ·  v2.1
   ------------------------------------------------------------
   Ready-made classes for the patterns that repeat in the
   expressive layer. Import AFTER tokens.css:

     <link rel="stylesheet" href="tokens/tokens.css">
     <link rel="stylesheet" href="tokens/patterns.css">

   Everything here came out of a real problem measured in
   production. The veil numbers come from docs/text-over-photo.md
   and are not guesses.
   ============================================================ */

/* ══ 1. TEXT OVER PHOTO ══════════════════════════════════════
   Text tokens for a darkened photographic surface.
   Put .on-photo on the container that holds the background photo. */
.on-photo {
  --m-text:        var(--m-paper);   /* heading             */
  --m-text-body:   #DDD8CE;          /* corpo               */
  --m-text-muted:  #C6C0B6;          /* eyebrow, legenda    */
  --m-text-faint:  #A9A49A;          /* apagado             */
  --m-accent-text: var(--m-signal-400);  /* 400, not 500 —
       500 sits right on the limit over a darkened photo */
  color: var(--m-text-body);
}

/* Calculated veils. NEVER edit the stops without re-measuring — see
   docs/text-over-photo.md, the "The procedure" section. */

/* text at the bottom (mobile): vertical ramp */
.scrim-v::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(25,23,20,.04)  0%,
    rgba(25,23,20,.06) 20%,
    rgba(25,23,20,.76) 27%,
    rgba(25,23,20,.84) 100%);
}

/* text on the left (desktop): horizontal PLATEAU.
   A linear ramp does not work: it weakens in the middle of the text. */
.scrim-h::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(90deg,
      rgba(25,23,20,.76)  0%,
      rgba(25,23,20,.76) 56%,
      rgba(25,23,20,0)   72%),
    linear-gradient(180deg,
      rgba(25,23,20,.06)  0%,
      rgba(25,23,20,.06) 55%,
      rgba(25,23,20,.22) 100%);
}

/* caption in a card's footer */
.scrim-card::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(25,23,20,.06)  0%, rgba(25,23,20,0)   34%,
    rgba(25,23,20,.72) 74%, rgba(25,23,20,.88) 100%);
}

/* ══ 2. SECTION BACKGROUND PHOTO ═════════════════════════════ */
.photo-bg { position: absolute; inset: 0; z-index: 0; }
.photo-bg picture { display: block; width: 100%; height: 100%; }
.photo-bg img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  /* stitches the photo into the warm palette of the rest of the page */
  filter: saturate(.86) contrast(1.02);
}

/* ══ 3. ALPHA CUT-OUT ════════════════════════════════════════
   drop-shadow follows the outline; box-shadow would draw the
   invisible rectangle and give the trick away.                   */
.cutout {
  filter:
    drop-shadow(0 18px 26px rgba(43,41,38,.14))
    drop-shadow(0 2px 3px rgba(43,41,38,.10))
    saturate(.78) sepia(.05) contrast(1.02);
}

/* ══ 4. SCROLLING CHIP ROW ═══════════════════════════════════
   7 chips at 360px become 3 rows and 156px. This way they become 44px. */
.chipbar { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--m-3); }
@media (max-width: 600px) {
  .chipbar {
    flex-wrap: nowrap; justify-content: flex-start;
    overflow-x: auto; scroll-snap-type: x proximity;
    scrollbar-width: none; -ms-overflow-style: none;
    padding-inline: var(--m-gutter);
    margin-inline: calc(var(--m-gutter) * -1);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  }
  .chipbar::-webkit-scrollbar { display: none; }
  .chipbar > * { flex: 0 0 auto; scroll-snap-align: start; }
}

/* ══ 5. CARD GRID ════════════════════════════════════════════
   auto-fit on its own collapses to ONE column on mobile and the 1fr
   stretches the card to the full width. Lock 2 columns.          */
.cardgrid {
  display: grid; gap: clamp(12px, 2vw, 24px);
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
}
@media (max-width: 600px) {
  .cardgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--m-3); }
}

/* ══ 6. EXPANDABLE LIST ══════════════════════════════════════
   [hidden] loses to any author display — the rule has to be
   explicit AND more specific.                                    */
.expandable { display: flex; flex-wrap: wrap; gap: var(--m-2); list-style: none; margin: 0; padding: 0; }
.expandable[hidden] { display: none; }

/* ══ 7. ANIMATED TEXT ════════════════════════════════════════
   Height reserved: without it the block jumps on every change and the
   text leaves the band where the contrast was measured.          */
.typer { min-height: calc(1.55em * 2); }          /* 2 linhas: mobile */
@media (min-width: 900px) { .typer { min-height: 1.55em; } }
.typer__cur {
  display: inline-block; width: 2px; height: 1em; margin-left: 3px;
  vertical-align: -.12em; background: var(--m-signal-400);
  animation: typer-blink 1s steps(1) infinite;
}
@keyframes typer-blink { 0%,50% { opacity: 1 } 50.01%,100% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .typer__cur { animation: none } }

/* ══ 8. SECTION DENSITY ══════════════════════════════════════
   The default 160px at both ends was twice what a section with a
   large card needed.                                             */
.sec-tight { padding-block: clamp(56px, 6vw, 96px); }
@media (max-width: 600px) { .sec-tight { padding-block: var(--m-10); } }

/* ══ 9. TEXTURE — a ceiling of 0.06 ══════════════════════════
   Above that the grain dirties the whole background grey.        */
.grain::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: var(--m-grain);
  opacity: .05; mix-blend-mode: multiply;
}
.on-ink.grain::after { mix-blend-mode: screen; }

/* ── .scrim-panel ─────────────────────────────────────────────────────────
   Footer-panel veil: the opaque block where the controls and the caption
   live, pinned to the bottom of a photo. It differs from .scrim-card (which
   is a veil for a loose caption) by being a SHELF — it comes in strong and
   stays strong.

   The stops guarantee alpha >= .78 at any point below 16% of the panel.
   That is the floor the .on-photo scale was calibrated for, so inside a
   .scrim-panel the SAME hero text tokens apply — no local colour, no
   per-photo exception.

   Measured at the worst pixel of the six demo photos:
     duration (--m-text-muted, alpha .795)  5.41 : 1   worst case
     lyric    (--m-text,       alpha .886) 12.25 : 1
     next     (--m-text-faint, alpha .905)  5.84 : 1
   Before, with .68 at 16%, demo-homenagem gave 4.38 and failed.
   Swapped the photo? Re-measure. See docs/text-over-photo.md.     */
.scrim-panel {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  background: linear-gradient(180deg,
    rgba(25, 23, 20, 0)   0%,
    rgba(25, 23, 20, .78) 16%,
    rgba(25, 23, 20, .88) 50%,
    rgba(25, 23, 20, .94) 100%);
}

/* ── .scrim-body ──────────────────────────────────────────────────────────
   FULL-BODY veil: text from the top of the piece to the bottom.

   There is no ramp here — there is a PLATEAU. A ramp weakens in the middle
   of the text itself: the first version of the demo card used .52 at 38% and
   the lines vanished over the light part of the photo.

   The .74 plateau only works alongside two TEXT decisions. Darkening further
   was not the way out — lighter text costs the photo nothing:

     lines in --m-text-faint  #A9A49A -> demanded alpha .845 (photo goes black)
     lines in --m-text-body   #DDD8CE -> demands alpha .690
     accent at 16px  (target 4.5)     -> demanded alpha .816
     accent at 19px bold (AA-large 3.0) -> demands alpha .704

   So, inside .scrim-body: body copy in `body`, never `faint`; and the accent
   line at >=18.66px in bold. Measured at the worst pixel of 6 photos, on
   desktop and on mobile. See docs/text-over-photo.md.                     */
.scrim-body::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(25, 23, 20, .62)   0%,
    rgba(25, 23, 20, .74)   6%,
    rgba(25, 23, 20, .74)  93%,
    rgba(25, 23, 20, .88) 100%);
}

/* ── Textures ─────────────────────────────────────────────────────────────
   Pure CSS, deliberately: a `repeating-gradient` costs neither a request nor
   a data URI. Paid traffic pays for every KB — a texture cannot be an image.

   A 4px period, never 1px: on a screen with a fractional DPR (1.25, 1.5) a
   1px pattern turns into moiré and the page looks like a rendering fault.

   Alpha on the floor. A texture is meant to be felt, not seen — the same
   rule as `--m-grain` (a ceiling of .06). If you can point at the weave,
   it is too high.                                                          */

/* The weave of kept paper — for the sections on Paper.
   The brand is family memory; the surface has to look like paper somebody
   kept, not a dashboard canvas. */
.tex-linen {
  background-image:
    repeating-linear-gradient(0deg,
      rgba(43, 41, 38, .038) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg,
      rgba(43, 41, 38, .028) 0 1px, transparent 1px 5px);
}

/* Concentric waves — for the section on Ink, where the listening happens.
   The sound leaving the card and filling the room.

   The CENTRE has to line up with the card, otherwise it becomes a random
   decorative pattern instead of sound coming from there. Measured: the
   card's centre sits at 62.3% of the section on desktop and 67.0% on
   mobile — 64% serves both, because a soft band forgives 3% of drift. If
   the section's layout changes, re-measure and adjust --tex-y.

   SOFT BANDS, not a 2px thread. A thin thread draws wire; a band with a
   feathered transition reads as a wave of light. And because it is soft, the
   brightness peak is lower — it disturbs the contrast of the text on top
   less.

   Alpha .055 at the peak: the grain rule (a ceiling of .06) does NOT apply
   here. Grain is noise and disappears into the background; this is drawing
   and needs to be seen. At .030 it was invisible — the texture existed and
   nobody could see it.

   Cost in contrast, measured over the band's peak:
     h2      --m-text        12.10  (was 13.20)
     lede    --m-text-body    9.37  (was 10.21)
     eyebrow --m-text-muted   5.60  (was  6.10)
   All comfortable. See docs/weight.md and docs/text-over-photo.md.        */
.tex-rings {
  --tex-x: 50%;
  --tex-y: 64%;   /* centre of the card, measured */
  background-image:
    repeating-radial-gradient(circle at var(--tex-x) var(--tex-y),
      rgba(240, 121, 59, 0)     0     38px,
      rgba(240, 121, 59, .055)  50px  58px,
      rgba(240, 121, 59, 0)     70px 104px);
}

/* The two coexist with .grain — grain is noise, texture is drawing.
   Together they give surface; grain alone gives dirt. */
@media (prefers-reduced-motion: reduce) {
  /* a texture is static, nothing to switch off here — a note for whoever animates it */
}

/* ── .lang · language selector ────────────────────────────────────────────
   The markup is injected by the build (one per language), so this CSS has to
   exist even when the element is not on the development page.

   The FLAG is recognisable decoration; what identifies is the LANGUAGE NAME
   in its own language — that is how someone who does not read Portuguese
   finds their own language. So the name is never translated into the page's
   language.                                                                */
.lang { position: relative; }

.lang__btn {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0 10px;
  background: transparent; border: 0; cursor: pointer;
  font-family: var(--m-font); font-size: 13px; font-weight: 500;
  letter-spacing: .04em; color: var(--m-text-body);
  border-radius: var(--m-r-pill);
  transition: color var(--m-t-fast), background var(--m-t-fast);
}
.lang__btn:hover { color: var(--m-text); background: var(--m-surface-alt); }
.lang__btn svg { transition: transform var(--m-t-fast); }
.lang__btn[aria-expanded="true"] svg { transform: rotate(180deg); }

/* SVG, not emoji: Windows ships no flag glyph and draws the regional
   indicator pair as two letters ("BR", "US"). See scripts/flags.mjs. */
.lang__fl {
  width: 18px; height: 12px; flex: 0 0 18px;
  border-radius: 2px; display: block;
  box-shadow: 0 0 0 1px rgba(43, 41, 38, .12) inset;
}

.lang__menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 210px; padding: 6px;
  background: var(--m-surface); border: 1px solid var(--m-border);
  border-radius: var(--m-r-lg); box-shadow: var(--m-sh-lg);
  display: flex; flex-direction: column;
}
/* `hidden` loses to any author `display` — the rule has to be more
   specific than the component's own. It already broke this project's mobile
   menu once (docs/pitfalls.md). */
.lang__menu[hidden] { display: none; }

/* AN <a> ON THE SITE AND A <button> IN THE CONSOLE. The site's selector
   navigates to /es/; the console changes a preference without leaving the
   page, which is a button. A button carries the browser's own grey
   `buttonface` background and its own font, so the menu rendered as six grey
   slabs with washed text — correct markup, wrong-looking screen. The reset
   belongs on the component rather than in whoever reuses it next. */
.lang__op {
  appearance: none;
  background: none;
  border: 0;
  font: inherit;
  width: 100%;
  text-align: left;
  cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 0 12px;
  border-radius: var(--m-r-md);
  color: var(--m-text-body); text-decoration: none;
  font-size: 14px; white-space: nowrap;
}
.lang__op:hover { background: var(--m-surface-alt); color: var(--m-text); }
.lang__op.is-on { color: var(--m-accent-text); font-weight: 500; }
.lang__op.is-on::after {
  content: ''; margin-left: auto; width: 14px; height: 2px;
  border-radius: 2px; background: currentColor;
}

@media (max-width: 900px) {
  /* in the narrow nav only the flag and the code stay; the name lives in the menu */
  .lang__btn { padding: 0 6px; }
  .lang__menu { right: -8px; }
}

/* MOVING BETWEEN PAGES WITHOUT A FLASH.
   The landing and the quiz are two separate documents — clicking "Criar
   música" is a full navigation, and it landed hard: white flash, then the
   quiz. Both pages opt in, so a browser that supports cross-document view
   transitions crosses between them instead of cutting.
   Only both together does anything: one page opting in alone is ignored. */
@view-transition { navigation: auto; }

/* THE TIMING IS OURS, not the browser's default.
   Left alone, a cross-document transition crossfades on the user agent's own
   curve. This is the same crossfade at our speed — short enough that it reads
   as the page settling rather than as an effect, and the mark named on both
   sides does not move at all, which is the part that makes it feel like one
   place instead of two. Both directions: leaving for the quiz and coming
   back. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .26s;
  animation-timing-function: cubic-bezier(.2, .8, .2, 1);
}


/* The mark is the one thing on both pages, in the same place. Naming it on
   each side is what turns a crossfade into the logo staying put while the
   page changes underneath it. */
.logo { view-transition-name: musimee-mark; }

/* Somebody who asked their system for less motion is asking for this too. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}


/* ══ 9. PROSE ═══════════════════════════════════════════════
   The long-form pages under the landing — privacy, terms, delivery
   times, the FAQ. One reading measure (--m-content, ~68ch), the body
   face, headings on the display face at h3/h4 size so a legal page
   does not shout, and the spacing scale for everything vertical. The
   markup is what Markdown produces (h2, h3, p, ul, ol, strong, a): no
   classes inside the text, so a translator edits words and nothing
   else. */
.prose { color: var(--m-text-body); font-size: var(--m-fs-md); line-height: 1.7; }
.prose > * + * { margin-top: var(--m-4); }
.prose h2 { font-family: var(--m-font-display); font-size: var(--m-fs-h3); line-height: 1.25;
            letter-spacing: -.015em; color: var(--m-text); margin-top: var(--m-12); }
.prose h3 { font-family: var(--m-font-display); font-size: var(--m-fs-h4); line-height: 1.3;
            color: var(--m-text); margin-top: var(--m-8); }
.prose h2 + *, .prose h3 + * { margin-top: var(--m-3); }
.prose ul, .prose ol { padding-left: var(--m-5); }
.prose li + li { margin-top: var(--m-2); }
.prose strong { color: var(--m-text); font-weight: 500; }
.prose a { color: var(--m-text-link); text-decoration: underline; text-underline-offset: .15em; }
.prose a:hover { color: var(--m-text); }
/* The date a page was last changed, and the group it belongs to, both set
   in mono like every other small label on the site. */
.prose__meta { font-family: var(--m-font-mono); font-size: var(--m-fs-sm); color: var(--m-text-muted);
               margin-top: var(--m-12); padding-top: var(--m-6); border-top: 1px solid var(--m-border); }
/* The page head sits above the prose on the same measure; the h1 is the
   landing's display size one step down, because these pages are read, not
   announced. */
.page-head { max-width: var(--m-content); margin-inline: auto; }
.page-head h1 { font-family: var(--m-font-display); font-size: var(--m-fs-h1); line-height: 1.08;
                letter-spacing: -.025em; color: var(--m-text); margin: var(--m-3) 0 var(--m-4); }

/* ══ 10. THE CONTACT CARD ═══════════════════════════════════
   "Não encontrou a sua pergunta?" — under the landing's FAQ, on /faq
   and on /contact, drawn by the build from company.json: one row per
   channel that has a value. A warm surface, the reading measure, rows
   that are whole-width links so a thumb lands anywhere on them. */
.ccard { max-width: var(--m-content); margin: var(--m-10) auto 0; padding: var(--m-6);
         border-radius: var(--m-r-2xl); background: var(--m-surface-alt); border: 1px solid var(--m-border-soft); }
.ccard__title { margin: 0; font-family: var(--m-font-display); font-size: var(--m-fs-h3); line-height: 1.2;
                letter-spacing: -.015em; color: var(--m-text); }
.ccard__lede { margin: var(--m-2) 0 0; font-size: var(--m-fs-md); line-height: 1.55; color: var(--m-text-body); }
.ccard__rows { list-style: none; margin: var(--m-5) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--m-2); }
.ccard__row { display: flex; align-items: center; gap: var(--m-3); padding: var(--m-3) var(--m-4);
              border-radius: var(--m-r-pill); background: var(--m-surface); color: var(--m-text);
              text-decoration: none; border: 1px solid var(--m-border); transition: border-color var(--m-t-fast); }
.ccard__row:hover { border-color: var(--m-border-strong); }
.ccard__row svg { flex: none; color: var(--m-accent-text); }
.ccard__label { font-family: var(--m-font-mono); font-size: var(--m-fs-xs); text-transform: uppercase;
                letter-spacing: var(--m-ls-eyebrow); color: var(--m-text-muted); min-width: 7ch; }
.ccard__value { font-size: var(--m-fs-md); font-weight: 500; overflow-wrap: anywhere; }
