/* porticosoft.com.

   The design comes from what the product makes: pages. The site is dark throughout — the company
   mark is silver and electric blue drawn on near black, and building the page around the one asset
   that cannot be recoloured is cheaper than fighting it. The colour that is not blue is borrowed
   from the three file formats the engine writes — PDF red, Word blue, PowerPoint orange — so the
   palette is the product's output rather than a decorative accent.

   The typeface is IBM Plex Sans (OFL 1.1), served from this host. It is the same family the engine
   embeds inside a report, and self-hosting it keeps the promise the rest of the site makes: this
   page asks nothing of a third party.

   Every colour is a semantic token. The block below is the only place a literal colour appears. */

@font-face {
    font-family: "Plex";
    src: url("/fonts/plex-400.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Plex";
    src: url("/fonts/plex-600.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Plex";
    src: url("/fonts/plex-700.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

:root {
    color-scheme: dark;

    --ground: #060a14;
    --ground-2: #04070f;
    --sheet: #0e1424;
    --sheet-2: #131a2e;
    --sunken: #0a0f1c;

    --line: rgba(255, 255, 255, .09);
    --line-strong: rgba(255, 255, 255, .18);
    --topbar-bg: rgba(8, 12, 22, .74);

    --ink: #e8ecf8;
    --ink-muted: #9aa3be;
    --ink-faint: #6c7590;
    --prose-ink: #ccd3e8;
    --code-ink: #d6dcf0;

    --current: #60a5fa;
    --current-ink: #93c5fd;
    --trace: #22d3ee;
    --current-soft: rgba(96, 165, 250, .13);
    --gradient: linear-gradient(135deg, #2563eb 0%, #3b82f6 48%, #22d3ee 100%);
    --on-accent: #fff;

    --pdf: #ff6459;
    --word: #4ba3ee;
    --deck: #f2814f;
    --workbook: #4bc98a;

    --warn: rgba(251, 191, 36, .12);
    --warn-line: rgba(251, 191, 36, .35);

    /* The two things on the stage that are objects rather than surfaces: the case around the screen
       and the sheet of paper in front of it. Neither follows the page — a monitor is dark and paper
       is white whatever is behind them. */
    --bezel: linear-gradient(150deg, #1b2436, #070b14);
    --paper: #fff;

    /* A panel is lit rather than printed, so its shadow is depth rather than a cast on paper. */
    --lift: 0 1px 2px rgba(0, 0, 0, .5), 0 14px 30px -14px rgba(0, 0, 0, .7);
    --lift-high: 0 2px 8px rgba(0, 0, 0, .55), 0 46px 80px -34px rgba(0, 0, 0, .85);

    --halo-1: rgba(37, 99, 235, .62);
    --halo-2: rgba(34, 211, 238, .38);
    --halo-3: rgba(242, 129, 79, .26);
    --halo-opacity: .7;

    --radius: .7rem;
    --radius-lg: 1.15rem;
    --radius-xl: 1.6rem;

    --shell: 1320px;
    --shell-reading: 1080px;
    --gutter: clamp(1.25rem, 3vw, 2.5rem);

    --sans: "Plex", "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
    --display: "Plex", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
    --mono: "Cascadia Mono", "SF Mono", Consolas, ui-monospace, monospace;
}

* { box-sizing: border-box; }

/* Anchors glide rather than teleport, for anyone who has not asked for less movement. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

body {
    margin: 0;
    background: var(--ground);
    color: var(--ink);
    font: 400 16px/1.65 var(--sans);
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;
}

img { max-width: 100%; }

a { color: var(--current-ink); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
    outline: 2px solid var(--current);
    outline-offset: 3px;
    border-radius: .35rem;
}

.shell { max-width: var(--shell); margin: 0 auto; padding-inline: var(--gutter); position: relative; }
.shell--reading { max-width: var(--shell-reading); }
.shell--article { max-width: calc(68ch + 2 * var(--gutter)); }

@media (min-width: 1600px) {
    :root { --shell: 1440px; }
    body { font-size: 17px; }
}

/* ------------------------------------------------------------------ aurora */

/* Three soft fields of colour behind everything, drifting slowly. Radial gradients rather than a
   blurred element: the same look without asking the compositor to blur a full-screen layer on
   every frame. */

.aurora { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }

.aurora span {
    position: absolute;
    width: 70vw;
    height: 70vw;
    min-width: 620px;
    min-height: 620px;
    border-radius: 50%;
    opacity: var(--halo-opacity);
    will-change: transform;
}

.aurora .halo-1 {
    top: -26vw; left: -12vw;
    background: radial-gradient(circle at 50% 50%, var(--halo-1) 0%, transparent 68%);
    animation: drift-a 32s ease-in-out infinite alternate;
}

.aurora .halo-2 {
    top: -14vw; right: -18vw;
    background: radial-gradient(circle at 50% 50%, var(--halo-2) 0%, transparent 66%);
    animation: drift-b 41s ease-in-out infinite alternate;
}

.aurora .halo-3 {
    top: 42vw; left: 34vw;
    background: radial-gradient(circle at 50% 50%, var(--halo-3) 0%, transparent 70%);
    animation: drift-c 47s ease-in-out infinite alternate;
}

@keyframes drift-a { to { transform: translate3d(9vw, 7vw, 0) scale(1.15); } }
@keyframes drift-b { to { transform: translate3d(-11vw, 10vw, 0) scale(1.1); } }
@keyframes drift-c { to { transform: translate3d(7vw, -9vw, 0) scale(1.2); } }

/* -------------------------------------------------------------- reveal */

/* Held back only when the script is there to release it, so a page without JavaScript is a page
   with all of its content rather than an empty one. */

.js .reveal {
    opacity: 0;
    transform: translate3d(0, 22px, 0);
    transition: opacity 1.1s cubic-bezier(.2, .7, .3, 1), transform 1.1s cubic-bezier(.2, .7, .3, 1);

    /* Siblings arrive in order rather than together. The place in the group is counted by the
       script and handed here, because a list of nth-child rules only staggers as far as somebody
       thought to number: the examples page grew a seventh card, and the seventh arrived first. */
    transition-delay: var(--reveal-delay, 0ms);
}

.js .reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    .js .reveal { opacity: 1; transform: none; transition: none; }
    .aurora span, .trace-pulses use, .trace-nodes circle, .screen-glare { animation: none; }
    .enter > *, .stage--enter { animation: none; opacity: 1; transform: none; }
    .hero-traces { display: none; }
    *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ------------------------------------------------------------- arrival */

/* The first screen assembles rather than appearing. This runs on load, not on scroll: everything
   here is already in view, and an IntersectionObserver would release it all in the same frame. */

.enter > * { animation: rise .85s cubic-bezier(.2, .75, .25, 1) both; }
.enter > *:nth-child(1) { animation-delay: .10s; }
.enter > *:nth-child(2) { animation-delay: .20s; }
.enter > *:nth-child(3) { animation-delay: .32s; }
.enter > *:nth-child(4) { animation-delay: .44s; }
.enter > *:nth-child(5) { animation-delay: .56s; }
.enter > *:nth-child(6) { animation-delay: .68s; }

@keyframes rise {
    from { opacity: 0; transform: translate3d(0, 26px, 0); }
    to { opacity: 1; transform: none; }
}

/* The stage arrives last and turns into place, so the artefact is the end of the sentence. */
.stage--enter { animation: settle 1.5s cubic-bezier(.2, .75, .25, 1) .5s both; }

@keyframes settle {
    from { opacity: 0; transform: translate3d(46px, 26px, 0) scale(.94); }
    to { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------- hero traces */

/* The traces live around the stage, where there is nothing to read. Being behind the text is not
   enough — a lit line crossing behind a letterform still drags the eye off the sentence.

   Measured against the stage, which is the picture's own box. Not the hero, and not the stage's
   grid area either: the items are centre-aligned rather than stretched, so the row is as tall as
   the copy beside it and a percentage of the row is nowhere near the artefact. The copy is in the
   other column, so it is not somewhere these can reach at any width.

   Deeper below than above: the last trace runs under the screenshot rather than behind it, and
   needs somewhere to be that the picture is not. The height is stated rather than left to the
   bottom inset — an svg is a replaced element, so with no height of its own it takes the one its
   viewBox implies and the inset is quietly ignored. */
.hero-traces {
    position: absolute;
    inset: -14% -22% auto -6%;
    height: 150%;
    z-index: -1;
    pointer-events: none;
    /* Only to keep them from starting on a hard edge in the gutter. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 16%);
    mask-image: linear-gradient(90deg, transparent 0, #000 16%);
}

/* Stacked, the copy occupies the whole width and there is nowhere left for them to be. */
@media (max-width: 1039px) {
    .hero-traces { display: none; }
}

.trace-lines path { fill: none; stroke: var(--trace); stroke-width: 1; opacity: .3; }

.trace-pulses use {
    fill: none;
    stroke: var(--trace);
    stroke-width: 1.6;
    stroke-linecap: round;
    /* A short dash chasing a long gap reads as one packet moving down the wire. */
    stroke-dasharray: 34 2600;
    stroke-dashoffset: 2634;
    filter: drop-shadow(0 0 4px var(--trace));
    animation: travel 7s linear infinite;
}

.pulse-b { animation-duration: 9s; animation-delay: 1.6s; }
.pulse-c { animation-duration: 8s; animation-delay: 3.1s; }

@keyframes travel { to { stroke-dashoffset: 0; } }

.trace-nodes circle {
    fill: var(--trace);
    opacity: .45;
    transform-box: fill-box;
    transform-origin: center;
    animation: breathe 4.5s ease-in-out infinite;
}

.trace-nodes circle:nth-child(2) { animation-delay: .9s; }
.trace-nodes circle:nth-child(3) { animation-delay: 1.8s; }
.trace-nodes circle:nth-child(4) { animation-delay: 2.7s; }
.trace-nodes circle:nth-child(5) { animation-delay: 3.6s; }

@keyframes breathe {
    0%, 100% { opacity: .28; transform: scale(1); }
    50% { opacity: .9; transform: scale(1.6); }
}

/* ---------------------------------------------------------------- chrome */

.topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: transparent;
    border-bottom: 1px solid transparent;
    transition: border-color .35s, background .35s, backdrop-filter .35s;
}

/* Nothing behind it on arrival, so the header reads as part of the page rather than as a bar laid
   over it. The ground arrives once there is something to separate it from. */
.topbar.is-stuck {
    background: var(--topbar-bg);
    backdrop-filter: blur(14px) saturate(1.4);
    border-bottom-color: var(--line);
}

.topbar-inner { display: flex; align-items: center; gap: 2rem; min-height: 64px; }

.brand {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    color: var(--ink);
    font: 700 1.06rem/1 var(--display);
    letter-spacing: -.02em;
}

.brand:hover { text-decoration: none; }

.brand img {
    display: block;
    width: 32px;
    height: 32px;
}

.brand-name b { font-weight: 400; color: var(--current); }

.topbar nav { margin-left: auto; display: flex; gap: 1.6rem; }

.topbar nav a {
    position: relative;
    color: var(--ink-muted);
    font-size: .93rem;
}

/* The underline grows from the middle rather than appearing: the nav is the one place on the page
   where a hover should feel like a mechanism. */
.topbar nav a::after {
    content: "";
    position: absolute;
    left: 50%; right: 50%; bottom: -.35rem;
    height: 2px;
    border-radius: 2px;
    background: var(--gradient);
    transition: left .25s, right .25s;
}

.topbar nav a:hover, .topbar nav a.current { color: var(--ink); text-decoration: none; }

/* Inside a product the bar carries the product's own sections and then, past a divider, the way
   back out of it. */
.topbar nav .nav-split { margin-left: .3rem; padding-left: 1.5rem; border-left: 1px solid var(--line-strong); }

/* Small enough to sit in a 64px bar, where the company's mark is 32px and does the same job. */
.brand .product-tile { width: 30px; height: 30px; }
.topbar nav a:hover::after, .topbar nav a.current::after { left: 0; right: 0; }

@media (max-width: 700px) {
    .topbar-inner { flex-wrap: wrap; gap: .5rem 1rem; padding-block: .7rem; }
    .topbar nav { order: 3; margin-left: 0; width: 100%; gap: 1.15rem; }

    /* A rule between two links reads as a divider across a row; wrapped, it reads as a mistake. */
    .topbar nav .nav-split { margin-left: 0; padding-left: 0; border-left: 0; }
}

/* ------------------------------------------------------------------ footer */

.footer { position: relative; margin-top: 4rem; padding-block: 3rem 1.75rem; border-top: 1px solid var(--line); }

.footer-cols {
    display: grid;
    grid-template-columns: minmax(220px, 2fr) 1fr 1fr;
    gap: 2.5rem;
    padding-bottom: 2rem;
}

@media (max-width: 720px) {
    .footer-cols { grid-template-columns: 1fr; gap: 1.75rem; }
}

.footer-brand img {
    display: block;
    width: 150px;
    height: auto;
}

.footer-brand p { margin: 1rem 0 0; max-width: 34ch; color: var(--ink-faint); font-size: .87rem; }

.footer h4, .eyebrow, .label {
    font: 600 .72rem/1 var(--mono);
    letter-spacing: .14em;
    text-transform: uppercase;
}

.footer h4 { margin: 0 0 .75rem; color: var(--ink-faint); }
.footer nav { display: flex; flex-direction: column; gap: .45rem; }
.footer nav a { color: var(--ink-muted); font-size: .89rem; }

.footer-legal {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.75rem;
    justify-content: space-between;
    padding-top: .5rem;
    color: var(--ink-faint);
    font-size: .82rem;
}

.footer-legal-said { display: flex; flex-wrap: wrap; gap: .35rem 1.25rem; }
.footer-legal nav { display: flex; flex-flow: row wrap; gap: .35rem 1.25rem; }
.footer-legal a { color: var(--ink-muted); }

/* -------------------------------------------------------------- primitives */

.eyebrow { display: inline-block; margin-bottom: 1.1rem; color: var(--trace); }
.label { color: var(--ink-faint); }

.section { padding-block: clamp(2.75rem, 1.5rem + 3vw, 4.25rem); }
.section--tight { padding-block-start: 0; }
.section-break { margin-top: 2.5rem; }
.cta--lead { margin-bottom: 1.5rem; }

.section > h2 {
    margin: 0 0 .6rem;
    font: 700 clamp(1.65rem, 1.2rem + 1.6vw, 2.5rem)/1.1 var(--display);
    letter-spacing: -.035em;
}

.section > .lede { margin: 0 0 2.25rem; font-size: 1.05rem; color: var(--ink-muted); }

.cta { display: flex; flex-wrap: wrap; gap: .75rem; }

.button {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .8rem 1.5rem;
    border: 0;
    border-radius: 999px;
    font-weight: 600;
    font-size: .95rem;
    transition: transform .2s, box-shadow .2s, border-color .2s;
}

.button:hover { text-decoration: none; transform: translateY(-2px); }

.button--primary {
    background: var(--gradient);
    color: var(--on-accent);
    box-shadow: 0 10px 26px -16px var(--current);
}

.button--primary:hover { box-shadow: 0 16px 32px -16px var(--current); }

.button--quiet {
    background: var(--sheet);
    color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--line-strong);
}

.button--quiet:hover { box-shadow: inset 0 0 0 1px var(--current); }

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 1.1rem; }

/* Every panel on the site is a sheet of paper: white on the light desk, and lit rather than
   printed on the dark one. */
.card, .product, .format, .report-card, .source, .stat {
    background: var(--sheet);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--lift);
}

.card { padding: 1.5rem; transition: transform .3s, box-shadow .3s, border-color .3s; }
.card:hover { transform: translateY(-4px); box-shadow: var(--lift-high); }
.card h3 { margin: 0 0 .5rem; font: 600 1.05rem/1.3 var(--display); letter-spacing: -.015em; }
.card p { margin: 0; color: var(--ink-muted); font-size: .93rem; }

pre.code {
    margin: 0;
    padding: 1.15rem 1.3rem;
    overflow-x: auto;
    background: var(--sunken);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font: .855rem/1.65 var(--mono);
    color: var(--code-ink);
}

.split { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); gap: 1.5rem; }

/* ---------------------------------------------------------------- pricing */

/* Three tiers, equal height so the figures line up across them — a price that sits lower than the
   one beside it reads as the lesser product before a word has been taken in. */
.prices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
    align-items: stretch;
    gap: 1.1rem;
}

.price {
    display: flex;
    flex-direction: column;
    padding: 1.75rem 1.6rem;
    background: var(--sheet);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--lift);
}

/* Two tiers stretched across the full shell read as a landing page with something missing. Capped
   so the pair sits as a pair. Drop this modifier if a third tier ever returns. */
.prices--two { max-width: 780px; }

/* The one we expect most buyers to take, marked without a badge saying so. */
.price--feature { border-color: color-mix(in srgb, var(--current) 34%, transparent); }

.price h3 {
    margin: 0 0 1rem;
    font: 600 1.05rem/1.3 var(--display);
    letter-spacing: -.015em;
}

.price-tag { margin: 0 0 1rem; }

.price-tag b {
    display: block;
    font: 700 clamp(1.9rem, 3.4vw, 2.4rem)/1 var(--display);
    letter-spacing: -.03em;
    color: var(--ink);
}

.price-tag span {
    display: block;
    margin-top: .4rem;
    color: var(--ink-faint);
    font-size: .87rem;
}

.price-note {
    margin: 0;
    color: var(--ink-muted);
    font-size: .93rem;
}
.split > * { display: flex; }
.split pre.code { flex: 1; }

.tag {
    display: inline-block;
    padding: .2rem .6rem;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    font: 600 .68rem/1.5 var(--mono);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

.tag--pdf { color: var(--pdf); border-color: color-mix(in srgb, var(--pdf) 45%, transparent); background: color-mix(in srgb, var(--pdf) 8%, transparent); }
.tag--word { color: var(--word); border-color: color-mix(in srgb, var(--word) 45%, transparent); background: color-mix(in srgb, var(--word) 8%, transparent); }
.tag--powerpoint, .tag--deck { color: var(--deck); border-color: color-mix(in srgb, var(--deck) 45%, transparent); background: color-mix(in srgb, var(--deck) 8%, transparent); }
.tag--excel { color: var(--workbook); border-color: color-mix(in srgb, var(--workbook) 45%, transparent); background: color-mix(in srgb, var(--workbook) 8%, transparent); }

/* ------------------------------------------------------------------- hero */

.hero, .home-hero { padding-block: clamp(2rem, 1rem + 3vw, 3.5rem) clamp(2rem, 1rem + 3vw, 3.5rem); }

.hero-inner, .home-hero-inner { display: grid; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
.hero-inner > *, .home-hero-inner > * { min-width: 0; }

@media (min-width: 1040px) {
    .hero-inner, .home-hero-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); }
}

.hero h1, .home-hero h1 {
    margin: 0 0 1.35rem;
    max-width: 15ch;
    font: 700 clamp(2.4rem, 1.2rem + 4vw, 4.15rem)/1.02 var(--display);
    letter-spacing: -.045em;
}

/* The last word of each headline carries the gradient from the mark, so the sentence ends on the
   brand rather than starting with it. */
.tint {
    background: var(--gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.hero-lede {
    margin: 0 0 2.25rem;
    max-width: 52ch;
    font-size: clamp(1.04rem, .96rem + .4vw, 1.2rem);
    color: var(--ink-muted);
}

/* The provenance line. Small, set apart by a rule rather than by weight: it is a footnote to the
   picture, not a second paragraph competing with the headline. */
.hero-note {
    margin: 2.25rem 0 0;
    padding-left: 1rem;
    border-left: 2px solid var(--trace);
    font: .78rem/1.75 var(--mono);
    color: var(--ink-faint);
}

.hero-note b { color: var(--ink-muted); font-weight: 600; }

/* --------------------------------------------------------------- the stage */

/* The signature. Everything on it is a real file the engine produced — a 16:9 deck and an A4
   invoice, both generated by the same code a customer runs — arranged in perspective so the page
   opens on the artefact rather than on a claim about it. */

.stage {
    position: relative;
    perspective: 1700px;
    perspective-origin: 60% 40%;
    padding-block: 1rem 2.5rem;
    --tilt: 15deg;
    --lean: 6deg;
}

.screen {
    position: relative;
    margin: 0;
    border-radius: clamp(.6rem, 1.4vw, 1rem);
    background: var(--bezel);
    padding: clamp(.4rem, .9vw, .75rem);
    box-shadow: var(--lift-high);
    transform: rotateY(calc(var(--tilt) * -1)) rotateX(var(--lean)) rotateZ(-1.2deg);
    transform-style: preserve-3d;
    transition: transform .8s cubic-bezier(.2, .7, .3, 1);
    overflow: hidden;
}

.screen img { display: block; width: 100%; height: auto; border-radius: clamp(.35rem, .9vw, .6rem); }

/* A slow bar of light crossing the glass. It is the only ornament on the stage. */
.screen-glare {
    position: absolute;
    inset: -40%;
    background: linear-gradient(78deg, transparent 42%, rgba(255, 255, 255, .16) 50%, transparent 58%);
    animation: sweep 7.5s ease-in-out 1.2s infinite;
    pointer-events: none;
}

@keyframes sweep {
    0%, 62% { transform: translateX(-120%); }
    100% { transform: translateX(120%); }
}

.sheet-float {
    position: absolute;
    left: -4%;
    bottom: 0;
    width: 31%;
    margin: 0;
    border-radius: .35rem;
    overflow: hidden;
    background: var(--paper);
    box-shadow: var(--lift-high);
    transform: rotateY(calc(var(--tilt) * -1)) rotateX(var(--lean)) rotateZ(-4.5deg) translateZ(90px);
    transition: transform .8s cubic-bezier(.2, .7, .3, 1);
}

.sheet-float img { display: block; width: 100%; height: auto; }

.chips {
    position: absolute;
    right: -8%;
    top: 6%;
    display: grid;
    gap: .55rem;
    margin: 0;
    padding: 0;
    list-style: none;
    transform: translateZ(120px);
}

.chip {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .85rem;
    background: var(--sheet);
    border: 1px solid var(--line);
    border-radius: 999px;
    box-shadow: var(--lift);
    font: 600 .72rem/1 var(--mono);
    letter-spacing: .08em;
    white-space: nowrap;
    animation: bob 6s ease-in-out infinite alternate;
}

.chip:nth-child(2) { animation-delay: .8s; }
.chip:nth-child(3) { animation-delay: 1.6s; }
.chip:nth-child(4) { animation-delay: 2.4s; }

.chip i { width: .5rem; height: .5rem; border-radius: 50%; background: currentColor; }
.chip--pdf { color: var(--pdf); }
.chip--word { color: var(--word); }
.chip--deck { color: var(--deck); }
.chip--excel { color: var(--workbook); }
.chip span { color: var(--ink-muted); }

@keyframes bob { to { transform: translateY(-9px); } }

/* Below the two-column hero the chips have no margin to float in, so they become a row under the
   screen. Left absolute they either covered the charts or pushed the grid track wider than the
   phone, which took the headline with it. */
@media (max-width: 1039px) {
    .chips {
        position: static;
        display: flex;
        flex-wrap: wrap;
        margin-top: 1.35rem;
        transform: none;
    }
}

@media (max-width: 720px) {
    .stage { perspective: none; padding-bottom: 1.5rem; --tilt: 0deg; --lean: 0deg; }
    .screen { transform: none; }
    .sheet-float { position: static; width: 60%; margin: -14% 0 0 4%; transform: rotateZ(-3deg); }
}

/* ---------------------------------------------------------- the formats */

/* The other signature: four lanes, one per format, drawn on scroll into four panels that take
   their own format's colour. Not a fork — a report produces one file, and the picture should not
   say otherwise. */

.formats { display: grid; gap: 1.25rem; }

/* Two abreast on the way to four: at the width three panels fitted, a fourth column leaves each
   one narrower than the heading inside it. */
@media (min-width: 700px) {
    .formats { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1100px) {
    .formats { grid-template-columns: repeat(4, 1fr); }
}

.format-rail { display: block; width: 100%; height: 54px; overflow: visible; }

/* Four lanes describe four columns. Wherever the panels are not in one row, the lanes point at
   nothing. */
@media (max-width: 1099px) {
    .format-rail { display: none; }
}
.format-rail path { fill: none; stroke: var(--line-strong); stroke-width: 1.5; }

.format-rail .live {
    stroke: var(--trace);
    stroke-dasharray: var(--len, 400);
    stroke-dashoffset: var(--len, 400);
    transition: stroke-dashoffset 1.4s cubic-bezier(.3, .7, .3, 1);
}

.is-in .format-rail .live { stroke-dashoffset: 0; }

.format {
    position: relative;
    padding: 1.6rem;
    overflow: hidden;
    transition: transform .3s, box-shadow .3s, border-color .3s;
}

.format::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 3px;
    background: var(--edge);
}

.format:hover { transform: translateY(-5px); box-shadow: var(--lift-high); border-color: color-mix(in srgb, var(--edge) 40%, transparent); }
.format--pdf { --edge: var(--pdf); }
.format--word { --edge: var(--word); }
.format--deck { --edge: var(--deck); }
.format--excel { --edge: var(--workbook); }

.format h3 { margin: .9rem 0 .5rem; font: 700 1.15rem/1.2 var(--display); letter-spacing: -.02em; color: var(--edge); }
.format p { margin: 0; color: var(--ink-muted); font-size: .93rem; }
.format .ext { font: 600 .72rem/1 var(--mono); letter-spacing: .12em; color: var(--ink-faint); }

/* ------------------------------------------------------------------ steps */

.steps { display: grid; gap: 1rem; counter-reset: step; }

@media (min-width: 900px) {
    .steps { grid-template-columns: repeat(3, 1fr); }
}

.step {
    position: relative;
    padding: 1.6rem 1.5rem 1.5rem;
    border-top: 2px solid var(--line);
    transition: border-color .5s;
}

.step.is-in { border-top-color: var(--current); }

.step::before {
    counter-increment: step;
    content: counter(step, decimal-leading-zero);
    display: block;
    margin-bottom: .9rem;
    font: 700 .78rem/1 var(--mono);
    letter-spacing: .12em;
    color: var(--trace);
}

.step h3 { margin: 0 0 .5rem; font: 600 1.08rem/1.3 var(--display); letter-spacing: -.015em; }
.step p { margin: 0; color: var(--ink-muted); font-size: .93rem; }

/* ---------------------------------------------------------------- catalogue */

/* Every card the same height, not every card in a row: grid equalises a row on its own, so a
   title that wraps makes one row taller than the next and the grid reads as ragged rather
   than as a grid. 1fr on the implicit rows sizes them all to the tallest. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 290px), 1fr)); grid-auto-rows: 1fr; gap: 1.1rem; }

/* The example rows show four, so they say four. Left to auto-fill the same markup lands differently
   in the two places it appears — the product panel's own padding costs it the fourth column, while
   the Paragon page's full-width section keeps it — and one row of four beside a row of three reads
   as a bug rather than as a layout. Below 1200px the fourth column is narrower than a report's name
   wants, so they pair up instead, and on a phone they stack. */
.cards--four { grid-template-columns: minmax(0, 1fr); }

@media (min-width: 560px) {
    .cards--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
    .cards--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.cards--grouped { margin-bottom: 2.5rem; }
.cards--spaced { margin-top: 2.5rem; }

/* A card fills the cell it was given. Most places it is the grid item itself and stretching is the
   grid's own doing — but on the examples page each one sits inside a .reveal wrapper, so it is the
   wrapper the row stretches and the card stops at its own text. That is what made a report with a
   two-line name a taller block than the one beside it. */
.cards > .reveal { display: flex; }
.cards > .reveal > .report-card { flex: 1; }

.report-card {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    padding: 1.35rem;
    color: inherit;
    transition: transform .3s, box-shadow .3s, border-color .3s;
}

.report-card:hover { transform: translateY(-4px); box-shadow: var(--lift-high); border-color: var(--current); text-decoration: none; }
.report-card h3 { margin: 0; font: 600 1.02rem/1.3 var(--display); letter-spacing: -.015em; }
.report-card p { margin: 0; color: var(--ink-muted); font-size: .92rem; }
/* Wraps, because a tag's text is not this stylesheet's to predict: without it a long one
   squashes the row rather than moving to the next line. */
.report-card .meta { display: flex; flex-wrap: wrap; gap: .4rem .5rem; align-items: center; }

/* ---------------------------------------------------------------- one report */

.page-head { padding-block: clamp(2.5rem, 1.5rem + 3vw, 4rem) 1.5rem; }

.page-head h1 {
    margin: .35rem 0 .6rem;
    font: 700 clamp(1.9rem, 1.4rem + 2vw, 2.8rem)/1.08 var(--display);
    letter-spacing: -.04em;
}

.page-head p { margin: 0; color: var(--ink-muted); font-size: 1.03rem; }
.crumb { color: var(--ink-faint); font: .8rem var(--mono); }

.report-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    margin-bottom: .35rem;
}

/* An example opens on the artefact, the way the home page does — except that here the picture is
   this report's own first sheet rather than a signature shot, so it is served flat and unadorned:
   the reader is being shown output to check, not a product photographed at an angle. */
/* Both columns start at the top rather than centring against each other: the picture is as tall
   as the report's page setup makes it, and centred text would begin lower on a portrait example
   than on a widescreen one. The title starts in the same place on every example. */
.report-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, .95fr);
    align-items: start;
    gap: clamp(1.75rem, 4vw, 3.5rem);
}

/* Sized from the sheet rather than to a ratio written here: a deck is wide and an A4 page is
   tall, and both have to sit in the same column without one of them setting the height of the
   section. The ratio is the sheet's own, measured when it was printed and handed over as a custom
   property, so the height is what a sheet of 34rem tall would be. */
.report-shot {
    justify-self: center;
    margin: 0;
    width: min(100%, calc(34rem * var(--sheet-ratio, 16 / 9)));
    padding: clamp(.4rem, .9vw, .7rem);
    border-radius: clamp(.6rem, 1.4vw, 1rem);
    background: var(--bezel);
    box-shadow: var(--lift-high);
}

/* A window of exactly one sheet. The rest are hung behind it and the strip is what moves. */
.report-shot-view {
    overflow: hidden;
    border-radius: clamp(.35rem, .9vw, .6rem);
    aspect-ratio: var(--sheet-ratio, 16 / 9);
}

.report-shot img {
    display: block;
    width: 100%;
    height: auto;
}

.report-shot-strip {
    display: flex;
    transition: transform .75s cubic-bezier(.4, 0, .2, 1);
    will-change: transform;
}

/* A deck cuts: each slide is exactly the frame, and the strip steps one frame at a time. */
.report-shot--slide .report-shot-strip { flex-direction: row; }
.report-shot--slide .report-shot-strip img { flex: 0 0 100%; }

/* A document is scrolled: the sheets stack, and the strip travels at a constant rate rather than
   easing, which is what makes it read as scrolling rather than as a sequence of moves. */
.report-shot--scroll .report-shot-strip { flex-direction: column; }
.report-shot--scroll .report-shot-strip { transition-timing-function: linear; }

/* The join between one sheet and the next, drawn without taking up any room: a border would add a
   pixel per sheet and the strip is moved by percentages of its own height. */
.report-shot--scroll .report-shot-strip img + img {
    box-shadow: inset 0 1px 0 rgba(8, 12, 22, .35);
}

@media (max-width: 860px) {
    .report-head { grid-template-columns: 1fr; }
}

.source { margin: 0 0 1.5rem; overflow: hidden; }

.source > summary {
    padding: .85rem 1.15rem;
    background: var(--sheet-2);
    cursor: pointer;
    font: 600 .82rem/1.4 var(--mono);
    letter-spacing: .04em;
}

.source > summary::marker { color: var(--ink-faint); }
.source pre { margin: 0; border: 0; border-radius: 0; max-height: 32rem; }

.notice {
    padding: .95rem 1.15rem;
    background: var(--current-soft);
    border: 1px solid color-mix(in srgb, var(--current) 28%, transparent);
    border-radius: var(--radius);
    color: var(--ink-muted);
    font-size: .92rem;
}

.notice--warn { background: var(--warn); border-color: var(--warn-line); }

/* ---------------------------------------------------------------- docs */

/* Three columns, because a guide has three things to show: which guide you are in, the guide, and
   where you are within it. Two of those used to be missing and the width went to margins instead. */
.docs-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2rem; padding-block: 2.5rem; }

@media (min-width: 900px) {
    .docs-layout { grid-template-columns: 14rem minmax(0, 1fr); gap: 3rem; }
}

@media (min-width: 1200px) {
    .docs-layout { grid-template-columns: 14rem minmax(0, 1fr) 13rem; }
}

.docs-nav { align-self: start; position: sticky; top: 84px; font-size: .92rem; }
.docs-nav h4 { margin: 1.25rem 0 .5rem; color: var(--ink-faint); font: 600 .72rem/1 var(--mono); letter-spacing: .14em; text-transform: uppercase; }
.docs-nav h4:first-child { margin-top: 0; }
.docs-nav a { display: block; padding: .32rem 0; color: var(--ink-muted); }
.docs-nav a:hover, .docs-nav a.current { color: var(--ink); text-decoration: none; }

/* Stacked, the guide list is a preamble to the guide rather than a rail beside it, and a sticky
   preamble would follow the reader down the page for no reason. */
@media (max-width: 899px) {
    .docs-nav { position: static; }
}

/* Below three columns there is no room for it, and the guide list is the better rail to keep. */
@media (max-width: 1199px) {
    .doc-toc { display: none; }
}

.doc-body { max-width: 80ch; }

/* Where you are inside the guide. Sticky like the guide list, quieter than it: this is a way back
   to a section you have already read, not the page's navigation. */
.doc-toc { align-self: start; position: sticky; top: 84px; font-size: .87rem; }
.doc-toc h4 { margin: 0 0 .6rem; color: var(--ink-faint); font: 600 .72rem/1 var(--mono); letter-spacing: .14em; text-transform: uppercase; }
.doc-toc nav { display: flex; flex-direction: column; gap: .1rem; border-left: 1px solid var(--line); }
.doc-toc a { padding: .3rem 0 .3rem .85rem; margin-left: -1px; border-left: 2px solid transparent; color: var(--ink-faint); }
.doc-toc a:hover { color: var(--ink); text-decoration: none; }
.doc-toc a.current { color: var(--ink); border-left-color: var(--trace); }
.doc-toc a.doc-toc-sub { padding-left: 1.6rem; font-size: .95em; }
.doc-body h1 { margin: 0 0 1.5rem; font: 700 clamp(1.9rem, 1.5rem + 1.4vw, 2.3rem)/1.12 var(--display); letter-spacing: -.035em; }
.doc-body h2 { margin: 2.75rem 0 .9rem; font: 600 1.42rem/1.25 var(--display); letter-spacing: -.02em; }

/* Jumping to a section from the rail would otherwise land it under the sticky header. */
.doc-body h2[id], .doc-body h3[id] { scroll-margin-top: 6rem; }
.doc-body h3 { margin: 2rem 0 .6rem; font: 600 1.08rem/1.3 var(--display); }
.doc-body p, .doc-body li { color: var(--prose-ink); }
.doc-body code { padding: .1rem .35rem; background: var(--sunken); border-radius: .3rem; font: .875em var(--mono); }
.doc-body pre { padding: 1.15rem 1.3rem; overflow-x: auto; background: var(--sunken); border: 1px solid var(--line); border-radius: var(--radius); }
.doc-body pre code { padding: 0; background: none; font-size: .855rem; line-height: 1.65; }
.doc-body blockquote { margin: 1.5rem 0; padding: .1rem 1.1rem; border-left: 3px solid var(--line-strong); color: var(--ink-muted); }
.doc-body table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.doc-body th, .doc-body td { padding: .55rem .75rem; border-bottom: 1px solid var(--line); text-align: start; vertical-align: top; }
.doc-body th { color: var(--ink-faint); font-weight: 600; }
.doc-table-scroll { overflow-x: auto; margin: 1.25rem 0; }

.doc-around { display: flex; justify-content: space-between; gap: 1rem; margin-top: 3.5rem; padding-top: 1.5rem; border-top: 1px solid var(--line); font-size: .93rem; }

/* ---------------------------------------------------------------- company */

.home-lockup {
    justify-self: start;
    width: min(320px, 100%);
    height: auto;
}

.product { padding: clamp(1.5rem, 1rem + 1.4vw, 2.25rem); }
.product + .product { margin-top: 1.1rem; }

.product-head { display: flex; align-items: center; flex-wrap: wrap; gap: .7rem; margin-bottom: .9rem; }

/* Big enough to read as the product's icon rather than as a glyph in front of a word. */
.product-tile { flex: none; width: 38px; height: 38px; }
.product-head h3 { margin: 0; font: 700 1.45rem/1.15 var(--display); letter-spacing: -.03em; }
.product-head .tag { margin-left: .35rem; }
.product-body { display: grid; gap: 1.75rem; }

/* Left the paragraph alone in a panel this wide, the right half was empty. The formats belong
   beside it: the sentence ends on three files, and this is what they are. */
@media (min-width: 900px) {
    .product-body { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 2.5rem; }
}

.product-body > div > p { margin: 0 0 1.5rem; max-width: 60ch; color: var(--ink-muted); }

.aside-panel { padding: 1.5rem 0 0; border-top: 1px solid var(--line); }

/* Beside the copy it is a column with a rule down its side; stacked under it, the rule goes across
   the top instead. Either way one hairline, never a second box. */
@media (min-width: 900px) {
    .aside-panel { padding: 0 0 0 2.5rem; border-top: 0; border-left: 1px solid var(--line); }
}

/* A label, not a heading: it names the list under it and must not compete with the product's own
   name a few lines above. */
.aside-panel h4 {
    margin: 0 0 1.1rem;
    color: var(--ink-faint);
    font: 600 .72rem/1 var(--mono);
    letter-spacing: .14em;
    text-transform: uppercase;
}

.format-list {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: .85rem 1rem;
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
}

.format-list li { display: contents; }
.format-list .tag { justify-self: stretch; text-align: center; }
.format-list p { margin: 0; color: var(--ink-muted); font-size: .93rem; }
.product .cards { margin-top: 1.75rem; }

.strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: clamp(1.6rem, 1rem + 1.8vw, 2.5rem);
    background: var(--current-soft);
    border: 1px solid color-mix(in srgb, var(--current) 22%, transparent);
    border-radius: var(--radius-xl);
}

.strip h2 { margin: 0 0 .35rem; font: 700 1.35rem/1.25 var(--display); letter-spacing: -.025em; }
.strip p { margin: 0; color: var(--ink-muted); }

.card--link { display: flex; flex-direction: column; color: inherit; }
.card--link:hover { border-color: var(--current); text-decoration: none; }
.card--link p { margin: 0 0 .9rem; }
.card--link .card-link { margin-top: auto; }
.card-link { color: var(--current-ink); font: 600 .82rem/1 var(--mono); letter-spacing: .04em; }

.checklist {
    display: grid;
    gap: 1.1rem;
    margin: 2rem 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 760px) {
    .checklist { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.checklist li { padding-left: 1.35rem; border-left: 2px solid var(--line-strong); color: var(--ink-muted); }
.checklist strong { color: var(--ink); font-weight: 600; }

/* -------------------------------------------------------------- editorial */

/* The company's writing. A single centred column left most of a wide screen empty, so the section
   heading moves out into a rail of its own beside the text: the page uses the width, and the reader
   still gets a line length they can follow. */

.editorial {
    display: grid;
    gap: 1.25rem;
    padding-block: clamp(2.25rem, 1.25rem + 2.5vw, 3.5rem);
    border-top: 1px solid var(--line);
}

@media (min-width: 1000px) {
    .editorial { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); gap: 4rem; }
    .editorial > h2 { position: sticky; top: 96px; }
}

.editorial > h2 {
    margin: 0;
    align-self: start;
    font: 700 clamp(1.3rem, 1.1rem + .9vw, 1.75rem)/1.12 var(--display);
    letter-spacing: -.035em;
}

.editorial-body { max-width: 68ch; }
.editorial-body > p { margin: 0 0 1.15rem; color: var(--prose-ink); }
.editorial-body > p:last-child { margin-bottom: 0; }
.editorial-body code { padding: .1rem .35rem; background: var(--sunken); border-radius: .3rem; font: .875em var(--mono); }

/* The section that carries the four principles is not a column of prose, so it is not held to a
   column of prose's width. */
.editorial-body--wide { max-width: none; }
.editorial-lede { max-width: 62ch; }

.principles { display: grid; gap: 1.25rem; margin-top: 2rem; }

@media (min-width: 760px) {
    .principles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.principle { padding-left: 1.35rem; border-left: 2px solid var(--line-strong); }
.principle h3 { margin: 0 0 .55rem; font: 600 1.06rem/1.3 var(--display); letter-spacing: -.02em; color: var(--ink); }
.principle p { margin: 0; color: var(--prose-ink); font-size: .96rem; }

/* ------------------------------------------------------------------ prose */

.prose { max-width: 68ch; padding-bottom: 2rem; }
.prose h2 { margin: 2.75rem 0 .9rem; font: 600 1.42rem/1.25 var(--display); letter-spacing: -.02em; }
.prose h3 { margin: 2rem 0 .5rem; font: 600 1.05rem/1.3 var(--display); }
.prose p { margin: 0 0 1.1rem; color: var(--prose-ink); }
.prose code { padding: .1rem .35rem; background: var(--sunken); border-radius: .3rem; font: .875em var(--mono); }
