/* Design system for the Radiata site.
   One product, a handful of pages: the landing page, the help pages, the tip
   jar, and a 404. --accent is the single brand orange, used for buttons, the
   status pill, and the current item in the nav. */

:root{
  --navy:#3D405B; --ink:#1c1e2b;
  --paper:#f7f7f9; --card:#ffffff; --line:#e6e7ec; --muted:#6b6f7d;
  --accent:#E07A5F;   /* the brand orange, site-wide */
  --green:#81B29A;    /* the logo's leaf green -- see assets/favicon.svg */
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0; color:var(--ink); background:var(--paper);
  font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased;
}
a{color:inherit}
.wrap{max-width:960px; margin:0 auto; padding:0 24px}

/* ── Page background wheel ───────────────────────────────
   A decorative wheel washed into the bottom-right corner of the page, with a
   quarter of it running off the right and bottom edges. It sits behind
   everything and eats no clicks; the layer is clipped to its own box, so the
   bleed never adds scroll.
   The layer spans the full page rather than the bottom half. The wheel is
   anchored to the bottom either way, so on a long page it lands in the bottom
   half regardless -- but a half-height box on a SHORT page is shorter than the
   wheel, which chopped its top off and left a band across the middle. The
   wheel needs 738px of room (984 tall, less the 246 that bleeds off the
   bottom); short pages get a mild trim instead of a hard slice.
   Deliberately plain CSS -- no calc(), no var() -- because the four-value
   background-position syntax is where browsers disagree most. */
/* The page fills the window even when the content does not. Without this a
   short page (the tip jar) ends wherever its content ends, and the wheel --
   anchored to the bottom of the BODY, not the window -- floats in the middle
   of the screen with bare paper beneath it. The flex column pins the footer to
   the bottom on those pages; on pages that already overflow the window it
   changes nothing, since margin-top:auto has no free space to absorb.
   body::after is absolutely positioned, so it stays out of the flex flow. */
body{min-height:100vh; display:flex; flex-direction:column}
footer{margin-top:auto}

/* Pages whose content does not fill the screen -- the tip jar, the 404 -- centre
   their main block in the leftover space instead of stacking it under the nav.
   Auto margins on the flex item do it: the free space is split evenly above and
   below, and the block pushes the footer to the bottom by itself. That is why
   footer's own margin-top:auto has to stand down wherever this applies -- three
   competing auto margins would share the space three ways and land the content
   a third of the way down rather than halfway.
   Pages taller than the viewport are untouched: there is no free space, the
   autos resolve to zero, and nothing moves.
   Deliberately NOT display:flex on the section. The home page depends on the
   hero video's bottom margin collapsing with the heading margin that follows
   it, and a flex container would stop that collapse and open a gap.
   The 404 is not listed: it already centres itself with its own flex:1 and
   align-items:center, and adding these would be dead weight. */
body > .project{margin-block:auto}
body > .project ~ footer{margin-top:0}

body{position:relative}
body::after{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  left:0; right:0; top:0; bottom:0;
  background-image:url("assets/wheel-background-alpha.png?v=2");
  background-repeat:no-repeat;
  background-size:992px 984px;                  /* source is 717x711, scaled 1.6x */
  background-position:right -248px bottom -246px;   /* 25% of each axis */
}
/* Below 861px the fixed 992px would crowd the column, so scale with the
   viewport: 115.2vw wide, 114.24vw tall (same ratio), bled by a quarter of each. */
@media (max-width:860px){
  body::after{
    background-size:115.2vw 114.24vw;
    background-position:right -28.8vw bottom -28.56vw;
  }
}

/* ── Buttons ─────────────────────────────────────────────── */
.btn{
  display:inline-block; padding:6px 9px; border-radius:9px; font-weight:600;
  text-decoration:none; font-size:14.5px; transition:transform .06s ease, filter .15s ease, background .15s ease, color .15s ease;
}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--accent); color:#fff; border:1px solid transparent}
.btn-primary:hover{filter:brightness(.93)}
.btn-ghost{background:transparent; color:var(--navy); border:1px solid var(--navy)}
.btn-ghost:hover{background:var(--navy); color:#fff}

/* ── Site stripe (global top nav) ─────────────────────────
   Sits above everything on every page. Links are absolute (/, /help/, /tip/)
   so the same markup works from the doc root, /help/, and /tip/ alike -- and
   from 404.html, which Apache serves under whatever URL the visitor asked for.
   Mark the current page with aria-current="page"; no JS involved. */
/* --nav-h is the rendered height of the stripe: the 45px min-height below plus
   its 1px bottom border. The help page's sticky sidebar and anchor offsets read
   it, so the three stay in step if the bar is ever resized again. */
:root{--nav-h:46px}

/* Double-height stripe. Every dimension is scaled together -- bar height,
   type, and the pill padding -- so the links keep their proportions inside
   the taller bar instead of sitting as small text in a big empty band. */
/* Pinned: the bar rides along instead of scrolling away. The background is
   fully opaque, so content passing underneath stays hidden rather than showing
   through. z-index clears the hero card, which is absolutely positioned and
   would otherwise paint over the bar as it scrolls past. */
.sitenav{
  position:sticky; top:0; z-index:50;
  background:#eceef2; border-bottom:1px solid var(--line);
  /* The bar never wraps; it shrinks its type to fit instead. 20px is the
     desktop size, holding from about 535px up, below which the middle term
     takes over and scales with the viewport. The -2.5px offset accounts for
     the parts of the row that do not scale -- the wrap's 24px side padding and
     the 8px gaps -- so the shrink tracks the space actually left for text. */
  font-size:clamp(11px, calc(4.2vw - 2.5px), 20px);
}
/* align-items:stretch, not center: it is what lets a link's background run the
   full height of the stripe. The links then centre their own text internally
   instead of relying on symmetric padding to place it. */
/* 68px, not an arbitrary trim: the links carry no vertical padding of their
   own, so the breathing room above and below the text is whatever this height
   leaves around the 44.8px line box (28px type at the 1.6 body line-height).
   That was 23.6px a side; halving it to 11.8 lands the bar on 68.4px, which is
   the 25% reduction, then 5px more off each side. That clearance is 6.6px a
   side, and 45px is what holds it now the type caps at 20px: a 32px line box
   (20px at the 1.6 body line-height) plus 6.6 above and below.
   Fixed, so the clearance is exact at the cap and opens up as the type shrinks
   on narrow screens -- which also keeps --nav-h a single honest number for the
   help page's sticky offsets. */
.sitenav .wrap{display:flex; gap:8px; align-items:stretch; min-height:45px; flex-wrap:nowrap}
.sitenav a{
  display:flex; align-items:center;
  text-decoration:none; color:var(--navy); font-weight:600;
  padding:0 .7em; white-space:nowrap;             /* em, so the pill shrinks with the type */
  border:1px solid transparent; transition:background .15s ease, color .15s ease;
}
.sitenav a:hover{background:rgba(20,22,34,.06)}   /* now a full-height wash, matching the active block */
/* The current page reads as a solid block of colour cut through the stripe,
   top edge to bottom edge. The gradient is deliberately shallow -- a couple of
   steps either side of the accent -- so it catches the light without turning
   into an obvious two-tone band. */
.sitenav a[aria-current="page"]{
  color:#fff;
  background:linear-gradient(180deg, #e78d74 0%, var(--accent) 52%, #cf6a4d 100%);
  /* Enough shadow to seat the white text on the orange without reading as an
     effect -- a 1px drop with a 3px blur. Tinted with the palette navy rather
     than black, which keeps it reading as shade on a warm surface instead of
     soot. */
  text-shadow:0 1px 3px rgba(61,64,91,.5);
}
@media (max-width:720px){
  .sitenav .wrap{gap:4px; padding:0 14px}
}

/* ── Masthead (site identity + jump nav) ─────────────────── */

/* ── Page section ────────────────────────────────────────── */
/* No top padding: the section runs flush to the top of the page so the hero
   video bleeds into the very top edge, with no band of paper above it. */
.project{padding:0 0 56px; border-bottom:1px solid var(--line)}
/* Headroom under the nav for the two pages that open on a logo and a headline.
   The home page is deliberately excluded: it opens on a full-bleed video that
   should meet the nav with no seam.
   This matters most on a short viewport, where the page scrolls and the
   vertical centring above has no free space to give them -- without it the
   flower sits flush against the bar. Written as an id selector so it also
   outranks the padding shorthand in the 720px block below, which would
   otherwise reset the top back to zero on phones. */
#download,#tip{padding-top:32px}
.project-head{display:flex; gap:20px; align-items:center; margin-bottom:22px}
.proj-logo{width:80px; height:80px; flex:0 0 auto; border-radius:16px; object-fit:contain}
.project-head .meta{flex:1 1 auto; min-width:0}
.project-head h2{margin:0; font-size:clamp(24px,3.4vw,32px); letter-spacing:-.01em; color:var(--navy)}
/* Negative on purpose. The visible gap under the h2 is not this margin -- it is
   mostly half-leading: a 32px heading on the 1.6 body line-height carries 6.6px
   of empty box below the glyphs, and the tagline another 2.4px above, so ink to
   ink measured 23px against a 4px margin. -7.5px pulls that to 11.5px, half.
   Taken here rather than off the h2's line-height, which would reflow the
   heading itself. */
.project-head .tagline{margin:-7.5px 0 0; color:var(--muted); font-size:13px}
.status{
  display:inline-block; font-size:12px; font-weight:600; color:var(--accent);
  border:1px solid var(--accent); border-radius:999px; padding:2px 10px; margin-top:10px;
}
.platform{display:inline-block; font-size:12px; font-weight:600; color:var(--muted);
  background:rgba(20,22,34,.05); border:1px solid var(--line); border-radius:999px; padding:2px 10px; margin:10px 0 0 6px}
.links{margin-top:14px; display:flex; gap:12px; flex-wrap:wrap}
.soon{display:block; margin-top:12px; color:var(--muted); font-size:14px; font-weight:500}
.about{color:var(--ink); max-width:500px; margin:0 0 6px}
.to-right{text-align:right; margin-left:auto; margin-right:0}


/* Video hero: an autoplaying, looping screen recording with the page head
   (icon + links) floating over it on a translucent frosted card. */
/* overflow is visible on purpose: the hero card is translated down past the
   bottom edge and would be sliced off by a clip. The bottom margin below is
   sized to clear the half-card that now hangs into it. */
/* 92px: 89px of card overhang plus a 3px gap. Was 123px (a 28px gap) -- the
   intro block now starts 25px higher.
   This margin, not the heading's own margin-top, is what sets that gap. The
   two are adjacent siblings, so they collapse to the larger of the pair, and
   at 30px the heading's margin has always been swallowed whole. Editing it
   would have changed nothing on screen. */
.hero-video{position:relative; width:100%; margin:0 0 92px}
.hero-video > video{width:100%; height:auto; display:block; background:#0b0d12}   /* full window width, proportional height */
/* Bottom band only — do NOT cover the whole video, or Chrome's autoplay policy
   treats it as occluded and refuses to autoplay. */
.hero-overlay{position:absolute; left:0; right:0; bottom:0; pointer-events:none}
/* translateY(50%) hangs exactly half the card below the video, and being a
   percentage it re-computes if the card's content changes height -- no fixed
   offset to keep in sync. The overlay is absolutely positioned, so nothing
   below is aware of the overhang; .hero-video's bottom margin reserves the
   room by hand. */
.hero-card{pointer-events:auto; margin:24px 0 0; transform:translateY(50%); max-width:560px;
  background:rgba(255,255,255,.80); -webkit-backdrop-filter:blur(16px) saturate(1.3);
  backdrop-filter:blur(16px) saturate(1.3); border:1px solid rgba(255,255,255,.55);
  border-radius:14px; box-shadow:0 12px 34px rgba(20,22,40,.24);
  /* Left padding is larger than the other three on purpose. The meta column is
     taller than the 120px logo, so align-items:center leaves 35px above and
     below it -- a matching 13px on the left would read as the logo being
     jammed against the edge. 34px + the 1px border gives the logo the same 35px
     of air on all three sides it touches.
     Tied to the card's current content height: if the meta block gains or loses
     a line, the vertical gap moves and this wants re-measuring. */
  padding:13px 13px 13px 28px}
.hero-card .project-head{margin:0}
/* 1.5x the standard 80px mark. Scoped to the hero card so the tip and help
   pages keep theirs at the original size. */
.hero-card .proj-logo{width:120px; height:120px}

/* uppercase block label inside a section (How it works / What it does / …) */
h3.blk{font-size:26px; text-transform:uppercase; letter-spacing:.08em; color:var(--muted);
       margin:30px 0 14px; font-weight:600}

/* ── Cards / steps / specs (shared) ──────────────────────── */
.grid{display:grid; grid-template-columns:repeat(3,1fr); gap:18px}
.card{
  border:1px solid var(--line); border-radius:14px; padding:20px; background:var(--paper);
  max-width:450px;   /* only bites in the single-column layout; desktop cards are ~296px */
  /* Two shadows rather than one: a wide soft pool for the "lifted off the
     page" feel, and a tight one right under the edge so the card still has a
     defined contact point instead of floating in fog. Tinted with the navy
     from the palette rather than black -- a neutral shadow reads grey and
     dirty against this warm paper. */
  box-shadow:0 14px 34px -10px rgba(61,64,91,.18), 0 3px 8px -3px rgba(61,64,91,.10);
}

/* Each card sits at its own slight angle, like notes pinned to a board. Every
   value is between 1 and 2.2 degrees: below 1 the tilt is too slight to read
   as deliberate, and past ~2.2 the body text starts to look like a rendering
   fault rather than a choice.
   Signs alternate down the columns as well as across the rows, so no card
   leans the same way as any neighbour it touches.
   Cycling on 9n keeps the pattern going if more cards are ever added.
   Rotation is visual only: it does not affect the grid track sizes, so the
   columns stay square and nothing reflows. */
.card{transform:rotate(-1.5deg)}
.card:nth-child(9n+1){transform:rotate(-1.9deg)}
.card:nth-child(9n+2){transform:rotate(1.2deg)}
.card:nth-child(9n+3){transform:rotate(-1.4deg)}
.card:nth-child(9n+4){transform:rotate(2.2deg)}
.card:nth-child(9n+5){transform:rotate(-1deg)}
.card:nth-child(9n+6){transform:rotate(1.7deg)}
.card:nth-child(9n+7){transform:rotate(-2.1deg)}
.card:nth-child(9n+8){transform:rotate(1.1deg)}
.card:nth-child(9n+9){transform:rotate(-1.5deg)}
.card .dot{width:11px; height:11px; border-radius:3px; display:inline-block; margin-bottom:10px; background:var(--accent)}
.card h4{margin:0 0 6px; font-size:16px}
.card p{margin:0; color:var(--muted); font-size:14px}

/* Whole-card links. The underline stays on just the phrase in the text, while
   a zero-content ::after on that same anchor is stretched across the card to
   make the whole block the hit target. Using the existing inline link rather
   than wrapping the card in a second anchor keeps exactly one link per card in
   the accessibility tree, and it is the underlined phrase -- not the card's
   whole contents -- that gets announced and shows the focus ring.
   .card is already a containing block for absolute children thanks to its
   rotate transform, but position is set explicitly rather than resting on a
   side effect of the tilt. */
.card{position:relative}
/* ── Feature card icons ──
   A 65px mark hung off the top-leading corner of each card, so it reads as
   pinned to the card rather than printed inside it. top and inset-inline-start
   are the anchor: the outer corner stays put at -18px/-18px and the box grows
   inward, toward the middle of the card. It sits
   inside .card (which is already position:relative and carries the tilt), so it
   rotates with the card and stays glued to that corner.

   inset-inline-start rather than left: on the Arabic page the whole grid
   mirrors, and the mark follows it to the top-right corner by itself. */
.card-icon{
  position:absolute; top:-18px; inset-inline-start:-18px;
  width:65px; height:65px; display:block;
}
/* The heading shares the icon's line, so it is pushed clear of it. Only the
   heading is indented -- the body copy below the icon keeps the full width.
   Every .card carries an icon, so that one is unconditional; only two of the
   .spec cards do, hence the :has() guard -- the rest keep their emoji and must
   not be indented for an icon they do not have. */
.card h4{padding-inline-start:38px}
.spec{position:relative}
.spec:has(> .card-icon) h4{padding-inline-start:38px}

/* The two platform cards are named rather than counted: unlike the nine feature
   cards, their order is not the thing choosing the colour, and the .specs grids
   on the download page hold other cards that must not be caught by an
   nth-child rule. */
.card-icon.icon-navy{fill:var(--navy)}
.card-icon.icon-accent{fill:var(--accent)}
.card-icon.icon-green{fill:var(--green)}

/* Three brand colours across nine cards, laid out as a Latin square: each
   colour appears exactly once in every row and every column of the 3-wide
   desktop grid, so no two neighbours repeat in either direction. Collapsing to
   one column on mobile leaves a clean 3-colour cycle. */
.card:nth-child(9n+1) .card-icon{fill:var(--accent)}
.card:nth-child(9n+2) .card-icon{fill:var(--navy)}
.card:nth-child(9n+3) .card-icon{fill:var(--green)}
.card:nth-child(9n+4) .card-icon{fill:var(--navy)}
.card:nth-child(9n+5) .card-icon{fill:var(--green)}
.card:nth-child(9n+6) .card-icon{fill:var(--accent)}
.card:nth-child(9n+7) .card-icon{fill:var(--green)}
.card:nth-child(9n+8) .card-icon{fill:var(--accent)}
.card:nth-child(9n+9) .card-icon{fill:var(--navy)}

.card.linked{cursor:pointer}
.card-link::after{content:""; position:absolute; inset:0}

.steps{display:grid; grid-template-columns:repeat(3,1fr); gap:18px}
.step{border:1px solid var(--line); border-radius:14px; padding:20px; background:var(--card)}
.step .n{width:32px; height:32px; border-radius:50%; background:var(--accent); color:#fff;
         display:flex; align-items:center; justify-content:center; font-weight:700; font-size:14px; margin-bottom:12px}
.step h4{margin:0 0 6px; font-size:16px}
.step p{margin:0; color:var(--muted); font-size:14px}

.specs{display:grid; grid-template-columns:1fr 1fr; gap:18px}
.spec{border:1px solid var(--line); border-radius:14px; padding:20px; background:var(--card)}
.spec h4{margin:0 0 10px; font-size:15px}
.spec ul{margin:0; padding-left:18px; color:var(--muted); font-size:14px}
.spec li{margin:4px 0}
code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:.88em;
     background:rgba(20,22,34,.06); padding:1px 5px; border-radius:5px}

.disc{margin-top:26px; font-size:12px; color:#9498a6; line-height:1.5; max-width:760px}

/* ── Footer ──────────────────────────────────────────────── */
footer{padding:40px 0 54px; color:var(--muted); font-size:13.5px}
footer .row{display:flex; justify-content:space-between; gap:20px; flex-wrap:wrap; align-items:center}
footer a{color:var(--navy); text-decoration:none; font-weight:500}
footer a:hover{text-decoration:underline}

@media (max-width:720px){
  .grid,.steps,.specs{grid-template-columns:1fr}
  /* Single column at this width, so the cards read as a vertical list --
     alternating the alignment gives that list a zigzag rhythm instead of one
     long flush-left stack. text-align on the card carries to its heading and
     body together, so each card stays internally consistent.
     Only inside this breakpoint: in the three-column desktop grid, alternating
     alignment would fight the column edges rather than create a rhythm. */
  /* With the 500px cap the card no longer fills the column, so it is free to
     sit on one side of it. Each card slides to whichever edge its text is
     ragged against -- left-aligned cards left, right-aligned cards right --
     which turns the alternating alignment into an actual zigzag rather than
     alternating text inside a static stack.
     margin-left:auto does the sliding: the grid item is narrower than its
     track, and the auto margin eats the leftover space on that side. */
  .card:nth-child(even){text-align:right; margin-left:auto}
  /* The icon follows its card's alignment: the odd cards keep it on the
     leading corner, the even (trailing-aligned) ones move it to the trailing
     corner, so the zigzag runs down the column through the marks as well as
     the text. Scoped to this breakpoint only -- in the three-column desktop
     grid every card keeps its icon on the leading corner.
     inset-inline-* rather than left/right, so the whole alternation mirrors on
     the Arabic page along with the alignment rule above. */
  .card:nth-child(even) .card-icon{inset-inline-start:auto; inset-inline-end:-18px}
  /* and the heading is pushed clear of whichever corner the icon is on */
  .card:nth-child(even) h4{padding-inline-start:0; padding-inline-end:38px}
  /* Right-ragged text needs a wide measure to read as a deliberate edge. In
     this single column it just looks like broken indentation, so the intro
     block goes back to left-aligned -- and back to its full top margin, since
     the card no longer overhangs it here. */
  .to-right{text-align:left}
  .project{padding:0 0 44px}
  .project-head{flex-direction:column; text-align:center}
  .links{justify-content:center}
  /* On narrow screens the card goes full width and roughly doubles in height
     (353px at 420px wide, against 190px on desktop), so a 50% overhang would
     dangle a huge slab past the video and swallow the text below it. A fixed
     56px drop keeps the effect readable and, being fixed, lets the reserved
     margin below be exact instead of tracking a card height that changes with
     every wrap of the tagline. */
  /* At this breakpoint .project-head stacks -- the logo jumps above the text
     and the card roughly doubles in height. Left as an overlay it covered
     nearly the whole video. So below 720px the card stops overlaying and drops
     into the flow beneath the video, pulled back up just far enough to bite
     30px into its bottom edge.
     Taking .hero-overlay out of absolute positioning is what makes this
     self-sizing: the card occupies real space again, so nothing has to reserve
     room for it by hand, and the reserved margin the overlay needed above can
     go back to the ordinary 28px. */
  .hero-overlay{position:static}
  /* Back to uniform padding here: .project-head stacks into a centred column,
     so the logo sits at the top with 13px above it and the asymmetric left
     padding would just push the whole centred stack off-centre. */
  .hero-card{max-width:none; transform:none; margin:-30px 0 0; padding:13px}
  .hero-video{margin-bottom:28px}
}

/* ── Language switcher + right-to-left ──────────────────────
   The switcher rides inside the nav bar, at the trailing end of the links
   (margin-inline-start:auto, so it swaps to the left on the RTL pages by
   itself). Below 880px the links and the five language pills no longer share a
   line, so the switcher takes a row of its own ABOVE the links: the links are
   the primary navigation and stay nearest the page content.

   Scoped to .sitenav throughout. The help pages carry their own .langs inside
   their header and style it locally, and these rules must not reach it.

   [dir=rtl] is set on the Arabic pages only; the few directional rules above
   get their mirror here rather than a second stylesheet. */
.sitenav .langs{
  display:flex; gap:6px; flex-wrap:wrap; align-items:center;
  flex:0 0 auto; margin-inline-start:auto;
}
.sitenav .langs a{
  align-self:center; white-space:nowrap;
  font-size:12px; font-weight:600; text-decoration:none; color:var(--navy);
  background:#fff; border:1px solid var(--line); padding:3px 9px; border-radius:999px;
}
/* The nav's own link rules would otherwise bleed through -- the grey hover wash,
   and the full-height orange gradient that marks the current page, which the
   current *language* also carries an aria-current for. */
.sitenav .langs a:hover{background:#fff; border-color:var(--navy)}
.sitenav .langs a[aria-current]{
  background:var(--navy); color:#fff; border-color:var(--navy); text-shadow:none;
}
@media (max-width:880px){
  /* Only the switcher is allowed to wrap; order:-1 puts it on top, and the rule
     below keeps the four links themselves on a single line as before. */
  .sitenav .wrap{flex-wrap:wrap}
  .sitenav .langs{
    order:-1; width:100%; margin-inline-start:0; justify-content:flex-end;
    padding:5px 0; border-bottom:1px solid var(--line);
  }
}
[dir=rtl] .to-right{text-align:left; margin-left:0; margin-right:auto}
[dir=rtl] .spec ul{padding-left:0; padding-right:18px}
@media (max-width:720px){
  [dir=rtl] .card:nth-child(even){text-align:left; margin-left:0; margin-right:auto}
  [dir=rtl] .to-right{text-align:right}
}
