/* =========================================================================
   O DIÁRIO DOS NOIVOS — newspaper-chic design system
   Ana & Renan · 18.10.2026 · La Selva

   Single committed look: newsprint. No dark mode — a newspaper is paper.
   ========================================================================= */

:root {
    /* Ink & paper — warm, not stark. The page should feel welcoming in the hand. */
    --newsprint:        #F7F4EC;
    --newsprint-deep:   #EFEBE0;
    --newsprint-edge:   #E5DFD1;
    --ink:              #1C1A15;
    --ink-soft:         #55503F;
    --ink-faint:        #918978;
    --ink-ghost:        #C4BCAA;
    --accent:           #8A4034;  /* one warm red, used sparingly */

    /* Rules — hairlines carry the structure; nothing needs to shout */
    --rule-hair:        0.5px solid var(--ink-ghost);
    --rule-thin:        1px solid var(--ink);
    --rule-thick:       2px solid var(--ink);
    --rule-faint:       1px solid var(--ink-ghost);

    /* Type */
    --display: "Playfair Display", "Times New Roman", Times, serif;
    --body:    "Libre Baskerville", Georgia, "Times New Roman", serif;
    --label:   "Archivo Narrow", "Helvetica Neue", Arial, sans-serif;

    /* Measure & rhythm */
    --measure:    72rem;
    --measure-text: 34rem;
    --gutter:     clamp(1.25rem, 4vw, 2.75rem);
}

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

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* The nav is sticky, so an anchor target would otherwise land behind it. */
    scroll-padding-top: 4rem;
    /* Keeps a full-bleed section's 100vw from turning the scrollbar's width into
       a horizontal scroll. `clip`, not `hidden`: hidden would make this a scroll
       container and kill position:sticky on the nav. */
    overflow-x: clip;
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
    margin: 0;
    background-color: var(--newsprint);
    /* Faint fibre tooth so the paper isn't a flat swatch */
    background-image:
        radial-gradient(circle at 15% 20%, rgba(23,21,15,.020) 0 1px, transparent 1px),
        radial-gradient(circle at 62% 71%, rgba(23,21,15,.016) 0 1px, transparent 1px),
        radial-gradient(circle at 84% 38%, rgba(23,21,15,.013) 0 1px, transparent 1px);
    background-size: 37px 41px, 53px 47px, 29px 61px;
    color: var(--ink);
    font-family: var(--body);
    font-size: 1rem;
    line-height: 1.72;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
}

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

a { color: inherit; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
a:hover { color: var(--accent); }

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

/* ...except where the focus was not the reader's doing. Routes.razor renders
   <FocusOnNavigate Selector="h1" />, so Blazor gives the page's heading
   tabindex="-1" and calls focus() on every navigation - which is right for a
   screen reader, since it announces the new page. But with no prior click the
   browser treats that as keyboard focus, so :focus-visible matches and draws a
   ring around a heading nobody focused: on the front page, a box around the
   couple's names on load.

   Only elements with tabindex="-1" are exempted. They are unreachable by Tab
   by definition, so this can never swallow the ring on a real control; a link,
   a button or an input keeps it. The heading itself is matched too, in case a
   future Blazor moves focus without setting the attribute. */
h1:focus-visible,
[tabindex="-1"]:focus-visible {
    outline: none;
}

/* ---------- Shell ------------------------------------------------------- */

.sheet {
    max-width: var(--measure);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.skip-link {
    position: absolute; left: -9999px;
    background: var(--ink); color: var(--newsprint);
    padding: .6rem 1rem; z-index: 100; font-family: var(--label);
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ---------- Masthead ---------------------------------------------------- */

.masthead {
    padding-top: clamp(1.5rem, 4vw, 2.75rem);
    text-align: center;
}

.masthead__flag {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--ink-soft);
    padding-bottom: .5rem;
    border-bottom: var(--rule-hair);
}
.masthead__flag span:nth-child(2) { text-align: center; }
.masthead__flag span:last-child { text-align: right; }

.masthead__title {
    font-family: var(--display);
    font-weight: 900;
    font-size: clamp(3.1rem, 13vw, 9.5rem);
    line-height: .86;
    letter-spacing: -0.02em;
    margin: clamp(.9rem, 2.5vw, 1.6rem) 0 clamp(.6rem, 2vw, 1rem);
    /* Optical tightening of the ampersand pair */
    font-feature-settings: "liga" 1, "kern" 1;
}
.masthead__title .amp {
    font-style: italic;
    font-weight: 400;
    padding-inline: .06em;
}

.masthead__dateline {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: .55rem 1.1rem;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-soft);
    padding: .7rem 0;
    border-top: var(--rule-thin);
    border-bottom: var(--rule-thick);
}
.masthead__dateline .dot { color: var(--ink-ghost); }

/* ---------- Navigation -------------------------------------------------- */

.rag {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--newsprint);
    border-bottom: var(--rule-thin);
    box-shadow: 0 1px 0 rgba(23,21,15,.06), 0 6px 14px -12px rgba(23,21,15,.5);
}

.rag__inner {
    max-width: var(--measure);
    margin-inline: auto;
    padding: 0 var(--gutter);
    display: flex;
    align-items: center;
    /* `center` is UNSAFE alignment: when the links overflow, the excess is split
       across both edges and the start edge sits at a negative scroll offset the
       browser will not let you reach — the first two links become permanently
       unreachable on a phone. `safe center` centres only when it fits; the
       unprefixed declaration above it is the fallback for older engines. */
    justify-content: flex-start;
    justify-content: safe center;
    gap: clamp(.75rem, 2.4vw, 2rem);
    overflow-x: auto;
    scrollbar-width: none;
}
.rag__inner::-webkit-scrollbar { display: none; }

.rag__link {
    flex: 0 0 auto;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--ink-soft);
    padding: .85rem .1rem;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
    transition: color .15s ease, border-color .15s ease;
}
.rag__link:hover,
.rag__link[aria-current="page"] {
    color: var(--ink);
    border-bottom-color: var(--accent);
}

/* ---------- Section heads ----------------------------------------------- */

.section {
    padding-block: clamp(3.25rem, 9vw, 6rem);
    border-top: var(--rule-hair);
}
.section:first-of-type { border-top: 0; }

/* ---------- Grid & gallery ---------------------------------------------- */

.grid {
    display: grid;
    gap: clamp(1.75rem, 5vw, 3.25rem);
    align-items: start;
}
@media (min-width: 52rem) {
    .grid--2 { grid-template-columns: 1fr 1fr; }
    .grid--3 { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- Caderno de viagem: a página da lua de mel -------------------- */

.foto-lua { margin: 0 0 clamp(1.2rem, 3vw, 2rem); }
.foto-lua--capa { margin-top: clamp(1.5rem, 4vw, 2.5rem); }

/* The five numbers that make the trip legible before any of the prose. */
.numeros {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0;
    margin: clamp(1.5rem, 4vw, 2.5rem) 0 0;
    border-top: var(--rule-thick);
    border-bottom: var(--rule-thick);
}
@media (min-width: 46rem) {
    .numeros { grid-template-columns: repeat(5, 1fr); }
}
.numeros > div {
    padding: .9rem .5rem;
    text-align: center;
    border-right: var(--rule-hair);
}
.numeros > div:last-child { border-right: 0; }
.numeros dt {
    font-family: var(--label);
    font-size: .6rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
    order: 2;
}
.numeros dd {
    font-family: var(--display);
    font-size: clamp(1.6rem, 5vw, 2.4rem);
    font-weight: 700;
    line-height: 1;
    margin: 0 0 .3rem;
    font-variant-numeric: lining-nums;
}
.numeros > div { display: flex; flex-direction: column; }

/* The ask: three groups of real line items, priced like a menu. */
.grupo-lua { margin-bottom: clamp(1.6rem, 4vw, 2.4rem); }
.grupo-lua__dek {
    font-style: italic;
    color: var(--ink-soft);
    max-width: var(--measure-text);
    margin: .3rem 0 .9rem;
}

.linha-roteiro { list-style: none; margin: 0; padding: 0; }
.linha-roteiro li {
    display: flex;
    align-items: baseline;
    gap: .6rem;
    padding: .55rem 0;
    border-top: var(--rule-hair);
}
.linha-roteiro li:last-child { border-bottom: var(--rule-hair); }

/* A dotted leader, the way a menu or an index sets a price against a name. */
.linha-roteiro__nome { flex: 0 1 auto; }
.linha-roteiro li::after {
    content: "";
    flex: 1 1 auto;
    border-bottom: 1px dotted var(--ink-ghost);
    transform: translateY(-.25em);
    min-width: 1.5rem;
}
.linha-roteiro__valor {
    order: 3;
    font-family: var(--label);
    font-size: .8rem;
    letter-spacing: .06em;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.linha-roteiro .tomada { color: var(--ink-faint); }
.linha-roteiro .tomada .linha-roteiro__nome { text-decoration: line-through; }

/* ---------- Embedded video ---------------------------------------------- */

.video {
    margin: 0 0 clamp(1.5rem, 4vw, 2.5rem);
}
/* On a page of its own the figure is the last thing in the sheet, and its
   bottom margin would stack on top of the section's own padding. */
.section > .video:last-child { margin-bottom: 0; }

.video__frame {
    border: var(--rule-thin);
    padding: .4rem;
    background: var(--newsprint-deep);
}

.video__frame iframe {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    height: auto;
    border: 0;
    background: var(--ink);
}

/* Justified gallery: each row is packed server-side so its aspect ratios add
   up to roughly one width, then flex-grow turns those ratios into widths. Every
   photo in a row lands on the same height and the row ends flush with the margin
   — column masonry could not do that, because balancing four tiles across three
   columns leaves one of them empty. */
.gallery {
    display: flex;
    flex-direction: column;
    gap: clamp(.5rem, 1.4vw, .9rem);
}

.gallery__linha {
    display: flex;
    gap: clamp(.5rem, 1.4vw, .9rem);
}

.gallery__item {
    /* flex-grow is set per photo from its ratio; basis 0 so the whole row width
       is shared out by ratio alone. */
    flex: 0 1 0;
    min-width: 0;
}
.gallery__item figure { margin: 0; }

/* A slow push in on hover — the only thing on the page that moves. */
.gallery .figure__frame { overflow: hidden; }
.gallery .figure__frame img { transition: transform .5s cubic-bezier(.2, .7, .3, 1); }
.gallery figure:hover .figure__frame img { transform: scale(1.04); }

/* On a phone a justified row of three would be three stamps. Below the
   breakpoint the rows dissolve — display: contents — and the photos pack as a
   two-column masonry instead, where the tiles balance evenly. */
@media (max-width: 51.999rem) {
    .gallery {
        display: block;
        columns: 2;
        column-gap: clamp(.5rem, 1.4vw, .9rem);
    }
    .gallery__linha { display: contents; }
    .gallery__item {
        break-inside: avoid;
        margin-bottom: clamp(.5rem, 1.4vw, .9rem);
    }
}

.section__head {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}
.section__head::before,
.section__head::after {
    content: "";
    flex: 1;
    border-top: var(--rule-thin);
}
/* The section rubric is a graphic device, not document structure. It renders as
   a <p> so it doesn't put an h2 ahead of the page's h1 in the heading outline;
   h2 stays matched so markup can migrate in any order. */
.section__head > :is(h2, p) {
    font-family: var(--label);
    font-size: clamp(.78rem, 2vw, .92rem);
    font-weight: 700;
    letter-spacing: .34em;
    text-transform: uppercase;
    margin: 0;
    text-align: center;
    text-indent: .34em; /* compensate for trailing letterspace */
}

/* A rubric that opens a run *inside* a section instead of the section itself.
   It has no section padding above it, so it carries its own. */
.section__head--mid { margin-top: clamp(2.5rem, 7vw, 4.5rem); }

/* Headline hierarchy inside a section */
.headline {
    font-family: var(--display);
    font-weight: 700;
    line-height: 1.06;
    letter-spacing: -0.015em;
    margin: 0 0 .6rem;
    font-size: clamp(1.9rem, 5vw, 3.2rem);
    text-wrap: balance;
}
.headline--sm { font-size: clamp(1.35rem, 3.2vw, 1.9rem); }

/* A headline that runs straight into a facts list, with no standfirst between
   them, needs the air the paragraph used to supply. */
:is(.headline, .headline--sm) + .facts { margin-top: 1.75rem; }

.kicker {
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 .5rem;
}

.standfirst {
    font-family: var(--body);
    font-style: italic;
    font-size: clamp(1rem, 2.4vw, 1.18rem);
    line-height: 1.6;
    color: var(--ink-soft);
    max-width: 46rem;
    margin: 0 auto 1.75rem;
    text-align: center;
    text-wrap: pretty;
}

.byline {
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
    padding-block: .5rem;
    border-block: var(--rule-hair);
    margin-bottom: 1.5rem;
}

/* ---------- Columns ----------------------------------------------------- */

.columns {
    column-gap: clamp(1.75rem, 4vw, 3rem);
    column-rule: var(--rule-faint);
    text-align: justify;
    hyphens: auto;
}
.columns--2 { column-count: 1; }
.columns--3 { column-count: 1; }

@media (min-width: 46rem) {
    .columns--2 { column-count: 2; }
    .columns--3 { column-count: 2; }
}
@media (min-width: 68rem) {
    .columns--3 { column-count: 3; }
}

.columns p { margin: 0 0 1.05em; }
.columns p:last-child { margin-bottom: 0; }
/* Never orphan a heading at the foot of a column */
.columns h3, .columns .headline--sm { break-after: avoid; break-inside: avoid; }
/* Justification belongs to the body copy, which has enough words per line to
   absorb the slack. A three-word question set in display type has two gaps to
   stretch, so a justified first line comes out as islands. Ragged right, and
   balanced so the turn falls where the sentence wants it. */
.columns h2, .columns h3, .columns h4,
.columns .headline, .columns .headline--sm,
.columns figcaption {
    text-align: left;
    text-wrap: balance;
    hyphens: manual;
}
.columns figure, .columns .box { break-inside: avoid; }

.dropcap::first-letter {
    font-family: var(--display);
    font-weight: 900;
    float: left;
    font-size: 3.9em;
    line-height: .82;
    padding: .06em .09em 0 0;
    margin-top: .04em;
    color: var(--ink);
}

.prose { max-width: var(--measure-text); }
.prose--center { margin-inline: auto; }

/* ---------- Figures ----------------------------------------------------- */

figure { margin: 0 0 1.5rem; }

.figure__frame {
    border: var(--rule-thin);
    padding: .4rem;
    background: var(--newsprint-deep);
}
.figure__frame img {
    /* Photos stay in colour. A wholesale grayscale filter reads cold against
       warm paper, and it would flatten the genuinely black-and-white frames
       into the same look as everything else. Just a whisper of print contrast. */
    filter: contrast(1.03) saturate(.97);
    width: 100%;
}

figcaption {
    font-family: var(--body);
    font-style: italic;
    font-size: .82rem;
    line-height: 1.5;
    color: var(--ink-soft);
    padding-top: .55rem;
    border-bottom: var(--rule-hair);
    padding-bottom: .55rem;
}
figcaption .credit {
    font-family: var(--label);
    font-style: normal;
    font-size: .66rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--ink-faint);
    display: block;
    margin-top: .25rem;
}

/* Placeholder plate for photos that don't exist yet */
.plate {
    aspect-ratio: 4 / 3;
    display: grid;
    place-items: center;
    background-color: var(--newsprint-edge);
    background-image:
        repeating-linear-gradient(45deg,
            rgba(23,21,15,.055) 0 2px, transparent 2px 5px);
    color: var(--ink-faint);
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    text-align: center;
    padding: 1rem;
}

/* ---------- Boxed items (sidebars, facts) ------------------------------- */

.box {
    border: var(--rule-thin);
    padding: clamp(1.1rem, 3vw, 1.6rem);
    background: rgba(255,255,255,.34);
}
.box--rule-top { border: 0; border-top: var(--rule-thick); padding-inline: 0; }

.box__label {
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 .6rem;
}

/* Fact list — "QUANDO / ONDE / TRAJE" */
.facts {
    display: grid;
    gap: 0;
    margin: 0;
}
.facts > div {
    display: grid;
    grid-template-columns: minmax(6.5rem, 8rem) 1fr;
    gap: 1rem;
    padding: .85rem 0;
    border-bottom: var(--rule-hair);
    align-items: baseline;
}
.facts > div:first-child { border-top: var(--rule-hair); }
/* On a phone the label column takes a third of the measure and shreds the value
   into four-word lines. Below 40rem the term sits above what it names. */
@media (max-width: 39.99rem) {
    .facts > div { grid-template-columns: 1fr; gap: .3rem; }
}
.facts dt {
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.facts dd { margin: 0; }
.facts dd strong { font-weight: 700; }
.facts dd small {
    display: block;
    font-size: .82rem;
    color: var(--ink-soft);
    font-style: italic;
}

/* ---------- Countdown ticker -------------------------------------------- */

.ticker {
    border-block: var(--rule-thick);
    padding-block: clamp(1rem, 3vw, 1.5rem);
    display: flex;
    justify-content: center;
    gap: clamp(1.1rem, 5vw, 3.5rem);
    text-align: center;
}
.ticker__unit { min-width: 3.5rem; }
.ticker__num {
    display: block;
    font-family: var(--display);
    font-weight: 900;
    font-size: clamp(2rem, 7vw, 3.4rem);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}
.ticker__lbl {
    display: block;
    font-family: var(--label);
    font-size: .62rem;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-top: .45rem;
}

/* ---------- Um presente: ficha e cupom num cartao so --------------------- */

/* A pagina de um presente e um cartao unico: gravura a esquerda, e a direita
   os dados seguidos do cupom de resposta, tudo dentro do mesmo tracejado.
   Empresta o desenho do .coupon, inclusive o entalhe, porque continua sendo um
   cupom — so que agora com a ficha do presente na cabeca. */
.presente-card {
    border: 2px dashed var(--ink);
    padding: clamp(1.25rem, 4vw, 2rem);
    background: rgba(255,255,255,.42);
    position: relative;
    margin-top: 1.5rem;
    display: grid;
    gap: clamp(1.25rem, 3vw, 2rem);
    align-items: start;
}
@media (min-width: 52rem) {
    .presente-card { grid-template-columns: minmax(0, 18rem) 1fr; }
}
.presente-card::before {
    content: "";
    position: absolute;
    top: -2px;
    left: 1.4rem;
    width: 2.25rem;
    height: 4px;
    background: var(--newsprint);
}

.presente-card__gravura {
    width: 100%;
    max-width: 18rem;
    margin: 0;
}

.presente-card .facts { margin-bottom: 1.25rem; }

/* Dentro do cartao o cupom nao tem moldura nem largura propria: seria moldura
   sobre moldura, e a largura maxima o centrava no meio da gravura ao lado.
   Vira so o trecho de baixo da coluna. A especificidade de dois niveis e de
   proposito, para vencer .coupon onde quer que ele esteja no arquivo. */
.presente-card .coupon {
    border: 0;
    background: none;
    padding: 0;
    max-width: none;
    margin: 0;
}
.presente-card .coupon::before { content: none; }
.presente-card .coupon__head {
    text-align: left;
    padding-bottom: .6rem;
    margin-bottom: 1rem;
}
.presente-card .coupon__head h3 { font-size: clamp(1.15rem, 2.6vw, 1.45rem); }

/* ---------- Reply coupon (RSVP) ----------------------------------------- */

.coupon {
    border: 2px dashed var(--ink);
    padding: clamp(1.25rem, 4vw, 2.25rem);
    background: rgba(255,255,255,.42);
    position: relative;
    max-width: 44rem;
    margin-inline: auto;
}
/* A short break in the dashed rule, where a printed coupon would carry its
   "destaque aqui" notch. */
.coupon::before {
    content: "";
    position: absolute;
    top: -2px;
    left: 1.4rem;
    width: 2.25rem;
    height: 4px;
    background: var(--newsprint);
}

.coupon__head {
    text-align: center;
    padding-bottom: 1rem;
    margin-bottom: 1.4rem;
    border-bottom: var(--rule-hair);
}
.coupon__head h3 {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(1.4rem, 4vw, 2rem);
    margin: 0 0 .3rem;
}
.coupon__head p {
    margin: 0;
    font-size: .84rem;
    font-style: italic;
    color: var(--ink-soft);
}

/* ---------- Classifieds (gift list) ------------------------------------- */

/* A grade, e nao colunas: numa coluna o anuncio flui e cada um termina numa
   altura, o que desalinha os precos. Na grade as celulas de uma mesma linha
   esticam juntas, e o preco de cada uma desce para a base. */
.classifieds {
    display: grid;
    grid-template-columns: 1fr;
    column-gap: clamp(1.5rem, 3.5vw, 2.5rem);
    border-bottom: var(--rule-hair);
}
@media (min-width: 44rem) { .classifieds { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .classifieds { grid-template-columns: repeat(3, 1fr); } }

.ad {
    padding: 1.1rem 0;
    border-top: var(--rule-hair);
    display: flex;
    flex-direction: column;
    height: 100%;
    text-decoration: none;
}
.ad:hover .ad__title { color: var(--accent); }

/* The "cut" — the square block a classified ad was always set around.
   Mounted like the article photos: hairline rule, a margin of paper, image within. */
.ad__cut {
    aspect-ratio: 1 / 1;
    border: var(--rule-thin);
    padding: .35rem;
    margin-bottom: .9rem;
    background: var(--newsprint-deep);
    overflow: hidden;
}

.ad__cut img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    filter: contrast(1.03) saturate(.97);
    transition: transform .5s ease;
}
.ad:hover .ad__cut img { transform: scale(1.045); }

.ad--taken .ad__cut img { filter: grayscale(1) opacity(.55); }

/* Larger cut for the single-item page. */
.cut--lg {
    width: min(20rem, 80vw);
    margin-bottom: 0;
}

.ad__title {
    font-family: var(--display);
    font-weight: 700;
    font-size: 1.14rem;
    line-height: 1.25;
    margin: 0 0 .35rem;
    transition: color .15s ease;
}
.ad__body {
    font-size: .85rem;
    line-height: 1.6;
    color: var(--ink-soft);
    margin: 0 0 .6rem;
}
.ad__meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-top: auto;
    padding-top: .35rem;
    gap: .75rem;
    font-family: var(--label);
    font-size: .74rem;
    letter-spacing: .12em;
    text-transform: uppercase;
}
.ad__price { font-weight: 700; letter-spacing: .06em; }
.ad__stock { color: var(--ink-faint); }
.ad--taken { opacity: .48; }
.ad--taken .ad__title { text-decoration: line-through; }

/* ---------- Map --------------------------------------------------------- */

/* Mounted like the article photos so it reads as part of the paper, not a widget. */
.map {
    border: var(--rule-thin);
    padding: .4rem;
    background: var(--newsprint-deep);
    margin-bottom: 1.5rem;
}
.map iframe {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    border: 0;
    /* Google's tiles are a cold blue-grey; warm and mute them onto the newsprint. */
    filter: sepia(.28) saturate(.72) contrast(1.02) brightness(1.02);
}
@media (min-width: 52rem) {
    .map iframe { aspect-ratio: 16 / 10; }
}

/* ---------- PIX panel --------------------------------------------------- */

.pix {
    display: grid;
    gap: clamp(1.25rem, 4vw, 2rem);
    align-items: start;
}
@media (min-width: 46rem) { .pix { grid-template-columns: auto 1fr; } }

.pix__qr {
    border: var(--rule-thin);
    padding: .5rem;
    background: #fff;
    width: min(15rem, 60vw);
    justify-self: center;
}
.pix__qr img { width: 100%; image-rendering: pixelated; }

.pix__key {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: .82rem;
    word-break: break-all;
    background: var(--newsprint-deep);
    border: var(--rule-faint);
    padding: .7rem .85rem;
    margin: .5rem 0 .85rem;
    line-height: 1.5;
}

/* ---------- Forms ------------------------------------------------------- */

.field { margin-bottom: 1.2rem; }

.field > label,
.fieldset__legend {
    display: block;
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: .45rem;
}

.input,
select.input,
textarea.input {
    width: 100%;
    font-family: var(--body);
    font-size: 1rem;
    color: var(--ink);
    background: rgba(255,255,255,.6);
    border: 0;
    border-bottom: var(--rule-thin);
    padding: .6rem .2rem;
    border-radius: 0;
    transition: background .15s ease, border-color .15s ease;
}
.input:focus {
    outline: none;
    background: #fff;
    border-bottom-color: var(--accent);
    box-shadow: 0 1px 0 0 var(--accent);
}
textarea.input { min-height: 5.5rem; resize: vertical; line-height: 1.6; }
select.input { appearance: none; background-image: none; cursor: pointer; padding-right: 1.5rem; }

.input--code {
    font-family: var(--label);
    font-size: clamp(1.3rem, 5vw, 1.9rem);
    letter-spacing: .32em;
    text-transform: uppercase;
    text-align: center;
    padding: .7rem .2rem;
}

.hint {
    font-size: .78rem;
    font-style: italic;
    color: var(--ink-faint);
    margin: .4rem 0 0;
}
/* Invitation URLs get echoed back here and have no natural break points. */
.hint strong { overflow-wrap: anywhere; }

/* Invite codes and request ids appear in <code>; without this they fall back to
   the UA monospace default at a size that fights the serif around it. */
code {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: .88em;
    overflow-wrap: anywhere;
}

/* Radio pair styled as a stamped choice */
.choice {
    display: inline-flex;
    border: var(--rule-thin);
}
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice label {
    font-family: var(--label);
    font-size: .74rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    padding: .5rem 1.05rem;
    cursor: pointer;
    background: transparent;
    color: var(--ink-soft);
    transition: background .12s ease, color .12s ease;
    user-select: none;
}
.choice label + input + label { border-left: var(--rule-thin); }
.choice input:checked + label { background: var(--ink); color: var(--newsprint); }
.choice input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Guest row on the coupon */
.guest-row {
    padding: 1rem 0;
    border-bottom: var(--rule-hair);
}
.guest-row:first-of-type { border-top: var(--rule-hair); }
.guest-row__top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
}
.guest-row__name {
    font-family: var(--display);
    font-weight: 700;
    font-size: 1.14rem;
    margin: 0;
}
/* Standalone: the admin tables use .tag outside a guest row, where a descendant
   selector left the status reading as undifferentiated body text. */
.tag {
    display: inline-block;
    font-family: var(--label);
    font-size: .6rem;
    font-weight: 400;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-faint);
    border: var(--rule-hair);
    padding: .1rem .4rem;
    white-space: nowrap;
}

/* Inside a guest row it's a quiet annotation on the name, not a badge. */
.guest-row__name .tag {
    border: 0;
    padding: 0;
    vertical-align: .22em;
    margin-left: .5rem;
}
/* One field per guest now that the meal picker is gone. It used to be two
   columns - a narrow one sized for the plate dropdown, a wide one for the
   dietary note. Left as-is, the surviving field inherited the narrow column
   and sat at 14rem with its placeholder clipped. */
.guest-row__extra {
    margin-top: .9rem;
    display: grid;
    gap: .9rem;
}

/* ---------- Buttons ----------------------------------------------------- */

.btn {
    display: inline-block;
    font-family: var(--label);
    font-size: .76rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    padding: .82rem 1.9rem;
    border: var(--rule-thin);
    background: var(--ink);
    color: var(--newsprint);
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    border-radius: 0;
    transition: background .15s ease, color .15s ease;
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: var(--newsprint); }
.btn:disabled { opacity: .4; cursor: not-allowed; }

.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: var(--newsprint); border-color: var(--ink); }

.btn--block { display: block; width: 100%; }

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    justify-content: center;
    margin-top: 1.5rem;
}

/* ---------- Notices ----------------------------------------------------- */

.notice {
    border-left: 3px solid var(--accent);
    background: rgba(140,47,47,.05);
    padding: .9rem 1.1rem;
    margin-bottom: 1.5rem;
    font-size: .9rem;
}
.notice--ok { border-left-color: var(--ink); background: rgba(23,21,15,.045); }

/* The stamp carries the whole "it worked" message on the confirmation screen —
   the headline that used to say it in words is gone — so it is sized to be the
   thing you see first, not a detail beside a heading. It scales with the
   viewport so the tilt never pushes it past the edge on a phone. */
.stamp {
    display: inline-block;
    border: 3px solid var(--accent);
    color: var(--accent);
    font-family: var(--label);
    font-size: clamp(1.15rem, 5vw, 1.9rem);
    font-weight: 600;
    letter-spacing: .2em;
    text-transform: uppercase;
    line-height: 1.1;
    padding: .75em 1.1em;
    margin-block: clamp(.5rem, 2vw, 1rem);
    transform: rotate(-3.5deg);

    /* The letterspacing pads the right side of the last letter; without this the
       word sits visibly off-centre inside its own box. */
    text-indent: .2em;
}

/* ---------- Tables (admin) ---------------------------------------------- */

.table-wrap { overflow-x: auto; border-block: var(--rule-thin); }

table.sheet-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .88rem;
}
table.sheet-table th {
    font-family: var(--label);
    font-size: .66rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-faint);
    text-align: left;
    padding: .7rem .8rem;
    border-bottom: var(--rule-thin);
    white-space: nowrap;
}
table.sheet-table td {
    padding: .65rem .8rem;
    border-bottom: var(--rule-hair);
    vertical-align: top;
}
table.sheet-table tr:last-child td { border-bottom: 0; }
table.sheet-table tbody tr:hover { background: rgba(23,21,15,.028); }

.tally {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
    gap: 0;
    border: var(--rule-thin);
    margin-bottom: 2rem;
}
.tally > div {
    padding: 1rem;
    text-align: center;
    border-right: var(--rule-hair);
}
.tally > div:last-child { border-right: 0; }
.tally__num {
    display: block;
    font-family: var(--display);
    font-weight: 900;
    /* Currency totals ("R$ 12.500,00") are long. Fixed at 2rem they overflowed
       a 120px tile and pushed the admin page into horizontal scroll on a phone. */
    font-size: clamp(1.25rem, 6vw, 2rem);
    line-height: 1.1;
    overflow-wrap: anywhere;
    font-variant-numeric: tabular-nums;
}
.tally__lbl {
    display: block;
    font-family: var(--label);
    font-size: .62rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-top: .4rem;
}

/* ---------- Footer ------------------------------------------------------ */

.colophon {
    border-top: var(--rule-thick);
    margin-top: clamp(2.5rem, 7vw, 4.5rem);
    padding-block: 2rem 3rem;
    text-align: center;
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-faint);
    line-height: 2;
}

/* ---------- Utilities --------------------------------------------------- */

.center { text-align: center; }
.stack > * + * { margin-top: 1.1rem; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* Blazor's built-in error UI, restyled so it isn't a blue Bootstrap bar */
#blazor-error-ui {
    position: fixed;
    bottom: 0; left: 0; right: 0;
    display: none;
    background: var(--ink);
    color: var(--newsprint);
    padding: .9rem 1.2rem;
    font-family: var(--label);
    font-size: .78rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; float: right; padding-left: 1rem; }

.validation-message {
    display: block;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .1em;
    color: var(--accent);
    margin-top: .35rem;
}

/* ---------- Convite: the invitation card -------------------------------- */

.convite {
    position: relative;
    max-width: 46rem;
    margin-inline: auto;
    padding: clamp(2.5rem, 8vw, 5rem) clamp(1.4rem, 6vw, 4rem);
    background: var(--newsprint-deep);
    border: var(--rule-thin);
    text-align: center;
}

/* An engraved card carries two frames: the rule, and a hairline set inside it. */
.convite::before {
    content: "";
    position: absolute;
    inset: .5rem;
    border: var(--rule-hair);
    pointer-events: none;
}

.convite__pais {
    font-family: var(--display);
    font-size: clamp(1rem, 2.6vw, 1.2rem);
    line-height: 1.5;
    margin: 0 0 1.1rem;
}
.convite__pais span { display: block; }

.convite__intro,
.convite__hora {
    font-family: var(--label);
    font-size: clamp(.7rem, 1.8vw, .8rem);
    letter-spacing: .24em;
    text-transform: uppercase;
    line-height: 2.1;
    color: var(--ink-soft);
    margin: 0;
}

/* Engraved type is light and open — the weight the masthead uses would shout here. */
.convite__nomes {
    font-family: var(--display);
    font-weight: 400;
    font-size: clamp(2.7rem, 11vw, 5rem);
    line-height: 1.02;
    letter-spacing: .005em;
    margin: clamp(1rem, 3vw, 1.6rem) 0 0;
}
.convite__nomes .amp {
    display: block;
    font-style: italic;
    font-size: .42em;
    color: var(--ink-faint);
    margin-block: .12em;
}

/* The verse sits between the invitation line and the names. Italic serif rather
   than the intro's letterspaced caps: scripture read in small caps looks like a
   heading, and this is meant to be read as a sentence. The reference goes on its
   own line, small and quiet, so it never competes with the names below. */
.convite__versiculo {
    font-family: var(--display);
    font-style: italic;
    font-size: clamp(.9rem, 2.4vw, 1.05rem);
    line-height: 1.5;
    color: var(--ink-soft);
    text-wrap: balance;
    margin: clamp(.8rem, 2vw, 1.1rem) 0 0;
}

.convite__referencia {
    display: block;
    font-family: var(--label);
    font-style: normal;
    font-size: .62em;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-top: .55em;
}

/* The verse opens the card, so what follows it needs the breathing room that
   .convite__intro and .convite__pais do not carry on their own (they were
   written to sit directly under the seal). */
.convite__versiculo + .convite__intro,
.convite__versiculo + .convite__pais {
    margin-top: clamp(1rem, 2.6vw, 1.5rem);
}

/* On the full-screen card every line competes for the same vertical space. */
.convite--capa .convite__versiculo {
    font-size: clamp(.82rem, 2.2vw, .98rem);
    margin-top: clamp(.6rem, 1.6vh, 1rem);
}

.convite__ornamento {
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 17rem;
    margin: clamp(1.4rem, 4vw, 2.2rem) auto;
}
.convite__ornamento::before,
.convite__ornamento::after {
    content: "";
    flex: 1;
    border-top: var(--rule-hair);
}
.convite__ornamento span {
    font-size: .5rem;
    line-height: 1;
    color: var(--ink-ghost);
}

.convite__data {
    display: inline-flex;
    align-items: center;
    border-block: var(--rule-thin);
    padding-block: .85rem;
    margin: 0;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--ink-soft);
}
.convite__data-lado {
    padding-inline: clamp(.9rem, 3vw, 1.6rem);
    line-height: 1.6;
}
.convite__dia {
    font-family: var(--display);
    font-size: clamp(2.6rem, 9vw, 3.6rem);
    font-weight: 500;
    letter-spacing: 0;
    line-height: 1;
    color: var(--ink);
    padding-inline: clamp(.9rem, 3vw, 1.6rem);
    border-inline: var(--rule-hair);
    font-variant-numeric: lining-nums;
}

.convite__hora { margin-top: 1.1rem; }

.convite__local {
    font-family: var(--display);
    font-size: clamp(1.6rem, 5vw, 2.3rem);
    line-height: 1.2;
    margin: clamp(1.4rem, 4vw, 2.1rem) 0 .35rem;
}
.convite__cidade {
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0;
}

.convite__notas {
    display: grid;
    gap: 1.1rem;
    margin: 0;
    padding-top: clamp(1.1rem, 3vw, 1.6rem);
    border-top: var(--rule-hair);
}
@media (min-width: 40rem) {
    .convite__notas { grid-template-columns: repeat(3, 1fr); gap: 1.6rem; }
}
.convite__notas dt {
    font-family: var(--label);
    font-size: .62rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: .3rem;
}
.convite__notas dd {
    margin: 0;
    font-size: .92rem;
    line-height: 1.5;
}

/* ---------- Palco: the card, full screen, on the photograph ------------- */

.palco {
    position: relative;
    display: grid;
    grid-template-rows: 1fr auto;
    justify-items: center;
    /* svh, not vh: on phones the address bar must not push the card off-screen. */
    min-height: calc(100svh - var(--rag-h, 3.2rem));
    padding: clamp(.8rem, 2vh, 2rem) var(--gutter)
             calc(clamp(.7rem, 1.8vh, 1.4rem) + env(safe-area-inset-bottom));
    overflow: hidden;
    isolation: isolate;
}

.palco__fundo {
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--ink);
}

.palco__fundo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 38%;
    animation: palco-entra 1.6s cubic-bezier(.2, .7, .3, 1) both,
               palco-respira 30s ease-in-out 1.6s infinite alternate;
}

/* Ink over the photograph: the card has to read as paper laid on top of it,
   which only happens if the ground behind it is darker than the paper. */
.palco__fundo::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        radial-gradient(120% 80% at 50% 45%, rgba(28, 26, 21, .38), rgba(28, 26, 21, .78)),
        linear-gradient(rgba(28, 26, 21, .3), rgba(28, 26, 21, .5));
}

/* ---- The card itself ---- */

.convite--capa {
    align-self: center;
    width: 100%;
    max-width: 33rem;
    padding: clamp(1.3rem, 3vh, 2.6rem) clamp(1.2rem, 5vw, 2.75rem);
    box-shadow: 0 34px 70px -30px rgba(12, 10, 6, .85),
                0 4px 18px -8px rgba(12, 10, 6, .45);
    /* The whole card arrives first; its contents then settle onto it. */
    animation: cartao-entra 1.15s cubic-bezier(.2, .75, .3, 1) both;
}

/* Every line rides the same rise; --d is its place in the queue. */
.convite--capa > *,
.convite--capa .convite__letra {
    animation: cartao-linha .8s cubic-bezier(.2, .7, .3, 1) both;
    animation-delay: calc(.45s + var(--d, 0) * 45ms);
}

/* ---- Monogram, stamped ---- */

.convite__selo {
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(2.6rem, min(8vw, 6vh), 3.4rem);
    aspect-ratio: 1;
    line-height: 1;
    margin: 0 auto clamp(.8rem, 2.2vh, 1.5rem);
    border: var(--rule-hair);
    border-radius: 50%;
    font-family: var(--display);
    font-size: clamp(.95rem, 3vw, 1.15rem);
    letter-spacing: .02em;
    color: var(--ink-soft);
    /* Overrides the shared rise: a seal is pressed down, not floated up. */
    animation: selo-carimbo .75s cubic-bezier(.3, 1.5, .5, 1) both !important;
    animation-delay: .3s !important;
}
.convite__selo .amp {
    font-style: italic;
    color: var(--accent);
    padding-inline: .06em;
}

.convite--capa .convite__intro {
    font-size: clamp(.64rem, 1.9vw, .78rem);
    letter-spacing: .16em;
    line-height: 1.95;
}

.convite--capa .convite__data { padding-block: clamp(.5rem, 1.2vh, .8rem); }
.convite--capa .convite__hora { margin-top: clamp(.7rem, 1.8vh, 1.1rem); }
.convite--capa .convite__ornamento { margin-block: clamp(.7rem, 1.8vh, 2rem); }
.convite--capa .convite__local {
    font-size: clamp(1.45rem, min(4.6vw, 3.4vh), 2.1rem);
    margin-top: clamp(.9rem, 2.4vh, 1.8rem);
}

/* ---- Names, letter by letter ---- */

.convite--capa .convite__nomes {
    font-size: clamp(2.3rem, min(12vw, 9vh), 4.4rem);
    animation: none;
}
.convite__palavra { display: block; }
.convite__letra {
    display: inline-block;
    animation-name: cartao-letra;
    animation-duration: .9s;
}
.convite--capa .convite__nomes .amp {
    animation: cartao-amp .9s cubic-bezier(.34, 1.45, .5, 1) both;
    animation-delay: calc(.45s + var(--d, 0) * 45ms);
}

/* ---- Foot of the card: the R.S.V.P. line and the one button ---- */

.convite__pe {
    margin-top: clamp(.9rem, 2.4vh, 1.8rem);
    padding-top: clamp(.8rem, 2vh, 1.4rem);
    border-top: var(--rule-hair);
}
.convite__rsvp {
    font-family: var(--label);
    font-size: clamp(.58rem, 1.9vw, .66rem);
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 clamp(.8rem, 2.5vw, 1.1rem);
    line-height: 1.9;
}
.convite__rsvp .dot { color: var(--ink-ghost); }
.convite__pe .btn { width: 100%; max-width: 20rem; }

/* ---- Scroll cue ---- */

.palco__rolar {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-top: clamp(.6rem, 1.6vh, 1.2rem);
    padding: .5rem;
    font-family: var(--label);
    font-size: .6rem;
    letter-spacing: .28em;
    text-transform: uppercase;
    color: var(--newsprint);
    text-decoration: none;
    text-shadow: 0 1px 8px rgba(12, 10, 6, .6);
    animation: cartao-linha .8s ease both 1.7s;
}
.palco__rolar:hover { color: var(--newsprint); opacity: .75; }

.palco__seta {
    width: .5rem;
    height: .5rem;
    border-right: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
    transform: rotate(45deg);
    animation: palco-seta 1.9s ease-in-out infinite;
}

/* Short screens — landscape phones, small laptops — get a denser card rather
   than a card that scrolls before its own R.S.V.P. line. */
@media (max-height: 46rem) {
    .convite--capa { padding-block: 1.2rem; max-width: 30rem; }
    .convite__selo { display: none; }
    .convite--capa .convite__nomes { font-size: clamp(2.1rem, 9vw, 3rem); }
    .convite--capa .convite__ornamento { margin-block: .7rem; }
    .convite--capa .convite__local { font-size: 1.4rem; margin-top: .9rem; }
}

/* ---- Keyframes ---- */

@keyframes cartao-entra {
    from { opacity: 0; transform: translateY(2.2rem) scale(.965); }
    to   { opacity: 1; transform: none; }
}

@keyframes cartao-linha {
    from { opacity: 0; transform: translateY(.9rem); }
    to   { opacity: 1; transform: none; }
}

@keyframes cartao-letra {
    from { opacity: 0; transform: translateY(.45em) rotate(3deg); filter: blur(5px); }
    to   { opacity: 1; transform: none; filter: none; }
}

@keyframes cartao-amp {
    from { opacity: 0; transform: scale(.4) rotate(-14deg); }
    to   { opacity: 1; transform: none; }
}

@keyframes selo-carimbo {
    from { opacity: 0; transform: scale(1.9); }
    60%  { opacity: 1; }
    to   { opacity: 1; transform: none; }
}

@keyframes palco-entra {
    from { opacity: 0; transform: scale(1.12); }
    to   { opacity: 1; transform: scale(1.06); }
}

/* Barely a zoom — enough that the photograph is alive, not enough to notice. */
@keyframes palco-respira {
    from { transform: scale(1.06); }
    to   { transform: scale(1); }
}

@keyframes palco-seta {
    0%, 100% { transform: rotate(45deg) translate(0, 0); }
    50%      { transform: rotate(45deg) translate(.18rem, .18rem); }
}

/* ---- Reveal on scroll ----
   Armed by site.js, never by the stylesheet: if the script fails to run, the
   details must still be on the page rather than invisible forever. */

[data-revelar].revelar {
    opacity: 0;
    transform: translateY(1.6rem);
    transition: opacity .8s ease, transform .8s cubic-bezier(.2, .7, .3, 1);
}
[data-revelar].revelar--visivel {
    opacity: 1;
    transform: none;
}
@media (prefers-reduced-motion: reduce) {
    [data-revelar].revelar { opacity: 1; transform: none; }
}

/* ---------- Print: it should actually print like a newspaper ------------ */

@media print {
    body { background: #fff; font-size: 10pt; }
    .rag, .btn, .btn-row, .coupon form { display: none !important; }
    .section { break-inside: auto; }
    a { text-decoration: none; }

    /* A heading that still holds focus would otherwise print its outline. */
    :focus-visible { outline: none; }

    /* The invitation is the one page meant to leave the screen as paper. The
       photograph behind it is screen furniture; the card is the artefact. */
    .palco { min-height: 0; padding: 0; }
    .palco__fundo, .palco__rolar, .convite__pe .btn { display: none !important; }
    .convite { background: #fff; max-width: none; break-inside: avoid; box-shadow: none; }

    /* Paper has no entrance to make, and a print job that catches the card
       mid-fade would come out of the tray half-inked. */
    .convite--capa, .convite--capa > *, .convite__letra, .convite__selo,
    .convite--capa .convite__nomes .amp {
        animation: none !important;
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
    }
    [data-revelar].revelar { opacity: 1 !important; transform: none !important; }
    .convite ~ .hint, .colophon { display: none !important; }
}

/* ---------- Admin: lista de convites ------------------------------------ */
/* A tabela original herdava os estilos da parte pública: botões de 3rem em
   maiúsculas espaçadas, a caixa monoespaçada da chave Pix para a URL, uma tag
   por convidado. Bonito numa página de convite, ilegível numa lista de 34
   linhas — cada convite ocupava meia tela e o código quebrava no meio.
   Aqui a régua é densidade: uma ficha por convite, três linhas, tudo visível
   sem rolar. */

.convites { display: grid; gap: .4rem; margin-top: 1.5rem; }

.conv {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .3rem .9rem;
    align-items: center;
    padding: .7rem .85rem;
    border: var(--rule-faint);
    background: rgba(255, 255, 255, .45);
}
.conv:hover { border-color: var(--ink-soft); }

.conv__nome {
    font-family: var(--display);
    font-size: 1.02rem;
    line-height: 1.25;
    margin: 0;
}

/* Linha de metadados: código, lugares, situação, abertura. */
.conv__meta {
    grid-column: 1;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .1rem .55rem;
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0;
}
.conv__meta > * { white-space: nowrap; }

/* O código é o que se dita por telefone: nunca deve quebrar no meio. */
.conv__codigo {
    font-family: var(--label);
    font-weight: 700;
    letter-spacing: .18em;
    color: var(--ink);
}

.conv__sim  { color: var(--accent); font-weight: 700; }
.conv__nao  { color: var(--ink-faint); }

/* Os nomes em texto corrido: uma etiqueta por pessoa virava um mosaico. */
.conv__gente {
    grid-column: 1;
    margin: 0;
    font-size: .82rem;
    line-height: 1.45;
    color: var(--ink-soft);
}
.conv__crianca { color: var(--ink-faint); font-style: italic; }

/* Ações compactas, à direita, ocupando as três linhas da ficha. */
.conv__acoes {
    grid-column: 2;
    grid-row: 1 / -1;
    display: flex;
    gap: .3rem;
    align-items: center;
}
.conv__btn {
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    padding: .35rem .6rem;
    border: var(--rule-faint);
    background: transparent;
    color: var(--ink-soft);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    line-height: 1.6;
}
.conv__btn:hover { background: var(--ink); color: var(--newsprint); border-color: var(--ink); }
.conv__btn--perigo:hover { background: var(--accent); border-color: var(--accent); color: var(--newsprint); }

@media (max-width: 40rem) {
    .conv { grid-template-columns: minmax(0, 1fr); }
    .conv__acoes { grid-column: 1; grid-row: auto; margin-top: .35rem; }
}

/* ---------- Dress code (/dress-code) ------------------------------------ */

/* A colour brief is the one page where the swatch has to be the real thing:
   a photograph of a green dress is a photograph of one light, not the colour.
   So the chips are painted, and the illustrations only carry the shape. */

.paleta {
    list-style: none;
    margin: 2rem 0 0;
    padding: 0;
    display: grid;
    gap: 1px;
    grid-template-columns: repeat(2, 1fr);
    background: var(--ink-ghost);
    border: var(--rule-thin);
}
@media (min-width: 46rem) {
    .paleta { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 62rem) {
    .paleta { grid-template-columns: repeat(6, 1fr); }
}

/* Five swatches never divide evenly into two or three columns, and the grid's
   1px gaps paint the leftover cell grey — an empty seventh chip. The last one
   stretches to close it, and the wide breakpoint just drops to five columns. */
.paleta--cinco .paleta__item:last-child { grid-column: span 2; }
@media (min-width: 62rem) {
    .paleta--cinco { grid-template-columns: repeat(5, 1fr); }
    .paleta--cinco .paleta__item:last-child { grid-column: auto; }
}

.paleta__item {
    background: var(--newsprint);
    padding: .85rem .85rem 1rem;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.paleta__chip {
    display: block;
    height: 3.6rem;
    border: var(--rule-hair);
}

.paleta__nome {
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    line-height: 1.35;
    text-align: center;
    text-indent: .16em; /* compensate for trailing letterspace */
}

.paleta__hex {
    font-family: var(--label);
    font-size: .62rem;
    letter-spacing: .1em;
    color: var(--ink-faint);
    font-variant-numeric: tabular-nums;
}

/* The men's list sits inside a column, so it drops to a single file of rows. */
.paleta--compacta {
    grid-template-columns: repeat(2, 1fr);
    margin-top: 0;
}
@media (min-width: 46rem) {
    .paleta--compacta { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 62rem) {
    .paleta--compacta { grid-template-columns: repeat(4, 1fr); }
}
.paleta--compacta .paleta__chip { height: 2.4rem; }

/* ---- The six families ---- */

.cores {
    display: grid;
    gap: clamp(1.5rem, 4vw, 2.5rem);
    grid-template-columns: repeat(2, 1fr);
    margin-top: 2rem;
}
@media (min-width: 52rem) {
    .cores { grid-template-columns: repeat(3, 1fr); }
}

.cor-card { margin: 0; }

.cor-card__figura {
    border: var(--rule-thin);
    padding: .4rem;
    background: var(--newsprint-deep);
}
.cor-card__figura img { width: 100%; }
.cor-card__figura--larga { align-self: center; }

.cor-card__nome {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(1.05rem, 2.4vw, 1.25rem);
    letter-spacing: -0.01em;
    margin: .8rem 0 .5rem;
}

.tons {
    list-style: none;
    display: flex;
    gap: .3rem;
    margin: 0 0 .6rem;
    padding: 0;
}
.tons__tom {
    flex: 1 1 0;
    height: 1.5rem;
    border: var(--rule-hair);
}

.cor-card__dek {
    margin: 0;
    font-size: .82rem;
    line-height: 1.55;
    color: var(--ink-soft);
}

/* ---- What to leave at home ---- */

.evitar {
    margin: 2rem 0 2.5rem;
    border-top: var(--rule-thin);
}

.evitar__linha {
    display: grid;
    gap: .15rem .5rem;
    padding: .85rem 0;
    border-bottom: var(--rule-hair);
}
@media (min-width: 46rem) {
    .evitar__linha { grid-template-columns: 16rem 1fr; gap: 1.5rem; align-items: baseline; }
}

.evitar dt {
    font-family: var(--label);
    font-size: .74rem;
    letter-spacing: .13em;
    text-transform: uppercase;
    /* The cross is decoration, not content: a screen reader gets the term itself. */
    position: relative;
    padding-left: 1.25rem;
}
.evitar dt::before {
    content: "\00d7";
    position: absolute;
    left: 0;
    color: var(--accent);
    font-family: var(--body);
    font-size: .95rem;
    line-height: 1.2;
}

.evitar dd {
    margin: 0;
    font-size: .88rem;
    color: var(--ink-soft);
}

/* A full-width illustrated banner. The frame matches .video__frame and
   .cor-card__figura so the drawn images on the page all sit in the same
   border-and-mount treatment as the photographs. */
.banner { margin: 0 0 1.5rem; }

.banner__frame {
    border: var(--rule-thin);
    padding: .4rem;
    background: var(--newsprint-deep);
}
.banner__frame img { width: 100%; }

/* ------------------------------------------------------------------ HOSPEDAGEM
   The lodging guide. Each entry is a listing rather than a classified ad: the
   ads in /presentes are one line of copy around a square cut, while a hotel has
   to carry an address, two ratings, a price and a phone number. So the cut turns
   landscape and the facts sit beside it, the way a travel page sets a hotel.
   Photo left, facts right, stacked on a phone. Five entries read as a list; a
   two-up grid would leave the fifth one orphaned. */
.hosp {
    display: flex;
    flex-direction: column;
    gap: clamp(1.75rem, 4vw, 2.75rem);
    margin-top: 2rem;
}

.hosp__item {
    display: grid;
    gap: clamp(1rem, 3vw, 1.75rem);
    padding-bottom: clamp(1.75rem, 4vw, 2.75rem);
    border-bottom: var(--rule-hair);
}
.hosp__item:last-child { border-bottom: 0; padding-bottom: 0; }

@media (min-width: 52rem) {
    .hosp__item { grid-template-columns: 26rem 1fr; align-items: start; }
}

/* Matches .cor-card__figura and .banner__frame so every mounted image on the
   site sits in the same border-and-margin treatment. */
.hosp__foto { margin: 0; }

/* A hotel with no photograph of its own gets a plate with its name, not a stock
   image of somebody else's breakfast. */
.hosp__foto--vazia {
    aspect-ratio: 3 / 2;
    border: var(--rule-thin);
    background: var(--newsprint-deep);
    display: grid;
    place-items: center;
    padding: 1rem;
}
.hosp__foto--vazia span {
    font-family: var(--display);
    font-size: clamp(1.1rem, 3vw, 1.5rem);
    color: var(--ink-faint);
    text-align: center;
}

.hosp__dist {
    font-family: var(--label);
    font-size: .74rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 .3rem;
}

.hosp__nome {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(1.3rem, 3.2vw, 1.65rem);
    line-height: 1.15;
    letter-spacing: -0.015em;
    margin: 0 0 .6rem;
}


/* Ratings, each stamped with the site it came from. Booking and TripAdvisor
   disagree about this town and the reader is owed both numbers, not an average. */
.hosp__notas {
    list-style: none;
    margin: 0 0 1rem;
    padding: .6rem 0;
    border-top: var(--rule-hair);
    border-bottom: var(--rule-hair);
    font-size: .82rem;
}
.hosp__notas li { display: flex; gap: .6rem; align-items: baseline; }
.hosp__notas li + li { margin-top: .3rem; }

.hosp__fonte {
    flex: 0 0 6.5rem;
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

/* The page carries five of these dl blocks, so they run tighter than the single
   fact list on /local. */
.facts--tight { margin: 0 0 1.1rem; }


/* ------------------------------------------------------------------ CHECKLIST
   The couple's private planning checklist (/checklist/{token}). Read on a phone,
   standing up, one thumb — so the row puts the tick box within reach on the left,
   lets the copy breathe in the middle, and drops the four small actions onto
   their own line rather than crowding the title. Widens into a single calm row
   once there's a hand free to use a mouse.

   The forms wrapping each button are plumbing, not layout: display: contents on
   them so the buttons themselves are the grid/flex children. */

/* The strip itself: the sticky bit is .check-abas around it, so this only has
   to lay the pills out and scroll them sideways when they do not fit. */
.check-nav {
    display: flex;
    gap: .5rem;
    max-width: var(--measure);
    margin-inline: auto;
    padding: .55rem var(--gutter);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.check-nav::-webkit-scrollbar { display: none; }

.check-nav__link {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    min-height: 2.75rem;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--ink-soft);
    border: var(--rule-faint);
    padding: .6rem .95rem;
    white-space: nowrap;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.check-nav__link:hover { color: var(--ink); border-color: var(--ink-soft); }
.check-nav__link[aria-current="page"],
.check-nav__link[aria-current="page"]:hover {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--newsprint);
}

.check-nav__cnt {
    font-variant-numeric: tabular-nums;
    opacity: .7;
}

@media (min-width: 48rem) {
    .check-nav {
        overflow-x: visible;
        flex-wrap: wrap;
        justify-content: center;
    }
}

/* ---------- Progresso ---------------------------------------------------- */

.check-prog { margin: 0 0 1rem; }

.check-prog__trilho {
    height: .4rem;
    background: var(--newsprint-edge);
    border: var(--rule-hair);
    overflow: hidden;
}
.check-prog__barra {
    height: 100%;
    background: var(--accent);
    transition: width .3s ease;
}

.check-prog__lbl {
    margin: .55rem 0 0;
    text-align: center;
    font-family: var(--label);
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

/* ---------- A lista ------------------------------------------------------ */

.check {
    list-style: none;
    margin: 0;
    padding: 0;
}

.check__item {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
        "marca corpo"
        "marca acoes";
    column-gap: .65rem;
    row-gap: .4rem;
    align-items: start;
    padding: .5rem 0 .5rem .6rem;
    border-bottom: var(--rule-hair);
    /* Reserved even when off, so a row turning priority on doesn't shove
       everything sideways. */
    border-left: 3px solid transparent;
    transition: border-color .15s ease, opacity .15s ease;
}
.check__item:first-child { border-top: var(--rule-hair); }
.check__item:last-child { border-bottom: 0; }

.check__item--prio { border-left-color: var(--accent); }

/* The row recedes but stays legible — it's a record, not a deletion. */
.check__item--feita { opacity: .8; }

.check__marca,
.check__acoes form { display: contents; }

.check__box {
    grid-area: marca;
    width: 2.75rem;
    height: 2.75rem;
    min-width: 2.75rem; /* 44px */
    min-height: 2.75rem;
    display: grid;
    place-items: center;
    padding: 0;
    border: var(--rule-thin);
    background: transparent;
    color: var(--ink);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.check__item--feita .check__box {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--newsprint);
}
/* The outline is always there; only the mark itself hides. */
.check__item:not(.check__item--feita) .check__box span { visibility: hidden; }

.check__corpo { grid-area: corpo; min-width: 0; }

.check__titulo {
    /* A submit button wearing the title's clothes: tapping the task is how you
       edit it, so the text itself has to be the target. Everything here undoes a
       button's defaults except the pointer and the wider tap area. */
    display: block;
    width: 100%;
    margin: 0;
    padding: .15rem 0;
    border: 0;
    background: none;
    font-family: var(--body);
    font-size: .94rem;
    line-height: 1.3;
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}
.check__titulo:hover { color: var(--accent); }
.check__item--feita .check__titulo {
    color: var(--ink-faint);
    text-decoration: line-through;
}

.check__nota {
    margin: .1rem 0 0;
    font-size: .8rem;
    line-height: 1.4;
    font-style: italic;
    color: var(--ink-soft);
}
.check__item--feita .check__nota { color: var(--ink-ghost); }

.check__quando {
    margin: .15rem 0 0;
    font-family: var(--label);
    font-size: .66rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

.check__acoes {
    grid-area: acoes;
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
}

.check__btn {
    width: 2.75rem;
    height: 2.75rem;
    min-width: 2.75rem; /* 44px */
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-family: var(--label);
    font-size: 1rem;
    border: var(--rule-faint);
    background: transparent;
    color: var(--ink-soft);
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.check__btn:hover { background: var(--ink); border-color: var(--ink); color: var(--newsprint); }
.check__btn--perigo:hover { background: var(--accent); border-color: var(--accent); color: var(--newsprint); }

/* Same class, two states: quiet outline when the item isn't priority, filled
   accent when it is — the star itself never changes, the row's state does. */
.check__item--prio .check__btn--prio {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--newsprint);
}
.check__item--prio .check__btn--prio:hover { background: var(--ink); border-color: var(--ink); }

/* A disabled arrow at the top or bottom of a section: still occupies its slot so
   the row of actions never reflows, but reads as unavailable and takes no tap. */
.check__btn:disabled {
    opacity: .25;
    cursor: default;
}
.check__btn:disabled:hover { background: transparent; border-color: var(--ink-ghost); color: var(--ink-soft); }

@media (min-width: 48rem) {
    .check__item {
        grid-template-columns: auto 1fr auto;
        grid-template-areas: "marca corpo acoes";
        align-items: center;
        padding-right: .25rem;
    }
    .check__acoes { flex-wrap: nowrap; }
}

/* ---------- Nova tarefa -------------------------------------------------- */

.check-novo { margin-top: .9rem; }

/* ---------- Compactação da página ---------------------------------------
   The checklist borrows the newspaper's furniture but not its pace. The
   editorial sections breathe at clamp(3.25rem, 9vw, 6rem) because they hold
   one idea each; this page holds seventy lines of work, and that rhythm turns
   into a scroll nobody finishes. Scoped to .check-page so the rest of the site
   keeps its air. */

.check-page .section {
    padding-block: clamp(1.5rem, 4vw, 2.25rem);
}

.check-page .section__head {
    margin-bottom: .9rem;
}

.check-page .headline {
    font-size: clamp(1.6rem, 4vw, 2.3rem);
    margin-bottom: .35rem;
}
.check-page .headline--sm { font-size: clamp(1.15rem, 2.6vw, 1.45rem); }

.check-page .standfirst {
    font-size: clamp(.92rem, 2vw, 1rem);
    margin-bottom: 1rem;
}

.check-page .notice { margin-bottom: 1rem; }

/* A mouse does not need a thumb's target. Below 48rem these stay at 44px. */
@media (min-width: 48rem) {
    .check__box,
    .check__btn {
        width: 2.1rem;
        height: 2.1rem;
        min-width: 2.1rem;
        min-height: 2.1rem;
        font-size: .9rem;
    }
}

/* ---------- O diálogo de tarefa -----------------------------------------
   New task and edit task share one dialog, and it is always in the markup —
   Blazor has to find its form to dispatch its own POST (see Checklist.razor).
   So "closed" is a class, not a conditional block, and that class has to win over the
   display:flex below. */

.modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: flex-end;      /* thumb-first: the sheet rises from the bottom */
    justify-content: center;
    padding: 0;
    background: rgba(28, 26, 21, .55);
    /* The list behind is long; letting the dialog scroll on a short screen
       keeps Salvar reachable with the keyboard open. */
    overflow-y: auto;
    overscroll-behavior: contain;
}

.modal__caixa {
    width: 100%;
    max-width: 34rem;
    max-height: 92vh;
    overflow-y: auto;
    background: var(--newsprint);
    border-top: var(--rule-thick);
    padding: 1.25rem var(--gutter) 1.5rem;
    box-shadow: 0 -8px 40px rgba(28, 26, 21, .25);
}

@media (min-width: 40rem) {
    /* Room to breathe: on a desktop it centres as a card rather than a sheet. */
    .modal {
        align-items: center;
        padding: 1.5rem;
    }
    .modal__caixa {
        border: var(--rule-thick);
        padding: 1.5rem 2rem 1.75rem;
        box-shadow: 0 10px 50px rgba(28, 26, 21, .3);
    }
}

.modal__rubrica {
    font-family: var(--label);
    font-size: .68rem;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 .2rem;
}

.modal__titulo {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(1.3rem, 4vw, 1.7rem);
    line-height: 1.15;
    letter-spacing: -0.015em;
    margin: 0 0 1.1rem;
}

.modal__opcional {
    letter-spacing: .12em;
    color: var(--ink-faint);
    font-weight: 400;
}

/* Salvar on top: it is the one people reach for, and on a phone the lower
   button is the easier tap only when your thumb is already down there. */
.modal__acoes {
    display: grid;
    gap: .5rem;
    margin-top: 1.25rem;
}
@media (min-width: 40rem) {
    .modal__acoes {
        grid-template-columns: 1fr 1fr;
        gap: .75rem;
    }
}

/* Beats .modal's display:flex, so the dialog is genuinely gone when closed —
   no overlay swallowing taps on the list behind it. */
.modal--fechado { display: none; }

/* ---------- A casca da página avulsa -------------------------------------
   /checklist runs on its own layout, with none of the jornal's furniture: no
   rag of guest links, no expediente. What is left is a tool, so the one thing
   pinned to the top of the screen is the thing you navigate with. */

.check-shell {
    display: block;
    min-height: 100vh;
}

/* The tab strip, pinned. It is a sibling of .sheet rather than a child of the
   first <section> on purpose — sticky only holds inside its own parent's box,
   and nested in a section it would come unstuck as soon as that section
   scrolled past.

   The opaque background is not decoration: without it seventy rows of text
   scroll through the bar. */
.check-abas {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--newsprint);
    border-bottom: var(--rule-thin);
}

/* Anchors have to clear the pinned bar, or "Lua de mel" lands with its heading
   hidden underneath it. Slightly more than the bar's own height, so the section
   rule above the heading stays visible too. */
html { scroll-padding-top: 4.5rem; }

/* First section runs tighter: the bar above it already separates it from the
   top of the screen, so the usual opening air reads as a gap. */
.check-page > .section:first-of-type { padding-top: 1.25rem; }

/* ---------- Arrastar para reordenar -------------------------------------
   The up/down arrows are gone: on a phone, held one-handed, moving a task
   fourteen places meant fourteen taps. Now the row is dragged by a handle.
   Everything below only dresses the gesture — the ordering itself is the
   JavaScript's, and it works by comparing the pointer against each row's
   midpoint, so nothing here may change a row's height. Padding, margins and
   row-gap are therefore left exactly as .check__item set them; the drag state
   changes only paint (colour, shadow, opacity, stacking). */

/* The handle. It sits first inside .check__acoes, shoulder to shoulder with
   the .check__btn's, so it borrows their box down to the pixel — the strip of
   controls has to read as one set, not as a stray widget bolted on the front.
   What differs is the cursor: this one is taken hold of, not pressed. */
.check__punho {
    width: 2.75rem;
    height: 2.75rem;
    min-width: 2.75rem; /* 44px */
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-family: var(--label);
    font-size: 1rem;
    border: var(--rule-faint);
    background: transparent;
    color: var(--ink-faint);
    cursor: grab;
    /* Load-bearing, not a nicety: with the default touch-action the browser
       claims the first vertical movement for scrolling and cancels the pointer
       stream, so on a phone the drag simply never begins. Every other rule in
       this block is decoration; this one is the feature. */
    touch-action: none;
    /* A drag that starts on a button still selects the text around it on a
       desktop, and on iOS a long press raises the callout menu ("Copiar",
       "Compartilhar") right on top of the row being moved. */
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.check__punho:hover { border-color: var(--ink-soft); color: var(--ink); }

/* The hand closes while the row is actually being carried. :active covers the
   pointer being down on the handle; the list gets the same cursor further down,
   for the stretch where the pointer has travelled off the handle. */
.check__punho:active { cursor: grabbing; }

/* ArrowUp/ArrowDown on the focused handle move the row, so this is a real
   keyboard control and its ring has to stay visible — the global
   2px solid var(--accent) at outline-offset 3px, untouched. The offset draws
   the ring outside the button's box, where it would otherwise be painted over
   by the next row's hairline; lifting only the focused handle keeps it whole
   without giving the button a stacking context the rest of the time. */
.check__punho:focus-visible {
    position: relative;
    z-index: 1;
}

/* The row in flight. It is a grid item in the <ul>, so it keeps display: grid
   and every track it had — only its paint changes. The opaque paper background
   matters: .check__item is transparent by default, and without it the rows
   sliding underneath show straight through the titles. */
.check__item--arrastando {
    position: relative;   /* z-index needs a positioned box to bite on */
    z-index: 5;
    background: var(--newsprint);
    box-shadow: 0 6px 18px rgba(28, 26, 21, .18);
    /* Just off full strength, so the paper reads as lifted rather than pasted
       on — not far enough to make the title hard to read while it moves. */
    opacity: .95;
    cursor: grabbing;
}

/* ---- The list, while a row is being carried ---- */

/* Everything that is not the dragged row goes quiet. The cursor stays closed
   across the whole list, because the pointer spends most of the drag over the
   other rows, not over the handle it started on. */
.check--ordenando {
    cursor: grabbing;
    user-select: none;
    -webkit-user-select: none;
}

/* Hover is a mouse resting on a thing it might press; mid-drag it is neither.
   Taking the pointer off the other rows' controls suppresses their :hover
   outright — cleaner than restating each hover rule in reverse, which would
   also have to fight .check__item--prio .check__btn--prio for its filled star —
   and it stops a finger that drifts sideways from tripping a delete on the way
   past. The dragged row's own controls go with them: the thumb is parked on the
   handle, and nothing else under it should light up. */
.check--ordenando .check__box,
.check--ordenando .check__titulo,
.check--ordenando .check__acoes {
    pointer-events: none;
}
/* The handle keeps its events: the pointer capture that drives the whole drag
   is anchored on it, and it is what the JavaScript listens to. */
.check--ordenando .check__punho { pointer-events: auto; }

/* The drop hint, deliberately faint. The rows that will shift step back a
   little and their hairlines come forward — the seams are where the carried row
   can land, so this is the one thing worth making easier to see. No inserted
   gap, no growing placeholder, no border width change: the JavaScript is
   measuring these rows' midpoints while this class is on, and anything that
   moved them would send the drop to the wrong index. Height is untouched.
   (.check__item already transitions border-color and opacity, so this eases in
   on its own and the file's global prefers-reduced-motion rule already flattens
   that duration — no new motion is introduced here, so none needs its own
   reduced-motion block.) */
.check--ordenando .check__item:not(.check__item--arrastando) {
    opacity: .75;
    border-bottom-color: var(--ink-faint);
}
.check--ordenando .check__item:not(.check__item--arrastando):first-child {
    border-top-color: var(--ink-faint);
}

/* A mouse does not need a thumb's target — the same trade the box and the
   buttons make above, and the handle has to shrink with them or it would stand
   a head taller than the controls it sits beside. */
@media (min-width: 48rem) {
    .check__punho {
        width: 2.1rem;
        height: 2.1rem;
        min-width: 2.1rem;
        min-height: 2.1rem;
        font-size: .9rem;
    }
}

/* ---------- A linha única no telefone -----------------------------------
   Below the desktop breakpoint the row collapses to one line: tick box, title,
   controls, all on the same baseline. The two-row arrangement above this is
   roomier and reads better, but it cost ~87px a row, and a list you scroll for
   half a minute to reach "Terno" stops being a list you glance at.

   What pays for it: the note moves out of the row (it is still there, in the
   dialog, one tap away on the title) and the title takes one line with an
   ellipsis. Both are recoverable by tapping; row height is not. */

@media (max-width: 47.999rem) {

    .check__item {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas: "marca corpo acoes";
        align-items: center;
        column-gap: .28rem;
        row-gap: 0;
        padding: .22rem 0 .22rem .3rem;
    }

    /* The tick box: a small square with a big target underneath it.

       Drawn at 44px like everything else it dominates the line — it is a filled
       black square when checked, and the eye goes there first even though the
       title is the content. So the BUTTON keeps its 2.3rem of tappable area and
       the visible box shrinks to 1.35rem inside it. Nothing is harder to hit;
       the ink just stops shouting. */
    .check__box {
        width: 2.05rem;
        height: 2.05rem;
        min-width: 2.05rem;
        min-height: 2.05rem;
        border: 0;
        background: none;
        padding: 0;
    }

    .check__box span {
        width: 1.15rem;
        height: 1.15rem;
        display: grid;
        place-items: center;
        border: var(--rule-thin);
        font-size: .78rem;
        line-height: 1;
    }

    /* The base rule hides the whole span to hide the tick. Here the span IS the
       box, so it stays and only the glyph goes transparent. Same specificity as
       that rule, further down the file, so it wins. */
    .check__item:not(.check__item--feita) .check__box span {
        visibility: visible;
        color: transparent;
    }

    /* The fill moves off the button and onto the little square with it. */
    .check__item--feita .check__box { background: none; }
    .check__item--feita .check__box span {
        background: var(--ink);
        border-color: var(--ink);
        color: var(--newsprint);
    }

    /* 36px rather than 44. Below the usual thumb minimum on paper, still about
       10mm on a real screen, and the alternative was either a second row or a
       title clipped to a dozen characters. The tick box above keeps the full
       target, so the one action people repeat is not the one that shrank. */
    .check__btn,
    .check__punho {
        width: 2rem;
        height: 2rem;
        min-width: 2rem;
        min-height: 2rem;
        font-size: .8rem;
    }

    .check__acoes {
        flex-wrap: nowrap;
        gap: .1rem;
    }

    /* minmax(0, 1fr) on the column plus overflow here is what actually lets the
       ellipsis appear: a grid track defaults to min-content, so without it the
       longest title simply widens the row and pushes the buttons off-screen. */
    .check__titulo {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        /* Down from .94rem, and the single biggest thing buying the title room
           back: at this size a couple more words clear the ellipsis, and the
           serif still sets cleanly because it is only ever one line here. */
        font-size: .82rem;
        line-height: 1.25;
        padding: .05rem 0;
    }

    /* Out of the row, not out of the data. */
    .check__nota,
    .check__quando { display: none; }

    /* ...but a hidden note should not be a secret. A row carrying one gets a
       small accent mark after its title, so nobody has to open tasks at random
       to find where they wrote the phone number. Browsers without :has() simply
       show nothing extra, which is the state we were in anyway. */
    .check__item:has(.check__nota) .check__titulo::after {
        content: " •";
        color: var(--accent);
    }

    /* The delete confirmation still needs to breathe: it drops below the line
       and must not inherit the title's nowrap. */
    .check__corpo .notice {
        white-space: normal;
        margin: .4rem 0 .2rem;
    }

    /* The one row that may break the single line. A confirmation squeezed into
       the title's column between the tick box and four buttons is unreadable,
       and this is the moment to be sure of what you are about to delete, so the
       row goes back to stacking for as long as it is asking. */
    .check__item--removendo {
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-areas:
            "marca corpo"
            "marca acoes";
        align-items: start;
        row-gap: .4rem;
        padding-top: .5rem;
        padding-bottom: .5rem;
    }
    .check__item--removendo .check__titulo { white-space: normal; }
    .check__item--removendo .check__nota { display: block; }
}

/* The dialog's way out. Quiet, set apart, and last: it is the one irreversible
   thing on this page, so it must not sit in the same visual rank as Salvar.
   Not a .btn — those are equals, and this is not. */
.modal__remover {
    display: block;
    width: 100%;
    margin-top: 1rem;
    padding: .7rem 0;
    border: 0;
    border-top: var(--rule-hair);
    background: none;
    font-family: var(--label);
    font-size: .72rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-faint);
    cursor: pointer;
    transition: color .15s ease;
}
.modal__remover:hover { color: var(--accent); }

/* The one hotel the couple actually recommends, called out above its name. Set in
   the accent red like .hosp__dist, but ruled and heavier so the eye lands on it
   first — it is the lead item of the listing, not another line of metadata. */
.hosp__selo {
    display: inline-block;
    font-family: var(--label);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--newsprint);
    background: var(--accent);
    padding: .2rem .55rem;
    margin: 0 0 .55rem;
}

/* ---------------------------------------------------------------- OFERTA
   A named example set into the classifieds intro — the couple naming a few of
   the things on offer before the ads themselves start. Ruled top and bottom
   like .hosp__notas so it reads as an inset within the copy, not as another
   paragraph of it. */
.oferta {
    margin: 1.1rem 0;
    padding-left: 1rem;
    border-left: var(--rule-thin);
}
.oferta__nome {
    font-family: var(--display);
    font-weight: 700;
    font-size: 1.02rem;
    line-height: 1.3;
    margin: 0 0 .2rem;
}
.oferta__dek {
    margin: 0;
    font-size: .85rem;
    line-height: 1.6;
    color: var(--ink-soft);
}

/* The epigraph under the lead photo: the line the whole page opens on, and the
   only thing between the photograph and the countdown. It is set as a pull
   quote rather than a caption — display serif, italic, a size up from body
   copy, at a measure narrow enough to break into two or three balanced lines.
   A short centred rule above it reads as an ornament; a full-width one would
   read as a section divider and the countdown's thick rule already does that
   job right below. Hence the generous space underneath: without it the last
   line sits straight on top of that rule. */
.epigrafe {
    max-width: 30rem;
    margin: clamp(2.25rem, 5vw, 3.5rem) auto clamp(2.5rem, 6vw, 4rem);
    padding: 0;
    border: 0;
}

.epigrafe::before {
    content: "";
    display: block;
    width: 3.25rem;
    margin: 0 auto clamp(1.25rem, 3vw, 1.75rem);
    border-top: var(--rule-thin);
}

.epigrafe p {
    margin: 0;
    font-family: var(--display);
    font-style: italic;
    font-size: clamp(1.18rem, 3.4vw, 1.65rem);
    line-height: 1.42;
    letter-spacing: -0.01em;
    text-align: center;
    text-wrap: balance;
    color: var(--ink);
}

/* The line that closes the story by speaking to the reader. Ruled top, centred,
   and set wider than body copy so it lands as a send-off. */
.envoi {
    max-width: 34rem;
    margin: 2.25rem auto 0;
    padding-top: 1.5rem;
    border-top: var(--rule-hair);
    font-family: var(--display);
    font-size: clamp(1.02rem, 2.6vw, 1.2rem);
    line-height: 1.5;
    text-align: center;
    text-wrap: balance;
}
