/*
 * Ghost Keyboard, drawn in HTML (site/js/kbd.js builds the markup; this draws it).
 *
 * Every length is in "dp": --kb-dp is one of the phone's dp in CSS pixels, set by the component from the keyboard's
 * width (width / 412, clamped), so the keyboard keeps a phone's proportions at any size. The numbers are the app's:
 * 60dp rows (a 48dp key and 12dp of gap), 3dp/6dp of padding round each key's face, 6dp corners, a 44dp row above.
 *
 * Colours are KeyboardTheme.kt's, as custom properties the component writes on .gk for the theme in use. The values
 * below are the Ghost theme, the app's own, so the keyboard is right before a theme is set.
 */

.gk {
  --kb-bg: #0A0B0C;
  --kb-key: #232528;
  --kb-key-down: #383B3F;
  --kb-fn: #16181A;
  --kb-fn-down: #2C2F33;
  --kb-accent: #51FCFB;
  --kb-accent-down: #2ED8D7;
  --kb-on-accent: #00302F;
  --kb-text: #E8EDED;
  --kb-hint: #8A9393;
  --kb-popup: #2C2F33;
  --kb-popup-sel: #51FCFB;
  --kb-on-popup-sel: #00302F;
  --kb-popup-shadow: rgba(0, 0, 0, .5);
  --kb-icon: #B9C4C4;
  --kb-dp: 1px;
  --kb-shrink: 6px;

  position: relative;
  display: block;
  width: 100%;
  min-width: 0;
  /* Before the component has measured: a phone's keyboard, so the page does not jump. */
  min-height: calc(18 * var(--kb-dp));
  box-sizing: border-box;
  background: var(--kb-bg);
  color: var(--kb-text);
  /* The phone's own face first (Roboto on Android), then the site's. */
  font-family: Roboto, Inter, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  font-size: calc(16 * var(--kb-dp));
  line-height: 1;
  letter-spacing: 0;
  text-align: left;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  outline: none;
}

.gk *,
.gk *::before,
.gk *::after { box-sizing: border-box; }

.gk [hidden] { display: none !important; }

.gk:focus-visible {
  outline: 2px solid var(--kb-accent);
  outline-offset: 3px;
}

/* A keyboard that only plays: nothing in it takes a pointer or the focus. */
.gk--static,
.gk--static * { pointer-events: none; }

/*
 * A fragment (GhostKbd's `fragment` option): part of the keyboard in a small place. It is as wide as its host and
 * centres its keys; `bare: true` takes the keyboard's own ground away, so the keys stand on whatever is behind them
 * (a keycap's face).
 */
.gk--frag { min-height: 0; }

.gk--bare,
.gk--bare .gk-bar { background: none; }

.gk--bare .gk-dict { background: var(--cap-face, var(--kb-bg)); }
.gk--bare .gk-badge { box-shadow: 0 0 0 calc(1.6 * var(--kb-dp)) var(--cap-face, var(--kb-bg)); }

/* Bordered keys in the light theme are white: on a pale face they need an edge of their own. */
.gk--bare.gk--light.gk--borders .gk-face {
  box-shadow: 0 0 0 1px rgba(31, 31, 31, .07), 0 1px 0 rgba(31, 31, 31, .14);
}

/* And in a dark theme they are barely lighter than the face they stand on: the same, a hairline of light and a foot. */
.gk--bare.gk--borders:not(.gk--light) .gk-face {
  box-shadow: 0 0 0 1px rgba(232, 237, 237, .09), 0 1px 0 rgba(0, 0, 0, .45);
}

/* ------------------------------------------------------------------------------------------------ the keys ------- */

.gk-keys {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: calc(18 * var(--kb-dp));
  touch-action: pan-y;
}

.gk-key {
  position: absolute;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: 1;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  overflow: visible;
  -webkit-tap-highlight-color: transparent;
}

/* The visible face inside a key's touch area. */
.gk-face {
  position: absolute;
  left: calc(3 * var(--kb-dp));
  right: calc(3 * var(--kb-dp));
  top: calc(6 * var(--kb-dp));
  bottom: calc(6 * var(--kb-dp));
  border-radius: calc(6 * var(--kb-dp));
  display: grid;
  place-items: center;
  pointer-events: none;
}

.gk-cap { display: contents; }

/* The legend: what the backlight lights (--kb-lit, --kb-lit-hint) or dims (--kb-lit-a), and nothing else. */
.gk-legend {
  position: relative;
  display: grid;
  place-items: center;
  color: var(--kb-lit, var(--kb-text));
  opacity: var(--kb-lit-a, 1);
  font-size: calc(21 * var(--kb-dp));
  white-space: pre;
}

.gk-hint {
  position: absolute;
  right: calc(7 * var(--kb-dp));
  top: calc(3.6 * var(--kb-dp));
  transform: translateX(50%);
  font-size: calc(10.5 * var(--kb-dp));
  color: var(--kb-lit-hint, var(--kb-hint));
  opacity: var(--kb-lit-a, 1);
}

.gk--nohints .gk-hint { display: none; }

.gk-k-mode .gk-legend {
  font-size: calc(15 * var(--kb-dp));
  font-weight: 500;
}

.gk-ico { display: block; flex: none; }

.gk-k-shift .gk-ico { width: calc(23 * var(--kb-dp)); height: calc(23 * var(--kb-dp)); }
.gk-k-delete .gk-ico { width: calc(24 * var(--kb-dp)); height: calc(24 * var(--kb-dp)); }
.gk-k-enter .gk-ico,
.gk-k-mic .gk-ico { width: calc(22 * var(--kb-dp)); height: calc(22 * var(--kb-dp)); }

/* Caps lock: the filled arrow, underlined. */
.gk-k-shift.is-locked .gk-legend::after {
  content: "";
  position: absolute;
  left: 50%;
  top: calc(50% + 13 * var(--kb-dp));
  width: calc(10 * var(--kb-dp));
  height: calc(2 * var(--kb-dp));
  margin-left: calc(-5 * var(--kb-dp));
  border-radius: var(--kb-dp);
  background: currentColor;
}

/*
 * The app's own look, as it is installed (Settings: key borders off): only ?123, space and enter have a shape, pills
 * about three quarters of a key's height; every other key is only its label, with a wash while it is pressed.
 */
.gk-key.is-down .gk-face { background: var(--kb-key); }

.gk-pill .gk-face {
  top: calc(6 * var(--kb-dp) + var(--kb-shrink));
  bottom: calc(6 * var(--kb-dp) + var(--kb-shrink));
  border-radius: 999px;
  background: var(--kb-key);
}

.gk-pill.is-down .gk-face { background: var(--kb-key-down); }

.gk-k-enter .gk-face { background: var(--kb-accent); }
.gk-k-enter.is-down .gk-face { background: var(--kb-accent-down); }
.gk-k-enter .gk-legend { color: var(--kb-on-accent); }

/* Key borders on: every key has its own face; shift, delete and the page keys in the darker function colour. */
.gk--borders .gk-key .gk-face {
  top: calc(6 * var(--kb-dp));
  bottom: calc(6 * var(--kb-dp));
  border-radius: calc(6 * var(--kb-dp));
  background: var(--kb-key);
}

.gk--borders .gk-key.is-down .gk-face { background: var(--kb-key-down); }
.gk--borders .gk-fn .gk-face { background: var(--kb-fn); }
.gk--borders .gk-fn.is-down .gk-face { background: var(--kb-fn-down); }
.gk--borders .gk-k-shift.is-on .gk-face { background: var(--kb-key); }
.gk--borders .gk-k-shift.is-on.is-down .gk-face { background: var(--kb-key-down); }
.gk--borders .gk-k-enter .gk-face { background: var(--kb-accent); }
.gk--borders .gk-k-enter.is-down .gk-face { background: var(--kb-accent-down); }

/*
 * Retro: raised mechanical keycaps. A darker skirt below a cap with a lit top edge; pressed, the cap sinks most of the
 * way into its skirt (KeyboardView.drawKeycap: 5dp deep, 70% of it travelled).
 */
.gk--mech .gk-key .gk-face {
  --cap: var(--kb-key);
  top: calc(6 * var(--kb-dp));
  bottom: calc(6 * var(--kb-dp));
  border-radius: calc(4.8 * var(--kb-dp));
  background: color-mix(in srgb, var(--cap) 62%, #000);
  box-shadow: 0 calc(1.5 * var(--kb-dp)) 0 calc(-.5 * var(--kb-dp)) rgba(0, 0, 0, .33);
  display: block;
}

.gk--mech .gk-fn .gk-face { --cap: var(--kb-fn); }
.gk--mech .gk-k-shift.is-on .gk-face { --cap: var(--kb-key); }
.gk--mech .gk-k-enter .gk-face { --cap: var(--kb-accent); }

.gk--mech .gk-cap {
  position: absolute;
  left: calc(1.5 * var(--kb-dp));
  right: calc(1.5 * var(--kb-dp));
  top: 0;
  bottom: calc(6 * var(--kb-dp));
  border-radius: calc(4.8 * var(--kb-dp));
  background: var(--cap);
  display: grid;
  place-items: center;
}

.gk--mech .gk-cap::before {
  content: "";
  position: absolute;
  left: calc(2 * var(--kb-dp));
  right: calc(2 * var(--kb-dp));
  top: var(--kb-dp);
  height: calc(2 * var(--kb-dp));
  border-radius: var(--kb-dp);
  background: color-mix(in srgb, var(--cap) 65%, #fff);
  opacity: .55;
}

.gk--mech .gk-key.is-down .gk-face { top: calc(9.5 * var(--kb-dp)); }

.gk--mech .gk-key.is-down .gk-cap {
  bottom: calc(2.5 * var(--kb-dp));
  background: color-mix(in srgb, var(--cap) 92%, #000);
}

.gk--mech .gk-key.is-down .gk-cap::before { opacity: .235; }

/* ------------------------------------------------------------------------------------------------ the popups ----- */

/* The key preview: a bubble the width of the key standing on top of it, the character drawn large. */
.gk-pop {
  position: absolute;
  z-index: 3;
  display: grid;
  place-items: center;
  border-radius: calc(6 * var(--kb-dp));
  background: var(--kb-popup);
  color: var(--kb-text);
  box-shadow: 0 var(--kb-dp) calc(4 * var(--kb-dp)) var(--kb-popup-shadow);
  white-space: pre;
  pointer-events: none;
}

/* The long-press panel: the choices in rows of eight, the first nearest the finger. */
.gk-panel {
  position: absolute;
  z-index: 4;
  border-radius: calc(9 * var(--kb-dp));
  background: var(--kb-popup);
  box-shadow: 0 calc(2 * var(--kb-dp)) calc(6 * var(--kb-dp)) var(--kb-popup-shadow);
  pointer-events: none;
}

.gk-cell {
  position: absolute;
  display: grid;
  place-items: center;
  isolation: isolate;
  color: var(--kb-text);
  white-space: pre;
}

.gk-cell.is-sel { color: var(--kb-on-popup-sel); }

.gk-cell.is-sel::before {
  content: "";
  position: absolute;
  inset: calc(2 * var(--kb-dp));
  z-index: -1;
  border-radius: calc(6 * var(--kb-dp));
  background: var(--kb-popup-sel);
}

/* The canvas over the keys: the swipe trail, the adaptive keys heatmap, a demo's own drawing. */
.gk-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ------------------------------------------------------------------------------------------------ the row -------- */

/* PluginRow.kt: the cog, a strip that scrolls sideways, the arrow keys pinned at its end. */
.gk-bar {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: calc(44 * var(--kb-dp));
  display: none;
  align-items: stretch;
  background: var(--kb-bg);
}

.gk--row .gk-bar { display: flex; }

.gk-ib {
  position: relative;
  flex: 0 0 calc(44 * var(--kb-dp));
  min-width: calc(44 * var(--kb-dp));
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: none;
  color: var(--kb-icon);
  font: inherit;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -webkit-tap-highlight-color: transparent;
}

.gk-ib:active { background: rgba(128, 128, 128, .133); }
.gk-ib .gk-ico { width: calc(20 * var(--kb-dp)); height: calc(20 * var(--kb-dp)); }

.gk-ib:focus-visible,
.gk-ri:focus-visible {
  outline: 2px solid var(--kb-accent);
  outline-offset: -2px;
}

/* An update is waiting: a dot on the cog in the accent, ringed in the row's ground. */
.gk-badge {
  position: absolute;
  left: calc(50% + 3.6 * var(--kb-dp));
  top: calc(50% - 11.6 * var(--kb-dp));
  width: calc(8 * var(--kb-dp));
  height: calc(8 * var(--kb-dp));
  border-radius: 50%;
  background: var(--kb-accent);
  box-shadow: 0 0 0 calc(1.6 * var(--kb-dp)) var(--kb-bg);
}

.gk-strip {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: stretch;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.gk-strip::-webkit-scrollbar { display: none; }

/* More of the row past its right edge than fits (kbd.js sets this): it fades there, since no scrollbar says so. */
.gk-strip.is-cut {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}

.gk-ri {
  position: relative;
  flex: 0 0 auto;
  min-width: 0;
  margin: 0;
  padding: 0 calc(12 * var(--kb-dp));
  border: 0;
  border-radius: calc(18 * var(--kb-dp));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(6 * var(--kb-dp));
  background: none;
  color: var(--kb-text);
  font: inherit;
  font-size: calc(14 * var(--kb-dp));
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -webkit-tap-highlight-color: transparent;
}

/*
 * A finger's room. A chip is 32dp tall inside the row's 44dp, and a fragment's row may be scaled below a phone's: the
 * button answers a touch a little past its own edge, up and down, without looking any larger.
 */
button.gk-ri::after,
button.gk-ib::after {
  content: "";
  position: absolute;
  inset: -7px 0;
}

span.gk-ri { cursor: default; }
button.gk-ri:active { background: var(--kb-key-down); }

.gk-ri-t {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.gk-ri .gk-ico,
.gk-ri > svg {
  flex: none;
  width: calc(18 * var(--kb-dp));
  height: calc(18 * var(--kb-dp));
  color: var(--kb-icon);
}

.gk-ri.gk-wide {
  padding: 0 calc(6 * var(--kb-dp));
  font-size: calc(16 * var(--kb-dp));
}

.gk-ri.gk-bold { font-weight: 500; }

.gk-ri.gk-chip,
.gk-view {
  margin: calc(6 * var(--kb-dp)) calc(4 * var(--kb-dp));
}

.gk-ri.gk-chip { background: var(--kb-fn); }
.gk-view { flex: 0 0 auto; align-self: center; }

.gk-ri.gk-fill {
  flex: 1 1 0;
  justify-content: flex-start;
}

.gk-arrows { display: flex; }

.gk-arrows .gk-ib {
  flex-basis: calc(36 * var(--kb-dp));
  min-width: calc(36 * var(--kb-dp));
  touch-action: none;
}

/* ------------------------------------------------------------------------------------------------ dictation ------ */

/* The listening panel over the keys: a ring that swells with the voice (--gk-level, 0 to 1), and what to do. */
.gk-dict {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: calc(18 * var(--kb-dp));
  z-index: 5;
  background: var(--kb-bg);
  cursor: pointer;
  --gk-level: 0;
}

.gk--row .gk-dict,
.gk--row .gk-keys { top: calc(44 * var(--kb-dp)); }

/* Over a row or two of keys (a fragment) the panel lies down: the dot, then the words beside it. */
.gk--short .gk-dict {
  display: grid;
  grid-template-columns: auto auto;
  place-content: center;
  align-items: center;
  gap: calc(12 * var(--kb-dp));
}

.gk--short .gk-dict[hidden] { display: none; }

.gk--short .gk-dict-dot,
.gk--short .gk-dict-halo {
  position: static;
  grid-area: 1 / 1;
  width: calc(34 * var(--kb-dp));
  height: calc(34 * var(--kb-dp));
  margin: 0;
}

.gk--short .gk-dict-dot .gk-ico {
  width: calc(18 * var(--kb-dp));
  height: calc(18 * var(--kb-dp));
  transform: none;
}

.gk--short .gk-dict-label {
  position: static;
  grid-area: 1 / 2;
  font-size: calc(13 * var(--kb-dp));
  text-align: left;
}

.gk-dict-dot,
.gk-dict-halo {
  position: absolute;
  left: 50%;
  top: 42%;
  width: calc(68 * var(--kb-dp));
  height: calc(68 * var(--kb-dp));
  margin: calc(-34 * var(--kb-dp)) 0 0 calc(-34 * var(--kb-dp));
  border-radius: 50%;
  background: var(--kb-accent);
}

.gk-dict-halo {
  opacity: .235;
  /* r + 4dp at rest, 26dp more at full voice, over a 34dp circle. */
  transform: scale(calc(1.1176 + .7647 * var(--gk-level)));
  transition: transform 90ms linear;
}

.gk-dict-dot {
  display: grid;
  place-items: center;
  color: var(--kb-on-accent);
}

.gk-dict-dot .gk-ico {
  width: calc(35 * var(--kb-dp));
  height: calc(35 * var(--kb-dp));
  transform: translateY(var(--kb-dp));
}

.gk-dict-label {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(42% + 56 * var(--kb-dp));
  text-align: center;
  font-size: calc(15 * var(--kb-dp));
  font-weight: 500;
  color: var(--kb-text);
}

/* ------------------------------------------------------------------------------------------------ the field ------ */

/*
 * What the keyboard types into (GhostKbd.attach(el) on a block element): the text as it is, a caret that blinks once
 * the typing rests. The caret takes --gk-caret, else the site's --accent.
 */
.gk-field {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  cursor: text;
  outline: none;
}

.gk-field--empty::after {
  content: attr(data-placeholder);
  color: var(--ink-2, #8A9393);
  pointer-events: none;
}

.gk-caret {
  display: inline-block;
  width: 0;
  height: 1.15em;
  margin: 0 -1px;
  border-left: 2px solid var(--gk-caret, var(--accent, #51FCFB));
  vertical-align: text-bottom;
  animation: gk-blink 1.06s steps(1, end) .53s infinite;
}

/*
 * A headline typed out (typeInto(el, text) in kbd.js): the whole sentence is laid out from the start, the part not
 * yet typed unseen, so the lines never move; a screen reader gets the sentence whole from .gk-vh.
 */
.gk-vh {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.gk-typed-rest { visibility: hidden; }

/* In running text the caret is an inline box, so it never offers a line a place to break. */
.gk-caret--inline {
  display: inline;
  height: auto;
  margin: 0 calc(max(2px, .06em) / -2);
  border-left-width: max(2px, .06em);
  vertical-align: baseline;
}

/* A marked run in a field (attach's marks: [{re, cls: 'gk-mark'}]): a <<request>>, a [[note]], a #tag. */
.gk-mark { color: var(--gk-caret, var(--accent, #51FCFB)); }

@keyframes gk-blink {
  50% { opacity: 0; }
}

/* Off screen nothing moves. */
.gk-field--paused .gk-caret { animation-play-state: paused; }
.gk--paused .gk-dict-halo { transition: none; }

/* The listening panel tapped by a script's own finger (a second tap ends a take): the dot goes down a moment. */
.gk-dict.is-tapped .gk-dict-dot { transform: scale(.9); filter: brightness(.86); }

@media (prefers-reduced-motion: reduce) {
  .gk-caret { animation: none; }
  .gk-dict-halo { transition: none; }
}

@media (forced-colors: active) {
  .gk-key.is-down .gk-face,
  .gk-pill .gk-face { outline: 1px solid CanvasText; }
  .gk-cell.is-sel { outline: 2px solid Highlight; }
}
