/* The line: one row of screens adrift in space (line.js draws it). The look began as seanaiux.com's, screens bent
   along a curve under a sun, and was then taken off its water and out among the stars. Ours differs where the job
   differs: the row is a ring, it opens on a card about the studio and has the contact card across from that, it
   has four chapters, each marked by a line down the sky where it starts, and a screen that stops in front of you
   is described in words.
   It wears the brand as the design board locked it (12 Oct 2026): gold on black, the dove over the name, Oxanium
   for every word. The name is the board's drawing, on the opening card, so nothing on this page is set in Pirata
   One and its file is not fetched: the one headline that was, on the contact card, the owner found ridiculous in
   it. Colour and type come from base.css's tokens; nothing here or in line.js holds a raw colour. */
:root {
  --font-text: var(--font-ui);
  color-scheme: dark;
  /* the page's ink where no project has lent it its own: cream on black, the quiet ink second, gold for the name */
  --fg: var(--cream); --muted: var(--quiet); --accent: var(--gold); --ground: var(--black);
  /* three sizes of words, the brand's: what is read, the capitals, and a screen's title (drawn in the scene, sized
     by line.js); and one of the page's own, the address on the contact card, as large as a narrow phone's screen
     has room for */
  --t-words: var(--t-body); --t-caps: var(--t-tag); --t-mail: clamp(20px, 5.6vw, 28px);
  --gutter: clamp(20px, 5.3vw, 76px);
  /* what line.js reads. A project lends the page its own colours while it is in front; a project with no palette
     of its own that names no sky takes these. The two cards that are the studio's own are the brand's: black space
     under a gold sun (line.js, worldOf()). */
  --sky-tint: var(--gold); --sky-glow: var(--gold);
  --card: var(--surface); --card-word: var(--cream); --card-mark: var(--gold); --card-mark-hi: var(--gold-hi); --card-mark-lo: var(--gold-lo);
  --title: var(--cream);
  /* space before the scene has drawn, and where it cannot */
  --sky-top: var(--black); --sky-low: var(--surface);
}
/* A phone, upright or on its side, and only a phone (a finger for a pointer; a narrow window on a desk keeps the
   brand's sizes): the words under a screen a step smaller. The owner, of their own phone: "it just feels too
   cluttered, we can tone down the text sizes (descriptions n titles n shi), just a tiny bit ... rn the text
   overshadows even the text in the reel. make it smaller on mobile only!". At 17px what is read under a screen was
   larger than the largest words in the film over it. The title, which the scene draws, goes down with them
   (line.js, layout(): 18px for 20). */
@media (pointer: coarse) and (max-width: 599px), (pointer: coarse) and (max-height: 480px) { :root { --t-words: 15px; --t-caps: 12px; } }

html, body { height: 100%; }
::selection { background: var(--accent); color: var(--black); }
body { overflow: clip; overscroll-behavior: none; background: linear-gradient(var(--sky-top), var(--sky-low) 46%, var(--sky-top) 130%) fixed; color: var(--fg); font: 400 var(--t-words)/1.5 var(--font-text); }
:focus-visible { outline: 2px solid var(--fg); outline-offset: 4px; border-radius: 2px; }

/* The bar: the four chapters of the line and nothing else, in the order they stand in either side of the studio's
   card, the two to its left and then the two to its right. It had the brand in one corner and a Contact button in
   the other, and in a narrow window that made two rows of it. The owner, of their phone: "too cluttered. idk what
   to remove. the top part or the bottom sections part"; and, having had it both ways: "let's remove the
   logo/wordmark and contact from the navbar, let's leave the section titles, and space them out a lil bit. on
   mobile they are already kinda crammed in there so it's ok". So the brand is on the opening card and nowhere
   else, and the way to the contact card is that card's own link, or going along the row, which comes round to it
   either way.
   In a window 600px wide and more the four are set about the middle of the bar, over the screen in front, and
   further apart than they were (24px and 32px, when they stood beside the Contact button): the row is the same
   either side of its middle now, and four words alone in a corner would leave the other corner looking as if
   something had gone from it. There they are the chapters' whole names. In a narrower window they are as they
   were on a phone: spread from gutter to gutter, saying "AI" and "Web", and on the narrowest set a size smaller.
   The bar is one row in every window, so the scene begins at the same height under it everywhere (line.js, BAR). */
.bar { position: fixed; z-index: 3; inset: 0 0 auto; padding: max(var(--s3), env(safe-area-inset-top)) max(var(--gutter), env(safe-area-inset-right)) 0 max(var(--gutter), env(safe-area-inset-left)); pointer-events: none; }     /* the sides too: on its side a phone's notch is at one end of the bar */
.bar a { pointer-events: auto; display: inline-flex; align-items: center; min-height: var(--tap); text-decoration: none; }
.bar ul { display: flex; justify-content: space-between; gap: var(--s2); }
@media (min-width: 600px) { .bar ul { justify-content: center; gap: clamp(var(--s6), 4.5vw, var(--s8)); } }
.bar nav a { --ls: .04em; position: relative; padding: 0 var(--s1); margin: 0 calc(var(--s1) * -1); color: var(--muted); font-size: 12px; font-weight: 600; letter-spacing: var(--ls); text-transform: uppercase; white-space: nowrap; transition: color .15s ease-out; }     /* the padding is room for a finger: "AI" alone is 11px wide on the narrowest phone */
@media (max-width: 359px) { .bar nav a { font-size: 11px; } }
@media (min-width: 480px) { .bar nav a { --ls: .08em; font-size: var(--t-caps); } }
/* The line under the current link is one rule under the whole name, drawn by the link and not by its text: as a text
   underline it was two, one under "AI" and one under "solutions" (the two are separate boxes, so that the second can
   be left out in a narrow window), and they did not meet. The owner: "make this underline spread the whole section
   name, fill that ugly ahh gap". It begins under the first letter and ends under the last (the space the last
   letter's spacing leaves after it is not underlined), where the text underline was: its top edge 5px under the
   letters' feet, which are a third of their size under the link's middle. A border and not a background, so that it is still there where the system sets the colours. */
.bar nav a[aria-current] { color: var(--fg); }
.bar nav a[aria-current]::after { content: ""; position: absolute; left: var(--s1); right: calc(var(--s1) + var(--ls)); top: calc(50% + .33em + 5px); border-top: 1px solid; }
@media (hover: hover) { .bar nav a:hover { color: var(--fg); } }
.bar nav a:active { color: var(--fg); }
@media (max-width: 599px) { .long { display: none; } }     /* "AI" and "Web" where the row has not the room for both words */

/* the stage */
.stage { position: fixed; inset: 0; touch-action: none; -webkit-user-select: none; user-select: none; cursor: grab; }
.stage.is-held { cursor: grabbing; }
.gl { position: absolute; inset: 0; display: block; width: 100%; height: 100%; opacity: 0; transition: opacity .5s ease-out; }
.is-ready .gl { opacity: 1; }

/* A screen's words. They sit under its bottom edge, as wide as the screen, and ride with it (line.js sets the
   place). Two columns where the screen is wide enough: what it is on the left, under its title, and the description
   on the right. --title-h is the room the drawn title takes. */
.stops { margin: 0; padding: 0; list-style: none; }
.stop h2 { font: 600 var(--t-row)/1.2 var(--font-text); }     /* seen only on the plain page; the scene draws a screen's title itself */
.stop { position: absolute; left: 0; top: 0; width: var(--cw, 300px); display: grid; row-gap: var(--s3); padding-top: var(--title-h, 36px); visibility: hidden; opacity: 0; color: var(--fg); transition: opacity .16s ease-out, visibility 0s .16s, color .2s ease-out; transform-origin: 0 0; will-change: transform; }
.stop.is-on { visibility: visible; opacity: 1; transition: opacity .16s ease-out, color .2s ease-out; }
.facts { display: grid; row-gap: var(--s1); align-content: start; min-width: 0; }
.facts:empty { display: none; }     /* the studio's cards have none (the opening card's motto is gone, line.js) */
.facts p, .links { font-size: var(--t-caps); font-weight: 600; letter-spacing: .08em; line-height: 1.4; text-transform: uppercase; color: var(--muted); transition: color .2s ease-out; }
.facts .role { color: var(--fg); }
.say { position: relative; min-width: 0; order: -1; }     /* in one column the description comes first, then what it is and where to see it */
.desc { text-wrap: pretty; }
/* The caret is a box of its own over the words, not a thing among the letters: line.js puts it after the last letter
   typed. Among the letters it was a pixel of width and a place a line could break, and a full line dropped its end to
   the next row while the caret was in it. */
.caret { position: absolute; left: 0; top: 0; width: 0; height: 1em; margin-top: -.07em; outline: 1px solid var(--mark, var(--accent)); opacity: 0; pointer-events: none; }
.desc.is-done + .caret { animation: blink 1s steps(1) 3 forwards; }
@keyframes blink { 50% { outline-color: transparent; } 100% { outline-color: transparent; } }
.links { display: flex; flex-wrap: wrap; column-gap: var(--s5); }
.links a { display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); color: var(--muted); font-weight: 700; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 5px; cursor: pointer; }
.links a.is-first { color: var(--fg); text-decoration-color: var(--mark, var(--accent)); text-decoration-thickness: 2px; }     /* the one action: the page's ink over a rule in the world's brand colour */
.links a.as-is { font-size: var(--t-mail); letter-spacing: 0; line-height: 1.2; text-transform: none; text-decoration: none; }     /* an address is not set in capitals, and it is what the contact card is for: the largest words under any screen. No underline: "remove the underline from paul@codificio.dev" */
/* A stop with nothing said under it (the contact card): no caret waits there. It is read down its middle line: the
   question the scene draws on the card, then its one link, the address, which is not under the card but in it,
   right under that question (line.js sets --mail-y, how far above the block's own corner), then the card's title.
   The block itself is only the room the title takes, so the sky is cleared behind the title as behind any words. */
.is-bare .say { display: none; }
.is-end .links { position: absolute; left: 0; right: 0; top: var(--mail-y, 0px); justify-content: center; margin: 0; transform: translateY(-50%); }
/* The address in the scene is drawn on the card by the scene itself (line.js, contactArt()): in the dove's gold, as
   large as the card has room for, in the card's lower part, there whenever the card is. The owner, of the address
   with a picture of the card: "i just wanna make the mail bigger and maybe give it some effect to stand out more",
   then "make the cotact email stick there, be permanenylt visible". This link is over it, the same size
   (--mail-size, line.js) and in the same place, only while the card is in front, and is not seen: it is there to be
   followed, focused and read out. */
html:not(.no-gl) .is-end .links a.as-is { font-size: var(--mail-size, var(--t-mail)); color: transparent; -webkit-text-fill-color: transparent; }
.stops.is-cols .stop.is-end { display: block; padding-top: var(--title-h, 52px); }     /* not the two columns: a link placed out of the flow would be measured from its column, not from the card */
.links svg { width: 10px; height: 10px; flex: none; }
@media (hover: hover) { .links a:hover { color: var(--fg); text-decoration-thickness: 2px; } }
.links a:active { opacity: .7; }
.links .note { display: inline-flex; align-items: center; min-height: var(--tap); }
.stops.is-cols .stop { grid-template-columns: minmax(0, 34fr) minmax(0, 66fr); column-gap: var(--s6); row-gap: var(--s1); padding-top: 0; }
.stops.is-cols .facts { grid-row: 1 / span 2; padding-top: var(--title-h, 52px); }
.stops.is-cols .say { order: 0; grid-column: 2; padding-top: var(--s4); }     /* the paragraph's first line level with the title's */
.stops.is-cols .links { grid-column: 2; }
/* The opening card's two columns: its title, which the scene draws at the card's left corner, and beside it the
   paragraph, as a project's are, but the first column only as wide as the title (--name-w, line.js), so the
   paragraph begins the columns' own gap after it. A project's first column is a third of the card, which its facts
   need; the opening card has no facts, and its title stood alone there with the paragraph far off. The owner, of a
   desk: "it follws the spacing of the project cards, but that's justified cuz it stretches for the video size, but
   here it's eh"; then, of the paragraph set down the card's middle under the title: "no i don't like it. keep the
   left aligment as before, but bring the about us closer to the description, they were very spaced apart before". */
.stops.is-cols .stop[data-id="about"] { grid-template-columns: var(--name-w, minmax(0, 34fr)) minmax(0, 66fr); }
/* a window too small for the words (line.js, layout()): first without the list of facts, then without the last lines */
.stops.is-tight .stop:not(.is-own) .facts { display: none; }
.stops.is-clamped .desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--lines, 8); overflow: hidden; }
/* A window wider than it is high with no room under a screen for any of its description (a phone on its side; line.js,
   layout()). Whoever turns a phone on its side wants the films larger, so the screen takes all the height the bar
   leaves and one line is kept under it: its name, which the scene draws at its left corner, and across from it, on
   the name's own line, the way in. The description is still there for a screen reader. The words ran off the bottom
   of such a window before, and the address on the contact card stood on the line typed over it. */
.stops.is-brief .stop:not(.is-end) { display: block; padding-top: calc(var(--title-h) - 36px); }     /* the link's letters on the line the name stands on */
.stops.is-brief .stop .facts, .stops.is-brief .desc, .stops.is-brief .caret, .stops.is-brief .links a:not(.is-first) { display: none; }
.stops.is-brief .say { display: contents; }
.stops.is-brief .stop:not(.is-end) .links { flex-wrap: nowrap; justify-content: flex-end; }
.stops.is-brief .stop:not(.is-end) .links > * { flex: none; white-space: nowrap; }
/* where the way in would reach the name it shares the line with, line.js first has it say only "Visit"; where that would still reach, the name is the one kept */
.stops.is-brief .stop.is-linkless .links { visibility: hidden; }

/* Previous and Next: for a keyboard, shown only when it reaches them */
.steps { position: absolute; z-index: 4; left: var(--gutter); bottom: var(--s4); display: flex; gap: var(--s2); margin: 0; pointer-events: none; }
.steps button { min-height: var(--tap); padding: 0 var(--s4); border: 1px solid var(--fg); border-radius: var(--radius); background: var(--ground); color: var(--fg); font: 600 var(--t-caps)/1 var(--font-text); letter-spacing: .08em; text-transform: uppercase; cursor: pointer; opacity: 0; pointer-events: none; }
.steps button:focus-visible { opacity: 1; pointer-events: auto; }

/* A chapter's start: a line down the sky in the middle of the break before the chapter's first screen, and beside
   it, over the screens either side, the names of the two chapters it parts, in spaced capitals, the way a star
   chart names things.
   line.js sets where they are, frame by frame, and the inks: --muted for the line, --label for the names. */
.mark { position: absolute; left: 0; top: 0; width: 1px; height: 100%; margin: 0; visibility: hidden; pointer-events: none; will-change: transform; }
.mark::before { content: ""; position: absolute; inset: 0; background: linear-gradient(transparent 72px, var(--muted) 150px, var(--muted) 82%, transparent); opacity: .45; }
.mark span { position: absolute; left: 0; top: 0; color: var(--label, var(--muted)); font-size: var(--t-caps); font-weight: 400; letter-spacing: .24em; line-height: 1; text-transform: uppercase; white-space: nowrap; text-shadow: 0 0 6px var(--black), 0 0 2px var(--black); will-change: transform, opacity; }     /* the shadow is not seen on the dark of space; it is what a name is read by in the moment the sun passes behind it */

.plain { position: fixed; inset: auto var(--gutter) var(--s7); }

/* No WebGL 2: the same words as a plain page, each under a still of its site */
.no-gl body { overflow: auto; }
.no-gl .bar { position: absolute; }     /* this page scrolls, and the bar has no ground of its own: it stays at the top and is not carried down over the words */
.no-gl .stage { position: static; cursor: auto; touch-action: auto; padding: var(--s10) var(--gutter) var(--s9); }
.no-gl .gl, .no-gl .steps { display: none; }
.no-gl .stops { display: grid; row-gap: var(--s8); max-width: 640px; }
.no-gl .stop { position: static; width: auto; visibility: visible; opacity: 1; padding-top: 0; }
.no-gl .stop h2 { position: static; width: auto; height: auto; clip-path: none; white-space: normal; }
.no-gl .is-bare .say { display: block; }     /* what the contact card asks in the scene is said in words here (line.js, plain()) */
.no-gl .is-end .links { position: static; transform: none; justify-content: flex-start; }
.no-gl .stop img { display: block; width: 100%; height: auto; border-radius: var(--s3); }
.no-gl .say { order: 0; } .no-gl .facts { order: 1; } .no-gl .links { order: 2; }     /* the name, the still, the words, the facts, the way in */
.no-gl .caret { display: none; }

@media (prefers-reduced-motion: reduce) { .gl, .stop { transition: none; } .caret { display: none; } }
