/* ============================================================
   CONTENT — the small additions the Stage 1 inner pages need.
   Loads after main.css and adds only what main.css has no class for;
   tokens, type and spacing all come from there.
   ============================================================ */

/* ---------- breadcrumb, above the page title ---------- */
.crumbs{
  font:400 12px/1 var(--mono);letter-spacing:.22em;text-transform:uppercase;
  color:var(--dim);margin-bottom:clamp(18px,2.4vw,26px);
}
.crumbs a{color:inherit;text-decoration:none;transition:color .3s var(--ease)}
.crumbs a:hover{color:var(--gold)}

/* ---------- the reading column ---------- */
.prose{max-width:62ch;color:var(--dim);font:400 clamp(16px,1.1vw,18px)/1.7 var(--sans)}
.prose p + p{margin-top:1.1em}
.prose strong{color:var(--ivory);font-weight:500}
.prose li{margin-top:.6em}
.prose a{
  color:var(--ivory);text-decoration:none;
  border-bottom:1px solid var(--gold-line);
  transition:border-color .3s var(--ease),color .3s var(--ease);
}
.prose a:hover{color:var(--gold);border-bottom-color:var(--gold)}

/* ---------- the three practice areas, as an inline row ---------- */
.practice-links{
  display:flex;flex-wrap:wrap;gap:clamp(18px,3vw,40px);
  list-style:none;margin:clamp(18px,2.4vw,28px) 0 0;padding:0;
}
.practice-links a{
  font:400 12px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--dim);text-decoration:none;transition:color .3s var(--ease);
}
.practice-links a:hover{color:var(--gold)}

/* ---------- the quieter second link on a work card ---------- */
.feat-case{
  display:inline-flex;align-items:center;gap:8px;position:relative;
  margin-top:clamp(20px,2.6vw,28px);margin-left:clamp(18px,2.4vw,28px);
  font:400 12px/1 var(--mono);letter-spacing:.22em;text-transform:uppercase;
  color:var(--dim);text-decoration:none;
  padding:10px 0;transition:gap .3s var(--ease),color .3s var(--ease);
}
.feat-case::after{content:"";position:absolute;left:50%;top:50%;width:100%;height:44px;transform:translate(-50%,-50%)}
.feat-case:hover{gap:14px;color:var(--gold)}

/* ---------- a screen shown on its own, with no copy column ----------
   .feat is a two-column grid (main.css). A case-study page reuses the screen
   half alone — so the viewer in tv.js still binds — and this gives it the whole
   row instead of the 1fr side of a 1fr/1.2fr split. */
.feat-solo{grid-template-columns:minmax(0,1fr)}

/* ---------- previous / next, at the foot of a case study ----------
   Markup contract — a div, NOT a nav (a second landmark makes html-validate
   demand a name on every nav), and each link MUST carry the label span:
   <div class="pager"><a href="/work/the-bridge/"><span class="pager-label">Previous</span>The Bridge</a>
   <a href="/work/openbots/"><span class="pager-label">Next</span>OpenBots</a></div> */
.pager{
  display:flex;justify-content:space-between;gap:clamp(20px,4vw,60px);
  margin-top:var(--sec-gap);padding-top:clamp(24px,3vw,36px);
  border-top:1px solid var(--hairline);
}
.pager a{text-decoration:none;color:var(--ivory);transition:color .3s var(--ease)}
.pager a:last-child{text-align:right}
.pager .pager-label{
  display:block;margin-bottom:8px;
  font:400 12px/1 var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--dim);
}
.pager a:hover{color:var(--gold)}

/* ---------- a still, with its caption ---------- */
figure.still-frame{margin:clamp(28px,4vw,48px) 0;background:var(--surface);border:1px solid var(--hairline);border-radius:8px;overflow:hidden}
figure.still-frame img{display:block;width:100%;height:auto}
figure.still-frame figcaption{padding:12px 16px;font:400 12px/1.5 var(--mono);letter-spacing:.06em;color:var(--dim)}

/* ---------- a code block ---------- */
pre.code{
  margin:clamp(20px,3vw,32px) 0;padding:clamp(16px,2vw,24px);
  background:var(--surface);border:1px solid var(--hairline);border-radius:8px;
  font:400 13px/1.7 var(--mono);color:var(--ivory);
  overflow-x:auto;white-space:pre;
}
/* one narrow-screen fix: the two card links stack instead of keeping the gap */
@media (max-width:620px){.feat-case{margin-left:0;margin-top:4px}}

/* ============================================================
   DOCUMENT PAGES — the rhythm of reading, not of screens.

   main.css sizes a section around a full-bleed project screen: 99px of air
   above and below it, a 72px drop under every heading, and an h2 that runs
   to 2.6rem. All correct on the home, where each section IS a screen. On a
   page whose section is two rows or one paragraph, the same numbers read as
   an empty page — which is what Lorenzo saw on /work/ and on the case
   studies, and he was right. Every rule below is scoped to <main class="doc">,
   so index.html and background/index.html keep the spacing they shipped with.
   ============================================================ */
.doc > section{padding-top:clamp(30px,4vw,52px);padding-bottom:clamp(30px,4vw,52px)}
.doc > section.bg-intro{padding-top:clamp(108px,13vw,150px);padding-bottom:clamp(26px,3.4vw,44px)}
.doc > section:last-of-type{padding-bottom:clamp(48px,6vw,84px)}
.doc .section-head{margin-bottom:clamp(18px,2.2vw,28px)}
.doc .bg-intro .section-head{margin-bottom:clamp(22px,2.6vw,32px)}

/* Four levels, measured rather than asserted. The first version of this rule
   put the h2 within 2.4px of the row title it governs — a 1.115 step where
   main.css declares a 1.25 scale — because it was sized against the h1 and not
   against the h3s that today's heading repair placed under it. Resolved sizes
   at 1440: h1 41.6px · h2 33.9px · row title 19.2px · body 18px. The h2 also
   wraps before the h1 does, which a subordinate heading should. */
.doc h2.section-title{font-size:clamp(1.6rem,2.4vw,2.2rem);max-width:26ch;letter-spacing:-.008em}
.doc .item-title,.doc .pub-title{font-size:clamp(1.1rem,1.35vw,1.2rem)}

/* a definition sentence, where the timeline row carries an organisation.
   .item-org is 10.5px uppercase mono at .14em tracking — right for
   "MCR Groupe", unreadable for a ninety-character sentence. /work/ says
   what each piece IS, so its rows use this instead. */
/* This sat at 15.04px above an .item-desc running 16–18px, so the sentence
   that says what the thing IS was smaller and tighter than the role-and-status
   line under it. main.css §19 raised the dossier's small text to a 16px floor
   for the same reason; this is inside that floor now, and the metadata below it
   drops to the mono meta voice it actually is. */
.item-lede{margin-top:8px;font-size:clamp(1rem,1.15vw,1.1rem);line-height:1.55;color:var(--ivory);max-width:64ch}
/* Keyed off a class that means "this is metadata", not off .item-desc — which
   on /work/ holds "Designer & developer · v0.5.0 · MIT" and on /about/ holds
   160-character prose sentences. Keying on the element set 12px mono on seven
   paragraphs of work history, and overrode the 16px reading floor main.css
   deliberately applies on phones. */
.doc .item-meta{font:400 12px/1.6 var(--mono);letter-spacing:.06em;color:var(--dim);margin-top:10px}

/* the links row is a caption above the screen, not a lid sitting on it */
.practice-links + .feat{margin-top:clamp(22px,2.6vw,34px)}

/* the pager inherits --sec-gap (up to 160px) from main.css; on a document
   page the section padding already carries that air */
.doc .pager{margin-top:clamp(30px,4vw,52px)}

/* the meta grid sits directly above a full-width screen on a case study, so
   it takes the same width — main.css caps .creds at 900px, which is right on
   the home where nothing full-width follows it. And the last cell stretches
   to the end of its row: an odd number of meta rows otherwise leaves a lit
   empty square, because the grid paints its own hairline background. */
.doc .creds{max-width:none}
.doc .creds-list{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
/* No count patch. The grid painted its own hairline through the gaps, so any
   partial last row lit an empty square; the first fix stretched the last cell
   (width then depended on the item count) and the second added one filler cell
   (which at six items produced a spurious third row). The rule moves onto the
   cells, so the grid stops caring how many there are. */
/* Nothing here about the hairline: main.css already paints one line per edge
   through 1px gaps, and restating that was two no-op declarations under a
   comment claiming an effect they did not have. Per-cell insets, which an
   earlier pass tried, doubled every interior edge to 2px. The only rule that
   does any work is auto-fit above — it collapses tracks left empty across the
   whole grid, which is what stops an unfilled cell showing as a lit square and
   what removes the dependency on the item count. */

/* ============================================================
   STATES — what the four-lens audit of 2026-09-04 found missing.

   All three schema lenses landed on the same root cause: main.css:87 resets
   every link to `color:inherit;text-decoration:none`, and the components
   these pages borrowed from the home were never interactive there, so they
   arrived without their hover, active or resting states. A link inside a
   heading or a grid cell rendered byte-identical to the text around it, and
   on /work/ the only target was the words — 113x34px inside a 900x159px row,
   2.7% of it. Everything below is scoped to .doc.
   ============================================================ */

/* the whole row is the target, the way .card and .feat-case already do it */
.doc .exp-item:has(.item-title a){position:relative;transition:background .34s var(--ease)}
.doc .exp-item:has(.item-title a):hover{
  background:rgba(212,165,116,.06);
  box-shadow:inset 2px 0 0 var(--gold-line-hi);
}
.doc .item-title a::after{content:"";position:absolute;inset:0}
.doc .exp-item:has(.item-title a:hover) .item-title a,
.doc .exp-item:has(.item-title a:focus-visible) .item-title a{color:var(--gold)}
.doc .exp-item:has(.item-title a:focus-visible){background:rgba(212,165,116,.05)}

/* a link inside a metadata cell needs to look like one at rest */
.doc .cred-name a{
  border-bottom:1px solid var(--gold-line-hi);
  transition:color .3s var(--ease),border-color .3s var(--ease);
}
.doc .cred-name a:hover,.doc .cred-name a:focus-visible{color:var(--gold);border-bottom-color:var(--gold)}

/* the practice cards on /about/ lift on hover but only their words were the
   target — the home solved this for .card at main.css:512 and :524 */
.doc .offer-item{position:relative}
.doc .offer-name a::after{content:"";position:absolute;inset:0}
.doc .offer-item:has(.offer-name a:hover) .offer-name,
.doc .offer-item:has(.offer-name a:focus-visible) .offer-name{color:var(--gold)}
.doc .offer-item:has(.offer-name a:focus-visible){border-color:var(--gold-line-hi)}

/* a prose link was ivory-on-dim at a measured 2.32:1 with a 1.60:1 underline.
   The underline carries the signifier, so it has to be visible: gold-line-hi
   measures 3.34:1 on the page ground, past the 3:1 WCAG G183 asks for. */
.doc .prose a{border-bottom-color:var(--gold-line-hi);text-underline-offset:3px}

/* five rows in /about/'s credentials list are <div class="pub-row"> — not
   links — but main.css:561 keys the hover on the class, so they slid sideways
   and turned gold under the cursor while doing nothing. */
.doc div.pub-row:hover{padding-left:0;background:none}
.doc div.pub-row:hover .pub-title{color:var(--ivory)}

/* the shot pane is the biggest thing on the page and the only hover response
   that leads nowhere: tv.js binds the buttons, never the image */
.doc .feat-screen.is-shot:hover{transform:none;border-color:var(--hairline)}

/* every state on these pages was a dim-to-gold swap, measured 1.28:1 — a hue
   change with no luminance step, which vanishes in peripheral vision and
   under deuteranopia. Pair the hue with a value change. */
.doc .crumbs a:hover,.doc .pager a:hover .pager-label,.doc .practice-links a:hover{color:var(--ivory)}
.doc .pager a:hover{color:var(--gold)}

/* nothing on twelve pages said which one you were on, and the practice row
   linked the page you were standing on */
/* The nav and the footer are siblings of <main class="doc">, not inside it, so
   scoping this under .doc announced "you are here" to assistive tech and painted
   it nowhere — a fix that left sighted and screen-reader visitors holding
   different models, which is worse than the silence it replaced. Verified safe
   to unscope: neither live page carries a single aria-current, so the rule is
   inert on both. */
[aria-current="page"]{color:var(--gold)}
#nav [aria-current="page"],footer [aria-current="page"]{cursor:default}
#nav [aria-current="page"]::after{display:none}   /* no underline sweep on the page you are on */
.doc .practice-links [aria-current="page"]{border-bottom:1px solid var(--gold-line-hi);padding-bottom:3px}

/* press acknowledgement: no :active rule existed anywhere in either sheet,
   so a click on a link that triggers a document load returned nothing at all
   between the press and the repaint */
.doc .exp-item:has(.item-title a):active,.doc a.pub-row:active{background:rgba(212,165,116,.26)}
.doc .btn-hire:active,.doc .pager a:active,.doc .practice-links a:active{opacity:.75}

/* the closing action — the four lenses agreed this was the site's worst gap:
   eight of the twelve pages ended with no way to act */
.doc .closer .contact-links{margin-top:clamp(20px,2.4vw,30px)}

/* ============================================================
   THE SECTION RAIL — where am I inside this page, and what is left.

   Built for the document pages because the film transport cannot serve them: it
   resolves its ticks from the home's section ids, so on a case study it renders
   as a fill with no landmarks. The rail takes over there and the transport is
   hidden, so a page never carries two progress devices.

   Rewritten 2026-09-04 after a three-lens audit. What the lenses changed:
   the pointer guard (a touch device at this width got marks whose destinations
   could never be revealed); the plate under the label (it was 10px dim ink on
   whatever happened to scroll under it, including a full-width screenshot); the
   hover reach (the labels sat left of the only hover zone, so moving to read one
   erased it); the shape of the states (hover and current were both plain gold,
   two pixels apart); the focus ring (it framed the 44px hit box, not the 5px
   mark); the width (44px crossed into the content column below 1240px, the same
   sin main.css:682 records the transport being fixed for); and the anchoring
   (a viewport offset drifts 666px from the column at 2560px).
   ============================================================ */
.srail{display:none}

/* Gated like the transport it replaces, for the reason main.css:667 gives: this
   is a pointer device, and every label here is revealed by hover. A touch
   device would get marks it cannot read and no transport either. */
@media (min-width:1240px) and (hover:hover) and (pointer:fine){
  body:has(main.doc) #transport{display:none}   /* one wayfinding device per page */
  .srail{
    display:flex;flex-direction:column;align-items:flex-end;gap:2px;
    position:fixed;top:50%;transform:translateY(-50%);
    /* anchored to the content column, not to the viewport, so the marks stay a
       constant distance from the text at any width above 1280px instead of
       drifting into empty ground */
    right:calc(20px + max(0px, (100vw - 1280px) / 2));
    z-index:54;                       /* above #nav (50), below #transport (55) */
  }

  /* the spine: the honest half of the device. Marks encode order; this encodes
     distance, filled from the top to the scroll position. */
  /* On the marks' own axis. It ran 12px inward while the marks sat 0–5px
     inward, so the progress line and its stops were two parallel hairlines
     10px apart rather than one instrument. */
  .srail-spine{
    position:absolute;right:2px;top:0;bottom:0;width:1px;
    background:var(--hairline);
  }
  /* height, not scaleY: the fill is now a measured distance down the column to
     the lit mark, and scaling also squashed the gradient into whatever fraction
     was showing, so the ramp meant nothing until the page was fully read. */
  .srail-fill{
    position:absolute;left:0;right:0;top:0;height:0;
    background:linear-gradient(to bottom,var(--gold-line-hi),var(--gold));
    background-size:100% 400px;
  }

  .srail-dot{
    position:relative;display:flex;align-items:center;justify-content:flex-end;
    /* 26px wide, not 44 — main.css:682 records why the transport's tick halo was
       cut to 26: a 44px halo reached into the page and shadowed real links, and
       44 is the touch spec on a device that has no touch. Height stays 26 so the
       column keeps a comfortable pitch. */
    width:26px;height:26px;padding:0;border:0;background:none;cursor:pointer;
  }
  /* the mark itself — the button is the target, this is the signifier */
  .srail-dot::after{
    content:"";width:5px;height:5px;border-radius:50%;
    background:var(--gold-line-hi);
    transition:background .3s var(--ease),transform .3s var(--ease),box-shadow .3s var(--ease);
  }
  /* Current is FILLED, hovered is RINGED. They were both plain gold two pixels
     apart, so during a hover the only thing separating "where you are" from
     "what you are pointing at" was 2px of diameter. Scale rather than width, so
     the mark grows from its own centre and the column keeps one axis. */
  .srail-dot.is-on::after{background:var(--gold);transform:scale(1.4)}
  .srail-dot:hover::after{box-shadow:0 0 0 1px var(--gold);background:var(--gold)}
  /* the focus ring belongs on the mark, not on the invisible box around it */
  .srail-dot:focus-visible{outline:none}
  .srail-dot:focus-visible::after{
    background:var(--gold);
    box-shadow:0 0 0 2px var(--stage),0 0 0 4px var(--gold);
  }
  .srail-dot:active::after{transform:scale(1.1)}

  .srail-label{
    position:absolute;right:22px;white-space:nowrap;
    font:400 10px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
    color:var(--dim);
    /* the plate is the transport's own (main.css:692) — a fixed overlay label
       has no control over the ground scrolling under it, and this one crosses
       full-width screenshots */
    background:rgba(11,10,16,.92);padding:6px 9px;border-radius:3px;
    opacity:0;transform:translateX(6px);pointer-events:none;
    /* The delay does two jobs. Coming in it is hover intent, so a mouse crossing
       the edge does not detonate ten labels. Going out it is a linger: the
       labels sit to the LEFT of the only hover-able column, so without it a
       reader whose eye and cursor drift left to read one erases the thing being
       read. Widening the column instead would put a 220px invisible corridor
       over the article text, and a corridor that cannot be hovered — which is
       what pointer-events:none makes it — buys nothing at all. */
    transition:opacity .3s var(--ease) .45s,transform .3s var(--ease) .45s,color .3s var(--ease);
  }
  /* One label at a time, the one you are pointing at. Revealing the whole map
     on rail hover was the intent — "what is on this page" is the question you
     bring to a rail — but the margin is only empty on a document page. On the
     home the column sits over the hero portrait, and five plated lines land
     across a face. The usability lens asked for one at a time and it was right;
     the lit mark already answers "where am I" without any label at all. */
  .srail-dot:hover .srail-label,.srail-dot:focus-visible .srail-label{
    opacity:1;transform:none;
    transition-delay:.12s;            /* quick in, slow out */
  }
  /* Same rule as the marks: two meanings must not share one value. The one you
     are pointing at goes gold; the one you are IN goes ivory, so during a hover
     the page can still tell you where you actually are. */
  .srail-dot:hover .srail-label,.srail-dot:focus-visible .srail-label{color:var(--gold)}
  .srail-dot.is-on .srail-label{color:var(--ivory)}
}

/* Inside the rail's own query, not outside it. Hiding the transport
   unconditionally left a fine-pointer window between roughly 700 and 1099px —
   a browser dragged to half a laptop screen beside Slack, which is how a
   forwarded link is usually opened — with neither device, where this morning it
   had the transport. A landmark-less fill beats nothing. */

/* One landing for both jump paths — the Lenis flight and the native fallback
   reduced-motion visitors take. It was a magic number inside the Lenis call, so
   the two landed in different places. */
.doc > section{scroll-margin-top:72px}

@media (prefers-reduced-motion:reduce){
  .srail-dot::after,.srail-label,.srail-fill{transition:none}
}

/* The hire affordance opens a mail client, which is a surprise unless it says
   so — the site's own rule, stated in tv.js, is that an affordance tells the
   truth about what will happen. Written by hire.js, so it never appears when
   the script has not run and the link is still a plain mailto.

   It was 10px uppercase mono in --dim: the site's smallest, most-skipped
   register, the one the whole web uses for disclaimers, with an email address
   inside it rendering as LORENZO.COLOMBANI@LIVE.FR. Two lenses called that a
   signifier present and unseen. It is a promise, not fine print, so it reads
   at the body's small size in sentence case. */
.hire-hint{
  display:block;margin-top:12px;max-width:52ch;
  font-size:clamp(15px,1vw,16px);line-height:1.5;color:var(--dim);
}
/* after the click it stops being a label and becomes the recovery */
.hire-hint.is-live{color:var(--ivory)}

/* the static address beside the button — the fallback for a swallowed click,
   present in the markup so it survives the script being blocked */
.hire-address{margin-top:14px;font-size:clamp(15px,1vw,16px);line-height:1.5;color:var(--dim);max-width:52ch}
.hire-address strong{color:var(--ivory);font-weight:500}
.hire-address a{
  color:var(--ivory);text-decoration:none;
  border-bottom:1px solid var(--gold-line-hi);
  transition:color .3s var(--ease),border-color .3s var(--ease);
}
.hire-address a:hover,.hire-address a:focus-visible{color:var(--gold);border-bottom-color:var(--gold)}

/* In the closer, Email and LinkedIn shipped as identical rows at identical
   weight — and LinkedIn is both the lower-commitment click and the one that
   asks the visitor to compose nothing. So the cheapest path was not the one
   the site exists for. The mail row is the distinct one now; LinkedIn stays,
   quieter, because a second door matters when a mailto silently fails. */
/* The first version asserted a promotion it did not make: two of its three
   declarations repeated values main.css already sets, and its tint measured
   1.06:1 — below the site's own hairline, so invisible. The visible difference
   was entirely the demotion of the row beside it. The mail row now carries a
   gold edge, which is a real signal at any tint. */
/* main.css gives these rows zero left padding, so the label starts flush at the
   row's edge — and the gold bar was painting straight over the first letter.
   "EMAIL" read as "MAIL". Every row in the block gets the same gutter, so the
   labels stay in one column and the bar has somewhere of its own to sit; the
   hover shift main.css animates is preserved, just from the new resting value. */
.doc .contact-links .contact-link{padding-left:18px}
.doc .contact-links .contact-link:hover{padding-left:32px}
.doc .contact-links .contact-link:first-child{
  background:rgba(212,165,116,.16);
  box-shadow:inset 3px 0 0 var(--gold);
}
.doc .contact-links .contact-link:first-child:hover{background:rgba(212,165,116,.24)}
.doc .contact-links .contact-link:not(:first-child) .cl-value{color:var(--dim)}

/* A rail jump moves focus to the chapter heading, which is right — a jump a
   keyboard user cannot continue from is half a jump. It should not paint the
   global focus ring around a whole heading for someone who used a mouse, and
   Chrome counts programmatic focus on a tabindex="-1" element as focus-visible,
   so it did. sectionrail.js marks the pointer case; the keyboard case keeps its
   ring, which is the whole point of having one. */
[data-quiet-focus]:focus-visible{outline:none}
