/*
 * サイネージ表示画面のデザインは固定。横画面 16:9 を基準とし、
 * 装飾は物理的なサイネージ枠で対応するため画面内には持たない。
 * 大会名は入力画面にだけ小さく出す（表示画面はタイムに集中させる）。
 * すべての寸法を vh / vw 基準にして解像度非依存にしてある。
 *
 * ---- 屋外・直射日光下での視認性について ----
 *
 * 「屋外の日当たりの良い場所でも視認性が良く」という要件のため、
 * 明るい背景に濃い文字にしている。液晶は周囲光を反射するので、暗い背景ほど
 * 反射光で浮いてコントラストが落ちる。背景を明るくすれば画面自身の輝度が上がり、
 * 反射光との差を保ちやすい。
 *
 * あわせて、細い字と中間調のグレーはコントラストが落ちたときに先に読めなくなるため、
 * 文字は太く、色は十分な明度差のあるものだけを使う。
 */

:root {
  --bg: #ffffff;
  --fg: #101010;
  /* 補助文字。白地に対して約 7:1 を確保する。 */
  --dim: #5a5a5a;
  /* ビブスのアクセント。白地に対して約 11:1。黄色系は白地では読めない。 */
  --accent: #0b3d91;
  color-scheme: light;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: 'Hiragino Kaku Gothic ProN', 'Noto Sans JP', 'Yu Gothic', sans-serif;
  /* 会場では誰も文字を選択しないし、誤ってドラッグ選択されると見苦しい。 */
  user-select: none;
  cursor: none;
  overflow: hidden;
}

#app {
  height: 100%;
}

[hidden] {
  display: none !important;
}

.screen {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4vh;
  text-align: center;
  padding: 0 4vw;
}

.screen__label {
  font-size: 4vh;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.1em;
}

/*
 * 入力画面に出す大会名。会場のサイネージ枠に大会名が入っていても、
 * 1 台で複数会場を使い回すと「今どの大会を出しているか」が画面から分からないため、
 * URL の大会 ID に対応する名前をここに出す。
 *
 * 右上に小さく、目立たないように置く（指示による）。
 * 流れから外してあるので、出ても出なくても桁の位置は動かない。
 * 長い大会名（「秩父宮記念 第51回富士登山駅伝競走大会【一般の部】」など）は
 * 右寄せのまま折り返す。ここは詰めて 1 行に収める場所ではないので fit.ts は使わない。
 */
.screen__race {
  position: fixed;
  top: 2vh;
  right: 1.5vw;
  max-width: 40vw;
  font-size: 2.2vh;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.05em;
  line-height: 1.4;
  text-align: right;
}

/*
 * テスト表示中の札（指示による）。存在しない大会 ID で起動したときだけ出る。
 *
 * 大会名と同じ右上に出すが、**同じ見た目にはしない**。ここに出ているのは
 * 実在しない記録なので、大会名と見分けがつかないと作り物のタイムが
 * 本物として読まれる。枠と赤字で、離れていても大会名でないと分かるようにする。
 */
.demo-badge {
  position: fixed;
  top: 2vh;
  right: 1.5vw;
  max-width: 40vw;
  padding: 0.6vh 1.2vh;
  border: 0.4vh solid #b3261e;
  border-radius: 0.3em;
  font-size: 2.6vh;
  font-weight: 700;
  color: #b3261e;
  letter-spacing: 0.08em;
  line-height: 1.3;
  text-align: right;
}

.demo-badge__hint {
  display: block;
  font-size: 1.8vh;
  font-weight: 600;
  letter-spacing: 0.05em;
}

.screen__hint {
  font-size: 2.6vh;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.08em;
}

.screen__hint.is-busy {
  color: var(--accent);
}

/* 保守キーの案内。設営時にしか要らないので、ログイン画面にだけ置く。 */
.screen__note {
  font-size: 2.2vh;
  color: var(--dim);
  letter-spacing: 0.06em;
  line-height: 1.8;
}

.screen__note kbd {
  font: inherit;
  font-weight: 700;
  border: 2px solid var(--dim);
  border-radius: 0.25em;
  padding: 0.1em 0.45em;
  margin: 0 0.15em;
}

/*
 * ログイン画面だけは設営時にキーボードで操作するので、素のフォーム部品を使う。
 * 会場運用中に出ることはない画面なので、装飾は最小限でよい。
 */
.login {
  display: flex;
  flex-direction: column;
  gap: 2.5vh;
  width: min(60vw, 40rem);
}

.login__field,
.login__submit {
  font: inherit;
  font-size: 4vh;
  font-weight: 700;
  padding: 1.5vh 2vh;
  border-radius: 0.2em;
  border: 3px solid var(--dim);
  background: var(--bg);
  color: var(--fg);
  text-align: center;
}

.login__field::placeholder {
  color: #8a8a8a;
  font-weight: 400;
}

.login__field:focus {
  outline: none;
  border-color: var(--accent);
}

.login__submit {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--bg);
  cursor: pointer;
  letter-spacing: 0.2em;
}

.login__submit:disabled {
  opacity: 0.5;
  cursor: default;
}

.screen__hint.is-error {
  color: #b3261e;
  font-weight: 700;
}

/* ログイン画面では入力するのでカーソルを出す。 */
body:has(#screen-login:not([hidden])) {
  cursor: auto;
}

/*
 * 起動に失敗したときだけ出る。4 画面はすべて hidden で始まるので、
 * これが無いと会場のサイネージは真っ白のままになり、原因が何も残らない。
 */
.fatal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6vw;
  background: var(--bg);
  color: #b3261e;
  font-size: 3vh;
  font-weight: 700;
  line-height: 1.6;
  text-align: center;
}

/* 入力中の桁。等幅数字にしないと 1 を打つたびに桁がずれて落ち着かない。 */
.digits {
  --max-font: 28vh;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
}

/*
 * fit.ts が実測で詰めるための前提。収まり先は fit.ts が親から測るので、
 * ここでは折り返さないことと、上限サイズの置き場だけを決める。
 *   nowrap         ── 折り返すと scrollWidth が伸びず、はみ出しを検出できない
 *   overflow+省略記号 ── 下限サイズでも収まらない極端な長さのときの保険
 *   --max-font     ── 各クラスが上限を宣言し、ここで font-size に流す
 */
.fit-line {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--max-font);
}

/* 等幅数字。打つたびに桁がずれないようにする。 */
.tabular {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}

/* ---- 表示画面 ---------------------------------------------------------- */

/*
 * サイネージは横置き固定。縦の余白を切り詰めて 3 行を可能な限り大きく取る。
 * 大きさの序列は タイム > ビブス ＝ 氏名（「特にタイムを一番に」「ビブスと氏名は
 * 同じ大きさに」という指示による）。縦方向の配分もタイムに最も多く振っている。
 *
 * タイムを画面の中央に置き、ビブスと氏名をその上に載せる（指示による）。
 * 上下を同じ 1fr で挟むので、タイムの行は枠の中央に来る。
 * 原因コードと残り時間バーは position: fixed で流れの外にあるため、行を取らない。
 */
.screen--result {
  padding: 3vh 4vw;
  display: grid;
  grid-template-rows: 1fr auto 1fr;
  align-items: center;
}

/*
 * ビブスと氏名の行。タイムの真上に置くので、下端を揃える。
 *
 * 行は左揃え（指示による）。中央揃えだと氏名の長さで行頭の位置が動くが、
 * 大きさを最悪ケースに揃えてある（→ .result__headline.is-found）ので、
 * 左揃えにすれば「No.」の位置も毎回同じになる。
 */
.result__headline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: flex-start;
  text-align: left;
  gap: 1vh 2.5vw;
  width: 100%;
  align-self: end;
}

/*
 * 記録ありのときは、ビブスと氏名を**同じ大きさ**で 1 行に並べる（指示による）。
 * 大きさを行全体に持たせて子に継がせるので、2 つは必ず同じ寸法になる。
 * 折り返さずに行ごと fit.ts で詰める（別々に詰めると片方だけが大きくなる）。
 */
.result__headline.is-found {
  --max-font: 16vh;
  font-size: var(--max-font);
  flex-wrap: nowrap;
}

.result__headline.is-found .result__bib,
.result__headline.is-found .result__name {
  font-size: inherit;
}

.result__bib {
  /* 記録なしのときだけ効く上限。記録ありは行（is-found）の大きさを継ぐ。 */
  --max-font: 8vh;
  font-weight: 700;
  line-height: 1.1;
  color: var(--accent);
  letter-spacing: 0.06em;
}

/*
 * 氏名は 22vh が上限。実際に収まらなければ fit.ts が実測して font-size を詰める。
 * 海外ランナーの長い氏名でも必ず画面内に収まる。
 */
.result__name {
  --max-font: 22vh;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.02em;
}

/*
 * 記録なしの文言。氏名の枠をそのまま使うと横幅いっぱいまで拡大され、
 * 記録ありの氏名より大きくなってしまうので、上限を落として色も沈める。
 */
.result__name.is-nodata {
  /* ビブスと同じ行には並べず、必ず次の行へ落とす（指示による）。 */
  flex-basis: 100%;
  --max-font: 12vh;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.1em;
}

/* 画面の主役。横幅が許す限り大きく出す。 */
.result__time {
  --max-font: 46vh;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.01em;
}

/*
 * 原因コードは切り分け用なので目立たせない。ただし読めなくては意味がない。
 * 白地に #c4c4c4 はコントラスト 1.7:1 で、日光下どころか室内でも視認できず、
 * 「error のときだけ原因を出す」という設計がそのまま無効になっていた。
 * 3.4:1（プレースホルダと同じ濃さ）まで上げる。離れて見れば十分控えめ。
 */
.result__note {
  position: fixed;
  right: 1.5vw;
  bottom: 2vh;
  font-size: 1.6vh;
  color: #8a8a8a;
  letter-spacing: 0.05em;
}

/* 残り時間バー。JS のタイマーを使わず CSS トランジションだけで 3 分かけて縮む。 */
/* 日光下では細い線と薄い色が最初に消えるので、太く・不透明にする。 */
.idle-bar__track {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
}

.idle-bar {
  height: 100%;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(0);
}
