/* ═══════════════════════════════════════════════════════════════
   LOCALWORKS V2 - design tokens

   Distilled from the four reference systems (Huly, Dala, Auros,
   Dope Security). All four independently agree on the rules this
   file enforces:

     · near-black neutral canvas, never navy
     · exactly ONE accent colour                (gold #F2A950)
     · ZERO shadows - elevation is hairline borders + surface steps
     · display type 80-113px, weights 200-500, tracking -0.04em
     · hierarchy from SCALE, not weight
     · 4px spacing scale, 96-120px between sections
     · exactly three radii: card / input / pill
     · uppercase mono labels, tracking 0.14-0.2em

   If a style needs something this file doesn't provide, the answer
   is almost always "no it doesn't".
   ═══════════════════════════════════════════════════════════════ */

:root{
  /* ── canvas & ink ── */
  --bg:        #08090B;   /* the page. Near-black, faintly warm, never navy */
  --bg-2:      #0D0F12;   /* raised surface (cards) - a step, not a shadow */
  --bg-3:      #14161A;   /* highest surface (inputs, hover) */
  --ink:       #F2F3F1;   /* primary text */
  --dim:       #A2A7AC;   /* supporting text */
  --faint:     #5C6167;   /* metadata, disabled */

  /* ── THE accent. There is no second one. ──
     AZURE, and it is the :root default on purpose.

     It used to be iris violet here, with azure applied only by
     [data-theme="azure"] on the html tag. That meant losing the
     attribute, on a new page, in an edit, or in a client site built
     from these tokens, silently fell back to PURPLE. Purple is the
     colour AI models reach for by default, which is the one thing this
     whole design exists to avoid. The safe colour must not be the one
     that depends on an attribute being present.

     Iris, abyss and platinum still exist as themes further down, so
     nothing is lost. They now have to be asked for. */
  --accent:    #5683DA;
  --on-accent: #FFFFFF;
  --accent-dim:rgba(86,131,218,.14);   /* tint wash for marks, never text */
  --orb-glow:  86,131,218;
  --orb-stops: #0D1B33,#3E6ED2,#5683DA,#A8C8F5,#FFFFFF;

  /* ── the one exception to "there is no second colour" ──
     A form has to be able to say "this field is wrong" in a way that
     cannot be confused with "this field is focused", and focus is the
     accent. Using one colour for both leaves a person staring at a
     form that will not send, unable to see why. So this is FUNCTIONAL,
     not decorative, and it is allowed in exactly two places: error text
     and the underline of the field that error belongs to. It never
     appears in a heading, a button, a border, an icon or a background.
     Muted on purpose so it corrects rather than alarms. */
  --warn:      #D4756A;

  /* ── hairlines do the work shadows used to ── */
  --line:        rgba(242,243,241,.09);
  --line-strong: rgba(242,243,241,.17);

  /* ── spacing - 4px scale ── */
  --s1:4px;  --s2:8px;  --s3:12px; --s4:16px; --s5:24px;
  --s6:32px; --s7:48px; --s8:64px; --s9:96px; --s10:120px;

  /* ── type ── */
  --font-ui:  'Inter','Segoe UI Variable Display','Segoe UI',-apple-system,system-ui,sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,'Cascadia Mono',Consolas,monospace;

  --fs-display: clamp(58px, 8.6vw, 112px);   /* the hero voice */
  --fs-h2:      clamp(34px, 4.6vw, 60px);    /* section openers */
  --fs-h3:      clamp(22px, 2.2vw, 28px);
  --fs-lede:    clamp(16px, 1.4vw, 19px);
  --fs-body:    16px;
  --fs-small:   14px;
  --fs-label:   12px;

  --lh-display: 0.98;
  --lh-tight:   1.08;
  --lh-body:    1.55;

  --track-display: -0.04em;
  --track-tight:   -0.02em;
  --track-label:    0.18em;

  /* ── exactly three radii ── */
  --r-card:  14px;
  --r-input: 6px;
  --r-pill:  999px;

  /* ── motion ── */
  --ease-out: cubic-bezier(.16,1,.3,1);
  --t-fast: 160ms;
  --t-slow: 480ms;

  /* ── glass: a CLEAR plate, not frosted ──
     The references use glass as a pane of window, not a fogged
     panel: you read straight through it. So: tiny blur (2px, just
     enough to settle noisy backdrops), a barely-there white film,
     a hairline edge, and a 1px top highlight where light catches
     the bevel. No shadow, no 26px frost, no inner glow. */
  --glass-film:   rgba(255,255,255,.035);
  --glass-edge:   rgba(255,255,255,.11);
  --glass-bevel:  rgba(255,255,255,.20);
  --glass-blur:   2px;
}

/* ═══ palettes - cool/neutral only.
   Switch with <html data-theme="…">. Every one is drawn from the
   reference systems' own tokens; warm gold is deliberately absent.

   --orb-stops is the swarm's depth ramp, back of the sphere to front.
   Each one now runs FIVE stops instead of four: a deeper shadow end, a
   fully saturated accent through the body, and a true white at the
   front. This is still one hue - it is a wider luminance range of the
   same accent, not a second colour, so the one-accent law holds. The
   old four-stop ramps topped out at a pale tint, which is why the
   marks read washed out no matter how the alpha was tuned. ═══ */

/* Iris - Dala #8052ff / Dope #af50ff family. The default. */
:root[data-theme="iris"]{
  --bg:#08090B; --bg-2:#0D0F12; --bg-3:#14161A;
  --accent:#8052FF; --on-accent:#FFFFFF; --accent-dim:rgba(128,82,255,.14);
  --orb-glow:128,82,255;
  --orb-stops:#200F45,#6B3AF0,#8052FF,#CDB4FF,#FFFFFF;
}
/* Azure - Huly's electric iris on obsidian. Calm, corporate-clean. */
:root[data-theme="azure"]{
  --bg:#090A0C; --bg-2:#111214; --bg-3:#191B1F;
  --accent:#5683DA; --on-accent:#FFFFFF; --accent-dim:rgba(86,131,218,.14);
  --orb-glow:86,131,218;
  --orb-stops:#0D1B33,#3E6ED2,#5683DA,#A8C8F5,#FFFFFF;
}
/* Abyss - Auros: deep teal-green canvas, bioluminescent water. */
:root[data-theme="abyss"]{
  --bg:#011D1C; --bg-2:#012624; --bg-3:#003734;
  --ink:#EDFFFE; --dim:#BBC7C6; --faint:#707777;
  --accent:#00A79E; --on-accent:#00201F; --accent-dim:rgba(0,167,158,.16);
  --line:rgba(237,255,254,.10); --line-strong:rgba(237,255,254,.18);
  --orb-glow:0,130,124;
  --orb-stops:#002E2C,#00857D,#00A79E,#7FE9E0,#FFFFFF;
}
/* Platinum - no colour at all: cool white on near-black, the accent
   is light itself. The most restrained and arguably most expensive. */
:root[data-theme="platinum"]{
  --bg:#090909; --bg-2:#0F0F10; --bg-3:#161617;
  --ink:#F7F9FA; --dim:#A9A9AA; --faint:#6B6B6B;
  --accent:#D6DEE8; --on-accent:#0A0A0B; --accent-dim:rgba(214,222,232,.12);
  --orb-glow:190,205,222;
  --orb-stops:#20262E,#6C7A8B,#A8B4C2,#D6DEE8,#FFFFFF;
}

/* ═══ base ═══ */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  background:var(--bg); color:var(--ink);
  font-family:var(--font-ui); font-size:var(--fs-body); line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
::selection{background:var(--accent); color:var(--on-accent)}
img,canvas,svg,video{display:block; max-width:100%}
a{color:inherit; text-decoration:none}

/* Shadows are banned. This is load-bearing: every reference system
   states it outright ("shadowless by design"). If something needs
   lift, give it --bg-2 and a --line border. */
*{box-shadow:none !important}

/* ═══ type voices ═══ */
.display{
  font-size:var(--fs-display); line-height:var(--lh-display);
  letter-spacing:var(--track-display); font-weight:300; color:var(--ink);
}
.display b, .display strong{font-weight:500}   /* emphasis via a HALF step, not bold */
h2.h2, .h2{
  font-size:var(--fs-h2); line-height:var(--lh-tight);
  letter-spacing:var(--track-tight); font-weight:350;
}
.lede{
  font-size:var(--fs-lede); line-height:var(--lh-body);
  color:var(--dim); max-width:52ch; font-weight:400;
}
/* Labels are SANS, not monospace. A small tracked monospace caption is
   the single most over-used tell in machine-made layouts, and wrapping
   it in brackets doubles it. Founder call: the numbered index in front
   of each label ("01 How it works") was a second tell of the same kind,
   so labels are now plain words. Nothing on this site is numbered. */
.label{
  font-family:var(--font-ui); font-size:11px;
  letter-spacing:.2em; text-transform:uppercase;
  color:var(--faint); font-weight:500;
  display:flex; align-items:baseline; gap:10px;
}
.label .tick{color:var(--accent)}

/* ═══ controls ═══
   Founder rule: a button's label is FULL INK at all times. Text that
   starts grey and brightens on hover reads as disabled until you touch
   it, and a call to action that looks disabled is a bug. So the hover
   signal is a RING drawn around the whole button - `outline` with an
   offset, which sits outside the box and therefore cannot shift layout
   the way a border that appears would. Colour never carries the state. */
.btn{
  /* These five lines exist because .btn is used on BOTH <a> and <button>,
     and a real <button> arrives carrying user-agent styling that an <a>
     never had. Without them the submit button on the enquiry form was
     drawn with the browser's default 2px OUTSET border, which by
     definition paints light on the top and left and dark on the bottom
     and right: a grey hairline round two sides of the button and nothing
     round the other two. It was also rendering in Arial while every
     other button on the site was Inter, because a button does not
     inherit the page font unless it is told to.
     Both were invisible in review because every other .btn is an <a>. */
  appearance:none; -webkit-appearance:none;
  border:0;
  font-family:inherit;
  background:none;
  cursor:pointer;

  display:inline-flex; align-items:center; gap:var(--s2);
  font-size:var(--fs-small); font-weight:500; letter-spacing:.01em;
  padding:14px 26px; border-radius:var(--r-pill);
  outline:1px solid transparent; outline-offset:3px;
  transition:background var(--t-fast) var(--ease-out),
             border-color var(--t-fast) var(--ease-out),
             outline-color var(--t-fast) var(--ease-out),
             outline-offset var(--t-fast) var(--ease-out),
             transform var(--t-fast) var(--ease-out);
}
/* The button lifts toward the cursor as the ring forms, then presses
   back down on the click. Physical, and it makes the ring read as a
   consequence of reaching the button rather than a colour change.

   The lift is MOVEMENT ONLY. No drop shadow under it, ever: every
   reference system we studied bans shadows outright, and a lifted
   button with a soft shadow under it is one of the loudest tells that
   a page came out of a builder. 2px is deliberately small. Enough to
   feel, not enough to notice as an animation. */
.btn:hover, .btn:focus-visible{outline-color:var(--line-strong); outline-offset:5px;
  transform:translateY(-2px)}
/* pressed: it goes back down and the ring tightens onto it. Faster than
   the lift, because a click should feel immediate while a hover can
   afford to glide. */
.btn:active{transform:translateY(0); outline-offset:3px;
  transition-duration:70ms}

.btn-solid{background:var(--accent); color:var(--on-accent)}
/* The ring around a SOLID button must not be the same colour as the
   button. It was set to the accent, on a button already filled with the
   accent, so the ring had almost no contrast against the thing it was
   drawn around. Floating 5px off on a dark background it read as a
   smudge or a half-formed halo rather than a boundary, which is exactly
   how the founder described it.

   Ink instead. A pale ring around a filled accent button is unmistakably
   a ring. The ghost buttons keep the subtle --line-strong, because they
   are transparent and a bright ring there would shout. */
.btn-solid:hover, .btn-solid:focus-visible{outline-color:var(--ink)}

.btn-ghost{border:1px solid var(--line-strong); color:var(--ink)}
.btn-ghost:hover, .btn-ghost:focus-visible{border-color:var(--ink); background:var(--bg-2)}

/* Somebody who has asked their machine to stop animating things still
   gets the ring, which is the part that carries the meaning. They just
   do not get the movement. */
@media (prefers-reduced-motion: reduce){
  .btn, .btn:hover, .btn:focus-visible, .btn:active{transform:none}
}

/* ═══════════════════════════════════════════════════════════════
   THE TRAVELLING EDGE  (founder chose this 2026-08-01)

   On hover, one narrow highlight runs round the rim of the button,
   the way light catches the edge of something machined. It REPLACES
   the ring above rather than joining it.

   How it works, because it is not obvious. A gradient cannot be
   animated directly: to the browser `--a:0deg` is just a string, so
   an animation on it snaps from one value to the next with nothing
   in between. Registering the property with syntax:'<angle>' tells
   the browser what the value MEANS, and it can then interpolate it.
   Feed that angle to conic-gradient(from var(--a)) and the gradient
   turns. Everything else here is delivery.

   The ring and the light must never both be drawn. A button wearing
   a light on its edge AND an outline floating 5px off it has two
   boundaries, and the eye takes the outer one as the edge, which
   demotes the light to decoration sitting inside a border. So the
   whole thing lives in a feature query: browsers that can animate
   the angle get the light and lose the ring, and anything that
   cannot keeps exactly the design that shipped before, ring and all.
   Unknown falls back to known-good, deliberately.

   The 2px LIFT survives in both. That is movement, not a second
   line, and it is what makes the button feel like it comes up to
   meet the cursor.
   ═══════════════════════════════════════════════════════════════ */
@property --btn-angle{ syntax:'<angle>'; initial-value:0deg; inherits:false }

@keyframes btn-edge-turn{ to{ --btn-angle:360deg } }

/* The hover gate is not decoration. On a touch screen :hover latches
   ON after a tap and stays there until you tap something else, so a
   phone visitor who tapped "Get a free sample" would be left watching
   a call to action spin at them for as long as the page was open.
   A pointing device that can genuinely hover is the only place this
   effect means anything. Touch keeps the ring, which is what it has
   always had. */
@supports at-rule(@property){
 @media (hover: hover) and (pointer: fine){
  .btn{position:relative}

  /* The rim is a pseudo-element two pixels larger than the button
     with its own middle punched out, so only the frame survives:
     the first mask covers the whole box, the second covers only the
     content box, and excluding one from the other leaves the edge.

     2px, not 1px. At a hairline this was technically correct and
     practically invisible, which is a pointless place to end up. */
  .btn::after{
    content:''; position:absolute; inset:-2px;
    border-radius:inherit; padding:2px;
    -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
    -webkit-mask-composite:xor;
            mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
            mask-composite:exclude;
    opacity:0; transition:opacity 180ms var(--ease-out);
    pointer-events:none;
  }
  /* Nothing animates at rest. The rim sits at opacity 0 with no
     running animation until a cursor actually arrives. */
  .btn:hover::after, .btn:focus-visible::after{
    opacity:1; animation:btn-edge-turn 2800ms linear infinite;
  }
  /* 2.8s a lap on purpose. Around a second it reads as a loading
     spinner, which is the last thing a call to action should say. */

  .btn:hover, .btn:focus-visible{outline-color:transparent}
  .btn-solid:hover, .btn-solid:focus-visible{outline-color:transparent}
  .btn-ghost:hover, .btn-ghost:focus-visible{border-color:var(--line-strong)}

  /* GHOST: plain azure into a pale core. It has near-black to sit
     against, so it needs no help. Measured 5.36:1 at the shoulders
     and 17.62:1 at the core. */
  .btn-ghost::after{
    background:conic-gradient(from var(--btn-angle),
      transparent 0deg, transparent 232deg,
      var(--accent) 272deg, #EAF2FF 318deg, var(--accent) 348deg,
      transparent 360deg);
  }

  /* SOLID: the same lesson as the ring above, arrived at a second
     time. An azure arc on an azure fill is the 1.00:1 smudge again,
     so the core goes white. But white on azure is only 3.71:1 and
     that is the CEILING, because white and azure are as far apart
     as those two colours get. Brightness cannot fix it.

     So the arc gets DARK shoulders either side of the core, which is
     how a specular highlight actually sits on a curved edge. The eye
     reads a highlight by the step between it and what is beside it,
     not by how bright it is on its own. Core against fill is still
     3.71. Core against its own shoulder went from 1.70 to 10.31, and
     that is the number doing the work. */
  .btn-solid::after{
    background:conic-gradient(from var(--btn-angle),
      transparent 0deg, transparent 228deg,
      rgba(8,9,11,.55) 258deg,
      #FFFFFF 298deg, #FFFFFF 328deg,
      rgba(8,9,11,.45) 352deg, transparent 360deg);
  }

  /* Reduced motion: the light would be a moving thing they asked not
     to have. Give them the ring back, exactly as it shipped before,
     because the boundary is the part that carries the meaning. */
  @media (prefers-reduced-motion: reduce){
    .btn::after{content:none}
    .btn:hover, .btn:focus-visible{outline-color:var(--line-strong)}
    .btn-solid:hover, .btn-solid:focus-visible{outline-color:var(--ink)}
    .btn-ghost:hover, .btn-ghost:focus-visible{border-color:var(--ink)}
  }
 }
}

/* ═══ surfaces ═══ */
.card{
  background:var(--bg-2); border:1px solid var(--line);
  border-radius:var(--r-card);
}

/* CLEAR glass - a pane you read through, not a frosted slab.
   The v1 site's glass was 26px blur + travelling glare + pointer
   tilt; that reads as a gimmick. This is one plate of window. */
.glass{
  position:relative;
  background:var(--glass-film);
  backdrop-filter:blur(var(--glass-blur));
  -webkit-backdrop-filter:blur(var(--glass-blur));
  border:1px solid var(--glass-edge);
  border-radius:var(--r-card);
}
/* the bevel catch: one hairline of light along the top edge */
.glass::before{
  content:''; position:absolute; inset:0 0 auto 0; height:1px;
  border-radius:var(--r-card) var(--r-card) 0 0;
  background:linear-gradient(90deg, transparent, var(--glass-bevel) 22%,
             var(--glass-bevel) 78%, transparent);
  pointer-events:none;
}
/* on glass, content sits above the bevel line */
.glass > *{position:relative}

/* ═══ layout ═══ */
.wrap{width:min(1240px, 100% - clamp(40px, 6vw, 96px)); margin-inline:auto}
.section{padding-block:var(--s10)}
