/* ============================================================================
   MICROSHALA - landing page
   Dark stage, brand-coded chrome: #FFA500 orange · #FF0000 red · #1A1A1A black
   The fibre strands keep their own spectrum - that spread is what makes the
   burst read, and it is the one thing deliberately not brand-coded.
============================================================================ */

/* ---------------------------------------------------------------- TOKENS -- */
:root {
  /* Brand */
  --white:  #FFFFFF;
  --orange: #FFA500;
  --red:    #FF0000;
  --black:  #1A1A1A;

  --page-bg: #04060C;      /* the page itself, behind the frame */

  /* Surfaces */
  --ink-900: #04060C;      /* page */
  --ink-850: #05080F;
  --ink-800: #070B15;
  --ink-700: #141414;
  --ink-600: #1F1F1F;

  /* Lines and text are pure white at reduced alpha - the old blue-tinted
     ramp was left over from the first draft and was not a brand colour. */
  --line:   rgba(255, 255, 255, .11);
  --line-2: rgba(255, 255, 255, .19);

  /* Text - the brand white, stepped down by opacity */
  --txt:   #FFFFFF;
  --txt-2: rgba(255, 255, 255, .72);
  --txt-3: rgba(255, 255, 255, .48);

  /* On this background #FFA500 clears 9:1, so the flat brand orange is safe
     for text as well as fills - no darkened ink variant needed. */
  --orange-ink: var(--orange);
  --orange-08: rgba(255, 165, 0, .08);
  --orange-16: rgba(255, 165, 0, .16);
  --orange-45: rgba(255, 165, 0, .45);

  --a: var(--orange);        /* per-card accent, set by JS */

  /* Spacing - 8pt */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

  /* Radius */
  --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 20px; --r-2xl: 24px;

  /* Motion */
  --t-fast: 150ms;
  --t-mid:  250ms;
  --t-slow: 400ms;
  --ease:   cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);

  /* Elevation */
  --sh-1: 0 1px 2px rgba(0,0,0,.4);
  --sh-2: 0 8px 24px -8px rgba(0,0,0,.6);
  --sh-3: 0 28px 70px -18px rgba(0,0,0,.78);

  --topbar-h: 64px;

  /* The stage is where the whole scene lives, and it is the one box that
     genuinely cannot be squashed: below this the board and the six blocks
     have nowhere to go. Every breakpoint sets its own floor further down,
     and the page scrolls if a viewport is too short to hold it. */
  --stage-min: 460px;

  /* ------------------------------------------------------------ SHELL --
     ONE container for the whole site. Before this the header ran edge to
     edge, the panel head sat at the gutter, the panel body was centred in
     1560px and the footer in 1360px: at 1920 that put their left edges at
     24 / 48 / 228 / 304px. A panel's own title was 180px left of its own
     body text.

     --edge is the single inset every shell element uses. It is the gutter
     until the viewport passes --shell-max, then it grows to centre the
     content instead. Because it is a custom property, the 100% resolves
     against each element's own containing block when it is used, which is
     the viewport for all of them - so one declaration lines them all up
     without wrapping anything in a new container div.

     Backgrounds stay full-bleed, which is why this is padding rather than
     max-width: the header bar, the panel head gradient and the footer rule
     still span the window. */
  --shell-max: 1560px;
  --pad-x: clamp(16px, 3.4vw, 48px);          /* the gutter, on any screen */
  --edge: max(var(--pad-x), (100% - var(--shell-max)) / 2);

  /* Devanagari faces are named explicitly after the Latin ones. The Latin
     fonts cover Latin, so these are only reached for तक्षशिला and anything
     else in the script: Kohinoor on Mac, Nirmala on Windows, Noto elsewhere.
     Without naming them the browser falls back to whatever that machine
     happens to have, which is how the same word ends up looking different on
     every PC. */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI",
          "Inter", Roboto, "Helvetica Neue", Arial,
          "Kohinoor Devanagari", "Nirmala UI", "Noto Sans Devanagari",
          "Devanagari Sangam MN", sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
}

/* ------------------------------------------------------------------ BASE -- */
*, *::before, *::after { box-sizing: border-box; }
html, body { min-height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.55;
  color: var(--txt);
  background: var(--page-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
}
/* the panel covers the viewport, so the page behind it must not scroll under
   the finger. JS pads for the scrollbar it removes. */
body.is-locked { overflow: hidden; }

/* ------------------------------------------------------- PAGE FRAME -- */
/* A normal responsive column: top bar, stage, footer. It fills the viewport
   on anything tall enough and grows past it when it has to, so the scene is
   never crushed to fit. */
.frame {
  position: relative;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;      /* the small viewport: no jump as the URL bar hides */
  display: flex;
  flex-direction: column;
  background: var(--ink-900);
}

h1, h2, p, ul { margin: 0; }
ul { padding: 0; list-style: none; }

button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

body.is-booting .topbar,
body.is-booting .footer,
body.is-booting .stage__grid,
body.is-booting .stage__wash,
body.is-booting .ctl { opacity: 0 !important; }

/* --------------------------------------------------------------- TOPBAR -- */
.topbar {
  /* Sticky rather than relative, and above the panel rather than under it, so
     it stays put over an open section. Sticky keeps it in the flow, so the
     stage still gets the same height it always did. The panel is inset by
     --topbar-h to sit underneath; the two have to stay in step. */
  position: sticky; top: 0;
  z-index: 45;
  flex: 0 0 auto;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding-inline: var(--edge);
  border-bottom: 1px solid transparent;
  background: var(--ink-900);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 700ms var(--ease-out), transform 700ms var(--ease-out),
              border-color 700ms var(--ease-out);
}
.topbar.is-in { opacity: 1; transform: none; border-bottom-color: var(--line); }

/* The bar is a space-between row, so the two marks need to be one item or
   they get pushed to opposite ends of it. */
.topbar__left { display: flex; align-items: center; }

.topbar__brand { display: flex; align-items: center; gap: 10px; }

/* Microscan is the parent company and Microshala is its learning and
   development initiative, so the two marks sit together with a hairline
   between them. Side by side with a rule between reads as "part of"; parked at
   the far end of the bar it would read as an unrelated second destination.
   The globe alone, not the full lockup: see the note in content.js. */
.topbar__parent {
  display: flex; align-items: center;
  margin-left: 14px; padding-left: 14px;
  border-left: 1px solid var(--line-2);
  opacity: .8;
  transition: opacity var(--t-mid) var(--ease);
}
.topbar__parent:hover, .topbar__parent:focus-visible { opacity: 1; }
.topbar__parent[hidden] { display: none; }
/* 26px, down from 30. Matched to the Microshala mark beside it by letter
   height rather than by box: at 26 the word "microscan" is about 18px against
   Microshala's 30px, so the parent reads as the parent. The two come out 147
   and 148px wide, which is why they sit as a pair. */
.topbar__parentLogo { display: block; height: 26px; width: auto; }
/* Only ever one of the two on screen. The wordmark is a single flat colour, so
   the black one vanishes on the dark bar and the white one vanishes on the
   light bar; the artwork is otherwise identical, so nothing shifts. */
.topbar__parentLogo--light { display: none; }
[data-theme="light"] .topbar__parentLogo--dark  { display: none; }
[data-theme="light"] .topbar__parentLogo--light { display: block; }
/* the real logo, small. The plate behind it keeps the black wordmark legible
   in dark mode, exactly as the board does. */
/* 44px of the cropped mark, which puts the word itself at about 30px - half
   again the 23px it managed at 48px with the strapline still taking a third of
   the artwork's height. Smaller number, considerably bigger logo. */
.topbar__logo {
  display: block; height: 44px; width: auto;
  padding: 4px 10px; border-radius: 8px;
  background: #FFFFFF;
}
.topbar__name { font-size: 13px; font-weight: 650; letter-spacing: .2em; color: var(--txt); }
.topbar__name[hidden] { display: none; }

/* light / dark switch */
.theme {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  margin-left: var(--s2);
  border-radius: 50%;
  border: 1px solid var(--line-2);
  color: var(--txt-2);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease);
}
.theme:hover { color: var(--orange); border-color: var(--orange-45); background: var(--orange-08); }
.theme svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
             stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.theme__moon { display: none; }
[data-theme="light"] .theme__sun  { display: none; }
[data-theme="light"] .theme__moon { display: block; }

/* Devanagari sits smaller than Latin at the same font-size. Measured in this
   stack at 16px: the base glyph of क is 10.27px against a Latin cap height of
   11.27, so a word like तक्षशिला reads at about 91% of the sentence it sits
   in. 1.1 brings its base glyphs up to the height of the capitals beside it.

   Relative, so it holds at every size the word appears at - body copy, list
   item, folded heading - without a rule for each.

   line-height:1 is load bearing. Without it the taller inline box opens the
   line up and any paragraph containing the word gets a wider gap above it than
   its neighbours. At 1 the span's box is 1.1em, comfortably inside the 1.55em
   line the paragraph already has, so nothing shifts. */
.deva { font-size: 1.1em; line-height: 1; }

/* The sound toggle borrows .theme for its shape and sits beside it. Muted is
   the resting state, so the crossed-out speaker is what shows by default and
   the button only takes the accent once sound is actually on. */
.sound__on { display: none; }
.sound.is-on .sound__on  { display: block; }
.sound.is-on .sound__off { display: none; }
.sound.is-on { color: var(--orange); border-color: var(--orange-45); background: var(--orange-08); }

.topbar__nav { display: flex; align-items: center; gap: var(--s2); }

.topbar__link {
  padding: 8px 12px;
  border-radius: var(--r-sm);
  /* transparent rather than none, so hovering can bring a border in without
     the button changing size as it does */
  border: 1px solid transparent;
  font-size: 13.5px; font-weight: 500;
  color: var(--txt-2);
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
/* The same orange lift Home and the theme toggle use, so every control in the
   bar answers a hover the same way instead of LMS alone going grey. */
.topbar__link:hover {
  color: var(--orange-ink);
  background: var(--orange-08);
  border-color: var(--orange-45);
}

/* Home only means something once you have gone somewhere. Hidden on the
   landing page, shown by .is-panel while a section is open. Carries the pill
   and the house icon the panel's own Home button used to have, so the control
   that used to sit inside a section now reads the same way up here. */
.topbar__link--home { display: none; }
.is-panel .topbar__link--home {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 15px 8px 12px;
  border-radius: 999px;
  border: 1px solid var(--line-2);
  font-weight: 580;
}
.topbar__link--home svg { width: 16px; height: 16px; flex: 0 0 auto; }
.topbar__link--home:hover {
  background: var(--orange-08);
  border-color: var(--orange-45);
  color: var(--orange-ink);
}
[data-theme="light"] .topbar__link--home:hover { color: var(--orange-ink); }

.topbar__cta {
  margin-left: var(--s2);
  padding: 9px 18px;
  border-radius: 999px;
  font-size: 13.5px; font-weight: 620;
  color: var(--black);                 /* 9.2:1 on orange */
  background: var(--orange);
  transition: filter var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
}
.topbar__cta:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 8px 22px -8px var(--orange-45);
}
.topbar__cta:active { transform: translateY(0); }
.topbar__cta--drawer { display: none; }

/* ------------------------------------------------------------ NAV MENUS -- */
/* Two section menus in the bar. Built on <details> for the same reason every
   fold on this site is: it gives keyboard operation, Escape and the open state
   without a line of script, and it degrades to an open list if script fails.
   site.js only adds what markup cannot do - closing on an outside click and
   keeping the two from being open at once. */

.topbar__primary { display: flex; align-items: center; gap: var(--s2); }

.menu { position: relative; }

.menu__btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  font-size: 13.5px; font-weight: 500;
  color: var(--txt-2);
  cursor: pointer;
  list-style: none;                    /* kills the default disclosure marker */
  white-space: nowrap;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.menu__btn::-webkit-details-marker { display: none; }
.menu__btn:hover,
.menu[open] > .menu__btn {
  color: var(--orange-ink);
  background: var(--orange-08);
  border-color: var(--orange-45);
}
.menu__btn:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

.menu__caret {
  width: 14px; height: 14px; flex: 0 0 auto;
  opacity: .75;
  transition: transform var(--t-fast) var(--ease);
}
.menu[open] .menu__caret { transform: rotate(180deg); }

/* The popover. Anchored to the button rather than the bar so it stays under
   its own label whichever menu is open. */
.menu__pop {
  position: absolute; top: calc(100% + 8px); left: 0;
  min-width: 260px;
  padding: 6px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: var(--ink-850);
  box-shadow: 0 26px 60px -26px rgba(0, 0, 0, .85),
              0 0 0 1px rgba(255, 255, 255, .02);
  z-index: 50;
}
/* the wide one would run off the right edge if it opened leftwards */
#menuExternal .menu__pop { left: auto; right: 0; }

.menu__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.menu__item {
  display: block; width: 100%;
  padding: 9px 12px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  text-align: left;
  font-size: 13.5px; font-weight: 500; line-height: 1.35;
  color: var(--txt-2);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.menu__item:hover { color: var(--orange-ink); background: var(--orange-08); }
.menu__item:focus-visible { outline: 2px solid var(--orange); outline-offset: -2px; }
/* where you already are */
.menu__item[aria-current="true"] { color: var(--orange); font-weight: 620; }

/* --- Mode & Sound, one labelled pair rather than two loose icons ----------- */
.topbar__modes {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
}
.topbar__modes .theme { border-color: transparent; }

/* --- LMS: the secondary action, outlined against the one solid button ------ */
.topbar__lms {
  border-radius: 999px;
  border-color: var(--line-2);
  font-weight: 580;
  white-space: nowrap;
}

/* --- the drawer button, phones only ---------------------------------------- */
.navtoggle {
  display: none;
  /* never let the row squeeze it: a squashed hamburger is a squashed tap
     target, and this is the only way into the menu on a phone */
  flex: 0 0 auto;
  width: 36px; height: 36px;
  align-items: center; justify-content: center;
  flex-direction: column; gap: 4px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.navtoggle__bar {
  display: block; width: 16px; height: 1.6px;
  background: currentColor; border-radius: 2px;
  transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.navtoggle:hover { color: var(--orange-ink); border-color: var(--orange-45); background: var(--orange-08); }
.topbar.is-open .navtoggle__bar:nth-child(1) { transform: translateY(5.6px) rotate(45deg); }
.topbar.is-open .navtoggle__bar:nth-child(2) { opacity: 0; }
.topbar.is-open .navtoggle__bar:nth-child(3) { transform: translateY(-5.6px) rotate(-45deg); }

[data-theme="light"] .menu__pop { background: #FFFFFF; }
[data-theme="light"] .menu__btn:hover,
[data-theme="light"] .menu[open] > .menu__btn,
[data-theme="light"] .menu__item:hover { color: var(--orange-ink); }

/* ---------------------------------------------------------------- STAGE -- */
.stage {
  position: relative;
  flex: 1 1 auto;
  min-height: var(--stage-min);
  overflow: hidden;
  isolation: isolate;
  background: var(--ink-900);
}

/* depth wash so the black never reads flat */
.stage__wash {
  position: absolute; inset: -10%;
  background:
    radial-gradient(58% 52% at 50% 46%, rgba(28,64,110,.42), transparent 68%),
    radial-gradient(38% 40% at 78% 24%, rgba(168,121,255,.13), transparent 70%),
    radial-gradient(42% 46% at 18% 78%, rgba(240,180,74,.09), transparent 72%);
  opacity: 0;
  transition: opacity 1400ms var(--ease-out);
}
.stage__wash.is-in { opacity: 1; }

.stage__grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right,  var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 68px 68px;
  -webkit-mask-image: radial-gradient(70% 62% at 50% 50%, #000 0%, transparent 78%);
          mask-image: radial-gradient(70% 62% at 50% 50%, #000 0%, transparent 78%);
  opacity: 0;
  transition: opacity 1600ms var(--ease-out);
}
.stage__grid.is-in { opacity: .5; }

.scene { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }

/* --- drop cable ---------------------------------------------------------- */
.cable-core   { fill: none; stroke: #121212; stroke-linecap: round; }
.cable-jacket { fill: none; stroke: #8C8C8C; stroke-width: 1.8; stroke-linecap: round; }
.cable-strand { fill: none; stroke-width: 1.4; stroke-linecap: round; opacity: .9; }
.cleat        { stroke: #7E7E7E; stroke-width: 2.6; stroke-linecap: round; }
.cleat.is-lit { stroke: var(--orange); }
.cleat-screw  { fill: #9C9C9C; }

/* --- stick figure + its tools -------------------------------------------- */
.fig       { fill: none; stroke: #FFFFFF; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.fig--head { fill: #FFFFFF; stroke: none; }
/* the dress: one solid shape with the torso, which is what makes it read */
.fig--skirt { fill: #FFFFFF; stroke: none; }
/* the solid body, its counterpart on the other figure */
.fig--torso { fill: #FFFFFF; stroke: none; }
/* trousers: 4.8 wide, so their outer edge meets the side of the body exactly */
.fig--leg   { stroke-width: 4.8; }

/* Tools sit in the figure's own frame so they scale with it. Strokes are
   heavier than the last attempt - thin ones disappeared at this size. */
.prop        { fill: none; stroke: #E2E2E2; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.prop--box   { fill: #262626; stroke: #E2E2E2; stroke-width: 1.9; }
.prop--fibre { stroke: #9A9A9A; stroke-width: 1.7; }
.prop--lit   { fill: var(--orange); stroke: none; }
.prop--flash { fill: none; stroke: #FFFFFF; stroke-width: 2.6; }
.prop--ray   { fill: none; stroke: #FFFFFF; stroke-width: 2.2; stroke-linecap: round; }

/* Sitting on the white board, light strokes vanish - everything goes dark, and
   the arc flash goes orange because white-on-white would not show at all. */
.fig, .fig--head, .fig--skirt, .fig--torso, .prop, .prop--box, .prop--fibre, .prop--flash, .prop--ray {
  transition: stroke 450ms var(--ease), fill 450ms var(--ease);
}
.on-board .fig        { stroke: var(--black); }
.on-board .fig--head  { fill: var(--black); }
.on-board .fig--skirt { fill: var(--black); }
.on-board .fig--torso { fill: var(--black); }
.on-board .prop       { stroke: #46586E; }
.on-board .prop--box  { fill: #DFE4EB; stroke: #46586E; }
.on-board .prop--fibre{ stroke: #9DB2C9; }   /* these sit above the plate, on dark */
.on-board .prop--flash{ stroke: #FF6A00; }
.on-board .prop--ray  { stroke: #FF6A00; }

/* --- the mortarboard ----------------------------------------------------- */
/* The red cap off the Microshala logo. It drops onto the figure's head as it
   takes its seat on the board, so the climber becomes the graduate. Filled
   rather than stroked, so the .on-board colour flip leaves it alone - the red
   has to stay red on the white plate as much as on the dark stage. */
.fig-cap__crown  { fill: #E4121B; stroke: #9C0A10; stroke-width: .7; }
.fig-cap__board  { fill: #ED1C24; stroke: #9C0A10; stroke-width: .7; stroke-linejoin: round; }
.fig-cap__btn    { fill: #F2B01E; stroke: none; }
.fig-cap__cord   { fill: none; stroke: #F2B01E; stroke-width: 1.1; stroke-linecap: round; }
.fig-cap__fringe { fill: none; stroke: #F2B01E; stroke-width: 2.3; stroke-linecap: round; }

/* the outline only earns its keep once the figure is over the white plate */
.fig-halo {
  fill: none; stroke: #FFFFFF; stroke-width: 7;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
  transition: opacity 450ms var(--ease);
}
.fig-halo--head { fill: #FFFFFF; }
.fig-halo--skirt { fill: #FFFFFF; }
.fig-halo--torso { fill: #FFFFFF; }

.on-board .fig-halo { opacity: 1; }

/* --- fibres -------------------------------------------------------------- */
/* solid core over a permanent halo, so every strand reads as lit rather than
   only the hovered one */
.fibre      { fill: none; stroke-width: 2; stroke-linecap: round; }
.fibre-glow { filter: url(#fx-soft); opacity: .62; }
.fibre-glow .fibre { stroke-width: 6; }
.pulse      { fill: none; stroke-width: 3.2; stroke-linecap: round; }
.spark      { fill: none; stroke-linecap: round; }
.shock      { fill: none; stroke-width: 2; }

/* --- the board (a light plate, so the black in the logo reads) ------------ */
/* Solid white, not a gradient: the logo PNG carries a white matte, and any
   tint on the plate shows up as a visible box seam around it. */
.board       { fill: #FFFFFF; stroke: rgba(255,255,255,.28); stroke-width: 1.2;
               filter: url(#fx-board); }
/* rack detail on the landing box - a rack lying on its side */
.rack-ear    { fill: #C2C6CE; }
.rack-bay    { stroke: rgba(26,26,26,.22); stroke-width: 1; }
.rack-vent   { stroke: rgba(26,26,26,.15); stroke-width: 1.5; stroke-linecap: round; }
.rack-led    { stroke: none; }
.board-edge  { stroke: var(--orange); stroke-width: 2.5; stroke-linecap: round; }
.board-screw { fill: #C4C4C9; }

/* --------------------------------------------------------- HUB WORDMARK -- */
/* Sits centred on the board. JS sizes the box to the plate minus even padding,
   so the logo lands dead centre with the figure standing clear to its left. */
.hub {
  position: absolute;
  z-index: 3;
  /* flex, not grid: JS gives .hub a definite height, and a percentage height
     on the logo resolves against that. In an auto-sized grid row it would not
     resolve at all and the logo would overflow the plate. */
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 620ms var(--ease-out);
}
.hub.is-in { opacity: 1; }

/* Fill the box and let object-fit letterbox inside it. max-height:100% would
   resolve against an auto-sized grid row and never actually constrain, so the
   logo would overflow the plate. */
.hub__logo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* stand-in until the real logo file is dropped in - dark, because the plate
   it sits on is light */
.hub__fallback[hidden] { display: none; }
.hub__mark {
  font-size: clamp(20px, 1.6vw + 9px, 36px);
  font-weight: 750;
  letter-spacing: .2em;
  line-height: 1.05;
  color: var(--black);
}
.hub__sub {
  margin-top: 6px;
  font-size: clamp(10.5px, 0.31vw + 6.5px, 13.5px);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(26, 26, 26, .5);
}
.hub__sub strong { color: #A85F00; font-weight: 700; }

/* ---------------------------------------------------------------- NODES -- */
.nodes { position: absolute; inset: 0; z-index: 4; pointer-events: none; }

.node {
  position: absolute;
  pointer-events: auto;
  width: var(--nw); height: var(--nh);
  padding: 14px 15px;
  display: flex; flex-direction: column;
  align-items: flex-start; justify-content: space-between;
  text-align: left;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, rgba(38,38,38,.92), rgba(13,13,13,.92));
  box-shadow: var(--sh-2);
  -webkit-backdrop-filter: blur(9px) saturate(130%);
          backdrop-filter: blur(9px) saturate(130%);
  color: var(--txt);
  opacity: 0;
  /* --dx/--dy/--k are driven by JS during the intro, then removed so the
     CSS hover lift below can take over cleanly. */
  transform: translate(calc(-50% + var(--dx, 0px)), calc(-50% + var(--dy, 0px)))
             scale(var(--k, 1));
  transition: border-color var(--t-mid) var(--ease),
              box-shadow   var(--t-mid) var(--ease);
  will-change: transform, opacity;
}

/* `hidden` on a .node does nothing on its own: the attribute works through the
   browser's own [hidden] { display: none }, and .node sets display: flex at the
   same specificity, so the author rule wins. A folded branch was therefore
   still laid out and still hit-testing - invisible only because .node starts at
   opacity 0 - and `certifications` sat across the middle of the shut External
   Professionals bar and swallowed every click on it. */
.node[hidden] { display: none; }

.node.is-settled { transition: border-color var(--t-mid) var(--ease),
                               box-shadow   var(--t-mid) var(--ease),
                               opacity      var(--t-mid) var(--ease),
                               transform    var(--t-slow) var(--ease); }
.node.is-settled:hover, .node.is-settled:focus-visible { --dy: -4px; }

/* the strand that feeds this block */
.node::before {
  content: '';
  position: absolute; inset: -1px auto auto -1px;
  width: calc(100% + 2px); height: 2px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: linear-gradient(90deg, transparent, var(--a), transparent);
  opacity: .9;
}
.node::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: radial-gradient(120% 90% at 20% 0%, var(--orange), transparent 62%);
  opacity: 0;
  transition: opacity var(--t-mid) var(--ease);
  pointer-events: none;
}

.node:hover, .node:focus-visible {
  border-color: color-mix(in srgb, var(--orange) 55%, transparent);
  box-shadow: var(--sh-3), 0 0 0 1px color-mix(in srgb, var(--orange) 26%, transparent),
              0 18px 46px -20px var(--orange);
}
.node:hover::after, .node:focus-visible::after { opacity: .13; }

.node__top { display: flex; align-items: center; justify-content: space-between; width: 100%; }

.node__icon {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--orange) 30%, transparent);
  background: color-mix(in srgb, var(--orange) 11%, transparent);
  color: var(--orange);
  flex: 0 0 auto;
}
.node__icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor;
                  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* --nfs is the one dial site.js turns to make the longest label fit whatever
   block height the layout could afford. 14.5px is the size it starts from. */
.node__label {
  /* 17px, up from 14.5. Measured against the narrowest label column this
     layout produces (152px, beside the icon in a 220px block): the longest
     label still breaks to two lines and stands 44px tall in a 69px box.
     Phones set their own 13px below, so this only reaches 560px and up. */
  font-size: var(--nfs, 17px); font-weight: 600;
  line-height: 1.3;
  letter-spacing: -.005em;
  color: var(--txt);
}

.node__go {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 520; letter-spacing: .01em;
  color: var(--txt-2);
  transition: color var(--t-mid) var(--ease), gap var(--t-mid) var(--ease);
}
.node:hover .node__go, .node:focus-visible .node__go { color: var(--orange); gap: 10px; }
.node__go svg { width: 13px; height: 13px; fill: none; stroke: currentColor;
                stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* A long label with no room to spare. Tighter padding and a smaller icon buy
   about 12px, which is usually enough to keep the Explore row; site.js only
   reaches for node--sm when the type ladder has run out too. */
.node--tight { padding: 12px 14px; --nfs: 13.5px; }
.node--tight .node__icon { width: 30px; height: 30px; border-radius: 9px; }
.node--tight .node__icon svg { width: 16px; height: 16px; }
.node--tight .node__label { line-height: 1.26; }
.node--tight .node__go { font-size: 11.5px; }

/* The two umbrella headings. They open a panel like any other block, but they
   are not cards: they are the label over an arc of three. Orange throughout,
   set in caps, so the eye reads them as a heading before it reads them as
   something to click. */
.node--head {
  padding: 11px 16px;
  border-color: color-mix(in srgb, var(--orange) 58%, transparent);
  background: linear-gradient(118deg,
    color-mix(in srgb, var(--orange) 21%, rgba(24, 18, 6, .95)),
    color-mix(in srgb, var(--orange) 7%,  rgba(13, 13, 13, .95)));
  box-shadow: var(--sh-2), inset 0 0 0 1px color-mix(in srgb, var(--orange) 10%, transparent);
}
/* the whole bar is the accent now, so the strand across the top is redundant */
.node--head::before { display: none; }
.node--head .node__label {
  font-size: var(--nfs, 13px);
  font-weight: 700;
  letter-spacing: .085em;
  text-transform: uppercase;
  color: var(--orange);
  /* two lines of caps set tighter, so the air lands around the block rather
     than between its own lines */
  line-height: 1.3;
}
.node--head .node__icon {
  width: 30px; height: 30px; border-radius: 9px;
  border-color: color-mix(in srgb, var(--orange) 55%, transparent);
  background: color-mix(in srgb, var(--orange) 20%, transparent);
}
.node--head .node__icon svg { width: 16px; height: 16px; }
.node--head .node__go { color: color-mix(in srgb, var(--orange) 85%, var(--txt-2)); }
/* On a bar there is no room for it, and a heading that says "Explore" beside
   its own name is telling you something the arc underneath already has. */
.node--head.node--bar .node__go { display: none; }

/* A heading with branches folded under it takes two clicks: the first opens
   the three, the second opens the heading's own panel. The caret is what says
   there is something in here. Once it is open the caret has nothing left to
   promise, so it goes and Explore takes its place - which is the second click
   asking to be made. Nothing has to be learned from a tooltip. */
.node__caret {
  display: none; flex: 0 0 auto; margin-left: auto;
  color: color-mix(in srgb, var(--orange) 85%, var(--txt-2));
  transition: transform var(--t-mid) var(--ease);
}
.node__caret svg { display: block; width: 19px; height: 19px; }
.node--head.is-shut .node__caret { display: block; }
.node--head.is-shut .node__go { display: none; }
.node--head.is-shut:hover .node__caret,
.node--head.is-shut:focus-visible .node__caret { transform: translateY(2px); }
.node--head.is-open.node--bar .node__go { display: flex; }
.node--head.node--bar .node__label { flex: 1 1 auto; }

.node--head:hover, .node--head:focus-visible {
  border-color: var(--orange);
  background: linear-gradient(118deg,
    color-mix(in srgb, var(--orange) 30%, rgba(24, 18, 6, .95)),
    color-mix(in srgb, var(--orange) 11%, rgba(13, 13, 13, .95)));
}

[data-theme="light"] .node--head {
  background: linear-gradient(118deg,
    color-mix(in srgb, var(--orange) 26%, #FFFFFF),
    color-mix(in srgb, var(--orange) 10%, #FFFFFF));
}
[data-theme="light"] .node--head .node__label { color: var(--orange-ink); }

/* A block far wider than it is tall cannot stack its parts, so it lays them
   out along the bar instead. This is what the headings and the centre card
   become once the stage folds into rows. */
.node--bar {
  flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 12px;
  padding: 10px 15px;
}
.node--bar .node__top   { width: auto; flex: 0 0 auto; }
.node--bar .node__label { flex: 0 1 auto; }
.node--bar .node__go    { flex: 0 0 auto; margin-left: auto; }
.node--bar.node--head .node__label { font-size: var(--nfs, 15px); }

/* Short cards drop the "Explore" row rather than crowd the label, which left
   the icon at the top and the label stranded at the bottom of the box. They
   sit side by side now, the same way the heading bars carry theirs, so every
   block on the stage reads as one icon-and-label unit. */
.node--sm {
  padding: 12px 13px;
  flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 12px;
}
.node--sm .node__top   { width: auto; flex: 0 0 auto; }
.node--sm .node__label { flex: 1 1 0; min-width: 0; }
.node--sm .node__go { display: none; }
.node--sm .node__icon { width: 30px; height: 30px; }
.node--sm .node__icon svg { width: 16px; height: 16px; }

/* Every panel's opening block, level with its illustration.

   The illustration centres itself in the first row; the text used to start at
   the top of it, so the two sat as much as 56px out of step - worst on
   Well-Being, and visible on five of the nine sections.

   The band is a multi-column container and cannot centre a child. Making it a
   grid was the first attempt and it reflowed enough to trip the overlap test,
   which threw the illustration into a row of its own: worse than the problem.
   So the BLOCK takes the row's own height floor and centres its own contents.
   The band's layout is untouched, nothing reflows, and the two middles land on
   the same line. Paragraph spacing survives because it comes from an explicit
   sibling margin rather than from collapsing.

   About Us is excepted: it centres by its own means, is already exact, and is
   signed off. */
@media (min-width: 1100px) {
  .panel__body.has-scene:not([data-node="about"]) > .band:first-child > .blk {
    min-height: 196px;
    display: flex; flex-direction: column; justify-content: center;
  }
}

/* ---------------------------------------------------- THE ABOUT STORY --- */
/* One argument, read top to bottom: the claim, the years behind it, four
   movements from experience to capability, and what the reader leaves with.
   Deliberately not cards - a transformation is a line, and boxing each step
   would make five equal things out of one sequence. */
.sty { display: block; }

/* --- the claim ---------------------------------------------------------- */
/* Its own block now, and narrow, so it takes the first column of the band and
   the illustration takes the second - level with it, rather than stacked
   above it. Stacked inside: heading, then the line under it. */
.sty__claim {
  display: flex; flex-direction: column;
  gap: var(--s3);
}
/* The body is ONE column at this width, so a block that is merely "not wide"
   still spans it - and a full-width claim reaches under the illustration.
   The scene test reads that as an overlap and drops the artwork into a row of
   its own above the heading, which is what made them look like two separate
   things. Held to 68%, the claim clears the illustration and the two share a
   row. Centred against each other rather than both starting at the top.
   Scoped to this claim, so no other panel's scene placement moves. */
@media (min-width: 1100px) {
  .panel__body.has-scene .sty__claim { max-width: 68%; }
  .panel__body.has-scene > .band:first-child:has(.sty__claim) {
    display: grid;
    align-content: center;
  }
}
.sty__title {
  margin: 0;
  font-size: clamp(30px, 3.1vw + 14px, 68px);
  font-weight: 680;
  line-height: 1.06;
  letter-spacing: -0.022em;
  text-wrap: balance;
  color: var(--txt);
}
.sty__lede {
  margin: 0;
  max-width: 62ch;
  font-size: clamp(15.5px, 0.55vw + 9px, 21px);
  line-height: 1.62;
  color: var(--txt-2);
}

/* --- the years ---------------------------------------------------------- */
/* One line, reading straight through: the numeral is emphasis, not a separate
   element, so it must not push the words it belongs to onto a second row. */
.sty__fig {
  margin: var(--s5) 0 0;
  /* centred against the numeral, not sitting on its baseline. Baseline-aligned,
     the words hung at the very foot of a 68px figure and read as dropped. */
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}
.sty__num {
  font-size: clamp(38px, 3.9vw + 18px, 86px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.035em;
  color: var(--orange);
  font-variant-numeric: tabular-nums;
}
.sty__nlab {
  font-size: clamp(15.5px, 1.1vw + 6px, 26px);
  font-weight: 560;
  line-height: 1.2;
  letter-spacing: -0.005em;
  /* the same orange as the numeral, so the whole line reads as one figure
     rather than a number with a grey caption beside it */
  color: var(--orange);
  white-space: nowrap;
}
/* only where the row genuinely cannot hold it */
@media (max-width: 560px) {
  .sty__nlab { white-space: normal; text-wrap: balance; }
}

/* --- the four movements ------------------------------------------------- */
/* The strand is the site's own language, and it is load-bearing here: it is
   what says these four are one sequence rather than four statements. */
.sty__steps {
  list-style: none;
  margin: var(--s5) 0 0;
  padding: 0 0 0 var(--s5);
  position: relative;
}
.sty__steps::before {
  content: '';
  position: absolute; left: 5px; top: 6px; bottom: 6px;
  width: 2px; border-radius: 2px;
  background: linear-gradient(180deg,
    var(--orange),
    color-mix(in srgb, var(--orange) 30%, transparent) 55%,
    color-mix(in srgb, var(--orange) 8%, transparent));
  transform-origin: top;
  transform: scaleY(0);
  transition: transform 1100ms var(--ease-out);
}
.sty.is-in .sty__steps::before { transform: scaleY(1); }

.sty__step {
  position: relative;
  padding: 0 0 var(--s5) 0;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
  transition-delay: calc(240ms + var(--i) * 150ms);
}
.sty.is-in .sty__step { opacity: 1; transform: none; }
.sty__step:last-child { padding-bottom: 0; }

/* the node on the strand */
.sty__step::before {
  content: '';
  position: absolute; left: calc(var(--s5) * -1 + 1px); top: 5px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--ink-900);
  border: 2px solid var(--orange);
}

.sty__k {
  display: block;
  margin-bottom: 8px;
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .17em; text-transform: uppercase;
  color: var(--orange);
}
.sty__t {
  margin: 0;
  max-width: 64ch;
  font-size: clamp(15.5px, 0.55vw + 9px, 20px);
  line-height: 1.62;
  color: var(--txt-2);
}

/* --- what the reader leaves with ---------------------------------------- */
.sty__close {
  margin: var(--s5) 0 0;
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
  font-size: clamp(20px, 1.9vw + 8px, 40px);
  font-weight: 640;
  letter-spacing: -0.015em;
  line-height: 1.25;
  color: var(--txt);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
  transition-delay: 900ms;
}
.sty.is-in .sty__close { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .sty__steps::before, .sty__step, .sty__close { transition: none; }
}

/* --------------------------------------------- THE COMING-SOON CARD ----- */
/* Gallery and Blog have nowhere to go yet. The label stays a real control and
   the click answers honestly, with the same three dots the site already uses
   for anything pending. */
.footer__soon {
  background: none; border: 0; padding: 0;
  font: inherit; color: var(--txt-3);
  cursor: pointer; text-align: left;
  transition: color var(--t-fast) var(--ease);
}
.footer__soon:hover, .footer__soon:focus-visible { color: var(--txt); }

.soonpop {
  position: fixed; inset: 0; z-index: 210;
  display: grid; place-items: center;
  padding: var(--s4);
  opacity: 0; pointer-events: none;
  transition: opacity 220ms var(--ease-out);
}
.soonpop.is-open { opacity: 1; pointer-events: auto; }
.soonpop__scrim {
  position: absolute; inset: 0;
  background: rgba(2, 4, 8, .62);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.soonpop__card {
  position: relative;
  min-width: min(320px, 100%);
  padding: var(--s5) var(--s5) var(--s4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background: linear-gradient(160deg, rgba(38,38,38,.96), rgba(13,13,13,.96));
  box-shadow: var(--sh-2);
  text-align: center;
  transform: translateY(10px) scale(.96);
  transition: transform 260ms var(--ease-out);
}
.soonpop.is-open .soonpop__card { transform: none; }
.soonpop__k {
  margin: 0 0 6px;
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .17em; text-transform: uppercase;
  color: var(--orange);
}
.soonpop__card .soon__t { margin: 0; justify-content: center; }
.soonpop__x {
  position: absolute; top: 8px; right: 8px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%;
  color: var(--txt-3); cursor: pointer;
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.soonpop__x:hover, .soonpop__x:focus-visible { color: var(--txt); background: var(--ink-700); }
.soonpop__x svg { width: 16px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
  .soonpop, .soonpop__card { transition: none; }
}

/* --------------------------------------------- THE FOOTER, IN A PANEL --- */
/* The same footer, at the end of every section rather than only under the
   landing page. It scrolls up with the content the way a page footer should,
   and sits above the panel's own Next-node bar. Its own top rule is enough of
   a division, so the panel's padding is what is removed rather than added. */
.footer--inpanel {
  margin-top: var(--s5);
  border-top: 1px solid var(--line);
  background: transparent;      /* the panel already has its own ground */
}
.footer--inpanel .footer__inner { padding-block: var(--s5); }
.footer--inpanel .footer__bar   { padding-bottom: var(--s5); }

/* ------------------------------------------------------ THE ENTRY PAGE -- */
/* What a visitor lands on, and the reason the intro's sound can be heard at
   all. It reuses the .brand lockup defined below, so the two cannot drift
   apart - only its size and the page around it are set here. */
.gate {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  padding: var(--s5) var(--s4);
  background: var(--ink-900);
  transition: opacity 620ms var(--ease-out);
}
/* the same washes the stage uses, so what comes next is visibly the same
   world rather than something behind a flat cover */
.gate::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(70% 55% at 50% 42%,
      color-mix(in srgb, var(--orange) 9%, transparent), transparent 70%),
    radial-gradient(90% 70% at 50% 100%,
      color-mix(in srgb, #3355FF 8%, transparent), transparent 70%);
}
.gate.is-out { opacity: 0; pointer-events: none; }
.gate[hidden] { display: none; }

.gate__inner {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s5);
  text-align: center;
  animation: gateIn 900ms var(--ease-out) both;
}
@keyframes gateIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* the lockup, one size up and without the card it wears inside a panel */
.gate .brand {
  max-width: 520px;
  border: 0; background: none; padding: 0;
  gap: var(--s4);
}
/* Matched on WIDTH, not height: 326px against 306px. Matching heights is
   meaningless when one lockup is 2.47:1 and the other is 5.66:1. */
.gate .brand__mark       { height: 132px; }
.gate .brand__parentLogo { height: 54px; }

.gate__go {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 30px;
  font: inherit; font-size: 14px; font-weight: 600; letter-spacing: .04em;
  color: #1A1A1A;
  background: var(--orange);
  border: 0; border-radius: 999px;
  cursor: pointer;
  transition: transform var(--t-mid) var(--ease), box-shadow var(--t-mid) var(--ease);
}
.gate__go:hover, .gate__go:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--orange) 40%, transparent);
}
.gate__go svg { width: 16px; height: 16px; }

/* Sound is the whole point of the button above, but nobody should have a page
   make a noise at them with no way round it. */
.gate__quiet {
  background: none; border: 0; padding: 6px;
  font: inherit; font-size: 12px; letter-spacing: .02em;
  color: var(--txt-3); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.gate__quiet:hover, .gate__quiet:focus-visible { color: var(--txt-2); }

@media (prefers-reduced-motion: reduce) {
  .gate, .gate__inner { transition: none; animation: none; }
}
@media (max-width: 559px) {
  .gate .brand__mark       { height: 96px; }
  .gate .brand__parentLogo { height: 39px; }
  .gate__go { padding: 13px 26px; }
}

/* ------------------------------------------------ THE BRAND LOCKUP ------ */
/* The two marks with the relationship written between them, centred in a band
   of its own. Two rules across the "An initiative by" line, which is what
   stops it reading as a caption for the mark above it and makes it the join
   between the two. */
.blk--brand { display: flex; justify-content: center; }
.brand {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s3);
  width: 100%; max-width: 420px;
  padding: var(--s5) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--ink-700) 45%, transparent);
  text-align: center;
}
/* the Microshala artwork carries its own white ground, so on a dark panel it
   needs the same plate the top bar gives it */
.brand__plate {
  display: block; padding: 8px 14px; border-radius: 10px; background: #FFFFFF;
}
/* 120px, not the 46 a mark would take. This is the whole logo, and its
   strapline is a tenth of its height - at 46px that line renders under 5px and
   is the grey smudge the top bar had to drop. At 120 it lands near 13px and
   reads, in the lettering it was drawn in. */
.brand__mark { display: block; height: 120px; width: auto; }

/* the rules are drawn on the line itself, so they meet the text exactly */
.brand__by {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%;
  font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--txt-3);
}
.brand__by::before, .brand__by::after {
  content: ''; flex: 1 1 0; height: 1px; background: var(--line);
}

.brand__parent { display: block; opacity: .92; transition: opacity var(--t-mid) var(--ease); }
.brand__parent:hover, .brand__parent:focus-visible { opacity: 1; }
/* 49px against the mark's 120. The two lockups are different shapes - 2.47:1
   stacked against 5.66:1 horizontal - so matching their heights would make one
   of them enormous. Matched on WIDTH instead, which is what "the same size"
   looks like to the eye: 296px against 297px. */
.brand__parentLogo { display: block; height: 49px; width: auto; }
/* only ever one of the two, exactly as in the top bar */
.brand__parentLogo--light { display: none; }
[data-theme="light"] .brand__parentLogo--dark  { display: none; }
[data-theme="light"] .brand__parentLogo--light { display: block; }
/* the artwork already sits on white here, so the plate is redundant */
[data-theme="light"] .brand__plate { background: transparent; padding: 8px 0; }

/* --------------------------------------------- THE WORKING FIGURE ------- */
/* A speech bubble saying what the job is, parked on whichever side of the
   figure is clear. site.js sets --tailx so the tail keeps pointing back at
   the head wherever the bubble ends up, and adds .is-below when the bubble
   sits under the figure rather than over it. */
.figcloud {
  position: absolute;
  z-index: 5;
  display: flex; align-items: center; gap: 7px;
  width: max-content;
  /* wide enough that the longer job titles keep to one line: the band above
     the figure is short, so height is the scarce dimension, not width */
  max-width: 290px;
  padding: 7px 11px 7px 12px;
  border: 1px solid var(--orange-45);
  border-radius: 13px;
  background: rgba(12, 12, 12, .9);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: var(--sh-2);
  cursor: help;
  opacity: 0;
  transform: translateY(5px);
  /* 220ms, not the 620ms this used to have. That was an entrance for
     something arriving once at the end of the intro; this is a hover, and a
     620ms hover reads as lag. */
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out),
              border-color var(--t-fast) var(--ease-out),
              background-color var(--t-fast) var(--ease-out);
}
.figcloud[hidden] { display: none; }
/* Was `body.is-done .figcloud`, which left it parked on the stage for the
   whole visit. site.js now adds .is-in only while a figure is hovered. */
.figcloud.is-in { opacity: 1; transform: none; }

/* the other two shapes site.js can choose: the same words on two or three
   lines, narrow enough to drop into the channel between two blocks */
.figcloud.is-narrow { max-width: 180px; }
.figcloud.is-slim   { max-width: 148px; }

.figcloud:hover,
.figcloud:focus-visible { border-color: var(--orange); background: rgba(20, 15, 6, .94); }

.figcloud__txt {
  font-size: 11.5px; font-weight: 560; line-height: 1.3;
  letter-spacing: -.005em;
  color: var(--txt);
  text-align: left;
  /* the standing "there is more here" cue, without a tooltip icon */
  text-decoration: underline dotted var(--orange-45);
  text-underline-offset: 3px;
}
.figcloud:hover .figcloud__txt,
.figcloud:focus-visible .figcloud__txt { text-decoration-color: var(--orange); }

.figcloud__dot {
  flex: 0 0 auto; position: relative;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--orange);
  box-shadow: 0 0 9px 1px rgba(255, 165, 0, .75);
}
.figcloud__dot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  animation: ping 2.8s var(--ease-out) infinite;
}

/* a rotated square showing two of its sides, so it reads as a tail rather
   than a box corner */
.figcloud__tail {
  position: absolute;
  left: var(--tailx, 18px);
  bottom: -5px;
  width: 9px; height: 9px;
  margin-left: -4.5px;
  border-right: 1px solid var(--orange-45);
  border-bottom: 1px solid var(--orange-45);
  background: rgba(12, 12, 12, .9);
  transform: rotate(45deg);
}
.figcloud.is-below .figcloud__tail {
  bottom: auto; top: -5px;
  border: 0;
  border-left: 1px solid var(--orange-45);
  border-top: 1px solid var(--orange-45);
}
.figcloud:hover .figcloud__tail,
.figcloud:focus-visible .figcloud__tail { border-color: var(--orange); background: rgba(20, 15, 6, .94); }

/* The hover targets over the figures. Invisible by design, but a real button
   so the keyboard and the screen reader get the same content the pointer does.
   `cursor: help` is the only thing that hints there is anything to hover at
   all, now that the bubble is not sitting there advertising itself. */
.fighit {
  position: absolute;
  /* 3, deliberately: .nodes is 4 and these buttons come after it in the
     document, so at 4 they would paint on top and quietly eat clicks
     meant for a node wherever the two overlap. */
  z-index: 3;
  background: none;
  border: 0;
  padding: 0;
  border-radius: 14px;
  cursor: help;
}
.fighit[hidden] { display: none; }
.fighit:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

.figcloud__sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The explanation, opened by hovering the bubble. Small on purpose: it floats
   over the landing page, and one that covers a block is worse than none. */
.figtip {
  position: absolute;
  /* above .ctl (20) so Replay never clips the text, below .panel (40) */
  z-index: 25;
  width: max-content;
  max-width: 228px;
  padding: 9px 11px;
  border: 1px solid var(--orange-45);
  border-radius: var(--r-md);
  background: rgba(10, 10, 10, .94);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: var(--sh-3);
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--t-mid) var(--ease-out), transform var(--t-mid) var(--ease-out);
}
.figtip[hidden] { display: none; }
.figtip.is-in { opacity: 1; transform: none; }

/* the phone treatment: a sheet across the foot of the stage rather than a
   floating card, because on a phone there is nowhere for it to float */
.figtip.is-sheet {
  left: 10px; right: 10px; top: auto; bottom: 10px;
  width: auto; max-width: none;
  transform: translateY(8px);
}
.figtip.is-sheet.is-in { transform: none; }

.figtip__b {
  font-size: 11.5px; line-height: 1.45;
  color: var(--txt-2);
}

[data-theme="light"] .figtip   { background: rgba(255, 255, 255, .96); }
[data-theme="light"] .figcloud { background: rgba(255, 255, 255, .95); }
[data-theme="light"] .figcloud__tail { background: rgba(255, 255, 255, .95); }
[data-theme="light"] .figcloud:hover,
[data-theme="light"] .figcloud:focus-visible { background: #FFFFFF; }
[data-theme="light"] .figcloud:hover .figcloud__tail,
[data-theme="light"] .figcloud:focus-visible .figcloud__tail { background: #FFFFFF; }

.stage__hint {
  position: absolute;
  left: 50%; bottom: 16px;
  transform: translateX(-50%);
  z-index: 4;
  display: flex; align-items: center; gap: 9px;
  font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--txt-3);
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
  pointer-events: none;
}
.stage__hint.is-in { opacity: 1; }

/* steady glow on the dot itself, expanding ring on the pseudo-element - one
   box-shadow cannot do both, the animation would overwrite the glow */
.stage__hintDot {
  position: relative;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--orange);
  box-shadow: 0 0 9px 1px rgba(255, 165, 0, .75);
}
.stage__hintDot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  animation: ping 2.4s var(--ease-out) infinite;
}
@keyframes ping {
  0%   { box-shadow: 0 0 0 0 rgba(255, 165, 0, .55); }
  70%  { box-shadow: 0 0 0 10px rgba(255, 165, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 165, 0, 0); }
}
/* in grid mode the cards run to the floor - no clear space for the hint */
.stage.is-grid .stage__hint { display: none; }

/* --------------------------------------------------------------- FOOTER -- */
.footer {
  position: relative;
  z-index: 6;
  flex: 0 0 auto;
  border-top: 1px solid transparent;
  background: var(--ink-850);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 760ms var(--ease-out), transform 760ms var(--ease-out),
              border-color 760ms var(--ease-out);
}
.footer.is-in { opacity: 1; transform: none; border-top-color: var(--line); }

.footer__inner {
  display: grid;
  grid-template-columns: 1.5fr 1.35fr 1.1fr .7fr;
  gap: var(--s5);
  /* symmetric, and the same 24px total the asymmetric 14/10 came to, so the
     footer's height is unchanged - it is what the stage measures itself
     against, and the radial layout has been broken by a taller footer before */
  padding-block: var(--s3);
  padding-inline: var(--edge);
}

.footer__mark { font-size: clamp(13px, 0.31vw + 9px, 15px); font-weight: 680; letter-spacing: .2em; color: var(--txt); }
.footer__tag { margin-top: 6px; font-size: 12px; line-height: 1.5; color: var(--txt-3); max-width: 40ch; }

/* --- the parent mark, in the footer ------------------------------------- */
/* Sized on width, not height: the Microscan wordmark is 5.66:1 and the
   Microshala one 2.47:1, so matching heights would leave one looking twice
   the size of the other. */
.footer__by {
  margin: 20px 0 8px;
  font-size: clamp(10px, 0.23vw + 7px, 11.5px);
  font-weight: 620; letter-spacing: .14em; text-transform: uppercase;
  color: var(--txt-3);
}
.footer__parent { display: inline-block; line-height: 0; }
.footer__parent[hidden] { display: none; }
.footer__parentLogo { display: block; width: 132px; height: auto;
                      transition: opacity var(--t-mid) var(--ease); }
/* One of the pair at a time. The wordmark is a single flat colour, so the black
   one disappears on the dark footer and the white one on the light footer. */
.footer__parentLogo--light { display: none; }
[data-theme="light"] .footer__parentLogo--dark  { display: none; }
[data-theme="light"] .footer__parentLogo--light { display: block; }
.footer__parent:hover .footer__parentLogo,
.footer__parent:focus-visible .footer__parentLogo { opacity: .82; }

.footer__head {
  font-size: clamp(11.5px, 0.23vw + 8.5px, 13px); font-weight: 620;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--txt-3);
  margin-bottom: 7px;
}
.footer__list { display: grid; gap: 5px; font-size: clamp(13px, 0.31vw + 9px, 15px); color: var(--txt-2); }
.footer__list--row { grid-auto-flow: column; justify-content: start; gap: var(--s4); }
/* two columns, so five links are three rows rather than five - this selector
   said #footerLinks long after the element was renamed, which is why the
   footer had grown tall enough to push the page out of the radial layout */
/* One column. It was two when the list held five items; with three it split
   them across a gap and read as two unrelated groups. Class, not id, because
   the footer that follows you into a panel is a clone with its ids stripped -
   an id rule would style the landing page's footer and not that one, and the
   two would quietly disagree. */
.footer__list { gap: 5px; }
.footer__list a, .footer__list button {
  color: var(--txt-2); text-align: left;
  transition: color var(--t-fast) var(--ease);
}
.footer__list a:hover, .footer__list button:hover { color: var(--orange); }
.footer__muted { color: var(--txt-3); }

/* a heading that is not a page yet. Visible, so people know it is coming,
   but plainly not clickable. */
.footer__soon { color: var(--txt-3); cursor: default; }

/* --- WhatsApp ---------------------------------------------------------------
   The number on this site is for WhatsApp only. The mark sits with it wherever
   it appears so it is never mistaken for something to dial. */
.wa__icon {
  width: 1em; height: 1em; flex: 0 0 auto;
  fill: currentColor; stroke: none;
  vertical-align: -.13em;
}
.footer__wa { display: inline-flex; align-items: center; gap: 7px; }
.enq__wa { display: inline-flex; align-items: center; gap: 9px; }
.enq__wa .wa__icon { width: .92em; height: .92em; }

.footer__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4);
  padding-block: var(--s2);
  padding-inline: var(--edge);
  border-top: 1px solid var(--line);
  font-size: 11.5px;
  color: var(--txt-3);
}
.footer__bar-right { display: flex; align-items: center; gap: 10px; }
.footer__bar a { transition: color var(--t-fast) var(--ease); }
.footer__bar a:hover { color: var(--txt-2); }

/* ------------------------------------------------------------- CONTROLS -- */
.ctl {
  position: absolute;
  z-index: 20;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 15px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: rgba(15,15,15,.82);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  font-size: 12.5px; font-weight: 500;
  color: var(--txt-2);
  box-shadow: var(--sh-2);
  transition: opacity var(--t-mid) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.ctl:hover { color: var(--txt); border-color: var(--orange-45); transform: translateY(-1px); }
.ctl[hidden] { display: none; }
.ctl svg { width: 14px; height: 14px; }

/* JS sets --ctl-bottom from the real grid bottom. When the grid reaches the
   floor there is no strip left, so it sets --ctl-top instead and these move to
   the top-right corner rather than sitting on top of a card. */
.ctl--skip   { right: var(--s5); bottom: var(--ctl-bottom, var(--s5)); top: var(--ctl-top, auto); }
.ctl--replay { right: var(--s5); bottom: var(--ctl-bottom, var(--s5)); top: var(--ctl-top, auto); }

.ctl kbd {
  font-family: var(--mono); font-size: 9.5px;
  padding: 2px 5px; border-radius: 5px;
  border: 1px solid var(--line-2);
  color: var(--txt-3);
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- PANEL -- */
/* Unfolds full-screen from the node that was clicked, so the content gets the
   whole width - the old centred sheet was portrait-shaped and cramped. */
/* fixed, not absolute: the page itself can scroll now, and the panel has to
   cover the viewport wherever that scroll happens to be */
/* Inset from the top by exactly the bar's height, so the sticky top bar above
   stays visible and usable over an open section. Everything inside, the scrim
   included, is absolute to this box, which is why the bar never dims. */
.panel { position: fixed; top: var(--topbar-h); left: 0; right: 0; bottom: 0; z-index: 40; }
.panel[hidden] { display: none; }

.panel__scrim {
  position: absolute; inset: 0;
  background: rgba(2,4,9,.6);
  opacity: 0;
  transition: opacity var(--t-slow) var(--ease-out);
}
.panel.is-open .panel__scrim { opacity: 1; }

.panel__sheet {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, #101010 0%, #050505 60%);
  overflow: hidden;
  transform-origin: var(--ox, 50%) var(--oy, 50%);
  transform: scale(.16);
  opacity: 0;
  transition: transform 520ms var(--ease-out), opacity 300ms var(--ease-out);
}
.panel.is-open .panel__sheet { transform: scale(1); opacity: 1; }

/* the strand colour of the node it came out of */
.panel__thread {
  position: absolute; left: 0; right: 0; top: 0; height: 4px;
  background: var(--a);
  z-index: 2;
}

.panel__head {
  flex: 0 0 auto;
  position: relative;
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4);
  padding-block: clamp(20px, 3vw, 48px) var(--s5);
  padding-inline: var(--edge);
  border-bottom: 1px solid var(--line);
  background: radial-gradient(70% 160% at 0% 0%,
              color-mix(in srgb, var(--orange) 14%, transparent), transparent 62%);
}
.panel__headInner { max-width: 74ch; }

/* --- the hero hugs its content ---------------------------------------------
   A floor was tried here, set to the tallest natural hero at each width, so
   that switching nodes did not shift the content below. It cost too much: on
   the shortest hero it left 77px of empty space between the tagline and the
   rule under it, which reads as a mistake on the node you are looking at,
   where the height difference only reads when you flip between two.

   So the RULES are uniform and the height is not: every hero starts at the
   same place (eyebrow at 43px, title at 72px at this width) and closes the
   same distance under its last line. Length decides the rest. */

.panel__eyebrow {
  display: flex; align-items: center; gap: 10px;
  font-size: 10.5px; font-weight: 620;
  letter-spacing: .17em; text-transform: uppercase;
  color: var(--orange);
}
.panel__icon { width: 17px; height: 17px; display: block; }
.panel__icon svg { width: 100%; height: 100%; fill: none; stroke: currentColor;
                   stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.panel__title {
  margin-top: 12px;
  font-size: clamp(28px, 2.5vw + 12px, 56px);
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1.08;
  color: var(--txt);
}
/* A panel whose opening line lives in the tagline above leaves this empty.
   Without this it would still contribute its top margin as a stray gap. */
.panel__lede:empty { display: none; }
.panel__lede { margin-top: 12px; font-size: clamp(16px, 0.63vw + 8px, 22px); line-height: 1.55; color: var(--txt-2); }

/* Top right of every panel. A cross would have said "close"; this says where
   you land, which is the whole point of it replacing the footer's back button. */
/* Superseded by Home in the top bar, which is now always on screen while a
   section is open. Kept in the markup because site.js still holds a reference
   to it as its focus fallback, and kept styled in case it is ever shown again. */
.panel__home {
  flex: 0 0 auto;
  display: none; align-items: center; gap: 8px;
  padding: 10px 18px 10px 15px;
  border-radius: 999px;
  border: 1px solid var(--line-2);
  font-size: 13.5px; font-weight: 580;
  color: var(--txt-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.panel__home:hover {
  background: var(--orange-08);
  border-color: var(--orange-45);
  color: var(--orange-ink);
  transform: translateY(-1px);
}
.panel__home svg { width: 17px; height: 17px; flex: 0 0 auto; }

.panel__scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}
.panel__scroll::-webkit-scrollbar { width: 10px; }
.panel__scroll::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: 99px;
  border: 3px solid transparent; background-clip: content-box;
}

/* Landscape: content flows in columns across the full width instead of one
   tall narrow strip. Wide blocks span every column. */
/* Bands, not `column-span: all`. That property duplicated the block after a
   spanning element on screen, so full-width content gets its own band and the
   narrow blocks flow in multi-column bands between them. */
.panel__body {
  display: grid;
  gap: var(--s6);
  padding-block: var(--s6) var(--s8);
  padding-inline: var(--edge);
}
/* one column of prose per ~46 characters of comfortable measure: three on a
   wide monitor, two on a laptop, one on anything narrow */
.band { column-count: 2; column-gap: var(--s7); }
.band--full { column-count: 1; }
/* :not(.band--full) matters. These two rules are single-class selectors, the
   same specificity as .band--full above, and they come later in the file, so
   an unscoped `.band` here BEATS .band--full. That is what happened: above
   1560px every full-width band split into three columns and its content
   collapsed into the left third of the panel, which is why wide monitors
   showed a narrow strip of text and a lot of empty space. */
@media (max-width: 900px)  { .band:not(.band--full) { column-count: 1; } }
@media (min-width: 1560px) { .band:not(.band--full) { column-count: 3; } }

/* long prose and forms read badly in columns */
/* margin-inline, not just max-width. The body used to carry `margin: 0 auto`
   for its 1560px cap; that moved to a padding-based shell inset, and this
   narrower reading column was left relying on a centring rule that no longer
   existed - so Get in Touch, Privacy and Terms sat flush left with the rest of
   the panel empty beside them. */
.panel__sheet--read .panel__body { max-width: 82ch; margin-inline: auto; }
.panel__sheet--read .band { column-count: 1; }
/* The body is a grid, and its single track was sizing to the widest thing in
   it rather than to the panel. Prose never showed it, because a paragraph's
   natural width is larger than the cap. The form did: on a 1920 screen it sat
   in 466px of an 826px body, and its paired fields collapsed to 57px each.
   An explicit filling column holds the track to the panel instead. Scoped to
   the three panels that read in one column: Get in Touch, Privacy and Terms. */
.panel__sheet--read .panel__body { grid-template-columns: minmax(0, 1fr); }
/* --edge is the shell inset: a gutter, OR half the space left over past 1560px,
   whichever is larger. That is right for a body that runs the full width and
   has to centre itself. It is wrong here, where max-width has already centred
   the body at a reading measure: the inset then grows with the screen while the
   cap does not, so the panel eats itself. At 1920 it took 180px a side and left
   the form 466px. At 2560 it took 500px a side and left it NOTHING, so Get in
   Touch, Privacy and Terms were blank on a large monitor. Just the gutter. */
.panel__sheet--read .panel__body { padding-inline: clamp(16px, 3.4vw, 48px); }

/* --- the panel scenes -----------------------------------------------------
   The two figures off the landing page turn up once inside a panel, on a
   floor, doing the thing that section is about. A beat between two blocks of
   text rather than a mascot parked in the margin. */
/* Layered, not laid out. Giving the scene a column of its own narrowed every
   band and the whole page read as having shifted left, so it is taken out of
   the flow and hung at the top right instead. The text keeps exactly the
   width and the position it had before there was a scene at all. The opening
   band reserves the height under it, which buys vertical room and moves
   nothing sideways. */
.panel__body.has-scene { position: relative; }

@media (min-width: 1100px) {
  /* A grid item in the first row rather than an absolutely positioned one.
     Pinned to the top it sat level with the first LINE of the opening text and
     ran on below it; sharing the row lets align-self centre it against
     whatever that text turns out to be, at any length, with no number to keep
     in step. justify-self reaches the content edge on its own, so the shell
     inset no longer has to be repeated here. It comes after the first band in
     the DOM, so it paints over the empty second column, and pointer-events
     keeps it from taking any clicks. */
  .panel__body.has-scene > .figscene {
    grid-row: 1; grid-column: 1;
    justify-self: end;
    align-self: center;
    pointer-events: none;
  }
  /* every scene is 92 units tall drawn at two pixels to the unit */
  /* Explicitly in row 1 as well. Auto-placement skips a cell an explicitly
     placed item already holds, so without this the scene took the row to
     itself and pushed the opening text down below it instead of beside it. */
  .panel__body.has-scene > .band:first-child {
    grid-row: 1; grid-column: 1;
    min-height: 196px;
  }

  /* Where the top right is already occupied it takes a row of its own at the
     top instead, right aligned. Being the first child is also what turns the
     reserved height above off, since no band is :first-child any more. */
  .panel__body.has-scene.scene-inflow > .figscene {
    position: static;
    justify-self: end;
  }
}
/* below this there is no width to spare, so it goes back into the flow and
   sits under the opening text at full width, where it is bigger anyway */
/* --- laptops and small desktops: tighten the bar before anything is lost --- */
@media (max-width: 1180px) {
  .topbar__nav { gap: 6px; }
  .menu__btn, .topbar__link { padding: 8px 10px; font-size: 13px; }
}

@media (max-width: 1099px) {
  .panel__body.has-scene > .figscene { position: static; }
}

/* Block, not flex. Out of the flow a flex container has no height of its own
   to give the drawing, so the whole box collapsed to a couple of dozen pixels
   and the scene rendered at a fraction of its size. The svg carries its own
   pixel width, set per scene, and works its height out from the viewBox. */
.figscene { display: block; }
.figscene__svg {
  display: block; height: auto;
  overflow: visible;
}


/* the floor, and whatever kit the scene needs standing on it */
.sc-ground { stroke: #FFFFFF; stroke-width: 1.4; opacity: .16; }
.sc-kit {
  fill: none; stroke: #FFFFFF; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.sc-kit--fill { fill: rgba(255, 255, 255, .06); }
.sc-line { stroke: #FFFFFF; stroke-width: 1.5; stroke-linecap: round; opacity: .45; }
.sc-bub  { fill: rgba(255, 255, 255, .07); stroke: #FFFFFF; stroke-width: 1.5; }

/* anything the scene lights up takes the panel's own accent */
.sc-lit        { fill: var(--a); stroke: none; }
.sc-lit-stroke { fill: none; stroke: var(--a); stroke-width: 2.2; stroke-linecap: round; }
.sc-flash      { fill: none; stroke: var(--a); stroke-width: 2; stroke-linecap: round; }
.sc-glow       { fill: var(--a); opacity: 0; }

[data-theme="light"] .sc-ground { stroke: var(--black); opacity: .2; }
[data-theme="light"] .sc-kit    { stroke: var(--black); }
[data-theme="light"] .sc-kit--fill { fill: rgba(26, 26, 26, .05); }
[data-theme="light"] .sc-line   { stroke: var(--black); opacity: .3; }
[data-theme="light"] .sc-bub    { fill: rgba(26, 26, 26, .04); stroke: var(--black); }

.blk { break-inside: avoid; margin: 0 0 var(--s6); }
.band .blk:last-child { margin-bottom: 0; }

.blk__head {
  font-size: 12px; font-weight: 660;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--orange);
  margin-bottom: var(--s3);
}
.blk__p { font-size: clamp(16px, 0.47vw + 10px, 19px); line-height: 1.75; color: var(--txt-2); }
.blk__p + .blk__p { margin-top: var(--s3); }
/* An opening line set in the same accent as .blk__head above, at the same
   weight, so the two read as a pair. Written ==like this== in content.js. */
.blk__em { color: var(--orange); font-weight: 660; }

/* Inline link inside panel prose. The global `a` rule above strips colour and
   underline from every anchor, so an inline link has to put both back or it
   reads as plain text with nothing to say it can be clicked. */
.inlink {
  color: var(--orange);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  cursor: pointer;
}
.inlink:hover { text-decoration-thickness: 2px; }

/* Every highlighted phrase in the panels reads in the accent. Bold appears in
   three places - list items, button copy and prose - and all three sit inside
   the panel body, so one scoped rule covers the lot. */
.panel__body strong { color: var(--orange); }
[data-theme="light"] .panel__body strong { color: var(--orange-ink); }

/* Sections sitting under an open heading. Stepped in by the same amount a
   nested fold uses, so an umbrella reads as one, given every heading on the
   site is the same size. */
.blk__nest { margin-left: var(--s4); }
.blk__nest > .blk:last-child { margin-bottom: 0; }

/* A nest holding fold ROWS is a set of things that belong to the heading above
   it, not a note attached to one. The rule down the left says so: the rows are
   the contents of that section rather than three more sections after it. Only
   where the nest actually contains folds, so a nest holding a plain list is
   left as the quiet indent it already was. */
.blk__nest:has(> .blk > .fold) {
  margin-left: 0;
  padding-left: var(--s5);
  border-left: 1px solid var(--line-2);
}
/* the run already closes up its own row borders; the container carries the
   grouping, so the last row does not need a line under it as well */
.blk__nest:has(> .blk > .fold) > .blk:last-child > .fold { border-bottom: 0; }

/* --- "coming soon" -------------------------------------------------------- */
/* The line, three dots keeping time after it, and a slow breath of the panel's
   accent behind. No diagram: a placeholder is a status, not a thing to draw. */
.blk--soon {
  position: relative;
  text-align: center;
  padding: var(--s6) 0 var(--s5);
}
.blk--soon::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(44% 62% at 50% 44%, var(--a, var(--orange)), transparent 70%);
  opacity: .07;
  animation: soonBreathe 5.4s var(--ease) infinite;
  pointer-events: none;
}
.soon__t {
  position: relative;
  display: inline-flex; align-items: center; gap: 11px;
  font-size: 22px; font-weight: 660; letter-spacing: -.01em;
  color: var(--orange);
  opacity: 0;
  animation: soonIn 520ms var(--ease-out) 120ms forwards;
}
/* the one thing here that keeps going, because "soon" has not arrived yet */
.soon__dots { display: inline-flex; align-items: center; gap: 6px; }
.soon__dots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .22;
  animation: soonBlink 1.5s ease-in-out infinite;
}
.soon__dots i:nth-child(2) { animation-delay: .22s; }
.soon__dots i:nth-child(3) { animation-delay: .44s; }

.soon__b {
  position: relative;
  font-size: clamp(16px, 0.47vw + 10px, 19px); line-height: 1.75; color: var(--txt-2);
  max-width: 44ch; margin: var(--s3) auto 0;
  opacity: 0;
  animation: soonIn 520ms var(--ease-out) 280ms forwards;
}
[data-theme="light"] .soon__t { color: var(--orange-ink); }
[data-theme="light"] .blk--soon::before { opacity: .10; }

@keyframes soonBlink   { 0%, 70%, 100% { opacity: .22; transform: translateY(0); }
                         35%           { opacity: 1;   transform: translateY(-3px); } }
@keyframes soonBreathe { 0%, 100% { opacity: .06; } 50% { opacity: .15; } }
@keyframes soonIn      { from { opacity: 0; transform: translateY(7px); }
                         to   { opacity: 1; transform: none; } }

/* --- folded headings ------------------------------------------------------ */
/* Every heading in a panel is a door. The rule is the same everywhere, right
   down inside the expanding cards and the legal clauses, so a panel opens as a
   short list of what it covers rather than a wall of prose. */
.fold { border-top: 1px solid var(--line); }

/* Each folded heading is its own band, and .panel__body is a grid whose gap
   separates them. Left alone a run of folds is a set of rules floating in air
   rather than a list you can read down, so consecutive ones pull that gap back
   and stack. Only the top rule is drawn: a bottom rule plus the next one's top
   rule is two lines with a gap between, which reads as a mistake. */
.band--fold > .blk { margin-bottom: 0; }
.band--fold + .band--fold { margin-top: calc(var(--s6) * -1); }
/* the run needs a rule to close on */
.band--fold:not(:has(+ .band--fold)) > .blk > .fold { border-bottom: 1px solid var(--line); }

/* Inside an expanding card, and inside another fold, the blocks are plain
   siblings rather than bands, so the same run closes up on the block margin
   instead of the grid gap. Same result, different lever. */
.xcard__body .blk:has(> .fold),
.fold__body .blk:has(> .fold) { margin-bottom: 0; }
.xcard__body .blk:has(> .fold) + .blk:not(:has(> .fold)),
.fold__body .blk:has(> .fold) + .blk:not(:has(> .fold)) { margin-top: var(--s6); }
.xcard__body .blk:has(> .fold):not(:has(+ .blk > .fold)) > .fold,
.fold__body .blk:has(> .fold):not(:has(+ .blk > .fold)) > .fold {
  border-bottom: 1px solid var(--line);
}

.fold__sum {
  list-style: none;
  cursor: pointer;
  display: flex; align-items: center; gap: var(--s4);
  padding: 13px 0;
  transition: color var(--t-mid) var(--ease);
}
.fold__sum::-webkit-details-marker { display: none; }
.fold__sum:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; border-radius: 4px; }

.fold__h {
  flex: 1 1 auto;
  font-size: 12px; font-weight: 660;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--orange);
}

/* the same plus-into-cross as the cards use, one size down */
.fold__tog {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line-2);
  color: var(--orange);
  transition: transform 320ms var(--ease-out), background var(--t-mid) var(--ease),
              border-color var(--t-mid) var(--ease);
}
.fold__tog svg { width: 13px; height: 13px; fill: none; stroke: currentColor;
                 stroke-width: 2.4; stroke-linecap: round; }
.fold__sum:hover .fold__tog { border-color: color-mix(in srgb, var(--orange) 55%, transparent); }
.fold[open] .fold__tog {
  transform: rotate(135deg);
  background: color-mix(in srgb, var(--orange) 15%, transparent);
  border-color: color-mix(in srgb, var(--orange) 55%, transparent);
}

.fold__body { padding: 2px 0 var(--s5); }
/* Glides open, the way an expand-card already does. The two were the same
   interaction behaving differently: cards eased, headings snapped. */
.fold[open] > .fold__body { animation: foldOpen 300ms var(--ease-out); }
@keyframes foldOpen { from { opacity: 0; transform: translateY(-6px); }
                      to   { opacity: 1; transform: none; } }
.fold__body > .blk__p:first-child,
.fold__body > .cards,
.fold__body > .blk__list { margin-top: 0; }

/* A heading inside an opened heading. Stepped down and indented behind a rule,
   so a sub-heading reads as belonging to the one above it rather than as
   another item of the same rank. */
.fold__body .fold {
  margin-left: var(--s4);
  border-top-color: var(--line-2);
}
.fold__body .fold .fold__h {
  font-size: 12.5px; font-weight: 640;
  letter-spacing: 0; text-transform: none;
  color: var(--txt-2);
}
.fold__body .fold[open] .fold__h { color: var(--orange); }
.fold__body .fold .fold__tog { width: 22px; height: 22px; }
.fold__body .fold .fold__tog svg { width: 11px; height: 11px; }
.fold__body .fold .fold__body { padding-bottom: var(--s4); }

/* a legal clause is a heading in sentence case, not a label */

.blk__list { display: grid; gap: 11px; }
.blk__list li { display: flex; align-items: flex-start; gap: 12px;
                font-size: 15px; line-height: 1.6; color: var(--txt-2); }
.blk__bullet {
  flex: 0 0 auto; width: 6px; height: 6px; margin-top: 9px;
  border-radius: 50%; background: var(--orange);
  box-shadow: 0 0 8px color-mix(in srgb, var(--orange) 70%, transparent);
}

.blk--note {
  padding: var(--s5);
  border: 1px solid var(--orange-45);
  border-radius: var(--r-md);
  background: var(--orange-08);
}
.blk--note p { font-size: clamp(15px, 0.39vw + 10px, 17.5px); line-height: 1.65; color: var(--txt); }

/* --- stats --------------------------------------------------------------- */
/* min(100%, …) rather than a bare minimum, so a track can never be wider than
   the column it sits in and push the panel into a sideways scroll */
.blk__stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: var(--s4); }
/* A row left with one figure. auto-fit stretches a lone track to the full
   panel width, which puts a small number in the corner of a very wide box and
   reads as a mistake rather than a statistic. Capping the tile keeps it at the
   size it has when it has siblings; the track still spans the row, so nothing
   else in the panel moves. */
.blk__stats .stat:only-child { max-width: 340px; }
.stat {
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: rgba(255,255,255,.04);
  transition: border-color var(--t-mid) var(--ease), background var(--t-mid) var(--ease);
}
/* Brightens rather than lifting. A tile is not a button, and the 3px rise the
   cards use would offer a click that is not there. */
.stat:hover {
  border-color: var(--line-2);
  background: rgba(255,255,255,.07);
}
/* Tiles arrive once, staggered, when their row is first scrolled to. site.js
   puts .is-in on the row and counts the numbers at the same moment. */
.blk__stats.is-in .stat {
  animation: statIn 460ms var(--ease-out) forwards;
  animation-delay: calc(var(--i) * 70ms);
}
/* Only a row site.js has taken charge of starts hidden. Without the script, or
   with reduced motion, the tiles are simply there. */
@media (prefers-reduced-motion: no-preference) {
  .blk__stats.is-armed .stat { opacity: 0; }
}
@keyframes statIn { from { opacity: 0; transform: translateY(10px); }
                    to   { opacity: 1; transform: none; } }

/* tabular figures, so a counting number does not jiggle as its digits change */
.stat__v { font-size: clamp(26px, 1.6vw + 13px, 42px); font-weight: 720; letter-spacing: -.02em;
           color: var(--orange); line-height: 1.05;
           font-variant-numeric: tabular-nums; }
.stat__l { margin-top: 7px; font-size: 12.5px; line-height: 1.45; color: var(--txt-3); }

/* --- cards --------------------------------------------------------------- */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); gap: var(--s4); }
.card {
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(165deg, rgba(38,38,38,.7), rgba(13,13,13,.7));
  transition: border-color var(--t-mid) var(--ease), transform var(--t-mid) var(--ease);
}
.card:hover { border-color: color-mix(in srgb, var(--orange) 45%, transparent); transform: translateY(-3px); }
/* A card that is the way in to another section. It is a real button, so it
   answers to the keyboard, and it says "Open" rather than leaving people to
   guess that this one is clickable and the one beside it is not. */
.card--go {
  display: flex; flex-direction: column; align-items: flex-start;
  text-align: left;
  width: 100%;
  cursor: pointer;
  color: inherit;
}
.card--go:hover, .card--go:focus-visible {
  border-color: color-mix(in srgb, var(--orange) 55%, transparent);
  box-shadow: 0 16px 40px -22px var(--orange);
}
.card__go {
  margin-top: auto;
  padding-top: var(--s4);
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 560;
  color: var(--orange);
  transition: gap var(--t-mid) var(--ease);
}
.card--go:hover .card__go, .card--go:focus-visible .card__go { gap: 11px; }
.card__go svg { width: 14px; height: 14px; fill: none; stroke: currentColor;
                stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.card__t { font-size: 17px; font-weight: 650; color: var(--txt); letter-spacing: -.01em; }
.card__b { margin-top: 9px; font-size: 14.5px; line-height: 1.65; color: var(--txt-2); }
.card__pts { margin-top: var(--s4); display: grid; gap: 8px; }
.card__pts li {
  position: relative; padding-left: 18px;
  font-size: 13.5px; line-height: 1.5; color: var(--txt-3);
}
.card__pts li::before {
  content: ''; position: absolute; left: 0; top: 8px;
  width: 8px; height: 1.5px; border-radius: 2px; background: var(--orange);
}

/* --- expandable cards ----------------------------------------------------- */
/* Built on <details>, so each card holds its own detail instead of scattering
   it down the page, and it still opens from the keyboard without JS. */
/* Collapsed cards sit side by side so a six-card node fits on one screen;
   the one you open takes the full width to read in. */
.xcards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
  gap: var(--s4);
}
.xcard[open] { grid-column: 1 / -1; }

/* One card per row, for a set meant to be read down the page rather than
   scanned across it. Only applies where content.js asks for it. */
.xcards--stack { grid-template-columns: 1fr; }

.xcard {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(165deg, rgba(38,38,38,.7), rgba(13,13,13,.7));
  overflow: hidden;
  transition: border-color var(--t-mid) var(--ease), box-shadow var(--t-mid) var(--ease);
}
.xcard:hover { border-color: var(--line-2); }
.xcard[open] {
  border-color: color-mix(in srgb, var(--orange) 50%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--orange) 20%, transparent),
              0 22px 54px -28px var(--orange);
}

.xcard__sum {
  list-style: none;
  cursor: pointer;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px var(--s5);
  padding: var(--s5);
}
.xcard__sum::-webkit-details-marker { display: none; }
.xcard__sum:focus-visible { outline: 2px solid var(--orange); outline-offset: -4px; }

.xcard__t {
  grid-column: 1;
  font-size: clamp(18px, 1.1vw + 8px, 27px); font-weight: 660;
  letter-spacing: -.015em; color: var(--txt);
}
.xcard__lede { grid-column: 1; font-size: 14.5px; line-height: 1.6; color: var(--txt-2); }

/* the plus rotates into a cross, and lights up in the node's strand colour */
.xcard__tog {
  grid-column: 2; grid-row: 1 / span 2;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line-2);
  color: var(--orange);
  transition: transform 320ms var(--ease-out), background var(--t-mid) var(--ease),
              border-color var(--t-mid) var(--ease);
}
.xcard__tog svg { width: 19px; height: 19px; fill: none; stroke: currentColor;
                  stroke-width: 2.2; stroke-linecap: round; }
.xcard__sum:hover .xcard__tog { border-color: color-mix(in srgb, var(--orange) 55%, transparent); }
.xcard[open] .xcard__tog {
  transform: rotate(135deg);
  background: color-mix(in srgb, var(--orange) 15%, transparent);
  border-color: color-mix(in srgb, var(--orange) 55%, transparent);
}

.xcard__body { position: relative; padding: var(--s2) var(--s5) var(--s5); }
/* a lit strand runs across the top of the revealed content */
.xcard__body::before {
  content: '';
  position: absolute; left: var(--s5); right: var(--s5); top: 0; height: 1px;
  background: linear-gradient(90deg, var(--orange), transparent 70%);
}
.xcard[open] .xcard__body { animation: xopen 340ms var(--ease-out); }
@keyframes xopen {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
.xcard__body .blk:last-child { margin-bottom: 0; }

/* Section headings inside an expandable card carry more weight than the ones
   in open prose: they are the only structure a long card has, and at the shared
   12px they sat below the body text they were meant to head. Scoped to the card
   so the panel-level heads, which sit against a fold row, are left alone. */
.xcard__body .blk__head,
.xcard__body .fold__h { font-size: 15px; letter-spacing: .12em; }
/* already inside a full-width block - spanning again would do nothing good */


/* --- video --------------------------------------------------------------- */
.video {
  position: relative;
  aspect-ratio: 16 / 9;
  /* full panel width at 16:9 is ~870px tall and swamps the page */
  max-width: 980px;
  margin-inline: auto;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: #0D0D0D;
}
.video iframe,
.video video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* letterbox rather than crop: the frame is 16:9 and so is the footage, but a
   clip of another ratio should not have its edges cut off */
.video video { object-fit: contain; background: #0D0D0D; }
.video__ph {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center; gap: 10px;
  text-align: center; padding: var(--s5);
  color: var(--txt-3);
  background: radial-gradient(60% 60% at 50% 45%, color-mix(in srgb, var(--orange) 10%, transparent), transparent 70%);
}
.video__play {
  width: 62px; height: 62px; display: grid; place-items: center;
  border-radius: 50%; border: 1.5px solid var(--orange);
  color: var(--orange);
}
.video__play svg { width: 26px; height: 26px; fill: currentColor; }
.video__hint { font-size: 12px; }
.video__hint code {
  font-family: var(--mono); font-size: 11px;
  padding: 2px 6px; border-radius: 5px;
  background: rgba(255,255,255,.09); color: var(--txt-2);
}

/* --- call to action ------------------------------------------------------- */
.blk--cta { display: grid; gap: var(--s4); justify-items: start; }
.cta {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 26px;
  border-radius: 999px;
  font-size: 15px; font-weight: 640;
  color: var(--black);
  background: var(--orange);
  transition: filter var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease), gap var(--t-fast) var(--ease);
}

/* A CTA block holds one button or a pair. The row wraps rather than shrinking
   them, so on a narrow screen the second drops under the first at full size
   instead of both becoming too small to tap. */
.cta__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }

/* The secondary route. Same shape and the same accent, drawn as an outline so
   the solid button beside it is plainly the first thing to do. Nothing about
   what it does changes. */
.cta--soft {
  color: var(--orange-ink);
  background: transparent;
  border: 1px solid var(--orange-45);
}
.cta--soft:hover { background: var(--orange-08); border-color: var(--orange); box-shadow: none; }
[data-theme="light"] .cta--soft { color: var(--orange-ink); }
.cta:hover { filter: brightness(1.08); transform: translateY(-2px); gap: 14px;
             box-shadow: 0 12px 30px -10px var(--orange-45); }
.cta svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
           stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* --- enquiry form --------------------------------------------------------- */
/* The contact column carries the email address, which is 227px wide at 19px
   and was wrapping to two lines in a 199px column. 288px is the floor that
   keeps it on one line with room to spare on a different font stack. It only
   applies in the two-column layout; below 900px this is a single column. */
.enq { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(288px, .9fr); gap: var(--s7); align-items: start; }
.enq__form { display: grid; gap: var(--s4); }
.enq__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.enq__other[hidden] { display: none; }

.field { display: grid; gap: 7px; }
.field > span { font-size: 12px; font-weight: 560; letter-spacing: .05em; color: var(--txt-3); }
.field input, .field select, .field textarea {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  background: rgba(255,255,255,.05);
  color: var(--txt);
  font: inherit; font-size: 14.5px;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.field textarea { resize: vertical; min-height: 96px; }
/* What is left in the message box. A count that only shows up near the limit
   arrives too late to be useful, so it is always there and only changes colour
   at the end. Second class beats the plain .field > span label rule. */
.field > span.field__left {
  justify-self: end; margin-top: -3px;
  font-size: 11.5px; font-weight: 500; letter-spacing: .04em;
  color: var(--txt-3); font-variant-numeric: tabular-nums;
}
.field > span.field__left.is-low { color: var(--orange); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--orange);
  background: rgba(255,165,0,.06);
}
.field select { appearance: none; cursor: pointer; }
.field option { background: #161616; color: var(--txt); }

.enq__submit {
  justify-self: start;
  display: inline-flex; align-items: center; gap: 10px;
  padding: 13px 28px;
  border-radius: 999px;
  font-size: 15px; font-weight: 640;
  color: var(--black); background: var(--orange);
  transition: filter var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.enq__submit:hover { filter: brightness(1.08); transform: translateY(-2px); }
.enq__submit svg { width: 17px; height: 17px; fill: none; stroke: currentColor;
                   stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.enq__submit[disabled] { opacity: .6; cursor: default; transform: none; filter: none; }

.enq__status { font-size: 13px; line-height: 1.55; color: var(--txt-3); }
.enq__status.is-warn { color: var(--orange); }
.enq__status.is-ok   { color: #34D399; }
.enq__status.is-err  { color: #FF7A5E; }

/* The honeypot. Off-screen rather than display:none, because a bot worth
   worrying about skips anything the CSS has hidden outright. */
.enq__pot {
  position: absolute;
  left: -9999px; top: auto;
  width: 1px; height: 1px;
  overflow: hidden;
}

.enq__aside {
  display: grid; gap: 8px; align-content: start;
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: rgba(255,255,255,.04);
}
/* Same uppercase label the rest of the site sets in the accent, so it reads
   as a section heading rather than fine print. */
.enq__aside h3 { font-size: 11.5px; font-weight: 640; letter-spacing: .14em;
                 text-transform: uppercase; color: var(--orange); }
.enq__aside h3 + p { font-size: 13.5px; color: var(--txt-3); }
/* The address. Microshala is set in the same white as the email address above
   it, so the name reads first and the street sits quietly under it. Each line
   is its own row rather than a wrapped sentence, so the block stays square on
   the left at every width, and the space inside 400 093 is a hard one, so the
   number can never break in two. */
.enq__aside p.enq__addr {
  display: grid; gap: 7px; margin-top: 2px;
  font-size: 13.5px; line-height: 1.5; color: var(--txt-2);
}
.enq__addr__name {
  font-size: 16px; font-weight: 620; color: var(--txt); letter-spacing: -.01em;
}
/* The whole block lights up together, the same orange the email address and
   Start a Chat take. Each line is named because each sets its own colour, and
   an inherited one would not be overridden by the container alone. */
.enq__aside p.enq__addr,
.enq__addr__name,
.enq__parent { transition: color var(--t-mid) var(--ease); }
.enq__aside p.enq__addr:hover,
.enq__aside p.enq__addr:hover .enq__addr__name,
.enq__aside p.enq__addr:hover .enq__parent { color: var(--orange); }
.enq__where { display: grid; gap: 2px; }
.enq__parent { font-size: 12.5px; line-height: 1.45; color: var(--txt-3); font-style: italic; }
}
.enq__aside h3:not(:first-child) { margin-top: var(--s4); }
.enq__big { font-size: 19px; font-weight: 620; color: var(--txt); word-break: break-word; }
.enq__big:hover { color: var(--orange); }

/* --- legal documents ------------------------------------------------------ */
.legal { color: var(--txt-2); }
.legal__h {
  margin: var(--s6) 0 var(--s3);
  font-size: 17px; font-weight: 660; letter-spacing: -.01em;
  color: var(--txt);
}
.legal__h:first-child { margin-top: 0; }
/* Each clause is its own section now that nothing folds, so the space between
   them belongs to the section rather than to the heading inside it. */
.legal__sec + .legal__sec { margin-top: var(--s6); }
.legal__sec .legal__h { margin-top: 0; }
.legal p { font-size: 15px; line-height: 1.75; }
.legal p + p { margin-top: var(--s3); }
.legal ul { margin-top: var(--s3); display: grid; gap: 9px; }
.legal li {
  position: relative; padding-left: 20px;
  font-size: 14.5px; line-height: 1.65;
}
.legal li::before {
  content: ''; position: absolute; left: 2px; top: 10px;
  width: 6px; height: 1.5px; border-radius: 2px; background: var(--orange);
}

/* --- panel footer --------------------------------------------------------- */
/* only Next lives here now that Home has moved into the header */
/* No longer a frozen strip across the foot of the sheet. It rides in with the
   content and comes to rest over the footer's top-right corner: the negative
   margin below is what pulls it down onto the footer rather than leaving it
   stranded on a line of its own. z-index so it stays above the footer's own
   top rule. */
.panel__foot {
  flex: 0 0 auto;
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--s3);
  padding-inline: var(--edge);
  margin-bottom: calc(var(--s5) * -1);
}
.panel__next {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--line-2);
  font-size: 13.5px; font-weight: 560;
  color: var(--txt);
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), gap var(--t-fast) var(--ease);
}
.panel__next:hover { background: var(--orange-08); border-color: var(--orange-45); gap: 13px; }
.panel__next svg { width: 15px; height: 15px; }
.panel__next[hidden] { display: none; }
.panel__foot[hidden] { display: none; }


/* ============================================================================
   LIGHT MODE
   Every colour the site uses is a token, so the whole theme swaps here. The
   board stays white in both themes because the logo carries black type.
============================================================================ */
[data-theme="light"] {
  --page-bg: #EFEFF1;

  --ink-900: #FFFFFF;      /* page */
  --ink-850: #FAFAFB;
  --ink-800: #F5F5F6;
  --ink-700: #F0F0F2;
  --ink-600: #E8E8EA;

  --line:   rgba(26, 26, 26, .11);
  --line-2: rgba(26, 26, 26, .18);

  --txt:   #1A1A1A;
  --txt-2: rgba(26, 26, 26, .70);
  --txt-3: rgba(26, 26, 26, .50);

  /* #FFA500 is only 1.9:1 on white, so accent TEXT uses a darker ink here */
  --orange-ink: #A85F00;
  --orange-08: rgba(255, 165, 0, .10);
  --orange-16: rgba(255, 165, 0, .18);
  --orange-45: rgba(255, 165, 0, .55);

  --sh-1: 0 1px 2px rgba(26,26,26,.06);
  --sh-2: 0 4px 16px -4px rgba(26,26,26,.12), 0 1px 3px rgba(26,26,26,.06);
  --sh-3: 0 24px 60px -16px rgba(26,26,26,.24), 0 4px 12px rgba(26,26,26,.07);
}

/* --- chrome --------------------------------------------------------------- */
[data-theme="light"] .topbar,
[data-theme="light"] .stage,
[data-theme="light"] .frame { background: var(--ink-900); }
[data-theme="light"] .footer { background: var(--ink-850); }
/* Left the grey wash here from before the bar's links picked up the orange
   hover, which would have undone it in light mode only. */
[data-theme="light"] .topbar__link:hover { background: var(--orange-08); }
/* No plate needed on a white bar - but the padding STAYS. Dropping the 10px
   each side took 20px out of the mark's box, which pulled the rule and the
   Microscan mark 20px left every time the theme was switched. Padding with no
   background behind it is invisible; all it does here is hold the space, so
   the two themes now lay out identically and nothing moves. */
[data-theme="light"] .topbar__logo { background: transparent; }

/* accent text needs the darker ink on a light background */
[data-theme="light"] .hub__sub strong,
[data-theme="light"] .footer__list a:hover,
[data-theme="light"] .footer__list button:hover,
[data-theme="light"] .node:hover .node__go,
[data-theme="light"] .enq__aside h3,
[data-theme="light"] .panel__eyebrow,
[data-theme="light"] .blk__head,
[data-theme="light"] .blk__em,
[data-theme="light"] .stat__v { color: var(--orange-ink); }

/* --- stage backdrop -------------------------------------------------------- */
[data-theme="light"] .stage__wash {
  background:
    radial-gradient(52% 48% at 50% 46%, rgba(255,165,0,.10), transparent 70%),
    radial-gradient(34% 36% at 80% 22%, rgba(91,139,255,.07), transparent 72%),
    radial-gradient(40% 42% at 16% 80%, rgba(255,122,94,.06), transparent 74%);
}
[data-theme="light"] .stage__grid {
  background-image:
    linear-gradient(to right,  rgba(26,26,26,.055) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(26,26,26,.055) 1px, transparent 1px);
}

/* --- scene ---------------------------------------------------------------- */
[data-theme="light"] .cable-core   { stroke: #EDEDEE; }
[data-theme="light"] .cable-jacket { stroke: var(--black); }
[data-theme="light"] .cleat        { stroke: #8A8A8D; }
[data-theme="light"] .cleat-screw  { fill: #B4B4B7; }

/* the figure is dark on both the light board and the light stage, so the
   halo has nothing to do and would only show as a white smear */
[data-theme="light"] .fig       { stroke: var(--black); }
[data-theme="light"] .fig--head { fill: var(--black); }
[data-theme="light"] .fig--skirt { fill: var(--black); }
[data-theme="light"] .fig--torso { fill: var(--black); }
[data-theme="light"] .fig-halo  { opacity: 0 !important; }
[data-theme="light"] .prop        { stroke: #46586E; }
[data-theme="light"] .prop--box   { fill: #E4E7EC; stroke: #46586E; }
[data-theme="light"] .prop--fibre { stroke: #6E8093; }
[data-theme="light"] .prop--flash,
[data-theme="light"] .prop--ray   { stroke: #FF6A00; }

/* the board keeps its white plate; on a white page it needs an edge instead
   of a shadow to stay distinct */
[data-theme="light"] .board { stroke: rgba(26,26,26,.16); filter: none; }
[data-theme="light"] .rack-ear  { fill: #C2C6CE; }
[data-theme="light"] .rack-bay  { stroke: rgba(26,26,26,.22); }
[data-theme="light"] .rack-vent { stroke: rgba(26,26,26,.15); }

/* the spectrum is light against white, so widen both layers */
[data-theme="light"] .fibre      { stroke-width: 2.6; }
[data-theme="light"] .fibre-glow { opacity: .3; }
[data-theme="light"] .shock      { stroke: var(--black); }

/* --- surfaces -------------------------------------------------------------- */
[data-theme="light"] .node,
[data-theme="light"] .card,
[data-theme="light"] .xcard { background: #FFFFFF; }
[data-theme="light"] .node { box-shadow: var(--sh-2); }
[data-theme="light"] .panel__sheet { background: linear-gradient(180deg, #FFFFFF 0%, #FAFAFB 60%); }
[data-theme="light"] .panel__scrim { background: rgba(26,26,26,.42); }
[data-theme="light"] .panel__foot  { background: rgba(250,250,251,.9); }
[data-theme="light"] .ctl          { background: rgba(255,255,255,.9); }
[data-theme="light"] .stat,
[data-theme="light"] .enq__aside,
[data-theme="light"] .ph { background: rgba(26,26,26,.03); }
[data-theme="light"] .field input,
[data-theme="light"] .field select,
[data-theme="light"] .field textarea { background: rgba(26,26,26,.04); }
[data-theme="light"] .field option { background: #FFFFFF; color: var(--txt); }
[data-theme="light"] .video { background: #F4F4F5; }
[data-theme="light"] .panel__home:hover { color: var(--orange-ink); }
[data-theme="light"] .panel__note code,
[data-theme="light"] .video__hint code { background: rgba(26,26,26,.07); }

/* ============================================================================
   BACKGROUND PATTERNS
   One per node, sitting behind the panel content. All of them are deliberately
   faint and slow; they should register as texture, never as decoration you
   have to read past.
============================================================================ */
.panel__bg {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: .5;
  -webkit-mask-image: radial-gradient(120% 90% at 70% 10%, #000 0%, transparent 78%);
          mask-image: radial-gradient(120% 90% at 70% 10%, #000 0%, transparent 78%);
}
.panel__head, .panel__scroll, .panel__foot { position: relative; z-index: 1; }

/* strands drifting diagonally */
.bg--fibre {
  background-image: repeating-linear-gradient(
    118deg, transparent 0 46px,
    color-mix(in srgb, var(--a) 22%, transparent) 46px 47px,
    transparent 47px 96px);
  animation: bgDrift 42s linear infinite;
}

/* splice arcs, pulsing outward */
.bg--splice {
  background-image:
    radial-gradient(circle at 78% 26%, color-mix(in srgb, var(--a) 16%, transparent) 0 1px, transparent 1px 90px),
    radial-gradient(circle at 78% 26%, transparent 0 118px, color-mix(in srgb, var(--a) 13%, transparent) 118px 119px, transparent 119px),
    radial-gradient(circle at 78% 26%, transparent 0 188px, color-mix(in srgb, var(--a) 10%, transparent) 188px 189px, transparent 189px),
    radial-gradient(circle at 78% 26%, transparent 0 262px, color-mix(in srgb, var(--a) 8%, transparent) 262px 263px, transparent 263px);
  animation: bgBreathe 9s var(--ease-out) infinite;
}

/* rack bays and shelf lines */
.bg--rack {
  background-image:
    repeating-linear-gradient(90deg, transparent 0 118px,
      color-mix(in srgb, var(--a) 15%, transparent) 118px 119px, transparent 119px 238px),
    repeating-linear-gradient(0deg, transparent 0 54px,
      color-mix(in srgb, var(--a) 9%, transparent) 54px 55px, transparent 55px 110px);
  animation: bgShift 60s linear infinite;
}

/* a light pulse travelling along the span */
.bg--pulse {
  background-image: repeating-linear-gradient(
    90deg, transparent 0 150px,
    color-mix(in srgb, var(--a) 20%, transparent) 150px 152px,
    transparent 152px 300px);
  animation: bgRun 26s linear infinite;
}

/* soft concentric protection rings */
.bg--shield {
  background-image:
    radial-gradient(circle at 84% 18%, transparent 0 90px, color-mix(in srgb, var(--a) 11%, transparent) 90px 91px, transparent 91px),
    radial-gradient(circle at 84% 18%, transparent 0 160px, color-mix(in srgb, var(--a) 9%, transparent) 160px 161px, transparent 161px),
    radial-gradient(circle at 84% 18%, transparent 0 240px, color-mix(in srgb, var(--a) 7%, transparent) 240px 241px, transparent 241px);
  animation: bgBreathe 13s var(--ease-out) infinite;
}

/* a network of points, for the reach-outward section */
.bg--globe {
  background-image:
    radial-gradient(circle, color-mix(in srgb, var(--a) 26%, transparent) 1.4px, transparent 1.5px),
    repeating-linear-gradient(74deg, transparent 0 128px,
      color-mix(in srgb, var(--a) 9%, transparent) 128px 129px, transparent 129px 256px);
  background-size: 44px 44px, auto;
  animation: bgDrift 70s linear infinite;
}

@keyframes bgDrift   { from { background-position: 0 0; }     to { background-position: 460px 0; } }
@keyframes bgShift   { from { background-position: 0 0; }     to { background-position: 0 440px; } }
@keyframes bgRun     { from { background-position: -300px 0; } to { background-position: 300px 0; } }
@keyframes bgBreathe { 0%,100% { opacity: .55; } 50% { opacity: 1; } }

/* ============================================================================
   THE MOVING LAYER
   The gradients above are texture. This is the part that actually moves, and
   it is a different piece of plant per node: light running down a bundle,
   traffic crossing a span, a scan down the racks, a splice striking, cover
   breathing outward, a network passing packets.

   Markup comes from FX in site.js, keyed to each section's `bg`. Everything
   is CSS-animated SVG, so the reduced-motion rule at the foot of this file
   stops all of it at once. --a is the node's own strand colour.
============================================================================ */
/* Its own layer, not part of .panel__bg: the texture there is masked hard into
   the top right corner, and cropping the traffic to one corner defeats the
   point of it. This mask only softens the far edges.

   z-index 0 already puts it under the content at z-index 1, so nothing here
   ever paints over a word. The opacity is doing a different job: the text sits
   on a transparent ground, so a strand crossing a paragraph shows through the
   gaps between letters and reads as being on top of them. Held at this level
   it stays a plane further back, which is where it belongs. */
.panel__fx {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: .38;
  -webkit-mask-image: radial-gradient(150% 125% at 62% 4%, #000 0%, #000 46%, transparent 94%);
          mask-image: radial-gradient(150% 125% at 62% 4%, #000 0%, #000 46%, transparent 94%);
}
.fx { position: absolute; inset: 0; width: 100%; height: 100%; }
.fx path, .fx line, .fx circle { fill: none; }

/* light travelling down a strand. The dash is the pulse; the offset carries
   it from one end of the path to the other. */
.fx-strands path,
.fx-spans line,
.fx-bays line,
.fx-shelves line,
.fx-mesh line,
.fx-end { stroke: currentColor; stroke-width: 1; opacity: .16; }

.fx-runs path {
  stroke: var(--a); stroke-width: 1.9; stroke-linecap: round;
  stroke-dasharray: 130 2400;
  opacity: .7;
  animation: fxRun var(--d, 10s) linear infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxRun { from { stroke-dashoffset: 2530; } to { stroke-dashoffset: 0; } }

/* traffic crossing a span */
.fx-pkts rect {
  fill: var(--a); opacity: .55;
  animation: fxSlide var(--d, 8s) linear infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxSlide { from { transform: translateX(0); } to { transform: translateX(1320px); } }

/* anything that just needs to be alive: taps, LEDs, hubs */
.fx-taps circle,
.fx-leds circle,
.fx-hubs circle {
  fill: var(--a);
  animation: fxBlink var(--d, 3s) ease-in-out infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxBlink { 0%, 100% { opacity: .18; } 50% { opacity: .8; } }

/* a scan running down the rack faces */
.fx-scan {
  fill: var(--a); opacity: .1;
  animation: fxScan 15s linear infinite;
}
@keyframes fxScan { from { transform: translateY(-160px); } to { transform: translateY(900px); } }

/* the splice: an arc strikes, then the joint rings outward */
.fx-ring {
  stroke: var(--a); stroke-width: 1.5;
  transform-box: fill-box; transform-origin: center;
  animation: fxRing 4.6s var(--ease-out) infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxRing {
  0%   { transform: scale(.12); opacity: .7; }
  100% { transform: scale(4.4); opacity: 0; }
}
.fx-arc { fill: var(--a); animation: fxArc 4.6s var(--ease-out) infinite; }
@keyframes fxArc {
  0%  { opacity: 0; }
  5%  { opacity: 1; }
  20% { opacity: .2; }
  100%{ opacity: .2; }
}

/* cover, breathing */
.fx-breathe {
  stroke: var(--a); stroke-width: 1.3;
  transform-box: fill-box; transform-origin: center;
  animation: fxBreathe 7.5s ease-in-out infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxBreathe {
  0%, 100% { transform: scale(1);    opacity: .14; }
  50%      { transform: scale(1.06); opacity: .4; }
}

.fx-motes circle {
  fill: var(--a); opacity: .42;
  animation: fxFloat var(--d, 16s) ease-in-out infinite;
  animation-delay: var(--t, 0s);
}
@keyframes fxFloat {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(28px, -44px); }
}

/* On a phone the panel is narrow, so the motif compresses into the reading
   column instead of running beside it. Step it back there. */
@media (max-width: 759px) { .panel__fx { opacity: .26; } }

/* the strand spectrum is pale against white, same as the burst on the landing
   page: widen it rather than let it disappear */
[data-theme="light"] .fx-runs path { stroke-width: 3.2; }
[data-theme="light"] .fx-strands path,
[data-theme="light"] .fx-spans line,
[data-theme="light"] .fx-bays line,
[data-theme="light"] .fx-shelves line,
[data-theme="light"] .fx-mesh line,
[data-theme="light"] .fx-end { opacity: .2; }
[data-theme="light"] .fx-ring,
[data-theme="light"] .fx-breathe { stroke-width: 2; }

/* the same idea behind the landing stage, weaker still */
.stage__weave {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
  background-image: repeating-linear-gradient(
    118deg, transparent 0 74px,
    rgba(255,165,0,.10) 74px 75px,
    transparent 75px 150px);
  animation: bgDrift 60s linear infinite;
  transition: opacity 1800ms var(--ease-out);
  -webkit-mask-image: radial-gradient(80% 70% at 50% 50%, transparent 0%, #000 85%);
          mask-image: radial-gradient(80% 70% at 50% 50%, transparent 0%, #000 85%);
}
.stage__weave.is-in { opacity: 1; }
[data-theme="light"] .stage__weave {
  background-image: repeating-linear-gradient(
    118deg, transparent 0 74px,
    rgba(26,26,26,.05) 74px 75px,
    transparent 75px 150px);
}

/* ============================================================================
   PANEL TAGLINE - "Microscan does X. Microshala makes it happen."
============================================================================ */
.panel__tagline {
  margin-top: 12px;
  font-size: 17px;
  font-weight: 620;
  line-height: 1.4;
  letter-spacing: -.005em;
  color: var(--orange-ink);
}
.panel__tagline[hidden] { display: none; }

/* ============================================================================
   SOCIAL ICONS
============================================================================ */
.socials { display: flex; align-items: center; gap: 10px; }
.social {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--txt-2);
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.social svg { width: 15px; height: 15px; fill: currentColor; }
.social:hover {
  color: var(--orange-ink);
  border-color: var(--orange-45);
  background: var(--orange-08);
  transform: translateY(-2px);
}
/* links are not live yet; still hoverable so the row does not look broken */
.social.is-inactive { cursor: default; }
.social.is-inactive:hover { transform: none; }

/* ============================================================================
   RESPONSIVE
   Four tiers, each named for what actually changes rather than for a device:

     >= 1120   the six blocks ring the board. Needs width for the ellipse and
               about 460px of height before the top block touches the board.
     <  1120   the ring will not fit, so JS lays the blocks out in a grid under
               the board. The stage needs more height to hold both.
     <  760    two columns of blocks instead of three, and the top bar drops
               everything it can spare.
     <  560    one column of footer, stacked bars, tightest type.

   The stage floor per tier is the number that matters: it is what stops the
   scene being squashed. Where a viewport is too short to hold stage + bar +
   footer, the page scrolls rather than compressing the scene.
============================================================================ */

/* --- the ring gives way to the grid --------------------------------------- */
@media (max-width: 1119px) {
  /* nine blocks now, not seven: the stage needs the room or the rows are
     shorter than the labels they hold */
  :root { --stage-min: 700px; }
  /* ...but only when all nine are showing. Folded, the floor for nine makes a
     phone scroll to reach a page that would have fitted. Set on body rather
     than :root so it wins for the stage inside it. */
  body.heads-1 { --stage-min: 620px; }
  body.heads-0 { --stage-min: 460px; }

  /* the page can scroll now, so keep the nav and its CTA reachable */
  .topbar { position: sticky; top: 0; }
}

/* --- the bar runs out of room ---------------------------------------------
   Measured, not guessed: at 768px the row wanted 795px. Rather than shave
   padding until the labels touch, Home and the two menus drop into a drawer
   from here down. The mode, sound, LMS and Get in Touch controls stay in the
   bar - they are the ones that must never be duplicated or moved out of the
   document, and they are small enough to stay. ---------------------------- */
@media (max-width: 900px) {
  /* ---- the drawer -------------------------------------------------------
     Below this width the bar cannot hold Home and two menus as well: measured
     at 375px there were 40px of free space before any of this was added. So
     the SAME elements move under the bar instead of being copied into a second
     menu, which is what keeps the mode and sound controls alive. */
  .navtoggle { display: inline-flex; }

  .topbar__primary {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch;
    gap: 4px;
    padding: var(--s3) var(--s4) var(--s4);
    border-bottom: 1px solid var(--line);
    background: var(--ink-850);
    box-shadow: 0 26px 50px -30px rgba(0, 0, 0, .9);
    /* closed: not just invisible. visibility and pointer-events keep it out of
       the tab order, so a shut drawer cannot be reached with a keyboard. */
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(-6px);
    transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
                visibility var(--t-fast) var(--ease);
  }
  .topbar.is-open .topbar__primary {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: none;
  }

  /* full-width rows in the drawer, and Home is one of them */
  .topbar.is-open .topbar__link--home,
  .is-panel .topbar__link--home { width: 100%; justify-content: flex-start; }
  .menu { width: 100%; }
  .menu__btn { width: 100%; justify-content: space-between; padding: 11px 12px; font-size: 14px; }

  /* in a drawer a popover has nothing to pop over: the list just opens in
     place, the way every other fold on the site does */
  .menu__pop,
  #menuExternal .menu__pop {
    position: static; min-width: 0;
    margin: 2px 0 4px;
    padding: 4px;
    background: transparent;
    border-color: var(--line);
    box-shadow: none;
  }
  .menu__item { padding: 11px 12px; font-size: 13.5px; }

  /* Get in Touch stands down to an outline so the LMS pill is not competing
     with a solid orange button in a 375px row. Both still work. */
  .topbar__cta {
    background: transparent;
    color: var(--txt);
    border: 1px solid var(--line-2);
  }
  .topbar__cta:hover { background: var(--orange-08); border-color: var(--orange-45); color: var(--orange-ink); }
}

/* --- the footer's four columns stop fitting -------------------------------- */
/* Held off as long as possible: every row it gains is a row of height the
   stage does not get. */
@media (max-width: 860px) {
  .footer__inner { grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s5); }
  .footer__col--brand { grid-column: 1 / -1; }
  .footer__tag { max-width: 60ch; }
}

/* --- panels stop being landscape ------------------------------------------ */
@media (max-width: 900px) {
  .enq { grid-template-columns: 1fr; gap: var(--s5); }
  .panel__headInner { max-width: none; }
  .panel__title { font-size: clamp(26px, 5.4vw, 40px); }
}

/* --- two columns of blocks ------------------------------------------------- */
@media (max-width: 759px) {
  :root { --topbar-h: 56px; --stage-min: 820px; }
  body.heads-1 { --stage-min: 660px; }
  body.heads-0 { --stage-min: 480px; }

  .topbar { gap: var(--s2); }
  .topbar__nav { gap: 2px; }
  /* 34 plus padding is 42px inside a 56px bar */
  /* 36 + 4 + 4 is 44px in this bar's 56px */
  .topbar__logo { height: 36px; padding: 4px 8px; }
  /* the bar is already dropping words to fit at this width, so the second
     mark goes as well - it is a link out, not navigation for this site */
  .topbar__parent { display: none; }
  .topbar__link { padding: 7px 9px; font-size: 13px; }
  .topbar__cta { margin-left: 4px; padding: 8px 14px; font-size: 13px; }
  .theme { margin-left: 2px; width: 32px; height: 32px; }
  /* "Login to LMS" becomes "LMS": the row has to hold five things at 390px */
  .topbar__long { display: none; }



  /* small blocks: the label is the whole card, so give it the room the
     "Explore" row was using and tighten it a step */
  .node--sm { padding: 11px 12px; }
  .node--sm .node__icon { width: 27px; height: 27px; border-radius: 8px; }
  .node--sm .node__icon svg { width: 15px; height: 15px; }
  .node--sm { --nfs: 13px; }
  .node--sm .node__label { line-height: 1.24; }

  .panel__home { padding: 9px 15px 9px 12px; font-size: 13px; }
  .panel__lede { font-size: 15px; }
  .panel__tagline { font-size: 15.5px; }
  .blk { margin-bottom: var(--s5); }
  .xcard__sum { padding: var(--s4); gap: 6px var(--s4); }
  .xcard__tog { width: 38px; height: 38px; }
  .xcard__tog svg { width: 17px; height: 17px; }
  .xcard__body { padding: var(--s2) var(--s4) var(--s4); }
  .xcard__body::before { left: var(--s4); right: var(--s4); }

  .footer__inner { padding-block: var(--s3); }
  .footer__bar { padding-block: var(--s2); }
}

/* --- phones ---------------------------------------------------------------- */
@media (max-width: 559px) {
  /* a group of three wraps to two rows here, so two more rows to fit */
  :root { --stage-min: 900px; }
  body.heads-1 { --stage-min: 700px; }
  body.heads-0 { --stage-min: 520px; }

  /* At two columns on a 320px screen a block is about 142px wide, and the
     longest label runs to four lines. The card is sized by JS from the height
     the stage has left, so the label has to come down to it rather than the
     other way round: the "Explore" row goes and the type steps down. A tap
     target this size does not need to be told it is a link. */
  /* The only clear space on a phone is the strip above the board, and it is
     about 78px tall. A 208px-wide tooltip is 109px tall and does not fit, so
     it ends up on the logo. Wide and short does fit. */
  .figtip { max-width: calc(100vw - 28px); padding: 8px 11px; }
  /* The bubble goes as wide as it likes here, which sounds backwards until you
     count the pixels: the strip above the figure is about 44px tall, and a
     narrow bubble wraps to two lines and needs 43 of them plus a gap it has
     not got, so it ends up sitting on the figure's head. One wide line is
     26px and clears it. The tail is clamped to the head either way. */
  .figcloud { max-width: calc(100vw - 28px); padding: 6px 9px 6px 10px; }
  .figcloud__txt { font-size: 11px; }

  .node { padding: 11px 12px; --nfs: 13px; }
  .node__go { display: none; }
  .node__icon { width: 30px; height: 30px; border-radius: 9px; }
  .node__icon svg { width: 16px; height: 16px; }
  .node__label { line-height: 1.24; }

  /* Follow reads as a row rather than a stack, which is one heading's worth of
     height back on a screen where the footer is already the longest section */
  .footer__inner { gap: var(--s3) var(--s5); padding-block: 10px; }
  .footer__col--social {
    grid-column: 1 / -1;
    display: flex; align-items: center; gap: var(--s4);
  }
  .footer__col--social .footer__head { margin-bottom: 0; }
  .footer__bar {
    flex-direction: column; align-items: flex-start; gap: 4px;
    text-align: left; padding: 7px var(--s4);
  }

  .enq__row { grid-template-columns: 1fr; }
  .enq__submit { justify-self: stretch; text-align: center; }

  .panel__foot { gap: 8px; }
  .panel__next { padding: 9px 13px; font-size: 12.5px; gap: 7px; }
  .panel__next svg { width: 14px; height: 14px; }

  .blk--note { padding: var(--s4); }
  .stat { padding: var(--s4); }
  .card { padding: var(--s4); }
  .video__play { width: 50px; height: 50px; }
  .video__play svg { width: 21px; height: 21px; }
}

/* --- the smallest phones in circulation ------------------------------------ */
@media (max-width: 389px) {
  /* the constraint here is the width of the row, not the height of the bar,
     and the cropped mark is wider for its height than the full logo was */
  .topbar__logo { height: 24px; }
  .topbar__link { padding: 7px 7px; }
  .topbar__cta { padding: 8px 12px; }
  /* At 320px the row had nothing left over. The frame around the mode pair is
     the cheapest thing to give up: the two controls still read as a pair
     because they are still adjacent, and it buys back 20px. */
  .topbar__modes { padding: 0; border-color: transparent; gap: 0; }
  .topbar__modes .theme { width: 30px; height: 30px; margin: 0; }
  .navtoggle { width: 32px; height: 32px; }
  /* "Get in Touch" wrapped to two lines here and took the bar's height with
     it. The row keeps the logo, the modes, LMS and the menu button; the
     enquiry button moves down into the drawer. */
  .topbar__nav > .topbar__cta { display: none; }
  .topbar__cta--drawer { display: block; width: 100%; margin: 4px 0 0; text-align: center; }
  .footer__inner { grid-template-columns: 1fr; }

}

/* --- short and wide: a phone on its side, or a laptop with a small panel ---- */
/* Trim the chrome rather than the scene, so the stage keeps its height. */
@media (max-height: 700px) and (min-width: 760px) {
  .footer__inner { padding-block: 9px; gap: var(--s4); }
  .footer__tag { display: none; }
  .footer__bar { padding-block: 6px; }
}

/* --- notches and home indicators ------------------------------------------- */
/* The viewport meta is viewport-fit=cover, so the page runs under the rounded
   corners and the home indicator. Pad the three edges that carry content.
   Last in the file on purpose: it has to win over the shorthand paddings the
   breakpoints above set. */
@supports (padding: env(safe-area-inset-left)) {
  .topbar,
  .footer__inner,
  .footer__bar,
  .panel__head,
  .panel__body,
  .panel__foot {
    padding-left:  calc(var(--edge) + env(safe-area-inset-left));
    padding-right: calc(var(--edge) + env(safe-area-inset-right));
  }
  .footer__bar { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
  .panel__foot { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  .panel__body { padding-bottom: calc(var(--s8) + env(safe-area-inset-bottom)); }
}
/* --edge is defined once, at the top of this file, and every shell element
   uses that one value. It used to be set here three different ways - s5 for
   the chrome, s4 on phones, pad-x for the panels - which is what put the
   header, the panel title, the panel body and the footer on four different
   left edges. Nothing overrides it any more. */

/* --- touch: no sticky hover states ----------------------------------------- */
/* A tap leaves :hover latched on the last thing touched, so a card would stay
   lifted after you moved on. Colour cues stay, movement goes. */
@media (hover: none) {
  .node.is-settled:hover { --dy: 0px; }
  .card:hover,
  .cta:hover,
  .topbar__cta:hover,
  .enq__submit:hover,
  .social:hover,
  .ctl:hover { transform: none; }
  .panel__home:hover { transform: none; }
}

/* --------------------------------------------------- REDUCED MOTION --- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ==========================================================================
   TEXT AND ILLUSTRATION, SIDE BY SIDE
   ==========================================================================
   On a wide monitor the opening band was 1560px of three columns holding a
   single block of text. The text landed in the first column, the other two
   stood empty, and the scene was pinned to the far right - so the two things
   the reader is meant to take in together sat 700px apart with a void between
   them, and the eye had to travel the whole width to connect them.

   Two changes, and only for the opening band that shares its row with a scene:
   the text stops at a proper reading measure instead of spreading, and the
   scene sits just past it rather than at the edge. Everything below the first
   band keeps the column behaviour it already had.

   min() is what keeps this honest across screen sizes: the scene sits after
   the reading column where there is room, and falls back to whatever the panel
   can actually spare once the screen narrows, so it never overruns the edge.
   ========================================================================== */
@media (min-width: 1100px) {
  /* Three things were in tension on a wide monitor: a readable line length,
     the figures sitting at the right edge, and no dead space between them.
     Widening the text alone bought the gap at the cost of 108-character lines;
     moving the figures inward bought both but took them off the edge.

     Growing the illustration settles all three. It is line art in an SVG, so it
     scales without loss, and a 2000px screen has room for figures twice the
     size of the ones drawn for a laptop. The text keeps a proper measure, the
     figures stay hard right, and what used to be a void is now the drawing. */
  /* The illustration keeps the size it was drawn at and sits hard right. The
     text takes everything else.

     Widening the box alone does nothing, because .blk carries break-inside:
     avoid, so one block can never flow past the first column - which is how
     the opening band ended up as narrow text with a void beside it. The
     columns therefore go on the block itself, and its paragraphs spread.

     columns: 400px 2 means at least 400px per column and never more than two.
     A laptop gets one column of comfortable width, a wide monitor gets two,
     and the switch happens on available room rather than a guessed breakpoint. */
  .panel__body.has-scene > .band:first-child {
    column-count: 1;
    max-width: calc(100% - 460px);
  }
  /* One block across the whole width, on request. No columns: the text is a
     single run occupying everything the two columns used to cover.

     The flex centring from the earlier alignment rule is left in place. It sits
     the text level with the middle of the illustration rather than at the top
     of the band, which is what closes the gap under a short paragraph - the LMS
     node has three lines against a 184px drawing, and without this they sat at
     the top with the space hanging below them.

     display:flex and multi-column are mutually exclusive, so anything wanting
     columns here has to turn the flex off first. Nothing does now. */
  .panel__body.has-scene:not([data-node="about"]) > .band:first-child > .blk,
  .panel__body.has-scene[data-node="about"] > .band:first-child > .blk {
    columns: auto;
    /* Stated here rather than left to the earlier rule, which was not winning.
       A short opening paragraph otherwise sat at the top of a band whose height
       is reserved for the illustration, leaving the space hanging below it - on
       the LMS node that was 112px of nothing between the text and the button. */
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  /* The 58ch cap has to come off in here. A paragraph capped at 692px inside a
     400px column is wider than the column it sits in, so every one of them
     overflowed back into the first column and the second stayed empty - the
     text looked untouched however wide the box became. The column itself is
     the measure now, which is what a column is for. */
  .panel__body.has-scene > .band:first-child > .blk > p,
  .panel__body.has-scene > .band:first-child > .blk > .blk__p,
  .panel__body.has-scene > .band:first-child > .blk > ul > li {
    max-width: none;
  }
  .panel__body.has-scene > .figscene {
    justify-self: end;
  }
  .panel__body.has-scene.scene-inflow > .figscene { justify-self: end; }
  /* Where a block already reaches under the scene, site.js drops the scene into
     a row of its own; that arrangement stays as it was and must not inherit the
     offset above, or it would sit indented in its own row for no reason. */
  .panel__body.has-scene.scene-inflow > .figscene {
    justify-self: end;
    margin-inline-start: 0;
  }
}

/* ==========================================================================
   A READING MEASURE, EVERYWHERE
   ==========================================================================
   Capping the opening band fixed the band beside the illustration and nothing
   else. Every band below it still ran the full 1560px in a single column, so
   prose reached 181 characters a line - roughly two and a half times the point
   at which the eye stops finding the start of the next line reliably. It was
   110 lines across nine nodes.

   Only running prose is capped. Cards, grids and tables are left alone: they
   set their own widths and their text is already held by the card around it.
   ========================================================================== */
.panel__body .blk__p,
.panel__body .blk__lede,
.panel__body .blk--note p,
.panel__body .blk > p,
.panel__body .blk__list > li,
.panel__body .legal__sec > p,
.panel__body .legal__sec > ul > li {
  /* The reading cap was removed on request: every block of prose now takes the
     width its container gives it. It was 58ch, which measured near 78 real
     characters. Lines now run to about 130 on a wide monitor, which is long by
     typographic convention and is a deliberate choice, not an oversight.
     Restore by putting a length back here. */
  max-width: none;
}

/* The About panel's story block sets its own text, outside the .blk classes
   above, so it needed naming separately. sty__step is the row holding a label
   and its paragraph; capping the row is what stops the paragraph running on. */
.panel__body .sty__t,
.panel__body .sty__lede,
.panel__body .sty__step { max-width: none; }
/* sty__close is display type at 32px, where 1ch is about 25px - 66ch would be
   1860px and cap nothing at all. Large text needs a shorter measure, not the
   same one. */
.panel__body .sty__close { max-width: 22ch; }
/* Text inside a card is held by the card, but the widest cards still ran to a
   hundred characters. */
.panel__body .xcard .blk__p,
.panel__body .fold .blk__p,
.panel__body .blk--wide > p { max-width: none; }

/* ==========================================================================
   TOUCH TARGETS ON A PHONE
   ==========================================================================
   Footer links measured 15px tall, the "coming soon" buttons 20px and the
   parent mark 23px. WCAG asks for 24px and Apple suggests 44px, so on a phone
   these were a thumb-width apart and easy to miss.

   Padding only. Nothing moves on a desktop, no link changes what it does, and
   the vertical rhythm is preserved by trimming the list gap by the amount the
   padding adds back.
   ========================================================================== */
/* Width alone was the wrong test: an iPad is 768 to 1024 wide and still
   operated with a thumb. (hover: none) catches every touch device whatever its
   size, and leaves a mouse-driven desktop exactly as it was. */
@media (max-width: 700px), (hover: none) {
  .footer__list a,
  .footer__list button,
  .footer__soon {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
  }
  .footer__list { gap: 0; }
  .footer__parent { padding: 3px 0; }
}

/* The heading on a note block. Only the two legal disclaimers use it; the
   well-being note has no head and is untouched. Sized and coloured like the
   eyebrow labels elsewhere, so it reads as a label on the box rather than
   competing with the section headings around it. */
.blk--note__h {
  margin: 0 0 8px;
  font-size: clamp(11px, 0.23vw + 8px, 12.5px);
  font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--orange);
}

/* WhatsApp is dimmed until the number goes live. Same treatment the social
   icons had before their profiles existed: visible, so the layout does not
   change shape, but plainly not something to press. */
.enq__wa.is-inactive {
  color: var(--txt-3);
  border-color: var(--line);
  cursor: default;
  opacity: .72;
}

/* ==========================================================================
   "TURNING INDUSTRY EXPERIENCE INTO CAPABILITY" ON ONE LINE
   ==========================================================================
   Two things were stopping it. The claim was held to 68% of the band so it
   cleared the illustration, which left the heading 601px to work in; and at
   58px the line needs 1148px. Three lines was the result.

   The claim now takes the whole band, which it can because the band is already
   capped short of the illustration, so nothing reaches under the artwork and
   the scene-overlap test still does not fire.

   The size is capped at 46px rather than 68. That is what fits on one line in
   the width a laptop actually has. Above about 1400px it sits on one line; on
   narrower screens it wraps, which is correct - the alternative is a heading
   too small to read as one.
   ========================================================================== */
@media (min-width: 1100px) {
  .panel__body.has-scene .sty__claim { max-width: 100%; }
  /* Steeper than the site's other headings on purpose. This one line has to
     fit a box that narrows faster than the viewport does, because the band is
     capped short of the illustration. Measured: it needs about 38px at 1280
     and 46px is the most that still fits at 1440. */
  .sty__title { font-size: clamp(30px, 5vw - 26px, 46px); }
}

/* ==========================================================================
   THE "HAVE A LOOK AROUND" CARD, ONE LINE PER ITEM
   ==========================================================================
   The 58ch reading cap was holding these to 548px inside a card 1294px wide,
   so every second room name wrapped and the list read as ten lines rather than
   five. The longest needs 836px and the line above the video 922px, both well
   inside the card.

   Scoped to this card in the About panel. The cap stays everywhere else, where
   it is doing the job it was added for.
   ========================================================================== */
@media (min-width: 1100px) {
  /* Applied to every card, not just the About one. A card already sets its own
     width, so the 58ch cap inside it was a second constraint doing the same job
     badly: it held text to roughly half the card and left the rest empty while
     the line wrapped underneath. Cards are scanned rather than read straight
     through, so the wider measure suits them. Prose out in the open keeps the
     cap. */
  .panel__body .xcard__body .blk__list > li,
  .panel__body .xcard__body .blk__p,
  .panel__body .xcard__body p,
  .panel__body .fold__body .blk__list > li,
  .panel__body .fold__body .blk__p,
  .panel__body .fold__body p {
    max-width: none;
  }
}

/* --- the WhatsApp card ---------------------------------------------------
   Rides on .soonpop, so the scrim, the panel and the open animation are the
   ones the Coming Soon card already uses. Only the message needs styling. */
.soonpop__card--wa { max-width: 460px; text-align: left; }
.soonpop__msg {
  margin: 10px 0 0;
  font-size: clamp(14px, 0.39vw + 9px, 16px);
  line-height: 1.65;
  color: var(--txt-2);
}
/* A button rather than a link, because it opens a panel rather than going to
   a page, but it has to read as a link inside a sentence. */
.soonpop__link {
  font: inherit; color: var(--orange);
  background: none; border: 0; padding: 0;
  cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.soonpop__link:hover, .soonpop__link:focus-visible { color: var(--txt); }

/* --- the WhatsApp card in light mode ---------------------------------------
   The card carried a dark gradient written out in full, with no light version
   to answer it. In light mode the text tokens flip to near-black while the
   card stayed near-black behind them, so the message was black on black and
   the only way to read it was to switch the site back to dark. The card now
   follows the theme, and the kicker uses the darker accent ink, because
   #FFA500 on white is 1.9:1 and unreadable as text. */
[data-theme="light"] .soonpop__card {
  background: linear-gradient(160deg, #FFFFFF, #F4F4F6);
  border-color: var(--line-2);
}
[data-theme="light"] .soonpop__k,
[data-theme="light"] .soonpop__link { color: var(--orange-ink); }
[data-theme="light"] .soonpop__link:hover,
[data-theme="light"] .soonpop__link:focus-visible { color: var(--txt); }
[data-theme="light"] .soonpop__scrim { background: rgba(26, 26, 26, .34); }
/* Start a Chat, in the Get in Touch aside, for the same reason. */
[data-theme="light"] .enq__wa.is-inactive:hover,
[data-theme="light"] .enq__wa.is-inactive:focus-visible,
[data-theme="light"] .enq__big:hover { color: var(--orange-ink); }
/* Start a chat, in the Get in Touch panel. It was dimmed to match the footer
   icon, but that was wrong once it started doing something: a control that
   opens a card should not look dead. It now carries the accent and underlines
   on hover, so it reads as pressable. The footer icon stays dimmed, because it
   sits in a row of social links where dimming is what marks it apart. */
.enq__wa.is-inactive {
  font: inherit; background: none; border: 0; padding: 0;
  /* after `font: inherit`, which wipes the weight back to the aside's own */
  font-weight: 700;
  text-align: left; cursor: pointer;
  /* var(--txt), the same white the email address uses, so the two things in
     this card read as a matching pair. .enq__big already turns orange on
     hover, which is where the sense of it being pressable now comes from. */
  color: var(--txt);
  opacity: 1;
  transition: color var(--t-mid) var(--ease);
}
/* Stated explicitly. .enq__big:hover already turns orange, but the rule above
   carries the same weight and comes later in the file, so without this it
   would win at every moment including hover and nothing would happen. */
.enq__wa.is-inactive:hover,
.enq__wa.is-inactive:focus-visible { color: var(--orange); }
