/* ============================================================
   connexio portal — structural stylesheet (palette-independent)
   ------------------------------------------------------------
   Everything here is layout, spacing, type scale and behaviour.
   COLOUR lives in the variant sheet loaded after this one and is
   read exclusively through the tokens declared in :root there:

     --bg --surface --border --text --text-dim
     --accent --accent-ink --accent-soft
     --tiss --nfs            (per-product accent)
     --logo-colour --logo-foot-colour

   A variant sheet sets those and nothing else.
   ============================================================ */

/* ── Spacing scale ────────────────────────────────────────────
   Every vertical gap on this page comes from this scale. It exists
   because the first pass used ten ad-hoc values (14/18/22/26/30/
   40/44/56/64), nine of them off any grid, and the page read as
   subtly unresolved — the router sat 30px below the hero and 44px
   above the cards, so it attached itself to the hero instead of
   standing between the two. Reach for a token; do not type a px
   value into a margin. */
:root {
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 16px;
    --s-4: 24px;
    --s-5: 32px;
    --s-6: 48px;
    --s-7: 64px;
    --s-8: 96px;
}

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

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

/* The sticky-footer shell, same pattern both apps already use: tiss is
   Bootstrap's `d-flex flex-column min-vh-100` + `mt-auto`, nfs.cx is
   `.shell{display:flex;min-height:100dvh}` + `.rail-foot{margin-top:auto}`.
   This page is short enough that without it the footer floats mid-screen
   on a desktop viewport.
   100dvh is the real viewport; iOS Safari's 100vh counts the collapsed URL
   bar and clips the column, so the vh line stays first as the fallback —
   the same two-line guard, and the same reason, as nfs.cx app.css. */
body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 17px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;

    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}

/* Takes the slack so the footer is pushed to the bottom on a short page
   and simply follows the content on a long one. */
main { flex: 1 0 auto; }

a { color: var(--accent-ink); }

/* ── Shell ─────────────────────────────────────────────────── */
.wrap {
    max-width: 880px;
    margin: 0 auto;
    padding: 0 var(--s-4); /* phone gutter; never let content touch the edge */
}

/* ── Header — the logo is the continuity signal with both apps ─ */
.site-head {
    padding: var(--s-7) 0 0;
}

/* Each logo is a SPAN holding its real name as text, with the lockup laid
   over it as a CSS mask. The SVG's shape is the stencil and the element's
   background is the paint, so a token recolours the logo exactly, and the
   one connexio.svg serves the header and the dimmed footer.

   Three properties of this arrangement are load-bearing:

   - A SPAN, not an <img>: masking an <img> clips the box but its own
     pixels still paint, so the logo keeps the file's colour.
   - The text stays in the DOM, so the name reaches screen readers, and
     `color: transparent` only hides it from sight.
   - The mask sits inside @supports. Without mask support the span would be
     a solid block, so that browser gets the bare text below instead: the
     name, in the right colour, with no raster fallback to keep in step
     with the logo. The boxes use aspect-ratio with overflow:hidden, which
     also stops that hidden text from stretching them.

   The aspect ratios are the viewBoxes of the files, which were cropped
   from the 2026-10-26 logo kit. See "The logos" in README.md. */
.logo {
    display: block;
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: var(--display-weight);
    line-height: 1.3;
    letter-spacing: var(--display-tracking);
    color: var(--logo-colour);
}

@supports ((-webkit-mask-image: url("connexio.svg")) or (mask-image: url("connexio.svg"))) {
    .logo {
        width: 224px;
        aspect-ratio: 6043 / 900;
        overflow: hidden;
        white-space: nowrap;
        color: transparent;
        background-color: var(--logo-colour);
        -webkit-mask: url("connexio.svg") no-repeat left center / contain;
        mask: url("connexio.svg") no-repeat left center / contain;
    }
}

/* ── Hero ──────────────────────────────────────────────────── */
.hero {
    padding: var(--s-6) 0 0;
}

/* 26ch, not the 20ch this started at: at 20ch the title ended 411px short
   of the content below it (measured at 1200px wide), so it read as stranded
   in the top-left of a much wider page. The ragged gap was the problem, not
   the left alignment — centring it is worse, because everything below stays
   left-anchored and the page gets two competing axes. */
.hero h1 {
    font-family: var(--font-display);
    font-size: clamp(2.1rem, 6vw, 3.1rem);
    line-height: 1.12;
    letter-spacing: var(--display-tracking);
    font-weight: var(--display-weight);
    margin: 0 0 var(--s-3);
    color: var(--text);
    max-width: 26ch;
}

/* :not(.router) matters. .router is also a <p> inside .hero, so a bare
   `.hero p` selector matches it too — and at equal-or-higher specificity,
   so its `margin: 0` shorthand silently beat .router's own margin-top and
   the gap above the reminder collapsed to zero. Scope the lead paragraph
   explicitly rather than relying on source order. */
.hero p:not(.router) {
    margin: 0;
    font-size: 1.12rem;
    color: var(--text-dim);
    max-width: 58ch;
}

/* ── Router line — the returning-visitor shortcut ──────────────
   A bookmark-holder who typed connexio.digital expecting the converter
   needs routing in one glance, before they start reading two product
   descriptions. --s-6 above it so it reads as its own object sitting
   between the hero and the products, not as the hero's last paragraph —
   which is exactly how it looked when `.hero p` was silently zeroing this
   margin (see the :not(.router) note above). */
.router {
    margin: var(--s-6) 0 0;
    padding: var(--s-2) 0 var(--s-2) var(--s-3);
    border-left: 2px solid var(--accent);
    font-size: 1rem;
    color: var(--text-dim);
}

.router a {
    color: var(--accent-ink);
    font-weight: 600;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* ── Products — transformation rows, not cards ─────────────────
   Both products do the same thing: a spreadsheet the client already
   keeps goes in, a file an insurer or a prefeitura will accept comes
   out. So each product is a ROW that reads left to right and states
   that transformation, rather than a card you read top-down.

   Deliberately NOT two identical rounded boxes with matching shadows:
   that treatment says "SaaS product grid" and could carry any two
   products in any industry. Here the layout itself carries the one
   idea the audience recognises. */
.products {
    margin: var(--s-5) 0 0;
    border-top: 1px solid var(--border);
}

/* The destination rides on the flow line's baseline, not in a column of
   its own: floated right and cleared by the description, so it reads as
   the end of the transformation rather than an orphan in whitespace. */
/* --s-6 top and bottom, not --s-5: the .flow line box is ~53px tall (the
   miniature inflates it well past the text), so a 32px pad read as cramped
   — much of the apparent gap was empty space inside the line, not between
   the rows. 48px gives each row the air the measurement says it needs. */
.product {
    display: block;
    padding: var(--s-6) 0;
    border-bottom: 1px solid var(--border);
    text-decoration: none;
    color: inherit;
}

/* ── The miniature ─────────────────────────────────────────────
   A small, static picture of the transformation: a spreadsheet on
   the left, the product's output on the right. It echoes the tiss.cx
   hero art (a .xlsx card with an XML card overlapping it) at a size
   that suits a portal row.

   Drawn from empty <span>/<i> elements with borders and backgrounds —
   no image file to keep in sync with the palette, nothing to
   translate, and it costs no extra request. It is decorative and
   aria-hidden: the .flow line immediately below says the same thing
   in words, so a screen reader gets the meaning once, not twice.

   Sized in em so it tracks the row's type. It is a flex child of the
   flow line rather than a float: a float taller than that one line
   intrudes into the paragraph below it, and `clear` then fights the
   layout instead of describing it. */
.mini {
    display: flex;
    align-items: center;
    gap: .55em;
    flex-shrink: 0;
    margin-right: var(--s-2);
    font-size: 12px; /* the miniature's own scale; everything below is em */
}

.mini-sheet,
.mini-out {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: rgba(255, 255, 255, .03);
}

.mini-sheet { width: 4.4em; }

/* The coloured title bar each little window carries, like the green
   .xlsx bar and blue .xml bar in the tiss hero. */
.mini-bar {
    height: .55em;
    background: var(--mini-bar, var(--text-dim));
    opacity: .85;
}

.mini-sheet .mini-bar { --mini-bar: var(--sheet); }

.mini-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    padding: 1px;
}

.mini-grid i {
    height: .5em;
    background: var(--text-dim);
    opacity: .22;
}

/* First row reads as the header row, like a real sheet. */
.mini-grid i:nth-child(-n+3) { opacity: .4; }

/* The arrow: a short rule with a chevron, so it reads as flow at 11px
   where a glyph would just be a smudge. */
.mini-arrow {
    position: relative;
    width: 1.5em;
    height: 1px;
    background: var(--accent);
    opacity: .7;
}

.mini-arrow::after {
    content: "";
    position: absolute;
    right: 0;
    top: -2px;
    width: 5px;
    height: 5px;
    border-top: 1px solid var(--accent);
    border-right: 1px solid var(--accent);
    transform: rotate(45deg);
}

/* tiss.cx — one structured XML file. The lines are indented like a
   document tree, which is what makes it read as XML and not prose. */
.mini-out--xml { width: 3.8em; }
.mini-out--xml .mini-bar { --mini-bar: var(--out); }

.mini-lines {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: .3em .35em;
}

.mini-lines i {
    height: 1px;
    background: var(--out);
    opacity: .55;
}

.mini-lines i:nth-child(1) { width: 85%; }
.mini-lines i:nth-child(2) { width: 60%; margin-left: .3em; }
.mini-lines i:nth-child(3) { width: 50%; margin-left: .3em; }
.mini-lines i:nth-child(4) { width: 75%; }

/* nfs.cx — many notas, not one file. A fanned stack says "em lote",
   which is the real difference between the two products. */
.mini-out--notas {
    position: relative;
    width: 3.8em;
    height: 2.5em;
    border: 0;
    background: none;
    overflow: visible;
}

.mini-nota {
    position: absolute;
    width: 2.6em;
    height: 2.1em;
    border: 1px solid var(--border);
    border-radius: 2px;
    background: var(--bg);
    border-top: .3em solid var(--out);
}

.mini-nota:nth-child(1) { left: 0;    top: .4em; opacity: .45; }
.mini-nota:nth-child(2) { left: .5em; top: .2em; opacity: .7; }
.mini-nota:nth-child(3) { left: 1em;  top: 0; }

/* The transformation line: sua planilha → o arquivo exigido.
   The two sides are deliberately UNEQUAL — the input is quiet because
   it is what the client already has, the output is emphasised because
   it is the thing they were told to produce. An arrow between two equal
   labels would be a diagram; this is a claim. */
/* align-items is CENTER, not baseline: the miniature is much taller than
   the text, and baseline-aligning a tall box against a line of type drops
   the box ~20px, which reads as the words sitting on top of the picture
   rather than beside it. Centre puts both on one optical line. */
.flow {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-3);
    margin: 0 0 var(--s-4);
    font-size: 1.35rem;
    line-height: 1.3;
}

/* The words carry their own connector, drawn as an arrow (::after is the
   shaft, ::before the head). The miniature also contains an arrow, but the
   two are at different scales and read as one idea stated twice at
   different sizes rather than as a stutter — and an arrow is what actually
   says "becomes", which a bare rule does not.
   position:relative so the head can pin to the shaft's right end; ::after
   is the last flex item, so that edge IS the shaft's tip. */
.flow-in {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .55em;
    color: var(--text-dim);
    font-weight: 400;
}

/* The connector between input and output: an arrow that GROWS on hover,
   as if being drawn from the planilha toward what it becomes. It answers a
   pointer action rather than looping on its own, so it shows what changed
   instead of decorating. A BONUS, never the carrier of meaning — on touch
   there is no hover and the row reads exactly the same.

   It carries the PRODUCT's colour at rest, not the shared gold accent.
   When hover switched the colour from --accent to --out, the two rows
   behaved differently from one rule: gold→gold on tiss is a 1° hue shift
   (reads as the line simply growing) but gold→indigo on nfs is 197°
   (reads as the line turning into something else). Same interaction,
   two meanings. Now only length and brightness change, identically on
   both rows, and the resting line reinforces the colour-coding. */
.flow-in::after {
    content: "";
    width: 1.4em;
    height: 1px;
    background: var(--out);
    opacity: .55;
    transition: width .22s ease, opacity .22s ease;
}

/* The arrowhead: a rotated border wedge on ::before, pinned to the right
   end of the shaft. Both pseudo-elements animate the same `right` offset,
   so the head rides the shaft's tip as it grows instead of drifting.
   (A mask on a single element was tried and renders as a broken stub —
   the conic-gradient head and the linear shaft do not meet cleanly at
   this size.) */
.flow-in::before {
    content: "";
    position: absolute;
    right: 0;
    top: 50%;
    width: 4px;
    height: 4px;
    border-top: 1px solid var(--out);
    border-right: 1px solid var(--out);
    transform: translate(0, -50%) rotate(45deg);
    opacity: .55;
    transition: opacity .22s ease;
}

.product:hover .flow-in::after,
.product:focus-visible .flow-in::after {
    width: 2.6em;
    opacity: 1;
}

.product:hover .flow-in::before,
.product:focus-visible .flow-in::before {
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .flow-in::after { transition: none; }
}

.flow-out {
    font-family: var(--font-display);
    font-weight: var(--display-weight);
    letter-spacing: var(--display-tracking);
    color: var(--out);
}

.product--tiss { --out: var(--tiss); }
.product--nfs  { --out: var(--nfs); }

.product-desc {
    margin: 0;
    font-size: 1rem;
    color: var(--text-dim);
    max-width: 68ch; /* the row is full width now, so the prose can be too */
}

/* The domain is the brand (ADR-0007), so the product's logo is the
   destination label — not a "Saiba mais" button, and not the same text
   with an arrow glued on. Floated so it sits on the flow line at the
   right edge, which is where the transformation ends.

   It carries the PRODUCT's colour, not the shared accent: tiss.cx in the
   same gold as "XML TISS válido na ANS", nfs.cx in the same indigo as
   "NFS-e emitidas". That closes the loop — the destination and what you
   get there are visibly the same thing. Both clear AA on --bg (8.14:1
   and 5.94:1). These are the LIFTED hues on purpose: the kit's own
   #3A3FC4 fails on this ground, so a verbatim lockup would be unreadable.

   .product-go is the shrink-wrapped label and .product-logo inside it is
   the masked logo. Two elements because a mask clips everything on its own
   element, so the hover rule has to live outside it, and text-decoration
   has no glyphs to underline once the text is transparent. */
.product-go {
    float: right;
    margin: 2px 0 0 var(--s-4); /* the 2px is baseline alignment with .flow-out, not spacing */
    padding-bottom: var(--s-1);
    border-bottom: 2px solid transparent;
    font-family: var(--font-display);
    font-size: 1.05rem;
    font-weight: var(--display-weight);
    letter-spacing: var(--display-tracking);
    line-height: 1.3;
    color: var(--out);
    white-space: nowrap;
}

.product-logo { display: block; }

/* Equal HEIGHTS, unequal widths: both files keep the kit's full 957-unit
   band, so the same height gives both the same type size. */
@supports ((-webkit-mask-image: url("tiss-cx.svg")) or (mask-image: url("tiss-cx.svg"))) {
    .product-logo {
        height: 32px;
        overflow: hidden;
        color: transparent;
        background-color: var(--out);
    }

    .product--tiss .product-logo {
        aspect-ratio: 2771 / 957;
        -webkit-mask: url("tiss-cx.svg") no-repeat left center / contain;
        mask: url("tiss-cx.svg") no-repeat left center / contain;
    }

    .product--nfs .product-logo {
        aspect-ratio: 2538 / 957;
        -webkit-mask: url("nfs-cx.svg") no-repeat left center / contain;
        mask: url("nfs-cx.svg") no-repeat left center / contain;
    }
}

.product:hover .product-go,
.product:focus-visible .product-go {
    border-bottom-color: var(--out);
}

/* The row is the link target; a visible ring on the row itself is
   clearer than one around the small label. */
.product:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
    border-radius: 2px;
}

/* Contains the float so the next row's rule is not pulled up beside it. */
.product::after {
    content: "";
    display: block;
    clear: both;
}

/* Below 740px the words drop the transformation entirely: "sua planilha" and
   the arrow are hidden, and the row leads with the output alone.

   This is a deliberate simplification, not a shortcut. Measured: the two
   halves stop fitting on one line at 726px, and every attempt to keep them
   both — wrapping, then stacking with the arrow rotated to point down —
   produced an arrow that had to be re-solved at each width, and got it wrong
   more than once (an arrow left pointing at empty space, then one stretching
   over the text). The miniature at the top of the row ALREADY draws the
   spreadsheet turning into the output, so on a narrow screen the words were
   restating a picture that is right above them. Dropping them costs nothing
   and removes a whole class of layout bug.

   740, not 726: the breakpoint must sit above the width where the line stops
   fitting, never at it. It was 700 against a 680 wrap until the product
   logos replaced the shorter text labels and moved the wrap to 726. If the
   flow copy or a logo gets wider, re-measure. */
@media (max-width: 740px) {
    /* Too narrow to sit the destination beside the flow line. The row
       becomes a flex column and `order` puts the destination last —
       source order has it first, which only suits the float. */
    .product {
        display: flex;
        flex-direction: column;
    }
    .product-go {
        float: none;
        order: 3;
        align-self: flex-start; /* a flex item stretches, and so would the hover rule */
        margin: var(--s-3) 0 0;
    }
    .product-desc { order: 2; }

    .flow {
        order: 1;
        display: block;
        font-size: 1.15rem;
    }

    .mini {
        display: flex;
        margin: 0 0 var(--s-3);
    }

    /* Hidden, not restyled: with .flow-in gone the ::after shaft and ::before
       head go with it, so there is no arrow geometry left to get wrong. */
    .flow-in { display: none; }

    .flow-out { display: block; }
}

/* ── Footer — pinned to the bottom by the flex shell on body ── */
.site-foot {
    flex-shrink: 0;
    margin: var(--s-8) 0 0;
    padding: var(--s-4) 0 var(--s-5);
    font-size: .86rem;
    color: var(--text-dim);
}

.site-foot .wrap {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3) var(--s-4);
    flex-wrap: wrap; /* the legal line drops under the mark on a phone */
}

/* The footer logo, at 60% of the header's — present, not competing.
   Same mechanism as .logo, text fallback included; --logo-foot-colour lets
   a variant dim it without touching the header. */
.foot-mark {
    display: block;
    flex-shrink: 0;
    font-weight: var(--display-weight);
    color: var(--logo-foot-colour, var(--logo-colour));
}

@supports ((-webkit-mask-image: url("connexio.svg")) or (mask-image: url("connexio.svg"))) {
    .foot-mark {
        width: 134px;
        aspect-ratio: 6043 / 900;
        overflow: hidden;
        white-space: nowrap;
        color: transparent;
        background-color: var(--logo-foot-colour, var(--logo-colour));
        -webkit-mask: url("connexio.svg") no-repeat left center / contain;
        mask: url("connexio.svg") no-repeat left center / contain;
    }
}

/* margin-left:auto pins it to the right edge rather than letting
   space-between centre it in the leftover gap. On a phone the wrap drops
   it to its own line, where auto is a no-op and it reads left. */
/* margin-left:auto pins it to the right edge rather than letting
   space-between centre it in the leftover gap. */
.legal {
    margin: 0 0 0 auto;
    text-align: right;
    font-variant-numeric: tabular-nums; /* keeps the CNPJ digits even */
}

/* Wide footer: the three parts read as one line, with the em-dashes put
   back between them. They live in CSS rather than in the markup so that
   the narrow layout can drop them without leaving stray punctuation. */
.legal span + span::before {
    content: " — ";
    white-space: pre;
}

/* 600px, not 560px. Measured: the footer's flex row wraps at 588px, but the
   reset used to be at 560 — so between 588 and 560 the legal line sat on its
   own row while still carrying `margin-left:auto` and `text-align:right`,
   i.e. underneath the logo but shoved to the right edge. Same off-by-one
   breakpoint bug as the flow arrow had. The reset must fire at or above the
   wrap width, never below it.
   Below this the three parts stack: one per line, so the break falls between
   them by design instead of the browser splitting "São / Paulo" mid-phrase
   (measured: that happened from 420px down). */
@media (max-width: 600px) {
    /* A column, not a wrapping row. Stacking the legal text into three short
       lines makes it NARROW, so a flex row happily keeps it beside the mark
       and then clips it — the parts must be told to go below, not left to
       wrap on width. */
    .site-foot .wrap {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--s-3);
    }

    .legal {
        margin-left: 0;
        text-align: left;
    }

    .legal span {
        display: block;
    }

    .legal span + span::before {
        content: none; /* the line break is the separator now */
    }
}
