/** This stylesheet defines a basic framework for rendering charts. A top-level
 * `css-chart` element is the root of the chart. This element accepts some
 * `padding`, which is the space around the chart. This is also a CSS container,
 * allowing browsers to do optimizations regarding the children of this element.
 * The content box of the css-chart element then defines the coordinate system.
 * The following variables are required to define that coordinate system:
 * --min-x: The left-most value on the x-axis, as a number
 * --max-x: The right-most value on the x-axis, as a number
 * --min-y: The bottom-most value on the y-axis, as a number
 * --max-y: The top-most value on the y-axis, as a number
 * Within the `css-chart`, we can then use a basic set of elements to mark up
 * the chart. To determine their positions and sizes, the `css-canvas` exposes
 * some CSS variables.
 * --delta-x: Translates "1" in terms of data to a horizontal CSS length
 * --delta-y: Translates "1" in terms of data to a vertical CSS length
 * --offset-x: The x-offset accounting for scales not starting at 0
 * --offset-y: The y-offset accounting for scales not starting at 0
 * Now, each element defines "basic" properties that define their size and
 * location on the canvas. These properties are prefixed with `--real-`.
 * By default, the `--real-*` properties are computed from their non-prefixed
 * counterparts together with `--delta-x` and `--delta-y`. That maens you can
 * supply the underlying data to the elements directly, without having to do
 * the length conversions yourself.
 * Note that this stylesheet must load inside shadow DOM, to introduce a scope
 * for these styles. */

/** This is the root element. It has a shadow root, but its descendants must be
 * placed in the light DOM. This is done in order to support the absolute
 * positioning of (light) children while also taking the padding into account. */
:where(css-chart){
  display: block;
  width: 100%;
  height: 20rem;
  padding: 2rem 1.5rem 2rem 4rem;
  box-sizing: border-box;
  contain: strict;
  overflow: clip;
}

/** This is the direct child of the css-chart. It creates the proper coordinate
 * system and container for queries or units. */
#plot {
  --delta-x: calc(100cqw / (var(--max-x) - var(--min-x)));
  --delta-y: calc(100cqh / (var(--min-y) - var(--max-y)));
  --offset-x: calc(100cqw * var(--min-x) / (var(--max-x) - var(--min-x)));
  --offset-y: calc(100cqh + 100cqh * var(--min-y) / (var(--max-y) - var(--min-y)));
  width: 100%;
  height: 100%;
  position: relative;
  container: css-chart / size;
}


css-line, css-rect, css-point, css-text {
  position: absolute;
}

/** Define a point on the canvas, such as a square or circle, centered at
 * certain coordinates.
 * --x: The x-coordinate of the point.
 * --y: The y-coordinate of the point.
 * --r: The width and height of the point. This may be overwritten using the
 *      `width` and `height` properties. */
css-point {
  --real-x: calc(var(--x) * var(--delta-x) + var(--offset-x));
  --real-y: calc(var(--y) * var(--delta-y) + var(--offset-y));
  inset: var(--real-y) auto auto var(--real-x);
  width: calc(2 * var(--r));
  height: calc(2 * var(--r));
  transform: translate(-50%, -50%);
}

/** Defines a rectangle. It may be defined by its opposing corners, but to make
 * drawing lines easier, the coordinates of the second point are optional and
 * default to the first given point on that axis. Alternatively, a width and
 * height may be given directly to go along with the coordinates of the first
 * corner, though note that that must then be the top-left corner.
 * It is completely fine (and encouraged, in some situations) to nest content
 * inside css-rect elements. This also moves the coordinate space accordingly.
 * The coordinates define the padding-box, meaning borders are drawn outside of
 * the rectangle completely.
 * --x1: The x-coordinate of the top-left corner of the rectangle.
 * --y1: The y-coordinate of the top-left of the rectangle.
 * --x2: The x-coordinate of the bottom-right corner of the rectangle.
 * --y2: The y-coordinate of the bottom-right corner of the rectangle. */
css-rect {
  --real-x1: calc(var(--x1) * var(--delta-x) + var(--offset-x));
  --real-y1: calc(var(--y1) * var(--delta-y) + var(--offset-y));
  --real-x2: calc(var(--x2, var(--x1)) * var(--delta-x) + var(--offset-x));
  --real-y2: calc(var(--y2, var(--y1)) * var(--delta-y) + var(--offset-y));
  width: calc(var(--real-x2) - var(--real-x1));
  height: calc(var(--real-y2) - var(--real-y1));
  top: calc((var(--real-y1) + var(--real-y2)) / 2);
  left: calc((var(--real-x1) + var(--real-x2)) / 2);
  transform: translate(-50%, -50%);
}

/** Defines a line. This is a component with shadow that detects changes in
 * coordinates on order to update the `--angle`. Currently, `atan2()` does not
 * (correctly) interpret arguments with mixed units, so this'll have to do.
 * This also means that `css-rect` should be preferred for performance whenever
 * the lines are strictly horizontal or vertical. It must be given a `border`
 * for it to render. It is not rendered while the component is not defined,
 * because it wouldn't render correctly anyway.
 * --x1: The x-coordinate of the start of the line segment.
 * --y1: The y-coordinate of the start of the line segment.
 * --x2: The x-coordinate of the end of the line segment.
 * --y2: The y-coordinate of the end of the line segment. */
css-line {
  --real-x1: calc(var(--x1) * var(--delta-x) + var(--offset-x));
  --real-y1: calc(var(--y1) * var(--delta-y) + var(--offset-y));
  --real-x2: calc(var(--x2, var(--x1)) * var(--delta-x) + var(--offset-x));
  --real-y2: calc(var(--y2, var(--y1)) * var(--delta-y) + var(--offset-y));
  --length: hypot(
    var(--real-x2) - var(--real-x1),
    var(--real-y2) - var(--real-y1)
  );
  top: calc((var(--real-y2) + var(--real-y1)) / 2);
  left: calc((var(--real-x2) + var(--real-x1)) / 2);
  width: var(--length);
  height: 0;
  transform: translate(-50%, -50%) rotate(var(--angle));
  &:not(:defined){ display: none; }
}

/** For rendering text. This is subject under anything that might happen to text
 * normally, such as wrapping when the text is overflowing, or inheriting font
 * properties. Note that text may be aligned differently using the `translate`
 * property, e.g. `translate: -100% -50%` to make the right-center of the text
 * box coincide with the given coordinates.
 * --x: The x-coordinate for the text.
 * --y: The y-coordinate for the text. */
css-text {
  --real-x: calc(var(--x) * var(--delta-x) + var(--offset-x));
  --real-y: calc(var(--y) * var(--delta-y) + var(--offset-y));
  inset: var(--real-y) auto auto var(--real-x);
}

/** The tables are for screen readers. Visual users don't need to see it,
 * because they have a fancy chart to look at. This doesn't technically have
 * anything to do with rendering the css-chart, but they go hand-in-hand so
 * there's really no point in putting this in a separate stylesheet. */
table {
  display: block;
  width: 1px;
  height: 1px;
  margin: -1px;
  position: absolute;
  inset: 0 auto auto 0;
  overflow: clip;
  opacity: 0.01;
}

/** Displayed text above the chart. */
#title {
  margin: .5rem 0 -.5rem 4rem;
  font-weight: 600;
  font-size: 1.1rem;
}

/** For chart labels. There are two main `css-rect` elements, one `#x-axis` and
 * one `#y-axis`. They may additionally have a class `.guided`, which renders
 * guide lines from the labels into the main chart. A few variables are
 * available to customize the colors and spacing:
 * --label-color: The text color for the labels.
 * --axis-color: The color for the axis itself.
 * --guide-color: The color for the guide lines.
 * --guide-style: The style for the guide lines (as per `border-style`).
 * --label-space: The space between the labels and the axis. */
#x-axis, #y-axis {
  --label-color: #5a788a;
  --axis-color: #d0d6db;
  --guide-color: var(--axis-color);
  --guide-style: dotted;
  inset: 0;
  color: var(--label-color);
  font-size: clamp(.6rem, 6vw - .6rem, .9rem);
  &.guided css-text::after {
    content: "";
    position: absolute;
  }
}
#x-axis {
  --label-space: .4rem;
  transform: translate(0%, 100%);
  border-top: 1px solid var(--axis-color);
  css-text {
    --x: var(--value);
    --real-y: var(--label-space);
    translate: -50% 0;
    white-space: nowrap;
  }
  &.guided css-text::after {
    width: 0;
    height: 100cqh;
    inset: auto auto calc(100% + var(--label-space)) calc(50% - 1px);
    border-right: 1px var(--guide-style) var(--guide-color);
  }
}
@container css-chart (width < 40rem){
  css-chart {
    padding-bottom: 3rem;
  }
  #x-axis css-text { rotate: 18deg; }
}
#y-axis {
  --label-space: .8rem;
  transform: translate(-100%, 0%);
  border-right: 1px solid var(--axis-color);
  css-text {
    --real-x: calc(100% - var(--label-space));
    --y: var(--value);
    translate: -100% -50%;
    white-space: nowrap;
  }
  &.guided css-text::after {
    width: 100cqw;
    height: 0;
    inset: 50% auto auto calc(100% + var(--label-space));
    border-bottom: 1px var(--guide-style) var(--guide-color);
  }
}

/** The chart legend. This shows off how to interpret different parts of the
 * chart. It is rendered below the chart; in fact, it is not part of the chart
 * at all.*/
#legend {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 2rem;
  padding: 1rem 2rem;
  margin: 0;
  &:where(:has(li:only-child)){ display: none; }
  li {
    display: flex;
    align-items: center;
    gap: .5rem;
  }
  .shape {
    background-color: var(--color);
    width: 1rem;
    height: 1rem;
    box-sizing: border-box;
    &.disc {
      border-radius: 50%;
    }
    &.circle {
      background-color: transparent;
      border: 2px solid var(--color);
      border-radius: 50%;
    }
    &.square {/*…*/}
    &.line {
      height: .2rem;
    }
    &.dotline {
      width: 1.5rem;
      height: .2rem;
      position: relative;
      &::after {
        content: "";
        width: .7rem;
        height: .7rem;
        position: absolute;
        inset: 50% auto auto 50%;
        translate: -50% -50%;
        background-color: inherit;
        border-radius: 50%;
      }
    }
  }
}
