code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

import { classes } from 'common/react';
import { useDispatch } from 'common/redux';
import { decodeHtmlEntities, toTitleCase } from 'common/string';
import { Component } from 'inferno';
import { backendSuspendStart, useBackend } from '../backend';
import { Icon } from '../components';
import { UI_DISABLED, UI_INTERACTIVE, UI_UPDATE } from '../constants';
import { useDebug } from '../debug';
import { toggleKitchenSink } from '../debug/actions';
import { dragStartHandler, recallWindowGeometry, resizeStartHandler, setWindowKey } from '../drag';
import { createLogger } from '../logging';
import { Layout } from './Layout';

const logger = createLogger('Window');

const DEFAULT_SIZE = [400, 600];

export class Window extends Component {
  componentDidMount() {
    const { suspended } = useBackend(this.context);
    const { canClose = true } = this.props;
    if (suspended) {
      return;
    }
    Byond.winset(window.__windowId__, {
      'can-close': Boolean(canClose),
    });
    logger.log('mounting');
    this.updateGeometry();
  }

  componentDidUpdate(prevProps) {
    const shouldUpdateGeometry = (
      this.props.width !== prevProps.width
      || this.props.height !== prevProps.height
    );
    if (shouldUpdateGeometry) {
      this.updateGeometry();
    }
  }

  updateGeometry() {
    const { config } = useBackend(this.context);
    const options = {
      size: DEFAULT_SIZE,
      ...config.window,
    };
    if (this.props.width && this.props.height) {
      options.size = [this.props.width, this.props.height];
    }
    if (config.window?.key) {
      setWindowKey(config.window.key);
    }
    recallWindowGeometry(options);
  }

  render() {
    const {
      canClose = true,
      theme,
      title,
      children,
      buttons,
    } = this.props;
    const {
      config,
      suspended,
    } = useBackend(this.context);
    const { debugLayout } = useDebug(this.context);
    const dispatch = useDispatch(this.context);
    const fancy = config.window?.fancy;
    // Determine when to show dimmer
    const showDimmer = config.user && (
      config.user.observer
        ? config.status < UI_DISABLED
        : config.status < UI_INTERACTIVE
    );
    return (
      <Layout
        className="Window"
        theme={theme}>
        <TitleBar
          className="Window__titleBar"
          title={!suspended && (title || decodeHtmlEntities(config.title))}
          status={config.status}
          fancy={fancy}
          onDragStart={dragStartHandler}
          onClose={() => {
            logger.log('pressed close');
            dispatch(backendSuspendStart());
          }}
          canClose={canClose}>
          {buttons}
        </TitleBar>

          {!suspended && children}
          {showDimmer && (

          )}

        {fancy && (
          <>

          </>
        )}
      </Layout>
    );
  }
}

const WindowContent = props => {
  const {
    className,
    fitted,
    children,
    ...rest
  } = props;
  return (
    <Layout.Content
      className={classes([
        'Window__content',
        className,
      ])}
      {...rest}>
      {fitted && children || (

          {children}

      )}
    </Layout.Content>
  );
};

Window.Content = WindowContent;

const statusToColor = status => {
  switch (status) {
    case UI_INTERACTIVE:
      return 'good';
    case UI_UPDATE:
      return 'average';
    case UI_DISABLED:
    default:
      return 'bad';
  }
};

const TitleBar = (props, context) => {
  const {
    className,
    title,
    status,
    canClose,
    fancy,
    onDragStart,
    onClose,
    children,
  } = props;
  const dispatch = useDispatch(context);
  return (

      {status === undefined && (
        <Icon
          className="TitleBar__statusIcon"
          name="tools"
          opacity={0.5} />
      ) || (
        <Icon
          className="TitleBar__statusIcon"
          color={statusToColor(status)}
          name="eye" />
      )}

 fancy && onDragStart(e)} />

        {typeof title === 'string'
          && title === title.toLowerCase()
          && toTitleCase(title)
          || title}
        {!!children && (

            {children}

        )}

      {process.env.NODE_ENV !== 'production' && (

 dispatch(toggleKitchenSink())}>
          <Icon name="bug" />

      )}
      {Boolean(fancy && canClose) && (

          {Byond.IS_LTE_IE8 ? 'x' : '×'}

      )}

  );
};


code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

export { Layout } from './Layout';
export { NtosWindow } from './NtosWindow';
export { Pane } from './Pane';
export { Window } from './Window';

repo_name: AnalWerewolf/sundown

license: mit


code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

/**
 * Prevents baby jailing the user when he clicks an external link.
 */
export const captureExternalLinks = () => {
  // Subscribe to all document clicks
  document.addEventListener('click', e => {
    /** @type {HTMLElement} */
    let target = e.target;
    // Recurse down the tree to find a valid link
    while (true) {
      // Reached the end, bail.
      if (!target || target === document.body) {
        return;
      }
      const tagName = String(target.tagName).toLowerCase();
      if (tagName === 'a') {
        break;
      }
      target = target.parentElement;
    }
    const hrefAttr = target.getAttribute('href') || '';
    // Leave BYOND links alone
    const isByondLink = hrefAttr.charAt(0) === '?'
      || hrefAttr.startsWith('byond://');
    if (isByondLink) {
      return;
    }
    // Prevent default action
    e.preventDefault();
    // Normalize the URL
    let url = hrefAttr;
    if (url.toLowerCase().startsWith('www')) {
      url = 'https://' + url;
    }
    // Open the link
    Byond.topic({
      tgui: 1,
      window_id: window.__windowId__,
      type: 'openLink',
      url,
    });
  });
};


code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

import { sendLogEntry } from 'tgui-dev-server/link/client.cjs';

const LEVEL_DEBUG = 0;
const LEVEL_LOG = 1;
const LEVEL_INFO = 2;
const LEVEL_WARN = 3;
const LEVEL_ERROR = 4;

const log = (level, ns, ...args) => {
  // Send logs to a remote log collector
  if (process.env.NODE_ENV !== 'production') {
    sendLogEntry(level, ns, ...args);
  }
  // Send important logs to the backend
  if (level >= LEVEL_INFO) {
    const logEntry = [ns, ...args]
      .map(value => {
        if (typeof value === 'string') {
          return value;
        }
        if (value instanceof Error) {
          return value.stack || String(value);
        }
        return JSON.stringify(value);
      })
      .filter(value => value)
      .join(' ')
      + '\nUser Agent: ' + navigator.userAgent;
    Byond.topic({
      tgui: 1,
      window_id: window.__windowId__,
      type: 'log',
      ns,
      message: logEntry,
    });
  }
};

export const createLogger = ns => {
  return {
    debug: (...args) => log(LEVEL_DEBUG, ns, ...args),
    log: (...args) => log(LEVEL_LOG, ns, ...args),
    info: (...args) => log(LEVEL_INFO, ns, ...args),
    warn: (...args) => log(LEVEL_WARN, ns, ...args),
    error: (...args) => log(LEVEL_ERROR, ns, ...args),
  };
};

/**
 * A generic instance of the logger.
 *
 * Does not have a namespace associated with it.
 */
export const logger = createLogger();


code:
import { perf } from 'common/perf';
import { render } from 'inferno';
import { createLogger } from './logging';

const logger = createLogger('renderer');

let reactRoot: any;
let initialRender: string | boolean = true;
let suspended = false;

// These functions are used purely for profiling.
export const resumeRenderer = () => {
  initialRender = initialRender || 'resumed';
  suspended = false;
};

export const suspendRenderer = () => {
  suspended = true;
};

type CreateRenderer = <T extends unknown[] = [unknown]>(
  getVNode?: (...args: T) => any,
) => (...args: T) => void;

export const createRenderer: CreateRenderer = (getVNode) => (...args) => {
  perf.mark('render/start');
  // Start rendering
  if (!reactRoot) {
    reactRoot = document.getElementById('react-root');
  }
  if (getVNode) {
    render(getVNode(...args), reactRoot);
  }
  else {
    render(args[0] as any, reactRoot);
  }
  perf.mark('render/finish');
  if (suspended) {
    return;
  }
  // Report rendering time
  if (process.env.NODE_ENV !== 'production') {
    if (initialRender === 'resumed') {
      logger.log('rendered in',
        perf.measure('render/start', 'render/finish'));
    }
    else if (initialRender) {
      logger.debug('serving from:', location.href);
      logger.debug('bundle entered in',
        perf.measure('inception', 'init'));
      logger.debug('initialized in',
        perf.measure('init', 'render/start'));
      logger.log('rendered in',
        perf.measure('render/start', 'render/finish'));
      logger.log('fully loaded in',
        perf.measure('inception', 'render/finish'));
    }
    else {
      logger.debug('rendered in',
        perf.measure('render/start', 'render/finish'));
    }
  }
  if (initialRender) {
    initialRender = false;
  }
};


code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

import { selectBackend } from './backend';
import { selectDebug } from './debug/selectors';
import { Window } from './layouts';

const requireInterface = require.context('./interfaces');

const routingError = (type, name) => () => {
  return (
    <Window>
      <Window.Content scrollable>
        {type === 'notFound' && (

Interface {name} was not found.

        )}
        {type === 'missingExport' && (

Interface {name} is missing an export.

        )}
      </Window.Content>
    </Window>
  );
};

const SuspendedWindow = () => {
  return (
    <Window>
      <Window.Content scrollable />
    </Window>
  );
};

export const getRoutedComponent = store => {
  const state = store.getState();
  const { suspended, config } = selectBackend(state);
  if (suspended) {
    return SuspendedWindow;
  }
  if (process.env.NODE_ENV !== 'production') {
    const debug = selectDebug(state);
    // Show a kitchen sink
    if (debug.kitchenSink) {
      return require('./debug').KitchenSink;
    }
  }
  const name = config?.interface;
  const interfacePathBuilders = [
    name => `./${name}.tsx`,
    name => `./${name}.js`,
    name => `./${name}/index.tsx`,
    name => `./${name}/index.js`,
  ];
  let esModule;
  while (!esModule && interfacePathBuilders.length > 0) {
    const interfacePathBuilder = interfacePathBuilders.shift();
    const interfacePath = interfacePathBuilder(name);
    try {
      esModule = requireInterface(interfacePath);
    }
    catch (err) {
      if (err.code !== 'MODULE_NOT_FOUND') {
        throw err;
      }
    }
  }
  if (!esModule) {
    return routingError('notFound', name);
  }
  const Component = esModule[name];
  if (!Component) {
    return routingError('missingExport', name);
  }
  return Component;
};


code:
/**
 * Uses DOMPurify to purify/sanitise HTML.
 */

import DOMPurify from 'dompurify';

// Default values
let defTag = [
  'b',
  'br',
  'center',
  'code',
  'div',
  'font',
  'hr',
  'i',
  'li',
  'menu',
  'ol',
  'p',
  'pre',
  'span',
  'table',
  'td',
  'th',
  'tr',
  'u',
  'ul',
];
let defAttr = ['class', 'style'];

/**
 * Feed it a string and it should spit out a sanitized version.
 *
 * @param {string} input
 * @param {array} tags
 * @param {array} forbidAttr
 */
export const sanitizeText = (input, tags = defTag, forbidAttr = defAttr) => {
  // This is VERY important to think first if you NEED
  // the tag you put in here.  We are pushing all this
  // though dangerouslySetInnerHTML and even though
  // the default DOMPurify kills javascript, it dosn't
  // kill href links or such
  return DOMPurify.sanitize(input, {
    ALLOWED_TAGS: tags,
    FORBID_ATTR: forbidAttr,
  });
};


code:
/**
 * @file
 * @copyright 2020 Aleksej Komarov
 * @license MIT
 */

import { flow } from 'common/fp';
import { applyMiddleware, combineReducers, createStore } from 'common/redux';
import { Component } from 'inferno';
import { assetMiddleware } from './assets';
import { backendMiddleware, backendReducer } from './backend';
import { debugMiddleware, debugReducer, relayMiddleware } from './debug';
import { createLogger } from './logging';

const logger = createLogger('store');

export const configureStore = (options = {}) => {
  const { sideEffects = true } = options;
  const reducer = flow([
    combineReducers({
      debug: debugReducer,
      backend: backendReducer,
    }),
    options.reducer,
  ]);
  const middleware = !sideEffects ? [] : [
    ...(options.middleware?.pre || []),
    assetMiddleware,
    backendMiddleware,
    ...(options.middleware?.post || []),
  ];
  if (process.env.NODE_ENV !== 'production') {
    // We are using two if statements because Webpack is capable of
    // removing this specific block as dead code.
    if (sideEffects) {
      middleware.unshift(
        loggingMiddleware,
        debugMiddleware,
        relayMiddleware);
    }
  }
  const enhancer = applyMiddleware(...middleware);
  const store = createStore(reducer, enhancer);
  // Globals
  window.__store__ = store;
  window.__augmentStack__ = createStackAugmentor(store);
  return store;
};

const loggingMiddleware = store => next => action => {
  const { type, payload } = action;
  if (type === 'update' || type === 'backend/update') {
    logger.debug('action', { type });
  }
  else {
    logger.debug('action', action);
  }
  return next(action);
};

/**
 * Creates a function, which can be assigned to window.__augmentStack__
 * to augment reported stack traces with useful data for debugging.
 */
const createStackAugmentor = store => (stack, error) => {
  if (!error) {
    error = new Error(stack.split('\n')[0]);
    error.stack = stack;
  }
  else if (typeof error === 'object' && !error.stack) {
    error.stack = stack;
  }
  logger.log('FatalError:', error);
  const state = store.getState();
  const config = state?.backend?.config;
  let augmentedStack = stack;
  augmentedStack += '\nUser Agent: ' + navigator.userAgent;
  augmentedStack += '\nState: ' + JSON.stringify({
    ckey: config?.client?.ckey,
    interface: config?.interface,
    window: config?.window,
  });
  return augmentedStack;
};

/**
 * Store provider for Inferno apps.
 */
export class StoreProvider extends Component {
  getChildContext() {
    const { store } = this.props;
    return { store };
  }

  render() {
    return this.props.children;
  }
}


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Blink, Section } from '../components';

export const meta = {
  title: 'Blink',
  render: () => <Story />,
};

const Story = (props, context) => {
  return (
    <Section>
      <Blink>
        Blink
      </Blink>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { BlockQuote, Section } from '../components';
import { BoxWithSampleText } from './common';

export const meta = {
  title: 'BlockQuote',
  render: () => <Story />,
};

const Story = (props, context) => {
  return (

        <BoxWithSampleText />

  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Box, Section } from '../components';

export const meta = {
  title: 'Box',
  render: () => <Story />,
};

const Story = (props, context) => {
  return (
    <Section>
      <Box bold>
        bold
      </Box>
      <Box italic>
        italic
      </Box>
      <Box opacity={0.5}>
        opacity 0.5
      </Box>
      <Box opacity={0.25}>
        opacity 0.25
      </Box>
      <Box m={2}>
        m: 2
      </Box>
      <Box textAlign="left">
        left
      </Box>
      <Box textAlign="center">
        center
      </Box>
      <Box textAlign="right">
        right
      </Box>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Box, Button, Section } from '../components';

export const meta = {
  title: 'Button',
  render: () => <Story />,
};

const COLORS_SPECTRUM = [
  'red',
  'orange',
  'yellow',
  'olive',
  'green',
  'teal',
  'blue',
  'violet',
  'purple',
  'pink',
  'brown',
  'grey',
];

const COLORS_STATES = [
  'good',
  'average',
  'bad',
  'black',
  'white',
];

const Story = (props, context) => {
  return (

      <Box mb={1}>
        <Button content="Simple" />
        <Button selected content="Selected" />
        <Button altSelected content="Alt Selected" />
        <Button disabled content="Disabled" />
        <Button color="transparent" content="Transparent" />
        <Button icon="cog" content="Icon" />
        <Button icon="power-off" />
        <Button fluid content="Fluid" />
        <Button
          my={1}
          lineHeight={2}
          minWidth={15}
          textAlign="center"
          content="With Box props" />
      </Box>
      <Box mb={1}>
        {COLORS_STATES.map(color => (
          <Button
            key={color}
            color={color}
            content={color} />
        ))}

        {COLORS_SPECTRUM.map(color => (
          <Button
            key={color}
            color={color}
            content={color} />
        ))}

        {COLORS_SPECTRUM.map(color => (
          <Box inline
            mx="7px"
            key={color}
            color={color}>
            {color}
          </Box>
        ))}
      </Box>

  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Box, Button, ByondUi, Section } from '../components';
import { logger } from '../logging';

export const meta = {
  title: 'ByondUi',
  render: () => <Story />,
};

const Story = (props, context) => {
  const [code, setCode] = useLocalState(context,
    'byondUiEvalCode',
    `Byond.winset('${window.__windowId__}', {\n  'is-visible': true,\n})`);
  return (
    <>
      <Section title="Button">
        <ByondUi
          params={{
            type: 'button',
            text: 'Button',
          }} />
      </Section>
      <Section
        title="Make BYOND calls"
        buttons={(
          <Button
            icon="chevron-right"
            onClick={() => setImmediate(() => {
              try {
                const result = new Function('return (' + code + ')')();
                if (result && result.then) {
                  logger.log('Promise');
                  result.then(logger.log);
                }
                else {
                  logger.log(result);
                }
              }
              catch (err) {
                logger.log(err);
              }
            })}>
            Evaluate
          </Button>
        )}>
        <Box
          as="textarea"
          width="100%"
          height="10em"
          onChange={e => setCode(e.target.value)}>
          {code}
        </Box>
      </Section>
    </>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Button, Collapsible, Section } from '../components';
import { BoxWithSampleText } from './common';

export const meta = {
  title: 'Collapsible',
  render: () => <Story />,
};

const Story = (props, context) => {
  return (
    <Section>
      <Collapsible
        title="Collapsible Demo"
        buttons={(
          <Button icon="cog" />
        )}>
        <BoxWithSampleText />
      </Collapsible>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Button, Flex, Section } from '../components';

export const meta = {
  title: 'Flex & Sections',
  render: () => <Story />,
};

const Story = (props, context) => {
  const [grow, setGrow] = useLocalState(
    context, 'fs_grow', 1);
  const [direction, setDirection] = useLocalState(
    context, 'fs_direction', 'column');
  const [fill, setFill] = useLocalState(
    context, 'fs_fill', true);
  const [hasTitle, setHasTitle] = useLocalState(
    context, 'fs_title', true);
  return (
    <Flex
      height="100%"
      direction="column">
      <Flex.Item mb={1}>
        <Section>
          <Button
            fluid
            onClick={() => setDirection(
              direction === 'column' ? 'row' : 'column'
            )}>
            {`Flex direction="${direction}"`}
          </Button>
          <Button
            fluid
            onClick={() => setGrow(Number(!grow))}>
            {`Flex.Item grow={${grow}}`}
          </Button>
          <Button
            fluid
            onClick={() => setFill(!fill)}>
            {`Section fill={${String(fill)}}`}
          </Button>
          <Button
            fluid
            selected={hasTitle}
            onClick={() => setHasTitle(!hasTitle)}>
            {`Section title`}
          </Button>
        </Section>
      </Flex.Item>
      <Flex.Item grow={1}>
        <Flex
          height="100%"
          direction={direction}>
          <Flex.Item
            mr={direction === 'row' && 1}
            mb={direction === 'column' && 1}
            grow={grow}>
            <Section
              title={hasTitle && 'Section 1'}
              fill={fill}>
              Content
            </Section>
          </Flex.Item>
          <Flex.Item grow={grow}>
            <Section
              title={hasTitle && 'Section 2'}
              fill={fill}>
              Content
            </Section>
          </Flex.Item>
        </Flex>
      </Flex.Item>
    </Flex>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Box, DraggableControl, Icon, Input, Knob, LabeledList, NumberInput, Section, Slider } from '../components';

export const meta = {
  title: 'Input',
  render: () => <Story />,
};

const Story = (props, context) => {
  const [
    number,
    setNumber,
  ] = useLocalState(context, 'number', 0);
  const [
    text,
    setText,
  ] = useLocalState(context, 'text', "Sample text");
  return (
    <Section>
      <LabeledList>
        <LabeledList.Item label="Input (onChange)">
          <Input
            value={text}
            onChange={(e, value) => setText(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="Input (onInput)">
          <Input
            value={text}
            onInput={(e, value) => setText(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="NumberInput (onChange)">
          <NumberInput
            animated
            width="40px"
            step={1}
            stepPixelSize={5}
            value={number}
            minValue={-100}
            maxValue={100}
            onChange={(e, value) => setNumber(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="NumberInput (onDrag)">
          <NumberInput
            animated
            width="40px"
            step={1}
            stepPixelSize={5}
            value={number}
            minValue={-100}
            maxValue={100}
            onDrag={(e, value) => setNumber(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="Slider (onDrag)">
          <Slider
            step={1}
            stepPixelSize={5}
            value={number}
            minValue={-100}
            maxValue={100}
            onDrag={(e, value) => setNumber(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="Knob (onDrag)">
          <Knob
            inline
            size={1}
            step={1}
            stepPixelSize={2}
            value={number}
            minValue={-100}
            maxValue={100}
            onDrag={(e, value) => setNumber(value)} />
          <Knob
            ml={1}
            inline
            bipolar
            size={1}
            step={1}
            stepPixelSize={2}
            value={number}
            minValue={-100}
            maxValue={100}
            onDrag={(e, value) => setNumber(value)} />
        </LabeledList.Item>
        <LabeledList.Item label="Rotating Icon">
          <Box inline position="relative">
            <DraggableControl
              value={number}
              minValue={-100}
              maxValue={100}
              dragMatrix={[0, -1]}
              step={1}
              stepPixelSize={5}
              onDrag={(e, value) => setNumber(value)}>
              {control => (
                <Box onMouseDown={control.handleDragStart}>
                  <Icon
                    size={4}
                    color="yellow"
                    name="times"
                    rotation={control.displayValue * 4} />
                  {control.inputElement}
                </Box>
              )}
            </DraggableControl>
          </Box>
        </LabeledList.Item>
      </LabeledList>
    </Section>
  );
};


code:
import { Box, Popper } from "../components";

export const meta = {
  title: "Popper",
  render: () => <Story />,
};

const Story = () => {
  return (
    <>
      <Popper popperContent={
        <Box style={{
          background: "white",
          border: "2px solid blue",
        }}>
          Loogatme!
        </Box>
      } options={{
        placement: "bottom",
      }}>
        <Box style={{
          border: "5px solid white",
          height: "300px",
          width: "200px",
        }} />
      </Popper>

      <Popper popperContent={
        <Box style={{
          background: "white",
          border: "2px solid blue",
        }}>
          I am on the right!
        </Box>
      } options={{
        placement: "right",
      }}>
        <Box style={{
          border: "5px solid white",
          height: "500px",
          width: "100px",
        }} />
      </Popper>
    </>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Box, Button, Input, LabeledList, ProgressBar, Section } from '../components';

export const meta = {
  title: 'ProgressBar',
  render: () => <Story />,
};

const Story = (props, context) => {
  const [
    progress,
    setProgress,
  ] = useLocalState(context, 'progress', 0.5);
  const [
    color,
    setColor,
  ] = useLocalState(context, 'color', '');

  const color_data = color
    ? { color: color }
    : { ranges: {
      good: [0.5, Infinity],
      bad: [-Infinity, 0.1],
      average: [0, 0.5],
    } };

  return (
    <Section>
      <ProgressBar
        {...color_data}
        minValue={-1}
        maxValue={1}
        value={progress}>
        Value: {Number(progress).toFixed(1)}
      </ProgressBar>
      <Box mt={1}>
        <LabeledList mt="2em">
          <LabeledList.Item label="Adjust value">
            <Button
              content="-0.1"
              onClick={() => setProgress(progress - 0.1)} />
            <Button
              content="+0.1"
              onClick={() => setProgress(progress + 0.1)} />
          </LabeledList.Item>
          <LabeledList.Item label="Override color">
            <Input
              value={color}
              onChange={(e, value) => setColor(value)} />
          </LabeledList.Item>
        </LabeledList>
      </Box>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Box, Section, Stack } from '../components';

export const meta = {
  title: 'Stack',
  render: () => <Story />,
};

const Filler = () => (
  <Box inline width={1} height={1}>
    A
  </Box>
);

const SmallStackItems = () => (
  <>
    <Stack.Item>
      <Filler />
    </Stack.Item>
    <Stack.Divider />
    <Stack.Item>
      <Filler />
    </Stack.Item>
  </>
);

const Story = (props, context) => {
  return (
    <Section fill>
      <Stack fill className="debug-layout">
        <SmallStackItems />
        <Stack.Item grow={1}>
          <Stack fill vertical>
            <SmallStackItems />
            <Stack.Item>
              <Stack fill>
                <SmallStackItems />
                <Stack.Item grow={1} />
                <SmallStackItems />
                <SmallStackItems />
              </Stack>
            </Stack.Item>
            <Stack.Item grow={1} />
            <SmallStackItems />
          </Stack>
        </Stack.Item>
      </Stack>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { storage } from 'common/storage';
import { Button, LabeledList, NoticeBox, Section } from '../components';
import { formatSiUnit } from '../format';

export const meta = {
  title: 'Storage',
  render: () => <Story />,
};

const Story = (props, context) => {
  if (!window.localStorage) {
    return (
      <NoticeBox>
        Local storage is not available.
      </NoticeBox>
    );
  }
  return (
    <Section
      title="Local Storage"
      buttons={(
        <Button
          icon="recycle"
          onClick={() => {
            localStorage.clear();
            storage.clear();
          }}>
          Clear
        </Button>
      )}>
      <LabeledList>
        <LabeledList.Item label="Keys in use">
          {localStorage.length}
        </LabeledList.Item>
        <LabeledList.Item label="Remaining space">
          {formatSiUnit(localStorage.remainingSpace, 0, 'B')}
        </LabeledList.Item>
      </LabeledList>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Button, Section, Tabs } from '../components';

export const meta = {
  title: 'Tabs',
  render: () => <Story />,
};

const TAB_RANGE = [
  'Tab #1',
  'Tab #2',
  'Tab #3',
  'Tab #4',
];

const Story = (props, context) => {
  const [tabProps, setTabProps] = useLocalState(context, 'tabProps', {});
  return (
    <>

        <Button.Checkbox
          inline
          content="vertical"
          checked={tabProps.vertical}
          onClick={() => setTabProps({
            ...tabProps,
            vertical: !tabProps.vertical,
          })} />
        <Button.Checkbox
          inline
          content="leftSlot"
          checked={tabProps.leftSlot}
          onClick={() => setTabProps({
            ...tabProps,
            leftSlot: !tabProps.leftSlot,
          })} />
        <Button.Checkbox
          inline
          content="rightSlot"
          checked={tabProps.rightSlot}
          onClick={() => setTabProps({
            ...tabProps,
            rightSlot: !tabProps.rightSlot,
          })} />
        <Button.Checkbox
          inline
          content="icon"
          checked={tabProps.icon}
          onClick={() => setTabProps({
            ...tabProps,
            icon: !tabProps.icon,
          })} />
        <Button.Checkbox
          inline
          content="fluid"
          checked={tabProps.fluid}
          onClick={() => setTabProps({
            ...tabProps,
            fluid: !tabProps.fluid,
          })} />
        <Button.Checkbox
          inline
          content="centered"
          checked={tabProps.centered}
          onClick={() => setTabProps({
            ...tabProps,
            centered: !tabProps.centered,
          })} />

        <TabsPrefab />

        <TabsPrefab />
        Some text

        Section-less tabs appear the same as tabs in a fitted section:

      <TabsPrefab />
    </>
  );
};

const TabsPrefab = (props, context) => {
  const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
  const [tabProps] = useLocalState(context, 'tabProps', {});
  return (
    <Tabs
      vertical={tabProps.vertical}
      fluid={tabProps.fluid}
      textAlign={tabProps.centered && 'center'}>
      {TAB_RANGE.map((text, i) => (
        <Tabs.Tab
          key={i}
          selected={i === tabIndex}
          icon={tabProps.icon && 'info-circle'}
          leftSlot={tabProps.leftSlot && (
            <Button
              circular
              compact
              color="transparent"
              icon="times" />
          )}
          rightSlot={tabProps.rightSlot && (
            <Button
              circular
              compact
              color="transparent"
              icon="times" />
          )}
          onClick={() => setTabIndex(i)}>
          {text}
        </Tabs.Tab>
      ))}
    </Tabs>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { useLocalState } from '../backend';
import { Input, LabeledList, Section } from '../components';

export const meta = {
  title: 'Themes',
  render: () => <Story />,
};

const Story = (props, context) => {
  const [theme, setTheme] = useLocalState(context, 'kitchenSinkTheme');
  return (
    <Section>
      <LabeledList>
        <LabeledList.Item label="Use theme">
          <Input
            placeholder="theme_name"
            value={theme}
            onInput={(e, value) => setTheme(value)} />
        </LabeledList.Item>
      </LabeledList>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Box, Button, Section, Tooltip } from '../components';

export const meta = {
  title: 'Tooltip',
  render: () => <Story />,
};

const Story = () => {
  const positions = [
    'top',
    'left',
    'right',
    'bottom',
    'bottom-start',
    'bottom-end',
  ];

  return (
    <Section>
      <Box>
        <Tooltip content="Tooltip text.">
          <Box inline position="relative" mr={1}>
            Box (hover me).
          </Box>
        </Tooltip>
        <Button
          tooltip="Tooltip text."
          content="Button" />
      </Box>
      <Box mt={1}>
        {positions.map(position => (
          <Button
            key={position}
            color="transparent"
            tooltip="Tooltip text."
            tooltipPosition={position}
            content={position} />
        ))}
      </Box>
    </Section>
  );
};


code:
/**
 * @file
 * @copyright 2021 Aleksej Komarov
 * @license MIT
 */

import { Box } from '../components';

export const BoxWithSampleText = props => {
  return (
    <Box {...props}>
      <Box italic>
        Jackdaws love my big sphinx of quartz.
      </Box>
      <Box mt={1} bold>
        The wide electrification of the southern
        provinces will give a powerful impetus to the
        growth of agriculture.
      </Box>
    </Box>
  );
};


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.candystripe:nth-child(odd) {
  background-color: rgba(0, 0, 0, 0.25);
}


code:
.centered-image {
  position: absolute;
  height: 100%;
  left: 50%;
  top: 50%;
  transform: translateX(-50%) translateY(-50%) scale(0.8);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../colors.scss';

$fg-map: colors.$fg-map !default;
$bg-map: colors.$bg-map !default;

@each $color-name, $color-value in $fg-map {
  .color-#{$color-name} {
    color: $color-value !important;
  }
}

@each $color-name, $color-value in $bg-map {
  .color-bg-#{$color-name} {
    background-color: $color-value !important;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.debug-layout,
.debug-layout *:not(g):not(path) {
	color: rgba(255, 255, 255, 0.9) !important;
	background: transparent !important;
	outline: 1px solid rgba(255, 255, 255, 0.5) !important;
	box-shadow: none !important;
  filter: none !important;

  &:hover {
    outline-color: rgba(255, 255, 255, 0.8) !important;
  }
}


code:
$mqIterations: 19;
@mixin fontResize($iterations) {
  $i: 1;
  @while $i <= $iterations {
    @media all and (min-width: 100px * $i) { .fit-text { font-size:0.1em * $i; } }
    $i: $i + 1;
  }
}

@include fontResize($mqIterations);


code:
@use '../colors.scss';

a {
  &:link, &:visited {
    color: colors.$blue;
  }
  &:hover, &:active {
    color: colors.$primary;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../colors.scss';

.outline-dotted {
  outline-style: dotted !important;
}

.outline-dashed {
  outline-style: dashed !important;
}

.outline-solid {
  outline-style: solid !important;
}

.outline-double {
  outline-style: double !important;
}

.outline-groove {
  outline-style: groove !important;
}

.outline-ridge {
  outline-style: ridge !important;
}

.outline-inset {
  outline-style: inset !important;
}

.outline-outset {
  outline-style: outset !important;
}

$fg-map: colors.$fg-map !default;
$bg-map: colors.$bg-map !default;

@each $color-name, $color-value in $fg-map {
  .outline-color-#{$color-name} {
    outline: 0.167rem solid $color-value !important;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.text-left {
  text-align: left;
}

.text-center {
  text-align: center;
}

.text-right {
  text-align: right;
}

.text-baseline {
  text-align: baseline;
}

.text-justify {
  text-align: justify;
}

.text-nowrap {
  white-space: nowrap;
}

.text-pre {
  white-space: pre;
}

.text-bold {
  font-weight: bold;
}

.text-italic {
  font-style: italic;
}

.text-underline {
  text-decoration: underline;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use 'sass:math';

$color-fg: #ffffff !default;
$color-bg: #252525 !default;
$color-bg-section: rgba(0, 0, 0, 0.33) !default;
$color-bg-grad-spread: 2% !default;
$color-bg-start: color.adjust(
  $color-bg, $lightness: $color-bg-grad-spread) !default;
$color-bg-end: color.adjust(
  $color-bg, $lightness: -$color-bg-grad-spread) !default;

$unit: 12px;
$font-size: 1 * $unit !default;
$border-radius: 0.16em !default;

@function em($px) {
  @return 1em * math.div($px, $unit);
}

@function rem($px) {
  @return 1rem * math.div($px, $unit);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use 'sass:map';
@use 'sass:meta';

// Base colors
$black: #000000 !default;
$white: #ffffff !default;
$red: #db2828 !default;
$orange: #f2711c !default;
$yellow: #fbd608 !default;
$olive: #b5cc18 !default;
$green: #20b142 !default;
$teal: #00b5ad !default;
$blue: #2185d0 !default;
$violet: #6435c9 !default;
$purple: #a333c8 !default;
$pink: #e03997 !default;
$brown: #a5673f !default;
$grey: #767676 !default;
$light-grey: #aaa !default;
$paperplease: rgba(87, 72, 72, 0.6) !default;

$primary: #4972a1 !default;
$good: #5baa27 !default;
$average: #f08f11 !default;
$bad: #db2828 !default;
$label: #7e90a7 !default;

// Background and foreground color lightness ratios
$bg-lightness: -15% !default;
$fg-lightness: 10% !default;

@function bg($color) {
  @return color.scale($color, $lightness: $bg-lightness);
}

@function fg($color) {
  @return color.scale($color, $lightness: $fg-lightness);
}

// Mappings of color names

$_gen_map: (
  'black': $black,
  'white': $white,
  'red': $red,
  'orange': $orange,
  'yellow': $yellow,
  'olive': $olive,
  'green': $green,
  'teal': $teal,
  'blue': $blue,
  'violet': $violet,
  'purple': $purple,
  'pink': $pink,
  'brown': $brown,
  'grey': $grey,
  'light-grey': $light-grey,
  'good': $good,
  'average': $average,
  'bad': $bad,
  'label': $label,
  'paperplease': $paperplease,
);

// Foreground color names for which to generate a color map
$fg-map-keys: map.keys($_gen_map) !default;
// Background color names for which to generate a color map
$bg-map-keys: map.keys($_gen_map) !default;

$fg-map: ();
@each $color-name in $fg-map-keys {
  $fg-map: map-merge($fg-map, (
    $color-name: fg(map.get($_gen_map, $color-name)),
  ));
}

$bg-map: ();
@each $color-name in $bg-map-keys {
  $bg-map: map-merge($bg-map, (
    $color-name: bg(map.get($_gen_map, $color-name)),
  ));
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';

$color-default: colors.fg(colors.$label) !default;

.BlockQuote {
  color: $color-default;
  border-left: base.em(2px) solid $color-default;
  padding-left: 0.5em;
  margin-bottom: 0.5em;

  &:last-child {
    margin-bottom: 0;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

$color-default: colors.bg(colors.$primary) !default;
$color-disabled: #999999 !default;
$color-selected: colors.bg(colors.$green) !default;
$color-caution: colors.bg(colors.$yellow) !default;
$color-danger: colors.bg(colors.$red) !default;
$color-transparent-text: rgba(255, 255, 255, 0.5) !default;
$border-radius: base.$border-radius !default;
$bg-map: colors.$bg-map !default;

@mixin button-color($color) {
  // Adapt text color to background luminance to ensure high contast
  $luminance: luminance($color);
  $text-color: if($luminance > 0.4,
    rgba(0, 0, 0, 1),
    rgba(255, 255, 255, 1));

  transition: color 50ms, background-color 50ms;
  background-color: $color;
  color: $text-color;

  &:hover {
    transition: color 0ms, background-color 0ms;
  }

  &:focus {
    transition: color 100ms, background-color 100ms;
  }

  &:hover,
  &:focus {
    background-color: lighten($color, 30%);
    color: $text-color;
  }
}

.Button {
  position: relative;
  display: inline-block;
  line-height: 1.667em;
  padding: 0 0.5em;
  margin-right: base.em(2px);
  white-space: nowrap;
  outline: 0;
  border-radius: $border-radius;
  margin-bottom: base.em(2px);
  // Disable selection in buttons
  user-select: none;
  -ms-user-select: none;

  &:last-child {
    margin-right: 0;
    margin-bottom: 0;
  }

  .fa, .fas, .far {
    margin-left: -0.25em;
    margin-right: -0.25em;
    min-width: 1.333em;
    text-align: center;
  }
}

.Button--hasContent {
  // Add a margin to the icon to keep it separate from the text
  .fa, .fas, .far {
    margin-right: 0.25em;
  }
}

.Button--hasContent.Button--iconPosition--right {
  .fa, .fas, .far {
    margin-right: 0px;
    margin-left: 3px;
  }
}

.Button--ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
}

.Button--fluid {
  display: block;
  margin-left: 0;
  margin-right: 0;
}

.Button--circular {
  border-radius: 50%;
}

.Button--compact {
  padding: 0 0.25em;
  line-height: 1.333em;
}

@each $color-name, $color-value in $bg-map {
  .Button--color--#{$color-name} {
    @include button-color($color-value);
  }
}

.Button--color--default {
  @include button-color($color-default);
}

.Button--color--caution {
  @include button-color($color-caution);
}

.Button--color--danger {
  @include button-color($color-danger);
}

.Button--color--transparent {
  @include button-color(base.$color-bg);
  background-color: rgba(base.$color-bg, 0);
  color: $color-transparent-text;
}

.Button--disabled {
  background-color: $color-disabled !important;
}

.Button--selected {
  @include button-color($color-selected);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.ColorBox {
  display: inline-block;
  width: 1em;
  height: 1em;
  line-height: 1em;
  text-align: center;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.Dimmer {
  // Align everything in the middle.
  // A fat middle finger for anything less than IE11.
  display: flex;
  justify-content: center;
  align-items: center;
  // Fill positioned parent
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  // Dim everything around it
  background-color: rgba(0, 0, 0, 0.75);
  z-index: 1;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

$color: rgba(255, 255, 255, 0.1) !default;
$thickness: base.em(2px) !default;
$spacing: 0.5em;

.Divider--horizontal {
  margin: $spacing 0;

  &:not(.Divider--hidden) {
    border-top: $thickness solid $color;
  }
}

.Divider--vertical {
  height: 100%;
  margin: 0 $spacing;

  &:not(.Divider--hidden) {
    border-left: $thickness solid $color;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

.Dropdown {
  position: relative;
}

.Dropdown__control {
  position: relative;
  display: inline-block;
  font-family: Verdana, sans-serif;
  font-size: base.em(12px);
  width: base.em(100px);
  line-height: base.em(17px);
  user-select: none;
}

.Dropdown__arrow-button {
  float: right;
  padding-left: 0.35em;
  width: 1.2em;
  height: base.em(22px);
  border-left: base.em(1px) solid #000;
  border-left: base.em(1px) solid rgba(0, 0, 0, 0.25);
}

.Dropdown__menu {
  position: absolute;
  overflow-y: auto;
  z-index: 5;
  width: base.em(100px);
  max-height: base.em(200px);
  overflow-y: scroll;
  border-radius: 0 0 base.em(2px) base.em(2px);
  color: #fff;
  background-color: #000;
  background-color: rgba(0, 0, 0, 0.75);
}

.Dropdown__menu-noscroll {
  position: absolute;
  overflow-y: auto;
  z-index: 5;
  width: base.em(100px);
  max-height: base.em(200px);
  border-radius: 0 0 base.em(2px) base.em(2px);
  color: #fff;
  background-color: #000;
  background-color: rgba(0, 0, 0, 0.75);
}

.Dropdown__menuentry {
  padding: base.em(2px) base.em(4px);
  font-family: Verdana, sans-serif;
  font-size: base.em(12px);
  line-height: base.em(17px);
  transition: background-color 100ms ease-out;

  &:hover {
    background-color: rgba(255, 255, 255, 0.2);
    transition: background-color 0ms;
  }
}

.Dropdown__over {
  top: auto;
  bottom: 100%
}

.Dropdown__selected-text {
  display: inline-block;
  text-overflow: ellipsis;
  white-space: nowrap;
  height: base.em(17px);
  width: calc(100% - 1.2em);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.Flex {
  display: -ms-flexbox;
  display: flex;
}

.Flex--inline {
  display: inline-flex;
}

.Flex--iefix {
  display: block;
}

.Flex--iefix.Flex--inline {
  display: inline-block;
}

.Flex__item--iefix {
  display: inline-block;
}

.Flex--iefix--column {
  & > .Flex__item--iefix {
    display: block;
  }
}


code:
/**
 * @file
 * @copyright 2020
 * @author ThePotato97 (https://github.com/ThePotato97)
 * @license ISC
 */

.IconStack > .Icon {
  position: absolute;
  width: 100%;
  text-align: center;
}

.IconStack {
  position: relative;
  display: inline-block;
  height: 1.2em;
  line-height: 2em;
  vertical-align: middle;

  &:after {
    color: transparent;
    content: '.';
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../functions.scss' as *;

$text-color: base.$color-fg !default;
$background-color: #0a0a0a !default;
$border-color: #88bfff !default;
$border-radius: base.$border-radius !default;

.Input {
  position: relative;
  display: inline-block;
  width: base.em(120px);
  border: base.em(1px) solid $border-color;
  border: base.em(1px) solid rgba($border-color, 0.75);
  border-radius: $border-radius;
  color: $text-color;
  background-color: $background-color;
  padding: 0 base.em(4px);
  margin-right: base.em(2px);
  line-height: base.em(17px);
  overflow: visible;
}

.Input--fluid {
  display: block;
  width: auto;
}

.Input__baseline {
  display: inline-block;
  color: transparent;
}

.Input__input {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border: 0;
  outline: 0;
  width: 100%;
  font-size: base.em(12px);
  line-height: base.em(17px);
  height: base.em(17px);
  margin: 0;
  padding: 0 0.5em;
  font-family: Verdana, sans-serif;
  background-color: transparent;
  color: $text-color;
  color: inherit;

  &:-ms-input-placeholder {
    font-style: italic;
    color: #777;
    color: rgba(255, 255, 255, 0.45);
  }
}

.Input--monospace .Input__input {
  font-family: 'Consolas', monospace;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

$bg-map: colors.$bg-map !default;
$fg-map: colors.$fg-map !default;
$ring-color: #6a96c9 !default;
$knob-color: #333333 !default;
$popup-background-color: #000000 !default;
$popup-text-color: #ffffff !default;

$inner-padding: 0.1em;

.Knob {
  position: relative;
  font-size: 1rem;
  width: 2.6em;
  height: 2.6em;
  margin: 0 auto;
  margin-bottom: -0.2em;
  cursor: n-resize;

  // Adjusts a baseline in a way, that makes knob middle-aligned
  // when it flows with the text.
  &:after {
    content: '.';
    color: transparent;
    line-height: 2.5em;
  }
}

.Knob__circle {
  position: absolute;
  top: $inner-padding;
  bottom: $inner-padding;
  left: $inner-padding;
  right: $inner-padding;
  margin: 0.3em;
  background-color: $knob-color;
  background-image: linear-gradient(to bottom,
    rgba(255, 255, 255, 0.15) 0%,
    rgba(255, 255, 255, 0) 100%);
  border-radius: 50%;
  box-shadow: 0 0.05em 0.5em 0 rgba(0, 0, 0, 0.5);
}

.Knob__cursorBox {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.Knob__cursor {
  position: relative;
  top: 0.05em;
  margin: 0 auto;
  width: 0.2em;
  height: 0.8em;
  background-color: rgba(255, 255, 255, 0.9);
}

.Knob__popupValue {
  position: absolute;
  top: -2rem;
  right: 50%;
  font-size: 1rem;
  text-align: center;
  padding: 0.25rem 0.5rem;
  color: $popup-text-color;
  background-color: $popup-background-color;
  transform: translateX(50%);
  white-space: nowrap;
}

.Knob__ring {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  padding: $inner-padding;
}

$pi: 3.1416;

.Knob__ringTrackPivot {
  transform: rotateZ(135deg);
}

.Knob__ringTrack {
  // transform-origin: 50% 50%;
  fill: transparent;
  stroke: rgba(255, 255, 255, 0.1);
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 75 * $pi;
}

.Knob__ringFillPivot {
  transform: rotateZ(135deg);
}

.Knob--bipolar .Knob__ringFillPivot {
  transform: rotateZ(270deg);
}

.Knob__ringFill {
  fill: transparent;
  stroke: $ring-color;
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 100 * $pi;
  transition: stroke 50ms ease-out;
}

@each $color-name, $color-value in $fg-map {
  .Knob--color--#{$color-name} {
    .Knob__ringFill {
      stroke: $color-value;
    }
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

.LabeledList {
  display: table;
  // IE8: Does not support calc
  width: 100%;
  // Compensate for negative margin
  width: calc(100% + 1em);
  border-collapse: collapse;
  border-spacing: 0;
  margin: -0.25em -0.5em;
  margin-bottom: 0;
  padding: 0;
}

.LabeledList__row {
  display: table-row;
}

.LabeledList__row:last-child .LabeledList__cell {
  padding-bottom: 0;
}

.LabeledList__cell {
  display: table-cell;
  margin: 0;
  padding: 0.25em 0.5em;
  border: 0;
  text-align: left;
}

.LabeledList__label {
  width: 1%;
  white-space: nowrap;
  min-width: 5em;
}

.LabeledList__buttons {
  width: 0.1%;
  white-space: nowrap;
  text-align: right;
  padding-top: base.em(1px);
  padding-bottom: 0;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

$background-color: base.$color-bg !default;

.Modal {
  background-color: $background-color;
  max-width: calc(100% - 1rem);
  padding: 1rem;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

// NoticeBox
$background-color: #bb9b68 !default;
$color-stripes: rgba(0, 0, 0, 0.1) !default;
$color-border: #272727 !default;
$bg-map: colors.$bg-map !default;

.NoticeBox {
  // Adapt text color to background luminance to ensure high contast
  $luminance: luminance($background-color);
  $text-color: if($luminance > 0.35,
    rgba(0, 0, 0, 1),
    rgba(255, 255, 255, 1));

  padding: 0.33em 0.5em;
  margin-bottom: 0.5em;
  box-shadow: none;
  font-weight: bold;
  font-style: italic;
  color: $text-color;
  background-color: $background-color;
  background-image: repeating-linear-gradient(
    -45deg,
    transparent,
    transparent base.em(10px),
    $color-stripes base.em(10px),
    $color-stripes base.em(20px));
}

@mixin box-color($color) {
  $luminance: luminance($color);
  $text-color: if($luminance > 0.35,
    rgba(0, 0, 0, 1),
    rgba(255, 255, 255, 1));
  color: $text-color;
  background-color: color.adjust(
    $color,
    $saturation: -15%,
    $lightness: -15%);
}

@each $color-name, $color-value in $bg-map {
  .NoticeBox--color--#{$color-name} {
    @include box-color($color-value)
  }
}

.NoticeBox--type--info {
  @include box-color(colors.$blue);
}

.NoticeBox--type--success {
  @include box-color(colors.$green);
}

.NoticeBox--type--warning {
  @include box-color(colors.$orange);
}

.NoticeBox--type--danger {
  @include box-color(colors.$red);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use '../base.scss';
@use '../functions.scss' as *;
@use './Input.scss';

$text-color: Input.$text-color !default;
$background-color: Input.$background-color !default;
$border-color: Input.$border-color !default;
$border-radius: Input.$border-radius !default;

.NumberInput {
  position: relative;
  display: inline-block;
  border: base.em(1px) solid $border-color;
  border: base.em(1px) solid rgba($border-color, 0.75);
  border-radius: $border-radius;
  color: $border-color;
  background-color: $background-color;
  padding: 0 base.em(4px);
  margin-right: base.em(2px);
  line-height: base.em(17px);
  text-align: right;
  overflow: visible;
  cursor: n-resize;
}

.NumberInput--fluid {
  display: block;
}

.NumberInput__content {
  margin-left: 0.5em;
}

.NumberInput__barContainer {
  position: absolute;
  top: base.em(2px);
  bottom: base.em(2px);
  left: base.em(2px);
}

.NumberInput__bar {
  position: absolute;
  bottom: 0;
  left: 0;
  width: base.em(3px);
  box-sizing: border-box;
  border-bottom: base.em(1px) solid $border-color;
  background-color: $border-color;
}

.NumberInput__input {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border: 0;
  outline: 0;
  width: 100%;
  font-size: base.em(12px);
  line-height: base.em(17px);
  height: base.em(17px);
  margin: 0;
  padding: 0 0.5em;
  font-family: Verdana, sans-serif;
  background-color: $background-color;
  color: $text-color;
  text-align: right;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

$color-default-fill: colors.bg(colors.$primary) !default;
$background-color: rgba(0, 0, 0, 0) !default;
$border-radius: base.$border-radius !default;
$bg-map: colors.$bg-map !default;

.ProgressBar {
  display: inline-block;
  position: relative;
  width: 100%;
  padding: 0 0.5em;
  border-width: base.em(1px) !important;
  border-style: solid !important;
  border-radius: $border-radius;
  background-color: $background-color;
  transition: border-color 900ms ease-out;
}

.ProgressBar__fill {
  position: absolute;
  top: -0.5px;
  left: 0px;
  bottom: -0.5px;
}

.ProgressBar__fill--animated {
  transition: background-color 900ms ease-out, width 900ms ease-out;
}

.ProgressBar__content {
  position: relative;
  line-height: base.em(17px);
  width: 100%;
  text-align: right;
}

.ProgressBar--color--default {
  border: base.em(1px) solid $color-default-fill;

  .ProgressBar__fill {
    background-color: $color-default-fill;
  }
}

@each $color-name, $color-value in $bg-map {
  .ProgressBar--color--#{$color-name} {
    border-color: $color-value !important;

    .ProgressBar__fill {
      background-color: $color-value;
    }
  }
}


code:
/**
 * Copyright (c) 2020 bobbahbrown (https://github.com/bobbahbrown)
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

$fg-map: colors.$fg-map !default;
$ring-color: #6a96c9 !default;

.RoundGauge {
  font-size: 1rem;
  width: 2.6em;
  height: 1.3em;
  margin: 0 auto;
  margin-bottom: 0.2em;
}

$pi: 3.1416;

.RoundGauge__ringTrack {
    fill: transparent;
    stroke: rgba(255, 255, 255, 0.1);
    stroke-width: 10;
    stroke-dasharray: 50 * $pi;
    stroke-dashoffset: 50 * $pi;
}

.RoundGauge__ringFill {
  fill: transparent;
  stroke: $ring-color;
  stroke-width: 10;
  stroke-dasharray: 100 * $pi;
  transition: stroke 50ms ease-out;
}

.RoundGauge__needle, .RoundGauge__ringFill {
  transition: transform 50ms ease-in-out;
}

.RoundGauge__needleLine, .RoundGauge__needleMiddle {
  fill: colors.$bad;
}

.RoundGauge__alert {
  fill-rule: evenodd;
  clip-rule: evenodd;
  stroke-linejoin: round;
  stroke-miterlimit: 2;
  fill: rgba(255, 255, 255, 0.1);
}

.RoundGauge__alert.max {
  fill: colors.$bad;
}

@each $color-name, $color-value in $fg-map {
  .RoundGauge--color--#{$color-name}.RoundGauge__ringFill {
    stroke: $color-value;
  }
}

@each $color-name, $color-value in $fg-map {
  .RoundGauge__alert--#{$color-name} {
    fill: $color-value;
    transition: opacity 0.6s cubic-bezier(0.25, 1, 0.5, 1);
    animation: RoundGauge__alertAnim 1s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
  }
}

@keyframes RoundGauge__alertAnim {
  0% {
    opacity: 0.1;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0.1;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss';

$title-text-color: base.$color-fg !default;
$background-color: base.$color-bg-section !default;
$separator-color: colors.$primary !default;

.Section {
  position: relative;
  margin-bottom: 0.5em;
  background-color: functions.fake-alpha($background-color, base.$color-bg);
  background-color: $background-color;
  box-sizing: border-box;

  &:last-child {
    margin-bottom: 0;
  }
}

.Section__title {
  position: relative;
  padding: 0.5em;
  border-bottom: base.em(2px) solid $separator-color;
}

.Section__titleText {
  font-size: base.em(14px);
  font-weight: bold;
  color: $title-text-color;
}

.Section__buttons {
  position: absolute;
  display: inline-block;
  right: 0.5em;
  margin-top: base.em(-1px);
}

.Section__rest {
  position: relative;
}

.Section__content {
  padding: 0.66em 0.5em;
}

.Section--fitted > .Section__rest > .Section__content {
  padding: 0;
}

.Section--fill {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.Section--fill > .Section__rest {
  flex-grow: 1;
}

.Section--fill > .Section__rest > .Section__content {
  height: 100%;
}

.Section--fill.Section--scrollable > .Section__rest > .Section__content {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.Section--fill.Section--iefix {
  display: table !important;
  width: 100% !important;
  height: 100% !important;
  border-collapse: collapse;
  border-spacing: 0;

  & > .Section__rest {
    display: table-row !important;
    height: 100% !important;
  }
}

.Section--scrollable {
  overflow-x: hidden;
  overflow-y: hidden;

  & > .Section__rest > .Section__content {
    overflow-y: scroll;
    overflow-x: hidden;
  }
}

// Nested sections
.Section .Section {
  background-color: transparent;
  margin-left: -0.5em;
  margin-right: -0.5em;

  // Remove extra space above the first nested section
  &:first-child {
    margin-top: -0.5em;
  }
}

// Level 2 section title
.Section .Section .Section__titleText {
  font-size: base.em(13px);
}

// Level 3 section title
.Section .Section .Section .Section__titleText {
  font-size: base.em(12px);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

$cursor-color: base.$color-fg !default;
$popup-background-color: #000000 !default;
$popup-text-color: #ffffff !default;

.Slider {
  cursor: e-resize;
}

.Slider__cursorOffset {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  transition: none !important;
}

.Slider__cursor {
  position: absolute;
  top: 0;
  right: base.em(-1px);
  bottom: 0;
  width: 0;
  border-left: base.em(2px) solid $cursor-color;
}

.Slider__pointer {
  position: absolute;
  right: base.em(-5px);
  bottom: base.em(-4px);
  width: 0;
  height: 0;
  border-left: base.em(5px) solid transparent;
  border-right: base.em(5px) solid transparent;
  border-bottom: base.em(5px) solid $cursor-color;
}

.Slider__popupValue {
  position: absolute;
  right: 0;
  top: -2rem;
  font-size: 1rem;
  padding: 0.25rem 0.5rem;
  color: $popup-text-color;
  background-color: $popup-background-color;
  transform: translateX(50%);
  white-space: nowrap;
}


code:
/**
 * Copyright (c) 2021 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use './Divider.scss';

.Stack--fill {
  height: 100%;
}

.Stack--horizontal > .Stack__item {
  margin-left: 0.5em;

  &:first-child {
    margin-left: 0;
  }
}

.Stack--vertical > .Stack__item {
  margin-top: 0.5em;

  &:first-child {
    margin-top: 0;
  }
}

.Stack--horizontal > .Stack__divider:not(.Stack__divider--hidden) {
  border-left: Divider.$thickness solid Divider.$color;
}

.Stack--vertical > .Stack__divider:not(.Stack__divider--hidden) {
  border-top: Divider.$thickness solid Divider.$color;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.Table {
  display: table;
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
  margin: 0;
}

.Table--collapsing {
  width: auto;
}

.Table__row {
  display: table-row;
}

.Table__cell {
  display: table-cell;
  padding: 0 0.25em;

  &:first-child {
    padding-left: 0;
  }

  &:last-child {
    padding-right: 0;
  }
}

.Table__row--header .Table__cell,
.Table__cell--header {
  font-weight: bold;
  padding-bottom: 0.5em;
}

.Table__cell--collapsing {
  width: 1%;
  white-space: nowrap;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use 'sass:math';
@use '../base.scss';
@use '../colors.scss';

$color-default: color.scale(colors.fg(colors.$primary), $lightness: 75%) !default;
$text-color: rgba(255, 255, 255, 0.5) !default;
$text-color-selected: color.scale($color-default, $lightness: 25%) !default;
$tab-color: transparent !default;
$tab-color-hovered: rgba(255, 255, 255, 0.075) !default;
$tab-color-selected: rgba(255, 255, 255, 0.125) !default;
$border-radius: base.$border-radius !default;
$fg-map: colors.$fg-map !default;

.Tabs {
  display: flex;
  align-items: stretch;
  overflow: hidden;
  background-color: base.$color-bg-section;
}

.Tabs--fill {
  height: 100%;
}

// Interoperability with sections
.Section .Tabs {
  background-color: transparent;
}

.Section:not(.Section--fitted) .Tabs {
  margin: 0 -0.5em 0.5em;

  &:first-child {
    margin-top: -0.5em;
  }
}

.Tabs--vertical {
  flex-direction: column;
  padding: 0.25em 0 0.25em 0.25em;
}

.Tabs--horizontal {
  margin-bottom: 0.5em;
  padding: 0.25em 0.25em 0 0.25em;

  &:last-child {
    margin-bottom: 0;
  }
}

.Tabs__Tab {
  flex-grow: 0;
}

.Tabs--fluid .Tabs__Tab {
  flex-grow: 1;
}

.Tab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background-color: $tab-color;
  color: $text-color;
  min-height: 2.25em;
  min-width: 4em;
}

.Tab:not(.Tab--selected):hover {
  background-color: $tab-color-hovered;
}

.Tab--selected {
  background-color: $tab-color-selected;
  color: $text-color-selected;
}

.Tab__text {
  flex-grow: 1;
  margin: 0 0.5em;
}

.Tab__left {
  min-width: 1.5em;
  text-align: center;
  margin-left: 0.25em;
}

.Tab__right {
  min-width: 1.5em;
  text-align: center;
  margin-right: 0.25em;
}

.Tabs--horizontal {
  .Tab {
    border-top: math.div(1em, 6) solid transparent;
    border-bottom: math.div(1em, 6) solid transparent;
    border-top-left-radius: 0.25em;
    border-top-right-radius: 0.25em;
  }

  .Tab--selected {
    border-bottom: math.div(1em, 6) solid $color-default;
  }
}

.Tabs--vertical {
  .Tab {
    min-height: 2em;
    border-left: math.div(1em, 6) solid transparent;
    border-right: math.div(1em, 6) solid transparent;
    border-top-left-radius: 0.25em;
    border-bottom-left-radius: 0.25em;
  }

  .Tab--selected {
    border-right: math.div(1em, 6) solid $color-default;
  }
}

@each $color-name, $color-value in $fg-map {
  .Tab--selected.Tab--color--#{$color-name} {
    color: color.scale($color-value, $lightness: 25%);
  }

  .Tabs--horizontal .Tab--selected.Tab--color--#{$color-name} {
    border-bottom-color: $color-value;
  }

  .Tabs--vertical .Tab--selected.Tab--color--#{$color-name} {
    border-right-color: $color-value;
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../functions.scss' as *;
@use './Input.scss';

$text-color: Input.$text-color !default;
$background-color: Input.$background-color !default;
$border-color: Input.$border-color !default;
$border-radius: Input.$border-radius !default;

.TextArea {
  position: relative;
  display: inline-block;
  border: base.em(1px) solid $border-color;
  border: base.em(1px) solid rgba($border-color, 0.75);
  border-radius: $border-radius;
  background-color: $background-color;
  margin-right: base.em(2px);
  line-height: base.em(17px);
  box-sizing: border-box;
  width: 100%;
}

.TextArea--fluid {
  display: block;
  width: auto;
  height: auto;
}

.TextArea__textarea {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border: 0;
  outline: 0;
  width: 100%;
  height: 100%;
  font-size: 1em;
  line-height: base.em(17px);
  min-height: base.em(17px);
  margin: 0;
  padding: 0 0.5em;
  font-family: inherit;
  background-color: transparent;
  color: inherit;
  box-sizing: border-box;
  // Make sure the div and the textarea wrap words in the same way
  word-wrap: break-word;
  overflow: hidden;

  &:-ms-input-placeholder {
    font-style: italic;
    color: #777;
    color: rgba(255, 255, 255, 0.45);
  }
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../functions.scss'as *;

$color: #ffffff !default;
$background-color: #000000 !default;
$border-radius: base.$border-radius !default;

.Tooltip {
  z-index: 2;
  padding: 0.5em 0.75em;
  pointer-events: none;
  text-align: left;
  transition: opacity 150ms ease-out;
  background-color: $background-color;
  color: $color;
  box-shadow: 0.1em 0.1em 1.25em -0.1em rgba(0, 0, 0, 0.5);
  border-radius: $border-radius;
  max-width: base.em(250px);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use 'sass:map';
@use 'sass:math';
@use 'sass:meta';

//  Type-casting
// --------------------------------------------------------

// Get a unit-less numeric value
@function num($value) {
  @if meta.type-of($value) != number {
    @error 'Could not convert `#{$value}` - must be `type-of number`';
    @return null;
  }
  @if math.unit($value) == '%' {
    @return math.div($value, 100%);
  }
  @return math.div($value, $value * 0 + 1);
}

//  Color
// --------------------------------------------------------

// Increases perceptual color lightness.
@function lighten($color, $percent) {
  $scaled: hsl(
    color.hue($color),
    color.saturation($color),
    color.lightness($color) * (1 + num($percent)));
  $mixed: color.mix(#ffffff, $color, 100% * num($percent));
  @return color.mix($scaled, $mixed, 75%);
}

// Returns the NTSC luminance of `$color` as a float (between 0 and 1).
// 1 is pure white, 0 is pure black.
@function luminance($color) {
  $colors: (
    'red': color.red($color),
    'green': color.green($color),
    'blue': color.blue($color)
  );

  @each $name, $value in $colors {
    $adjusted: 0;
    $value: math.div($value, 255);
    @if $value < 0.03928 {
      $value: math.div($value, 12.92);
    }
    @else {
      $value: math.div($value + .055, 1.055);
      $value: math.pow($value, 2.4);
    }
    $colors: map.merge($colors, ($name: $value));
  }

  @return (map.get($colors, 'red') * .2126)
    + (map.get($colors, 'green') * .7152)
    + (map.get($colors, 'blue') * .0722);
}

// Blends an RGBA color with a static background color based on its
// alpha channel. Returns an RGB color, which is compatible with IE8.
@function fake-alpha($color-rgba, $color-background) {
  @return color.mix(
    color.change($color-rgba, $alpha: 1),
    $color-background,
    color.alpha($color-rgba) * 100%);
}


code:
/**
 * Copyright (c) 2020 bobbahbrown (https://github.com/bobbahbrown)
 * SPDX-License-Identifier: MIT
 */

@use '../colors.scss';

.AlertModal__Message {
  text-align: center;
  justify-content: center;
}

.AlertModal__Buttons {
  justify-content: center;
}

.AlertModal__Loader {
  width: 100%;
  position: relative;
  height: 4px;
}

.AlertModal__LoaderProgress {
  position: absolute;
  transition: background-color 500ms ease-out, width 500ms ease-out;
  background-color: colors.bg(colors.$primary);
  height: 100%;
}


code:
@use '../base.scss';

$background-color: rgba(0, 0, 0, 0.33) !default;

.CameraConsole__left {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: base.em(220px);
}

.CameraConsole__right {
  position: absolute;
  top: 0;
  bottom: 0;
  left: base.em(220px);
  right: 0;
  background-color: $background-color;
}

.CameraConsole__toolbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2em;
  line-height: 2em;
  margin: 0.25em 1em 0;
}

.CameraConsole__toolbarRight {
  position: absolute;
  top: 0;
  right: 0;
  height: 2em;
  line-height: 2em;
  margin: 0.33em 0.5em 0;
}

.CameraConsole__map {
  position: absolute;
  top: base.em(26px);
  bottom: 0;
  left: 0;
  right: 0;
  margin: 0.5em;
  text-align: center;

  .NoticeBox {
    margin-top: calc(50% - 2em);
  }
}


code:
.Changelog {
  &__Button {
    height: 22px;
  }

  &__Cell {
    padding: 3px 0;

    &--Icon {
      width: 25px;
    }
  }
}


code:
@use '../colors.scss';

$department_map: (
  'Command': colors.$yellow,
  'Security': colors.$red,
  'Engineering': colors.$orange,
  'Medical': colors.$teal,
  'Misc': colors.$white,
  'Science': colors.$purple,
  'Supply': colors.$brown,
  'Service': colors.$green,
  'Silicon': colors.$pink,
);

.CrewManifest {
  @each $department-name, $color-value in $department_map {
    &--#{$department-name} {
      .Section {
        &__title {
          border-color: $color-value;
        }
        &__titleText {
          color: $color-value;
        }
      }
    }
  }

  &__Cell {
    padding: 3px 0;

    &--Rank {
      color: colors.$label;
    }
  }

  &__Icons {
    padding: 3px 9px;
    text-align: right;
  }

  &__Icon {
    color: colors.$label;
    position: relative;

    &:not(:last-child) {
      margin-right: 7px;
    }

    &--Chevron {
      padding-right: 2px;
    }

    &--Command {
      color: colors.$yellow;
    }
  }
}


code:
.ExperimentTechwebServer__Web,
.ExperimentConfigure__ExperimentPanel {
  background: #000000;
  border: 1px solid #40628a;
  margin: 3px 0;
}

.ExperimentTechwebServer__WebHeader {
  background: #40628a;
  padding: 2px;
}

.ExperimentTechwebServer__WebName {
  font-size: 18px;
}

.ExperimentTechwebServer__WebContent {
  padding: 4px;
}

.ExperimentTechwebServer__WebContent > .LabeledList {
  margin: 0.25rem 0.25rem 0.25rem 1rem;
}

.ExperimentConfigure__ExperimentName {
  font-weight: bold;
  border-radius: 0;
}

.ExperimentConfigure__ExperimentContent {
  padding: 0.25rem 1.5rem 0.25rem 0.25rem;
}

.ExperimentStage__Indicator {
  font-weight: bold;
  margin-right: 1rem;
  text-align: center;
}

.ExperimentStage__StageContainer.complete .ExperimentStage__Description {
  opacity: 0.4;
  text-decoration: line-through;
}

.ExperimentStage__StageContainer {
  margin-bottom: 5px;
}

.ExperimentStage__Table {
  border-collapse: separate;
  border-spacing: 0.25rem 0.25rem;
}

.ExperimentConfigure__PerformExperiment {
  text-align: center;
  padding: 0.75rem 0;
}

.ExperimentConfigure__ExperimentsContainer {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.ExperimentConfigure__ExperimentsContainer > :last-child {
  flex: 1;
  overflow-y: auto;
}

// Required for the time being otherwise we'll end up coloring the tooltip
// by accident when we color the tag.
.ExperimentConfigure__TagContainer {
  position: absolute;
  right: 0;
  top: 0;
}

.ExperimentConfigure__PerformanceHint * {
  position: absolute;
  width: 100%;
  height: 100%;
  right: 0;
  top: 0;
  color: rgba(255, 255, 255, 0.5);
}


code:
@use '../colors.scss';
@use '../base.scss';

$bg-map: colors.$bg-map !default;

.HellishRunes__demonrune {
  font-size: 20px;
  margin-top: -240px;
  animation-duration: 5s;
  animation-name: demonrune;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes demonrune {
  from {
    transform: translate(0px, 0px);
  }
  to {
    transform: translate(0px,194px);
  }
}


code:
@use '../colors.scss';

/// Index @{

.hypertorus-layout {
  margin-right: -0.5em;
};

.hypertorus-layout > * {
  margin-bottom: 0.5em;
  margin-right: 0.5em;
};

/// @}

/// Recipes @{

// Subtle horizontal stripes for the table
.hypertorus-recipes__row:nth-child(odd) {
  background-color: rgba(0, 0, 0, .37);
};
.hypertorus-recipes__row:nth-child(even) {
  background-color: rgba(14, 14, 14, .37);
};

// Highlight an active row
.hypertorus-recipes__activerow:nth-child(odd) {
  background-color: rgba(20, 50, 81, .37);
};
.hypertorus-recipes__activerow:nth-child(even) {
  background-color: rgba(29, 61, 98, .37);
};

// Headers shouldn't get stripes, but should take the label text color.
// Headers get left borders as a separator, except if they're furtherest left.
.hypertorus-recipes__row.Table__row--header {
  color: colors.fg(colors.$label);
  background-color: rgba(0,0,0,0);
};
.hypertorus-recipes__row.Table__row--header > td {
  border-left: 1px solid colors.bg(colors.$label);
  text-align: center;
};
.hypertorus-recipes__row.Table__row--header > td:first-child {
  border-left: none;
};

// Recipe icons are red, unless they're in a header
.hypertorus-recipes__icon {
  text-align: center;
  position: relative;
  color: colors.fg(colors.$red);
  width: 10px;
};
.hypertorus-recipes__row.Table__row--header * .hypertorus-recipes__icon {
  color: colors.fg(colors.$label);
};

/// @}

/// Temperatures @{

// Note: Height must be kept in sync with Hypertorus/Temperatures.js
$height: 200px;

// Calculate the space needed for the labels to the left of the chart
$yAxisLabelWidth: 60px;
$yAxisIconPadding: 2px;
$yAxisIconWidth: 14px;
$yAxisMargin: $yAxisLabelWidth + $yAxisIconWidth + $yAxisIconPadding * 2;

// Relative container respects the padding provided by Section
.hypertorus-temperatures__container {
  position: relative;
  margin-right: 10px;
  overflow-y: hidden;
}

.hypertorus-temperatures__y-axis-marks {
  margin-left: 1px;
  margin-right: 1px;
  width: $yAxisMargin;
}

.hypertorus-temperatures__x-axis {
  background-color: colors.bg(colors.$label);
  position: absolute;
  top: $height;
  height: 1px;
  left: $yAxisMargin;
  right: 0;
};

.hypertorus-temperatures__y-axis {
  background-color: colors.bg(colors.$label);
  position: absolute;
  width: 1px;
  left: $yAxisMargin;
  height: $height;
};

// Note: top set in js
.hypertorus-temperatures__y-axis-tick-anchor {
  position: absolute;
  left: 0px;
  right: 0;
};

.hypertorus-temperatures__y-axis-tick {
  background-color: colors.bg(colors.$label);
  position: absolute;
  left: $yAxisMargin;
  right: 0;
  height: 1px;
};

.hypertorus-temperatures__y-axis-label {
  text-align: right;
  color: colors.fg(colors.$label);
  position: absolute;
  top: 0;
  left: 0;
  width: $yAxisMargin;
};

.hypertorus-temperatures__y-axis-icon {
  display: inline-block;
  margin-right: $yAxisIconPadding;
}

.hypertorus-temperatures__chart {
  left: 0;
  right: 0;
  margin-left: $yAxisMargin;
};

.hypertorus-temperatures__vertical-bar {
  height: $height;
  position: relative;
  width: 17px;
  bottom: 0;
};

// Note: top set in js
.hypertorus-temperatures__vertical-bar > * {
  position: absolute;
  left: -0.5px;
  right: -0.5px;
  bottom: 0px;
  border-radius: 0.16em;
};

.hypertorus__unselectable {
  user-select: none;
  -ms-user-select: none;
};

/// @}


code:
@use '../colors.scss';
@use '../base.scss';

$fg-map: colors.$fg-map !default;

.CircuitInfo__Examined {
  background-color: rgba(0, 0, 0, 1);
  padding: 8px;
  border-radius: 5px;

  user-select: none;
  pointer-events: none;
}

.ObjectComponent__Titlebar {
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;

  white-space: nowrap;

  -ms-user-select: none;
  user-select: none;
}

.ObjectComponent__Content {
  white-space: nowrap;
  background-color: rgba(0, 0, 0, 0.5);

  -ms-user-select: none;
  user-select: none;
}

.ObjectComponent__Port {
  position: relative;
  width: 13px;
  height: 13px;
}

.ObjectComponent__PortPos {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

@each $color-name, $color-value in $fg-map {
  .color-stroke-#{$color-name} {
    stroke: $color-value !important;
  }

  .color-fill-#{$color-name} {
    fill: $color-value !important;
  }
}

$border-color: #88bfff !default;
$border-radius: base.$border-radius !default;

.Datatype__Option {
  border: base.em(1px) solid $border-color;
  border: base.em(1px) solid rgba($border-color, 0.75);
  border-radius: $border-radius;
}


code:
/**
 * Copyright (c) 2020 bobbahbrown (https://github.com/bobbahbrown)
 * SPDX-License-Identifier: MIT
 */

 @use '../colors.scss';
 @use '../base.scss';

.ListInput__Section .Section__title{
  flex-shrink: 0;
}

.ListInput__Section .Section__titleText {
  font-size: base.em(12px);
}

 .ListInput__Loader {
   width: 100%;
   position: relative;
   height: 4px;
 }

 .ListInput__LoaderProgress {
   position: absolute;
   transition: background-color 500ms ease-out, width 500ms ease-out;
   background-color: colors.bg(colors.$primary);
   height: 100%;
 }


code:
@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss' as *;

$color-danger: colors.bg(colors.$red) !default;
$color-caution: colors.bg(colors.$yellow) !default;

$background-beige: #E8E4C9;

.NuclearBomb__displayBox {
  background-color: #002003;
  border: 0.167em inset $background-beige;
  color: #03e017;
  font-size: 2em;
  font-family: monospace;
  padding: 0.25em;
}

.NuclearBomb__Button {
  outline-width: 0.25rem !important;
  border-width: 0.65rem !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}

.NuclearBomb__Button--keypad {
  background-color: $background-beige;
  border-color: $background-beige;
  &:hover {
    background-color: lighten($background-beige, 15%) !important;
    border-color: lighten($background-beige, 15%) !important;
  }
}

.NuclearBomb__Button--1 {
  background-color: #D3CFB7 !important;
  border-color:#D3CFB7 !important;
  color: #A9A692 !important;
}

.NuclearBomb__Button--E {
  background-color: $color-caution !important;
  border-color: $color-caution !important;
  &:hover {
    background-color: lighten($color-caution, 15%) !important;
    border-color: lighten($color-caution, 15%) !important;
  }
}

.NuclearBomb__Button--C {
  background-color: $color-danger !important;
  border-color: $color-danger !important;
  &:hover {
    background-color: lighten($color-danger, 15%) !important;
    border-color: lighten($color-danger, 15%) !important;
  }
}

.NuclearBomb__NTIcon {
  background-image: url('../assets/bg-zoomtech.svg');
  background-size: 70%;
  background-position: center;
  background-repeat: no-repeat;
}


code:
/**
  * @file
  * @copyright 2020
  * @author ThePotato97 (https://github.com/ThePotato97)
  * @license ISC
  */

.Paper {
  &__Stamp {
    position: absolute;
    pointer-events: none;
    user-select: none;
  }
  &__Page {
    word-break: break-word;
    word-wrap: break-word;
  }
}


code:
@use 'sass:color';
@use 'sass:map';
@use '../components/Button.scss';
@use '../colors.scss';

$department_map: (
  'Assistant': colors.$grey,
  'Captain': colors.fg(colors.$blue),
  'Command': colors.$yellow,
  'Security': colors.$red,
  'Engineering': #f1a839,
  'Medical': colors.$teal,
  'Misc': colors.$white,
  'Science': colors.fg(colors.$purple),
  'Supply': colors.$brown,
  'Service': colors.$green,
  'Silicon': colors.$pink,
  'Nobility': colors.$blue,
  'Bourgeouis': colors.$yellow,
  'Proletariat': colors.$green,
  'Unpersons': colors.$brown,
);

.PreferencesMenu {
  &__Antags {
    &__antagSelection {
      $antagonist_bottom_padding: 10px;

      margin-bottom: -$antagonist_bottom_padding;

      @mixin animate-hover {
        .antagonist-icon-parent .antagonist-icon {
          &:hover {
            transform: scale(1.3);
            transition: transform 1s ease-out;
          }
        }
      }

      &__antagonist {
        padding-bottom: $antagonist_bottom_padding;
        padding-right: 20px;

        .antagonist-icon-parent {
          border-style: solid;
          border-radius: 50%;
          border-width: 4px;
          box-sizing: content-box;
          overflow: hidden;
          position: relative;

          height: 96px;
          width: 96px;

          .antagonist-icon {
            border-radius: 50%;
            -ms-interpolation-mode: nearest-neighbor;
            overflow: hidden;
            transition: transform 0.1s ease-in;
          }
        }

        &--off {
          @include animate-hover;

          .antagonist-icon-parent {
            border-color: colors.$red;

            .antagonist-icon {
              opacity: 0.5;

              &:hover {
                opacity: 1;
              }
            }
          }
        }

        &--on {
          @include animate-hover;

          .antagonist-icon-parent {
            border-color: colors.$green;
          }
        }

        &--banned {
          .antagonist-icon-parent {
            border-color: colors.$grey;
          }

          .antagonist-icon {
            opacity: 0.5;
          }
        }

        .antagonist-banned-slash {
          background: colors.$grey;

          width: 100%;
          height: 3px;

          position: absolute;
          top: 50%;
          left: 50%;
          transform: translateY(-50%) translateX(-50%) rotate(35deg);

          opacity: 0.8;
        }

        .antagonist-days-left {
          text-align: center;
          text-shadow: 1px 1px 1px #222;

          width: 100%;

          position: absolute;
          top: 50%;
          left: 50%;
          transform: translateY(-50%) translateX(-50%);
        }
      }
    }
  }

  &__papericonbox {
    border: 3px dotted rgba(90, 76, 76, 0.7);
  }

  &__papersplease {
    font-family: "Courier New";
    color: rgb(69, 55, 52);
    background: rgb(237, 224, 216);
    padding-left: 8px;
    padding-right: 12px;
    &__dotted {
      font-family: "Courier New";
      color: rgb(69, 55, 52);
      background: rgb(237, 224, 216);
      padding-top: 8px;
      padding-left: 8px;
      padding-right: 12px;
      border: 4px dotted rgba(90, 76, 76, 0.7);
    }
    &__center {
      font-family: "Courier New";
      color: rgb(69, 55, 52);
      background: rgb(237, 224, 216);
      padding-left: 8px;
      padding-right: 12px;
      border-left: 8px solid rgb(162, 148, 145);
      border-right: 4px solid rgb(162, 148, 145);
      border-bottom: 8px solid rgb(162, 148, 145);
    }
    &__left {
      font-family: "Courier New";
      color: rgb(69, 55, 52);
      background: rgb(237, 224, 216);
      padding-left: 8px;
      padding-right: 12px;
      border-left: 8px solid rgb(162, 148, 145);
      border-bottom: 8px solid rgb(162, 148, 145);
    }
    &__right {
      font-family: "Courier New";
      color: rgb(69, 55, 52);
      background: rgb(237, 224, 216);
      padding-left: 8px;
      padding-right: 12px;
      border-right: 2px solid rgb(162, 148, 145);
      border-bottom: 8px solid rgb(162, 148, 145);
    }
    &__header {
      font-family: "Courier New";
      color: rgb(69, 55, 52);
      background: rgb(237, 224, 216);
      border-radius: 3px 3px 0 0;
      &__dotted {
        font-family: "Courier New";
        color: rgb(69, 55, 52);
        background: rgba(162, 148, 147, 0.5);
        border-radius: 0 0 0 0;
        border: 4px dotted rgba(90, 76, 76, 0.7);
        font-size: 400%;
        font-weight: bold;
      }
      &__center {
        font-family: "Courier New";
        color: rgb(69, 55, 52);
        background: rgb(237, 224, 216);
        border-left: 8px solid rgb(162, 148, 145);
        border-right: 4px solid rgb(162, 148, 145);
        border-radius: 3px 0 0 0;
      }
      &__left {
        font-family: "Courier New";
        color: rgb(69, 55, 52);
        background: rgb(237, 224, 216);
        border-left: 8px solid rgb(162, 148, 145);
        border-radius: 3px 0 0 0;
      }
      &__right {
        font-family: "Courier New";
        color: rgb(69, 55, 52);
        background: rgb(237, 224, 216);
        border-right: 2px solid rgb(162, 148, 145);
        border-radius: 0 3px 0 0;
      }
      &__title {
        font-family: "Courier New";
        color: rgb(69, 55, 52);
        background: rgba(162, 148, 147, 0.5);
        padding-top: 8px;
        border-radius: 0 0 0 0;
        border-bottom: 4px dotted rgba(90, 76, 76, 0.7);
        font-size: 400%;
        font-weight: bold;
      }
    }
  }

  &__Jobs {
    > * {
      flex: 1;
    }

    &__joblessdropdown {
      background: rgba(162, 148, 147, 0.5);
    }

    &__departments {
      @each $department-name, $color-value in $department_map {
        &--#{$department-name} {
          &.head {
            background: rgb(152, 138, 137);

            .job-name {
              border-right: 2px solid $color-value;
              font-weight: bold;
            }
          }

          .job-name {
            border-right: 2px dotted colors.fg($color-value);
          }

          color: rgb(230, 219, 214);
          background: rgb(162, 148, 147);
          font-family: "Tahoma";
          border-bottom: 2px solid rgba(0, 0, 0, 0.3);
          border-left: 2px solid rgba(0, 0, 0, 0.3);
          border-right: 2px solid rgba(0, 0, 0, 0.3);

          > * {
            height: calc(100% + 0.2em);
            padding-bottom: 0.2em;
          }

          &:first-child {
            border-top: 2px solid rgba(0, 0, 0, 0.3);
          }

          .options {
            background: rgba(233, 218, 199, 1);
            height: 100%;
          }
        }

        &--Captain {
          border: 3px solid rgba(200, 200, 0, 1);

          &:first-child {
            border-top: 3px solid rgba(200, 200, 0, 1);
          }

          .job-name {
            font-size: 17px;
          }
        }
      }

      &__priority {
        border: 1px solid rgba(0, 0, 0, 0.3);

        &--off::after {
          content: "";

          background: rgba(0, 0, 0, 0.2);
          display: block;
          height: 80%;
          left: 50%;
          position: relative;
          top: 50%;
          transform: translateX(-50%) translateY(-50%) rotate(40deg);
          width: 2px;
        }
      }
    }

    &__PriorityHeader {
      font-weight: bold;
      transform:
        translateX(-4px)
        translateY(-8px)
        rotate(315deg);
      white-space: nowrap;
      width: 19px;
    }
  }

  &__Quirks {
    &__QuirkList {
      background-color: colors.$light-grey;
      height: calc(90vh - 170px);
      min-height: 100%;
      overflow-y: scroll;

      &__quirk {
        background-color: colors.$white;
        border-bottom: 1px solid black;
        color: #111;
        transition: background-color 0.1s ease-in;

        $quality_map: (
          "positive": colors.$green,
          "neutral": colors.$white,
          "negative": colors.$red,
        );

        @each $quality, $color-value in $quality_map {
          &--#{$quality} {
            background-color: $color-value;
            transition: background-color 0.1s ease-in;
          }
        }

        &:hover {
          background-color: colors.$grey;
          transition: background-color 0.1s ease-out;

          @each $quality, $color-value in $quality_map {
            .PreferencesMenu__Quirks__QuirkList__quirk--#{$quality} {
              background-color: color.scale($color-value, $lightness: -25%);
              transition: background-color 0.1s ease-out;
            }
          }
        }
      }
    }
  }
}


code:
/**
 * Copyright (c) 2021 bobbahbrown (https://github.com/bobbahbrown)
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';
@use '../colors.scss';
@use '../functions.scss';

$color-prayer: colors.bg(colors.$purple) !default;
$color-centcom: colors.bg(colors.$yellow) !default;
$color-syndicate: colors.bg(colors.$red) !default;
$color-nuke: colors.bg(colors.$yellow) !default;
$color-muted: colors.bg(colors.$label) !default;
$color-header: colors.bg(colors.$label) !default;
$background-color: base.$color-bg-section !default;
$text-color: base.$color-fg !default;

.RequestManager__request_centcom,
.RequestManager__request_nuke,
.RequestManager__request_prayer,
.RequestManager__request_syndicate {
  margin-right: 0.5rem;
}

.RequestManager__request_prayer {
  color: $color-prayer;
}

.RequestManager__request_centcom {
  color: $color-centcom;
}

.RequestManager__request_syndicate {
  color: $color-syndicate;
}

.RequestManager__request_nuke {
  color: $color-nuke;
}

.RequestManager__header {
  line-height: 1.375rem;
  min-height: 1.375rem;
  color: $color-muted;
  white-space: break-spaces;
  word-wrap: pre-wrap;
  vertical-align: baseline;
  margin-bottom: 0;
  padding-bottom: 0.1rem;
}

.RequestManager__headerText {
  margin-right: 0.5rem;
  font-size: 1rem;
  font-weight: bold;
  color: $color-header;
}

.RequestManager__timestamp {
  font-size: 0.75rem;
}

.RequestManager__row:first-of-type {
  margin-top: 0rem;
}

.RequestManager__row {
  padding-top: 0.125rem;
  padding-bottom: 0.125rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  margin-top: 0.75rem;
  min-height: 2.75rem;
}

.RequestManager__row:hover {
  background-color: rgba(0, 0, 0, 0.25);
}

.RequestManager__row:hover .RequestManager__controlsContainer {
  opacity: 1;
}

.RequestManager__controlsContainer {
  opacity: 0;
  position: absolute;
  right: 3px;
  top: 3px;
}

.RequestManager__row {
  position: relative;
}

.RequestManager__filterPanel {
  margin: 0.5rem;
  padding: 0.5rem;
  background-color: functions.fake-alpha($background-color, base.$color-bg);
  background-color: $background-color;
  color: $text-color
}

.RequestManager__filterPanel tr:not(:last-of-type) {
  margin-bottom: 0.5rem;
}


code:
@use '../base.scss';
@use '../colors.scss';

.Roulette__container {
  display: flex;
}

.Roulette__board-cell {
  display: table-cell;
  padding: 0;
  margin: 0;
  border: 1px solid colors.$white;
  vertical-align: bottom;
}

.Roulette__board-cell-number {
  width: 35px;
}

.Roulette__board-cell-number--colspan-2 {
  width: 71px;
}

.Roulette__board-cell-number--colspan-4 {
  width: 143px;
}

.Roulette__board-button {
  display: table-cell !important;
  border: none !important;
  width: inherit;
  height: 40px;
  padding: 0;
  margin: 0;
  text-align: center;
  vertical-align: middle;
  color: colors.$white !important;
}

.Roulette__board-button--rowspan-3 {
  height: 122px;
}

.Roulette__board-button-text {
  text-align: center;
  font-size: 16px;
  font-weight: bold;
}

.Roulette__lowertable {
  margin-top: 8px;
  border-collapse: collapse;
  border: 1px solid colors.$white;
  border-spacing: 0;
}

.Roulette__lowertable--cell {
  border: 2px solid colors.$white;
  padding: 0px;
  margin: 0px;
}

.Roulette__lowertable--betscell {
  vertical-align: top;
}

.Roulette__lowertable--spinresult {
  text-align: center;
  font-size: 100px;
  font-weight: bold;
  vertical-align: middle;
}

.Roulette__lowertable--spinresult-black {
  background-color: colors.$black;
}

.Roulette__lowertable--spinresult-red {
  background-color: colors.$red;
}

.Roulette__lowertable--spinresult-green {
  background-color: colors.$green;
}

.Roulette__lowertable--spinbutton {
  margin: 0 !important;
  border: none !important;
  font-size: 50px;
  line-height: 60px !important;
  text-align: center;
  font-weight: bold;
}

.Roulette__lowertable--header {
  width: 1%;
  text-align: center;
  font-size: 16px;
  font-weight: bold;
}


code:
.Safe__engraving {
  position: absolute;
  width: 95%;
  height: 96%;
  left: 2.5%;
  top: 2%;
  border: 5px outset #3e4f6a;
  padding: 5px;
  text-align: center;
}

.Safe__engraving-arrow {
  color: #35435a;
}

.Safe__engraving-hinge {
  content: " ";
  background-color: #191f2a;
  width: 25px;
  height: 40px;
  position: absolute;
  right: -15px;
  margin-top: -20px;
}

.Safe__dialer {
  margin-bottom: 1.25rem;
  .Button {
    width: 80px;
  }
}

.Safe__dialer-right {
  .Button {
    i {
      z-index: -100;
    }
  }
}

.Safe__dialer-number {
  color: #bbbbbb;
  display: inline;
  background-color: #191f2a;
  font-size: 1.5rem;
  font-weight: bold;
  padding: 0 0.5rem;
}

.Safe__contents {
  border: 10px solid #191f2a;
  background-color: #0f131a;
  height: calc(85% + 7.5px);
  text-align: left;
  padding: 5px;
}

.Safe__help {
  position: absolute;
  top: 73%;
  left: 10px;
  width: 50%;

  font-family: 'Comic Sans MS', cursive, sans-serif;
  font-style: italic;
  color: black;
  box-shadow: 5px 5px #111111;
  background-image: linear-gradient(to bottom,
    #b2ae74 0%,
    #8e8b5d 100%);
  transform: rotate(-1deg);

  &:before {
    content: " ";
    display: block;
    width: 24px;
    height: 40px;
    background-image: linear-gradient(to bottom,
      transparent 0%,
      #ffffff 100%);
    box-shadow: 1px 1px #111111;
    opacity: 0.2;
    position: absolute;
    top: -30px;
    left: calc(50% - 12px);
    transform: rotate(-5deg);
  }
}


code:
.TachyonArray__ActiveRecord {
  margin: 0 0.5em 0 0.8em;
}

.TachyonArray__Content {
  overflow-x: hidden;
  overflow-y: auto;
}

.TachyonArray__ResearchFooter > * {
  width: 100%;
  text-align: center;
}


code:
@use '../colors.scss';

.Techweb__NodeProgress {
  margin-bottom: 1rem;
}

.Techweb__NodeProgress > *:not(:last-child) {
  margin-right: 0.4rem;
}

.Techweb__DesignIcon {
  margin-left: 0.25rem;
  margin-right: 0.25rem;
}

.Techweb__OverviewNodes {
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 6px;
  padding-top: 4px;
}

.Techweb__HeaderContent {
  background-color: black;
  padding: 6px;
  border: 1px solid #40628a;
}

.Techweb__HeaderContent > * > :not(:last-child) {
  margin-bottom: 5px;
}

.Techweb__HeaderSectionTabs {
  margin-top: 8px;
  background-color: black;
  border: 1px solid #40628a;
  padding-left: 5px;
  padding-right: 5px;
}

.Techweb__HeaderTabTitle {
  border-right: 1px solid #40628a;
  padding-right: 0.5em;
  margin-right: 0.5em;
  font-weight: bold;
}

.Techweb__HeaderSectionTabs input {
  background-color: rgba(255, 255, 255, 0.05);
}

.Techweb__PointSummary {
  list-style: none;
  margin: 0.4em 0 0 1em;
  padding: 0;
}

.Techweb__SecProtocol {
  color: colors.$bad;
  margin-left: 0.2em;
}

.Techweb__SecProtocol.engaged {
  color: colors.$good;
}

.Techweb__DesignModal > :not(:last-child) {
  margin-bottom: 0.5em;
}

.Techweb__LockedModal > :not(:last-child) {
  margin-bottom: 0.5em;
}

.Techweb__ExperimentDiscount {
  color: colors.$label;
  margin: 0.5em 0;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use '../base.scss';

$scrollbar-color-multiplier: 1 !default;

.Layout,
.Layout * {
  // Fancy scrollbar
  scrollbar-base-color: color.scale(
    base.$color-bg,
    $lightness: -25% * $scrollbar-color-multiplier);
  scrollbar-face-color: color.scale(
    base.$color-bg,
    $lightness: 10% * $scrollbar-color-multiplier);
  scrollbar-3dlight-color: color.scale(
    base.$color-bg,
    $lightness: 0% * $scrollbar-color-multiplier);
  scrollbar-highlight-color: color.scale(
    base.$color-bg,
    $lightness: 0% * $scrollbar-color-multiplier);
  scrollbar-track-color: color.scale(
    base.$color-bg,
    $lightness: -25% * $scrollbar-color-multiplier);
  scrollbar-arrow-color: color.scale(
    base.$color-bg,
    $lightness: 50% * $scrollbar-color-multiplier);
  scrollbar-shadow-color: color.scale(
    base.$color-bg,
    $lightness: 10% * $scrollbar-color-multiplier);
}

.Layout__content {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow-x: hidden;
  overflow-y: hidden;
}

.Layout__content--scrollable {
  overflow-y: scroll;
  margin-bottom: 0;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

.NtosHeader__left {
  position: absolute;
  left: 1em;
}

.NtosHeader__right {
  position: absolute;
  right: 1em;
}

.NtosHeader__icon {
  margin-top: -0.75em;
  margin-bottom: -0.5em;
  vertical-align: middle;
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use '../base.scss';

.NtosWindow__header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2em;
  line-height: 1.928em;
  background-color: rgba(0, 0, 0, 0.5);
  font-family: Consolas, monospace;
  font-size: base.em(14px);
  user-select: none;
  -ms-user-select: none;
}

.NtosWindow__content .Layout__content {
  margin-top: 2em;
  font-family: Consolas, monospace;
  font-size: base.em(14px);
}


code:
/**
 * Copyright (c) 2020 Aleksej Komarov
 * SPDX-License-Identifier: MIT
 */

@use 'sass:color';
@use '../base.scss';
@use '../colors.scss';

$text-color: rgba(255, 255, 255, 0.75) !default;
$background-color: #363636 !default;
$shadow-color-core: #161616 !default;
$shadow-color: rgba(0, 0, 0, 0.1) !default;

.TitleBar {
  background-color: $background-color;
  border-bottom: 1px solid $shadow-color-core;
  box-shadow: 0 2px 2px $shadow-color;
  box-shadow: 0 base.rem(2px) base.rem(2px) $shadow-color;
  user-select: none;
  -ms-user-select: none;
}

.TitleBar__clickable {
  color: color.change($text-color, $alpha: 0.5);
  background-color: $background-color;
  transition: color 250ms ease-out, background-color 250ms ease-out;

  &:hover {
    color: rgba(255, 255, 255, 1.0);
    background-color: #cc0000;
    transition: color 0ms, background-color 0ms;
  }
}

.TitleBar__title {
  position: absolute;
  display: inline-block;
  top: 0;
  left: 46px;
  left: base.rem(46px);
  color: $text-color;
  font-size: 14px;
  font-size: base.rem(14px);
  line-height: 31px;
  line-height: base.rem(31px);
  white-space: nowrap;
  pointer-events: none;
}

.TitleBar__buttons {
  pointer-events: initial;
  display: inline-block;
  width: 100%;
  margin-left: 10px;
}

.TitleBar__dragZone {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 32px;
  height: base.rem(32px);
}

.TitleBar__statusIcon {
  position: absolute;
  top: 0;
  left: 12px;
  left: base.rem(12px);
  transition: color 0.5s;
  font-size: 20px;
  font-size: base.rem(20px);
  line-height: 32px !important;
  line-height: base.rem(32px) !important;
}

.TitleBar__close {
  position: absolute;
  top: -1px;
  right: 0;
  width: 45px;
  width: base.rem(45px);
  height: 32px;
  height: base.rem(32px);
  font-size: 20px;
  font-size: base.rem(20px);
  line-height: 31px;
  line-height: base.rem(31px);
  text-align: center;
}

.TitleBar__devBuildIndicator {
  position: absolute;
  top: 6px;
  top: base.rem(6px);
  right: 52px;
  right: base.rem(52px);
  min-width: 20px;
  min-width: base.rem(20px);
  padding: 2px 4px;
  padding: base.rem(2px) base.rem(4px);
  background-color: rgba(colors.$good, 0.75);
  color: #fff;
  text-align: center;
}