diff --git a/frontend/index.html b/frontend/index.html
index 8ff978d..be3741f 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -53,17 +53,52 @@
diff --git a/frontend/lib/options-pane.mjs b/frontend/lib/options-pane.mjs
new file mode 100644
index 0000000..1efb066
--- /dev/null
+++ b/frontend/lib/options-pane.mjs
@@ -0,0 +1,43 @@
+export const toggleOptionPane = (event, element) => {
+ event.preventDefault();
+ // show options
+ if (
+ !document.querySelector("#options").style.display ||
+ document.querySelector("#options").style.display === "none"
+ ) {
+ // using this to do any potential init logic for the fields too
+ loadPlayerControlsShown(document.querySelector("#playerControlsShown"))
+ loadPlingVolume(document.querySelector("#plingVolume"))
+
+ element.innerText = "❌";
+ document.querySelector("#options").style.display = "block";
+ return (document.querySelector("#viewing").style.display = "none");
+ }
+ // hide options
+ element.innerText = "⚙️";
+ document.querySelector("#options").style.display = "none";
+ document.querySelector("#viewing").style.display = "block";
+};
+
+const getPlayerControlsShown = () => localStorage.getItem("watch-party-default-allow-controls") || false
+// delete from storage on false to prevent weird js boolean parsing (Boolean('false') === True)
+const setPlayerControlShown = (boolean) => !boolean
+? localStorage.removeItem("watch-party-default-allow-controls")
+: localStorage.setItem("watch-party-default-allow-controls", boolean)
+export const togglePlayerControlsShown = (element) => {
+ const isShown = element.checked
+ setPlayerControlShown(!isShown)
+}
+const loadPlayerControlsShown = (element) => {
+ const isShown = getPlayerControlsShown()
+ element.checked = !isShown
+}
+
+const getPlingVolume = () => localStorage.getItem("watch-party-pling-volume") || 100
+const setPlingVolume = (value) => localStorage.setItem("watch-party-pling-volume", value)
+export const handlePlingVolume = (element) => {
+ setPlingVolume(element.value)
+}
+const loadPlingVolume = (element) => {
+ element.value = getPlingVolume()
+}
diff --git a/frontend/lib/pling.mjs b/frontend/lib/pling.mjs
index 9305dfb..d332613 100644
--- a/frontend/lib/pling.mjs
+++ b/frontend/lib/pling.mjs
@@ -1,80 +1,82 @@
-export const pling = () => {
- const maxGain = 0.3;
- const duration = 0.22;
- const fadeDuration = 0.1;
- const secondBeepOffset = 0.05;
- const thirdBeepOffset = 2 * secondBeepOffset;
-
- const ctx = new AudioContext();
-
- const firstBeepGain = ctx.createGain();
- firstBeepGain.connect(ctx.destination);
- firstBeepGain.gain.setValueAtTime(0.01, ctx.currentTime);
- firstBeepGain.gain.exponentialRampToValueAtTime(
- maxGain,
- ctx.currentTime + fadeDuration
- );
- firstBeepGain.gain.setValueAtTime(
- maxGain,
- ctx.currentTime + (duration - fadeDuration)
- );
- firstBeepGain.gain.exponentialRampToValueAtTime(
- 0.01,
- ctx.currentTime + duration
- );
-
- const firstBeep = ctx.createOscillator();
- firstBeep.connect(firstBeepGain);
- firstBeep.frequency.value = 400;
- firstBeep.type = "sine";
-
- const secondBeepGain = ctx.createGain();
- secondBeepGain.connect(ctx.destination);
- secondBeepGain.gain.setValueAtTime(0.01, ctx.currentTime + secondBeepOffset);
- secondBeepGain.gain.exponentialRampToValueAtTime(
- maxGain,
- ctx.currentTime + secondBeepOffset + fadeDuration
- );
- secondBeepGain.gain.setValueAtTime(
- maxGain,
- ctx.currentTime + secondBeepOffset + (duration - fadeDuration)
- );
- secondBeepGain.gain.exponentialRampToValueAtTime(
- 0.01,
- ctx.currentTime + secondBeepOffset + duration
- );
-
- const secondBeep = ctx.createOscillator();
- secondBeep.connect(secondBeepGain);
- secondBeep.frequency.value = 600;
- secondBeep.type = "sine";
-
- const thirdBeepGain = ctx.createGain();
- thirdBeepGain.connect(ctx.destination);
- thirdBeepGain.gain.setValueAtTime(0.01, ctx.currentTime + thirdBeepOffset);
- thirdBeepGain.gain.exponentialRampToValueAtTime(
- maxGain,
- ctx.currentTime + thirdBeepOffset + fadeDuration
- );
- thirdBeepGain.gain.setValueAtTime(
- maxGain,
- ctx.currentTime + thirdBeepOffset + (duration - fadeDuration)
- );
- thirdBeepGain.gain.exponentialRampToValueAtTime(
- 0.01,
- ctx.currentTime + thirdBeepOffset + duration
- );
-
- const thirdBeep = ctx.createOscillator();
- thirdBeep.connect(thirdBeepGain);
- thirdBeep.frequency.value = 900;
- thirdBeep.type = "sine";
-
- firstBeep.start(ctx.currentTime);
- firstBeep.stop(ctx.currentTime + duration);
- secondBeep.start(ctx.currentTime + secondBeepOffset);
- secondBeep.stop(ctx.currentTime + (secondBeepOffset + duration));
- thirdBeep.start(ctx.currentTime + thirdBeepOffset);
- thirdBeep.stop(ctx.currentTime + (thirdBeepOffset + duration));
-};
-
+export const pling = () => {
+ // technically volume 0 breaks it but its the wanted outcome i guess?
+ const maxGain =
+ (Number(localStorage.getItem("watch-party-pling-volume")) / 100 ?? 1) * 0.3;
+
+ const duration = 0.22;
+ const fadeDuration = 0.1;
+ const secondBeepOffset = 0.05;
+ const thirdBeepOffset = 2 * secondBeepOffset;
+
+ const ctx = new AudioContext();
+
+ const firstBeepGain = ctx.createGain();
+ firstBeepGain.connect(ctx.destination);
+ firstBeepGain.gain.setValueAtTime(0.01, ctx.currentTime);
+ firstBeepGain.gain.exponentialRampToValueAtTime(
+ maxGain,
+ ctx.currentTime + fadeDuration
+ );
+ firstBeepGain.gain.setValueAtTime(
+ maxGain,
+ ctx.currentTime + (duration - fadeDuration)
+ );
+ firstBeepGain.gain.exponentialRampToValueAtTime(
+ 0.01,
+ ctx.currentTime + duration
+ );
+
+ const firstBeep = ctx.createOscillator();
+ firstBeep.connect(firstBeepGain);
+ firstBeep.frequency.value = 400;
+ firstBeep.type = "sine";
+
+ const secondBeepGain = ctx.createGain();
+ secondBeepGain.connect(ctx.destination);
+ secondBeepGain.gain.setValueAtTime(0.01, ctx.currentTime + secondBeepOffset);
+ secondBeepGain.gain.exponentialRampToValueAtTime(
+ maxGain,
+ ctx.currentTime + secondBeepOffset + fadeDuration
+ );
+ secondBeepGain.gain.setValueAtTime(
+ maxGain,
+ ctx.currentTime + secondBeepOffset + (duration - fadeDuration)
+ );
+ secondBeepGain.gain.exponentialRampToValueAtTime(
+ 0.01,
+ ctx.currentTime + secondBeepOffset + duration
+ );
+
+ const secondBeep = ctx.createOscillator();
+ secondBeep.connect(secondBeepGain);
+ secondBeep.frequency.value = 600;
+ secondBeep.type = "sine";
+
+ const thirdBeepGain = ctx.createGain();
+ thirdBeepGain.connect(ctx.destination);
+ thirdBeepGain.gain.setValueAtTime(0.01, ctx.currentTime + thirdBeepOffset);
+ thirdBeepGain.gain.exponentialRampToValueAtTime(
+ maxGain,
+ ctx.currentTime + thirdBeepOffset + fadeDuration
+ );
+ thirdBeepGain.gain.setValueAtTime(
+ maxGain,
+ ctx.currentTime + thirdBeepOffset + (duration - fadeDuration)
+ );
+ thirdBeepGain.gain.exponentialRampToValueAtTime(
+ 0.01,
+ ctx.currentTime + thirdBeepOffset + duration
+ );
+
+ const thirdBeep = ctx.createOscillator();
+ thirdBeep.connect(thirdBeepGain);
+ thirdBeep.frequency.value = 900;
+ thirdBeep.type = "sine";
+
+ firstBeep.start(ctx.currentTime);
+ firstBeep.stop(ctx.currentTime + duration);
+ secondBeep.start(ctx.currentTime + secondBeepOffset);
+ secondBeep.stop(ctx.currentTime + (secondBeepOffset + duration));
+ thirdBeep.start(ctx.currentTime + thirdBeepOffset);
+ thirdBeep.stop(ctx.currentTime + (thirdBeepOffset + duration));
+};
diff --git a/frontend/lib/watch-session.mjs b/frontend/lib/watch-session.mjs
index 4cdeb04..f2c6820 100644
--- a/frontend/lib/watch-session.mjs
+++ b/frontend/lib/watch-session.mjs
@@ -228,7 +228,6 @@ export const joinSession = async () => {
is_playing
);
- // TODO: Allow the user to set this somewhere
let defaultAllowControls = false;
try {
defaultAllowControls = localStorage.getItem(
diff --git a/frontend/main.mjs b/frontend/main.mjs
index 04b0b44..bef1c87 100644
--- a/frontend/main.mjs
+++ b/frontend/main.mjs
@@ -1,7 +1,15 @@
import { setupJoinSessionForm } from "./lib/join-session.mjs?v=bfdcf2";
+import {
+ toggleOptionPane,
+ togglePlayerControlsShown,
+ handlePlingVolume
+} from "./lib/options-pane.mjs?v=bfdcf2";
const main = () => {
setupJoinSessionForm();
+ window.toggleOptionPane = toggleOptionPane;
+ window.togglePlayerControlsShown = togglePlayerControlsShown;
+ window.handlePlingVolume = handlePlingVolume
};
if (document.readyState === "complete") {
diff --git a/frontend/styles.css b/frontend/styles.css
index 0c08cc9..61e44b9 100644
--- a/frontend/styles.css
+++ b/frontend/styles.css
@@ -1,397 +1,465 @@
-*,
-*:before,
-*:after {
- box-sizing: border-box;
-}
-
-:root {
- --bg-rgb: 28, 23, 36;
- --fg-rgb: 234, 234, 248;
- --accent-rgb: 181, 127, 220;
- --fg: rgb(var(--fg-rgb));
- --bg: rgb(var(--bg-rgb));
- --default-user-color: rgb(126, 208, 255);
- --accent: rgb(var(--accent-rgb));
- --fg-transparent: rgba(var(--fg-rgb), 0.25);
- --bg-transparent: rgba(var(--bg-rgb), 0.25);
- --autocomplete-bg: linear-gradient(
- var(--fg-transparent),
- var(--fg-transparent)
- ),
- linear-gradient(var(--bg), var(--bg));
- --chip-bg: linear-gradient(
- var(--accent-transparent),
- var(--accent-transparent)
- ),
- linear-gradient(var(--bg), var(--bg));
- --accent-transparent: rgba(var(--accent-rgb), 0.25);
-}
-
-html {
- background-color: var(--bg);
- color: var(--fg);
- font-size: 1.125rem;
- font-family: sans-serif;
-}
-
-html,
-body {
- margin: 0;
- padding: 0;
- overflow: hidden;
- overscroll-behavior: none;
- width: 100%;
- height: 100%;
-}
-
-body {
- display: flex;
- flex-direction: column;
-}
-
-video {
- display: block;
- width: 100%;
- height: 100%;
- object-fit: contain;
-}
-
-#video-container {
- flex-grow: 0;
- flex-shrink: 1;
- display: none;
-}
-
-a {
- color: var(--accent);
-}
-
-.chip {
- color: var(--fg);
- background: var(--chip-bg);
- text-decoration: none;
- padding: 0 0.5rem 0 1.45rem;
- display: inline-flex;
- position: relative;
- font-size: 0.9rem;
- height: 1.125rem;
- align-items: center;
- border-radius: 2rem;
- overflow: hidden;
-}
-
-.chip::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- width: 1.125rem;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- background: var(--accent-transparent);
- background-repeat: no-repeat;
- background-size: 18px;
- background-position: center;
-}
-
-.join-chip::before {
- background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTggNXYxNGwxMS03eiIvPjwvc3ZnPg==");
-}
-
-.time-chip::before {
- background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTExLjk5IDJDNi40NyAyIDIgNi40OCAyIDEyczQuNDcgMTAgOS45OSAxMEMxNy41MiAyMiAyMiAxNy41MiAyMiAxMlMxNy41MiAyIDExLjk5IDJ6TTEyIDIwYy00LjQyIDAtOC0zLjU4LTgtOHMzLjU4LTggOC04IDggMy41OCA4IDgtMy41OCA4LTggOHoiLz48cGF0aCBkPSJNMTIuNSA3SDExdjZsNS4yNSAzLjE1Ljc1LTEuMjMtNC41LTIuNjd6Ii8+PC9zdmc+");
-}
-
-label {
- display: block;
-}
-
-input[type="url"],
-input[type="text"] {
- background: #fff;
- background-clip: padding-box;
- border: 1px solid rgba(0, 0, 0, 0.12);
- border-radius: 6px;
- color: rgba(0, 0, 0, 0.8);
- display: block;
-
- margin: 0.5em 0;
- padding: 0.5em 1em;
- line-height: 1.5;
-
- font-family: sans-serif;
- font-size: 1em;
- width: 100%;
-
- resize: none;
- overflow-x: wrap;
- overflow-y: scroll;
-}
-
-button {
- background-color: var(--accent);
- border: var(--accent);
- border-radius: 6px;
- color: #fff;
- padding: 0.5em 1em;
- display: inline-block;
- font-weight: 400;
- text-align: center;
- white-space: nowrap;
- vertical-align: middle;
-
- font-family: sans-serif;
- font-size: 1em;
- width: 100%;
-
- user-select: none;
- border: 1px solid rgba(0, 0, 0, 0);
- line-height: 1.5;
- cursor: pointer;
- margin: 0.5em 0;
-}
-
-button:disabled {
- filter: saturate(0.75);
- opacity: 0.75;
- cursor: default;
-}
-
-button.small-button {
- font-size: 0.75em;
- padding-top: 0;
- padding-bottom: 0;
-}
-
-.subtitle-track-group {
- display: flex;
-}
-
-.subtitle-track-group > * {
- margin-top: 0 !important;
- margin-bottom: 0 !important;
- margin-right: 1ch !important;
-}
-
-#pre-join-controls,
-#create-controls {
- margin: 0;
- flex-grow: 1;
- overflow-y: auto;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-
-#join-session-form,
-#create-session-form {
- width: 500px;
- max-width: 100%;
- padding: 1rem;
-}
-
-#join-session-form > *:first-child,
-#create-session-form > *:first-child {
- margin-top: 0;
-}
-
-#post-create-message {
- display: none;
- width: 100%;
- font-size: 0.85em;
-}
-
-#chatbox-container {
- display: none;
-}
-
-.chat-message {
- overflow-wrap: break-word;
- margin-bottom: 0.125rem;
-}
-
-.chat-message > strong,
-#viewer-list strong {
- color: var(--user-color, var(--default-user-color));
-}
-
-.chat-message.user-join,
-.chat-message.user-leave,
-.chat-message.ping {
- font-style: italic;
-}
-
-.chat-message.set-time,
-.chat-message.set-playing,
-.chat-message.join-session {
- font-style: italic;
- text-align: right;
- font-size: 0.85em;
-}
-
-.chat-message.command-message {
- font-size: 0.85em;
-}
-
-.chat-message.set-time > strong,
-.chat-message.set-playing > strong,
-.chat-message.join-session > strong {
- color: unset !important;
-}
-
-.emoji {
- width: 2ch;
- height: 2ch;
- object-fit: contain;
- margin-bottom: -0.35ch;
-}
-
-#chatbox {
- padding: 0.5em 1em;
- overflow-y: scroll;
- flex-shrink: 1;
- flex-grow: 1;
-}
-
-#viewer-list {
- padding: 0.5em 1em;
- /* TODO: turn this into max-height instead of fixed height without breaking the chatbox height */
- overflow-y: scroll;
- border-bottom: var(--fg-transparent);
- border-bottom-style: solid;
- max-height: 4rem;
- flex-shrink: 0;
-}
-
-#chatbox-container {
- background-color: var(--bg);
- flex-direction: column;
- flex-grow: 1;
- flex-shrink: 1;
- flex-basis: 36ch;
- min-width: 36ch;
- overflow: hidden;
-}
-
-#chatbox-send {
- padding: 0 1em;
- padding-bottom: 0.5em;
- position: relative;
-}
-
-#chatbox-send > input {
- font-size: 0.75em;
- width: 100%;
-}
-
-#emoji-autocomplete {
- position: absolute;
- bottom: 3.25rem;
- background-image: var(--autocomplete-bg);
- border-radius: 6px;
- width: calc(100% - 2rem);
- max-height: 8.5rem;
- overflow-y: auto;
- clip-path: inset(0 0 0 0 round 8px);
-}
-
-#emoji-autocomplete:empty {
- display: none;
-}
-
-.emoji-option {
- background: transparent;
- font-size: 0.75rem;
- text-align: left;
- margin: 0 0.25rem;
- border-radius: 4px;
- width: calc(100% - 0.5rem);
- display: flex;
- align-items: center;
- padding: 0.25rem 0.5rem;
- scroll-margin: 0.25rem;
-}
-
-.emoji-option:first-child {
- margin-top: 0.25rem;
-}
-
-.emoji-option:last-child {
- margin-bottom: 0.25rem;
-}
-
-.emoji-option .emoji {
- width: 1.25rem;
- height: 1.25rem;
- margin: 0 0.5rem 0 0;
- font-size: 2.25ch;
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- flex-shrink: 0;
-}
-
-.emoji-name {
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.emoji-option.selected {
- background: var(--fg-transparent);
-}
-
-#join-session-colour {
- -moz-appearance: none;
- -webkit-appearance: none;
- appearance: none;
- border: none;
- padding: 0;
- border-radius: 6px;
- overflow: hidden;
- margin: 0.5em 0;
- height: 2rem;
- width: 2.5rem;
- cursor: pointer;
-}
-
-input[type="color"]::-moz-color-swatch {
- border: none;
- margin: 0;
- padding: 0;
-}
-
-input[type="color"]::-webkit-color-swatch {
- border: none;
- margin: 0;
- padding: 0;
-}
-
-input[type="color"]::-webkit-color-swatch-wrapper {
- border: none;
- margin: 0;
- padding: 0;
-}
-
-@media (min-aspect-ratio: 4/3) {
- body {
- flex-direction: row;
- }
-
- #chatbox-container {
- height: 100vh !important;
- flex-grow: 0;
- }
-
- #video-container {
- flex-grow: 1;
- }
-
- #chatbox {
- height: calc(100vh - 5em - 4em) !important;
- }
-}
+*,
+*:before,
+*:after {
+ box-sizing: border-box;
+}
+
+:root {
+ --bg-rgb: 28, 23, 36;
+ --fg-rgb: 234, 234, 248;
+ --accent-rgb: 181, 127, 220;
+ --accent-darker: rgb(95, 37, 136); /*50% darker*/
+ --accent-darkest: rgb(47, 19, 68); /*75% darker*/
+ --fg: rgb(var(--fg-rgb));
+ --bg: rgb(var(--bg-rgb));
+ --default-user-color: rgb(126, 208, 255);
+ --accent: rgb(var(--accent-rgb));
+ --fg-transparent: rgba(var(--fg-rgb), 0.25);
+ --bg-transparent: rgba(var(--bg-rgb), 0.25);
+ --autocomplete-bg: linear-gradient(
+ var(--fg-transparent),
+ var(--fg-transparent)
+ ),
+ linear-gradient(var(--bg), var(--bg));
+ --chip-bg: linear-gradient(
+ var(--accent-transparent),
+ var(--accent-transparent)
+ ),
+ linear-gradient(var(--bg), var(--bg));
+ --accent-transparent: rgba(var(--accent-rgb), 0.25);
+}
+
+html {
+ background-color: var(--bg);
+ color: var(--fg);
+ font-size: 1.125rem;
+ font-family: sans-serif;
+}
+
+html,
+body {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ overscroll-behavior: none;
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+video {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+#video-container {
+ flex-grow: 0;
+ flex-shrink: 1;
+ display: none;
+}
+
+a {
+ color: var(--accent);
+}
+
+.chip {
+ color: var(--fg);
+ background: var(--chip-bg);
+ text-decoration: none;
+ padding: 0 0.5rem 0 1.45rem;
+ display: inline-flex;
+ position: relative;
+ font-size: 0.9rem;
+ height: 1.125rem;
+ align-items: center;
+ border-radius: 2rem;
+ overflow: hidden;
+}
+
+.chip::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 1.125rem;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ background: var(--accent-transparent);
+ background-repeat: no-repeat;
+ background-size: 18px;
+ background-position: center;
+}
+
+.join-chip::before {
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTggNXYxNGwxMS03eiIvPjwvc3ZnPg==");
+}
+
+.time-chip::before {
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTExLjk5IDJDNi40NyAyIDIgNi40OCAyIDEyczQuNDcgMTAgOS45OSAxMEMxNy41MiAyMiAyMiAxNy41MiAyMiAxMlMxNy41MiAyIDExLjk5IDJ6TTEyIDIwYy00LjQyIDAtOC0zLjU4LTgtOHMzLjU4LTggOC04IDggMy41OCA4IDgtMy41OCA4LTggOHoiLz48cGF0aCBkPSJNMTIuNSA3SDExdjZsNS4yNSAzLjE1Ljc1LTEuMjMtNC41LTIuNjd6Ii8+PC9zdmc+");
+}
+
+label {
+ display: block;
+}
+
+input[type="url"],
+input[type="text"] {
+ background: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.12);
+ border-radius: 6px;
+ color: rgba(0, 0, 0, 0.8);
+ display: block;
+
+ margin: 0.5em 0;
+ padding: 0.5em 1em;
+ line-height: 1.5;
+
+ font-family: sans-serif;
+ font-size: 1em;
+ width: 100%;
+
+ resize: none;
+ overflow-x: wrap;
+ overflow-y: scroll;
+}
+
+button {
+ background-color: var(--accent);
+ border: var(--accent);
+ border-radius: 6px;
+ color: #fff;
+ padding: 0.5em 1em;
+ display: inline-block;
+ font-weight: 400;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+
+ font-family: sans-serif;
+ font-size: 1em;
+ width: 100%;
+
+ user-select: none;
+ border: 1px solid rgba(0, 0, 0, 0);
+ line-height: 1.5;
+ cursor: pointer;
+ margin: 0.5em 0;
+}
+
+button:disabled {
+ filter: saturate(0.75);
+ opacity: 0.75;
+ cursor: default;
+}
+
+button.small-button {
+ font-size: 0.75em;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.subtitle-track-group {
+ display: flex;
+}
+
+.subtitle-track-group > * {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ margin-right: 1ch !important;
+}
+
+#pre-join-controls,
+#create-controls {
+ margin: 0;
+ flex-grow: 1;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+#join-session-form,
+#create-session-form {
+ width: 500px;
+ max-width: 100%;
+ padding: 1rem;
+}
+
+#join-session-form > *:first-child,
+#create-session-form > *:first-child {
+ margin-top: 0;
+}
+
+#post-create-message {
+ display: none;
+ width: 100%;
+ font-size: 0.85em;
+}
+
+#chatbox-container {
+ display: none;
+}
+
+.chat-message {
+ overflow-wrap: break-word;
+ margin-bottom: 0.125rem;
+}
+
+.chat-message > strong,
+#viewer-list strong {
+ color: var(--user-color, var(--default-user-color));
+}
+
+.chat-message.user-join,
+.chat-message.user-leave,
+.chat-message.ping {
+ font-style: italic;
+}
+
+.chat-message.set-time,
+.chat-message.set-playing,
+.chat-message.join-session {
+ font-style: italic;
+ text-align: right;
+ font-size: 0.85em;
+}
+
+.chat-message.command-message {
+ font-size: 0.85em;
+}
+
+.chat-message.set-time > strong,
+.chat-message.set-playing > strong,
+.chat-message.join-session > strong {
+ color: unset !important;
+}
+
+.emoji {
+ width: 2ch;
+ height: 2ch;
+ object-fit: contain;
+ margin-bottom: -0.35ch;
+}
+
+#chatbox {
+ padding: 0.5em 1em;
+ overflow-y: scroll;
+ flex-shrink: 1;
+ flex-grow: 1;
+}
+
+#viewer-list {
+ padding: 0.5em 1em;
+ /* TODO: turn this into max-height instead of fixed height without breaking the chatbox height */
+ overflow-y: scroll;
+ border-bottom: var(--fg-transparent);
+ border-bottom-style: solid;
+ max-height: 4rem;
+ flex-shrink: 0;
+}
+
+#chatbox-container {
+ background-color: var(--bg);
+ flex-direction: column;
+ flex-grow: 1;
+ flex-shrink: 1;
+ flex-basis: 36ch;
+ min-width: 36ch;
+ overflow: hidden;
+}
+
+#chatbox-send {
+ padding: 0 1em;
+ position: relative;
+}
+
+#chatbox-send > input {
+ font-size: 0.75em;
+ width: 100%;
+}
+
+#emoji-autocomplete {
+ position: absolute;
+ bottom: 3.25rem;
+ background-image: var(--autocomplete-bg);
+ border-radius: 6px;
+ width: calc(100% - 2rem);
+ max-height: 8.5rem;
+ overflow-y: auto;
+ clip-path: inset(0 0 0 0 round 8px);
+}
+
+#emoji-autocomplete:empty {
+ display: none;
+}
+
+.emoji-option {
+ background: transparent;
+ font-size: 0.75rem;
+ text-align: left;
+ margin: 0 0.25rem;
+ border-radius: 4px;
+ width: calc(100% - 0.5rem);
+ display: flex;
+ align-items: center;
+ padding: 0.25rem 0.5rem;
+ scroll-margin: 0.25rem;
+}
+
+.emoji-option:first-child {
+ margin-top: 0.25rem;
+}
+
+.emoji-option:last-child {
+ margin-bottom: 0.25rem;
+}
+
+.emoji-option .emoji {
+ width: 1.25rem;
+ height: 1.25rem;
+ margin: 0 0.5rem 0 0;
+ font-size: 2.25ch;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ flex-shrink: 0;
+}
+
+.emoji-name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.emoji-option.selected {
+ background: var(--fg-transparent);
+}
+
+#join-session-colour {
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ appearance: none;
+ border: none;
+ padding: 0;
+ border-radius: 6px;
+ overflow: hidden;
+ margin: 0.5em 0;
+ height: 2rem;
+ width: 2.5rem;
+ cursor: pointer;
+}
+
+#options-toggle {
+ padding: 0 1em 1em;
+ position: relative;
+ text-align: right;
+ margin-top: auto;
+}
+
+#options-toggle #options-icon {
+ padding: 3px 10px;
+ font-size: 1em;
+ max-width: 3em;
+
+ color: transparent;
+ text-shadow: 0 0 0 white;
+ border: none;
+ box-shadow:0px 0px 0px 2px var(--accent-darkest) inset;
+
+ transform-style: preserve-3d;
+ transition: cubic-bezier(0, 0, 0.58, 1), cubic-bezier(0, 0, 0.58, 1);
+ transition-duration: 150ms;
+}
+
+#options-toggle #options-icon::before {
+ content: "";
+
+ position: absolute;
+
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ background-color: var(--accent-darker);
+ border-radius: inherit;
+ border: none;
+ box-shadow:0px 0px 0px 2px var(--accent-darkest) inset;
+
+ transform: translate3d(0, 0.5em, -1em);
+ transition: cubic-bezier(0, 0, 0.58, 1), cubic-bezier(0, 0, 0.58, 1);
+ transition-duration: 150ms;
+}
+
+#options-toggle #options-icon:hover {
+ transform: translate(0, 0.15em);
+ background-color: rgb(173, 113, 216); /*5% darker accent*/
+}
+
+#options-toggle #options-icon:hover::before {
+ transform: translate3d(0, 0.35em, -1em);
+}
+
+#options-toggle #options-icon:active {
+ transform: translate(0em, 0.5em);
+ background-color: rgb(165, 100, 213); /*10% darker accent*/
+}
+
+#options-toggle #options-icon:active::before {
+ transform: translate3d(0, 0, -1em);
+}
+
+#options {
+ display: none; /* default for sections is block */
+ padding: 01em;
+}
+
+input[type="color"]::-moz-color-swatch {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+input[type="color"]::-webkit-color-swatch {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+input[type="color"]::-webkit-color-swatch-wrapper {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+
+@media (min-aspect-ratio: 4/3) {
+ body {
+ flex-direction: row;
+ }
+
+ #chatbox-container {
+ height: 100vh !important;
+ flex-grow: 0;
+ }
+
+ #video-container {
+ flex-grow: 1;
+ }
+
+ #chatbox {
+ height: calc(100vh - 5em - 4em) !important;
+ }
+}