﻿/* ==========================================================================
   Replica of the Linoria menu, rebuilt from the library source inlined in
   havoc.lua rather than from screenshots.

   Two constructions drive almost everything and were previously wrong:

   1. DOUBLE BORDER. Every control is an outer Frame with a BLACK border
      wrapping an inner Frame whose border uses BorderMode.Inset. So the edge
      is always 1px black, then 1px OutlineColor, then the fill. In CSS:
      `border: 1px solid #000` + `box-shadow: inset 0 0 0 1px <outline>`.

   2. ACCENT BAR. Groupboxes and tabboxes carry a 2px AccentColor strip across
      the top of their inner frame, and the groupbox title is LEFT aligned
      just under it — not a centred title with an underline.
   ========================================================================== */

/* Roblox's Enum.Font.Code is Inconsolata (rbxasset://fonts/families/
   Inconsolata.json) — NOT Source Code Pro. Only weight 400 exists in the
   legacy enum, so never synthesise a heavier weight. */
@font-face {
  font-family: "RbxCode";
  src: url("Inconsolata.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

.rbx {
  /* havoc.lua:3510-3515. AccentColorDark is GetDarkerColor(AccentColor),
     which is HSV value / 1.5 — for #0055ff that is #0039aa. */
  --w-bg: #141414;      /* BackgroundColor  20,20,20   */
  --w-main: #1c1c1c;    /* MainColor        28,28,28   */
  --w-outline: #323232; /* OutlineColor     50,50,50   */
  --w-accent: #0055ff;  /* AccentColor      0,85,255   */
  --w-accent-d: #0039aa;/* AccentColorDark  0,57,170   */
  --w-font: #ffffff;    /* FontColor        255,255,255*/
  --w-risk: #ff3232;    /* RiskColor        255,50,50  */
  --w-black: #000000;

  /* NO zoom/transform. A non-integer scale turns every 1px border into
     0.833px, and the browser rounds that differently per edge — some edges
     paint, some drop out, which reads as a broken bracket-shaped outline.
     The in-game window is resizable (Config.Size is just a default), while
     the controls are fixed pixel sizes, so rendering full-width at 1:1 with
     13px checkboxes and 11px type is exactly a resized in-game window —
     and every border lands on a whole pixel. */
  /* Config.Size = UDim2.fromOffset(550, 600) — the replica is that window at
     1:1. max-width lets it shrink on a phone rather than overflow; every
     control inside keeps its fixed pixel size, so it degrades the way a
     resized in-game window does. */
  width: 550px;
  max-width: 100%;
  height: 600px;
  display: flex;
  flex-direction: column;
  font-family: "RbxCode", ui-monospace, Consolas, monospace;
  /* Roblox TextSize maps 1:1 to CSS px. The earlier "divide by 1.257"
     compensation only looked right because it was cancelling a second error
     — Source Code Pro's 0.6em advance against Inconsolata's 0.5em. With the
     correct face, TextSize 14 is simply 14px (7.0px per character). */
  font-size: 14px;
  line-height: 1;
  font-weight: 400;
  /* Roblox draws text with grayscale AA and every label carries
     UIStroke{black, 1px, Miter} (Library:ApplyTextStroke, line 3606).
     paint-order puts the fill back over the centred stroke, so 2px centred
     reads as the 1px outward stroke the library actually draws. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* `-webkit-font-smoothing` is a no-op on Windows — Chrome keeps using
     ClearType, which fringes every glyph red/cyan. Roblox draws with
     grayscale AA. Promoting the menu to its own compositing layer is the
     only way to get grayscale in Chrome/Windows, and it is safe here: the
     menu never animates, so the layer costs one texture and nothing else. */
  transform: translateZ(0);
  paint-order: stroke fill;
  -webkit-text-stroke: 2px #000;
  color: var(--w-font);
  /* Window Outer is black; Inner is MainColor with an INSET AccentColor
     border — that blue outline is the library's signature and was missing. */
  background: var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-accent);
  padding: 0;
  user-select: none;
  -webkit-user-select: none;
  container-type: inline-size;
}

.rbx *,
.rbx *::before,
.rbx *::after {
  box-sizing: border-box;
  border-radius: 0;
}

/* --- Window chrome ------------------------------------------------------- */

/* WindowLabel: 25px tall band, text at x=7, TextSize 16 (CreateLabel default). */
.rbx__title {
  height: 25px;
  display: flex;
  align-items: center;
  font-size: 16px;   /* TextSize 16 */
  padding: 0 7px;
  color: var(--w-font);
}

/* havoc.lua:9097 — the ".us" is rich-text coloured #7d996e (a muted sage),
   NOT the accent blue. */
.rbx__title i {
  font-style: normal;
  color: #7d996e;
}

/* MainSection: inset 8px, BackgroundColor fill, OutlineColor + black double
   border. Holds the tab strip and the TabContainer. */
.rbx__main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin: 0 8px 8px;
  padding: 8px;
  background: var(--w-bg);
  border: 1px solid var(--w-outline);
  box-shadow: inset 0 0 0 1px var(--w-black);
}

/* TabContainer: MainColor fill with an OutlineColor border — the groupboxes
   (which are the darker BackgroundColor) sit on top of it. */
.rbx__frame {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  border: 1px solid var(--w-outline);
  background: var(--w-main);
  padding: 7px;
}

/* Main tab strip. Each TabButton is sized to its own text
   (GetTextBounds(Name, Font, 16) + 8 + 4), NOT stretched to equal widths,
   and the label is TextSize 16 while everything else in the menu is 14.
   ACTIVE is MainColor (lighter); inactive is BackgroundColor (darker). */
.rbx__tabs {
  display: flex;
  /* TabListLayout Padding = Config.TabPadding. The library defaults it to 0,
     but this menu passes TabPadding = 8 at havoc.lua:9101 — hence the visible
     gaps between tabs that a defaults-only reading misses. */
  gap: 8px;
  height: 21px;      /* TabArea Size (1, -16, 0, 21) */
  margin: 0 0 1px;
  position: relative;
  z-index: 2;
}

/* TabButtonLabel is Size (1,0,1,-1) with the label centred on both axes
   inside the 21px TabArea. line-height:1.4 at 16px is 22.4px — taller than
   the whole button — so the text was forcing the button open and sitting
   off-centre. Inconsolata's glyph extent is exactly 1.0em, so line-height:1
   centred in 21px leaves 2.5px clear top and bottom: nothing clips. */
.rbx__tab {
  appearance: none;
  font: inherit;
  font-size: 16px;   /* TextSize 16 */
  flex: 0 0 auto;
  height: 21px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  color: var(--w-font);
  background: var(--w-bg);
  border: 1px solid var(--w-outline);
  padding: 0 6px;
  margin-bottom: -1px;
  cursor: pointer;
  white-space: nowrap;
}

.rbx__tab[aria-selected="true"] {
  background: var(--w-main);
  border-bottom-color: var(--w-main);
}

/* --- Page / columns ------------------------------------------------------ */

.rbx__page {
  display: none;
  gap: 8px;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  /* Fills whatever the 600px window leaves after the title, tab strip and
     padding, instead of a fixed height that has to be recomputed by hand. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--w-outline) transparent;
}

.rbx__page[data-active="true"] {
  display: grid;
}

.rbx__page::-webkit-scrollbar { width: 8px; }
.rbx__page::-webkit-scrollbar-track { background: transparent; }
.rbx__page::-webkit-scrollbar-thumb { background: var(--w-outline); }

.rbx__col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* --- Groupbox / Tabbox shell --------------------------------------------
   BoxOuter: BackgroundColor fill, OutlineColor inset border.
   BoxInner: 1px inside it, BackgroundColor fill, BLACK border.
   Highlight: 2px AccentColor across the top of BoxInner.
   --------------------------------------------------------------------- */

/* One black outline and nothing else.
   BoxOuter carries an OutlineColor border, but BoxInner sits 1px inside it
   with BorderMode left at the default (Outline, i.e. drawn OUTSIDE its own
   bounds) — so BoxInner's black border lands in exactly the same 1px ring and,
   at ZIndex 4 against 2, paints over the grey completely. Rendering both is
   what produced the stray bracket-shaped line inside every box. */
.rbx__box {
  border: 1px solid var(--w-black);
  background: var(--w-bg);
  padding: 0;
  min-width: 0;
}

.rbx__box-inner {
  border: 0;
  background: var(--w-bg);
  padding: 0 0 4px 4px;
}

/* The 2px accent strip sits above everything inside the box. */
.rbx__box-inner::before {
  content: "";
  display: block;
  height: 2px;
  margin: 0 0 0 -4px;
  background: var(--w-accent);
}

/* GroupboxLabel: TextXAlignment.Left, Position (0,4,0,2), 18px tall. */
.rbx__box-title {
  height: 18px;
  display: flex;
  align-items: center;
  text-align: left;
  color: var(--w-font);
  font-size: 14px;
  padding-left: 0;
}

/* Tabbox sub-tabs: 18px tall row, 1px below the accent bar. ACTIVE is
   BackgroundColor (dark, merging with the content), inactive is MainColor. */
/* Butts straight against the accent bar. In the library TabboxButtons sits at
   y=1 while the Highlight is 2px tall at ZIndex 10 against the buttons' 6, so
   the bar paints over each button's top border and it is never visible. */
/* Butts straight against the accent bar. In the library TabboxButtons sits at
   y=1 while the Highlight is 2px tall at ZIndex 10 against the buttons' 6, so
   the bar paints over each button's top border and it is never visible.
   The -5px/-1px pull puts the strip's own outer borders on top of the box's
   black outline instead of beside it: Roblox draws these borders OUTSIDE the
   frame (BorderMode.Outline), so neighbours overlap into one 1px line rather
   than stacking into two. */
.rbx__subtabs {
  display: flex;
  height: 18px;
  margin: 0 -1px 0 -5px;
}

/* Same collapse between adjacent buttons. */
.rbx__subtab + .rbx__subtab {
  margin-left: -1px;
}

.rbx__subtab {
  appearance: none;
  font: inherit;
  flex: 1 1 0;
  min-width: 0;
  color: var(--w-font);
  background: var(--w-main);
  border: 1px solid var(--w-black);
  border-top: 0;        /* covered by the accent bar in game */
  padding: 0 2px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  line-height: 1.4;   /* clears descenders in "Spoofing", "Lighting" */
}

.rbx__subtab[aria-selected="true"] {
  background: var(--w-bg);
  border-bottom-color: var(--w-bg);
}

.rbx__pane {
  display: none;
  flex-direction: column;
  padding-top: 3px;
}

.rbx__pane[data-active="true"] {
  display: flex;
}

/* --- Rows ---------------------------------------------------------------- */

.rbx__row {
  display: flex;
  align-items: center;
  gap: 6px;              /* ToggleLabel Position (1, 6) */
  height: 13px;          /* ToggleOuter is 13x13 */
  margin-bottom: 7px;    /* Groupbox:AddBlank(5 + 2) */
  padding-right: 4px;
}

.rbx__row-main {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
  height: 100%;
}

/* Labels never dim — the library only recolours them for Risky toggles.
   line-height must clear the descender: Source Code Pro drops 0.273em below
   the baseline, so at 11px the glyph box needs 13.8px. With the inherited
   line-height:1 the `overflow:hidden` that drives the ellipsis was slicing
   the tails off g, p, y and q. */
.rbx__label {
  color: var(--w-font);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}

.rbx__row.is-risky .rbx__label {
  color: var(--w-risk);
}

/* Standalone caption above a slider / dropdown: 10px tall, left, then a 3px
   blank. */
.rbx__caption {
  height: 10px;
  display: flex;
  align-items: flex-end;
  color: var(--w-font);
  margin-bottom: 3px;
  padding-right: 4px;
}

/* --- Toggle -------------------------------------------------------------
   13x13. Black outer border, inset OutlineColor inner border, MainColor fill.
   Checked swaps the fill to AccentColor and the inset border to
   AccentColorDark. Hovering the row turns the OUTER border accent.
   --------------------------------------------------------------------- */

.rbx__check {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  border: 1px solid var(--w-black);
  background: var(--w-main);
  box-shadow: inset 0 0 0 1px var(--w-outline);
}

.rbx__row.is-on .rbx__check {
  background: var(--w-accent);
  box-shadow: inset 0 0 0 1px var(--w-accent-d);
}

.rbx__row-main:hover .rbx__check {
  border-color: var(--w-accent);
}

/* --- Keybind pill & colour swatch --------------------------------------- */

.rbx__key {
  flex: 0 0 auto;
  min-width: 24px;
  height: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  color: var(--w-font);
  background: var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  padding: 0 3px;
  cursor: pointer;
}

.rbx__key:hover {
  border-color: var(--w-accent);
}

/* ColorPicker: 28x14, inset border of GetDarkerColor(value). */
.rbx__swatch {
  flex: 0 0 auto;
  width: 28px;
  height: 14px;
  border: 1px solid var(--w-black);
  cursor: pointer;
}

/* --- Slider -------------------------------------------------------------
   13px tall, full width minus 4. Fill carries its own AccentColorDark border.
   --------------------------------------------------------------------- */

.rbx__slider {
  position: relative;
  height: 13px;
  margin-right: 4px;
  margin-bottom: 6px;
  background: var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  cursor: ew-resize;
  overflow: hidden;
  touch-action: none;
}

.rbx__slider-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--w-accent);
  box-shadow: inset 0 0 0 1px var(--w-accent-d);
}

.rbx__slider-text {
  position: relative;
  display: block;
  text-align: center;
  font-size: 14px;
  line-height: 11px;
  color: var(--w-font);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

/* --- Dropdown -----------------------------------------------------------
   20px tall. Inner carries a vertical UIGradient from white to rgb(212,212,
   212), which multiplies MainColor — so it darkens slightly toward the
   bottom. Arrow is a 12x12 image 16px from the right edge.
   --------------------------------------------------------------------- */

.rbx__dd {
  position: relative;
  margin-right: 4px;
  margin-bottom: 5px;
}

.rbx__dd-btn {
  appearance: none;
  font: inherit;
  width: 100%;
  height: 20px;
  text-align: left;
  color: var(--w-font);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.17) 100%),
    var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  padding: 0 22px 0 5px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 18px;
}

.rbx__dd-btn:hover {
  border-color: var(--w-accent);
}

/* Stand-in for the `uidropdown` image: a small solid chevron, 12x12 box
   positioned 16px in from the right edge. */
.rbx__dd-btn::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 50%;
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid var(--w-font);
  transform: translateY(-2px);
}

.rbx__dd.is-open .rbx__dd-btn::after {
  border-top: 0;
  border-bottom: 4px solid var(--w-font);
}

/* ListOuter is parented to the ScreenGui at ZIndex 20 and positioned 1px
   below the closed box — so it overlays everything, it does not push layout. */
.rbx__dd-list {
  display: none;
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  top: calc(100% + 1px);
  background: var(--w-bg);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  padding: 1px;
  max-height: 162px;      /* MAX_DROPDOWN_ITEMS(8) * 20 + 2 */
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--w-outline) transparent;
}

.rbx__dd.is-open .rbx__dd-list {
  display: block;
}

.rbx__dd-opt {
  height: 20px;
  display: flex;
  align-items: center;
  padding: 0 5px;
  cursor: pointer;
  color: var(--w-font);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}

.rbx__dd-opt:hover {
  background: var(--w-main);
}

.rbx__dd-opt[aria-selected="true"] {
  color: var(--w-accent);
}

/* --- Input & button ------------------------------------------------------ */

.rbx__input {
  font: inherit;
  width: calc(100% - 4px);
  height: 20px;
  color: var(--w-font);
  background: var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  padding: 0 5px;
  margin-bottom: 5px;
}

.rbx__input::placeholder {
  color: #6a6a6a;
}

/* The Change avatar field only reports which bundled rig is on show, so it
   should not invite typing into it. */
.rbx__input[readonly] {
  cursor: default;
}

.rbx__input:focus-visible {
  outline: none;
  border-color: var(--w-accent);
}

.rbx__btn {
  appearance: none;
  font: inherit;
  width: calc(100% - 4px);
  height: 20px;
  color: var(--w-font);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.17) 100%),
    var(--w-main);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
  padding: 0 6px;
  cursor: pointer;
  margin-bottom: 5px;
  line-height: 18px;
}

.rbx__btn:hover {
  border-color: var(--w-accent);
}

/* --- Configure ESP context panel ----------------------------------------
   ContextMenu.new("Configure"), opened at (button.x, button.y + button.h)
   and overlaying the menu rather than pushing layout — same construction as
   the dropdown list.
   --------------------------------------------------------------------- */

.rbx__popup-wrap {
  position: relative;
  width: calc(100% - 4px);
  margin-bottom: 5px;
}

.rbx__popup-wrap .rbx__btn {
  width: 100%;
  margin-bottom: 0;
}

.rbx__popup {
  display: none;
  position: absolute;
  z-index: 20;
  left: 0;
  top: calc(100% + 1px);
  min-width: 132px;
  background: var(--w-bg);
  border: 1px solid var(--w-black);
  box-shadow: inset 0 0 0 1px var(--w-outline);
}

.rbx__popup-wrap.is-open .rbx__popup {
  display: block;
}

.rbx__popup-title {
  height: 18px;
  display: flex;
  align-items: center;
  padding-left: 4px;
  border-top: 2px solid var(--w-accent);
  color: var(--w-font);
}

.rbx__popup-body {
  padding: 2px 4px 4px;
}

.rbx__popup-body .rbx__row {
  margin-bottom: 5px;
}

.rbx__popup-body .rbx__row:last-child {
  margin-bottom: 0;
}

.rbx__btn-row {
  display: flex;
  gap: 4px;
  width: calc(100% - 4px);
  margin-bottom: 5px;
}

.rbx__btn-row .rbx__btn {
  width: 100%;
  margin-bottom: 0;
}

.rbx__divider {
  height: 1px;
  margin: 2px 4px 9px 0;
  background: var(--w-outline);
  box-shadow: 0 1px 0 var(--w-black);
}

/* --- ESP preview --------------------------------------------------------- */

/* Sized by the image, not by a fixed ratio. An aspect-ratio plus
   overflow:hidden was cropping the rig's head and feet off. */
.rbx__preview {
  position: relative;
  margin: 0 4px 7px 0;
  line-height: 0;
}

.rbx__preview-img {
  display: block;
  width: 100%;
  height: auto;
}

/* --- ESP elements over the preview --------------------------------------
   Every piece is hidden until the Configure panel switches it on. Geometry
   is a fraction of the rig so it tracks the image at any width.
   --------------------------------------------------------------------- */

/* Geometry comes from the rig's alpha bounding box, set per avatar by JS.
   The fallbacks only apply before the first image resolves. */
.rbx__esp {
  position: absolute;
  left: var(--esp-l, 12%);
  top: var(--esp-t, 5%);
  width: var(--esp-w, 72%);
  height: var(--esp-h, 92%);
  pointer-events: none;
}

.rbx__esp-box,
.rbx__esp-hp,
.rbx__esp-name,
.rbx__esp-dist,
.rbx__esp-weapon,
.rbx__esp-flags {
  display: none;
  position: absolute;
}

.rbx__preview.esp-box .rbx__esp-box,
.rbx__preview.esp-hp .rbx__esp-hp,
.rbx__preview.esp-name .rbx__esp-name,
.rbx__preview.esp-dist .rbx__esp-dist,
.rbx__preview.esp-weapon .rbx__esp-weapon,
.rbx__preview.esp-flags .rbx__esp-flags {
  display: block;
}

/* Measured off the real capture: black, white, black across the edge. A 1px
   white line with a 1px black outline on both sides. No corner radius. */
.rbx__esp-box {
  inset: 0;
  border: 1px solid #fff;
  box-shadow:
    0 0 0 1px #000,
    inset 0 0 0 1px #000;
}

/* Green bar hugging the left edge of the box, same black outline. */
.rbx__esp-hp {
  top: 0;
  bottom: 0;
  right: calc(100% + 3px);
  width: 2px;
  background: #04fb00;
  box-shadow: 0 0 0 1px #000;
}

.rbx__esp-name,
.rbx__esp-dist,
.rbx__esp-weapon,
.rbx__esp-flags {
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 10px;
  line-height: 1.2;
  color: #fff;
  text-shadow: 1px 0 0 #000, -1px 0 0 #000, 0 1px 0 #000, 0 -1px 0 #000;
}

.rbx__esp-name  { bottom: calc(100% + 2px); }
.rbx__esp-dist  { top: calc(100% + 2px); }
.rbx__esp-weapon { top: calc(100% + 14px); }

.rbx__esp-flags {
  left: calc(100% + 4px);
  top: 0;
  transform: none;
}

/* --- Narrow container ---------------------------------------------------- */

@container (max-width: 400px) {
  .rbx__page[data-active="true"] {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .rbx * {
    transition: none !important;
  }
}
