/* Self-hosted, latin subset, variable. The brief rules out the Google Fonts
 * CDN (LG München I, 2022), and font-src 'self' in the CSP blocks it anyway:
 * v5 shipped with the CDN <link>, which would have loaded nothing. */
@font-face{font-family:"Bricolage Grotesque";font-style:normal;font-weight:500 700;
  font-display:swap;src:url("assets/fonts/bricolage-grotesque-500-700-latin.woff2") format("woff2")}
@font-face{font-family:"Public Sans";font-style:normal;font-weight:400 600;
  font-display:swap;src:url("assets/fonts/public-sans-400-600-latin.woff2") format("woff2")}

:root{
  /* One palette, both modes. light-dark() reads the used color-scheme, so the
     OS preference drives it and the toggle just overrides color-scheme below,
     no duplicated dark block to keep in sync. Light is the first value, so
     light is the default. */
  color-scheme:light dark;
  --ground:light-dark(#EFF7F2,#071A13);
  --ink:light-dark(#09241A,#E4F2EA);
  --ink-soft:light-dark(#3A5C4B,#9DBBAC);
  --accent:light-dark(#07875A,#19A86F);
  /* Hover darkens on light, brightens on dark. Darkening on a dark ground
     dropped the button label to 4.25:1. */
  --accent-deep:light-dark(#055E40,#2ECB8B);
  --accent-text:light-dark(#066B49,#5FD6A0);
  --tint:light-dark(#D2EADC,#102C21);
  --rule:light-dark(rgba(9,36,26,.16),rgba(228,242,234,.16));
  --panel:light-dark(rgba(255,255,255,.62),rgba(255,255,255,.045));
  --field:light-dark(#fff,#0C2419);
  --on-accent:light-dark(#fff,#04150E);
  /* Non-colour values cannot ride light-dark(): it resolves to a <color> and
     nothing else, so `opacity:light-dark(.62,.3)` is invalid at computed-value
     time and silently falls back to 1. These three switch by selector below.
     Light values here, dark values in the one block that follows. */
  --aurora-op:.62;   /* full strength on a dark ground reads as a bruise */
  --knob-x:0;        /* toggle knob travel */
  --sun-o:1;         /* the icon showing is the mode you are IN */
  --moon-o:0;
  --font-display:"Bricolage Grotesque",Georgia,serif;
  --font-body:"Public Sans",system-ui,sans-serif;
  --measure:64ch;
}
[data-theme="roblox"]{
  --ground:light-dark(#FDF2E6,#1A0C04);
  --ink:light-dark(#2A1207,#F6E7DA);
  --ink-soft:light-dark(#6B4830,#C7A488);
  --accent:light-dark(#E2670C,#F5821F);
  --accent-deep:light-dark(#B14A06,#FF9C42);
  --accent-text:light-dark(#A64408,#F5A14E);
  --tint:light-dark(#F9DEC0,#33190A);
  --rule:light-dark(rgba(42,18,7,.18),rgba(246,231,218,.16));
  --panel:light-dark(rgba(255,255,255,.6),rgba(255,255,255,.05));
  --field:light-dark(#fff,#241206);
  --on-accent:light-dark(#fff,#1A0C04);
}
/* The manual override. theme.js writes data-mode on <html> from localStorage
   before first paint; with no override the media preference above stands. */
:root[data-mode="light"]{color-scheme:light}
:root[data-mode="dark"]{color-scheme:dark}
/* The colour half of dark mode is handled by light-dark() above; only these
   non-colour values have to be restated, once per route into dark. */
@media (prefers-color-scheme:dark){
  :root:not([data-mode="light"]){--aurora-op:.3; --knob-x:18px; --sun-o:0; --moon-o:1}
}
:root[data-mode="dark"]{--aurora-op:.3; --knob-x:18px; --sun-o:0; --moon-o:1}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; overflow-x:clip}
body{
  margin:0; background:var(--ground); color:var(--ink); position:relative; overflow-x:clip;
  font-family:var(--font-body); font-size:17px; line-height:1.6;
  transition:background-color .6s ease, color .6s ease;
}
.aurora{
  position:absolute; top:-160px; left:-8%; right:-8%; height:900px;
  z-index:0; pointer-events:none; filter:blur(80px); transition:opacity .7s ease;
  -webkit-mask-image:linear-gradient(to bottom,#000 0%,#000 42%,transparent 94%);
  mask-image:linear-gradient(to bottom,#000 0%,#000 42%,transparent 94%);
  animation:drift 38s ease-in-out infinite alternate;
}
.aurora-green{opacity:var(--aurora-op); background:
  radial-gradient(48% 54% at 16% 26%,#16B37A 0%,transparent 64%),
  radial-gradient(44% 50% at 70% 14%,#0E9BA8 0%,transparent 62%),
  radial-gradient(40% 46% at 46% 50%,#8AD96B 0%,transparent 64%);}
.aurora-orange{opacity:0; background:
  radial-gradient(48% 54% at 18% 24%,#F59120 0%,transparent 64%),
  radial-gradient(44% 50% at 72% 15%,#DE3F52 0%,transparent 62%),
  radial-gradient(40% 46% at 46% 52%,#FFC23D 0%,transparent 64%);}
[data-theme="roblox"] .aurora-green{opacity:0}
[data-theme="roblox"] .aurora-orange{opacity:var(--aurora-op)}
@keyframes drift{from{transform:translate3d(-2%,0,0) scale(1)} to{transform:translate3d(3%,2%,0) scale(1.12)}}
@media (prefers-reduced-motion:reduce){
  .aurora{animation:none}
  body,*{transition-duration:.01ms !important}
}

.wrap{max-width:1060px; margin:0 auto; padding:0 28px; position:relative; z-index:1}
a{color:var(--accent-text)}
:focus-visible{outline:3px solid var(--accent); outline-offset:3px; border-radius:2px}
.skip{position:absolute; left:-9999px}
.skip:focus{position:static; display:inline-block; padding:8px 14px}

.masthead{position:sticky; top:0; z-index:20; padding:24px 0 0;
  background:var(--ground); transform:translateY(0); transition:transform .25s ease}
.masthead.nav-hidden{transform:translateY(-100%)}
.mast-row{display:flex; align-items:center; justify-content:space-between; gap:28px; border-bottom:1px solid var(--rule)}
.wordmark{font-family:var(--font-display); font-weight:700; font-size:1.2rem; letter-spacing:-.01em; padding-bottom:16px; flex:0 0 auto}
/* .avail is no longer in the masthead: only the doc pages' plain
   "Back to the main page" link uses it now. */
.avail{font-size:.9rem; color:var(--ink-soft); padding-bottom:16px; flex:0 0 auto; text-align:right}
/* Sliding switch: 40x22 track, 18px knob, both icons stacked inside the knob
   and cross-faded so the face travels with it. Geometry follows VitePress's
   VPSwitchAppearance, which is the control this was asked to match. */
.mode{position:relative; appearance:none; cursor:pointer; padding:0; flex:0 0 auto;
  width:40px; height:22px; border-radius:11px; margin-bottom:16px;
  border:1px solid var(--rule); background:var(--tint); color:var(--ink-soft);
  transition:border-color .2s ease}
.mode[hidden]{display:none}
.mode:hover{border-color:var(--accent)}
/* The global :focus-visible sets border-radius:2px, which squares off the pill. */
.mode:focus-visible{border-radius:11px}
.knob{position:absolute; top:1px; left:1px; width:18px; height:18px; border-radius:50%;
  background:var(--field); box-shadow:0 1px 2px rgba(0,0,0,.2);
  display:grid; place-items:center;
  transform:translateX(var(--knob-x)); transition:transform .25s ease}
.knob svg{grid-area:1/1; width:12px; height:12px; transition:opacity .25s ease}
.knob .i-sun{opacity:var(--sun-o)}
.knob .i-moon{opacity:var(--moon-o)}
.doc-mast .mode{margin-bottom:0; align-self:center}


.hero{padding:76px 0 58px}
h1{font-family:var(--font-display); font-weight:700; font-size:clamp(2.6rem,6.4vw,4.3rem); line-height:1.02; letter-spacing:-.028em; margin:0 0 22px; max-width:16ch}
.lede{font-size:1.26rem; line-height:1.5; max-width:var(--measure); margin:0; color:var(--ink-soft)}

.proof{padding:0 0 60px}
.proof h2{font-family:var(--font-display); font-size:1.15rem; font-weight:600; margin:0 0 4px}
.proof .note{font-size:.94rem; color:var(--ink-soft); margin:0 0 20px; max-width:58ch}
.matrix{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:0;
  background:transparent; border:1px solid var(--rule); overflow:hidden}
/* Rules are drawn by the cells, not by gaps over a coloured container: with
   8 platforms in a 3-column grid the container approach painted the empty
   9th slot as a grey box. overflow:hidden clips the trailing edges. */
.cell{background:var(--panel); backdrop-filter:blur(6px); padding:16px;
  border-right:1px solid var(--rule); border-bottom:1px solid var(--rule); margin:0 -1px -1px 0; display:flex; align-items:center; gap:11px; font-size:.97rem; color:var(--ink); text-decoration:none}
.cell .dot{width:9px; height:9px; border-radius:50%; flex:0 0 auto; background:var(--accent)}
.cell .name{flex:1 1 auto}
a.cell .study{font-size:.78rem; color:var(--accent-text); font-weight:600; border-bottom:1px solid currentColor; padding-bottom:1px}
a.cell:hover{background:var(--tint)}
.cell.custom{background:transparent; color:var(--ink-soft)}
.cell.custom .dot{background:transparent; border:1.5px dashed var(--accent); width:10px; height:10px}
.cell.custom:hover{background:var(--tint); color:var(--ink)}

.engines{margin-top:20px; font-size:.94rem; color:var(--ink-soft); max-width:72ch}
.engines b{color:var(--ink); font-weight:600}

.block{padding:0 0 58px}
.block h2{font-family:var(--font-display); font-size:1.65rem; font-weight:600; letter-spacing:-.018em; margin:0 0 18px}
.cols{display:grid; grid-template-columns:1fr 1fr; gap:56px}
ul.doing{list-style:none; margin:0; padding:0; max-width:var(--measure)}
ul.doing li{padding:13px 0; border-bottom:1px solid var(--rule)}
ul.doing li:first-child{border-top:1px solid var(--rule)}
ul.doing .svc{display:block; font-weight:600}
ul.doing .svc-sub{display:block; font-size:.9rem; color:var(--ink-soft); line-height:1.45; margin-top:3px}
.para{margin:0 0 14px; max-width:var(--measure); color:var(--ink-soft)}

.price{border-top:1px solid var(--rule)}
.price-row{display:grid; grid-template-columns:1fr auto; gap:18px 32px; padding:22px 0; border-bottom:1px solid var(--rule); align-items:start}
.price-name{font-family:var(--font-display); font-weight:600; font-size:1.06rem; margin:0 0 5px}
.price-desc{margin:0; color:var(--ink-soft); font-size:.96rem; max-width:52ch}
.price-fig{font-family:var(--font-display); font-weight:600; font-size:1.06rem; color:var(--accent-text); text-align:right; line-height:1.4}
.price-fig small{display:block; font-family:var(--font-body); font-weight:400; font-size:.82rem; color:var(--ink-soft)}
.price-intro{color:var(--ink-soft); margin:0 0 4px; font-size:.96rem}
.block-head{margin:0 0 18px; display:flex; flex-wrap:wrap; gap:12px 24px; align-items:baseline; justify-content:space-between}
.block-head h2{margin-bottom:0}
.currency-pick[hidden]{display:none}
.currency-pick{display:flex; align-items:center; gap:8px; margin:0; font-size:.88rem; color:var(--ink-soft)}
.currency-pick select{width:auto; font-size:.88rem; padding:6px 9px}
.includes-drop{margin:10px 0 0}
.includes-drop summary{display:inline-flex; align-items:center; gap:7px; cursor:pointer; list-style:none;
  font-size:.88rem; color:var(--accent-text); padding:2px 0}
.includes-drop summary::-webkit-details-marker{display:none}
.includes-drop summary::before{content:""; width:6px; height:6px; border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor; transform:rotate(-45deg); transition:transform .15s ease}
.includes-drop[open] summary::before{transform:rotate(45deg)}
.includes-drop summary:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
ul.includes{list-style:none; margin:10px 0 0; padding:0; max-width:52ch}
ul.includes li{font-size:.9rem; color:var(--ink-soft); padding:3px 0 3px 16px; position:relative; line-height:1.45}
ul.includes li::before{content:""; position:absolute; left:0; top:.72em; width:6px; height:1px; background:var(--accent); opacity:.7}

/* Selected work sits on its own soft colour field, the same blurred-blob
   language as the page-top aurora, scaled down and contained to this block
   rather than pinned to the page. Cards float over it as tinted glass
   (background + backdrop-filter, no hard border) instead of opaque white
   boxes, so the section reads as one continuous surface rather than a grid
   of static cards dropped on top of the page. */
.work-block{position:relative; overflow:hidden; border-radius:22px; padding:28px 24px; margin-bottom:34px}
.work-block::before{
  content:""; position:absolute; inset:-70px -50px; z-index:0; pointer-events:none;
  filter:blur(60px); opacity:var(--aurora-op);
  -webkit-mask-image:linear-gradient(to bottom,transparent 0%,#000 20%,#000 82%,transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0%,#000 20%,#000 82%,transparent 100%);
  background:
    radial-gradient(46% 60% at 20% 22%,#16B37A 0%,transparent 64%),
    radial-gradient(40% 54% at 78% 18%,#0E9BA8 0%,transparent 62%),
    radial-gradient(42% 50% at 46% 86%,#8AD96B 0%,transparent 64%);
  animation:drift 38s ease-in-out infinite alternate;
}
[data-theme="roblox"] .work-block::before{
  background:
    radial-gradient(46% 60% at 20% 22%,#F59120 0%,transparent 64%),
    radial-gradient(40% 54% at 78% 18%,#DE3F52 0%,transparent 62%),
    radial-gradient(42% 50% at 46% 86%,#FFC23D 0%,transparent 64%);
}
.work-block > *{position:relative; z-index:1}
@media (prefers-reduced-motion:reduce){.work-block::before{animation:none}}

/* Titles with art go in a 3-column grid, collapsing to 2 then 1. Titles with
   no art yet go in a 2-up featured row above it instead, wider than the grid
   cards below since there are only two of them; renderWork() in app.js
   routes by whether the entry has an image, so a title moves itself into
   the grid the moment real art is added. */
.work-featured{display:flex; flex-wrap:wrap; justify-content:center; gap:20px; margin-bottom:20px}
.work-featured .work-item{flex:1 1 320px; max-width:calc(50% - 10px)}
@media (max-width:640px){.work-featured .work-item{max-width:100%}}

.work-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:20px}
@media (max-width:900px){.work-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:640px){.work-grid{grid-template-columns:1fr}}

.work-item{display:flex; flex-direction:column; border-radius:16px;
  background:var(--panel); backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
  box-shadow:0 1px 1px rgba(0,0,0,.03), 0 14px 30px -18px rgba(0,0,0,.28); overflow:hidden}
.work-body{display:flex; flex-direction:column; flex:1; padding:18px 20px 20px}
/* Cover art crops to a fixed short banner shape rather than each image's own
   ratio, so every card reads as the same shape regardless of source size. */
.work-img{width:100%; aspect-ratio:960/150; object-fit:cover; display:block}
.work-title{font-family:var(--font-display); font-weight:600; font-size:1.1rem; margin:0 0 6px}
.work-meta{font-size:.88rem; color:var(--ink-soft); margin:0 0 10px}
.work-metric{font-size:.92rem; font-weight:600; color:var(--accent-text); margin:0 0 4px}
.tags{display:flex; gap:8px; flex-wrap:wrap; margin:0 0 10px; padding:0; list-style:none}
.tags li{background:var(--tint); padding:3px 10px; font-size:.8rem; border-radius:3px}
.metric-source{font-weight:400; font-size:.83rem; color:var(--ink-soft)}
.scope-head{font-size:.82rem; font-weight:600; color:var(--ink); margin:10px 0 2px}
.scope-head:first-of-type{margin-top:12px}

/* Play button: a pill in the accent colour with a small circular play glyph,
   rather than a plain text link, so the one action visitors take from a
   case card (go play it) is the most visible thing on it. */
.play-btn{display:inline-flex; align-items:center; gap:9px; align-self:flex-start;
  margin-top:auto; padding:10px 20px 10px 12px; border-radius:999px;
  background:var(--accent); color:var(--on-accent); text-decoration:none;
  font-weight:600; font-size:.92rem; transition:background-color .2s ease}
.play-btn:hover{background:var(--accent-deep)}
.play-icon{display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:50%; background:var(--on-accent); flex:0 0 auto}
.play-icon svg{width:10px; height:10px; margin-left:1px; fill:var(--accent)}

.cta{background:var(--tint); padding:40px 34px; margin-bottom:58px; border-radius:4px}
.cta p{margin:0 0 18px; font-family:var(--font-display); font-size:1.32rem; font-weight:600; letter-spacing:-.018em}
.cta p.sub{font-family:var(--font-body); font-size:1rem; font-weight:400; color:var(--ink-soft); margin:14px 0 0; letter-spacing:0}
.btn{display:inline-block; background:var(--accent); color:var(--on-accent); text-decoration:none; font-weight:600; padding:14px 28px; border-radius:4px; font-size:1rem; transition:background-color .2s ease}
.btn:hover{background:var(--accent-deep)}

.contact{border-top:1px solid var(--rule); padding:54px 0}
.contact h2{font-family:var(--font-display); font-size:1.65rem; font-weight:600; margin:0 0 6px; letter-spacing:-.018em}
.contact .note{color:var(--ink-soft); margin:0 0 28px}
.field{margin-bottom:20px}
.field label{display:block; font-weight:600; font-size:.94rem; margin-bottom:6px}
.field .hint{font-weight:400; color:var(--ink-soft); font-size:.88rem}
input,textarea,select{width:100%; font-family:inherit; font-size:1rem; padding:11px 13px; border:1px solid var(--rule); border-radius:4px; background:var(--field); color:var(--ink)}
textarea{min-height:120px; resize:vertical}
.pair{display:grid; grid-template-columns:1fr 1fr; gap:20px}
.hp{position:absolute; left:-9999px}
button.send{appearance:none; border:0; cursor:pointer; background:var(--accent); color:var(--on-accent); font-family:inherit; font-weight:600; font-size:1rem; padding:14px 28px; border-radius:4px; transition:background-color .2s ease}
button.send:hover{background:var(--accent-deep)}

footer{border-top:1px solid var(--rule); padding:32px 0 64px; font-size:.92rem; color:var(--ink-soft)}
footer p{margin:0 0 10px; max-width:var(--measure)}
footer nav a{margin-right:18px; display:inline-block; padding:6px 0}
/* 24px minimum touch target (WCAG 2.2 AA). Line-height alone gave ~20px. */

@media (max-width:900px){
  .mast-row{flex-wrap:wrap; justify-content:space-between}
  .wordmark,.avail{padding-bottom:0}
  .mode{margin-bottom:0}
}
@media (max-width:760px){
  .cols{grid-template-columns:1fr; gap:44px}
  .matrix{grid-template-columns:repeat(2,1fr)}
  .pair{grid-template-columns:1fr}
  .hero{padding:52px 0 40px}
  .price-row{grid-template-columns:1fr}
  .price-fig{text-align:left}
  /* .wrap already carries a 28px side gutter; a full 24px more here would
     double up on a phone, so this block gets a lighter one. */
  .work-block{padding:22px 16px}
}
@media (max-width:460px){
  .matrix{grid-template-columns:1fr}
  .avail{font-size:.84rem}
}

/* Replaces the inline style attributes app.js used to write, since style-src 'self'
   blocks inline style attributes exactly as it blocks an inline <style>. */
.dayrate-label{margin-top:20px}
.dayrate-fig{font-size:1.55rem; text-align:left}

/* Form states. v5's send button was type="button" with no handler and its
   inputs had no name attributes, so the form could not submit at all. */
.field .help{display:block; min-height:1lh; font-size:.88rem; color:var(--ink-soft); margin-top:5px}
.field .help.is-error{color:var(--ink); font-weight:600}
input[aria-invalid="true"],textarea[aria-invalid="true"],select[aria-invalid="true"]{border-color:var(--ink)}
.form-status{min-height:1lh; margin:14px 0 0; font-size:.94rem; color:var(--ink-soft)}
.form-status.is-error,.form-status.is-ok{color:var(--ink); font-weight:600; max-width:var(--measure)}
form[hidden]{display:none}
.no-js-note{margin:0 0 24px; padding:12px 14px; border:1px solid var(--rule); font-size:.94rem}

@media print{
  @page{margin:16mm}
  :root,[data-theme="roblox"]{color-scheme:light; --ink:#000; --ink-soft:#333; --accent:#000;
    --accent-text:#000; --tint:transparent; --rule:#000; --panel:transparent;
    --field:#fff; --on-accent:#fff}
  html,body{background:#fff; color:#000; font-size:10.5pt}
  .aurora,.skip,.contact,.btn,.mode{display:none !important}
  .wrap{max-width:none; padding:0}
  h1{font-size:22pt}
  .matrix{background:none; border:none; gap:0}
  .cell{background:none; backdrop-filter:none; border:.5pt solid #000}
  .cta{background:none; border:1pt solid #000}
  a{color:#000; text-decoration:underline}
  footer nav a::after{content:" (" attr(href) ")"; font-size:8pt}
  .work-block{padding:0}
  .work-block::before{display:none}
  .work-item{background:none; backdrop-filter:none; box-shadow:none; border:.5pt solid #000; break-inside:avoid}
  .price-row{break-inside:avoid}
  /* Print the contents of every package, open or not. */
  .includes-drop>ul.includes{display:block}
  .includes-drop summary{display:none}
}

/* ---------- Document pages: legal, privacy, 404 ----------
   Same shell as the main page so the secondary pages don't read as a
   different brand. They carry no tabs, so the masthead collapses. */
.doc{padding:44px 0 80px}
.doc h1{font-size:clamp(2rem,4.4vw,2.9rem); max-width:22ch; margin-bottom:14px}
.doc h2{font-family:var(--font-display); font-size:1.2rem; font-weight:600; letter-spacing:-.015em;
  margin:40px 0 10px; padding-top:18px; border-top:1px solid var(--rule)}
.doc .lede{font-size:1.1rem; color:var(--ink-soft); max-width:var(--measure); margin:0 0 8px}
.doc p{max-width:var(--measure); margin:0 0 12px}
.doc dl{display:grid; grid-template-columns:minmax(0,14rem) minmax(0,1fr);
  column-gap:36px; row-gap:12px; margin:14px 0 0; max-width:62rem}
.doc dt{font-weight:600}
.doc dd{margin:0; color:var(--ink-soft); max-width:var(--measure)}
.doc .ph{color:var(--ink-soft); background:var(--tint); padding:0 4px; border-bottom:1px dashed var(--rule)}
.doc-mast{display:flex; align-items:baseline; justify-content:space-between; gap:28px;
  padding-bottom:16px; border-bottom:1px solid var(--rule)}
@media (max-width:640px){
  .doc dl{grid-template-columns:minmax(0,1fr); row-gap:4px}
  .doc dd{margin-bottom:12px}
  .doc-mast{flex-direction:column; gap:6px}
}
