/* ============================================================
   Victic - shared structure. Palette + background live in the
   per-version <style> block; nothing chromatic is decided here.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img { max-width: 100%; display: block; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

.wrap { width: min(1360px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter); }

/* The 12 columns hold at EVERY width unless something collapses them, and the
   section grids place their children with inline `style="grid-column:1/span 5"`
   and friends. Left alone, a five-column block on a 400px phone is a ~150px
   ribbon of prose: not overflowing, just unreadable, which is why it survives a
   link-and-status check. Collapse every direct child to full width on narrow
   screens.

   !important is load-bearing here, not laziness: the placements are INLINE
   styles, and inline beats any stylesheet selector without it. The alternative
   is replacing them with utility classes across four pages plus the drafts, for
   the same result. If the inline styles are ever refactored into classes, drop
   the !important with them.

   1080px matches the breakpoint where .hero__type and .stories already go full
   width, so the whole page reflows on one line rather than in stages. */
@media (max-width: 1080px) {
  .band .grid > * { grid-column: 1 / -1 !important; }
}

.meta {
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

/* ---------- boot: giant wordmark that collapses on scroll -------- */

/* Two layouts (07-10-2026). The DEFAULT is static: the wordmark, then the
   cue line, then the page, in normal flow. The pinned scrub below is an
   enhancement that needs site.js running (html.js) AND motion allowed; with
   Reduce Motion on, scripts off, or site.js failing, every rule it depends on
   (the pin, the -22vh overlap, the cue pinned to the bottom) is off together.
   Twice a phone with Reduce Motion got half of it and the hero landed on the
   wordmark, so keep the pinned rules inside this one block. */
.boot { position: relative; }
.boot ~ main { position: relative; }
.boot__stage {
  display: grid; place-items: center; align-content: center;
  padding: 22vh 0 12vh; row-gap: clamp(3rem, 10vh, 6rem);
  overflow: hidden;
}
@media screen and (prefers-reduced-motion: no-preference) {
  /* 150vh, down from 220vh: 50vh of pinned scrub instead of 120vh, which
     halves the empty stretch before the hero arrives (Andy, 21-09-2026). */
  .js .boot { height: 150vh; }
  /* The page slides up over the empty lower part of the pinned stage, so the
     hero starts arriving at the bottom while the wordmark is still near the
     top, instead of after it (Andy, 21-09-2026). */
  .js .boot ~ main { margin-top: var(--boot-overlap, -22vh); }
  /* Returning to Home from inside the site opens the page scrolled to the hero,
     with the wordmark still above it to scroll back up to (site.js, 07-10-2026;
     it used to be removed outright). svh: on iOS 100vh is the viewport with
     the toolbar hidden, which sat the wordmark low and the cue under the
     toolbar on arrival. */
  .js .boot__stage { position: sticky; top: 0; height: 100vh; height: 100svh; padding: 0; }
  .js .boot__sub { position: absolute; bottom: clamp(2rem, 8vh, 5rem); }
}
.boot__word {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(3.4rem, 17vw, 15rem);
  /* Owned by theme.css so the direction decides the letterform, and so
     site.js can read the same value rather than carrying its own copy. */
  font-variation-settings: "wdth" var(--wordmark-wdth);
  line-height: .84;
  letter-spacing: -.01em;
  color: var(--fg);
  white-space: nowrap;
  will-change: transform, opacity;
  margin: 0;
}
/* Logo-lettered wordmark (07-10-2026). html[data-boot] picks one of four,
   kept for Jakub to compare: 1 logo letters, 2 logo letters + logo V (default,
   Andy), 3 thin letters, 4 thin + logo V. The cobalt line shows under every one,
   its right edge on the last C's (flex-end; the negative margin takes back the
   letter-spacing after the final letter). */
.boot__word:has(.boot__svg) { display: flex; flex-direction: column; align-items: flex-end; line-height: 1; }
/* 1 and 2: filled logo letters on phones, a medium stroke on laptops, where the
   filled ones put too much black in the middle of the screen (07-10-2026). */
.boot__med { display: none; }
@media (min-width: 721px) {
  .boot__svg--thick .boot__fill { display: none; }
  .boot__svg--thick .boot__med { display: inline; }
}
.boot__svg { display: none; height: auto; color: var(--fg); }
.boot__svg--word { width: clamp(17rem, 50vw, 46rem); }
.boot__svg--logo { width: min(70vw, 56rem); }
[data-boot="1"] .boot__svg--word.boot__svg--thick,
[data-boot="2"] .boot__svg--logo.boot__svg--thick,
:root:not([data-boot]) .boot__svg--logo.boot__svg--thick,  /* scripts off: the default, 2 */
[data-boot="3"] .boot__svg--word.boot__svg--thin,
[data-boot="4"] .boot__svg--logo.boot__svg--thin { display: block; }
.boot__tag {
  display: block;
  /* Wraps (right-aligned) rather than outgrowing the screen when the text is
     enlarged; the wordmark column is as wide as this line, so an overlong one
     pushed the logo off the right edge. */
  white-space: normal; text-align: right;
  max-width: calc(100vw - 2 * var(--gutter));
  font-family: var(--mono); font-size: clamp(.62rem, 1.05vw, .95rem); font-weight: 400;
  letter-spacing: .24em; margin-right: -.24em; text-transform: uppercase; color: var(--signal);
}
/* The line sits close under the letters (07-10-2026). With the logo V the
   letters stop 95 of the logo's 598 units above its foot, so on wider screens
   the line is pulled up into that space beside the V's point. Not on phones:
   there the line is wider than the letters and would touch the point. */
.boot__tag { margin-top: clamp(.6rem, 1.2vw, 1.1rem); }
@media (min-width: 721px) {
  [data-boot="2"] .boot__tag, [data-boot="4"] .boot__tag, :root:not([data-boot]) .boot__tag {
    margin-top: calc(min(70vw, 56rem) * -95 / 1796 + clamp(.6rem, 1.2vw, 1.1rem));
  }
}
.boot__sub {
  width: min(1360px, 100% - 2 * var(--gutter));
  display: flex; justify-content: space-between; gap: 1rem;
  will-change: opacity;
}
.boot__cue { display: flex; align-items: center; gap: .6rem; }
.boot__sub > span:last-child { text-align: right; }
/* "/ Instagram" is always line two. The first line stays whole only where it
   fits: em, not px, so enlarged text (browser font size) or a zoomed phone
   lets it wrap instead of running off the screen. */
@media (min-width: 22em) { .boot__sub > span:last-child { white-space: nowrap; } }
.boot__cue i {
  width: 1px; height: 2.2rem; background: var(--line);
  position: relative; overflow: hidden; display: block;
}
.boot__cue i::after {
  content: ""; position: absolute; inset: 0;
  background: var(--signal);
  animation: cue 2.1s cubic-bezier(.7,0,.3,1) infinite;
}
@keyframes cue { 0% { transform: translateY(-100%); } 55%,100% { transform: translateY(100%); } }

/* ---------- persistent bars ---------- */

/* The height of the fixed bar, published as a variable so anything that has to
   clear it (interior page headings, scroll anchors) derives its offset instead
   of repeating a magic number that then goes stale when the bar changes. It
   changed today: the date-and-clock rail above the nav is gone. */
:root { --chrome-h: 62px; }
@media (max-width: 720px) { :root { --chrome-h: 56px; } }

.chrome { position: fixed; inset: 0 0 auto 0; z-index: 60; will-change: opacity; }

/* Anything linked to by #id stops below the bar rather than under it. */
[id] { scroll-margin-top: calc(var(--chrome-h) + 1rem); }

.rail {
  background: var(--rail-bg);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(14px);
}
.rail__inner {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; height: 30px;
}
.rail__inner > span { white-space: nowrap; }
.rail__mid { opacity: .8; }
@media (max-width: 820px) { .rail__mid { display: none; } }

/* OPAQUE. The translucent bar let a page heading read straight through
   the wordmark and the links, and a blur does not save it when the text
   underneath is large and dark. --nav-bg is solid in every direction now,
   so the blur has nothing left to do. */
.nav {
  background: var(--nav-bg);
  border-bottom: 1px solid var(--line);
}
.nav__inner {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1.5rem; height: var(--chrome-h);
}
.brand { display: flex; align-items: center; gap: .75rem; text-decoration: none; }
/* The logo itself (traced to paths from Drive marketing podklady/logo, 04-10-2026),
   replacing the placeholder mark and plain-text name. */
.brand__logo { display: block; height: 34px; width: auto; }
@media (max-width: 720px) { .brand__logo { height: 28px; } }
.brand__mark {
  width: 26px; height: 26px; flex: none;
  border: 1px solid var(--mark-line);
  display: grid; place-items: center;
  position: relative;
}
.brand__mark::before,
.brand__mark::after {
  content: ""; position: absolute; background: var(--fg);
}
.brand__mark::before { width: 9px; height: 1px; }
.brand__mark::after { width: 1px; height: 9px; }
.brand__name {
  font-family: var(--display);
  font-variation-settings: "wdth" 112;
  font-weight: 500;
  font-size: 1.12rem;
  letter-spacing: .01em;
}
.nav__links { display: flex; gap: 1.6rem; }
.nav__links a {
  font-family: var(--mono);
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none; color: var(--dim);
  padding-bottom: 2px; border-bottom: 1px solid transparent;
  transition: color .2s, border-color .2s;
}
.nav__links a:hover, .nav__links a[aria-current] { color: var(--fg); border-color: var(--signal); }
/* Phone: each label on at most two lines ("How it / works"; the first space is a
   no-break space in the markup), all centred on one level. */
@media (max-width: 720px) {
  .nav__links { gap: .9rem; align-items: center; }
  .nav__links a { font-size: .58rem; letter-spacing: .08em; width: min-content; text-align: center; line-height: 1.3; }
}
/* Narrow phones (07-10-2026): the logo was squeezed to 15px at 360 and gone
   at 320, where "Contact" also fell off the screen. The logo gives way first
   but keeps 44px; below 24em the links tighten; and on anything narrower
   still (a zoomed phone, enlarged text) they scroll sideways rather than
   vanish. em, so enlarged text counts as narrow. */
.brand { min-width: 44px; }
@media (max-width: 30em) {
  /* 1000:1, not 1:.001: once the logo stops at its minimum, flexbox only
     hands out that fraction of the overflow when the remaining factors sum
     below 1, so .001 left the links unshrunk. */
  .brand { flex-shrink: 1000; }
  .nav__links { min-width: 0; flex-shrink: 1; overflow-x: auto; scrollbar-width: none; }
  .nav__links::-webkit-scrollbar { display: none; }
}
@media (max-width: 24em) {
  .nav__inner { gap: 1rem; }
  .nav__links { gap: .6rem; }
  .nav__links a { letter-spacing: .04em; }
}

/* ---------- sections ---------- */

/* Section rhythm. Was clamp(5rem, 12vh, 9rem) top AND bottom, which on a
   tall monitor put 18rem of nothing between one section and the next and
   let a two-line section fill a whole viewport. Disney and Netflix run
   roughly 3 to 4rem between blocks; this matches. Andy, 19-09-2026. */
.band { padding: clamp(2rem, 4vh, 3rem) 0; position: relative; }
/* The marquee bar is a hard edge; the first section needs more air under it. */
.marquee + .band { padding-top: clamp(3.5rem, 7vh, 5rem); }
.sechead { margin-bottom: clamp(1.2rem, 2.5vh, 1.8rem); }
.sechead h2 {
  font-family: var(--display);
  font-variation-settings: "wdth" 108;
  font-weight: 400;
  font-size: clamp(1.9rem, 4.4vw, 3.4rem);
  line-height: 1.02;
  letter-spacing: -.02em;
  margin: .7rem 0 0;
}
.lede { font-size: clamp(1rem, 1.45vw, 1.18rem); color: var(--soft); max-width: 46ch; }
.note { color: var(--soft); max-width: 52ch; }
.pull {
  font-family: var(--display);
  font-variation-settings: "wdth" 100;
  font-weight: 400;
  font-size: clamp(1.25rem, 2.1vw, 1.7rem);
  line-height: 1.22; letter-spacing: -.015em;
  margin: 0;
}
.u-mt { margin-top: 1.1rem; }

/* ---------- hero ---------- */

.hero { padding: clamp(2rem, 5vh, 4rem) 0 clamp(2.5rem, 5vh, 4rem); position: relative; }
.hero__grid { align-items: center; row-gap: 3.5rem; }
.hero__type { grid-column: 1 / span 6; }
@media (max-width: 1080px) { .hero__type { grid-column: 1 / -1; } }

.stamp {
  display: inline-block;
  font-family: var(--mono);
  font-size: .64rem; letter-spacing: .14em; text-transform: uppercase;
  border: 1px solid var(--signal); color: var(--signal);
  padding: .34rem .6rem; margin-bottom: 1.4rem;
}
h1 {
  font-family: var(--display);
  font-weight: 300;
  font-variation-settings: "wdth" 110;
  font-size: clamp(2.4rem, 6.2vw, 5.1rem);
  line-height: .98; letter-spacing: -.028em;
  margin: 0 0 1.5rem;
}
h1 em { font-style: normal; color: var(--signal); font-variation-settings: "wdth" 118; }
/* "Our archive doesn't." (07-10-2026) is longer than the old "Ours don't.", so
   on the index hero the blue line drops to the face's normal width and the
   headline to a size that keeps it on one line beside the output cards. */
.hero h1 { font-size: clamp(2.4rem, 5.3vw, 4.8rem); }
.hero h1 em { font-variation-settings: "wdth" 100; }
.hero__cta { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 2.1rem; }

/* ---------- buttons: colour rolls up on hover ---------- */

.btn {
  --btn-bg: var(--fg);
  --btn-fg: var(--bg);
  display: inline-flex; align-items: center; gap: .7rem;
  background: var(--btn-bg); color: var(--btn-fg);
  text-decoration: none;
  font-family: var(--mono);
  font-size: .7rem; letter-spacing: .13em; text-transform: uppercase;
  padding: .95rem 1.5rem;
  border: 1px solid var(--fg);
  position: relative; overflow: hidden;
  transition: color .3s, border-color .3s;
}
.btn span { position: relative; z-index: 2; }
.btn::before {
  content: ""; position: absolute; inset: 0;
  background: var(--signal);
  transform: translateY(101%);
  transition: transform .44s cubic-bezier(.2,.75,.25,1);
  z-index: 1;
}
.btn:hover::before { transform: translateY(0); }
.btn:hover { color: var(--on-signal); border-color: var(--signal); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--fg); }

/* ---------- story stack: 4 cards, fan out on hover ---------- */

.stories { grid-column: 7 / -1; position: relative; }
@media (max-width: 1080px) { .stories { grid-column: 1 / -1; } }

.fan {
  position: relative;
  height: 380px;
  margin-bottom: 1rem;
  --card-w: 190px;   /* stacked width */
  --fan-count: 4;
  --fan-n: 3;        /* gaps between cards, i.e. count - 1 */
  --fan-gap: 10px;

  /* Open width: whatever is left after the gaps, shared between the cards.
     Deriving it means the row can never overlap itself nor overrun the
     container, at any container width. */
  --card-open: calc((100% - var(--fan-n) * var(--fan-gap)) / var(--fan-count));
}
.scard {
  position: absolute; top: 0; left: 0;
  width: var(--card-w); height: 340px;
  background: var(--card);
  border: 1px solid var(--card-line);
  transform: translate3d(calc(var(--i) * 15px), calc(var(--i) * 9px), 0) rotate(calc(var(--i) * .7deg));
  transition: transform .62s cubic-bezier(.19,.9,.22,1), left .62s cubic-bezier(.19,.9,.22,1),
              width .62s cubic-bezier(.19,.9,.22,1), border-color .3s, opacity .4s;
  z-index: calc(10 - var(--i));
  display: flex; flex-direction: column;
  will-change: transform;
}
/* The spread is derived from the container, not a fixed 166px. At 166px the
   fourth card sat 498px from the left edge and ran off the side of the column
   on anything but a very wide screen, which is exactly what it was doing: the
   last card was cut in half.

   The offset is applied through `left`, not `transform`, on purpose: a
   percentage inside translate() resolves against the ELEMENT, so
   `(100% - card-width)` there would mean 100% of the 152px card and spread the
   stack by a handful of pixels. A percentage in `left` resolves against the
   containing block, which is the geometry actually wanted. Dividing the
   leftover width by the number of gaps puts the last card's right edge flush
   with the container's at any width, so the stack always opens fully and never
   overflows. Four absolutely-positioned cards make the layout cost of
   animating `left` irrelevant. */
.fan:hover .scard, .fan:focus-within .scard, .fan.is-open .scard {
  width: var(--card-open);
  left: calc(var(--i) * (var(--card-open) + var(--fan-gap)));
  transform: none;
}
.fan:hover .scard, .fan.is-open .scard { border-color: var(--card-line-hi); }
.scard__bar { display: flex; gap: 3px; padding: 8px 8px 0; }
.scard__bar i { height: 2px; flex: 1; background: var(--tick); }
/* Seen segments and the one playing now; each card sits at a different
   point in its own set of four, as a real story viewer would. */
.scard__bar i.on { background: var(--signal); opacity: .55; }
.scard__bar i.now { background: var(--signal); }
.scard__head {
  display: flex; align-items: center; gap: .45rem;
  padding: .6rem .65rem .5rem;
}
.scard__av {
  width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid var(--card-line-hi); flex: none;
  object-fit: cover;   /* it is an <img> now, not an empty circle */
}
.scard__who { font-family: var(--mono); font-size: .55rem; letter-spacing: .06em; color: var(--soft); }
.scard__body { flex: 1; margin: 0 .65rem; background: var(--shade); position: relative; overflow: hidden; }
.scard__body img { width: 100%; height: 100%; object-fit: cover; }

/* The hatch is the EMPTY state only. With a real frame in the card it would
   sit on top of the picture, so it is scoped off whenever one is present -
   same guard as the report showcase slots. */
.scard__body:not(:has(img))::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(115deg, transparent 0 13px, var(--hatch) 13px 14px);
}

/* An expired story is still shown, but drained of colour and dimmed: the point
   of the card is that the competitor's version is gone while ours is kept, and
   that reads better as the same frame in two states than as two placeholders. */
.scard:not(.scard--kept) .scard__body img { filter: grayscale(1) contrast(.92); opacity: .5; }
.scard__foot {
  font-family: var(--mono); font-size: .52rem; letter-spacing: .09em;
  padding: .55rem .65rem; color: var(--dim); text-transform: uppercase;
  /* Clock on one row, status on the next: an open card is ~130px wide, too
     narrow for "12:23 - caught already" on one line, and two fixed rows mean
     nothing jumps when a status changes. */
  display: flex; flex-direction: column; gap: .2rem;
}
/* The clock: green on a story we caught, red once a competitor's runs out. */
/* Martian Mono ships only 400 and 500 here, so the clock is thickened with a
   hairline stroke rather than a synthesised 700. */
.scard__t {
  font-size: .7rem; font-variant-numeric: tabular-nums; color: var(--fg);
  font-weight: 500; -webkit-text-stroke: .35px currentColor;
}
.scard--kept .scard__t { color: #15803D; }
.scard__t.is-lost { color: #DC2626; }
.scard__s { font-size: .6rem; color: var(--soft); letter-spacing: .02em; white-space: nowrap; }

/* Story-frame cards (index hero, 05-10-2026). The fan, its geometry and the clocks are the
   live ones (base.css, site.js); only the inside of each card changes. */
.sframe { background: transparent; border: 0; }
.sframe__frame { flex: 1; position: relative; overflow: hidden; background: var(--shade); border: 1px solid var(--card-line); }
.fan.is-open .sframe__frame, .fan:hover .sframe__frame { border-color: var(--card-line-hi); }
.sframe__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sframe:not(.scard--kept) .sframe__bg { filter: grayscale(1) contrast(.92); opacity: .5; }
.sframe__ovl {
  position: relative; z-index: 1; height: 100%;
  display: flex; flex-direction: column; color: #fff;
  background: linear-gradient(180deg, rgba(0,0,0,.45) 0%, transparent 24%, transparent 74%, rgba(0,0,0,.5) 100%);
}
.sframe__bars { display: flex; gap: 3px; padding: 7px 7px 0; }
.sframe__bars i { height: 2px; flex: 1; background: rgba(255,255,255,.4); }
.sframe__bars i.on, .sframe__bars i.now { background: #fff; }
.sframe__top { display: flex; align-items: center; gap: .4rem; padding: .5rem .55rem; min-width: 0; }
.sframe__av { width: 17px; height: 17px; border-radius: 50%; border: 1px solid rgba(255,255,255,.7); flex: none; object-fit: cover; }
.sframe__who { font-family: var(--mono); font-size: .5rem; letter-spacing: .06em; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.sframe__chip {
  margin-left: auto; padding: .22rem .35rem; flex: none;
  background: var(--signal); color: var(--on-signal);
  font-family: var(--mono); font-size: .45rem; letter-spacing: .09em; text-transform: uppercase;
}
.sframe__top .ico { margin-left: auto; }
.sframe__chip + .ico { margin-left: .15rem; }
.sframe .ico { stroke: #fff; }
.sframe__reply { margin-top: auto; display: flex; align-items: center; gap: .45rem; padding: .55rem; }
.sframe__reply span {
  flex: 1; min-width: 0; border: 1px solid rgba(255,255,255,.75); padding: .3rem .45rem;
  font-size: .58rem; color: rgba(255,255,255,.9); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sframe .scard__foot { padding: .55rem 0 0; }

@media (max-width: 560px) {
  /* Only the card size changes. The spread is container-relative now, so it
     needs no per-breakpoint number: narrowing --card-w is enough for the open
     stack to re-fit itself. */
  .fan { height: 290px; --card-w: 128px; }
  .scard { height: 250px; }
}

/* ---------- hero outputs: Notion, Drive, the period report ----------
   Replaced the story fan on the index hero (07-10-2026); the fan rules above
   stay for the drafts. Each card is a link into What you get. The peek under
   each name is a miniature of the real thing, drawn in markup. */

.outs { grid-column: 7 / -1; }
@media (max-width: 1080px) { .outs { grid-column: 1 / -1; } }
.outs__h { margin: 0 0 1rem; color: var(--fg); letter-spacing: .22em; }
.outs__f { margin: 1rem 0 0; color: var(--signal); letter-spacing: .14em; }
/* Both lines sit flush right. They are positioned rather than text-aligned so
   the stack below can glide them there from the left (left 0 -> 100% while
   translateX 0 -> -100%, on the cards' timing). */
.outs__h, .outs__f {
  width: fit-content; position: relative; left: 100%; transform: translateX(-100%);
  transition: left 1.5s cubic-bezier(.19,.9,.22,1), transform 1.5s cubic-bezier(.19,.9,.22,1);
}
.outs__row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .7rem; }

/* Subgrid: mark, name and peek share rows across the three cards, so the peeks
   start on one line even though the report's list is a row longer. */
.out {
  display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0; min-width: 0;
  background: var(--card); border: 1px solid var(--card-line);
  color: inherit; text-decoration: none; position: relative;
  transition: border-color .3s, transform 1.5s cubic-bezier(.19,.9,.22,1);
  z-index: calc(3 - var(--i, 0));
}
.out:hover, .out:focus-visible { border-color: var(--card-line-hi); transform: translateY(-3px); }
/* The stack (07-10-2026), as the story fan was: until the headline scrolls up
   (site.js adds .is-stacked, then .is-open), each card sits on the first one,
   a little offset and turned, and the two lines sit left. Desktop only; the
   phone list never stacks, and without script nothing is hidden. */
@media (min-width: 641px) {
  .outs.is-stacked:not(.is-open) .out {
    transform: translate(calc(var(--i) * (-100% - .7rem + 14px)), calc(var(--i) * 9px)) rotate(calc(var(--i) * .7deg));
  }
  .outs.is-stacked:not(.is-open) .outs__h, .outs.is-stacked:not(.is-open) .outs__f { left: 0; transform: none; }
}
.out__n { position: absolute; top: .6rem; left: .7rem; font-family: var(--mono); font-size: .68rem; color: var(--signal); }
.out__top { height: 7.4rem; display: grid; place-items: center; padding: 1.6rem .9rem .4rem; }
.out__logo { width: auto; height: 4.2rem; }
/* The report card shows the demo cover as a small stack of pages. */
.out__top--deck img {
  height: 5rem; width: auto; max-width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border: 1px solid var(--card-line);
  box-shadow: 4px 4px 0 -1px var(--card), 4px 4px 0 0 var(--card-line);
}
.out__name { padding: .55rem .9rem .9rem; display: flex; flex-direction: column; gap: .2rem; }
.out__name b {
  font-family: var(--display); font-weight: 400; font-size: clamp(1rem, 1.5vw, 1.3rem);
  line-height: 1.05; letter-spacing: -.01em;
}
.out__name span { font-size: .72rem; color: var(--soft); line-height: 1.3; }

.out__peek { background: var(--bg); border-top: 1px solid var(--card-line); padding: .6rem .7rem .7rem; min-width: 0; }
.peek__bar {
  display: flex; align-items: center; gap: .35rem;
  font-size: .56rem; color: var(--soft); margin-bottom: .45rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.peek__bar img { width: 10px; height: 10px; flex: none; }
.peek { width: 100%; border-collapse: collapse; font-size: .5rem; line-height: 1.2; color: var(--soft); table-layout: fixed; }
.peek th { text-align: left; font-weight: 400; color: var(--dim); padding: 0 0 .3rem; }
.peek td { padding: .28rem 0; border-top: 1px solid var(--line); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.peek th:first-child, .peek td:first-child { width: 44%; }
/* Tags in Notion's own light-mode select colours, as in the feed screenshot on
   What you get: Post blue, Story purple, categories grey. */
.nt { font-style: normal; color: #32302C; background: #E3E2E0; padding: .08rem .25rem; border-radius: 2px; }
.nt--post { color: #183347; background: #D3E5EF; }
.nt--story { color: #412454; background: #E8DEEE; }
/* Drive folders in the colours of the folder screenshot (--f per row). */
.peek__dir::before {
  content: ""; display: inline-block; vertical-align: -1px;
  width: 10px; height: 8px; margin-right: .35rem;
  background: var(--f, var(--soft));
  clip-path: polygon(0 0, 40% 0, 52% 18%, 100% 18%, 100% 100%, 0 100%);
  border-radius: 1px;
}
.peek--list { list-style: none; margin: 0; padding: 0; counter-reset: p; }
.peek--list li { counter-increment: p; padding: .28rem 0; border-top: 1px solid var(--line); }
.peek--list li::before { content: "0" counter(p); color: var(--signal); margin-right: .45rem; font-family: var(--mono); }

/* Phone: three peeks side by side are unreadable, so each card becomes a row
   (mark, name) and the peeks go. */
@media (max-width: 640px) {
  .outs__row { grid-template-columns: 1fr; }
  .out { display: flex; grid-row: auto; align-items: center; }
  .out__top { height: auto; width: 6.5rem; flex: none; padding: 1.5rem .8rem .8rem; }
  .out__logo { height: 2.8rem; }
  .out__top--deck img { height: auto; width: 100%; }
  .out__peek { display: none; }

}

/* ---------- marquee ---------- */

.marquee {
  border-block: 1px solid var(--line);
  padding: .85rem 0;
  overflow: hidden;
  background: var(--rail-bg);
}
.marquee__t {
  display: flex; gap: 2.6rem; width: max-content;
  animation: slide 46s linear infinite;
}
.marquee__t span {
  font-family: var(--mono);
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--soft); white-space: nowrap;
  display: flex; align-items: center; gap: 2.6rem;
}
.marquee__t span::after { content: ""; width: 3px; height: 3px; background: var(--signal); }
@keyframes slide { to { transform: translateX(-50%); } }
.marquee:hover .marquee__t { animation-play-state: paused; }

/* ---------- instagram post grid ---------- */

.posts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gutter); }
@media (max-width: 940px) { .posts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.post {
  background: var(--card);
  border: 1px solid var(--card-line);
  display: flex; flex-direction: column;
  transition: border-color .3s, transform .45s cubic-bezier(.2,.8,.2,1);
}
.post:hover { border-color: var(--card-line-hi); transform: translateY(-5px); }
.post__head { display: flex; align-items: center; gap: .5rem; padding: .7rem .75rem; }
.post__av {
  width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--card-line-hi); flex: none;
  object-fit: cover;
}
.post__who { font-family: var(--mono); font-size: .58rem; letter-spacing: .06em; }
.post__tag { margin-left: auto; font-family: var(--mono); font-size: .5rem; letter-spacing: .08em; color: var(--dim); text-transform: uppercase; }
.post__media { aspect-ratio: 1; background: var(--shade); position: relative; overflow: hidden; }
.post__media img { width: 100%; height: 100%; object-fit: cover; }

/* Empty state only, as above: with a picture in the frame this texture would
   be painted over it. */
.post__media:not(:has(img))::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(135deg, var(--hatch) 0 1px, transparent 1px) 0 0 / 15px 15px,
    radial-gradient(circle at 62% 38%, var(--bloom) 0%, transparent 58%);
}
/* Action row in Instagram's order: like, comment, share, and save pushed right.
   The heart is filled red, as if the post had been liked. */
.post__acts { display: flex; align-items: center; gap: .85rem; padding: .7rem .75rem .45rem; }
.post__act { display: inline-flex; align-items: center; gap: .3rem; }
.post__act b { font-family: var(--mono); font-size: .6rem; font-weight: 500; letter-spacing: .04em; color: var(--fg); }
.post__save { margin-left: auto; }
/* Two cards across a phone are ~156px each, and the full row needs ~200px:
   the save icon stuck out past the right card and widened the page by 5px
   at 390, 20 at 360, so iPhones zoomed out or wobbled sideways (07-10-2026).
   Tighter on phones, share then comment dropped as the card narrows, and the
   row clips rather than ever widening the page again. */
.post__acts, .post:not(.post--story) .post__head { min-width: 0; overflow: hidden; }
.post:not(.post--story) .post__who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 30em) {
  .post__acts { gap: .45rem; padding: .6rem .6rem .4rem; }
  .post__acts .ico { width: 15px; height: 15px; }
  .post__act { gap: .2rem; }
  .post__act b { font-size: .55rem; }
  .post__act:nth-child(3):not(.post__save) { display: none; }
  /* Room for the whole handle beside "carousel" on a 390px phone. */
  .post:not(.post--story) .post__head { gap: .35rem; padding: .6rem; }
  .post__who { letter-spacing: .02em; }
  .post__tag { letter-spacing: .04em; }
}
@media (max-width: 22em) {
  .post__act:nth-child(2):not(.post__save) { display: none; }
}
.ico { width: 18px; height: 18px; fill: none; stroke: var(--fg); stroke-width: 1.7; stroke-linejoin: round; stroke-linecap: round; flex: none; }
.ico--liked { fill: #ED4956; stroke: #ED4956; }
.ico--sm { width: 14px; height: 14px; }

/* The story: the frame fills the card and the chrome sits on top of it,
   bars, handle and close at the top, the reply pill at the bottom. Square
   corners to stay in the site's language. */
.post--story { position: relative; overflow: hidden; min-height: 100%; }
.post--story .post__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.post__ovl {
  position: relative; z-index: 1; height: 100%;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, rgba(0,0,0,.45) 0%, transparent 22%, transparent 72%, rgba(0,0,0,.5) 100%);
  color: #fff;
}
.post__bars { display: flex; gap: 3px; padding: 8px 8px 0; }
.post__bars i { height: 2px; flex: 1; background: rgba(255,255,255,.45); }
.post__bars i:first-child { background: #fff; }
.post--story .post__who, .post--story .post__tag { color: #fff; }
.post--story .post__tag { opacity: .8; margin-right: .2rem; }
.post--story .ico { stroke: #fff; }
/* The story's category sits where a real story shows its age, top right. */
.post__cat.post__cat--chip {
  margin: 0 .2rem 0 auto; padding: .3rem .45rem;
  background: var(--signal); color: var(--on-signal);
}
.post__reply { margin-top: auto; display: flex; align-items: center; gap: .6rem; padding: .75rem; }
.post__reply span {
  flex: 1; border: 1px solid rgba(255,255,255,.75); padding: .45rem .65rem;
  font-size: .72rem; color: rgba(255,255,255,.9);
}
.post__cat {
  font-family: var(--mono); font-size: .5rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--signal);
  padding: 0 .75rem .7rem;
}

/* ---------- comparison table ---------- */

.cmp { width: 100%; border-collapse: collapse; }
.cmp th, .cmp td { text-align: left; padding: 1.05rem .9rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.cmp thead th {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dim); font-weight: 400;
  border-bottom-color: var(--fg);
}
.cmp tbody th { font-weight: 400; color: var(--soft); width: 32%; }
.cmp .yes { color: var(--fg); }
.cmp .yes::before { content: "+ "; color: var(--signal); font-family: var(--mono); }
.cmp .no { color: var(--dim); }
.cmp .no::before { content: "- "; font-family: var(--mono); }
.cmp tbody tr { transition: background .25s; }
.cmp tbody tr:hover { background: var(--rail-bg); }
@media (max-width: 760px) {
  .cmp thead { display: none; }
  .cmp tr { display: block; border-bottom: 1px solid var(--fg); padding: .7rem 0; }
  .cmp th, .cmp td { display: block; border: 0; padding: .3rem 0; }
  .cmp td::before { content: attr(data-l) " / "; font-family: var(--mono); font-size: .6rem; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); }
}

/* ---------- closing ---------- */

.end { text-align: center; padding: clamp(3rem, 7vh, 5rem) 0; }
.end h2 { max-width: 20ch; margin-inline: auto; }
.foot {
  border-top: 1px solid var(--line);
  padding: 1.6rem 0;
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
}

/* ---------- reveal ---------- */

/* Hidden until scrolled to ONLY while site.js runs (html.js, set in each
   page's head and taken off again if site.js never marks the page ready).
   An ungated .rv blanked most of every page with scripts off (07-10-2026). */
.js .rv { opacity: 0; transform: translateY(20px); transition: opacity .8s cubic-bezier(.2,.8,.2,1), transform .8s cubic-bezier(.2,.8,.2,1); }
.js .rv.in { opacity: 1; transform: none; }
/* Printing or "Save as PDF" must not drop the blocks not yet scrolled to. */
@media print { .js .rv { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .js .rv { opacity: 1; transform: none; transition: none; }
  .marquee__t { animation: none; }
  .boot__cue i::after { animation: none; }
  .scard, .out, .outs__h, .outs__f { transition: none; }
}

/* Index: the comparison table starts level with the first line of the pull
   quote beside it, and the method quote level with "Checked daily." rather
   than with the section label above it. */
.cmp--top thead th { padding-top: .35rem; }
.pull--level { margin-top: 2.25rem; }

/* Index "one place": four basics under the pull, and the Notion feed shot
   beside them starting level with the pull rather than below a gap. */
.basics { list-style: none; margin: 0; padding: 0; max-width: 52ch; }
.basics li { padding: .7rem 0; border-top: 1px solid var(--line); color: var(--soft); }
.basics li:last-child { border-bottom: 1px solid var(--line); }
.basics b { color: var(--fg); font-weight: 500; }
@media (max-width: 1080px) { .pull--level { margin-top: 0; } }
