/* ===========================================================================
   ZNetLab — the skins

   theme.css holds one palette in two modes, and that palette is deliberately
   quiet: one blue carries every interactive thing and the rest of the colour
   is state, not decoration. That is the right default for a tool somebody
   leaves open all day, and it stays the default. Nothing in this file
   applies until a reader asks for it.

   What this adds is a second axis. The reader already chose a MODE — dark,
   light, or follow-the-machine. They can now also choose a LOOK:

       classic          nothing here applies; theme.css as written
       [data-skin=colour]  the whole product in colour, the way the admin
                           portal already was: a hue per family of thing,
                           gradients on the surfaces that lead, tinted cards
       [data-skin=mono]    black and white, for print, for projectors, for
                           colour-blind readers, and for anyone who finds
                           the rest of it loud

   The two axes are independent on purpose. Colourful has a dark palette and
   a light one, and so does black and white, because a skin that forces the
   mode takes away the choice the reader already made.

   HOW THIS AVOIDS TOUCHING 350 KB OF MARKUP: the same way the merge did.
   Both halves of the product are written against custom properties — this
   file redefines those properties under a skin attribute and the whole site
   re-colours without a single element changing. The component rules further
   down are the small remainder: the places where the artwork is a gradient
   or a hue rather than a token, and a token cannot reach them.

   REVERTING: every rule below is scoped to [data-skin=...]. With no skin
   chosen this file is inert, so removing the <link> that loads it is a
   complete and safe undo. assets/theme-backup/ holds the originals of every
   file the skin work touched, and a script that puts them back.
   =========================================================================== */


/* ###########################################################################
   #                                                                         #
   #   COLOURFUL                                                             #
   #                                                                         #
   ########################################################################### */

/* ---------------------------------------------------------------------------
   A HUE PER FAMILY

   The value of colour here is not that it is pretty, it is that it sorts.
   A reader who has seen two screens should be able to tell a routing thing
   from a security thing from a host before reading either label — which is
   what the device icons already do with shape, and what the admin portal
   already does with its status pills.

   So the hues are assigned to families of MEANING and then used everywhere
   that family appears — the nav, the cards, the icon tiles, the tables.
   Six is the ceiling. A seventh hue stops being a category and starts being
   confetti, and the two nearest ones stop being told apart.
   --------------------------------------------------------------------------- */
:root[data-skin="colour"]{
  --h-route:#3b82f6;   /* routing, links, anything that moves a packet   */
  --h-sec:#f43f5e;     /* security — firewall, IPS, VPN, permissions     */
  --h-host:#a855f7;    /* hosts and endpoints — PC, server, phone        */
  --h-svc:#14b8a6;     /* services — cloud, NAT, load balancer, DNS      */
  --h-data:#f59e0b;    /* storage, images, capacity, anything measured   */
  --h-learn:#ec4899;   /* the teaching half: lessons, labs, progress     */

  /* The soft fills and edges each hue needs. Tokens rather than inline
     rgba() for the same reason theme.css keeps --tint-*: the alpha that
     reads on a near-black panel is not the alpha that reads on white, and
     these get redefined below for the light palette. */
  --h-route-t:rgba(59,130,246,.15);  --h-route-e:rgba(59,130,246,.42);
  --h-sec-t:rgba(244,63,94,.14);     --h-sec-e:rgba(244,63,94,.40);
  --h-host-t:rgba(168,85,247,.15);   --h-host-e:rgba(168,85,247,.40);
  --h-svc-t:rgba(20,184,166,.14);    --h-svc-e:rgba(20,184,166,.40);
  --h-data-t:rgba(245,158,11,.14);   --h-data-e:rgba(245,158,11,.40);
  --h-learn-t:rgba(236,72,153,.14);  --h-learn-e:rgba(236,72,153,.40);

  /* The gradient that leads a surface. One angle for the whole product:
     135deg, down and to the right, the direction a page is read. */
  --grad:linear-gradient(135deg,var(--accent),var(--h-host));
  --grad-soft:linear-gradient(135deg,var(--tint-accent),var(--h-host-t));
}

/* ---------------------------------------------------------------------------
   COLOURFUL · DARK

   Written on bare [data-skin="colour"] so it is what a dark machine and an
   explicit dark choice both get, exactly as theme.css puts dark on bare
   :root. The light palette overrides it further down, in the same two
   places and for the same reasons.

   The surfaces move from theme.css's neutral navy toward indigo, and each
   step gains a little saturation rather than only lightness. A stack of
   greys separated only by lightness goes flat the moment a screen is
   dimmed or a projector is involved; a stack separated by hue as well
   survives it.
   --------------------------------------------------------------------------- */
:root[data-skin="colour"]{
  --bg:#0a0f1f;
  --panel:#141b33;
  --panel2:#111730;
  --panel3:#1b2444;
  --line:#2a3357;
  --line2:#212a4a;

  --ink:#eef2fc;
  --ink2:#a4b1d4;
  --ink3:#7583ab;
  --ink4:#535f85;

  --accent:#5b8def;
  --accent2:#7ba7f7;
  --accent-ink:#ffffff;
  --ok:#2dd4a7;
  --warn:#fbbf24;
  --bad:#fb7185;
  --idle:#7c89b0;
  --pro:#c084fc;

  /* The colourful skin leans into them. */
  --area-accounts:#7aa2ff;
  --area-images:#c084fc;
  --area-monitor:#34e2c8;
  --area-market:#fbb040;
  --area-server:#9fb0d9;
  --area-community:#fb7bb4;

  --cable-copper:#8fb0d8;
  --cable-crossover:#5ee0d3;
  --cable-fiber:#f0c14b;
  --cable-serial:#c084fc;
  --cable-console:#98a1b8;
  --cable-coax:#c09070;
  --cable-wireless:#34e2c8;

  --tint-accent:rgba(91,141,239,.16);
  --tint-ok:rgba(45,212,167,.13);
  --tint-warn:rgba(251,191,36,.13);
  --tint-bad:rgba(251,113,133,.13);
  --tint-pro:rgba(192,132,252,.20);
  --edge-accent:rgba(91,141,239,.40);
  --edge-ok:rgba(45,212,167,.36);
  --edge-warn:rgba(251,191,36,.36);
  --edge-bad:rgba(251,113,133,.36);

  /* A console stays dark in both modes and in every skin — see theme.css.
     It gains a touch of the same indigo so it belongs to this palette
     rather than sitting in it as a black hole. */
  --term-bg:#060a17;
  --term-ink:#ccd8ef;

  --scroll-thumb:#32406b;
  --grid-dot:#233056;
  --wire:#4166a8;
  --node-edge:#4b6293;

  /* Shadows pick up the hue too. A neutral black shadow under a coloured
     panel is the one thing that makes a colourful dark theme look muddy. */
  --shadow:0 1px 2px rgba(4,8,22,.45), 0 4px 16px rgba(20,12,60,.38);
  --shadow-l:0 18px 50px rgba(8,6,32,.58);

  /* The teaching half's own tokens. It ships two complete palettes of its
     own keyed on [data-theme], which bridge.css deliberately leaves alone —
     but a skin is exactly the case where they must move, or half the
     product changes colour and half does not. */
  --brand:#5b8def;
  --brand-d:#7ba7f7;
  --green:#2dd4a7;
  --bg2:#111730;
  --bg3:#1b2444;
  --card:#141b33;
  --muted:#7583ab;
  --code:#060a17;
  --code-ink:#ccd8ef;
  --acbg:#0a0f1f;
  --acbar:#070b18;
  --acline:#2a3357;
  --sb-brand:#5b8def;
  --sb-brand-d:#7ba7f7;
  --sb-ink:#eef2fc;
  --sb-ink2:#a4b1d4;
  --sb-ink3:#7583ab;
  --sb-card:#141b33;
  --sb-line:#2a3357;
  --sb-field:#111730;
}

/* COLOURFUL · LIGHT — written twice, once for the machine and once for the
   toggle, guarded exactly as theme.css guards its own light palette. On
   white the hues have to go deeper to hold their contrast, and the tints
   have to go much weaker, or a tinted card reads as a filled one. */
@media (prefers-color-scheme: light){
  :root[data-skin="colour"]:not([data-theme="dark"]){
    --bg:#f4f6fd;
    --panel:#ffffff;
    --panel2:#edf1fb;
    --panel3:#e3e9f9;
    --line:#d8e0f2;
    --line2:#e6ebf7;

    --ink:#111830;
    --ink2:#465170;
    --ink3:#6b7699;
    --ink4:#95a0bd;

    --accent:#2b52c8;
    --accent2:#1f3fa8;
    --accent-ink:#ffffff;
    --ok:#087f5b;
    --warn:#8a5a06;
    --bad:#c02649;
    --idle:#78849f;
    --pro:#7c3aed;

    --h-route:#1d4ed8;
    --h-sec:#be123c;
    --h-host:#7e22ce;
    --h-svc:#0f766e;
    --h-data:#b45309;
    --h-learn:#be185d;

    --h-route-t:rgba(29,78,216,.09);   --h-route-e:rgba(29,78,216,.30);
    --h-sec-t:rgba(190,18,60,.08);     --h-sec-e:rgba(190,18,60,.28);
    --h-host-t:rgba(126,34,206,.08);   --h-host-e:rgba(126,34,206,.28);
    --h-svc-t:rgba(15,118,110,.08);    --h-svc-e:rgba(15,118,110,.28);
    --h-data-t:rgba(180,83,9,.09);     --h-data-e:rgba(180,83,9,.30);
    --h-learn-t:rgba(190,24,93,.08);   --h-learn-e:rgba(190,24,93,.28);

    --tint-accent:rgba(43,82,200,.08);
    --tint-ok:rgba(8,127,91,.08);
    --tint-warn:rgba(138,90,6,.10);
    --tint-bad:rgba(192,38,73,.07);
    --tint-pro:rgba(124,58,237,.10);
    --edge-accent:rgba(43,82,200,.28);
    --edge-ok:rgba(8,127,91,.26);
    --edge-warn:rgba(138,90,6,.28);
    --edge-bad:rgba(192,38,73,.24);

    --shadow:0 1px 2px rgba(23,32,64,.06), 0 6px 18px rgba(40,30,110,.09);
    --shadow-l:0 18px 50px rgba(30,24,90,.18);
    --scroll-thumb:#c2cce4;
    --grid-dot:#d3dcf1;
    --wire:#8299c6;
    --node-edge:#a6b3d0;
    /* ---- The hues, for a WHITE page. Everything above this comment was
       re-tuned for light and these were not, so the colourful skin in light
       theme kept the DARK canvas's hues: #8fb0d8 copper and #5ee0d3
       cross-over sit at about 1.9:1 and 1.5:1 against --bg #f4f6fd, so a
       2px cable and a 15px icon were there, coloured, and invisible — which
       reads exactly like the colour never having been applied. Same hues,
       same meanings, darkened until a line holds its own against paper. */
    --area-accounts:#1b4ae0;
    --area-images:#8534e8;
    --area-monitor:#0e8f82;
    --area-market:#b45c08;
    --area-server:#4b5a7a;
    --area-community:#cf1668;
    --cable-copper:#2f5fa8;
    --cable-crossover:#0d8f85;
    --cable-fiber:#a9760f;
    --cable-serial:#7a2fe0;
    --cable-console:#5b6478;
    --cable-coax:#8a5530;
    --cable-wireless:#0b8f7a;

    --brand:#2b52c8;
    --brand-d:#1f3fa8;
    --green:#087f5b;
    --bg2:#edf1fb;
    --bg3:#e3e9f9;
    --card:#ffffff;
    --muted:#6b7699;
    --acbg:#edf1fb;
    --acbar:#141b33;
    --acline:#d8e0f2;
    --sb-brand:#2b52c8;
    --sb-brand-d:#1f3fa8;
    --sb-ink:#111830;
    --sb-ink2:#465170;
    --sb-ink3:#6b7699;
    --sb-card:#ffffff;
    --sb-line:#d8e0f2;
    --sb-field:#f9fbff;
  }
}
:root[data-skin="colour"][data-theme="light"]{
  --bg:#f4f6fd;
  --panel:#ffffff;
  --panel2:#edf1fb;
  --panel3:#e3e9f9;
  --line:#d8e0f2;
  --line2:#e6ebf7;

  --ink:#111830;
  --ink2:#465170;
  --ink3:#6b7699;
  --ink4:#95a0bd;

  --accent:#2b52c8;
  --accent2:#1f3fa8;
  --accent-ink:#ffffff;
  --ok:#087f5b;
  --warn:#8a5a06;
  --bad:#c02649;
  --idle:#78849f;
  --pro:#7c3aed;

  --h-route:#1d4ed8;
  --h-sec:#be123c;
  --h-host:#7e22ce;
  --h-svc:#0f766e;
  --h-data:#b45309;
  --h-learn:#be185d;

  --h-route-t:rgba(29,78,216,.09);   --h-route-e:rgba(29,78,216,.30);
  --h-sec-t:rgba(190,18,60,.08);     --h-sec-e:rgba(190,18,60,.28);
  --h-host-t:rgba(126,34,206,.08);   --h-host-e:rgba(126,34,206,.28);
  --h-svc-t:rgba(15,118,110,.08);    --h-svc-e:rgba(15,118,110,.28);
  --h-data-t:rgba(180,83,9,.09);     --h-data-e:rgba(180,83,9,.30);
  --h-learn-t:rgba(190,24,93,.08);   --h-learn-e:rgba(190,24,93,.28);

  --tint-accent:rgba(43,82,200,.08);
  --tint-ok:rgba(8,127,91,.08);
  --tint-warn:rgba(138,90,6,.10);
  --tint-bad:rgba(192,38,73,.07);
  --tint-pro:rgba(124,58,237,.10);
  --edge-accent:rgba(43,82,200,.28);
  --edge-ok:rgba(8,127,91,.26);
  --edge-warn:rgba(138,90,6,.28);
  --edge-bad:rgba(192,38,73,.24);

  --shadow:0 1px 2px rgba(23,32,64,.06), 0 6px 18px rgba(40,30,110,.09);
  --shadow-l:0 18px 50px rgba(30,24,90,.18);
  --scroll-thumb:#c2cce4;
  --grid-dot:#d3dcf1;
  --wire:#8299c6;
  --node-edge:#a6b3d0;
  /* ---- The hues, for a WHITE page. Everything above this comment was
     re-tuned for light and these were not, so the colourful skin in light
     theme kept the DARK canvas's hues: #8fb0d8 copper and #5ee0d3
     cross-over sit at about 1.9:1 and 1.5:1 against --bg #f4f6fd, so a
     2px cable and a 15px icon were there, coloured, and invisible — which
     reads exactly like the colour never having been applied. Same hues,
     same meanings, darkened until a line holds its own against paper. */
  --area-accounts:#1b4ae0;
  --area-images:#8534e8;
  --area-monitor:#0e8f82;
  --area-market:#b45c08;
  --area-server:#4b5a7a;
  --area-community:#cf1668;
  --cable-copper:#2f5fa8;
  --cable-crossover:#0d8f85;
  --cable-fiber:#a9760f;
  --cable-serial:#7a2fe0;
  --cable-console:#5b6478;
  --cable-coax:#8a5530;
  --cable-wireless:#0b8f7a;

  --brand:#2b52c8;
  --brand-d:#1f3fa8;
  --green:#087f5b;
  --bg2:#edf1fb;
  --bg3:#e3e9f9;
  --card:#ffffff;
  --muted:#6b7699;
  --acbg:#edf1fb;
  --acbar:#141b33;
  --acline:#d8e0f2;
  --sb-brand:#2b52c8;
  --sb-brand-d:#1f3fa8;
  --sb-ink:#111830;
  --sb-ink2:#465170;
  --sb-ink3:#6b7699;
  --sb-card:#ffffff;
  --sb-line:#d8e0f2;
  --sb-field:#f9fbff;
}

/* ---------------------------------------------------------------------------
   COLOURFUL · THE SURFACES THAT LEAD

   Tokens did the whole site. What is left is the handful of places where
   the intent is "this surface is the one to look at", which a colour token
   cannot say on its own — a gradient can.

   Restraint rule for everything below: a gradient goes on a surface a
   reader ACTS on (the header, a primary button, the current tab, a hero) or
   one they are meant to read first (a stat). It does not go on a surface
   they read THROUGH — body text, tables, panels full of prose — because a
   moving background under a paragraph is the fastest way to make a page
   tiring.
   --------------------------------------------------------------------------- */

/* The page itself: a wash rather than a flat fill, anchored at the top
   where the header and the first heading are, fading out before the
   content starts. Fixed, so it does not slide as the page scrolls — a
   gradient that scrolls reads as an object, and this is meant to read as
   light. */
[data-skin="colour"] body{
  background-color:var(--bg);
  background-image:
    radial-gradient(1100px 620px at 8% -14%, var(--h-route-t), transparent 60%),
    radial-gradient(900px 540px at 96% -8%, var(--h-host-t), transparent 58%);
  background-attachment:fixed;
  background-repeat:no-repeat;
}

/* The shared header. It is above everything and it is the one element on
   every single page, so it carries the palette's signature. */
[data-skin="colour"] .nb-head{
  background-image:linear-gradient(100deg,var(--h-route-t),transparent 42%,var(--h-host-t));
  border-bottom-color:var(--edge-accent);
}
/* A wordmark in two colours says the two halves are one product. */
[data-skin="colour"] .nb-brand b{
  background:linear-gradient(120deg,var(--accent),var(--h-host));
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:var(--accent);
}
[data-skin="colour"] .nb-nav a.on{
  background:linear-gradient(135deg,var(--accent),var(--h-route));
  box-shadow:0 2px 10px var(--h-route-t);
}
/* Hover fills, the way the front page's header has always filled. Taking
   the background away left the bar with no hover at all on this skin. */
[data-skin="colour"] .nb-nav a:hover{background:var(--h-route-t);color:var(--accent)}

/* Buttons. The primary one is the only button on a page that leads, so it
   is the only one that gets the gradient. */
/* Same split as the mono rule below: the shared header's button is shared,
   the applications' own buttons are theirs. This gradient was the last thing
   from a shared sheet still reaching a Real labs button, and it overrode
   lab.css's flat accent fill. */
[data-skin="colour"] .nb-btn.pri,
:root[data-skin="colour"]:not([data-app]):not([data-sim]) .btn.pri,
:root[data-skin="colour"]:not([data-app]):not([data-sim]) .b.pri{
  background-image:linear-gradient(135deg,var(--accent),var(--h-host));
  border-color:transparent;
  box-shadow:0 2px 12px var(--tint-accent);
}
/* The hover gradient that used to be here is gone: hover is a line under
   the button, and brightening the fill as well is two answers to one
   question. The resting gradient above stays. */

/* Cards get a coloured hairline along the top rather than a coloured
   border all the way round. A full coloured border on every card is a page
   of boxes shouting at once; a 2px cap reads as a spine and lets the card
   stay quiet. */
[data-skin="colour"] .nb-card,
[data-skin="colour"] .card,
[data-skin="colour"] .feat,
[data-skin="colour"] .plan,
[data-skin="colour"] .half{
  position:relative;overflow:hidden;
}
[data-skin="colour"] .nb-card::before,
[data-skin="colour"] .card::before,
[data-skin="colour"] .feat::before,
[data-skin="colour"] .plan::before,
[data-skin="colour"] .half::before{
  content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:linear-gradient(90deg,var(--accent),var(--h-host));
  opacity:.75;
}
/* Cards that already mean something keep that meaning and take its hue. */
[data-skin="colour"] .half.free::before{background:linear-gradient(90deg,var(--ok),var(--h-svc))}
[data-skin="colour"] .half.pro::before {background:linear-gradient(90deg,var(--pro),var(--h-learn))}
[data-skin="colour"] .plan.mark::before{background:linear-gradient(90deg,var(--h-data),var(--h-sec))}
[data-skin="colour"] .plan.mark{box-shadow:0 0 0 3px var(--tint-accent),var(--shadow)}

/* The feature grid on the home page: the icon tile is the one spot on a
   marketing page where colour does real work, because it is what the eye
   lands on before any heading. Cycled through the family hues in order, so
   the row reads as a spectrum rather than as six blue squares. */
[data-skin="colour"] .feat .ic{
  border-color:transparent;color:#fff;
  box-shadow:0 3px 12px rgba(0,0,0,.18);
}
[data-skin="colour"] .feat:nth-child(6n+1) .ic{background:linear-gradient(135deg,var(--h-route),var(--accent))}
[data-skin="colour"] .feat:nth-child(6n+2) .ic{background:linear-gradient(135deg,var(--h-svc),var(--ok))}
[data-skin="colour"] .feat:nth-child(6n+3) .ic{background:linear-gradient(135deg,var(--h-host),var(--pro))}
[data-skin="colour"] .feat:nth-child(6n+4) .ic{background:linear-gradient(135deg,var(--h-data),var(--warn))}
[data-skin="colour"] .feat:nth-child(6n+5) .ic{background:linear-gradient(135deg,var(--h-sec),var(--bad))}
[data-skin="colour"] .feat:nth-child(6n+6) .ic{background:linear-gradient(135deg,var(--h-learn),var(--h-host))}

/* Except where the tile holds the real artwork, which brings its own
   colour. There the hue moves to a soft wash and a matching edge, so the
   row still reads as a spectrum without putting a painted object on a
   painted ground. */
[data-skin="colour"] .feat .ic.art{box-shadow:none;border-width:1px;border-style:solid}
[data-skin="colour"] .feat:nth-child(6n+1) .ic.art{background:var(--h-route-t);border-color:var(--h-route-e)}
[data-skin="colour"] .feat:nth-child(6n+2) .ic.art{background:var(--h-svc-t);  border-color:var(--h-svc-e)}
[data-skin="colour"] .feat:nth-child(6n+3) .ic.art{background:var(--h-host-t); border-color:var(--h-host-e)}
[data-skin="colour"] .feat:nth-child(6n+4) .ic.art{background:var(--h-data-t); border-color:var(--h-data-e)}
[data-skin="colour"] .feat:nth-child(6n+5) .ic.art{background:var(--h-sec-t);  border-color:var(--h-sec-e)}
[data-skin="colour"] .feat:nth-child(6n+6) .ic.art{background:var(--h-learn-t);border-color:var(--h-learn-e)}

/* ---------------------------------------------------------------------------
   COLOURFUL · THE HEADER'S OWN ICONS

   These are the one set that deliberately does NOT become artwork. They
   are 17px controls, and detailed drawing at 17px is mud — the reason the
   stroke set exists at all. What they take instead is the hue of the thing
   they open, which is the part a reader actually uses them for: five grey
   glyphs in a row are five identical targets, and five coloured ones are
   five places.
   --------------------------------------------------------------------------- */
[data-skin="colour"] .nb-ask{color:var(--h-host);border-color:var(--h-host-e)}
[data-skin="colour"] .nb-ask:hover{background:var(--h-host-t);color:var(--h-host)}
[data-skin="colour"] .nb-bell{color:var(--h-data)}
[data-skin="colour"] .nb-bell:hover{background:var(--h-data-t);color:var(--h-data)}
[data-skin="colour"] .nb-bell .dot{background:var(--h-sec);box-shadow:0 0 0 2px var(--panel)}
[data-skin="colour"] #nbLang{color:var(--h-svc)}
[data-skin="colour"] #nbLang:hover{background:var(--h-svc-t);color:var(--h-svc)}
/* The sun warm, the moon cool — the one pair where the hue is the meaning
   and not a category. */
[data-skin="colour"] #nbTheme .sun{color:var(--h-data)}
[data-skin="colour"] #nbTheme .moon{color:var(--h-route)}
[data-skin="colour"] #nbTheme:hover{background:var(--tint-accent)}
[data-skin="colour"] .nb-search:focus-within{
  border-color:var(--accent);box-shadow:0 0 0 3px var(--tint-accent);
}

/* In black and white they go back to being five grey glyphs, which is the
   correct answer there and needs saying because the rules above would
   otherwise be inherited by nothing and the dot would stay red. */
[data-skin="mono"] .nb-bell .dot{background:var(--ink);box-shadow:0 0 0 2px var(--panel)}

/* The hero. A heavier wash than the page behind it, and the emphasised
   words in the heading take the gradient — the one piece of type on the
   site that does, because it is the one piece nobody reads twice. */
[data-skin="colour"] .hero{
  background-image:
    radial-gradient(900px 460px at 12% -20%, var(--h-route-t), transparent 62%),
    radial-gradient(760px 420px at 88% -10%, var(--h-host-t), transparent 60%),
    radial-gradient(600px 320px at 50% 110%, var(--h-svc-t), transparent 70%);
  border-bottom-color:var(--edge-accent);
}
[data-skin="colour"] .hero h1 em{
  background:linear-gradient(110deg,var(--accent),var(--h-host) 55%,var(--h-learn));
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;
}

/* ---------------------------------------------------------------------------
   COLOURFUL · THE CONSOLES

   The admin portal, the company portal and the lab all show the same three
   shapes: a row of stats, a tab strip, and a table. They are what an
   administrator looks at all day, and they are what this whole skin was
   asked to look like, so they get the most attention.
   --------------------------------------------------------------------------- */

/* A stat is a number somebody scans. Giving each one its own hue turns a
   row of four identical boxes into four things that can be found by
   colour, and the number itself carries it so the card stays calm. */
[data-skin="colour"] .stat{
  position:relative;overflow:hidden;
  background-image:linear-gradient(135deg,var(--h-route-t),transparent 70%);
  border-color:var(--h-route-e);
}
[data-skin="colour"] .stat b{color:var(--h-route)}
[data-skin="colour"] .stat:nth-child(4n+2){
  background-image:linear-gradient(135deg,var(--h-svc-t),transparent 70%);
  border-color:var(--h-svc-e);
}
[data-skin="colour"] .stat:nth-child(4n+2) b{color:var(--h-svc)}
[data-skin="colour"] .stat:nth-child(4n+3){
  background-image:linear-gradient(135deg,var(--h-host-t),transparent 70%);
  border-color:var(--h-host-e);
}
[data-skin="colour"] .stat:nth-child(4n+3) b{color:var(--h-host)}
[data-skin="colour"] .stat:nth-child(4n+4){
  background-image:linear-gradient(135deg,var(--h-data-t),transparent 70%);
  border-color:var(--h-data-e);
}
[data-skin="colour"] .stat:nth-child(4n+4) b{color:var(--h-data)}

/* The tab strips — the portal's, the company portal's. The rail under the
   strip becomes a gradient so the whole row belongs together, and the
   current tab sits on a filled tint rather than only a coloured word. */
[data-skin="colour"] .portal,
[data-skin="colour"] .op-tabs{
  border-bottom-color:transparent;
  background-image:linear-gradient(90deg,var(--edge-accent),var(--h-host-e),transparent);
  background-size:100% 1px;background-repeat:no-repeat;background-position:bottom;
}
[data-skin="colour"] .portal button.on,
[data-skin="colour"] .op-tabs button.on{
  background:var(--tint-accent);
  border-radius:var(--r-sm) var(--r-sm) 0 0;
  border-bottom-color:var(--accent);
}
[data-skin="colour"] .portal button .c{
  background:var(--tint-accent);color:var(--accent);
}

/* Tables. The header row is the only part that takes colour — the rows
   themselves stay plain, because a table is read through. */
[data-skin="colour"] .t th,
[data-skin="colour"] table.tbl th{
  background-image:linear-gradient(180deg,var(--tint-accent),transparent);
  color:var(--accent);
  border-bottom-color:var(--edge-accent);
}
[data-skin="colour"] .t tr.sel td{background:var(--tint-accent)}

/* The activity trail already colours the verb by severity. Under this skin
   the whole row takes a tinted spine, which is what makes a failure findable
   by scrolling rather than by reading. */
[data-skin="colour"] .trail .row{border-left:3px solid transparent;padding-left:9px}
[data-skin="colour"] .trail .row.bad {border-left-color:var(--bad); background:var(--tint-bad)}
[data-skin="colour"] .trail .row.warn{border-left-color:var(--warn);background:var(--tint-warn)}
[data-skin="colour"] .trail .row.ok  {border-left-color:var(--ok);  background:var(--tint-ok)}
[data-skin="colour"] .trail .row.info{border-left-color:var(--accent)}

/* Pills and badges: saturated, and each one filled rather than tinted,
   because a badge is a label and a label should be legible at a glance
   from across a desk. */
[data-skin="colour"] .nb-badge.free{background:linear-gradient(135deg,var(--ok),var(--h-svc));color:#fff}
[data-skin="colour"] .nb-badge.pro{background:linear-gradient(135deg,var(--pro),var(--h-learn));color:#fff}
[data-skin="colour"] .nb-badge.enterprise{background:linear-gradient(135deg,var(--h-route),var(--h-host));color:#fff}
[data-skin="colour"] .nb-badge.admin{background:linear-gradient(135deg,var(--accent),var(--h-route));color:#fff}
[data-skin="colour"] .pill.adm{background-image:linear-gradient(135deg,var(--accent),var(--h-host));border-color:transparent}

/* The administrator's standing notice. It was already green; here it gets
   the gradient, because "you are looking at every account on this server"
   is worth being unmissable. */
[data-skin="colour"] .adminbar{
  background-image:linear-gradient(100deg,var(--tint-ok),var(--h-svc-t) 60%,transparent);
  border-color:var(--edge-ok);
}

/* Notes keep their four meanings and gain a left rule, which is what makes
   them scannable when several sit on one page. */
[data-skin="colour"] .nb-note{border-left-width:3px}
[data-skin="colour"] .nb-note.info{border-left-color:var(--accent)}
[data-skin="colour"] .nb-note.ok  {border-left-color:var(--ok)}
[data-skin="colour"] .nb-note.warn{border-left-color:var(--warn)}
[data-skin="colour"] .nb-note.bad {border-left-color:var(--bad)}

/* Progress, wherever it appears — an upload, a disk, a plan's usage. */
[data-skin="colour"] .prog i{background-image:linear-gradient(90deg,var(--accent),var(--h-host))}

/* ---------------------------------------------------------------------------
   COLOURFUL · THE TEACHING HALF

   Its tokens moved with the rest, above. These are the few rules it writes
   in raw hex, which no token can reach — the banner, the tags, the marks.
   --------------------------------------------------------------------------- */
[data-skin="colour"] .banner,
[data-theme="dark"][data-skin="colour"] .banner{
  background-image:linear-gradient(120deg,var(--accent),var(--h-host) 52%,var(--h-learn));
}
[data-skin="colour"] .tag.brand {background:var(--h-route-t);border-color:var(--h-route-e);color:var(--h-route)}
[data-skin="colour"] .tag.green {background:var(--h-svc-t);  border-color:var(--h-svc-e);  color:var(--h-svc)}
[data-skin="colour"] .tag.accent{background:var(--h-data-t); border-color:var(--h-data-e); color:var(--h-data)}
[data-skin="colour"] .rep       {background:var(--h-data-t); border-color:var(--h-data-e); color:var(--h-data)}
[data-skin="colour"] .rep.silver{background:var(--panel2);   border-color:var(--line);     color:var(--ink2)}
[data-skin="colour"] .rep.gold  {background:var(--h-data-t); border-color:var(--h-data-e); color:var(--warn)}

/* ---------------------------------------------------------------------------
   COLOURFUL · THE LAB CANVAS

   The canvas is drawn in JavaScript from the tokens above, so it already
   followed. These are its chrome: the shelf, the node, the link.
   --------------------------------------------------------------------------- */
[data-skin="colour"] .ntype:hover{border-color:var(--h-route-e);background:var(--h-route-t)}
[data-skin="colour"] .ntype.pick{border-color:var(--accent);background:var(--tint-accent)}


/* ###########################################################################
   #                                                                         #
   #   BLACK AND WHITE                                                       #
   #                                                                         #
   ########################################################################### */

/* ---------------------------------------------------------------------------
   The hard part of a black and white theme is not removing the colour. It
   is that this product SAYS things with colour — a node is green because it
   is running, a row is red because a boot failed, a badge is violet because
   the plan is Pro. Strip the hue and those distinctions vanish, and the
   reader is worse off than before.

   So every state that was a hue becomes a difference in LUMINANCE and in
   WEIGHT instead, and the gap between the levels is deliberately wide:

       ok    the brightest ink on the page, bold
       warn  mid, bold, on a tinted fill
       bad   brightest again but inverted — light ink on a dark fill, the
             only inversion in the theme, so a failure is still the thing
             you see first from across the room
       idle  the dimmest ink, regular weight

   That is also why this is worth shipping beyond taste: it is the only
   theme on the site that a reader with no colour vision at all loses
   nothing to, and it is the one that survives a photocopier and a bad
   projector.
   --------------------------------------------------------------------------- */

/* MONO · DARK — pure neutrals, no hue anywhere, and a slightly wider
   spread between the surfaces than the colour palettes need, because
   lightness is now doing all of the separating on its own. */
:root[data-skin="mono"]{
  --bg:#0c0c0d;
  --panel:#171718;
  --panel2:#121213;
  --panel3:#212123;
  --line:#333336;
  --line2:#262628;

  --ink:#f2f2f3;
  --ink2:#b4b4b8;
  --ink3:#85858b;
  --ink4:#5d5d63;

  --accent:#e8e8ea;
  --accent2:#ffffff;
  --accent-ink:#0c0c0d;
  --ok:#ffffff;
  --warn:#c8c8cc;
  --bad:#ffffff;
  --idle:#75757b;
  --pro:#d6d6da;

  /* Black and white means black and white: the areas are told apart by
     their words and their shapes here, not by a hue. */
  --area-accounts:#e8e8ea;
  --area-images:#d6d6da;
  --area-monitor:#c8c8cc;
  --area-market:#b4b4b8;
  --area-server:#9a9aa0;
  --area-community:#d6d6da;

  /* Black and white: cables are told apart by their dashes and their
     weight here, which is what this skin is for. */
  --cable-copper:#b4b4b8;
  --cable-crossover:#d6d6da;
  --cable-fiber:#e8e8ea;
  --cable-serial:#c8c8cc;
  --cable-console:#85858b;
  --cable-coax:#9a9aa0;
  --cable-wireless:#d6d6da;

  --tint-accent:rgba(255,255,255,.10);
  --tint-ok:rgba(255,255,255,.09);
  --tint-warn:rgba(255,255,255,.07);
  --tint-bad:rgba(255,255,255,.14);
  --tint-pro:rgba(255,255,255,.10);
  --edge-accent:rgba(255,255,255,.34);
  --edge-ok:rgba(255,255,255,.34);
  --edge-warn:rgba(255,255,255,.26);
  --edge-bad:rgba(255,255,255,.55);

  --term-bg:#000000;
  --term-ink:#dcdcde;

  --shadow:0 1px 2px rgba(0,0,0,.5), 0 4px 14px rgba(0,0,0,.4);
  --shadow-l:0 16px 44px rgba(0,0,0,.66);
  --scroll-thumb:#3c3c40;
  --grid-dot:#2b2b2e;
  --wire:#6e6e74;
  --node-edge:#7d7d84;

  --brand:#f2f2f3;
  --brand-d:#ffffff;
  --green:#e2e2e5;
  --bg2:#121213;
  --bg3:#212123;
  --card:#171718;
  --muted:#85858b;
  --code:#000000;
  --code-ink:#dcdcde;
  --acbg:#0c0c0d;
  --acbar:#000000;
  --acline:#333336;
  --sb-brand:#f2f2f3;
  --sb-brand-d:#ffffff;
  --sb-ink:#f2f2f3;
  --sb-ink2:#b4b4b8;
  --sb-ink3:#85858b;
  --sb-card:#171718;
  --sb-line:#333336;
  --sb-field:#121213;
}

/* MONO · LIGHT — black on paper. The one place the four state levels
   invert: on white the brightest ink is the darkest, so ok/bad become
   near-black and idle becomes the palest grey. */
@media (prefers-color-scheme: light){
  :root[data-skin="mono"]:not([data-theme="dark"]){
    --bg:#f7f7f8;
    --panel:#ffffff;
    --panel2:#f0f0f1;
    --panel3:#e6e6e8;
    --line:#d9d9dd;
    --line2:#e8e8ea;

    --ink:#0d0d0e;
    --ink2:#44444a;
    --ink3:#6c6c73;
    --ink4:#9a9aa1;

    --accent:#111113;
    --accent2:#000000;
    --accent-ink:#ffffff;
    --ok:#000000;
    --warn:#3c3c41;
    --bad:#000000;
    --idle:#8e8e95;
    --pro:#2a2a2e;

    --tint-accent:rgba(0,0,0,.06);
    --tint-ok:rgba(0,0,0,.05);
    --tint-warn:rgba(0,0,0,.08);
    --tint-bad:rgba(0,0,0,.11);
    --tint-pro:rgba(0,0,0,.07);
    --edge-accent:rgba(0,0,0,.26);
    --edge-ok:rgba(0,0,0,.30);
    --edge-warn:rgba(0,0,0,.22);
    --edge-bad:rgba(0,0,0,.52);

    --shadow:0 1px 2px rgba(0,0,0,.07), 0 4px 14px rgba(0,0,0,.07);
    --shadow-l:0 16px 44px rgba(0,0,0,.18);
    --scroll-thumb:#c4c4c9;
    --grid-dot:#dcdce0;
    --wire:#9a9aa1;
    --node-edge:#b4b4ba;
    /* ---- The greys, for a WHITE page. Black and white tells cables apart by
       LIGHTNESS, so the ramp has to turn over with the paper: on the dark
       canvas fibre is the lightest grey because that is what stands out
       there, and here it is the darkest. Left un-turned, every cable was a
       near-white line on a near-white page. The order of prominence is the
       one the dark skin already set. */
    --area-accounts:#2c2c30;
    --area-images:#3f3f45;
    --area-monitor:#53535a;
    --area-market:#67676e;
    --area-server:#7a7a81;
    --area-community:#3f3f45;
    --cable-copper:#5a5a61;
    --cable-crossover:#3f3f45;
    --cable-fiber:#2c2c30;
    --cable-serial:#4c4c53;
    --cable-console:#86868c;
    --cable-coax:#6e6e75;
    --cable-wireless:#3f3f45;

    --brand:#111113;
    --brand-d:#000000;
    --green:#1c1c1f;
    --bg2:#f0f0f1;
    --bg3:#e6e6e8;
    --card:#ffffff;
    --muted:#6c6c73;
    --acbg:#f0f0f1;
    --acbar:#0d0d0e;
    --acline:#d9d9dd;
    --sb-brand:#111113;
    --sb-brand-d:#000000;
    --sb-ink:#0d0d0e;
    --sb-ink2:#44444a;
    --sb-ink3:#6c6c73;
    --sb-card:#ffffff;
    --sb-line:#d9d9dd;
    --sb-field:#fbfbfc;
  }
}
:root[data-skin="mono"][data-theme="light"]{
  --bg:#f7f7f8;
  --panel:#ffffff;
  --panel2:#f0f0f1;
  --panel3:#e6e6e8;
  --line:#d9d9dd;
  --line2:#e8e8ea;

  --ink:#0d0d0e;
  --ink2:#44444a;
  --ink3:#6c6c73;
  --ink4:#9a9aa1;

  --accent:#111113;
  --accent2:#000000;
  --accent-ink:#ffffff;
  --ok:#000000;
  --warn:#3c3c41;
  --bad:#000000;
  --idle:#8e8e95;
  --pro:#2a2a2e;

  --tint-accent:rgba(0,0,0,.06);
  --tint-ok:rgba(0,0,0,.05);
  --tint-warn:rgba(0,0,0,.08);
  --tint-bad:rgba(0,0,0,.11);
  --tint-pro:rgba(0,0,0,.07);
  --edge-accent:rgba(0,0,0,.26);
  --edge-ok:rgba(0,0,0,.30);
  --edge-warn:rgba(0,0,0,.22);
  --edge-bad:rgba(0,0,0,.52);

  --shadow:0 1px 2px rgba(0,0,0,.07), 0 4px 14px rgba(0,0,0,.07);
  --shadow-l:0 16px 44px rgba(0,0,0,.18);
  --scroll-thumb:#c4c4c9;
  --grid-dot:#dcdce0;
  --wire:#9a9aa1;
  --node-edge:#b4b4ba;
  /* ---- The greys, for a WHITE page. Black and white tells cables apart by
     LIGHTNESS, so the ramp has to turn over with the paper: on the dark
     canvas fibre is the lightest grey because that is what stands out
     there, and here it is the darkest. Left un-turned, every cable was a
     near-white line on a near-white page. The order of prominence is the
     one the dark skin already set. */
  --area-accounts:#2c2c30;
  --area-images:#3f3f45;
  --area-monitor:#53535a;
  --area-market:#67676e;
  --area-server:#7a7a81;
  --area-community:#3f3f45;
  --cable-copper:#5a5a61;
  --cable-crossover:#3f3f45;
  --cable-fiber:#2c2c30;
  --cable-serial:#4c4c53;
  --cable-console:#86868c;
  --cable-coax:#6e6e75;
  --cable-wireless:#3f3f45;

  --brand:#111113;
  --brand-d:#000000;
  --green:#1c1c1f;
  --bg2:#f0f0f1;
  --bg3:#e6e6e8;
  --card:#ffffff;
  --muted:#6c6c73;
  --acbg:#f0f0f1;
  --acbar:#0d0d0e;
  --acline:#d9d9dd;
  --sb-brand:#111113;
  --sb-brand-d:#000000;
  --sb-ink:#0d0d0e;
  --sb-ink2:#44444a;
  --sb-ink3:#6c6c73;
  --sb-card:#ffffff;
  --sb-line:#d9d9dd;
  --sb-field:#fbfbfc;
}

/* ---------------------------------------------------------------------------
   MONO · KEEPING THE MEANING

   Weight and fill, standing in for the hue that is gone.
   --------------------------------------------------------------------------- */
[data-skin="mono"] .nb-note.ok,
[data-skin="mono"] .msg.ok,
[data-skin="mono"] .trail .row.ok .what b{font-weight:700}
[data-skin="mono"] .nb-note.warn,
[data-skin="mono"] .trail .row.warn .what b{font-weight:700}

/* The one inversion in the theme. A failure has to survive being glanced
   at, and on a page with no colour the only thing louder than bold is
   reversed out. */
[data-skin="mono"] .nb-note.bad,
[data-skin="mono"] .msg.bad{
  background:var(--ink);color:var(--bg);border-color:var(--ink);font-weight:600;
}
[data-skin="mono"] .nb-note.bad code{background:rgba(255,255,255,.18)}
[data-skin="mono"] .trail .row.bad{border-left:3px solid var(--ink);padding-left:9px}
[data-skin="mono"] .trail .row.bad .what b{font-weight:700;text-decoration:underline;
  text-underline-offset:2px}

/* Badges lose their fills and become outlines, which is what keeps five of
   them in one row from turning into five grey blocks. The plan a reader is
   on stays filled, because that one is a statement rather than a label. */
[data-skin="mono"] .nb-badge{
  background:transparent;border:1px solid var(--line);color:var(--ink2);
}
[data-skin="mono"] .nb-badge.pro,
[data-skin="mono"] .nb-badge.enterprise{
  background:var(--ink);color:var(--bg);border-color:var(--ink);
}
[data-skin="mono"] .pill.adm{background:var(--ink);color:var(--bg);border-color:var(--ink)}
[data-skin="mono"] .pill.off{text-decoration:line-through;color:var(--ink3)}

/* Every gradient in the product, off. These are the ones written in raw hex
   in a page's own <style>, where a token cannot reach them. */
[data-skin="mono"] .hero,
[data-skin="mono"] body,
[data-skin="mono"] .nb-head,
[data-skin="mono"] .banner,
[data-skin="mono"] .sheen,
[data-skin="mono"] .wrap::before{
  background-image:none !important;
}
/* `.nb-btn` is the SHARED header's button and stays shared — the header is
   the same component on every page, including inside the two applications.
   `.btn` and `.b` are the applications' own, so they are excluded there:
   Real labs styles its buttons from its own stylesheet, and the fact that
   this rule happened to set the same three values is not a reason to leave
   it reaching in. */
[data-skin="mono"] .nb-btn.pri,
:root[data-skin="mono"]:not([data-app]):not([data-sim]) .btn.pri,
:root[data-skin="mono"]:not([data-app]):not([data-sim]) .b.pri{
  background:var(--accent);color:var(--accent-ink);border-color:var(--accent);
  background-image:none;
}
[data-skin="mono"] .prog i{background:var(--ink)}

/* Artwork. The device drawings, the logos and any screenshot carry their
   own colour inside the file, so a token cannot touch them — a filter can.
   It is applied to the images themselves and never to a page-level
   container: `filter` makes an element the containing block for anything
   `position:fixed` inside it, which would leave the header, the drawers and
   every dialog scrolling away with the page. */
[data-skin="mono"] svg.nbart,
[data-skin="mono"] .op-logo img,
[data-skin="mono"] .nb-av-btn img,
[data-skin="mono"] .logoprev img,
[data-skin="mono"] .shot img,
[data-skin="mono"] figure img{
  filter:grayscale(1) contrast(1.06);
}

/* The last of the hue, in the places a token cannot reach it: rules in
   theme.css and in a page's own <style> that fill with a literal rgba()
   rather than with a tint token. There are not many, and they are listed
   one by one rather than swept up with a filter, because a filter over a
   container would take the fixed header and every dialog with it. */
[data-skin="mono"] .nb-note.ok,
[data-skin="mono"] .nb-note.warn{color:var(--ink)}
[data-skin="mono"] .adminbar{color:var(--ink2);background:var(--panel2);border-color:var(--line)}
[data-skin="mono"] .adminbar b{color:var(--ink)}
[data-skin="mono"] .nb-nav a .tag.pro{background:var(--panel3);color:var(--ink2)}
[data-skin="mono"] .nb-nav a.on .tag{background:rgba(255,255,255,.22)}
[data-skin="mono"] .t tr.sel td{background:var(--tint-accent)}
[data-skin="mono"] .badge.admin{background:var(--panel3);color:var(--ink)}
[data-skin="mono"] .msg.ok{background:var(--panel2);color:var(--ink);border:1px solid var(--line)}
[data-skin="mono"] .nb-signin .err{color:var(--ink);background:var(--panel2);border-left-color:var(--ink)}

/* Colourful wants the same two through the family hues rather than the
   accent, since both name a plan and a plan is not a state. */
[data-skin="colour"] .nb-nav a .tag.pro{background:var(--h-host-t);color:var(--pro)}
[data-skin="colour"] .badge.admin{background:var(--h-svc-t);color:var(--h-svc)}

/* ---------------------------------------------------------------------------
   MONO · THE TEACHING HALF'S FILLED CONTROLS

   Every one of these is written the same way in that half's stylesheet:
   `background:var(--brand); color:#fff`. That works in all four of the
   other palettes, where --brand is a mid-tone blue. It does not work here,
   because black and white has no mid-tone to spare: --brand is near-white
   on the dark palette so that a link can be read, which leaves white type
   on a white button.

   The token cannot be moved to fix it — the same --brand is a link colour
   on the page background, and the value that reads there is exactly the
   value that fails inside a button. So the ink is corrected instead, and
   these have to be listed by name because the rule that sets #fff is
   inside a page and cannot be reached any other way.
   --------------------------------------------------------------------------- */
[data-skin="mono"] .btn-primary,
[data-skin="mono"] .btn-green,
[data-skin="mono"] .btn-nav,
[data-skin="mono"] .btn-p,
[data-skin="mono"] .btn-g,
[data-skin="mono"] .pbtn.on,
[data-skin="mono"] .chip.on,
[data-skin="mono"] .cat.on,
[data-skin="mono"] .tbtn.on,
[data-skin="mono"] .modesw button.on,
[data-skin="mono"] .lnav a.on,
[data-skin="mono"] .dashnav a.on,
[data-skin="mono"] .acnav a.on,
[data-skin="mono"] .savemark.on,
[data-skin="mono"] .fchip,
[data-skin="mono"] .ribbon,
[data-skin="mono"] .win-t,
[data-skin="mono"] .billsw .save,
/* --accent is a THIRD token of the same name: the teaching half's amber,
   which this skin has already turned to ink. The same white-on-white. */
[data-skin="mono"] .btn-accent,
[data-skin="mono"] .dd-menu .cnt,
[data-skin="mono"] .badge.got .ico{
  background:var(--ink);border-color:var(--ink);color:var(--bg);
}
[data-skin="mono"] .savemark:hover{background:var(--panel3);color:var(--ink)}
/* Its background is a gradient, so `background-image:none` above would
   leave it transparent and print white on white in the light palette. */
[data-skin="mono"] .banner{background:var(--ink);color:var(--bg)}
[data-skin="mono"] .banner a{color:var(--bg)}

/* The lesson and lab cover art is an SVG the page generates from an hsl()
   hue, so no token touches it. An SVG is safe to filter — the caution
   about `filter` applies to anything that could contain a fixed element,
   and a picture cannot. */
[data-skin="mono"] svg.cover{filter:grayscale(1) contrast(1.04)}
/* A generated avatar picks its two colours from a hash of the person's
   name, which is the point of it — so there is no token to move, and the
   contributor list would otherwise be the one row of colour left on the
   page. Greyscaled, the hash still gives each person a distinct tone. */
[data-skin="mono"] svg.avatar{filter:grayscale(1) contrast(1.08)}
/* The wordmark's tile. Its navy and its blue are attributes on the SVG
   shapes, written once in shell.js and once in the sign-in page, so there
   is nothing to redefine — the mark is a logo and it is allowed to be
   literal. In a theme with no colour it is the last blue on the page. */
[data-skin="mono"] .nb-brand svg,
[data-skin="mono"] .mark svg,
[data-skin="mono"] .nb-signin .mark svg{filter:grayscale(1) contrast(1.1)}

/* The teaching half's hand-written decorations. */
[data-skin="mono"] .tag.brand,
[data-skin="mono"] .tag.green,
[data-skin="mono"] .tag.accent,
[data-skin="mono"] .rep,
[data-skin="mono"] .rep.silver,
[data-skin="mono"] .rep.gold{
  background:transparent;border-color:var(--line);color:var(--ink2);
}
[data-skin="mono"] mark{background:var(--panel3);color:var(--ink)}

/* A console in black and white is a console that was always nearly there.
   Its scheme is written in the terminal's own palette, in JavaScript, and
   term.js reads this class rather than the attribute. */
[data-skin="mono"] .term,
[data-skin="mono"] .console{filter:grayscale(1)}


/* ###########################################################################
   #   THE SIGN-IN PAGE                                                      #
   ########################################################################### */

/* login.html is the one page that does not take its palette from the design
   system. That is deliberate and it is explained in the page: a sign-in is
   the first thing anybody sees and it is meant to look like the company
   rather than like the tool, so it carries its own type and its own
   `--sb-*` tokens, in its own <style>.

   Which is exactly why the blocks above do not reach it. The page writes
   `:root[data-theme="dark"]{--sb-brand:…}` — the same specificity as
   `:root[data-skin="mono"]`, in a stylesheet that loads AFTER this one, so
   it wins the tie on order and the skin does nothing. Left there, choosing
   black and white and then signing out lands the reader on the one navy
   page on the site.

   `html:root[…]` rather than `:root[…]`: adding the element selector takes
   these to a specificity the page cannot match by accident, which is the
   right relationship — a reader's explicit choice of palette should outrank
   a page's opinion about its own colours. It is scoped to the six tokens
   that page owns and reaches nothing else. */
html:root[data-skin="colour"]{
  --sb-brand:#5b8def; --sb-brand-d:#7ba7f7;
  --sb-ink:#eef2fc;   --sb-ink2:#a4b1d4; --sb-ink3:#7583ab;
  --sb-bg:#0a0f1f;    --sb-card:#141b33; --sb-line:#2a3357; --sb-field:#111730;
}
@media (prefers-color-scheme: light){
  html:root[data-skin="colour"]:not([data-theme="dark"]){
    --sb-brand:#2b52c8; --sb-brand-d:#1f3fa8;
    --sb-ink:#111830;   --sb-ink2:#465170; --sb-ink3:#6b7699;
    --sb-bg:#f4f6fd;    --sb-card:#ffffff; --sb-line:#d8e0f2; --sb-field:#f9fbff;
  }
}
html:root[data-skin="colour"][data-theme="light"]{
  --sb-brand:#2b52c8; --sb-brand-d:#1f3fa8;
  --sb-ink:#111830;   --sb-ink2:#465170; --sb-ink3:#6b7699;
  --sb-bg:#f4f6fd;    --sb-card:#ffffff; --sb-line:#d8e0f2; --sb-field:#f9fbff;
}
html:root[data-skin="mono"]{
  --sb-brand:#f2f2f3; --sb-brand-d:#ffffff;
  --sb-ink:#f2f2f3;   --sb-ink2:#b4b4b8; --sb-ink3:#85858b;
  --sb-bg:#0c0c0d;    --sb-card:#171718; --sb-line:#333336; --sb-field:#121213;
}
@media (prefers-color-scheme: light){
  html:root[data-skin="mono"]:not([data-theme="dark"]){
    --sb-brand:#111113; --sb-brand-d:#000000;
    --sb-ink:#0d0d0e;   --sb-ink2:#44444a; --sb-ink3:#6c6c73;
    --sb-bg:#f7f7f8;    --sb-card:#ffffff; --sb-line:#d9d9dd; --sb-field:#fbfbfc;
  }
}
html:root[data-skin="mono"][data-theme="light"]{
  --sb-brand:#111113; --sb-brand-d:#000000;
  --sb-ink:#0d0d0e;   --sb-ink2:#44444a; --sb-ink3:#6c6c73;
  --sb-bg:#f7f7f8;    --sb-card:#ffffff; --sb-line:#d9d9dd; --sb-field:#fbfbfc;
}

/* The wash behind the card needs no rule of its own: this page paints it
   with a plain `body{}`, which the site-wide wash further up already
   outranks, and --sb-bg is set to the same value as --bg above so the two
   agree on the colour underneath it.

   The button does need one. */
[data-skin="colour"] .go{
  background-image:linear-gradient(135deg,var(--accent),var(--h-host));
}
/* `.go` hover: see above — the line does it, not the fill. */
/* The same white-on-white as the teaching half's buttons, for the same
   reason: `.go` fills with --sb-brand and prints #fff over it. */
[data-skin="mono"] .go,
[data-skin="mono"] .nb-signin .go{
  background:var(--ink);color:var(--bg);background-image:none;
}
/* No hover fill here either — black and white shows it with the line. */

/* ###########################################################################
   #   THE CONTROL                                                           #
   ########################################################################### */

/* The theme menu in the shared header. It is one control for two choices —
   the look and the mode — because they are independent and a single flat
   list of every combination is six lines the reader has to parse instead of
   two they can. shell.js builds it; this draws it. */
.nb-thememenu{
  display:none;position:absolute;right:0;top:calc(100% + 8px);z-index:140;
  min-width:232px;padding:10px;border-radius:var(--r);
  background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow-l);
}
.nb-thememenu.on{display:block}
.nb-themewrap{position:relative;display:inline-flex}
.nb-thememenu .grp{
  font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);margin:8px 4px 6px;
}
.nb-thememenu .grp:first-child{margin-top:2px}
.nb-thememenu .row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:5px}
.nb-thememenu .row.looks{grid-template-columns:1fr}
.nb-thememenu button{
  display:flex;align-items:center;gap:9px;width:100%;padding:8px 10px;
  border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--panel2);color:var(--ink2);
  font:inherit;font-size:12.5px;text-align:left;cursor:pointer;
}
.nb-thememenu .row:not(.looks) button{justify-content:center;gap:0;padding:7px 4px;font-size:12px}
.nb-thememenu button:hover{border-color:var(--accent);color:var(--ink)}
.nb-thememenu button.on{
  border-color:var(--accent);color:var(--ink);background:var(--tint-accent);font-weight:600;
}
/* A swatch, so the choice is shown rather than only named. Each is painted
   in the palette it selects — which is why they are literal colours here
   and not tokens: a swatch that followed the current theme would show the
   reader three copies of what they are already looking at. */
.nb-thememenu .sw{
  flex:0 0 auto;width:26px;height:16px;border-radius:4px;
  border:1px solid rgba(128,128,128,.45);
}
/* Classic has to show its blue, or it and black-and-white are the same
   two-tone chip and the swatch tells the reader nothing. */
.nb-thememenu .sw.classic{background:linear-gradient(135deg,#0b1220 0 38%,#4b7fd6 38% 62%,#f5f7fb 62%)}
.nb-thememenu .sw.colour {background:linear-gradient(135deg,#3b82f6,#a855f7 55%,#14b8a6)}
.nb-thememenu .sw.mono   {background:linear-gradient(90deg,#000 0 34%,#8a8a8a 34% 67%,#fff 67%)}



/* ###########################################################################
   #                                                                         #
   #   DEPTH                                                                 #
   #                                                                         #
   ###########################################################################

   The polish, for all three looks rather than a fourth one.

   This began as a separate skin. It should not have been: "cards that sit
   in front of the page" and "a running device you can see across a room"
   are not a taste, they are how the product should have looked in every
   palette — and making them a fourth entry in the menu meant two of the
   three looks people actually use never got them.

   So the effects live here, unscoped, and each look supplies the colours
   they are drawn in through the tokens below. Classic stays quiet, colourful
   stays colourful, black-and-white stays black and white; all three gain a
   front and a back.

   FOUR THINGS, and each earns its place:

     1. AN AMBIENT GROUND — two very wide, very dim washes on the body
        instead of one flat fill. Barely visible, and the single biggest
        reason a screen reads as designed rather than assembled: a flat
        background has no light source, so nothing on it can be lit.

     2. LAYERED SURFACES — a hairline of light along each card's top edge
        and a real shadow beneath. That pair is what the eye reads as "this
        is in front of that".

     3. GRADIENTS THAT LEAD — only on the primary button, the brand mark and
        the paid badge. A gradient on everything is a gradient on nothing.

     4. LIGHT THAT MEANS SOMETHING — a running device glows, a failed one
        glows red, a starting one pulses. On a screen somebody watches out
        of the corner of their eye while typing at a console, that is the
        whole status display.

   REVERTING is still one line: every rule here is in themes.css, and
   removing its <link> returns the site to theme.css exactly as written.
   Motion is off under prefers-reduced-motion at the end of the section.
   --------------------------------------------------------------------------- */

/* ---- classic, dark (the default palette) */
:root{
  --lift:rgba(255,255,255,.06);
  --lift2:rgba(255,255,255,.03);
  --glass:rgba(17,26,43,.72);
  --grad:linear-gradient(135deg,#4b7fd6,#6d9ae6 60%,#3fb9d4);
  --glow:0 0 0 3px rgba(75,127,214,.22);
  --glow-ok:0 0 10px rgba(52,211,153,.5);
  --glow-bad:0 0 10px rgba(248,113,113,.5);
  --wash1:rgba(75,127,214,.10);
  --wash2:rgba(109,154,230,.07);
}
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]){
    --lift:rgba(255,255,255,.9);
    --lift2:rgba(255,255,255,.6);
    --glass:rgba(255,255,255,.82);
    --glow:0 0 0 3px rgba(43,92,187,.18);
    --glow-ok:0 0 10px rgba(15,157,98,.34);
    --glow-bad:0 0 10px rgba(210,63,63,.34);
    --wash1:rgba(43,92,187,.06);
    --wash2:rgba(75,127,214,.04);
  }
}
:root[data-theme="light"]{
  --lift:rgba(255,255,255,.9);
  --lift2:rgba(255,255,255,.6);
  --glass:rgba(255,255,255,.82);
  --glow:0 0 0 3px rgba(43,92,187,.18);
  --glow-ok:0 0 10px rgba(15,157,98,.34);
  --glow-bad:0 0 10px rgba(210,63,63,.34);
  --wash1:rgba(43,92,187,.06);
  --wash2:rgba(75,127,214,.04);
}

/* ---- colourful. The washes take the two hues the skin already leads with,
   so the ground belongs to the palette rather than sitting under it. */
:root[data-skin="colour"]{
  --glass:rgba(20,27,51,.72);
  --glow:0 0 0 3px rgba(59,130,246,.24);
  --wash1:rgba(59,130,246,.14);
  --wash2:rgba(168,85,247,.11);
}
@media (prefers-color-scheme: light){
  :root[data-skin="colour"]:not([data-theme="dark"]){
    --glass:rgba(255,255,255,.84);
    --wash1:rgba(59,130,246,.07);
    --wash2:rgba(168,85,247,.06);
  }
}
:root[data-skin="colour"][data-theme="light"]{
  --glass:rgba(255,255,255,.84);
  --wash1:rgba(59,130,246,.07);
  --wash2:rgba(168,85,247,.06);
}

/* ---- black and white. The ground is neutral and the gradient is a ramp of
   greys: the whole point of this look is that nothing carries hue, so a
   blue wash under it would undo it. */
:root[data-skin="mono"]{
  --grad:linear-gradient(135deg,#f5f5f5,#9a9a9a);
  --glow:0 0 0 3px rgba(255,255,255,.22);
  --glow-ok:0 0 10px rgba(255,255,255,.45);
  --glow-bad:0 0 10px rgba(255,255,255,.45);
  --wash1:rgba(255,255,255,.05);
  --wash2:rgba(255,255,255,.03);
}
@media (prefers-color-scheme: light){
  :root[data-skin="mono"]:not([data-theme="dark"]){
    --grad:linear-gradient(135deg,#2a2a2a,#6a6a6a);
    --glow:0 0 0 3px rgba(0,0,0,.18);
    --glow-ok:0 0 10px rgba(0,0,0,.3);
    --glow-bad:0 0 10px rgba(0,0,0,.3);
    --wash1:rgba(0,0,0,.04);
    --wash2:rgba(0,0,0,.025);
  }
}
:root[data-skin="mono"][data-theme="light"]{
  --grad:linear-gradient(135deg,#2a2a2a,#6a6a6a);
  --glow:0 0 0 3px rgba(0,0,0,.18);
  --glow-ok:0 0 10px rgba(0,0,0,.3);
  --glow-bad:0 0 10px rgba(0,0,0,.3);
  --wash1:rgba(0,0,0,.04);
  --wash2:rgba(0,0,0,.025);
}
/* ---------------------------------------------------------------------------
   DEPTH · the components

   Applied to the SITE, and not to the two full-screen applications.

   Real labs (data-app) and the simulator (data-sim) each carry seventy
   kilobytes of component CSS of their own, written and tuned against their
   own surfaces. Layering card gradients, lifted tiles and frosted bars over
   that changed how those two tools looked, which is not what a shared layer
   is for. They keep their own component style and still get the palette —
   the tokens above are outside this scope — so all three looks work in both.

   Each block below says what it is for; the aim throughout is depth and
   light, not decoration. The colours come from the tokens, which each look
   defines for itself.
   --------------------------------------------------------------------------- */

/* 1 ── the ambient ground ---------------------------------------------------
   Two wide radial washes, fixed so they do not slide as the page scrolls,
   and behind everything. This is the light source the rest of the section
   assumes. `background-attachment:fixed` rather than a positioned pseudo-
   element: no extra box, nothing to stack, and it cannot get in front of a
   dialog. */
:root:not([data-app]):not([data-sim]) body{
  background-color:var(--bg);
  background-image:
    radial-gradient(1100px 700px at 12% -8%,  var(--wash1), transparent 60%),
    radial-gradient(900px 620px at 100% 0%,   var(--wash2), transparent 58%);
  background-attachment:fixed;
  background-repeat:no-repeat;
}

/* 2 ── surfaces that sit in front of it -------------------------------------
   The hairline along the top edge is the whole trick. One pixel of light
   where a surface catches it, and the card reads as raised rather than as
   a differently-coloured rectangle. */
:root:not([data-app]):not([data-sim]) .card,
:root:not([data-app]):not([data-sim]) .nb-card,
:root:not([data-app]):not([data-sim]) .moncard,
:root:not([data-app]):not([data-sim]) .plan,
:root:not([data-app]):not([data-sim]) .tile{
  position:relative;
  background:linear-gradient(180deg,var(--lift2),transparent 42%),var(--panel);
  border:1px solid var(--line);
  box-shadow:var(--shadow), inset 0 1px 0 var(--lift);
  transition:box-shadow .18s ease, border-color .18s ease, transform .18s ease;
}
/* A tile is a thing you click, so it answers the pointer. Cards do not
   move — a page where everything lifts is a page that is never still. */
:root:not([data-app]):not([data-sim]) .tile:hover{
  transform:translateY(-2px);
  border-color:var(--edge-accent);
  box-shadow:var(--shadow-l), inset 0 1px 0 var(--lift);
}
:root:not([data-app]):not([data-sim]) .plan.mine{
  border-color:var(--edge-accent);
  box-shadow:var(--shadow-l), inset 0 1px 0 var(--lift), 0 0 0 1px var(--edge-accent);
}

/* 3 ── the bars, frosted ----------------------------------------------------
   The header and the canvas bars sit over content that scrolls under them.
   Frosting says so; a solid fill pretends the content stops there. The
   fallback is the plain panel colour, because backdrop-filter is the one
   thing here that is not universal and an unfrosted bar must still be
   opaque enough to read. */
:root:not([data-app]):not([data-sim]) .nb-head,
:root:not([data-app]):not([data-sim]) .bar,
:root:not([data-app]):not([data-sim]) .capbar,
:root:not([data-app]):not([data-sim]) .adminbar{
  background:var(--panel);
  border-bottom:1px solid var(--line);
  box-shadow:0 1px 0 var(--lift2), var(--shadow);
}
@supports (backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px)){
  :root:not([data-app]):not([data-sim]) .nb-head,
  :root:not([data-app]):not([data-sim]) .bar{
    background:var(--glass);
    -webkit-backdrop-filter:saturate(1.5) blur(14px);
    backdrop-filter:saturate(1.5) blur(14px);
  }
}

/* 4 ── the things that lead -------------------------------------------------
   A gradient here and nowhere else. The primary button, the brand mark and
   the paid badge are the three things on any page that are meant to be
   looked at first. */
/* The primary button's FILL is set here; its states are not. Hover and
   pressed belong to the button layer at the end of this file, which shows
   them with a line and leaves the box alone. The brightness change that
   used to live here was the second style the product was reported as
   having — and it also painted over the line. */
:root:not([data-app]):not([data-sim]) .btn.pri,
:root:not([data-app]):not([data-sim]) .nb-btn.pri,
:root:not([data-app]):not([data-sim]) .go{
  background:var(--grad);
  border-color:transparent;
  color:#fff;
}

/* The brand, painted with the gradient rather than filled with it. */
:root:not([data-app]):not([data-sim]) .nb-brand b,
:root:not([data-app]):not([data-sim]) .brand b{
  background:var(--grad);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}
:root:not([data-app]):not([data-sim]) .nb-badge.pro,
:root:not([data-app]):not([data-sim]) .nb-badge.enterprise,
:root:not([data-app]):not([data-sim]) .badge.pro,
:root:not([data-app]):not([data-sim]) .badge.enterprise,
:root:not([data-app]):not([data-sim]) .pill.admin{
  background:var(--grad); color:#fff; border-color:transparent;
  box-shadow:0 1px 6px rgba(139,92,246,.4);
}

/* 5 ── the active tab -------------------------------------------------------
   Nothing here. theme.css already says what the header's current tab looks
   like — a filled accent background, which is what the front page has always
   shown — and this file had added a gradient underline ON TOP of it. Two
   marks for "you are here" is worse than either one, and it made the bar
   look different on pages that loaded this file. The navigation and the lab's
   tabs keep theme.css's own treatment.

   This is deliberately not the same gesture as a button. A button's hover is
   a passing state on something you are about to press; a tab's `on` is where
   you currently are. The first is a line, the second is a place, and giving
   them the same mark would flatten the difference. */

/* 6 ── light that means something -------------------------------------------
   A running device glows; a failed one glows red. On a canvas somebody
   watches out of the corner of their eye while typing, this is the whole
   status display. */
/* The classes the product actually renders. `.dot.on` is a device that is
   up and `.dot.online` is a connected server; `.pill.ready` is the badge
   beside it in every table. These were written against .dot.ok and
   .state.running, which exist nowhere — so the whole of this section, one
   of the four things the skin is for, was landing on no element at all.
   A preview is what found it. */
:root:not([data-app]):not([data-sim]) .dot.on,
:root:not([data-app]):not([data-sim]) .dot.online,
:root:not([data-app]):not([data-sim]) .led.on,
:root:not([data-app]):not([data-sim]) .pill.ready{ box-shadow:var(--glow-ok); }
:root:not([data-app]):not([data-sim]) .dot.error,
:root:not([data-app]):not([data-sim]) .led.error,
:root:not([data-app]):not([data-sim]) .pill.error{ box-shadow:var(--glow-bad); }
/* Starting pulses, because it is the one state that is going to change. */
:root:not([data-app]):not([data-sim]) .dot.warn,
:root:not([data-app]):not([data-sim]) .dot.connecting,
:root:not([data-app]):not([data-sim]) .pill.pending{ animation:nbPulse 1.6s ease-in-out infinite; }
@keyframes nbPulse{
  0%,100%{ box-shadow:0 0 0 0 rgba(255,192,67,.5); }
  50%    { box-shadow:0 0 10px 2px rgba(255,192,67,.45); }
}

/* 7 ── focus you cannot miss ------------------------------------------------
   A keyboard user should never have to hunt for where they are. */
:root:not([data-app]):not([data-sim]) :is(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--accent);
  outline-offset:2px;
  box-shadow:var(--glow);
  border-radius:var(--r-sm);
}

/* 8 ── notes, given a spine -------------------------------------------------
   A coloured left edge rather than a full tinted box: the tint stays quiet
   and the severity is still readable at a glance down the page. */
:root:not([data-app]):not([data-sim]) .msg{
  border-left:3px solid var(--line);
  box-shadow:var(--shadow), inset 0 1px 0 var(--lift2);
}
:root:not([data-app]):not([data-sim]) .msg.ok  { border-left-color:var(--ok); }
:root:not([data-app]):not([data-sim]) .msg.warn{ border-left-color:var(--warn); }
:root:not([data-app]):not([data-sim]) .msg.bad { border-left-color:var(--bad); }
:root:not([data-app]):not([data-sim]) .msg.info{ border-left-color:var(--accent); }

/* 9 ── tables that are easier to read across --------------------------------
   A header that stays put and rows that answer the pointer. Both matter
   most on the admin screens, which are the longest tables in the product. */
:root:not([data-app]):not([data-sim]) .tbl thead th,
:root:not([data-app]):not([data-sim]) .montbl thead th{
  background:linear-gradient(180deg,var(--panel3),var(--panel2));
  border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:1;
}
:root:not([data-app]):not([data-sim]) .tbl tbody tr:hover,
:root:not([data-app]):not([data-sim]) .montbl tbody tr:hover{ background:var(--tint-accent); }

/* 10 ── the trail, and its tree ---------------------------------------------
   The company heading gets the gradient rule under it, so a long trail
   reads as grouped from across the page. */
:root:not([data-app]):not([data-sim]) .trail-org{
  border-bottom:none;
  background:linear-gradient(90deg,var(--tint-accent),transparent 70%);
  border-radius:var(--r-sm);
  padding:9px 10px;
}
:root:not([data-app]):not([data-sim]) .trail-org-name{
  background:var(--grad);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}
:root:not([data-app]):not([data-sim]) .trail-person[open]>summary{ background:var(--panel2); }

/* 11 ── the console keeps its own ground ------------------------------------
   Untouched on purpose. A terminal is the one surface in the product that
   must not be lit, tinted or frosted: every colour scheme written for one
   assumes a flat dark ground, and putting a wash behind it breaks all of
   them at once. */

/* 12 ── motion is optional --------------------------------------------------
   Everything animated above is turned off here, not merely slowed. */
@media (prefers-reduced-motion:reduce){
  *,
  *::before,
  *::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  :root:not([data-app]):not([data-sim]) .tile:hover{ transform:none; }
}



/* ###########################################################################
   #                                                                         #
   #   ONE BUTTON                                                            #
   #                                                                         #
   ###########################################################################

   The product had twenty-three button classes and no agreement between them.
   `.btn:hover` meant three different things in three files, the accent came
   from three token vocabularies, and border radius came in eight values.
   None of that was a decision; it was two design systems meeting in the
   middle.

   ---------------------------------------------------------------------------
   HOW HOVER AND PRESSED ARE SHOWN: A LINE UNDER THE BUTTON. NOT THE BOX.

   The box colour does not change — not on hover, not on press, not when
   selected. Only a line appears beneath. That is the same gesture the
   navigation uses for the tab you are on, so hover, pressed and selected are
   one idea at three weights instead of three different ideas.

   THE MECHANISM MATTERS, because the obvious version does not work. Writing
   `background:var(--panel2)` into the hover rule beats the page's own hover
   rule, but it also FORCES a colour — and a family whose resting background
   is something else (`.btn-ghost` rests on `--card`) then visibly changes on
   hover. That is the second style that was still running.

   So each family declares its resting background ONCE into `--_bg`, and
   every state re-asserts that same variable. Hover cannot change the box
   colour, because hover is painted with the value the resting state is
   painted with. It is one source of truth per family rather than a value
   repeated in four rules and drifting in one of them.

   Drawn with an INSET SHADOW, not border-bottom: a border changes the box's
   height and every button in a row would shift two pixels as the pointer
   arrived.

   ---------------------------------------------------------------------------
   WHAT IS OUT OF SCOPE, and why

   `:root:not([data-sim]):not([data-app])` — BOTH full-screen applications are
   excluded, and their buttons come from their own stylesheets.

   The simulator has its own controls, its own light palette and its own class
   vocabulary (.btn-p, .btn-g, .btn-a, .btn-r, .tbtn, .navbtn, used only by its
   pt-*.js panels). Those four single-letter classes appear nowhere else in the
   product, so they are not listed below at all.

   Real labs (data-app) defines .btn, .btn.pri, .btn.danger and .btn.sm in its
   own 70 kB of component CSS, tuned against its own canvas and panels. That is
   what its buttons use — border and colour on hover, a filled accent for the
   primary one — and this layer does not reach them. A tool that is a single
   full-screen application is allowed to be internally consistent with itself
   rather than with a marketing page; what it shares with the rest of the
   product is the palette, not the components.

   `.winbtn` and `.gospill` keep their own colour — a window control and a
   filter pill are chrome, not buttons — and get the shared radius,
   transition, line and focus ring so they still feel like this product.

   REVERTING is one line, as everything in this file is: drop the themes.css
   <link> and every page returns to theme.css as written.
   --------------------------------------------------------------------------- */

/* ---- the shared shape. Radius, type and timing, for every button that is
   not the simulator's. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-primary, .btn-ghost, .btn-accent,
  .btn-green, .btn-nav, .btn-sm, .btn-xs, .nb-btn, .go, .authgo,
  .winbtn, .gospill){
  border-radius:var(--r-sm);
  font-family:var(--sans);
  transition:box-shadow .15s ease, color .15s ease, border-color .15s ease;
  cursor:pointer;
}

/* ---- each family's resting background, declared once into --_bg.
   Everything below paints with var(--_bg) and nothing else. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-sm, .btn-xs, .nb-btn){
  --_bg:var(--panel2);
  background:var(--_bg);
  border:1px solid var(--line);
  color:var(--ink);
}
/* The teaching half rests its ghost button on --card. Kept, because that is
   its resting colour and this layer's job is to stop hover changing it, not
   to repaint the product. */
:root:not([data-sim]):not([data-app]) .btn-ghost{
  --_bg:var(--card, var(--panel2));
  background:var(--_bg);
  border:1px solid var(--line);
  color:var(--ink);
}
/* The ones that lead: one gradient, from the token each look defines, so
   "primary" is the same button on both halves of the site. */
:root:not([data-sim]):not([data-app]) :is(.btn.pri, .nb-btn.pri, .btn-primary, .btn-accent,
  .go, .authgo){
  --_bg:var(--grad);
  background:var(--_bg);
  border:1px solid transparent;
  color:#fff;
  font-weight:600;
}
:root:not([data-sim]):not([data-app]) :is(.btn-green, .btn-nav){
  --_bg:var(--ok);
  background:var(--_bg);
  border:1px solid var(--ok);
  color:#06231a;
  font-weight:600;
}

/* ---- hover: the line, and the box exactly as it was. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-sm, .btn-xs, .nb-btn, .btn-ghost):hover{
  background:var(--_bg);
  border-color:var(--line);
  color:var(--ink);
  box-shadow:inset 0 -2px 0 var(--accent);
}
/* On a filled button the line is white, because a blue line on a blue
   gradient is not a line. */
:root:not([data-sim]):not([data-app]) :is(.btn.pri, .nb-btn.pri, .btn-primary, .btn-accent,
  .go, .authgo):hover{
  background:var(--_bg);
  color:#fff;
  box-shadow:inset 0 -2px 0 rgba(255,255,255,.9);
}
:root:not([data-sim]):not([data-app]) :is(.btn-green, .btn-nav):hover{
  background:var(--_bg);
  color:#06231a;
  box-shadow:inset 0 -2px 0 rgba(0,0,0,.5);
}
/* `.danger` is a modifier, not a filled button: it stays quiet until
   hovered, and then says so with a red line rather than a red box. A row of
   red buttons is a row nobody reads. */
:root:not([data-sim]):not([data-app]) :is(.btn, .nb-btn).danger:hover{
  background:var(--_bg);
  border-color:var(--line);
  color:var(--bad);
  box-shadow:inset 0 -2px 0 var(--bad);
}

/* ---- pressed: the same line, heavier. Nothing else moves. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-sm, .btn-xs, .nb-btn, .btn-ghost):active{
  background:var(--_bg);
  box-shadow:inset 0 -3px 0 var(--accent2, var(--accent));
}
:root:not([data-sim]):not([data-app]) :is(.btn.pri, .nb-btn.pri, .btn-primary, .btn-accent,
  .go, .authgo):active{
  background:var(--_bg);
  box-shadow:inset 0 -3px 0 #fff;
}
:root:not([data-sim]):not([data-app]) :is(.btn-green, .btn-nav):active{
  background:var(--_bg);
  box-shadow:inset 0 -3px 0 rgba(0,0,0,.6);
}
:root:not([data-sim]):not([data-app]) :is(.btn, .nb-btn).danger:active{
  background:var(--_bg);
  box-shadow:inset 0 -3px 0 var(--bad);
}

/* ---- selected: the button that is currently on. The same line again. */
:root:not([data-sim]):not([data-app]) :is(.btn, .nb-btn, .btn-ghost).on,
:root:not([data-sim]):not([data-app]) :is(.btn, .nb-btn).active,
:root:not([data-sim]):not([data-app]) :is(.btn, .nb-btn, .btn-ghost)[aria-pressed="true"]{
  background:var(--_bg);
  color:var(--ink);
  box-shadow:inset 0 -2px 0 var(--accent);
  font-weight:600;
}

/* ---- focus. The ring stays a ring: it has to be visible on a button the
   pointer is NOT over, and a line under one button looks identical to the
   line under the one being hovered. Different question, different answer. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-primary, .btn-ghost, .btn-accent,
  .btn-green, .btn-nav, .btn-sm, .btn-xs, .nb-btn, .go, .authgo,
  .winbtn, .gospill):focus-visible{
  outline:2px solid var(--accent);
  outline-offset:2px;
}

/* ---- unavailable. One look for it, rather than four opacities. */
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-primary, .btn-ghost, .btn-accent,
  .btn-green, .btn-nav, .btn-sm, .btn-xs, .nb-btn, .go, .authgo):disabled{
  opacity:.45; cursor:not-allowed;
}
:root:not([data-sim]):not([data-app]) :is(.btn, .btn-primary, .btn-ghost, .btn-accent,
  .btn-green, .btn-nav, .nb-btn, .go, .authgo):disabled:hover{
  background:var(--_bg); box-shadow:none; color:inherit;
}

/* ---- the two that keep their own colour, with the shared gesture. */
:root:not([data-sim]):not([data-app]) .winbtn:hover{ box-shadow:inset 0 -2px 0 var(--accent); }
:root:not([data-sim]):not([data-app]) .gospill{ --_bg:var(--panel2); }
:root:not([data-sim]):not([data-app]) .gospill:hover{ background:var(--_bg); box-shadow:inset 0 -2px 0 var(--accent); }
:root:not([data-sim]):not([data-app]) .gospill.on{ background:var(--_bg); box-shadow:inset 0 -2px 0 var(--accent);
                                   font-weight:600; color:var(--ink); }
