UI REFINEMENT
Browse files- core/api_handlers.py +161 -18
- core/nvidia_client.py +119 -43
- core/retry_handler.py +117 -12
- core/scoring_engine.py +22 -9
- frontend/index.html +97 -81
- frontend/script.js +520 -123
- frontend/styles.css +880 -202
core/api_handlers.py
CHANGED
|
@@ -201,8 +201,11 @@ def _build_followup_messages(
|
|
| 201 |
{"role": "system", "content": system_prompt},
|
| 202 |
]
|
| 203 |
|
| 204 |
-
# Replay conversation history as role turns (strip attack_tag metadata)
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
| 206 |
role = entry.get("role", "user")
|
| 207 |
content = entry.get("content", "")
|
| 208 |
if role == "assistant":
|
|
@@ -795,6 +798,148 @@ def _confidence_from_fill(ctx: dict[str, str]) -> str:
|
|
| 795 |
return "low"
|
| 796 |
|
| 797 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 |
def _structure_pitch_local_fallback(pitch_text: str) -> dict[str, Any]:
|
| 799 |
"""Heuristic extraction when Nemotron is unavailable."""
|
| 800 |
text = pitch_text.strip()
|
|
@@ -874,34 +1019,29 @@ def _structure_pitch_local_fallback(pitch_text: str) -> dict[str, Any]:
|
|
| 874 |
ctx["ask"] = sentence
|
| 875 |
break
|
| 876 |
|
| 877 |
-
missing = _missing_startup_fields(ctx)
|
| 878 |
summary = " ".join(sentences[:2])[:220] if sentences else text[:220]
|
|
|
|
| 879 |
|
| 880 |
return {
|
| 881 |
"ok": True,
|
| 882 |
"startup_context": ctx,
|
| 883 |
-
"missing_fields":
|
| 884 |
-
"confidence":
|
|
|
|
|
|
|
| 885 |
"brief_summary": summary,
|
| 886 |
"source": "local_fallback",
|
| 887 |
}
|
| 888 |
|
| 889 |
|
| 890 |
def _parse_structure_pitch_response(raw: str) -> dict[str, Any] | None:
|
|
|
|
|
|
|
| 891 |
parsed, _ = parse_model_json(raw)
|
| 892 |
if not isinstance(parsed, dict):
|
| 893 |
return None
|
| 894 |
|
| 895 |
ctx = _normalize_startup_context(parsed.get("startup_context"))
|
| 896 |
-
missing = parsed.get("missing_fields")
|
| 897 |
-
if not isinstance(missing, list):
|
| 898 |
-
missing = _missing_startup_fields(ctx)
|
| 899 |
-
else:
|
| 900 |
-
missing = [str(f).strip() for f in missing if str(f).strip() in _STARTUP_CONTEXT_FIELDS]
|
| 901 |
-
|
| 902 |
-
confidence = str(parsed.get("confidence", "")).strip().lower()
|
| 903 |
-
if confidence not in ("low", "medium", "high"):
|
| 904 |
-
confidence = _confidence_from_fill(ctx)
|
| 905 |
|
| 906 |
summary = str(parsed.get("brief_summary", "")).strip()
|
| 907 |
if not summary:
|
|
@@ -909,8 +1049,6 @@ def _parse_structure_pitch_response(raw: str) -> dict[str, Any] | None:
|
|
| 909 |
|
| 910 |
return {
|
| 911 |
"startup_context": ctx,
|
| 912 |
-
"missing_fields": missing,
|
| 913 |
-
"confidence": confidence,
|
| 914 |
"brief_summary": summary,
|
| 915 |
}
|
| 916 |
|
|
@@ -942,11 +1080,16 @@ def handle_structure_pitch(payload: dict[str, Any]) -> dict[str, Any]:
|
|
| 942 |
structured = _parse_structure_pitch_response(repair["content"])
|
| 943 |
|
| 944 |
if structured is not None:
|
|
|
|
|
|
|
|
|
|
| 945 |
return {
|
| 946 |
"ok": True,
|
| 947 |
"startup_context": structured["startup_context"],
|
| 948 |
-
"missing_fields":
|
| 949 |
-
"confidence":
|
|
|
|
|
|
|
| 950 |
"brief_summary": structured["brief_summary"],
|
| 951 |
"source": "nemotron",
|
| 952 |
}
|
|
|
|
| 201 |
{"role": "system", "content": system_prompt},
|
| 202 |
]
|
| 203 |
|
| 204 |
+
# Replay conversation history as role turns (strip attack_tag metadata).
|
| 205 |
+
# Cap at last 6 entries (3 full Q&A exchanges) so late-round input token
|
| 206 |
+
# growth never crowds out the output budget on the opponent mode call.
|
| 207 |
+
trimmed_history = history[-6:] if len(history) > 6 else history
|
| 208 |
+
for entry in trimmed_history:
|
| 209 |
role = entry.get("role", "user")
|
| 210 |
content = entry.get("content", "")
|
| 211 |
if role == "assistant":
|
|
|
|
| 798 |
return "low"
|
| 799 |
|
| 800 |
|
| 801 |
+
# ---------------------------------------------------------------------------
|
| 802 |
+
# Deterministic structure confidence (Part A of confidence-consistency fix)
|
| 803 |
+
# ---------------------------------------------------------------------------
|
| 804 |
+
|
| 805 |
+
_CONFIDENCE_FIELD_WEIGHTS: dict[str, int] = {
|
| 806 |
+
"name": 10,
|
| 807 |
+
"problem": 15,
|
| 808 |
+
"target_users": 12,
|
| 809 |
+
"solution": 15,
|
| 810 |
+
"why_ai": 10,
|
| 811 |
+
"traction": 15,
|
| 812 |
+
"competitors": 8,
|
| 813 |
+
"ask": 10,
|
| 814 |
+
}
|
| 815 |
+
|
| 816 |
+
# If any of these fields is absent the score cannot exceed its cap value.
|
| 817 |
+
_CONFIDENCE_CAPS: tuple[tuple[str, int], ...] = (
|
| 818 |
+
("problem", 60),
|
| 819 |
+
("solution", 60),
|
| 820 |
+
("target_users", 70),
|
| 821 |
+
("traction", 74), # cap below 75 so missing traction → at most medium
|
| 822 |
+
("competitors", 92), # missing competitors → visible gap from 100; still high
|
| 823 |
+
("why_ai", 90), # missing/nonsense why_ai → max 90; one-word answers caught by min-words
|
| 824 |
+
("ask", 85),
|
| 825 |
+
)
|
| 826 |
+
|
| 827 |
+
_FILLER_VALUES = frozenset({
|
| 828 |
+
"not specified", "n/a", "none", "unknown", "tbd", "-", "",
|
| 829 |
+
"idk", "i don't know", "i dont know", "not sure", "na", "no idea",
|
| 830 |
+
"dunno", "nothing", "?", "??", "???", "yes", "no", "nope", "yep",
|
| 831 |
+
"to be determined", "to be decided", "will update", "coming soon",
|
| 832 |
+
})
|
| 833 |
+
|
| 834 |
+
# Minimum real-word count for description fields — rejects single-word noise like "idk", "yes", "dunno".
|
| 835 |
+
# Name, competitors, ask intentionally use min=1 (a single-word name or ask is valid).
|
| 836 |
+
_CONF_MIN_WORDS: dict[str, int] = {
|
| 837 |
+
"problem": 2, "solution": 2, "why_ai": 2, "traction": 2, "target_users": 2,
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
_CONFIDENCE_USER_SEG_RE = re.compile(
|
| 841 |
+
r"\b(college students?|university students?|indie developers?|small businesses?|"
|
| 842 |
+
r"enterprise|founders?|educators?|teachers?|researchers?|professionals?|"
|
| 843 |
+
r"teams?|parents?|teenagers?|consumers?|startup founders?)\b",
|
| 844 |
+
re.IGNORECASE,
|
| 845 |
+
)
|
| 846 |
+
_CONFIDENCE_CONCRETE_ASK_RE = re.compile(
|
| 847 |
+
r"(\$[\d,]+[kKmM]?|\d+[kK]\s*(?:usd|dollars?)?|mentorship|campus pilot|"
|
| 848 |
+
r"equity partner|co.?founder|sponsorship|strategic partner)",
|
| 849 |
+
re.IGNORECASE,
|
| 850 |
+
)
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
def _field_is_filled(field: str, val: str) -> bool:
|
| 854 |
+
"""Return True when val contains genuine, substantive content.
|
| 855 |
+
|
| 856 |
+
Two checks:
|
| 857 |
+
1. Not a known filler phrase ("idk", "n/a", "not specified", …)
|
| 858 |
+
2. At least _CONF_MIN_WORDS[field] real words — blocks single-word noise
|
| 859 |
+
on description fields while allowing one-word names / ask phrases.
|
| 860 |
+
"""
|
| 861 |
+
clean = str(val or "").strip()
|
| 862 |
+
if clean.lower() in _FILLER_VALUES:
|
| 863 |
+
return False
|
| 864 |
+
min_w = _CONF_MIN_WORDS.get(field, 1)
|
| 865 |
+
return len(clean.split()) >= min_w
|
| 866 |
+
|
| 867 |
+
|
| 868 |
+
def calculate_structure_confidence(
|
| 869 |
+
startup_context: dict,
|
| 870 |
+
raw_pitch_text: str = "",
|
| 871 |
+
) -> dict[str, Any]:
|
| 872 |
+
"""Deterministic confidence score from field completeness + raw-text evidence.
|
| 873 |
+
|
| 874 |
+
For the same startup_context and raw_pitch_text the result is always identical —
|
| 875 |
+
no randomness, no model opinion.
|
| 876 |
+
"""
|
| 877 |
+
ctx = startup_context or {}
|
| 878 |
+
text = str(raw_pitch_text or "").strip()
|
| 879 |
+
reasons: list[str] = []
|
| 880 |
+
|
| 881 |
+
# --- Field completeness ---
|
| 882 |
+
score = 0
|
| 883 |
+
filled: list[str] = []
|
| 884 |
+
missing: list[str] = []
|
| 885 |
+
for field, weight in _CONFIDENCE_FIELD_WEIGHTS.items():
|
| 886 |
+
if _field_is_filled(field, str(ctx.get(field, "") or "")):
|
| 887 |
+
score += weight
|
| 888 |
+
filled.append(field)
|
| 889 |
+
else:
|
| 890 |
+
missing.append(field)
|
| 891 |
+
|
| 892 |
+
# --- Signal bonus from raw pitch text (pure regex — deterministic) ---
|
| 893 |
+
bonus = 0
|
| 894 |
+
number_hits = re.findall(r"\b\d[\d,]*\b", text)
|
| 895 |
+
if len(number_hits) >= 3:
|
| 896 |
+
bonus += 10
|
| 897 |
+
elif number_hits:
|
| 898 |
+
bonus += 5
|
| 899 |
+
|
| 900 |
+
if _CONFIDENCE_USER_SEG_RE.search(text):
|
| 901 |
+
bonus += 5
|
| 902 |
+
|
| 903 |
+
if _CONFIDENCE_CONCRETE_ASK_RE.search(text):
|
| 904 |
+
bonus += 5
|
| 905 |
+
|
| 906 |
+
bonus = min(bonus, 20)
|
| 907 |
+
score = min(score + bonus, 100)
|
| 908 |
+
|
| 909 |
+
# --- Apply caps for critical missing fields ---
|
| 910 |
+
for field, cap in _CONFIDENCE_CAPS:
|
| 911 |
+
if field in missing:
|
| 912 |
+
score = min(score, cap)
|
| 913 |
+
|
| 914 |
+
score = max(0, min(100, score))
|
| 915 |
+
|
| 916 |
+
# --- Label ---
|
| 917 |
+
if score >= 75:
|
| 918 |
+
label = "high"
|
| 919 |
+
elif score >= 45:
|
| 920 |
+
label = "medium"
|
| 921 |
+
else:
|
| 922 |
+
label = "low"
|
| 923 |
+
|
| 924 |
+
# --- Human-readable reasons ---
|
| 925 |
+
strong = [f for f in ("problem", "solution", "target_users", "traction") if f in filled]
|
| 926 |
+
if strong:
|
| 927 |
+
reasons.append(f"Strong signals: {', '.join(strong)}")
|
| 928 |
+
if bonus >= 10:
|
| 929 |
+
reasons.append("Concrete numbers detected")
|
| 930 |
+
elif bonus >= 5:
|
| 931 |
+
reasons.append("Some evidence detected")
|
| 932 |
+
if missing:
|
| 933 |
+
reasons.append(f"Not in pitch: {', '.join(missing)}")
|
| 934 |
+
|
| 935 |
+
return {
|
| 936 |
+
"confidence": label,
|
| 937 |
+
"confidence_score": score,
|
| 938 |
+
"confidence_reasons": reasons,
|
| 939 |
+
"missing_fields": missing,
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
|
| 943 |
def _structure_pitch_local_fallback(pitch_text: str) -> dict[str, Any]:
|
| 944 |
"""Heuristic extraction when Nemotron is unavailable."""
|
| 945 |
text = pitch_text.strip()
|
|
|
|
| 1019 |
ctx["ask"] = sentence
|
| 1020 |
break
|
| 1021 |
|
|
|
|
| 1022 |
summary = " ".join(sentences[:2])[:220] if sentences else text[:220]
|
| 1023 |
+
conf = calculate_structure_confidence(ctx, pitch_text)
|
| 1024 |
|
| 1025 |
return {
|
| 1026 |
"ok": True,
|
| 1027 |
"startup_context": ctx,
|
| 1028 |
+
"missing_fields": conf["missing_fields"],
|
| 1029 |
+
"confidence": conf["confidence"],
|
| 1030 |
+
"confidence_score": conf["confidence_score"],
|
| 1031 |
+
"confidence_reasons": conf["confidence_reasons"],
|
| 1032 |
"brief_summary": summary,
|
| 1033 |
"source": "local_fallback",
|
| 1034 |
}
|
| 1035 |
|
| 1036 |
|
| 1037 |
def _parse_structure_pitch_response(raw: str) -> dict[str, Any] | None:
|
| 1038 |
+
"""Parse Nemotron extraction output. Confidence is NOT taken from the model —
|
| 1039 |
+
it is calculated deterministically by the caller via calculate_structure_confidence."""
|
| 1040 |
parsed, _ = parse_model_json(raw)
|
| 1041 |
if not isinstance(parsed, dict):
|
| 1042 |
return None
|
| 1043 |
|
| 1044 |
ctx = _normalize_startup_context(parsed.get("startup_context"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1045 |
|
| 1046 |
summary = str(parsed.get("brief_summary", "")).strip()
|
| 1047 |
if not summary:
|
|
|
|
| 1049 |
|
| 1050 |
return {
|
| 1051 |
"startup_context": ctx,
|
|
|
|
|
|
|
| 1052 |
"brief_summary": summary,
|
| 1053 |
}
|
| 1054 |
|
|
|
|
| 1080 |
structured = _parse_structure_pitch_response(repair["content"])
|
| 1081 |
|
| 1082 |
if structured is not None:
|
| 1083 |
+
conf = calculate_structure_confidence(
|
| 1084 |
+
structured["startup_context"], pitch_text
|
| 1085 |
+
)
|
| 1086 |
return {
|
| 1087 |
"ok": True,
|
| 1088 |
"startup_context": structured["startup_context"],
|
| 1089 |
+
"missing_fields": conf["missing_fields"],
|
| 1090 |
+
"confidence": conf["confidence"],
|
| 1091 |
+
"confidence_score": conf["confidence_score"],
|
| 1092 |
+
"confidence_reasons": conf["confidence_reasons"],
|
| 1093 |
"brief_summary": structured["brief_summary"],
|
| 1094 |
"source": "nemotron",
|
| 1095 |
}
|
core/nvidia_client.py
CHANGED
|
@@ -50,66 +50,78 @@ class OmniAudioError(RuntimeError):
|
|
| 50 |
# rewrite — rewrite utility (thinking on, lighter budget)
|
| 51 |
# legacy_full_scorecard — diagnostic / legacy path only; not main path (thinking off)
|
| 52 |
_TASK_DEFAULTS: dict[str, dict[str, Any]] = {
|
|
|
|
|
|
|
| 53 |
"opponent": {
|
| 54 |
"enable_thinking": True,
|
| 55 |
-
"reasoning_budget":
|
| 56 |
-
"max_tokens":
|
| 57 |
"temperature": 0.65,
|
| 58 |
"top_p": 0.95,
|
| 59 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
"scorecard_scoring": {
|
| 61 |
"enable_thinking": False,
|
| 62 |
"reasoning_budget": 0,
|
| 63 |
-
"max_tokens":
|
| 64 |
"temperature": 0.1,
|
| 65 |
"top_p": 0.95,
|
| 66 |
},
|
| 67 |
"scorecard_scoring_repair": {
|
| 68 |
"enable_thinking": False,
|
| 69 |
"reasoning_budget": 0,
|
| 70 |
-
"max_tokens":
|
| 71 |
"temperature": 0.0,
|
| 72 |
"top_p": 0.95,
|
| 73 |
},
|
| 74 |
"scorecard_full": {
|
| 75 |
"enable_thinking": False,
|
| 76 |
"reasoning_budget": 0,
|
| 77 |
-
"max_tokens":
|
| 78 |
"temperature": 0.1,
|
| 79 |
"top_p": 0.95,
|
| 80 |
},
|
| 81 |
"scorecard_full_repair": {
|
| 82 |
"enable_thinking": False,
|
| 83 |
"reasoning_budget": 0,
|
| 84 |
-
"max_tokens":
|
| 85 |
"temperature": 0.0,
|
| 86 |
"top_p": 0.95,
|
| 87 |
},
|
| 88 |
"scorecard_coaching": {
|
| 89 |
"enable_thinking": False,
|
| 90 |
"reasoning_budget": 0,
|
| 91 |
-
"max_tokens":
|
| 92 |
"temperature": 0.2,
|
| 93 |
"top_p": 0.95,
|
| 94 |
},
|
| 95 |
"scorecard_coaching_repair": {
|
| 96 |
"enable_thinking": False,
|
| 97 |
"reasoning_budget": 0,
|
| 98 |
-
"max_tokens":
|
| 99 |
"temperature": 0.0,
|
| 100 |
"top_p": 0.95,
|
| 101 |
},
|
| 102 |
-
"rewrite": {
|
| 103 |
-
"enable_thinking": True,
|
| 104 |
-
"reasoning_budget": 256,
|
| 105 |
-
"max_tokens": 900,
|
| 106 |
-
"temperature": 0.45,
|
| 107 |
-
"top_p": 0.95,
|
| 108 |
-
},
|
| 109 |
"legacy_full_scorecard": {
|
| 110 |
"enable_thinking": False,
|
| 111 |
"reasoning_budget": 0,
|
| 112 |
-
"max_tokens":
|
| 113 |
"temperature": 0.1,
|
| 114 |
"top_p": 0.95,
|
| 115 |
},
|
|
@@ -123,71 +135,64 @@ _TASK_DEFAULTS: dict[str, dict[str, Any]] = {
|
|
| 123 |
"voice_extraction_repair": {
|
| 124 |
"enable_thinking": False,
|
| 125 |
"reasoning_budget": 0,
|
| 126 |
-
"max_tokens":
|
| 127 |
"temperature": 0.0,
|
| 128 |
"top_p": 0.95,
|
| 129 |
},
|
| 130 |
"voice_turn": {
|
| 131 |
"enable_thinking": False,
|
| 132 |
"reasoning_budget": 0,
|
| 133 |
-
"max_tokens":
|
| 134 |
"temperature": 0.0,
|
| 135 |
"top_p": 0.95,
|
| 136 |
},
|
| 137 |
"voice_turn_repair": {
|
| 138 |
"enable_thinking": False,
|
| 139 |
"reasoning_budget": 0,
|
| 140 |
-
"max_tokens":
|
| 141 |
"temperature": 0.0,
|
| 142 |
"top_p": 0.95,
|
| 143 |
},
|
| 144 |
"retry_comparison": {
|
| 145 |
"enable_thinking": False,
|
| 146 |
"reasoning_budget": 0,
|
| 147 |
-
"max_tokens":
|
| 148 |
"temperature": 0.15,
|
| 149 |
"top_p": 0.95,
|
| 150 |
},
|
| 151 |
"retry_comparison_repair": {
|
| 152 |
"enable_thinking": False,
|
| 153 |
"reasoning_budget": 0,
|
| 154 |
-
"max_tokens":
|
| 155 |
"temperature": 0.0,
|
| 156 |
"top_p": 0.95,
|
| 157 |
},
|
| 158 |
"deal_verdict": {
|
| 159 |
"enable_thinking": False,
|
| 160 |
"reasoning_budget": 0,
|
| 161 |
-
"max_tokens":
|
| 162 |
"temperature": 0.2,
|
| 163 |
"top_p": 0.95,
|
| 164 |
},
|
| 165 |
"deal_verdict_repair": {
|
| 166 |
"enable_thinking": False,
|
| 167 |
"reasoning_budget": 0,
|
| 168 |
-
"max_tokens":
|
| 169 |
"temperature": 0.0,
|
| 170 |
"top_p": 0.95,
|
| 171 |
},
|
| 172 |
-
"deal_round": {
|
| 173 |
-
"enable_thinking": True,
|
| 174 |
-
"reasoning_budget": 512,
|
| 175 |
-
"max_tokens": 900,
|
| 176 |
-
"temperature": 0.65,
|
| 177 |
-
"top_p": 0.95,
|
| 178 |
-
},
|
| 179 |
# Deal phase: semantic dimension scoring (JSON, split call 1 — scores only)
|
| 180 |
"deal_scorecard_scoring": {
|
| 181 |
"enable_thinking": False,
|
| 182 |
"reasoning_budget": 0,
|
| 183 |
-
"max_tokens":
|
| 184 |
"temperature": 0.1,
|
| 185 |
"top_p": 0.95,
|
| 186 |
},
|
| 187 |
"deal_scorecard_scoring_repair": {
|
| 188 |
"enable_thinking": False,
|
| 189 |
"reasoning_budget": 0,
|
| 190 |
-
"max_tokens":
|
| 191 |
"temperature": 0.0,
|
| 192 |
"top_p": 0.95,
|
| 193 |
},
|
|
@@ -195,28 +200,28 @@ _TASK_DEFAULTS: dict[str, dict[str, Any]] = {
|
|
| 195 |
"deal_scorecard_coaching": {
|
| 196 |
"enable_thinking": False,
|
| 197 |
"reasoning_budget": 0,
|
| 198 |
-
"max_tokens":
|
| 199 |
"temperature": 0.2,
|
| 200 |
"top_p": 0.95,
|
| 201 |
},
|
| 202 |
"deal_scorecard_repair": {
|
| 203 |
"enable_thinking": False,
|
| 204 |
"reasoning_budget": 0,
|
| 205 |
-
"max_tokens":
|
| 206 |
"temperature": 0.0,
|
| 207 |
"top_p": 0.95,
|
| 208 |
},
|
| 209 |
"structure_pitch": {
|
| 210 |
"enable_thinking": False,
|
| 211 |
"reasoning_budget": 0,
|
| 212 |
-
"max_tokens":
|
| 213 |
-
"temperature": 0.
|
| 214 |
"top_p": 0.95,
|
| 215 |
},
|
| 216 |
"structure_pitch_repair": {
|
| 217 |
"enable_thinking": False,
|
| 218 |
"reasoning_budget": 0,
|
| 219 |
-
"max_tokens":
|
| 220 |
"temperature": 0.0,
|
| 221 |
"top_p": 0.95,
|
| 222 |
},
|
|
@@ -265,6 +270,66 @@ def _extract_json_from_reasoning(reasoning: str) -> str | None:
|
|
| 265 |
return None
|
| 266 |
|
| 267 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
def _get_config() -> tuple[str, str, str]:
|
| 269 |
"""Return (api_key, base_url, model). Raises RuntimeError if key is absent."""
|
| 270 |
api_key = os.getenv("NVIDIA_API_KEY", "").strip()
|
|
@@ -357,6 +422,7 @@ def _complete_chat(
|
|
| 357 |
|
| 358 |
if not content:
|
| 359 |
if mode in _JSON_MODES and reasoning:
|
|
|
|
| 360 |
extracted = _extract_json_from_reasoning(reasoning)
|
| 361 |
if extracted:
|
| 362 |
logger.info(
|
|
@@ -366,21 +432,31 @@ def _complete_chat(
|
|
| 366 |
content = extracted
|
| 367 |
else:
|
| 368 |
logger.warning(
|
| 369 |
-
"Nemotron content empty;
|
| 370 |
mode,
|
| 371 |
)
|
| 372 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
logger.warning(
|
| 374 |
-
"Nemotron content empty
|
|
|
|
| 375 |
mode,
|
| 376 |
)
|
| 377 |
-
content = reasoning
|
| 378 |
|
| 379 |
if not content:
|
| 380 |
raise RuntimeError(
|
| 381 |
-
"
|
| 382 |
-
"
|
| 383 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
return content
|
| 385 |
|
| 386 |
|
|
|
|
| 50 |
# rewrite — rewrite utility (thinking on, lighter budget)
|
| 51 |
# legacy_full_scorecard — diagnostic / legacy path only; not main path (thinking off)
|
| 52 |
_TASK_DEFAULTS: dict[str, dict[str, Any]] = {
|
| 53 |
+
# Thinking modes: reasoning_budget=320 — enough room to think cleanly without
|
| 54 |
+
# spilling monologue into content, while leaving 1180 tokens for real output.
|
| 55 |
"opponent": {
|
| 56 |
"enable_thinking": True,
|
| 57 |
+
"reasoning_budget": 320,
|
| 58 |
+
"max_tokens": 1500,
|
| 59 |
"temperature": 0.65,
|
| 60 |
"top_p": 0.95,
|
| 61 |
},
|
| 62 |
+
"deal_round": {
|
| 63 |
+
"enable_thinking": True,
|
| 64 |
+
"reasoning_budget": 320,
|
| 65 |
+
"max_tokens": 1500,
|
| 66 |
+
"temperature": 0.65,
|
| 67 |
+
"top_p": 0.95,
|
| 68 |
+
},
|
| 69 |
+
"rewrite": {
|
| 70 |
+
"enable_thinking": True,
|
| 71 |
+
"reasoning_budget": 320,
|
| 72 |
+
"max_tokens": 1200,
|
| 73 |
+
"temperature": 0.45,
|
| 74 |
+
"top_p": 0.95,
|
| 75 |
+
},
|
| 76 |
+
# Scoring modes (thinking off — pure JSON output).
|
| 77 |
+
# max_tokens raised well above realistic output size so late-round battles
|
| 78 |
+
# with long conversation history never truncate mid-JSON.
|
| 79 |
"scorecard_scoring": {
|
| 80 |
"enable_thinking": False,
|
| 81 |
"reasoning_budget": 0,
|
| 82 |
+
"max_tokens": 2500,
|
| 83 |
"temperature": 0.1,
|
| 84 |
"top_p": 0.95,
|
| 85 |
},
|
| 86 |
"scorecard_scoring_repair": {
|
| 87 |
"enable_thinking": False,
|
| 88 |
"reasoning_budget": 0,
|
| 89 |
+
"max_tokens": 2000,
|
| 90 |
"temperature": 0.0,
|
| 91 |
"top_p": 0.95,
|
| 92 |
},
|
| 93 |
"scorecard_full": {
|
| 94 |
"enable_thinking": False,
|
| 95 |
"reasoning_budget": 0,
|
| 96 |
+
"max_tokens": 3500,
|
| 97 |
"temperature": 0.1,
|
| 98 |
"top_p": 0.95,
|
| 99 |
},
|
| 100 |
"scorecard_full_repair": {
|
| 101 |
"enable_thinking": False,
|
| 102 |
"reasoning_budget": 0,
|
| 103 |
+
"max_tokens": 3000,
|
| 104 |
"temperature": 0.0,
|
| 105 |
"top_p": 0.95,
|
| 106 |
},
|
| 107 |
"scorecard_coaching": {
|
| 108 |
"enable_thinking": False,
|
| 109 |
"reasoning_budget": 0,
|
| 110 |
+
"max_tokens": 3200,
|
| 111 |
"temperature": 0.2,
|
| 112 |
"top_p": 0.95,
|
| 113 |
},
|
| 114 |
"scorecard_coaching_repair": {
|
| 115 |
"enable_thinking": False,
|
| 116 |
"reasoning_budget": 0,
|
| 117 |
+
"max_tokens": 2400,
|
| 118 |
"temperature": 0.0,
|
| 119 |
"top_p": 0.95,
|
| 120 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
"legacy_full_scorecard": {
|
| 122 |
"enable_thinking": False,
|
| 123 |
"reasoning_budget": 0,
|
| 124 |
+
"max_tokens": 4000,
|
| 125 |
"temperature": 0.1,
|
| 126 |
"top_p": 0.95,
|
| 127 |
},
|
|
|
|
| 135 |
"voice_extraction_repair": {
|
| 136 |
"enable_thinking": False,
|
| 137 |
"reasoning_budget": 0,
|
| 138 |
+
"max_tokens": 1400,
|
| 139 |
"temperature": 0.0,
|
| 140 |
"top_p": 0.95,
|
| 141 |
},
|
| 142 |
"voice_turn": {
|
| 143 |
"enable_thinking": False,
|
| 144 |
"reasoning_budget": 0,
|
| 145 |
+
"max_tokens": 800,
|
| 146 |
"temperature": 0.0,
|
| 147 |
"top_p": 0.95,
|
| 148 |
},
|
| 149 |
"voice_turn_repair": {
|
| 150 |
"enable_thinking": False,
|
| 151 |
"reasoning_budget": 0,
|
| 152 |
+
"max_tokens": 700,
|
| 153 |
"temperature": 0.0,
|
| 154 |
"top_p": 0.95,
|
| 155 |
},
|
| 156 |
"retry_comparison": {
|
| 157 |
"enable_thinking": False,
|
| 158 |
"reasoning_budget": 0,
|
| 159 |
+
"max_tokens": 1500,
|
| 160 |
"temperature": 0.15,
|
| 161 |
"top_p": 0.95,
|
| 162 |
},
|
| 163 |
"retry_comparison_repair": {
|
| 164 |
"enable_thinking": False,
|
| 165 |
"reasoning_budget": 0,
|
| 166 |
+
"max_tokens": 1200,
|
| 167 |
"temperature": 0.0,
|
| 168 |
"top_p": 0.95,
|
| 169 |
},
|
| 170 |
"deal_verdict": {
|
| 171 |
"enable_thinking": False,
|
| 172 |
"reasoning_budget": 0,
|
| 173 |
+
"max_tokens": 1500,
|
| 174 |
"temperature": 0.2,
|
| 175 |
"top_p": 0.95,
|
| 176 |
},
|
| 177 |
"deal_verdict_repair": {
|
| 178 |
"enable_thinking": False,
|
| 179 |
"reasoning_budget": 0,
|
| 180 |
+
"max_tokens": 1200,
|
| 181 |
"temperature": 0.0,
|
| 182 |
"top_p": 0.95,
|
| 183 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
# Deal phase: semantic dimension scoring (JSON, split call 1 — scores only)
|
| 185 |
"deal_scorecard_scoring": {
|
| 186 |
"enable_thinking": False,
|
| 187 |
"reasoning_budget": 0,
|
| 188 |
+
"max_tokens": 2500,
|
| 189 |
"temperature": 0.1,
|
| 190 |
"top_p": 0.95,
|
| 191 |
},
|
| 192 |
"deal_scorecard_scoring_repair": {
|
| 193 |
"enable_thinking": False,
|
| 194 |
"reasoning_budget": 0,
|
| 195 |
+
"max_tokens": 2000,
|
| 196 |
"temperature": 0.0,
|
| 197 |
"top_p": 0.95,
|
| 198 |
},
|
|
|
|
| 200 |
"deal_scorecard_coaching": {
|
| 201 |
"enable_thinking": False,
|
| 202 |
"reasoning_budget": 0,
|
| 203 |
+
"max_tokens": 3000,
|
| 204 |
"temperature": 0.2,
|
| 205 |
"top_p": 0.95,
|
| 206 |
},
|
| 207 |
"deal_scorecard_repair": {
|
| 208 |
"enable_thinking": False,
|
| 209 |
"reasoning_budget": 0,
|
| 210 |
+
"max_tokens": 2000,
|
| 211 |
"temperature": 0.0,
|
| 212 |
"top_p": 0.95,
|
| 213 |
},
|
| 214 |
"structure_pitch": {
|
| 215 |
"enable_thinking": False,
|
| 216 |
"reasoning_budget": 0,
|
| 217 |
+
"max_tokens": 1000,
|
| 218 |
+
"temperature": 0.0,
|
| 219 |
"top_p": 0.95,
|
| 220 |
},
|
| 221 |
"structure_pitch_repair": {
|
| 222 |
"enable_thinking": False,
|
| 223 |
"reasoning_budget": 0,
|
| 224 |
+
"max_tokens": 900,
|
| 225 |
"temperature": 0.0,
|
| 226 |
"top_p": 0.95,
|
| 227 |
},
|
|
|
|
| 270 |
return None
|
| 271 |
|
| 272 |
|
| 273 |
+
# Phrases that only appear in internal reasoning monologue, never in a real response.
|
| 274 |
+
_REASONING_LEAK_SIGNALS = (
|
| 275 |
+
"need to keep under",
|
| 276 |
+
"check constraints",
|
| 277 |
+
"that's one sentence",
|
| 278 |
+
"actually it's",
|
| 279 |
+
"make sure we reference",
|
| 280 |
+
"under 3 sentences",
|
| 281 |
+
"the question itself",
|
| 282 |
+
"so okay.",
|
| 283 |
+
"let me ",
|
| 284 |
+
"i need to ",
|
| 285 |
+
"i should ",
|
| 286 |
+
"i'll ask",
|
| 287 |
+
"i will ask",
|
| 288 |
+
"one question,",
|
| 289 |
+
"plain language.",
|
| 290 |
+
"no advice,",
|
| 291 |
+
"no compliments,",
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def _strip_reasoning_leak(content: str) -> str:
|
| 296 |
+
"""Remove internal monologue that leaked into content for thinking-mode calls.
|
| 297 |
+
|
| 298 |
+
When reasoning_budget is insufficient the model continues "thinking" inside
|
| 299 |
+
the content field before reaching the actual response. This function detects
|
| 300 |
+
that pattern and returns only the final intended output (the last question).
|
| 301 |
+
"""
|
| 302 |
+
lower = content.lower()
|
| 303 |
+
if not any(sig in lower for sig in _REASONING_LEAK_SIGNALS):
|
| 304 |
+
return content
|
| 305 |
+
|
| 306 |
+
# Reasoning leaked — extract the last real sentence(s).
|
| 307 |
+
# Split on sentence boundaries and walk backwards to find the last
|
| 308 |
+
# question, which is the intended output.
|
| 309 |
+
import re
|
| 310 |
+
sentences = [s.strip() for s in re.split(r"(?<=[.!?])\s+", content) if s.strip()]
|
| 311 |
+
if not sentences:
|
| 312 |
+
return content
|
| 313 |
+
|
| 314 |
+
# Find the last sentence ending with "?"
|
| 315 |
+
for i in range(len(sentences) - 1, -1, -1):
|
| 316 |
+
if sentences[i].endswith("?"):
|
| 317 |
+
# Include the sentence before it (context) if it's clean prose
|
| 318 |
+
if i > 0 and not any(sig in sentences[i - 1].lower() for sig in _REASONING_LEAK_SIGNALS):
|
| 319 |
+
return f"{sentences[i - 1]} {sentences[i]}"
|
| 320 |
+
return sentences[i]
|
| 321 |
+
|
| 322 |
+
# No question mark found — return the last non-reasoning sentence
|
| 323 |
+
for sent in reversed(sentences):
|
| 324 |
+
if not any(sig in sent.lower() for sig in _REASONING_LEAK_SIGNALS):
|
| 325 |
+
return sent
|
| 326 |
+
|
| 327 |
+
# Couldn't isolate anything clean — raise so the caller serves its fallback
|
| 328 |
+
# message instead of showing garbage to the user.
|
| 329 |
+
logger.warning("_strip_reasoning_leak: could not isolate clean output, raising for caller fallback")
|
| 330 |
+
raise RuntimeError("Reasoning leaked into content and could not be cleaned (mode content fully contaminated)")
|
| 331 |
+
|
| 332 |
+
|
| 333 |
def _get_config() -> tuple[str, str, str]:
|
| 334 |
"""Return (api_key, base_url, model). Raises RuntimeError if key is absent."""
|
| 335 |
api_key = os.getenv("NVIDIA_API_KEY", "").strip()
|
|
|
|
| 422 |
|
| 423 |
if not content:
|
| 424 |
if mode in _JSON_MODES and reasoning:
|
| 425 |
+
# JSON mode: try to salvage a JSON block from the reasoning field.
|
| 426 |
extracted = _extract_json_from_reasoning(reasoning)
|
| 427 |
if extracted:
|
| 428 |
logger.info(
|
|
|
|
| 432 |
content = extracted
|
| 433 |
else:
|
| 434 |
logger.warning(
|
| 435 |
+
"Nemotron content empty; no JSON in reasoning_content (mode=%s)",
|
| 436 |
mode,
|
| 437 |
)
|
| 438 |
+
else:
|
| 439 |
+
# Non-JSON mode (opponent, deal_round, rewrite, …):
|
| 440 |
+
# reasoning_content is the model's compressed internal thinking —
|
| 441 |
+
# it is NEVER safe to display. Let it raise so the caller serves
|
| 442 |
+
# its own clean fallback message.
|
| 443 |
logger.warning(
|
| 444 |
+
"Nemotron content empty for non-JSON mode=%s; "
|
| 445 |
+
"reasoning_content not usable as output — raising for caller fallback",
|
| 446 |
mode,
|
| 447 |
)
|
|
|
|
| 448 |
|
| 449 |
if not content:
|
| 450 |
raise RuntimeError(
|
| 451 |
+
f"Nemotron returned empty content (mode={mode}). "
|
| 452 |
+
"Caller should serve its fallback."
|
| 453 |
)
|
| 454 |
+
|
| 455 |
+
# For thinking-mode calls (opponent, deal_round, rewrite) strip any internal
|
| 456 |
+
# monologue that leaked into the content field when reasoning_budget runs short.
|
| 457 |
+
if enable_thinking and mode not in _JSON_MODES:
|
| 458 |
+
content = _strip_reasoning_leak(content)
|
| 459 |
+
|
| 460 |
return content
|
| 461 |
|
| 462 |
|
core/retry_handler.py
CHANGED
|
@@ -128,6 +128,19 @@ def start_retry_drill(session: dict) -> dict[str, Any]:
|
|
| 128 |
)
|
| 129 |
difficulty_label = session.get("difficulty_label") or get_label(difficulty_profile)
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
retry_id = str(uuid.uuid4())
|
| 132 |
drill = {
|
| 133 |
"retry_id": retry_id,
|
|
@@ -143,7 +156,10 @@ def start_retry_drill(session: dict) -> dict[str, Any]:
|
|
| 143 |
"input_mode": "",
|
| 144 |
"retry_answer": "",
|
| 145 |
"result": {},
|
| 146 |
-
"dimension_score_before":
|
|
|
|
|
|
|
|
|
|
| 147 |
}
|
| 148 |
session.setdefault("retry_drills", {})[retry_id] = drill
|
| 149 |
|
|
@@ -380,6 +396,90 @@ def call_nemotron_retry_comparison(
|
|
| 380 |
return None
|
| 381 |
|
| 382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
def apply_retry_to_scorecard(
|
| 384 |
session: dict,
|
| 385 |
drill: dict,
|
|
@@ -486,18 +586,26 @@ def evaluate_retry_answer(
|
|
| 486 |
if voice_turn_id:
|
| 487 |
drill["voice_turn_id"] = voice_turn_id
|
| 488 |
|
| 489 |
-
|
| 490 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
comparison_result = build_local_retry_fallback(
|
| 492 |
drill.get("original_answer", ""),
|
| 493 |
answer,
|
| 494 |
drill.get("dimension", "objection_handling"),
|
| 495 |
drill.get("dimension_score_before", 30),
|
| 496 |
)
|
|
|
|
|
|
|
|
|
|
| 497 |
|
| 498 |
drill["result"] = comparison_result
|
| 499 |
comp = comparison_result.get("comparison", {})
|
| 500 |
-
|
| 501 |
|
| 502 |
response: dict[str, Any] = {
|
| 503 |
"session_id": session_id,
|
|
@@ -509,14 +617,11 @@ def evaluate_retry_answer(
|
|
| 509 |
"original_answer": drill.get("original_answer", ""),
|
| 510 |
"retry_answer": answer,
|
| 511 |
"comparison": comp,
|
|
|
|
| 512 |
"next_practice_prompt": comparison_result.get("next_practice_prompt", ""),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
}
|
| 514 |
-
if updated_scorecard:
|
| 515 |
-
response["updated_scorecard"] = updated_scorecard
|
| 516 |
-
try:
|
| 517 |
-
verdict = build_judge_verdict(session, updated_scorecard, local_only=True)
|
| 518 |
-
session["judge_verdict"] = verdict
|
| 519 |
-
response["judge_verdict"] = verdict
|
| 520 |
-
except Exception as exc:
|
| 521 |
-
logger.warning("retry_handler: could not refresh judge verdict — %s", exc)
|
| 522 |
return response
|
|
|
|
| 128 |
)
|
| 129 |
difficulty_label = session.get("difficulty_label") or get_label(difficulty_profile)
|
| 130 |
|
| 131 |
+
# Snapshot the scorecard baseline at drill-creation time so that any later
|
| 132 |
+
# scorecard mutation (or session reload) cannot shift the projection baseline.
|
| 133 |
+
sc_scores = scorecard.get("scores") or {}
|
| 134 |
+
original_overall_score = int(scorecard.get("overall", 0) or 0)
|
| 135 |
+
original_dimension_scores = {
|
| 136 |
+
k: int(v.get("score", 0) or 0)
|
| 137 |
+
for k, v in sc_scores.items()
|
| 138 |
+
if isinstance(v, dict)
|
| 139 |
+
}
|
| 140 |
+
dim_score_before = original_dimension_scores.get(
|
| 141 |
+
dimension, _dimension_score(scorecard, dimension)
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
retry_id = str(uuid.uuid4())
|
| 145 |
drill = {
|
| 146 |
"retry_id": retry_id,
|
|
|
|
| 156 |
"input_mode": "",
|
| 157 |
"retry_answer": "",
|
| 158 |
"result": {},
|
| 159 |
+
"dimension_score_before": dim_score_before,
|
| 160 |
+
# Authoritative baseline — never re-read from session after this point.
|
| 161 |
+
"original_overall_score": original_overall_score,
|
| 162 |
+
"original_dimension_scores": original_dimension_scores,
|
| 163 |
}
|
| 164 |
session.setdefault("retry_drills", {})[retry_id] = drill
|
| 165 |
|
|
|
|
| 396 |
return None
|
| 397 |
|
| 398 |
|
| 399 |
+
def compute_retry_projection(
|
| 400 |
+
session: dict,
|
| 401 |
+
drill: dict,
|
| 402 |
+
comparison: dict,
|
| 403 |
+
) -> dict[str, Any]:
|
| 404 |
+
"""Non-destructive training projection — original scorecard stays unchanged.
|
| 405 |
+
|
| 406 |
+
Uses the baseline snapshotted onto the drill at start_retry_drill time so that
|
| 407 |
+
any scorecard mutation between drill-start and drill-submit cannot corrupt the
|
| 408 |
+
displayed baseline (the bug was: practice-nudge stripped by a later resync left
|
| 409 |
+
scorecard["overall"]=28 while the UI showed 31 from the original API response).
|
| 410 |
+
"""
|
| 411 |
+
scorecard = session.get("latest_scorecard") or {}
|
| 412 |
+
dim = str(drill.get("dimension", "")).strip()
|
| 413 |
+
|
| 414 |
+
# --- Authoritative baseline: prefer drill snapshot, fall back to live session ---
|
| 415 |
+
original_overall = int(
|
| 416 |
+
drill.get("original_overall_score")
|
| 417 |
+
if drill.get("original_overall_score") is not None
|
| 418 |
+
else (scorecard.get("overall", 0) or 0)
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
# Use snapshotted dimension scores; fall back to live scorecard scores.
|
| 422 |
+
original_dim_scores: dict[str, int] = drill.get("original_dimension_scores") or {}
|
| 423 |
+
if not original_dim_scores:
|
| 424 |
+
scores = scorecard.get("scores") or {}
|
| 425 |
+
original_dim_scores = {
|
| 426 |
+
k: int(v.get("score", 0) or 0)
|
| 427 |
+
for k, v in scores.items() if isinstance(v, dict)
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
# --- Old dimension score for this specific target ---
|
| 431 |
+
old_dim_score = int(
|
| 432 |
+
original_dim_scores.get(
|
| 433 |
+
dim,
|
| 434 |
+
drill.get("dimension_score_before", 0) or 0,
|
| 435 |
+
)
|
| 436 |
+
)
|
| 437 |
+
|
| 438 |
+
# --- New dimension score from Nemotron/fallback comparison ---
|
| 439 |
+
try:
|
| 440 |
+
raw_new = int(comparison.get("estimated_dimension_after", old_dim_score))
|
| 441 |
+
except (TypeError, ValueError):
|
| 442 |
+
raw_new = old_dim_score
|
| 443 |
+
|
| 444 |
+
# Never allow the new score to appear lower than the old score in the projection.
|
| 445 |
+
new_dim_score = max(old_dim_score, raw_new)
|
| 446 |
+
dimension_delta = new_dim_score - old_dim_score
|
| 447 |
+
|
| 448 |
+
if dimension_delta > 0:
|
| 449 |
+
# Replace only the target dimension; all others stay at their original values.
|
| 450 |
+
projected_scores = dict(original_dim_scores)
|
| 451 |
+
projected_scores[dim] = new_dim_score
|
| 452 |
+
|
| 453 |
+
n_dims = len(projected_scores) or 1
|
| 454 |
+
dim_avg_projection = round(sum(projected_scores.values()) / n_dims)
|
| 455 |
+
|
| 456 |
+
# Proportional lift ensures even a single-dim improvement is visible when
|
| 457 |
+
# the raw average is still dragged down by other weak dims.
|
| 458 |
+
proportional_lift = max(1, round(dimension_delta / n_dims))
|
| 459 |
+
|
| 460 |
+
projected_overall = max(
|
| 461 |
+
dim_avg_projection,
|
| 462 |
+
original_overall,
|
| 463 |
+
min(100, original_overall + proportional_lift),
|
| 464 |
+
)
|
| 465 |
+
projected_overall_delta = max(0, projected_overall - original_overall)
|
| 466 |
+
else:
|
| 467 |
+
projected_overall = original_overall
|
| 468 |
+
projected_overall_delta = 0
|
| 469 |
+
|
| 470 |
+
return {
|
| 471 |
+
"target_dimension": dim,
|
| 472 |
+
"old_dimension_score": old_dim_score,
|
| 473 |
+
"new_dimension_score": new_dim_score,
|
| 474 |
+
"dimension_delta": dimension_delta,
|
| 475 |
+
"original_overall_score": original_overall,
|
| 476 |
+
"projected_overall_score": projected_overall,
|
| 477 |
+
"projected_overall_delta": projected_overall_delta,
|
| 478 |
+
"original_scorecard_unchanged": True,
|
| 479 |
+
"projection_method": "replace_target_dimension_only",
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
|
| 483 |
def apply_retry_to_scorecard(
|
| 484 |
session: dict,
|
| 485 |
drill: dict,
|
|
|
|
| 586 |
if voice_turn_id:
|
| 587 |
drill["voice_turn_id"] = voice_turn_id
|
| 588 |
|
| 589 |
+
nemotron_result = call_nemotron_retry_comparison(session, drill, answer)
|
| 590 |
+
if nemotron_result is not None:
|
| 591 |
+
comparison_result = nemotron_result
|
| 592 |
+
retry_score_source = "nemotron"
|
| 593 |
+
model_ok = True
|
| 594 |
+
fallback_reason = ""
|
| 595 |
+
else:
|
| 596 |
comparison_result = build_local_retry_fallback(
|
| 597 |
drill.get("original_answer", ""),
|
| 598 |
answer,
|
| 599 |
drill.get("dimension", "objection_handling"),
|
| 600 |
drill.get("dimension_score_before", 30),
|
| 601 |
)
|
| 602 |
+
retry_score_source = "local_fallback"
|
| 603 |
+
model_ok = False
|
| 604 |
+
fallback_reason = "Nemotron unavailable — local heuristic used"
|
| 605 |
|
| 606 |
drill["result"] = comparison_result
|
| 607 |
comp = comparison_result.get("comparison", {})
|
| 608 |
+
projection = compute_retry_projection(session, drill, comp)
|
| 609 |
|
| 610 |
response: dict[str, Any] = {
|
| 611 |
"session_id": session_id,
|
|
|
|
| 617 |
"original_answer": drill.get("original_answer", ""),
|
| 618 |
"retry_answer": answer,
|
| 619 |
"comparison": comp,
|
| 620 |
+
"projection": projection,
|
| 621 |
"next_practice_prompt": comparison_result.get("next_practice_prompt", ""),
|
| 622 |
+
"scorecard_unchanged": True,
|
| 623 |
+
"retry_score_source": retry_score_source,
|
| 624 |
+
"model_ok": model_ok,
|
| 625 |
+
"fallback_reason": fallback_reason,
|
| 626 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 627 |
return response
|
core/scoring_engine.py
CHANGED
|
@@ -1544,10 +1544,11 @@ def _call_nemotron_scoring(
|
|
| 1544 |
difficulty_profile: str,
|
| 1545 |
difficulty_label: str,
|
| 1546 |
resolved_mode: str,
|
| 1547 |
-
) -> tuple[dict[str, Any], str, str, str] | None:
|
| 1548 |
"""Call Nemotron for dimension scores only (Call 1).
|
| 1549 |
|
| 1550 |
-
Returns (scores, best_answer, weakest_answer, why_weak)
|
|
|
|
| 1551 |
"""
|
| 1552 |
messages = _build_scoring_only_prompt(
|
| 1553 |
session, signals, local_reference, difficulty_profile, difficulty_label
|
|
@@ -1558,11 +1559,12 @@ def _call_nemotron_scoring(
|
|
| 1558 |
if result.get("ok") and result.get("content"):
|
| 1559 |
raw_content = result["content"]
|
| 1560 |
else:
|
| 1561 |
-
|
| 1562 |
-
|
|
|
|
| 1563 |
except Exception as exc:
|
| 1564 |
logger.warning("scoring_engine: Nemotron scoring raised — %s", exc)
|
| 1565 |
-
return None
|
| 1566 |
|
| 1567 |
parsed, extraction_used = parse_model_json(raw_content)
|
| 1568 |
if not isinstance(parsed, dict) or not parsed:
|
|
@@ -1578,7 +1580,7 @@ def _call_nemotron_scoring(
|
|
| 1578 |
extraction_used,
|
| 1579 |
sanitize_for_log(raw_content),
|
| 1580 |
)
|
| 1581 |
-
return _normalize_scoring_result(parsed)
|
| 1582 |
|
| 1583 |
# Repair attempt
|
| 1584 |
logger.warning(
|
|
@@ -1597,12 +1599,12 @@ def _call_nemotron_scoring(
|
|
| 1597 |
repaired = _normalize_scoring_json(repaired)
|
| 1598 |
if isinstance(repaired, dict) and repaired and _validate_scoring_json(repaired):
|
| 1599 |
logger.info("scoring_engine: repaired scoring JSON OK")
|
| 1600 |
-
return _normalize_scoring_result(repaired)
|
| 1601 |
except Exception as exc:
|
| 1602 |
logger.warning("scoring_engine: scoring repair raised — %s", exc)
|
| 1603 |
|
| 1604 |
logger.warning("scoring_engine: Nemotron scoring failed — will fall back to local scores")
|
| 1605 |
-
return None
|
| 1606 |
|
| 1607 |
|
| 1608 |
# ---------------------------------------------------------------------------
|
|
@@ -1668,6 +1670,7 @@ def generate_claim_based_scorecard(
|
|
| 1668 |
|
| 1669 |
# Step 3: Nemotron scoring call (Call 1) — skip when no substantive battle answers
|
| 1670 |
nemotron_scoring_result = None
|
|
|
|
| 1671 |
skip_nemotron_scoring = engagement_info["substantive_answers"] == 0
|
| 1672 |
if skip_nemotron_scoring:
|
| 1673 |
logger.info(
|
|
@@ -1678,7 +1681,7 @@ def generate_claim_based_scorecard(
|
|
| 1678 |
has_startup,
|
| 1679 |
)
|
| 1680 |
elif resolved_mode == "premium_nvidia":
|
| 1681 |
-
nemotron_scoring_result = _call_nemotron_scoring(
|
| 1682 |
session, signals, local_reference,
|
| 1683 |
difficulty_profile, difficulty_label, resolved_mode,
|
| 1684 |
)
|
|
@@ -1776,6 +1779,7 @@ def generate_claim_based_scorecard(
|
|
| 1776 |
"coaching_source": coaching_source,
|
| 1777 |
"difficulty_profile": difficulty_profile,
|
| 1778 |
"difficulty_label": difficulty_label,
|
|
|
|
| 1779 |
}
|
| 1780 |
result = _sync_overall_to_dimensions(result)
|
| 1781 |
result["overall"] = _apply_practice_score_nudge(
|
|
@@ -1897,6 +1901,15 @@ def generate_claim_based_scorecard(
|
|
| 1897 |
else "Nemotron scoring failed; used local scoring fallback."
|
| 1898 |
)
|
| 1899 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1900 |
}
|
| 1901 |
|
| 1902 |
|
|
|
|
| 1544 |
difficulty_profile: str,
|
| 1545 |
difficulty_label: str,
|
| 1546 |
resolved_mode: str,
|
| 1547 |
+
) -> tuple[tuple[dict[str, Any], str, str, str] | None, str]:
|
| 1548 |
"""Call Nemotron for dimension scores only (Call 1).
|
| 1549 |
|
| 1550 |
+
Returns ((scores, best_answer, weakest_answer, why_weak), "") on success,
|
| 1551 |
+
or (None, failure_reason) on failure.
|
| 1552 |
"""
|
| 1553 |
messages = _build_scoring_only_prompt(
|
| 1554 |
session, signals, local_reference, difficulty_profile, difficulty_label
|
|
|
|
| 1559 |
if result.get("ok") and result.get("content"):
|
| 1560 |
raw_content = result["content"]
|
| 1561 |
else:
|
| 1562 |
+
err = str(result.get("error") or "api_call_failed")
|
| 1563 |
+
logger.warning("scoring_engine: Nemotron scoring call not ok — %s", err)
|
| 1564 |
+
return None, f"api_error:{err[:120]}"
|
| 1565 |
except Exception as exc:
|
| 1566 |
logger.warning("scoring_engine: Nemotron scoring raised — %s", exc)
|
| 1567 |
+
return None, f"exception:{str(exc)[:120]}"
|
| 1568 |
|
| 1569 |
parsed, extraction_used = parse_model_json(raw_content)
|
| 1570 |
if not isinstance(parsed, dict) or not parsed:
|
|
|
|
| 1580 |
extraction_used,
|
| 1581 |
sanitize_for_log(raw_content),
|
| 1582 |
)
|
| 1583 |
+
return _normalize_scoring_result(parsed), ""
|
| 1584 |
|
| 1585 |
# Repair attempt
|
| 1586 |
logger.warning(
|
|
|
|
| 1599 |
repaired = _normalize_scoring_json(repaired)
|
| 1600 |
if isinstance(repaired, dict) and repaired and _validate_scoring_json(repaired):
|
| 1601 |
logger.info("scoring_engine: repaired scoring JSON OK")
|
| 1602 |
+
return _normalize_scoring_result(repaired), ""
|
| 1603 |
except Exception as exc:
|
| 1604 |
logger.warning("scoring_engine: scoring repair raised — %s", exc)
|
| 1605 |
|
| 1606 |
logger.warning("scoring_engine: Nemotron scoring failed — will fall back to local scores")
|
| 1607 |
+
return None, "json_parse_failed"
|
| 1608 |
|
| 1609 |
|
| 1610 |
# ---------------------------------------------------------------------------
|
|
|
|
| 1670 |
|
| 1671 |
# Step 3: Nemotron scoring call (Call 1) — skip when no substantive battle answers
|
| 1672 |
nemotron_scoring_result = None
|
| 1673 |
+
nemotron_failure_reason = ""
|
| 1674 |
skip_nemotron_scoring = engagement_info["substantive_answers"] == 0
|
| 1675 |
if skip_nemotron_scoring:
|
| 1676 |
logger.info(
|
|
|
|
| 1681 |
has_startup,
|
| 1682 |
)
|
| 1683 |
elif resolved_mode == "premium_nvidia":
|
| 1684 |
+
nemotron_scoring_result, nemotron_failure_reason = _call_nemotron_scoring(
|
| 1685 |
session, signals, local_reference,
|
| 1686 |
difficulty_profile, difficulty_label, resolved_mode,
|
| 1687 |
)
|
|
|
|
| 1779 |
"coaching_source": coaching_source,
|
| 1780 |
"difficulty_profile": difficulty_profile,
|
| 1781 |
"difficulty_label": difficulty_label,
|
| 1782 |
+
"fallback_reason": "",
|
| 1783 |
}
|
| 1784 |
result = _sync_overall_to_dimensions(result)
|
| 1785 |
result["overall"] = _apply_practice_score_nudge(
|
|
|
|
| 1901 |
else "Nemotron scoring failed; used local scoring fallback."
|
| 1902 |
)
|
| 1903 |
),
|
| 1904 |
+
"fallback_reason": (
|
| 1905 |
+
"no_battle_answers"
|
| 1906 |
+
if skip_nemotron_scoring and not has_startup and signals.get("signal_count", 0) == 0
|
| 1907 |
+
else (
|
| 1908 |
+
"startup_context_only"
|
| 1909 |
+
if skip_nemotron_scoring
|
| 1910 |
+
else (nemotron_failure_reason or "nemotron_scoring_failed")
|
| 1911 |
+
)
|
| 1912 |
+
),
|
| 1913 |
}
|
| 1914 |
|
| 1915 |
|
frontend/index.html
CHANGED
|
@@ -19,6 +19,7 @@
|
|
| 19 |
<!-- Landing — Founder Pressure Arena (Pass 1) -->
|
| 20 |
<section id="screen-landing" class="screen active">
|
| 21 |
<div class="arena-landing">
|
|
|
|
| 22 |
<div class="arena-scene" aria-hidden="true">
|
| 23 |
<div class="arena-scene-base"></div>
|
| 24 |
<div class="hero-center-haze"></div>
|
|
@@ -234,26 +235,34 @@
|
|
| 234 |
</div>
|
| 235 |
</section>
|
| 236 |
|
| 237 |
-
<!-- Start method -->
|
| 238 |
-
<section id="screen-start-method" class="screen">
|
| 239 |
-
<div class="
|
| 240 |
-
<
|
| 241 |
-
<
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
<
|
| 246 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
<h3>Fill Details</h3>
|
| 248 |
-
<p>
|
|
|
|
| 249 |
</button>
|
| 250 |
-
<button id="btn-start-voice" class="start-method-card" type="button">
|
| 251 |
-
<span class="start-
|
| 252 |
-
<
|
| 253 |
-
<
|
|
|
|
|
|
|
| 254 |
</button>
|
| 255 |
</div>
|
| 256 |
-
<button id="btn-continue-start" class="btn btn-primary btn-wide">Continue</button>
|
| 257 |
</div>
|
| 258 |
</section>
|
| 259 |
|
|
@@ -335,7 +344,7 @@
|
|
| 335 |
<textarea
|
| 336 |
id="quick-pitch-text"
|
| 337 |
class="quick-pitch-textarea"
|
| 338 |
-
rows="
|
| 339 |
placeholder="We're building EventRadar AI for students who miss hackathons because events are scattered across WhatsApp, LinkedIn, and college groups. We collect events in one place and recommend the best ones based on student interests. We tested with 80 students and want mentorship and pilot support."
|
| 340 |
></textarea>
|
| 341 |
<div class="quick-pitch-actions">
|
|
@@ -345,15 +354,13 @@
|
|
| 345 |
<button id="btn-load-sample-setup" class="btn btn-secondary" type="button">Load Demo Founder</button>
|
| 346 |
</div>
|
| 347 |
</div>
|
| 348 |
-
<p id="structure-pitch-hint" class="quick-pitch-hint" hidden></p>
|
| 349 |
</div>
|
| 350 |
|
| 351 |
-
<!-- AI brief preview -->
|
| 352 |
<div id="brief-preview-panel" class="panel glass briefing-panel brief-preview-panel" hidden>
|
| 353 |
<div class="brief-preview-header">
|
| 354 |
<div class="brief-preview-title-row">
|
| 355 |
<h3 class="briefing-section-title">AI-Structured Founder Brief</h3>
|
| 356 |
-
<span id="brief-preview-confidence" class="brief-confidence-chip"></span>
|
| 357 |
</div>
|
| 358 |
<p class="brief-preview-helper">AI extracted this from your pitch. Review and confirm.</p>
|
| 359 |
<p id="brief-preview-hint" class="brief-preview-hint" hidden></p>
|
|
@@ -375,7 +382,7 @@
|
|
| 375 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 376 |
<input type="text" name="target_users" class="brief-read-input" hidden />
|
| 377 |
</div>
|
| 378 |
-
<div class="brief-read-card brief-read-
|
| 379 |
<div class="brief-read-head">
|
| 380 |
<span class="brief-read-label">Problem</span>
|
| 381 |
<button type="button" class="brief-read-edit" aria-label="Edit Problem">✎</button>
|
|
@@ -383,7 +390,7 @@
|
|
| 383 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 384 |
<textarea name="problem" class="brief-read-input brief-read-textarea" rows="2" hidden></textarea>
|
| 385 |
</div>
|
| 386 |
-
<div class="brief-read-card brief-read-
|
| 387 |
<div class="brief-read-head">
|
| 388 |
<span class="brief-read-label">Solution</span>
|
| 389 |
<button type="button" class="brief-read-edit" aria-label="Edit Solution">✎</button>
|
|
@@ -391,7 +398,7 @@
|
|
| 391 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 392 |
<textarea name="solution" class="brief-read-input brief-read-textarea" rows="2" hidden></textarea>
|
| 393 |
</div>
|
| 394 |
-
<div class="brief-read-card brief-read-
|
| 395 |
<div class="brief-read-head">
|
| 396 |
<span class="brief-read-label">Why AI</span>
|
| 397 |
<button type="button" class="brief-read-edit" aria-label="Edit Why AI">✎</button>
|
|
@@ -430,32 +437,17 @@
|
|
| 430 |
</div>
|
| 431 |
</div>
|
| 432 |
|
| 433 |
-
<!-- Advanced Briefing (
|
| 434 |
<div id="panel-advanced-briefing" class="panel glass briefing-panel advanced-briefing-panel" hidden>
|
| 435 |
-
<
|
| 436 |
-
|
| 437 |
-
<
|
| 438 |
-
|
| 439 |
-
</
|
| 440 |
-
<
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
</div>
|
| 445 |
-
<div class="briefing-group briefing-group-solution">
|
| 446 |
-
<h4 class="briefing-group-label">Solution + Why AI</h4>
|
| 447 |
-
<label>Solution<textarea name="solution" rows="3" placeholder="What do you build?"></textarea></label>
|
| 448 |
-
<label>Why AI<textarea name="why_ai" rows="2" placeholder="Why AI instead of rules or manual work?"></textarea></label>
|
| 449 |
-
</div>
|
| 450 |
-
<div class="briefing-group briefing-group-traction">
|
| 451 |
-
<h4 class="briefing-group-label">Traction + Competitors</h4>
|
| 452 |
-
<label>Competitors<input name="competitors" type="text" placeholder="Who else solves this?" /></label>
|
| 453 |
-
<label>Traction<input name="traction" type="text" placeholder="Users, pilots, revenue, demos…" /></label>
|
| 454 |
-
</div>
|
| 455 |
-
<div class="briefing-group briefing-group-ask">
|
| 456 |
-
<h4 class="briefing-group-label">Ask / Desired Outcome</h4>
|
| 457 |
-
<label>Ask<input name="ask" type="text" placeholder="Funding, pilot, mentorship, sponsorship…" /></label>
|
| 458 |
-
</div>
|
| 459 |
</form>
|
| 460 |
</div>
|
| 461 |
</div>
|
|
@@ -750,8 +742,8 @@
|
|
| 750 |
<div class="sc-ring-wrap">
|
| 751 |
<div class="score-orb sc-ring" aria-label="Overall pitch score">
|
| 752 |
<strong id="overall-score" class="score-orb-value">0</strong>
|
| 753 |
-
<span id="overall-label" class="score-orb-label"></span>
|
| 754 |
</div>
|
|
|
|
| 755 |
</div>
|
| 756 |
<div class="sc-hero-meta">
|
| 757 |
<p id="sc-hero-label" class="sc-hero-kicker">Pitch Battle Result</p>
|
|
@@ -761,15 +753,19 @@
|
|
| 761 |
<span class="sc-chip sc-chip-weak" id="chip-weakest-dim">↓ Weakest: —</span>
|
| 762 |
<span class="sc-chip sc-chip-model" id="chip-score-source" hidden>⚡ Nemotron</span>
|
| 763 |
</div>
|
| 764 |
-
<div class="sc-hero-actions">
|
| 765 |
-
<
|
| 766 |
-
|
| 767 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 768 |
</div>
|
| 769 |
</div>
|
| 770 |
-
<div class="sc-hero-conversation">
|
| 771 |
-
<button id="btn-view-conversation" class="btn sc-btn-conversation" type="button">View Conversation</button>
|
| 772 |
-
</div>
|
| 773 |
</div>
|
| 774 |
</header>
|
| 775 |
|
|
@@ -814,14 +810,14 @@
|
|
| 814 |
<article id="voice-delivery-section" class="sc-voice-inline" hidden>
|
| 815 |
<div class="sc-tab-divider"></div>
|
| 816 |
<p class="sc-tab-eyebrow">Voice Delivery</p>
|
| 817 |
-
<div id="voice-delivery-content" class="voice-delivery-
|
| 818 |
</article>
|
| 819 |
</div>
|
| 820 |
|
| 821 |
<div class="sc-tab-panel" data-panel="answers" role="tabpanel" hidden>
|
| 822 |
<div id="answers-empty-state" class="sc-answers-empty" hidden>
|
| 823 |
<p class="sc-empty-title">No battle answers recorded.</p>
|
| 824 |
-
<p class="sc-empty-sub">You ended the
|
| 825 |
<div class="sc-empty-actions">
|
| 826 |
<button type="button" id="btn-answers-retry" class="btn sc-btn-gold">Retry Weakest Question</button>
|
| 827 |
<button type="button" id="btn-answers-new-battle" class="btn sc-btn-secondary">New Battle</button>
|
|
@@ -859,29 +855,6 @@
|
|
| 859 |
</div>
|
| 860 |
</div>
|
| 861 |
</div>
|
| 862 |
-
|
| 863 |
-
<section id="judge-verdict-hero" class="sc-verdict-section sc-verdict-inline" hidden>
|
| 864 |
-
<p class="sc-section-label sc-verdict-heading">Judge Verdict</p>
|
| 865 |
-
<div id="judge-verdict-section" class="sc-verdict-card">
|
| 866 |
-
<div class="sc-verdict-head">
|
| 867 |
-
<span id="verdict-persona-badge" class="sc-verdict-judge"></span>
|
| 868 |
-
<span id="verdict-interest-badge" class="sc-verdict-pill"></span>
|
| 869 |
-
</div>
|
| 870 |
-
<blockquote id="verdict-reaction" class="sc-verdict-quote"></blockquote>
|
| 871 |
-
<div class="sc-verdict-meta">
|
| 872 |
-
<div class="sc-verdict-meta-item">
|
| 873 |
-
<span class="sc-meta-label">Deal Type</span>
|
| 874 |
-
<strong id="verdict-deal-type" class="sc-meta-value"></strong>
|
| 875 |
-
</div>
|
| 876 |
-
<div class="sc-verdict-meta-item sc-verdict-meta-wide">
|
| 877 |
-
<span class="sc-meta-label">Why</span>
|
| 878 |
-
<strong id="verdict-why" class="sc-meta-value"></strong>
|
| 879 |
-
</div>
|
| 880 |
-
</div>
|
| 881 |
-
<p id="verdict-opening-offer" class="sc-verdict-offer" hidden></p>
|
| 882 |
-
<div id="verdict-actions" class="sc-verdict-actions"></div>
|
| 883 |
-
</div>
|
| 884 |
-
</section>
|
| 885 |
</div>
|
| 886 |
</div>
|
| 887 |
|
|
@@ -1200,6 +1173,46 @@
|
|
| 1200 |
</div>
|
| 1201 |
</div>
|
| 1202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1203 |
<!-- Coaching Roadmap Overlay -->
|
| 1204 |
<div id="path80-overlay" class="path80-overlay conversation-modal" hidden aria-modal="true" role="dialog" aria-label="Path to 80+">
|
| 1205 |
<div class="path80-panel glass results-modal-panel">
|
|
@@ -1340,6 +1353,9 @@
|
|
| 1340 |
<span id="retry-overall-lift" class="retry-overall-lift"></span>
|
| 1341 |
<span id="retry-verdict-badge" class="retry-verdict-badge result-verdict-badge"></span>
|
| 1342 |
</div>
|
|
|
|
|
|
|
|
|
|
| 1343 |
<p id="retry-next-prompt" class="retry-next-prompt clamp-text"></p>
|
| 1344 |
<div class="retry-result-actions scorecard-actions">
|
| 1345 |
<button id="btn-retry-again" class="btn btn-retry-start" type="button">Retry Again</button>
|
|
|
|
| 19 |
<!-- Landing — Founder Pressure Arena (Pass 1) -->
|
| 20 |
<section id="screen-landing" class="screen active">
|
| 21 |
<div class="arena-landing">
|
| 22 |
+
<canvas id="pfFallCanvas" aria-hidden="true" style="position:absolute;inset:0;z-index:1;pointer-events:none;opacity:0.45;width:100%;height:100%;"></canvas>
|
| 23 |
<div class="arena-scene" aria-hidden="true">
|
| 24 |
<div class="arena-scene-base"></div>
|
| 25 |
<div class="hero-center-haze"></div>
|
|
|
|
| 235 |
</div>
|
| 236 |
</section>
|
| 237 |
|
| 238 |
+
<!-- Start method — entry path chooser -->
|
| 239 |
+
<section id="screen-start-method" class="screen screen-start-path">
|
| 240 |
+
<div class="start-path-shell">
|
| 241 |
+
<header class="start-path-header">
|
| 242 |
+
<div class="start-path-header-main">
|
| 243 |
+
<p class="results-eyebrow">Pre-Fight Briefing</p>
|
| 244 |
+
<h2 class="start-path-title">Choose your entry mode</h2>
|
| 245 |
+
<p class="start-path-sub">Same arena — two ways in. Both lead to your structured brief.</p>
|
| 246 |
+
</div>
|
| 247 |
+
<button id="btn-start-back-landing" class="btn btn-ghost" type="button">Back</button>
|
| 248 |
+
</header>
|
| 249 |
+
|
| 250 |
+
<div class="start-method-grid start-path-grid">
|
| 251 |
+
<button id="btn-start-text" class="start-method-card start-path-card start-path-card-text" type="button">
|
| 252 |
+
<span class="start-path-badge">Mode 01</span>
|
| 253 |
+
<span class="start-method-icon" aria-hidden="true">✎</span>
|
| 254 |
<h3>Fill Details</h3>
|
| 255 |
+
<p class="start-path-tagline">Written Briefing</p>
|
| 256 |
+
<p class="start-path-desc">Type or paste your pitch. AI structures it into a founder brief before the judge attacks.</p>
|
| 257 |
</button>
|
| 258 |
+
<button id="btn-start-voice" class="start-method-card start-path-card start-path-card-voice" type="button">
|
| 259 |
+
<span class="start-path-badge start-path-badge-voice">Mode 02</span>
|
| 260 |
+
<span class="start-method-icon voice-icon" aria-hidden="true">🎙</span>
|
| 261 |
+
<h3>Voice Mode</h3>
|
| 262 |
+
<p class="start-path-tagline">Live Pitch Capture</p>
|
| 263 |
+
<p class="start-path-desc">Speak for 60–90 seconds. AI transcribes, extracts, and structures your startup brief.</p>
|
| 264 |
</button>
|
| 265 |
</div>
|
|
|
|
| 266 |
</div>
|
| 267 |
</section>
|
| 268 |
|
|
|
|
| 344 |
<textarea
|
| 345 |
id="quick-pitch-text"
|
| 346 |
class="quick-pitch-textarea"
|
| 347 |
+
rows="5"
|
| 348 |
placeholder="We're building EventRadar AI for students who miss hackathons because events are scattered across WhatsApp, LinkedIn, and college groups. We collect events in one place and recommend the best ones based on student interests. We tested with 80 students and want mentorship and pilot support."
|
| 349 |
></textarea>
|
| 350 |
<div class="quick-pitch-actions">
|
|
|
|
| 354 |
<button id="btn-load-sample-setup" class="btn btn-secondary" type="button">Load Demo Founder</button>
|
| 355 |
</div>
|
| 356 |
</div>
|
|
|
|
| 357 |
</div>
|
| 358 |
|
| 359 |
+
<!-- AI brief preview — shown after Structure My Pitch (replaces quick pitch panel) -->
|
| 360 |
<div id="brief-preview-panel" class="panel glass briefing-panel brief-preview-panel" hidden>
|
| 361 |
<div class="brief-preview-header">
|
| 362 |
<div class="brief-preview-title-row">
|
| 363 |
<h3 class="briefing-section-title">AI-Structured Founder Brief</h3>
|
|
|
|
| 364 |
</div>
|
| 365 |
<p class="brief-preview-helper">AI extracted this from your pitch. Review and confirm.</p>
|
| 366 |
<p id="brief-preview-hint" class="brief-preview-hint" hidden></p>
|
|
|
|
| 382 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 383 |
<input type="text" name="target_users" class="brief-read-input" hidden />
|
| 384 |
</div>
|
| 385 |
+
<div class="brief-read-card brief-read-short" data-field="problem">
|
| 386 |
<div class="brief-read-head">
|
| 387 |
<span class="brief-read-label">Problem</span>
|
| 388 |
<button type="button" class="brief-read-edit" aria-label="Edit Problem">✎</button>
|
|
|
|
| 390 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 391 |
<textarea name="problem" class="brief-read-input brief-read-textarea" rows="2" hidden></textarea>
|
| 392 |
</div>
|
| 393 |
+
<div class="brief-read-card brief-read-short" data-field="solution">
|
| 394 |
<div class="brief-read-head">
|
| 395 |
<span class="brief-read-label">Solution</span>
|
| 396 |
<button type="button" class="brief-read-edit" aria-label="Edit Solution">✎</button>
|
|
|
|
| 398 |
<p class="brief-read-value is-empty">Not specified</p>
|
| 399 |
<textarea name="solution" class="brief-read-input brief-read-textarea" rows="2" hidden></textarea>
|
| 400 |
</div>
|
| 401 |
+
<div class="brief-read-card brief-read-short" data-field="why_ai">
|
| 402 |
<div class="brief-read-head">
|
| 403 |
<span class="brief-read-label">Why AI</span>
|
| 404 |
<button type="button" class="brief-read-edit" aria-label="Edit Why AI">✎</button>
|
|
|
|
| 437 |
</div>
|
| 438 |
</div>
|
| 439 |
|
| 440 |
+
<!-- Advanced Briefing (compact grid — matches structured brief layout) -->
|
| 441 |
<div id="panel-advanced-briefing" class="panel glass briefing-panel advanced-briefing-panel" hidden>
|
| 442 |
+
<form id="startup-form" class="startup-form briefing-form advanced-briefing-form">
|
| 443 |
+
<label class="adv-field adv-field-wide">Name<input name="name" type="text" placeholder="EventRadar AI" /></label>
|
| 444 |
+
<label class="adv-field">Problem<textarea name="problem" rows="2" placeholder="What pain are you solving?"></textarea></label>
|
| 445 |
+
<label class="adv-field">Target Users<input name="target_users" type="text" placeholder="Who feels this pain most?" /></label>
|
| 446 |
+
<label class="adv-field">Solution<textarea name="solution" rows="2" placeholder="What do you build?"></textarea></label>
|
| 447 |
+
<label class="adv-field">Why AI<textarea name="why_ai" rows="2" placeholder="Why AI instead of rules or manual work?"></textarea></label>
|
| 448 |
+
<label class="adv-field">Competitors<input name="competitors" type="text" placeholder="Who else solves this?" /></label>
|
| 449 |
+
<label class="adv-field">Traction<input name="traction" type="text" placeholder="Users, pilots, revenue, demos…" /></label>
|
| 450 |
+
<label class="adv-field adv-field-wide">Ask<input name="ask" type="text" placeholder="Funding, pilot, mentorship, sponsorship…" /></label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
</form>
|
| 452 |
</div>
|
| 453 |
</div>
|
|
|
|
| 742 |
<div class="sc-ring-wrap">
|
| 743 |
<div class="score-orb sc-ring" aria-label="Overall pitch score">
|
| 744 |
<strong id="overall-score" class="score-orb-value">0</strong>
|
|
|
|
| 745 |
</div>
|
| 746 |
+
<span id="overall-label" class="score-orb-label sc-ring-caption"></span>
|
| 747 |
</div>
|
| 748 |
<div class="sc-hero-meta">
|
| 749 |
<p id="sc-hero-label" class="sc-hero-kicker">Pitch Battle Result</p>
|
|
|
|
| 753 |
<span class="sc-chip sc-chip-weak" id="chip-weakest-dim">↓ Weakest: —</span>
|
| 754 |
<span class="sc-chip sc-chip-model" id="chip-score-source" hidden>⚡ Nemotron</span>
|
| 755 |
</div>
|
| 756 |
+
<div class="sc-hero-actions-row">
|
| 757 |
+
<div class="sc-hero-actions sc-hero-actions-primary">
|
| 758 |
+
<button id="btn-path-to-80" class="btn sc-btn-gold" type="button">View Path to 80+</button>
|
| 759 |
+
<button id="btn-scorecard-retry" class="btn sc-btn-secondary" type="button">Retry Weakest Question</button>
|
| 760 |
+
</div>
|
| 761 |
+
<div class="sc-hero-actions sc-hero-actions-secondary">
|
| 762 |
+
<button id="btn-view-deal-scorecard" class="btn score-action-btn score-action-btn-secondary" type="button" hidden>View Combined Readout</button>
|
| 763 |
+
<button id="btn-view-judge-verdict" class="btn score-action-btn score-action-btn-verdict" type="button" hidden>View Judge Verdict</button>
|
| 764 |
+
<button id="btn-view-conversation" class="btn score-action-btn score-action-btn-secondary" type="button">View Conversation</button>
|
| 765 |
+
<button id="btn-reset" class="btn score-action-btn score-action-btn-secondary" type="button">New Battle</button>
|
| 766 |
+
</div>
|
| 767 |
</div>
|
| 768 |
</div>
|
|
|
|
|
|
|
|
|
|
| 769 |
</div>
|
| 770 |
</header>
|
| 771 |
|
|
|
|
| 810 |
<article id="voice-delivery-section" class="sc-voice-inline" hidden>
|
| 811 |
<div class="sc-tab-divider"></div>
|
| 812 |
<p class="sc-tab-eyebrow">Voice Delivery</p>
|
| 813 |
+
<div id="voice-delivery-content" class="voice-delivery-wrap"></div>
|
| 814 |
</article>
|
| 815 |
</div>
|
| 816 |
|
| 817 |
<div class="sc-tab-panel" data-panel="answers" role="tabpanel" hidden>
|
| 818 |
<div id="answers-empty-state" class="sc-answers-empty" hidden>
|
| 819 |
<p class="sc-empty-title">No battle answers recorded.</p>
|
| 820 |
+
<p class="sc-empty-sub">You ended before responding, so the scorecard can only grade the startup brief.</p>
|
| 821 |
<div class="sc-empty-actions">
|
| 822 |
<button type="button" id="btn-answers-retry" class="btn sc-btn-gold">Retry Weakest Question</button>
|
| 823 |
<button type="button" id="btn-answers-new-battle" class="btn sc-btn-secondary">New Battle</button>
|
|
|
|
| 855 |
</div>
|
| 856 |
</div>
|
| 857 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
</div>
|
| 859 |
</div>
|
| 860 |
|
|
|
|
| 1173 |
</div>
|
| 1174 |
</div>
|
| 1175 |
|
| 1176 |
+
<!-- Judge Verdict Modal -->
|
| 1177 |
+
<div id="verdict-overlay" class="verdict-overlay conversation-modal" hidden aria-modal="true" role="dialog" aria-label="Judge Verdict">
|
| 1178 |
+
<div class="verdict-modal-panel glass results-modal-panel">
|
| 1179 |
+
<div class="verdict-modal-header">
|
| 1180 |
+
<div>
|
| 1181 |
+
<p class="results-eyebrow">Post-Battle</p>
|
| 1182 |
+
<h2 class="verdict-modal-title">Judge Verdict</h2>
|
| 1183 |
+
</div>
|
| 1184 |
+
<button id="btn-close-verdict" class="btn btn-ghost btn-sm" type="button" aria-label="Close">✕ Close</button>
|
| 1185 |
+
</div>
|
| 1186 |
+
<div id="judge-verdict-section" class="sc-verdict-card sc-verdict-modal-card">
|
| 1187 |
+
<div class="sc-verdict-head">
|
| 1188 |
+
<span id="verdict-persona-badge" class="sc-verdict-judge"></span>
|
| 1189 |
+
<span id="verdict-interest-badge" class="sc-verdict-pill"></span>
|
| 1190 |
+
</div>
|
| 1191 |
+
<blockquote id="verdict-reaction" class="sc-verdict-quote"></blockquote>
|
| 1192 |
+
<div class="sc-verdict-meta">
|
| 1193 |
+
<div class="sc-verdict-meta-item">
|
| 1194 |
+
<span class="sc-meta-label">Deal Type</span>
|
| 1195 |
+
<strong id="verdict-deal-type" class="sc-meta-value"></strong>
|
| 1196 |
+
</div>
|
| 1197 |
+
<div class="sc-verdict-meta-item sc-verdict-meta-wide">
|
| 1198 |
+
<span class="sc-meta-label">Why</span>
|
| 1199 |
+
<strong id="verdict-why" class="sc-meta-value"></strong>
|
| 1200 |
+
</div>
|
| 1201 |
+
</div>
|
| 1202 |
+
<p id="verdict-opening-offer" class="sc-verdict-offer" hidden></p>
|
| 1203 |
+
<p id="verdict-deal-locked-msg" class="verdict-deal-locked-msg" hidden>Deal phase is not unlocked for this verdict.</p>
|
| 1204 |
+
</div>
|
| 1205 |
+
<div class="verdict-modal-footer">
|
| 1206 |
+
<div id="verdict-actions" class="sc-verdict-actions verdict-modal-actions"></div>
|
| 1207 |
+
<div class="verdict-modal-footer-secondary">
|
| 1208 |
+
<button id="btn-verdict-retry" class="btn sc-btn-secondary" type="button">Retry Weakest Question</button>
|
| 1209 |
+
<button id="btn-verdict-new-battle" class="btn sc-btn-secondary" type="button">New Battle</button>
|
| 1210 |
+
<button id="btn-close-verdict-bottom" class="btn btn-ghost" type="button">Back to Scorecard</button>
|
| 1211 |
+
</div>
|
| 1212 |
+
</div>
|
| 1213 |
+
</div>
|
| 1214 |
+
</div>
|
| 1215 |
+
|
| 1216 |
<!-- Coaching Roadmap Overlay -->
|
| 1217 |
<div id="path80-overlay" class="path80-overlay conversation-modal" hidden aria-modal="true" role="dialog" aria-label="Path to 80+">
|
| 1218 |
<div class="path80-panel glass results-modal-panel">
|
|
|
|
| 1353 |
<span id="retry-overall-lift" class="retry-overall-lift"></span>
|
| 1354 |
<span id="retry-verdict-badge" class="retry-verdict-badge result-verdict-badge"></span>
|
| 1355 |
</div>
|
| 1356 |
+
<p id="retry-projection-note" class="retry-projection-note" hidden>
|
| 1357 |
+
Training projection only — your original scorecard stays unchanged.
|
| 1358 |
+
</p>
|
| 1359 |
<p id="retry-next-prompt" class="retry-next-prompt clamp-text"></p>
|
| 1360 |
<div class="retry-result-actions scorecard-actions">
|
| 1361 |
<button id="btn-retry-again" class="btn btn-retry-start" type="button">Retry Again</button>
|
frontend/script.js
CHANGED
|
@@ -23,9 +23,14 @@ const state = {
|
|
| 23 |
dealBattleLog: [],
|
| 24 |
battleMetaSnapshot: null,
|
| 25 |
scorecardSnapshot: null,
|
|
|
|
|
|
|
| 26 |
briefingMode: "quick",
|
| 27 |
briefStructured: false,
|
| 28 |
pitchExtractionConfidence: null,
|
|
|
|
|
|
|
|
|
|
| 29 |
battleConfidencePct: null,
|
| 30 |
};
|
| 31 |
|
|
@@ -62,12 +67,16 @@ const loadingText = document.getElementById("loading-message");
|
|
| 62 |
const battleStatus = document.getElementById("battle-status");
|
| 63 |
const errorBanner = document.getElementById("error-banner");
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
function showScreen(name) {
|
| 66 |
Object.entries(screens).forEach(([key, el]) => {
|
| 67 |
el.classList.toggle("active", key === name);
|
| 68 |
});
|
| 69 |
const app = document.getElementById("app");
|
| 70 |
-
app?.classList.toggle("app-arena-fullwidth", name === "battle" || name === "deal" || name === "scorecard");
|
| 71 |
app?.classList.toggle("app-scorecard-fullwidth", name === "scorecard");
|
| 72 |
if (name === "landing" && landingIntroComplete) {
|
| 73 |
finalizeLandingIntroStatic();
|
|
@@ -120,6 +129,168 @@ function typeText(el, text, speedMs, onDone) {
|
|
| 120 |
step();
|
| 121 |
}
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
function initLandingIntro() {
|
| 124 |
if (landingIntroRunning || landingIntroComplete) return;
|
| 125 |
landingIntroRunning = true;
|
|
@@ -169,6 +340,22 @@ function isNemotronScorecardSource(src) {
|
|
| 169 |
return NEMOTRON_SCORECARD_SOURCES.has(String(src ?? "").trim());
|
| 170 |
}
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
const INTERNAL_ERROR_PATTERNS = [
|
| 173 |
/nvidia/i,
|
| 174 |
/max_tokens/i,
|
|
@@ -243,6 +430,7 @@ function commitBriefFieldEdit(card) {
|
|
| 243 |
}
|
| 244 |
closeBriefFieldExpanded(card);
|
| 245 |
syncBriefToStartupForm();
|
|
|
|
| 246 |
}
|
| 247 |
|
| 248 |
function cancelBriefFieldEdit(card) {
|
|
@@ -338,7 +526,7 @@ function syncBriefToStartupForm() {
|
|
| 338 |
}
|
| 339 |
|
| 340 |
function getStartupPayload() {
|
| 341 |
-
if (
|
| 342 |
syncBriefToStartupForm();
|
| 343 |
}
|
| 344 |
const data = new FormData(startupForm);
|
|
@@ -356,7 +544,7 @@ function fillStartupForm(startup) {
|
|
| 356 |
function setBriefingMode(mode) {
|
| 357 |
state.briefingMode = mode;
|
| 358 |
const isQuick = mode === "quick";
|
| 359 |
-
const previewVisible = state.briefStructured;
|
| 360 |
|
| 361 |
document.getElementById("tab-quick-pitch")?.classList.toggle("active", isQuick);
|
| 362 |
document.getElementById("tab-advanced-briefing")?.classList.toggle("active", !isQuick);
|
|
@@ -364,6 +552,7 @@ function setBriefingMode(mode) {
|
|
| 364 |
document.getElementById("tab-advanced-briefing")?.setAttribute("aria-selected", String(!isQuick));
|
| 365 |
briefingLeftCol?.classList.toggle("mode-quick", isQuick);
|
| 366 |
briefingLeftCol?.classList.toggle("mode-advanced", !isQuick);
|
|
|
|
| 367 |
|
| 368 |
const advancedPanel = document.getElementById("panel-advanced-briefing");
|
| 369 |
if (advancedPanel) advancedPanel.hidden = isQuick;
|
|
@@ -380,7 +569,9 @@ function setBriefingMode(mode) {
|
|
| 380 |
const subtitle = document.getElementById("briefing-subtitle");
|
| 381 |
if (subtitle) {
|
| 382 |
subtitle.textContent = isQuick
|
| 383 |
-
?
|
|
|
|
|
|
|
| 384 |
: "Want full control? Edit every field manually.";
|
| 385 |
}
|
| 386 |
}
|
|
@@ -388,15 +579,7 @@ function setBriefingMode(mode) {
|
|
| 388 |
function showBriefPreview(meta = {}) {
|
| 389 |
if (quickPitchPanel) quickPitchPanel.hidden = true;
|
| 390 |
if (briefPreviewPanel) briefPreviewPanel.hidden = false;
|
| 391 |
-
|
| 392 |
-
if (advancedPanel) advancedPanel.hidden = true;
|
| 393 |
-
|
| 394 |
-
const confEl = document.getElementById("brief-preview-confidence");
|
| 395 |
-
if (confEl) {
|
| 396 |
-
const conf = meta.confidence || "medium";
|
| 397 |
-
confEl.textContent = `Confidence: ${String(conf).toUpperCase()}`;
|
| 398 |
-
confEl.className = `brief-confidence-chip confidence-${conf}`;
|
| 399 |
-
}
|
| 400 |
|
| 401 |
const hintEl = document.getElementById("brief-preview-hint");
|
| 402 |
if (hintEl) {
|
|
@@ -417,7 +600,7 @@ function hideBriefPreview() {
|
|
| 417 |
state.briefStructured = false;
|
| 418 |
if (quickPitchPanel) quickPitchPanel.hidden = false;
|
| 419 |
if (briefPreviewPanel) briefPreviewPanel.hidden = true;
|
| 420 |
-
|
| 421 |
document.getElementById("brief-preview-hint")?.setAttribute("hidden", "");
|
| 422 |
briefPreviewForm?.querySelectorAll(".brief-read-card.is-editing").forEach((card) => {
|
| 423 |
commitBriefFieldEdit(card);
|
|
@@ -439,6 +622,68 @@ function confidenceFromStartupFields(startup = {}) {
|
|
| 439 |
return "low";
|
| 440 |
}
|
| 441 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
function confidenceLevelToPct(level) {
|
| 443 |
const key = String(level || "medium").toLowerCase();
|
| 444 |
if (key === "high") return 76;
|
|
@@ -449,6 +694,10 @@ function confidenceLevelToPct(level) {
|
|
| 449 |
function syncPitchExtractionConfidence(meta = {}, startup = null) {
|
| 450 |
const level = extractionConfidenceLevel(meta) || confidenceFromStartupFields(startup) || "medium";
|
| 451 |
state.pitchExtractionConfidence = level;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
return level;
|
| 453 |
}
|
| 454 |
|
|
@@ -456,12 +705,16 @@ function fillBriefPreview(startup, meta = {}) {
|
|
| 456 |
state.briefStructured = true;
|
| 457 |
syncPitchExtractionConfidence(meta, startup);
|
| 458 |
fillStartupForm(startup);
|
|
|
|
| 459 |
showBriefPreview(meta);
|
| 460 |
}
|
| 461 |
|
| 462 |
function resetSetupScreen() {
|
| 463 |
state.briefStructured = false;
|
| 464 |
state.pitchExtractionConfidence = null;
|
|
|
|
|
|
|
|
|
|
| 465 |
setBriefingMode("quick");
|
| 466 |
hideBriefPreview();
|
| 467 |
const quickText = document.getElementById("quick-pitch-text");
|
|
@@ -476,6 +729,15 @@ async function structurePitch() {
|
|
| 476 |
showErrorBanner("Type or paste your pitch first.");
|
| 477 |
return;
|
| 478 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
try {
|
| 480 |
setGlobalLoading(true, "Structuring your pitch…");
|
| 481 |
state.startMode = "text";
|
|
@@ -484,6 +746,8 @@ async function structurePitch() {
|
|
| 484 |
showErrorBanner(data.error || "Could not structure your pitch. Try Advanced Briefing.");
|
| 485 |
return;
|
| 486 |
}
|
|
|
|
|
|
|
| 487 |
fillBriefPreview(data.startup_context, data);
|
| 488 |
hideErrorBanner();
|
| 489 |
} catch (error) {
|
|
@@ -643,7 +907,11 @@ function renderConfidenceMeter(confidencePct) {
|
|
| 643 |
}
|
| 644 |
|
| 645 |
function refreshBattleConfidenceFromPressure(pressurePct, round = 1) {
|
| 646 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
const pressureDrag = Math.round(Number(pressurePct) * 0.25);
|
| 648 |
const roundDrag = Math.max(0, Number(round) - 1) * 2;
|
| 649 |
const ceiling = Math.max(8, base - pressureDrag - roundDrag);
|
|
@@ -659,9 +927,12 @@ function adjustBattleConfidenceFromAnswer(quality) {
|
|
| 659 |
const deltas = { strong: 8, partial: -2, weak: -12, non_answer: -20 };
|
| 660 |
const delta = deltas[String(quality || "").toLowerCase()] ?? 0;
|
| 661 |
if (!delta) return;
|
|
|
|
|
|
|
|
|
|
| 662 |
state.battleConfidencePct = Math.max(
|
| 663 |
8,
|
| 664 |
-
Math.min(92, (state.battleConfidencePct ??
|
| 665 |
);
|
| 666 |
renderConfidenceMeter(state.battleConfidencePct);
|
| 667 |
}
|
|
@@ -1087,6 +1358,11 @@ export async function loadSample() {
|
|
| 1087 |
|
| 1088 |
export async function startSession() {
|
| 1089 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1090 |
setGlobalLoading(true, "AI judge is preparing the first attack…");
|
| 1091 |
battleStatus.hidden = true;
|
| 1092 |
hideBattleReadiness();
|
|
@@ -1265,11 +1541,13 @@ export async function resetBattle() {
|
|
| 1265 |
state.pendingDealVoiceTurn = null;
|
| 1266 |
state.startMode = "text";
|
| 1267 |
state.pitchExtractionConfidence = null;
|
|
|
|
| 1268 |
state.battleConfidencePct = null;
|
| 1269 |
state.conversationLog = [];
|
| 1270 |
state.dealConversationLog = [];
|
| 1271 |
state.battleLog = [];
|
| 1272 |
state.dealBattleLog = [];
|
|
|
|
| 1273 |
resetLiveTurnTracking();
|
| 1274 |
chatWindow.innerHTML = "";
|
| 1275 |
if (dealChatWindow) dealChatWindow.innerHTML = "";
|
|
@@ -1518,7 +1796,27 @@ function renderScorecard(data) {
|
|
| 1518 |
const { strongest, weakest } = getStrongestWeakest(scores);
|
| 1519 |
const se = data.score_explanation ?? {};
|
| 1520 |
const explanation = se;
|
| 1521 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1522 |
|
| 1523 |
const opponent = PERSONA_LABELS[state.persona] ?? data.opponent ?? "AI Judge";
|
| 1524 |
const mode = DIFFICULTY_LABELS[state.difficultyProfile]
|
|
@@ -1553,16 +1851,6 @@ function renderScorecard(data) {
|
|
| 1553 |
: "↓ Weakest: —";
|
| 1554 |
}
|
| 1555 |
|
| 1556 |
-
const chipSource = document.getElementById("chip-score-source");
|
| 1557 |
-
const sourceBadgeEl = document.getElementById("scorecard-source-badge");
|
| 1558 |
-
if (chipSource) {
|
| 1559 |
-
chipSource.textContent = nemotronScored ? "⚡ Nemotron" : "⚡ Local";
|
| 1560 |
-
chipSource.hidden = false;
|
| 1561 |
-
}
|
| 1562 |
-
if (sourceBadgeEl) {
|
| 1563 |
-
sourceBadgeEl.textContent = nemotronScored ? "Powered by NVIDIA Nemotron" : "Local scoring";
|
| 1564 |
-
}
|
| 1565 |
-
|
| 1566 |
const fallbackWarnEl = document.getElementById("scorecard-fallback-warning");
|
| 1567 |
if (fallbackWarnEl) {
|
| 1568 |
fallbackWarnEl.textContent = "";
|
|
@@ -1659,6 +1947,23 @@ function renderScorecard(data) {
|
|
| 1659 |
if (answersEmpty) answersEmpty.hidden = !noAnswers;
|
| 1660 |
if (answersContent) answersContent.hidden = noAnswers;
|
| 1661 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1662 |
state.scoreExplanation = data.score_explanation ?? null;
|
| 1663 |
|
| 1664 |
const pathBtn = document.getElementById("btn-path-to-80");
|
|
@@ -1683,6 +1988,8 @@ function renderScorecard(data) {
|
|
| 1683 |
weakest: weakest ? formatDimLabel(weakest[0]) : null,
|
| 1684 |
roundsCompleted: state.battleLog?.length ?? 0,
|
| 1685 |
};
|
|
|
|
|
|
|
| 1686 |
}
|
| 1687 |
|
| 1688 |
const DEAL_TYPE_LABELS = {
|
|
@@ -1704,13 +2011,36 @@ function verdictNegotiationCta(label, fallback = "Start Negotiation →") {
|
|
| 1704 |
.replace(/Deal Round/i, "Negotiation");
|
| 1705 |
}
|
| 1706 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1707 |
function renderJudgeVerdict(verdict) {
|
| 1708 |
-
const
|
| 1709 |
if (!verdict || !verdict.interest_level) {
|
| 1710 |
-
if (
|
| 1711 |
return;
|
| 1712 |
}
|
| 1713 |
-
if (
|
| 1714 |
|
| 1715 |
const interest = verdict.interest_level || "no_interest";
|
| 1716 |
const personaLine = `${verdict.persona_name || "Judge"} — ${verdict.persona_type || ""}`.trim();
|
|
@@ -1739,32 +2069,26 @@ function renderJudgeVerdict(verdict) {
|
|
| 1739 |
}
|
| 1740 |
}
|
| 1741 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1742 |
const actions = document.getElementById("verdict-actions");
|
| 1743 |
if (!actions) return;
|
| 1744 |
actions.innerHTML = "";
|
| 1745 |
|
| 1746 |
-
if (
|
| 1747 |
const btn = document.createElement("button");
|
| 1748 |
-
btn.className = "btn btn-deal-continue";
|
| 1749 |
-
btn.textContent = verdictNegotiationCta(verdict.next_step_label);
|
| 1750 |
btn.type = "button";
|
| 1751 |
-
btn.addEventListener("click",
|
|
|
|
|
|
|
|
|
|
| 1752 |
actions.appendChild(btn);
|
| 1753 |
-
} else if (interest === "too_early") {
|
| 1754 |
-
/* Footer sc-cta-row handles retry / setup */
|
| 1755 |
-
} else if (interest === "no_interest") {
|
| 1756 |
-
/* Footer sc-cta-row handles Path to 80+ and New Battle */
|
| 1757 |
-
} else if (interest === "mild_interest" || interest === "strong_interest") {
|
| 1758 |
-
if (!verdict.can_continue_to_deal) {
|
| 1759 |
-
const btn = document.createElement("button");
|
| 1760 |
-
btn.className = "btn btn-deal-continue";
|
| 1761 |
-
btn.textContent = verdictNegotiationCta(verdict.next_step_label);
|
| 1762 |
-
btn.type = "button";
|
| 1763 |
-
btn.addEventListener("click", startDealPhase);
|
| 1764 |
-
actions.appendChild(btn);
|
| 1765 |
-
}
|
| 1766 |
-
} else if (verdict.deal_type === "verdict_only") {
|
| 1767 |
-
/* Footer sc-cta-row handles Path to 80+ */
|
| 1768 |
}
|
| 1769 |
}
|
| 1770 |
|
|
@@ -1940,7 +2264,22 @@ export async function endDeal() {
|
|
| 1940 |
}
|
| 1941 |
}
|
| 1942 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1943 |
function renderDealScorecard(data) {
|
|
|
|
|
|
|
|
|
|
| 1944 |
const combined = data.combined_scorecard || {};
|
| 1945 |
const deal = data.deal_scorecard || {};
|
| 1946 |
|
|
@@ -2116,16 +2455,27 @@ function renderVoiceDelivery(vd) {
|
|
| 2116 |
|| (vd.delivery_notes ?? []).find((n) => String(n).trim()) || "";
|
| 2117 |
|
| 2118 |
content.innerHTML = `
|
| 2119 |
-
<div class="voice-
|
| 2120 |
-
|
| 2121 |
-
|
| 2122 |
-
|
| 2123 |
-
|
| 2124 |
-
|
| 2125 |
-
|
| 2126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2127 |
</div>
|
| 2128 |
-
${overallNote ? `<p class="voice-delivery-overall">${escapeHtml(overallNote)}</p>` : ""}
|
| 2129 |
`;
|
| 2130 |
|
| 2131 |
const notes = (vd.delivery_notes ?? []).filter((n) => {
|
|
@@ -2289,29 +2639,62 @@ function showRetryVoicePreview(data) {
|
|
| 2289 |
|
| 2290 |
function renderRetryResult(data) {
|
| 2291 |
const comp = data.comparison ?? {};
|
|
|
|
| 2292 |
document.getElementById("retry-result-old").textContent = comp.old_answer_summary ?? data.original_answer ?? "";
|
| 2293 |
document.getElementById("retry-result-new").textContent = comp.new_answer_summary ?? data.retry_answer ?? "";
|
| 2294 |
document.getElementById("retry-what-improved").textContent = comp.what_improved ?? "";
|
| 2295 |
document.getElementById("retry-still-missing").textContent = comp.still_missing ?? "";
|
| 2296 |
document.getElementById("retry-specific-tip").textContent = comp.specific_tip ?? "";
|
| 2297 |
|
| 2298 |
-
|
| 2299 |
-
const before = comp.estimated_dimension_before ?? 0;
|
| 2300 |
-
const after = comp.estimated_dimension_after ?? before;
|
| 2301 |
document.getElementById("retry-dim-estimate").textContent =
|
| 2302 |
`${formatDimLabel(data.dimension)}: ${before} → ${after}`;
|
| 2303 |
-
|
| 2304 |
-
|
| 2305 |
-
|
| 2306 |
-
|
| 2307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2308 |
|
| 2309 |
const verdictEl = document.getElementById("retry-verdict-badge");
|
| 2310 |
-
const verdict =
|
|
|
|
|
|
|
| 2311 |
const verdictLabels = {
|
| 2312 |
improved: "Improved",
|
| 2313 |
slightly_improved: "Slightly improved",
|
| 2314 |
-
needs_more_work: "
|
| 2315 |
};
|
| 2316 |
if (verdictEl) {
|
| 2317 |
verdictEl.textContent = verdictLabels[verdict] ?? verdict;
|
|
@@ -2353,14 +2736,6 @@ async function submitRetryAnswer() {
|
|
| 2353 |
return;
|
| 2354 |
}
|
| 2355 |
renderRetryResult(data);
|
| 2356 |
-
if (data.updated_scorecard) {
|
| 2357 |
-
renderScorecard(data.updated_scorecard);
|
| 2358 |
-
state.scoreExplanation = data.updated_scorecard.score_explanation ?? state.scoreExplanation;
|
| 2359 |
-
if (data.judge_verdict) {
|
| 2360 |
-
renderJudgeVerdict(data.judge_verdict);
|
| 2361 |
-
state.judgeVerdict = data.judge_verdict;
|
| 2362 |
-
}
|
| 2363 |
-
}
|
| 2364 |
hideErrorBanner();
|
| 2365 |
} catch (error) {
|
| 2366 |
console.error(error);
|
|
@@ -2380,49 +2755,54 @@ function openRetryFromPath80() {
|
|
| 2380 |
startRetryDrill();
|
| 2381 |
}
|
| 2382 |
|
| 2383 |
-
|
| 2384 |
-
|
| 2385 |
-
|
| 2386 |
-
|
| 2387 |
-
showScreen("setup");
|
| 2388 |
});
|
| 2389 |
-
|
| 2390 |
resetSetupScreen();
|
| 2391 |
showScreen("landing");
|
| 2392 |
});
|
| 2393 |
-
|
| 2394 |
-
|
| 2395 |
-
|
| 2396 |
-
|
| 2397 |
state.startMode = "voice";
|
|
|
|
| 2398 |
showScreen("voicePitch");
|
| 2399 |
});
|
| 2400 |
-
|
| 2401 |
syncBriefToStartupForm();
|
| 2402 |
startSession();
|
| 2403 |
});
|
| 2404 |
-
|
| 2405 |
hideBriefPreview();
|
| 2406 |
document.getElementById("quick-pitch-text")?.focus();
|
| 2407 |
});
|
| 2408 |
-
|
| 2409 |
|
| 2410 |
-
|
| 2411 |
state.startMode = "text";
|
| 2412 |
-
|
| 2413 |
-
|
|
|
|
| 2414 |
});
|
| 2415 |
-
|
|
|
|
| 2416 |
state.startMode = "voice";
|
| 2417 |
-
|
| 2418 |
-
|
| 2419 |
});
|
| 2420 |
-
|
| 2421 |
-
|
| 2422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2423 |
});
|
| 2424 |
-
|
| 2425 |
-
document.getElementById("btn-voice-edit-manual").addEventListener("click", () => {
|
| 2426 |
const form = document.getElementById("voice-extract-form");
|
| 2427 |
const data = new FormData(form);
|
| 2428 |
applyVoicePitchToBriefing({
|
|
@@ -2433,7 +2813,7 @@ document.getElementById("btn-voice-edit-manual").addEventListener("click", () =>
|
|
| 2433 |
setBriefingMode("advanced");
|
| 2434 |
showScreen("setup");
|
| 2435 |
});
|
| 2436 |
-
|
| 2437 |
const form = document.getElementById("voice-extract-form");
|
| 2438 |
const data = new FormData(form);
|
| 2439 |
applyVoicePitchToBriefing({
|
|
@@ -2444,7 +2824,7 @@ document.getElementById("btn-voice-looks-right").addEventListener("click", () =>
|
|
| 2444 |
});
|
| 2445 |
showScreen("setup");
|
| 2446 |
});
|
| 2447 |
-
|
| 2448 |
const transcript = document.getElementById("voice-turn-transcript")?.value?.trim();
|
| 2449 |
if (!transcript || !state.pendingVoiceTurn) return;
|
| 2450 |
sendMessage(transcript, {
|
|
@@ -2456,32 +2836,47 @@ document.getElementById("btn-voice-turn-send").addEventListener("click", () => {
|
|
| 2456 |
},
|
| 2457 |
});
|
| 2458 |
});
|
| 2459 |
-
|
| 2460 |
-
|
| 2461 |
-
|
| 2462 |
-
|
| 2463 |
-
|
| 2464 |
-
|
| 2465 |
-
|
| 2466 |
-
|
| 2467 |
-
|
| 2468 |
-
|
| 2469 |
-
|
| 2470 |
-
|
| 2471 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2472 |
if (e.target === e.currentTarget) closePath80();
|
| 2473 |
});
|
|
|
|
|
|
|
|
|
|
| 2474 |
|
| 2475 |
-
|
| 2476 |
-
|
| 2477 |
-
|
| 2478 |
if (state.retryDrill) populateRetryDrill(state.retryDrill);
|
| 2479 |
});
|
| 2480 |
-
|
| 2481 |
closeRetryOverlay();
|
| 2482 |
showScreen("scorecard");
|
| 2483 |
});
|
| 2484 |
-
|
| 2485 |
closeRetryOverlay();
|
| 2486 |
resetBattle();
|
| 2487 |
});
|
|
@@ -2489,7 +2884,7 @@ document.getElementById("retry-overlay")?.addEventListener("click", (e) => {
|
|
| 2489 |
if (e.target === e.currentTarget) closeRetryOverlay();
|
| 2490 |
});
|
| 2491 |
|
| 2492 |
-
|
| 2493 |
document.getElementById("btn-end-battle").hidden = true;
|
| 2494 |
document.getElementById("btn-back-scorecard").hidden = false;
|
| 2495 |
document.getElementById("chat-form").hidden = true;
|
|
@@ -2497,7 +2892,7 @@ document.getElementById("btn-view-conversation").addEventListener("click", () =>
|
|
| 2497 |
openBattleConversationLog(true);
|
| 2498 |
});
|
| 2499 |
|
| 2500 |
-
|
| 2501 |
closeRoundsDrawer("battle-rounds-drawer");
|
| 2502 |
document.getElementById("btn-end-battle").hidden = false;
|
| 2503 |
document.getElementById("btn-back-scorecard").hidden = true;
|
|
@@ -2540,7 +2935,7 @@ document.querySelectorAll(".difficulty-card").forEach((card) => {
|
|
| 2540 |
});
|
| 2541 |
});
|
| 2542 |
|
| 2543 |
-
document.getElementById("chat-form").addEventListener("submit", (event) => {
|
| 2544 |
event.preventDefault();
|
| 2545 |
sendMessage();
|
| 2546 |
});
|
|
@@ -2652,3 +3047,5 @@ if (document.readyState === "loading") {
|
|
| 2652 |
} else {
|
| 2653 |
boot();
|
| 2654 |
}
|
|
|
|
|
|
|
|
|
| 23 |
dealBattleLog: [],
|
| 24 |
battleMetaSnapshot: null,
|
| 25 |
scorecardSnapshot: null,
|
| 26 |
+
dealScorecardData: null,
|
| 27 |
+
voiceEntrySource: "arena",
|
| 28 |
briefingMode: "quick",
|
| 29 |
briefStructured: false,
|
| 30 |
pitchExtractionConfidence: null,
|
| 31 |
+
pitchExtractionConfidenceScore: null,
|
| 32 |
+
lastStructuredPitchText: null,
|
| 33 |
+
lastStructuredPitchData: null,
|
| 34 |
battleConfidencePct: null,
|
| 35 |
};
|
| 36 |
|
|
|
|
| 67 |
const battleStatus = document.getElementById("battle-status");
|
| 68 |
const errorBanner = document.getElementById("error-banner");
|
| 69 |
|
| 70 |
+
function bindClick(id, handler) {
|
| 71 |
+
document.getElementById(id)?.addEventListener("click", handler);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
function showScreen(name) {
|
| 75 |
Object.entries(screens).forEach(([key, el]) => {
|
| 76 |
el.classList.toggle("active", key === name);
|
| 77 |
});
|
| 78 |
const app = document.getElementById("app");
|
| 79 |
+
app?.classList.toggle("app-arena-fullwidth", name === "battle" || name === "deal" || name === "scorecard" || name === "dealScorecard");
|
| 80 |
app?.classList.toggle("app-scorecard-fullwidth", name === "scorecard");
|
| 81 |
if (name === "landing" && landingIntroComplete) {
|
| 82 |
finalizeLandingIntroStatic();
|
|
|
|
| 129 |
step();
|
| 130 |
}
|
| 131 |
|
| 132 |
+
/* ---- Landing currency fall canvas ---- */
|
| 133 |
+
|
| 134 |
+
const PF_FALL_BILL_COUNT = 38;
|
| 135 |
+
|
| 136 |
+
function initPfFallCanvas() {
|
| 137 |
+
const canvas = document.getElementById("pfFallCanvas");
|
| 138 |
+
const landingScreen = document.getElementById("screen-landing");
|
| 139 |
+
const host = canvas?.parentElement;
|
| 140 |
+
if (!canvas || !host || !landingScreen) return;
|
| 141 |
+
|
| 142 |
+
const ctx = canvas.getContext("2d");
|
| 143 |
+
if (!ctx) return;
|
| 144 |
+
|
| 145 |
+
let width = 0;
|
| 146 |
+
let height = 0;
|
| 147 |
+
let bills = [];
|
| 148 |
+
let rafId = 0;
|
| 149 |
+
let running = false;
|
| 150 |
+
const reducedMotion = prefersReducedMotion();
|
| 151 |
+
|
| 152 |
+
const rand = (min, max) => min + Math.random() * (max - min);
|
| 153 |
+
|
| 154 |
+
const roundRectPath = (c, x, y, w, h, r) => {
|
| 155 |
+
const radius = Math.min(r, w / 2, h / 2);
|
| 156 |
+
c.beginPath();
|
| 157 |
+
c.moveTo(x + radius, y);
|
| 158 |
+
c.lineTo(x + w - radius, y);
|
| 159 |
+
c.quadraticCurveTo(x + w, y, x + w, y + radius);
|
| 160 |
+
c.lineTo(x + w, y + h - radius);
|
| 161 |
+
c.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
|
| 162 |
+
c.lineTo(x + radius, y + h);
|
| 163 |
+
c.quadraticCurveTo(x, y + h, x, y + h - radius);
|
| 164 |
+
c.lineTo(x, y + radius);
|
| 165 |
+
c.quadraticCurveTo(x, y, x + radius, y);
|
| 166 |
+
c.closePath();
|
| 167 |
+
};
|
| 168 |
+
|
| 169 |
+
const createBill = () => {
|
| 170 |
+
const gold = Math.random() < 0.7;
|
| 171 |
+
return {
|
| 172 |
+
x: rand(0, width),
|
| 173 |
+
y: rand(-30, height),
|
| 174 |
+
scale: rand(0.5, 1.2),
|
| 175 |
+
vy: rand(0.25, 0.8),
|
| 176 |
+
vx: rand(-0.2, 0.2),
|
| 177 |
+
vr: rand(-0.025, 0.025),
|
| 178 |
+
alpha: rand(0.12, 0.35),
|
| 179 |
+
rot: rand(0, Math.PI * 2),
|
| 180 |
+
rgb: gold ? [245, 200, 66] : [0, 230, 200],
|
| 181 |
+
strokeAlpha: gold ? 0.35 : 0.25,
|
| 182 |
+
};
|
| 183 |
+
};
|
| 184 |
+
|
| 185 |
+
const drawBill = (bill) => {
|
| 186 |
+
const w = 48 * bill.scale;
|
| 187 |
+
const h = 20 * bill.scale;
|
| 188 |
+
const [r, g, b] = bill.rgb;
|
| 189 |
+
const inset = 3 * bill.scale;
|
| 190 |
+
const radius = 3 * bill.scale;
|
| 191 |
+
|
| 192 |
+
ctx.save();
|
| 193 |
+
ctx.translate(bill.x, bill.y);
|
| 194 |
+
ctx.rotate(bill.rot);
|
| 195 |
+
ctx.globalAlpha = bill.alpha;
|
| 196 |
+
|
| 197 |
+
roundRectPath(ctx, -w / 2, -h / 2, w, h, radius);
|
| 198 |
+
ctx.fillStyle = `rgba(${r},${g},${b},0.04)`;
|
| 199 |
+
ctx.fill();
|
| 200 |
+
|
| 201 |
+
ctx.strokeStyle = `rgba(${r},${g},${b},${bill.strokeAlpha})`;
|
| 202 |
+
ctx.lineWidth = 1;
|
| 203 |
+
ctx.stroke();
|
| 204 |
+
|
| 205 |
+
roundRectPath(ctx, -w / 2 + inset, -h / 2 + inset, w - inset * 2, h - inset * 2, Math.max(1, radius - inset));
|
| 206 |
+
ctx.strokeStyle = `rgba(${r},${g},${b},0.2)`;
|
| 207 |
+
ctx.stroke();
|
| 208 |
+
|
| 209 |
+
ctx.font = `${Math.max(8, 11 * bill.scale)}px monospace`;
|
| 210 |
+
ctx.fillStyle = `rgba(${r},${g},${b},0.4)`;
|
| 211 |
+
ctx.textAlign = "left";
|
| 212 |
+
ctx.textBaseline = "middle";
|
| 213 |
+
ctx.fillText("₹", -w / 2 + inset + 2, 0);
|
| 214 |
+
|
| 215 |
+
ctx.restore();
|
| 216 |
+
};
|
| 217 |
+
|
| 218 |
+
const drawFrame = () => {
|
| 219 |
+
ctx.clearRect(0, 0, width, height);
|
| 220 |
+
for (const bill of bills) {
|
| 221 |
+
drawBill(bill);
|
| 222 |
+
}
|
| 223 |
+
};
|
| 224 |
+
|
| 225 |
+
const tick = () => {
|
| 226 |
+
if (!running) return;
|
| 227 |
+
ctx.clearRect(0, 0, width, height);
|
| 228 |
+
for (const bill of bills) {
|
| 229 |
+
bill.y += bill.vy;
|
| 230 |
+
bill.x += bill.vx;
|
| 231 |
+
bill.rot += bill.vr;
|
| 232 |
+
if (bill.y > height + 30) {
|
| 233 |
+
bill.y = -30;
|
| 234 |
+
bill.x = rand(0, width);
|
| 235 |
+
}
|
| 236 |
+
drawBill(bill);
|
| 237 |
+
}
|
| 238 |
+
rafId = requestAnimationFrame(tick);
|
| 239 |
+
};
|
| 240 |
+
|
| 241 |
+
const resize = () => {
|
| 242 |
+
const rect = host.getBoundingClientRect();
|
| 243 |
+
const dpr = window.devicePixelRatio || 1;
|
| 244 |
+
width = Math.max(1, rect.width);
|
| 245 |
+
height = Math.max(1, rect.height);
|
| 246 |
+
canvas.width = Math.floor(width * dpr);
|
| 247 |
+
canvas.height = Math.floor(height * dpr);
|
| 248 |
+
canvas.style.width = `${width}px`;
|
| 249 |
+
canvas.style.height = `${height}px`;
|
| 250 |
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
| 251 |
+
bills = Array.from({ length: PF_FALL_BILL_COUNT }, () => createBill());
|
| 252 |
+
if (reducedMotion || !running) {
|
| 253 |
+
drawFrame();
|
| 254 |
+
}
|
| 255 |
+
};
|
| 256 |
+
|
| 257 |
+
const startAnimation = () => {
|
| 258 |
+
if (reducedMotion || running) return;
|
| 259 |
+
running = true;
|
| 260 |
+
cancelAnimationFrame(rafId);
|
| 261 |
+
rafId = requestAnimationFrame(tick);
|
| 262 |
+
};
|
| 263 |
+
|
| 264 |
+
const stopAnimation = () => {
|
| 265 |
+
running = false;
|
| 266 |
+
cancelAnimationFrame(rafId);
|
| 267 |
+
rafId = 0;
|
| 268 |
+
};
|
| 269 |
+
|
| 270 |
+
const syncVisibility = () => {
|
| 271 |
+
if (landingScreen.classList.contains("active")) {
|
| 272 |
+
resize();
|
| 273 |
+
startAnimation();
|
| 274 |
+
} else {
|
| 275 |
+
stopAnimation();
|
| 276 |
+
}
|
| 277 |
+
};
|
| 278 |
+
|
| 279 |
+
resize();
|
| 280 |
+
window.addEventListener("resize", resize);
|
| 281 |
+
|
| 282 |
+
const observer = new MutationObserver(syncVisibility);
|
| 283 |
+
observer.observe(landingScreen, { attributes: true, attributeFilter: ["class"] });
|
| 284 |
+
|
| 285 |
+
if (landingScreen.classList.contains("active")) {
|
| 286 |
+
if (reducedMotion) {
|
| 287 |
+
drawFrame();
|
| 288 |
+
} else {
|
| 289 |
+
startAnimation();
|
| 290 |
+
}
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
function initLandingIntro() {
|
| 295 |
if (landingIntroRunning || landingIntroComplete) return;
|
| 296 |
landingIntroRunning = true;
|
|
|
|
| 340 |
return NEMOTRON_SCORECARD_SOURCES.has(String(src ?? "").trim());
|
| 341 |
}
|
| 342 |
|
| 343 |
+
function getScorecardSourceDisplay(data = {}) {
|
| 344 |
+
const src = String(data.scorecard_source ?? "").trim().toLowerCase();
|
| 345 |
+
const provider = String(data.provider ?? "").trim().toLowerCase();
|
| 346 |
+
const modelOk = data.model_ok === true;
|
| 347 |
+
|
| 348 |
+
if (isNemotronScorecardSource(src) || (modelOk && provider === "nvidia")) {
|
| 349 |
+
return {
|
| 350 |
+
show: true,
|
| 351 |
+
chip: "⚡ Nemotron",
|
| 352 |
+
badge: "Powered by NVIDIA Nemotron",
|
| 353 |
+
title: "Scoring source: NVIDIA Nemotron",
|
| 354 |
+
};
|
| 355 |
+
}
|
| 356 |
+
return { show: false };
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
const INTERNAL_ERROR_PATTERNS = [
|
| 360 |
/nvidia/i,
|
| 361 |
/max_tokens/i,
|
|
|
|
| 430 |
}
|
| 431 |
closeBriefFieldExpanded(card);
|
| 432 |
syncBriefToStartupForm();
|
| 433 |
+
recomputeBriefConfidence();
|
| 434 |
}
|
| 435 |
|
| 436 |
function cancelBriefFieldEdit(card) {
|
|
|
|
| 526 |
}
|
| 527 |
|
| 528 |
function getStartupPayload() {
|
| 529 |
+
if (state.briefStructured) {
|
| 530 |
syncBriefToStartupForm();
|
| 531 |
}
|
| 532 |
const data = new FormData(startupForm);
|
|
|
|
| 544 |
function setBriefingMode(mode) {
|
| 545 |
state.briefingMode = mode;
|
| 546 |
const isQuick = mode === "quick";
|
| 547 |
+
const previewVisible = isQuick && state.briefStructured;
|
| 548 |
|
| 549 |
document.getElementById("tab-quick-pitch")?.classList.toggle("active", isQuick);
|
| 550 |
document.getElementById("tab-advanced-briefing")?.classList.toggle("active", !isQuick);
|
|
|
|
| 552 |
document.getElementById("tab-advanced-briefing")?.setAttribute("aria-selected", String(!isQuick));
|
| 553 |
briefingLeftCol?.classList.toggle("mode-quick", isQuick);
|
| 554 |
briefingLeftCol?.classList.toggle("mode-advanced", !isQuick);
|
| 555 |
+
briefingLeftCol?.classList.toggle("mode-structured", previewVisible);
|
| 556 |
|
| 557 |
const advancedPanel = document.getElementById("panel-advanced-briefing");
|
| 558 |
if (advancedPanel) advancedPanel.hidden = isQuick;
|
|
|
|
| 569 |
const subtitle = document.getElementById("briefing-subtitle");
|
| 570 |
if (subtitle) {
|
| 571 |
subtitle.textContent = isQuick
|
| 572 |
+
? (previewVisible
|
| 573 |
+
? "Review your structured brief, then pick opponent and start."
|
| 574 |
+
: "Pitch naturally. We'll structure it before the judge attacks it.")
|
| 575 |
: "Want full control? Edit every field manually.";
|
| 576 |
}
|
| 577 |
}
|
|
|
|
| 579 |
function showBriefPreview(meta = {}) {
|
| 580 |
if (quickPitchPanel) quickPitchPanel.hidden = true;
|
| 581 |
if (briefPreviewPanel) briefPreviewPanel.hidden = false;
|
| 582 |
+
briefingLeftCol?.classList.add("mode-structured");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
|
| 584 |
const hintEl = document.getElementById("brief-preview-hint");
|
| 585 |
if (hintEl) {
|
|
|
|
| 600 |
state.briefStructured = false;
|
| 601 |
if (quickPitchPanel) quickPitchPanel.hidden = false;
|
| 602 |
if (briefPreviewPanel) briefPreviewPanel.hidden = true;
|
| 603 |
+
briefingLeftCol?.classList.remove("mode-structured");
|
| 604 |
document.getElementById("brief-preview-hint")?.setAttribute("hidden", "");
|
| 605 |
briefPreviewForm?.querySelectorAll(".brief-read-card.is-editing").forEach((card) => {
|
| 606 |
commitBriefFieldEdit(card);
|
|
|
|
| 622 |
return "low";
|
| 623 |
}
|
| 624 |
|
| 625 |
+
// JS mirror of Python calculate_structure_confidence — same weights, caps, regexes.
|
| 626 |
+
const _CONF_FIELD_WEIGHTS = { name:10, problem:15, target_users:12, solution:15, why_ai:10, traction:15, competitors:8, ask:10 };
|
| 627 |
+
const _CONF_FIELD_CAPS = [["problem",60],["solution",60],["target_users",70],["traction",74],["competitors",92],["why_ai",90],["ask",85]];
|
| 628 |
+
const _CONF_FILLER = new Set([
|
| 629 |
+
"not specified","n/a","none","unknown","tbd","-","",
|
| 630 |
+
"idk","i don't know","i dont know","not sure","na","no idea",
|
| 631 |
+
"dunno","nothing","?","??","???","yes","no","nope","yep",
|
| 632 |
+
"to be determined","to be decided","will update","coming soon",
|
| 633 |
+
]);
|
| 634 |
+
// Minimum word count for description fields — single-word noise like "idk" fails this.
|
| 635 |
+
const _CONF_MIN_WORDS = { problem:2, solution:2, why_ai:2, traction:2, target_users:2 };
|
| 636 |
+
const _CONF_USER_SEG_RE = /\b(college students?|university students?|indie developers?|small businesses?|enterprise|founders?|educators?|teachers?|researchers?|professionals?|teams?|parents?|teenagers?|consumers?|startup founders?)\b/i;
|
| 637 |
+
const _CONF_CONCRETE_ASK_RE = /(\$[\d,]+[kKmM]?|\d+[kK]\s*(?:usd|dollars?)?|mentorship|campus pilot|equity partner|co.?founder|sponsorship|strategic partner)/i;
|
| 638 |
+
|
| 639 |
+
function _calcConfidenceFromCtx(ctx, rawText) {
|
| 640 |
+
let score = 0;
|
| 641 |
+
const missing = [];
|
| 642 |
+
for (const [field, weight] of Object.entries(_CONF_FIELD_WEIGHTS)) {
|
| 643 |
+
const val = String(ctx[field] || "").trim();
|
| 644 |
+
const valLower = val.toLowerCase();
|
| 645 |
+
const minWords = _CONF_MIN_WORDS[field] ?? 1;
|
| 646 |
+
const filled = val && !_CONF_FILLER.has(valLower) && val.split(/\s+/).length >= minWords;
|
| 647 |
+
if (filled) score += weight;
|
| 648 |
+
else missing.push(field);
|
| 649 |
+
}
|
| 650 |
+
const text = String(rawText || "").trim();
|
| 651 |
+
let bonus = 0;
|
| 652 |
+
const numHits = (text.match(/\b\d[\d,]*\b/g) || []).length;
|
| 653 |
+
if (numHits >= 3) bonus += 10;
|
| 654 |
+
else if (numHits >= 1) bonus += 5;
|
| 655 |
+
if (_CONF_USER_SEG_RE.test(text)) bonus += 5;
|
| 656 |
+
if (_CONF_CONCRETE_ASK_RE.test(text)) bonus += 5;
|
| 657 |
+
bonus = Math.min(bonus, 20);
|
| 658 |
+
score = Math.min(score + bonus, 100);
|
| 659 |
+
for (const [field, cap] of _CONF_FIELD_CAPS) {
|
| 660 |
+
if (missing.includes(field)) score = Math.min(score, cap);
|
| 661 |
+
}
|
| 662 |
+
score = Math.max(0, Math.min(100, score));
|
| 663 |
+
return { confidence: score >= 75 ? "high" : score >= 45 ? "medium" : "low", confidence_score: score, missing_fields: missing };
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
function recomputeBriefConfidence() {
|
| 667 |
+
if (!briefPreviewForm) return;
|
| 668 |
+
const ctx = {};
|
| 669 |
+
for (const key of BRIEF_FIELD_KEYS) {
|
| 670 |
+
const inp = briefPreviewForm.querySelector(`[name="${key}"]`);
|
| 671 |
+
ctx[key] = (inp?.value || "").trim();
|
| 672 |
+
}
|
| 673 |
+
const result = _calcConfidenceFromCtx(ctx, state.lastStructuredPitchText || "");
|
| 674 |
+
state.pitchExtractionConfidence = result.confidence;
|
| 675 |
+
state.pitchExtractionConfidenceScore = result.confidence_score;
|
| 676 |
+
const hintEl = document.getElementById("brief-preview-hint");
|
| 677 |
+
if (hintEl) {
|
| 678 |
+
if (result.missing_fields.length) {
|
| 679 |
+
hintEl.textContent = `Not in your pitch: ${result.missing_fields.join(", ")}`;
|
| 680 |
+
hintEl.hidden = false;
|
| 681 |
+
} else {
|
| 682 |
+
hintEl.hidden = true;
|
| 683 |
+
}
|
| 684 |
+
}
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
function confidenceLevelToPct(level) {
|
| 688 |
const key = String(level || "medium").toLowerCase();
|
| 689 |
if (key === "high") return 76;
|
|
|
|
| 694 |
function syncPitchExtractionConfidence(meta = {}, startup = null) {
|
| 695 |
const level = extractionConfidenceLevel(meta) || confidenceFromStartupFields(startup) || "medium";
|
| 696 |
state.pitchExtractionConfidence = level;
|
| 697 |
+
// Prefer numeric score from backend (0-100) so battle HUD has a precise baseline.
|
| 698 |
+
state.pitchExtractionConfidenceScore = typeof meta.confidence_score === "number"
|
| 699 |
+
? meta.confidence_score
|
| 700 |
+
: confidenceLevelToPct(level);
|
| 701 |
return level;
|
| 702 |
}
|
| 703 |
|
|
|
|
| 705 |
state.briefStructured = true;
|
| 706 |
syncPitchExtractionConfidence(meta, startup);
|
| 707 |
fillStartupForm(startup);
|
| 708 |
+
syncBriefToStartupForm();
|
| 709 |
showBriefPreview(meta);
|
| 710 |
}
|
| 711 |
|
| 712 |
function resetSetupScreen() {
|
| 713 |
state.briefStructured = false;
|
| 714 |
state.pitchExtractionConfidence = null;
|
| 715 |
+
state.pitchExtractionConfidenceScore = null;
|
| 716 |
+
state.lastStructuredPitchText = null;
|
| 717 |
+
state.lastStructuredPitchData = null;
|
| 718 |
setBriefingMode("quick");
|
| 719 |
hideBriefPreview();
|
| 720 |
const quickText = document.getElementById("quick-pitch-text");
|
|
|
|
| 729 |
showErrorBanner("Type or paste your pitch first.");
|
| 730 |
return;
|
| 731 |
}
|
| 732 |
+
|
| 733 |
+
// Return cached result when the same pitch text is re-submitted — confidence must not
|
| 734 |
+
// change on repeated clicks for the same input (Part C: stable re-structure).
|
| 735 |
+
if (state.lastStructuredPitchText === pitchText && state.lastStructuredPitchData) {
|
| 736 |
+
fillBriefPreview(state.lastStructuredPitchData.startup_context, state.lastStructuredPitchData);
|
| 737 |
+
hideErrorBanner();
|
| 738 |
+
return;
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
try {
|
| 742 |
setGlobalLoading(true, "Structuring your pitch…");
|
| 743 |
state.startMode = "text";
|
|
|
|
| 746 |
showErrorBanner(data.error || "Could not structure your pitch. Try Advanced Briefing.");
|
| 747 |
return;
|
| 748 |
}
|
| 749 |
+
state.lastStructuredPitchText = pitchText;
|
| 750 |
+
state.lastStructuredPitchData = data;
|
| 751 |
fillBriefPreview(data.startup_context, data);
|
| 752 |
hideErrorBanner();
|
| 753 |
} catch (error) {
|
|
|
|
| 907 |
}
|
| 908 |
|
| 909 |
function refreshBattleConfidenceFromPressure(pressurePct, round = 1) {
|
| 910 |
+
// Use the numeric confidence_score (0-100) from structure-pitch when available so
|
| 911 |
+
// the battle readiness meter reflects actual brief quality, not just high/mid/low buckets.
|
| 912 |
+
const base = state.pitchExtractionConfidenceScore != null
|
| 913 |
+
? Math.max(25, Math.min(95, state.pitchExtractionConfidenceScore))
|
| 914 |
+
: confidenceLevelToPct(state.pitchExtractionConfidence || "medium");
|
| 915 |
const pressureDrag = Math.round(Number(pressurePct) * 0.25);
|
| 916 |
const roundDrag = Math.max(0, Number(round) - 1) * 2;
|
| 917 |
const ceiling = Math.max(8, base - pressureDrag - roundDrag);
|
|
|
|
| 927 |
const deltas = { strong: 8, partial: -2, weak: -12, non_answer: -20 };
|
| 928 |
const delta = deltas[String(quality || "").toLowerCase()] ?? 0;
|
| 929 |
if (!delta) return;
|
| 930 |
+
const fallbackBase = state.pitchExtractionConfidenceScore != null
|
| 931 |
+
? state.pitchExtractionConfidenceScore
|
| 932 |
+
: confidenceLevelToPct(state.pitchExtractionConfidence);
|
| 933 |
state.battleConfidencePct = Math.max(
|
| 934 |
8,
|
| 935 |
+
Math.min(92, (state.battleConfidencePct ?? fallbackBase) + delta),
|
| 936 |
);
|
| 937 |
renderConfidenceMeter(state.battleConfidencePct);
|
| 938 |
}
|
|
|
|
| 1358 |
|
| 1359 |
export async function startSession() {
|
| 1360 |
try {
|
| 1361 |
+
if (state.briefingMode === "quick" && !state.briefStructured) {
|
| 1362 |
+
showErrorBanner("Structure your pitch first, or switch to Advanced Briefing.");
|
| 1363 |
+
return;
|
| 1364 |
+
}
|
| 1365 |
+
|
| 1366 |
setGlobalLoading(true, "AI judge is preparing the first attack…");
|
| 1367 |
battleStatus.hidden = true;
|
| 1368 |
hideBattleReadiness();
|
|
|
|
| 1541 |
state.pendingDealVoiceTurn = null;
|
| 1542 |
state.startMode = "text";
|
| 1543 |
state.pitchExtractionConfidence = null;
|
| 1544 |
+
state.pitchExtractionConfidenceScore = null;
|
| 1545 |
state.battleConfidencePct = null;
|
| 1546 |
state.conversationLog = [];
|
| 1547 |
state.dealConversationLog = [];
|
| 1548 |
state.battleLog = [];
|
| 1549 |
state.dealBattleLog = [];
|
| 1550 |
+
state.dealScorecardData = null;
|
| 1551 |
resetLiveTurnTracking();
|
| 1552 |
chatWindow.innerHTML = "";
|
| 1553 |
if (dealChatWindow) dealChatWindow.innerHTML = "";
|
|
|
|
| 1796 |
const { strongest, weakest } = getStrongestWeakest(scores);
|
| 1797 |
const se = data.score_explanation ?? {};
|
| 1798 |
const explanation = se;
|
| 1799 |
+
const sourceDisplay = getScorecardSourceDisplay(data);
|
| 1800 |
+
|
| 1801 |
+
const chipSource = document.getElementById("chip-score-source");
|
| 1802 |
+
const sourceBadgeEl = document.getElementById("scorecard-source-badge");
|
| 1803 |
+
if (chipSource) {
|
| 1804 |
+
if (sourceDisplay.show) {
|
| 1805 |
+
chipSource.textContent = sourceDisplay.chip;
|
| 1806 |
+
chipSource.title = sourceDisplay.title ?? "";
|
| 1807 |
+
chipSource.hidden = false;
|
| 1808 |
+
} else {
|
| 1809 |
+
chipSource.textContent = "";
|
| 1810 |
+
chipSource.hidden = true;
|
| 1811 |
+
}
|
| 1812 |
+
}
|
| 1813 |
+
if (sourceBadgeEl) {
|
| 1814 |
+
sourceBadgeEl.hidden = true;
|
| 1815 |
+
sourceBadgeEl.textContent = "";
|
| 1816 |
+
}
|
| 1817 |
+
if (!sourceDisplay.show && data.fallback_reason) {
|
| 1818 |
+
console.info("Scorecard used local fallback:", data.fallback_reason, data.model_error || "");
|
| 1819 |
+
}
|
| 1820 |
|
| 1821 |
const opponent = PERSONA_LABELS[state.persona] ?? data.opponent ?? "AI Judge";
|
| 1822 |
const mode = DIFFICULTY_LABELS[state.difficultyProfile]
|
|
|
|
| 1851 |
: "↓ Weakest: —";
|
| 1852 |
}
|
| 1853 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1854 |
const fallbackWarnEl = document.getElementById("scorecard-fallback-warning");
|
| 1855 |
if (fallbackWarnEl) {
|
| 1856 |
fallbackWarnEl.textContent = "";
|
|
|
|
| 1947 |
if (answersEmpty) answersEmpty.hidden = !noAnswers;
|
| 1948 |
if (answersContent) answersContent.hidden = noAnswers;
|
| 1949 |
|
| 1950 |
+
if (noAnswers) {
|
| 1951 |
+
if (whyScoredEl && !whyScoredEl.textContent?.trim()) {
|
| 1952 |
+
whyScoredEl.textContent =
|
| 1953 |
+
"You ended before responding, so the scorecard can only grade the startup brief.";
|
| 1954 |
+
}
|
| 1955 |
+
if (whatStoppedEl && !whatStoppedEl.textContent?.trim()) {
|
| 1956 |
+
whatStoppedEl.textContent = "Submit at least one battle answer to unlock answer-level coaching.";
|
| 1957 |
+
}
|
| 1958 |
+
if (nextText && !nextText.textContent?.trim()) {
|
| 1959 |
+
nextText.textContent = "Start a new battle and defend at least one question.";
|
| 1960 |
+
}
|
| 1961 |
+
const prepPanel = document.querySelector('.sc-tab-panel[data-panel="prep"]');
|
| 1962 |
+
if (prepPanel) prepPanel.classList.add("sc-prep-empty");
|
| 1963 |
+
} else {
|
| 1964 |
+
document.querySelector('.sc-tab-panel[data-panel="prep"]')?.classList.remove("sc-prep-empty");
|
| 1965 |
+
}
|
| 1966 |
+
|
| 1967 |
state.scoreExplanation = data.score_explanation ?? null;
|
| 1968 |
|
| 1969 |
const pathBtn = document.getElementById("btn-path-to-80");
|
|
|
|
| 1988 |
weakest: weakest ? formatDimLabel(weakest[0]) : null,
|
| 1989 |
roundsCompleted: state.battleLog?.length ?? 0,
|
| 1990 |
};
|
| 1991 |
+
|
| 1992 |
+
updateScorecardCrossNav();
|
| 1993 |
}
|
| 1994 |
|
| 1995 |
const DEAL_TYPE_LABELS = {
|
|
|
|
| 2011 |
.replace(/Deal Round/i, "Negotiation");
|
| 2012 |
}
|
| 2013 |
|
| 2014 |
+
function canContinueToDealFromVerdict(verdict) {
|
| 2015 |
+
if (!verdict) return false;
|
| 2016 |
+
if (verdict.can_continue_to_deal) return true;
|
| 2017 |
+
const interest = verdict.interest_level || "";
|
| 2018 |
+
if ((interest === "mild_interest" || interest === "strong_interest") && verdict.deal_type && verdict.deal_type !== "none") {
|
| 2019 |
+
return true;
|
| 2020 |
+
}
|
| 2021 |
+
return false;
|
| 2022 |
+
}
|
| 2023 |
+
|
| 2024 |
+
function openVerdictModal() {
|
| 2025 |
+
const overlay = document.getElementById("verdict-overlay");
|
| 2026 |
+
if (!overlay || !state.judgeVerdict?.interest_level) return;
|
| 2027 |
+
overlay.hidden = false;
|
| 2028 |
+
document.body.style.overflow = "hidden";
|
| 2029 |
+
}
|
| 2030 |
+
|
| 2031 |
+
function closeVerdictModal() {
|
| 2032 |
+
const overlay = document.getElementById("verdict-overlay");
|
| 2033 |
+
if (overlay) overlay.hidden = true;
|
| 2034 |
+
document.body.style.overflow = "";
|
| 2035 |
+
}
|
| 2036 |
+
|
| 2037 |
function renderJudgeVerdict(verdict) {
|
| 2038 |
+
const verdictBtn = document.getElementById("btn-view-judge-verdict");
|
| 2039 |
if (!verdict || !verdict.interest_level) {
|
| 2040 |
+
if (verdictBtn) verdictBtn.hidden = true;
|
| 2041 |
return;
|
| 2042 |
}
|
| 2043 |
+
if (verdictBtn) verdictBtn.hidden = false;
|
| 2044 |
|
| 2045 |
const interest = verdict.interest_level || "no_interest";
|
| 2046 |
const personaLine = `${verdict.persona_name || "Judge"} — ${verdict.persona_type || ""}`.trim();
|
|
|
|
| 2069 |
}
|
| 2070 |
}
|
| 2071 |
|
| 2072 |
+
const lockedMsg = document.getElementById("verdict-deal-locked-msg");
|
| 2073 |
+
const dealAvailable = canContinueToDealFromVerdict(verdict);
|
| 2074 |
+
if (lockedMsg) {
|
| 2075 |
+
lockedMsg.hidden = dealAvailable || interest === "verdict_only";
|
| 2076 |
+
}
|
| 2077 |
+
|
| 2078 |
const actions = document.getElementById("verdict-actions");
|
| 2079 |
if (!actions) return;
|
| 2080 |
actions.innerHTML = "";
|
| 2081 |
|
| 2082 |
+
if (dealAvailable) {
|
| 2083 |
const btn = document.createElement("button");
|
| 2084 |
+
btn.className = "btn btn-deal-continue sc-btn-gold";
|
| 2085 |
+
btn.textContent = verdictNegotiationCta(verdict.next_step_label, "Continue to Deal Phase →");
|
| 2086 |
btn.type = "button";
|
| 2087 |
+
btn.addEventListener("click", () => {
|
| 2088 |
+
closeVerdictModal();
|
| 2089 |
+
startDealPhase();
|
| 2090 |
+
});
|
| 2091 |
actions.appendChild(btn);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2092 |
}
|
| 2093 |
}
|
| 2094 |
|
|
|
|
| 2264 |
}
|
| 2265 |
}
|
| 2266 |
|
| 2267 |
+
function updateScorecardCrossNav() {
|
| 2268 |
+
const btn = document.getElementById("btn-view-deal-scorecard");
|
| 2269 |
+
if (!btn) return;
|
| 2270 |
+
btn.hidden = !state.dealScorecardData;
|
| 2271 |
+
}
|
| 2272 |
+
|
| 2273 |
+
function showDealScorecardScreen() {
|
| 2274 |
+
if (!state.dealScorecardData) return;
|
| 2275 |
+
renderDealScorecard(state.dealScorecardData);
|
| 2276 |
+
showScreen("dealScorecard");
|
| 2277 |
+
}
|
| 2278 |
+
|
| 2279 |
function renderDealScorecard(data) {
|
| 2280 |
+
state.dealScorecardData = data;
|
| 2281 |
+
updateScorecardCrossNav();
|
| 2282 |
+
|
| 2283 |
const combined = data.combined_scorecard || {};
|
| 2284 |
const deal = data.deal_scorecard || {};
|
| 2285 |
|
|
|
|
| 2455 |
|| (vd.delivery_notes ?? []).find((n) => String(n).trim()) || "";
|
| 2456 |
|
| 2457 |
content.innerHTML = `
|
| 2458 |
+
<div class="voice-delivery-layout">
|
| 2459 |
+
<div class="voice-delivery-metrics" role="list">
|
| 2460 |
+
<div class="voice-delivery-box" role="listitem">
|
| 2461 |
+
<span class="voice-delivery-box-label">Voice turns</span>
|
| 2462 |
+
<strong class="voice-delivery-box-value">${vd.total_voice_turns ?? 0}</strong>
|
| 2463 |
+
</div>
|
| 2464 |
+
<div class="voice-delivery-box" role="listitem">
|
| 2465 |
+
<span class="voice-delivery-box-label">Filler words</span>
|
| 2466 |
+
<strong class="voice-delivery-box-value">${vd.total_filler_words ?? 0}</strong>
|
| 2467 |
+
</div>
|
| 2468 |
+
<div class="voice-delivery-box" role="listitem">
|
| 2469 |
+
<span class="voice-delivery-box-label">Common fillers</span>
|
| 2470 |
+
<strong class="voice-delivery-box-value">${escapeHtml(fillers)}</strong>
|
| 2471 |
+
</div>
|
| 2472 |
+
<div class="voice-delivery-box" role="listitem">
|
| 2473 |
+
<span class="voice-delivery-box-label">Pace</span>
|
| 2474 |
+
<strong class="voice-delivery-box-value">${escapeHtml(vd.average_pace ?? "—")}</strong>
|
| 2475 |
+
</div>
|
| 2476 |
+
</div>
|
| 2477 |
+
${overallNote ? `<p class="voice-delivery-overall">${escapeHtml(overallNote)}</p>` : ""}
|
| 2478 |
</div>
|
|
|
|
| 2479 |
`;
|
| 2480 |
|
| 2481 |
const notes = (vd.delivery_notes ?? []).filter((n) => {
|
|
|
|
| 2639 |
|
| 2640 |
function renderRetryResult(data) {
|
| 2641 |
const comp = data.comparison ?? {};
|
| 2642 |
+
const proj = data.projection ?? {};
|
| 2643 |
document.getElementById("retry-result-old").textContent = comp.old_answer_summary ?? data.original_answer ?? "";
|
| 2644 |
document.getElementById("retry-result-new").textContent = comp.new_answer_summary ?? data.retry_answer ?? "";
|
| 2645 |
document.getElementById("retry-what-improved").textContent = comp.what_improved ?? "";
|
| 2646 |
document.getElementById("retry-still-missing").textContent = comp.still_missing ?? "";
|
| 2647 |
document.getElementById("retry-specific-tip").textContent = comp.specific_tip ?? "";
|
| 2648 |
|
| 2649 |
+
// Prefer normalized projection fields; fall back to legacy comparison fields.
|
| 2650 |
+
const before = proj.old_dimension_score ?? comp.estimated_dimension_before ?? 0;
|
| 2651 |
+
const after = proj.new_dimension_score ?? comp.estimated_dimension_after ?? before;
|
| 2652 |
document.getElementById("retry-dim-estimate").textContent =
|
| 2653 |
`${formatDimLabel(data.dimension)}: ${before} → ${after}`;
|
| 2654 |
+
|
| 2655 |
+
// Safety floor: never display a baseline lower than what the scorecard shows.
|
| 2656 |
+
// If the backend returns a projection baseline below the visible scorecard overall,
|
| 2657 |
+
// clamp up to the scorecard snapshot so the user never sees "score went down".
|
| 2658 |
+
const snapshotOverall = state.scorecardSnapshot?.overall ?? null;
|
| 2659 |
+
const rawOriginalOverall = proj.original_overall_score ?? snapshotOverall ?? "—";
|
| 2660 |
+
const originalOverall = (
|
| 2661 |
+
typeof rawOriginalOverall === "number" &&
|
| 2662 |
+
typeof snapshotOverall === "number" &&
|
| 2663 |
+
rawOriginalOverall < snapshotOverall
|
| 2664 |
+
) ? snapshotOverall : rawOriginalOverall;
|
| 2665 |
+
|
| 2666 |
+
const rawProjected = proj.projected_overall_score ?? originalOverall;
|
| 2667 |
+
const projectedOverall = (
|
| 2668 |
+
typeof rawProjected === "number" && typeof originalOverall === "number"
|
| 2669 |
+
) ? Math.max(rawProjected, originalOverall) : rawProjected;
|
| 2670 |
+
|
| 2671 |
+
const projectedDelta = (
|
| 2672 |
+
typeof projectedOverall === "number" && typeof originalOverall === "number"
|
| 2673 |
+
) ? Math.max(0, projectedOverall - originalOverall) : (proj.projected_overall_delta ?? 0);
|
| 2674 |
+
|
| 2675 |
+
const liftEl = document.getElementById("retry-overall-lift");
|
| 2676 |
+
if (liftEl) {
|
| 2677 |
+
if (projectedDelta > 0) {
|
| 2678 |
+
liftEl.textContent = `Projected overall: ${originalOverall} → ${projectedOverall} (+${projectedDelta})`;
|
| 2679 |
+
} else {
|
| 2680 |
+
liftEl.textContent = "No projected lift yet — keep practicing this dimension.";
|
| 2681 |
+
}
|
| 2682 |
+
}
|
| 2683 |
+
|
| 2684 |
+
const noteEl = document.getElementById("retry-projection-note");
|
| 2685 |
+
if (noteEl) {
|
| 2686 |
+
noteEl.textContent = "Training projection only — your original scorecard stays unchanged.";
|
| 2687 |
+
noteEl.hidden = false;
|
| 2688 |
+
}
|
| 2689 |
|
| 2690 |
const verdictEl = document.getElementById("retry-verdict-badge");
|
| 2691 |
+
const verdict = projectedDelta > 0
|
| 2692 |
+
? (projectedDelta >= 3 ? "improved" : "slightly_improved")
|
| 2693 |
+
: (comp.verdict ?? "needs_more_work");
|
| 2694 |
const verdictLabels = {
|
| 2695 |
improved: "Improved",
|
| 2696 |
slightly_improved: "Slightly improved",
|
| 2697 |
+
needs_more_work: "No lift yet",
|
| 2698 |
};
|
| 2699 |
if (verdictEl) {
|
| 2700 |
verdictEl.textContent = verdictLabels[verdict] ?? verdict;
|
|
|
|
| 2736 |
return;
|
| 2737 |
}
|
| 2738 |
renderRetryResult(data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2739 |
hideErrorBanner();
|
| 2740 |
} catch (error) {
|
| 2741 |
console.error(error);
|
|
|
|
| 2755 |
startRetryDrill();
|
| 2756 |
}
|
| 2757 |
|
| 2758 |
+
bindClick("btn-load-sample", loadSample);
|
| 2759 |
+
bindClick("btn-load-sample-setup", loadSample);
|
| 2760 |
+
bindClick("btn-go-setup", () => {
|
| 2761 |
+
showScreen("startMethod");
|
|
|
|
| 2762 |
});
|
| 2763 |
+
bindClick("btn-back-landing", () => {
|
| 2764 |
resetSetupScreen();
|
| 2765 |
showScreen("landing");
|
| 2766 |
});
|
| 2767 |
+
bindClick("tab-quick-pitch", () => setBriefingMode("quick"));
|
| 2768 |
+
bindClick("tab-advanced-briefing", () => setBriefingMode("advanced"));
|
| 2769 |
+
bindClick("btn-structure-pitch", structurePitch);
|
| 2770 |
+
bindClick("btn-record-voice-setup", () => {
|
| 2771 |
state.startMode = "voice";
|
| 2772 |
+
state.voiceEntrySource = "briefing";
|
| 2773 |
showScreen("voicePitch");
|
| 2774 |
});
|
| 2775 |
+
bindClick("btn-looks-good-start", () => {
|
| 2776 |
syncBriefToStartupForm();
|
| 2777 |
startSession();
|
| 2778 |
});
|
| 2779 |
+
bindClick("btn-restructure-pitch", () => {
|
| 2780 |
hideBriefPreview();
|
| 2781 |
document.getElementById("quick-pitch-text")?.focus();
|
| 2782 |
});
|
| 2783 |
+
bindClick("btn-start-back-landing", () => showScreen("landing"));
|
| 2784 |
|
| 2785 |
+
bindClick("btn-start-text", () => {
|
| 2786 |
state.startMode = "text";
|
| 2787 |
+
state.voiceEntrySource = "arena";
|
| 2788 |
+
resetSetupScreen();
|
| 2789 |
+
showScreen("setup");
|
| 2790 |
});
|
| 2791 |
+
|
| 2792 |
+
bindClick("btn-start-voice", () => {
|
| 2793 |
state.startMode = "voice";
|
| 2794 |
+
state.voiceEntrySource = "arena";
|
| 2795 |
+
showScreen("voicePitch");
|
| 2796 |
});
|
| 2797 |
+
|
| 2798 |
+
bindClick("btn-voice-pitch-back", () => {
|
| 2799 |
+
if (state.voiceEntrySource === "briefing") {
|
| 2800 |
+
showScreen("setup");
|
| 2801 |
+
return;
|
| 2802 |
+
}
|
| 2803 |
+
showScreen("startMethod");
|
| 2804 |
});
|
| 2805 |
+
bindClick("btn-voice-edit-manual", () => {
|
|
|
|
| 2806 |
const form = document.getElementById("voice-extract-form");
|
| 2807 |
const data = new FormData(form);
|
| 2808 |
applyVoicePitchToBriefing({
|
|
|
|
| 2813 |
setBriefingMode("advanced");
|
| 2814 |
showScreen("setup");
|
| 2815 |
});
|
| 2816 |
+
bindClick("btn-voice-looks-right", () => {
|
| 2817 |
const form = document.getElementById("voice-extract-form");
|
| 2818 |
const data = new FormData(form);
|
| 2819 |
applyVoicePitchToBriefing({
|
|
|
|
| 2824 |
});
|
| 2825 |
showScreen("setup");
|
| 2826 |
});
|
| 2827 |
+
bindClick("btn-voice-turn-send", () => {
|
| 2828 |
const transcript = document.getElementById("voice-turn-transcript")?.value?.trim();
|
| 2829 |
if (!transcript || !state.pendingVoiceTurn) return;
|
| 2830 |
sendMessage(transcript, {
|
|
|
|
| 2836 |
},
|
| 2837 |
});
|
| 2838 |
});
|
| 2839 |
+
bindClick("btn-start-battle", startSession);
|
| 2840 |
+
bindClick("btn-end-battle", endBattle);
|
| 2841 |
+
bindClick("btn-reset", resetBattle);
|
| 2842 |
+
bindClick("btn-back-setup", () => showScreen("setup"));
|
| 2843 |
+
bindClick("btn-path-to-80", openPath80);
|
| 2844 |
+
bindClick("btn-close-path80", closePath80);
|
| 2845 |
+
bindClick("btn-close-path80-bottom", closePath80);
|
| 2846 |
+
bindClick("btn-retry-question", openRetryFromPath80);
|
| 2847 |
+
bindClick("btn-scorecard-retry", startRetryDrill);
|
| 2848 |
+
bindClick("btn-prep-retry", startRetryDrill);
|
| 2849 |
+
bindClick("btn-answers-retry", startRetryDrill);
|
| 2850 |
+
bindClick("btn-answers-new-battle", resetBattle);
|
| 2851 |
+
bindClick("btn-view-judge-verdict", openVerdictModal);
|
| 2852 |
+
bindClick("btn-view-deal-scorecard", showDealScorecardScreen);
|
| 2853 |
+
bindClick("btn-close-verdict", closeVerdictModal);
|
| 2854 |
+
bindClick("btn-close-verdict-bottom", closeVerdictModal);
|
| 2855 |
+
bindClick("btn-verdict-retry", () => {
|
| 2856 |
+
closeVerdictModal();
|
| 2857 |
+
startRetryDrill();
|
| 2858 |
+
});
|
| 2859 |
+
bindClick("btn-verdict-new-battle", () => {
|
| 2860 |
+
closeVerdictModal();
|
| 2861 |
+
resetBattle();
|
| 2862 |
+
});
|
| 2863 |
+
document.getElementById("path80-overlay")?.addEventListener("click", (e) => {
|
| 2864 |
if (e.target === e.currentTarget) closePath80();
|
| 2865 |
});
|
| 2866 |
+
document.getElementById("verdict-overlay")?.addEventListener("click", (e) => {
|
| 2867 |
+
if (e.target === e.currentTarget) closeVerdictModal();
|
| 2868 |
+
});
|
| 2869 |
|
| 2870 |
+
bindClick("btn-close-retry", closeRetryOverlay);
|
| 2871 |
+
bindClick("btn-submit-retry", submitRetryAnswer);
|
| 2872 |
+
bindClick("btn-retry-again", () => {
|
| 2873 |
if (state.retryDrill) populateRetryDrill(state.retryDrill);
|
| 2874 |
});
|
| 2875 |
+
bindClick("btn-retry-back-scorecard", () => {
|
| 2876 |
closeRetryOverlay();
|
| 2877 |
showScreen("scorecard");
|
| 2878 |
});
|
| 2879 |
+
bindClick("btn-retry-new-battle", () => {
|
| 2880 |
closeRetryOverlay();
|
| 2881 |
resetBattle();
|
| 2882 |
});
|
|
|
|
| 2884 |
if (e.target === e.currentTarget) closeRetryOverlay();
|
| 2885 |
});
|
| 2886 |
|
| 2887 |
+
bindClick("btn-view-conversation", () => {
|
| 2888 |
document.getElementById("btn-end-battle").hidden = true;
|
| 2889 |
document.getElementById("btn-back-scorecard").hidden = false;
|
| 2890 |
document.getElementById("chat-form").hidden = true;
|
|
|
|
| 2892 |
openBattleConversationLog(true);
|
| 2893 |
});
|
| 2894 |
|
| 2895 |
+
bindClick("btn-back-scorecard", () => {
|
| 2896 |
closeRoundsDrawer("battle-rounds-drawer");
|
| 2897 |
document.getElementById("btn-end-battle").hidden = false;
|
| 2898 |
document.getElementById("btn-back-scorecard").hidden = true;
|
|
|
|
| 2935 |
});
|
| 2936 |
});
|
| 2937 |
|
| 2938 |
+
document.getElementById("chat-form")?.addEventListener("submit", (event) => {
|
| 2939 |
event.preventDefault();
|
| 2940 |
sendMessage();
|
| 2941 |
});
|
|
|
|
| 3047 |
} else {
|
| 3048 |
boot();
|
| 3049 |
}
|
| 3050 |
+
|
| 3051 |
+
initPfFallCanvas();
|
frontend/styles.css
CHANGED
|
@@ -26,9 +26,18 @@
|
|
| 26 |
box-sizing: border-box;
|
| 27 |
}
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
body {
|
| 30 |
margin: 0;
|
| 31 |
min-height: 100vh;
|
|
|
|
|
|
|
|
|
|
| 32 |
font-family: "Segoe UI", system-ui, sans-serif;
|
| 33 |
color: var(--text);
|
| 34 |
background: #09090f;
|
|
@@ -47,9 +56,10 @@ body {
|
|
| 47 |
|
| 48 |
.app {
|
| 49 |
position: relative;
|
| 50 |
-
|
|
|
|
| 51 |
margin: 0 auto;
|
| 52 |
-
padding:
|
| 53 |
}
|
| 54 |
|
| 55 |
.app:has(#screen-battle.active),
|
|
@@ -123,7 +133,8 @@ h1 {
|
|
| 123 |
.arena-landing {
|
| 124 |
position: relative;
|
| 125 |
width: 100vw;
|
| 126 |
-
min-height:
|
|
|
|
| 127 |
margin-left: calc(50% - 50vw);
|
| 128 |
margin-right: calc(50% - 50vw);
|
| 129 |
overflow: hidden;
|
|
@@ -736,7 +747,7 @@ h1 {
|
|
| 736 |
position: relative;
|
| 737 |
z-index: 2;
|
| 738 |
text-align: center;
|
| 739 |
-
padding:
|
| 740 |
max-width: 820px;
|
| 741 |
width: 100%;
|
| 742 |
}
|
|
@@ -766,8 +777,8 @@ h1 {
|
|
| 766 |
}
|
| 767 |
|
| 768 |
.arena-title {
|
| 769 |
-
margin: 0 0
|
| 770 |
-
font-size: clamp(
|
| 771 |
font-weight: 800;
|
| 772 |
letter-spacing: 0.03em;
|
| 773 |
line-height: 1.02;
|
|
@@ -780,8 +791,8 @@ h1 {
|
|
| 780 |
|
| 781 |
/* Pass 2 — hook lines */
|
| 782 |
.arena-hook {
|
| 783 |
-
min-height:
|
| 784 |
-
margin-bottom: 0.
|
| 785 |
}
|
| 786 |
|
| 787 |
.arena-hook-line {
|
|
@@ -815,7 +826,7 @@ h1 {
|
|
| 815 |
|
| 816 |
.arena-support {
|
| 817 |
max-width: 520px;
|
| 818 |
-
margin: 0 auto
|
| 819 |
font-size: clamp(0.88rem, 2vw, 0.95rem);
|
| 820 |
line-height: 1.5;
|
| 821 |
color: var(--text-muted);
|
|
@@ -892,7 +903,8 @@ h1 {
|
|
| 892 |
flex-wrap: wrap;
|
| 893 |
gap: 0.4rem;
|
| 894 |
justify-content: center;
|
| 895 |
-
margin-top:
|
|
|
|
| 896 |
opacity: 0;
|
| 897 |
transform: translateY(8px);
|
| 898 |
transition: opacity 0.5s ease 0.12s, transform 0.5s ease 0.12s;
|
|
@@ -922,8 +934,9 @@ h1 {
|
|
| 922 |
}
|
| 923 |
|
| 924 |
.arena-landing-footer {
|
| 925 |
-
margin-top: 1.
|
| 926 |
-
padding-top: 1.
|
|
|
|
| 927 |
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 928 |
opacity: 0;
|
| 929 |
transition: opacity 0.5s ease 0.2s;
|
|
@@ -1209,14 +1222,14 @@ textarea {
|
|
| 1209 |
|
| 1210 |
.persona-grid {
|
| 1211 |
display: grid;
|
| 1212 |
-
grid-template-columns: repeat(auto-fit, minmax(
|
| 1213 |
-
gap: 0.
|
| 1214 |
-
margin-top:
|
| 1215 |
}
|
| 1216 |
|
| 1217 |
.persona-card {
|
| 1218 |
text-align: left;
|
| 1219 |
-
padding:
|
| 1220 |
border-radius: 14px;
|
| 1221 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 1222 |
background: rgba(0, 0, 0, 0.22);
|
|
@@ -1243,7 +1256,7 @@ textarea {
|
|
| 1243 |
}
|
| 1244 |
|
| 1245 |
.difficulty-selector {
|
| 1246 |
-
margin-top:
|
| 1247 |
}
|
| 1248 |
|
| 1249 |
.difficulty-selector h3 {
|
|
@@ -1256,13 +1269,13 @@ textarea {
|
|
| 1256 |
|
| 1257 |
.difficulty-grid {
|
| 1258 |
display: grid;
|
| 1259 |
-
grid-template-columns: repeat(auto-fit, minmax(
|
| 1260 |
-
gap: 0.
|
| 1261 |
}
|
| 1262 |
|
| 1263 |
.difficulty-card {
|
| 1264 |
text-align: left;
|
| 1265 |
-
padding: 0.
|
| 1266 |
border-radius: 12px;
|
| 1267 |
background: rgba(255, 255, 255, 0.04);
|
| 1268 |
border: 1px solid var(--border);
|
|
@@ -1635,8 +1648,8 @@ textarea {
|
|
| 1635 |
position: relative;
|
| 1636 |
z-index: 1;
|
| 1637 |
width: min(1180px, 100%);
|
| 1638 |
-
height: min(
|
| 1639 |
-
max-height: calc(100dvh - 24px);
|
| 1640 |
display: flex;
|
| 1641 |
flex-direction: column;
|
| 1642 |
border: 1px solid rgba(78, 205, 196, 0.2);
|
|
@@ -2832,14 +2845,14 @@ body.rounds-drawer-open {
|
|
| 2832 |
.battle-arena.arena-shell {
|
| 2833 |
position: relative;
|
| 2834 |
z-index: 1;
|
| 2835 |
-
width: min(
|
| 2836 |
-
max-width:
|
| 2837 |
min-width: 0;
|
| 2838 |
margin: 0 auto;
|
| 2839 |
-
min-height:
|
| 2840 |
display: grid;
|
| 2841 |
grid-template-columns: 280px minmax(0, 1fr);
|
| 2842 |
-
gap:
|
| 2843 |
align-items: stretch;
|
| 2844 |
}
|
| 2845 |
|
|
@@ -2848,21 +2861,37 @@ body.rounds-drawer-open {
|
|
| 2848 |
margin: 0 auto;
|
| 2849 |
}
|
| 2850 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2851 |
.briefing-shell-wide {
|
| 2852 |
-
max-width:
|
|
|
|
|
|
|
| 2853 |
}
|
| 2854 |
|
| 2855 |
.briefing-mode-tabs {
|
| 2856 |
display: flex;
|
| 2857 |
-
gap: 0.
|
| 2858 |
-
margin-bottom:
|
| 2859 |
flex-wrap: wrap;
|
| 2860 |
}
|
| 2861 |
|
| 2862 |
.briefing-mode-tab {
|
| 2863 |
flex: 1;
|
| 2864 |
min-width: 140px;
|
| 2865 |
-
padding: 0.
|
| 2866 |
border-radius: 12px;
|
| 2867 |
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 2868 |
background: rgba(0, 0, 0, 0.25);
|
|
@@ -2887,21 +2916,39 @@ body.rounds-drawer-open {
|
|
| 2887 |
}
|
| 2888 |
|
| 2889 |
.briefing-grid-wide {
|
| 2890 |
-
grid-template-columns: minmax(0, 1.
|
| 2891 |
-
gap:
|
| 2892 |
align-items: stretch;
|
| 2893 |
}
|
| 2894 |
|
| 2895 |
.briefing-left-col {
|
| 2896 |
-
display:
|
| 2897 |
-
|
| 2898 |
-
|
| 2899 |
min-width: 0;
|
| 2900 |
overflow: visible;
|
| 2901 |
}
|
| 2902 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2903 |
.quick-pitch-panel {
|
| 2904 |
-
padding: 1.
|
| 2905 |
}
|
| 2906 |
|
| 2907 |
.quick-pitch-label {
|
|
@@ -2915,15 +2962,16 @@ body.rounds-drawer-open {
|
|
| 2915 |
|
| 2916 |
.quick-pitch-textarea {
|
| 2917 |
width: 100%;
|
| 2918 |
-
min-height:
|
| 2919 |
-
|
| 2920 |
-
|
|
|
|
| 2921 |
border: 1px solid rgba(255, 255, 255, 0.12);
|
| 2922 |
background: rgba(0, 0, 0, 0.35);
|
| 2923 |
color: var(--text);
|
| 2924 |
-
font-size:
|
| 2925 |
-
line-height: 1.
|
| 2926 |
-
resize:
|
| 2927 |
box-shadow: inset 0 2px 12px rgba(0, 0, 0, 0.25);
|
| 2928 |
}
|
| 2929 |
|
|
@@ -2942,8 +2990,8 @@ body.rounds-drawer-open {
|
|
| 2942 |
.quick-pitch-actions {
|
| 2943 |
display: flex;
|
| 2944 |
flex-direction: column;
|
| 2945 |
-
gap: 0.
|
| 2946 |
-
margin-top:
|
| 2947 |
}
|
| 2948 |
|
| 2949 |
.quick-pitch-actions .btn-wide {
|
|
@@ -2963,15 +3011,37 @@ body.rounds-drawer-open {
|
|
| 2963 |
}
|
| 2964 |
|
| 2965 |
.quick-pitch-hint {
|
| 2966 |
-
margin: 0.
|
| 2967 |
-
font-size: 0.
|
| 2968 |
color: rgba(244, 211, 94, 0.85);
|
| 2969 |
}
|
| 2970 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2971 |
.brief-preview-panel {
|
| 2972 |
-
padding: 1.
|
| 2973 |
overflow: visible;
|
| 2974 |
position: relative;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2975 |
}
|
| 2976 |
|
| 2977 |
.brief-preview-panel.is-editing-active::before {
|
|
@@ -2985,7 +3055,7 @@ body.rounds-drawer-open {
|
|
| 2985 |
}
|
| 2986 |
|
| 2987 |
.brief-preview-header {
|
| 2988 |
-
margin-bottom: 0.
|
| 2989 |
}
|
| 2990 |
|
| 2991 |
.brief-preview-title-row {
|
|
@@ -3001,53 +3071,27 @@ body.rounds-drawer-open {
|
|
| 3001 |
}
|
| 3002 |
|
| 3003 |
.brief-preview-helper {
|
| 3004 |
-
margin: 0.
|
| 3005 |
color: var(--muted);
|
| 3006 |
-
font-size: 0.
|
| 3007 |
}
|
| 3008 |
|
| 3009 |
.brief-preview-hint {
|
| 3010 |
-
margin: 0.
|
| 3011 |
-
font-size: 0.
|
| 3012 |
color: rgba(244, 211, 94, 0.85);
|
| 3013 |
}
|
| 3014 |
|
| 3015 |
-
.brief-confidence-chip {
|
| 3016 |
-
display: inline-block;
|
| 3017 |
-
padding: 0.25rem 0.65rem;
|
| 3018 |
-
border-radius: 999px;
|
| 3019 |
-
font-size: 0.72rem;
|
| 3020 |
-
font-weight: 700;
|
| 3021 |
-
letter-spacing: 0.06em;
|
| 3022 |
-
text-transform: uppercase;
|
| 3023 |
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
| 3024 |
-
color: var(--muted);
|
| 3025 |
-
}
|
| 3026 |
-
|
| 3027 |
-
.brief-confidence-chip.confidence-high {
|
| 3028 |
-
border-color: rgba(94, 244, 160, 0.45);
|
| 3029 |
-
color: #7dffb8;
|
| 3030 |
-
}
|
| 3031 |
-
|
| 3032 |
-
.brief-confidence-chip.confidence-medium {
|
| 3033 |
-
border-color: rgba(244, 211, 94, 0.45);
|
| 3034 |
-
color: var(--gold);
|
| 3035 |
-
}
|
| 3036 |
-
|
| 3037 |
-
.brief-confidence-chip.confidence-low {
|
| 3038 |
-
border-color: rgba(255, 140, 120, 0.45);
|
| 3039 |
-
color: #ffb09a;
|
| 3040 |
-
}
|
| 3041 |
-
|
| 3042 |
.brief-preview-grid {
|
| 3043 |
display: grid;
|
| 3044 |
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 3045 |
-
gap: 0.
|
|
|
|
| 3046 |
}
|
| 3047 |
|
| 3048 |
.brief-read-card {
|
| 3049 |
-
padding: 0.
|
| 3050 |
-
border-radius:
|
| 3051 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 3052 |
background: rgba(0, 0, 0, 0.22);
|
| 3053 |
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s, transform 0.2s;
|
|
@@ -3207,8 +3251,9 @@ body.rounds-drawer-open {
|
|
| 3207 |
.brief-preview-actions {
|
| 3208 |
display: flex;
|
| 3209 |
flex-direction: column;
|
| 3210 |
-
gap: 0.
|
| 3211 |
-
margin-top:
|
|
|
|
| 3212 |
}
|
| 3213 |
|
| 3214 |
.brief-preview-actions .btn-wide {
|
|
@@ -3216,13 +3261,116 @@ body.rounds-drawer-open {
|
|
| 3216 |
}
|
| 3217 |
|
| 3218 |
.advanced-briefing-panel {
|
| 3219 |
-
padding: 1.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3220 |
}
|
| 3221 |
|
| 3222 |
-
.advanced-briefing-
|
| 3223 |
-
|
| 3224 |
-
|
| 3225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3226 |
}
|
| 3227 |
|
| 3228 |
.briefing-opponent-note {
|
|
@@ -3238,15 +3386,17 @@ body.rounds-drawer-open {
|
|
| 3238 |
flex-direction: column;
|
| 3239 |
align-self: stretch;
|
| 3240 |
min-height: 100%;
|
|
|
|
| 3241 |
gap: 0;
|
|
|
|
| 3242 |
}
|
| 3243 |
|
| 3244 |
.briefing-opponent-footer {
|
| 3245 |
margin-top: auto;
|
| 3246 |
-
padding-top:
|
| 3247 |
display: flex;
|
| 3248 |
flex-direction: column;
|
| 3249 |
-
gap: 0.
|
| 3250 |
}
|
| 3251 |
|
| 3252 |
.briefing-opponent-panel .btn-arena-start {
|
|
@@ -3262,7 +3412,7 @@ body.rounds-drawer-open {
|
|
| 3262 |
justify-content: space-between;
|
| 3263 |
align-items: flex-start;
|
| 3264 |
gap: 1rem;
|
| 3265 |
-
margin-bottom:
|
| 3266 |
}
|
| 3267 |
|
| 3268 |
.briefing-title {
|
|
@@ -4697,8 +4847,8 @@ body.rounds-drawer-open {
|
|
| 4697 |
|
| 4698 |
.voice-delivery-summary {
|
| 4699 |
display: grid;
|
| 4700 |
-
grid-template-columns: repeat(
|
| 4701 |
-
gap: 0.
|
| 4702 |
}
|
| 4703 |
|
| 4704 |
.voice-wave-decor {
|
|
@@ -5760,41 +5910,160 @@ body.rounds-drawer-open {
|
|
| 5760 |
.outcome-strong_win { background: rgba(74, 222, 128, 0.18); color: #86efac; }
|
| 5761 |
.outcome-weak_concession { background: rgba(248, 113, 113, 0.18); color: #fca5a5; }
|
| 5762 |
|
| 5763 |
-
/* Voice mode — Phase 7 */
|
| 5764 |
-
.start-
|
| 5765 |
-
|
| 5766 |
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 5767 |
-
gap: 0.85rem;
|
| 5768 |
-
margin: 1rem 0 1.25rem;
|
| 5769 |
}
|
| 5770 |
|
| 5771 |
-
.start-
|
| 5772 |
-
|
| 5773 |
-
|
| 5774 |
-
|
| 5775 |
-
|
| 5776 |
-
background: rgba(0, 0, 0, 0.2);
|
| 5777 |
-
color: inherit;
|
| 5778 |
-
cursor: pointer;
|
| 5779 |
-
transition: border-color 0.2s, box-shadow 0.2s;
|
| 5780 |
}
|
| 5781 |
|
| 5782 |
-
.start-
|
| 5783 |
-
|
| 5784 |
-
|
|
|
|
|
|
|
|
|
|
| 5785 |
}
|
| 5786 |
|
| 5787 |
-
.start-
|
| 5788 |
margin: 0.35rem 0 0.25rem;
|
| 5789 |
-
font-size:
|
| 5790 |
}
|
| 5791 |
|
| 5792 |
-
.start-
|
| 5793 |
margin: 0;
|
| 5794 |
color: var(--muted);
|
| 5795 |
-
font-size: 0.
|
| 5796 |
-
line-height: 1.
|
| 5797 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5798 |
|
| 5799 |
.start-method-icon {
|
| 5800 |
font-size: 1.4rem;
|
|
@@ -5993,41 +6262,108 @@ body.rounds-drawer-open {
|
|
| 5993 |
margin-top: 0.5rem;
|
| 5994 |
}
|
| 5995 |
|
| 5996 |
-
.voice-delivery-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5997 |
display: grid;
|
| 5998 |
-
grid-template-columns: repeat(
|
| 5999 |
-
gap: 0.
|
| 6000 |
}
|
| 6001 |
|
| 6002 |
-
.voice-delivery-
|
| 6003 |
-
|
| 6004 |
-
|
| 6005 |
-
|
| 6006 |
-
border: 1px solid rgba(
|
|
|
|
| 6007 |
}
|
| 6008 |
|
| 6009 |
-
.voice-delivery-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6010 |
display: block;
|
| 6011 |
-
font-size: 0.
|
| 6012 |
-
|
|
|
|
| 6013 |
text-transform: uppercase;
|
| 6014 |
-
|
| 6015 |
}
|
| 6016 |
|
| 6017 |
-
.voice-delivery-
|
| 6018 |
display: block;
|
| 6019 |
-
|
| 6020 |
-
font-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6021 |
}
|
| 6022 |
|
| 6023 |
.voice-delivery-notes {
|
| 6024 |
-
|
| 6025 |
-
|
| 6026 |
-
|
| 6027 |
-
|
|
|
|
|
|
|
| 6028 |
color: var(--muted);
|
| 6029 |
}
|
| 6030 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6031 |
.voice-overall-feedback {
|
| 6032 |
grid-column: 1 / -1;
|
| 6033 |
margin: 0.5rem 0 0;
|
|
@@ -7219,17 +7555,17 @@ body.rounds-drawer-open {
|
|
| 7219 |
================================ */
|
| 7220 |
|
| 7221 |
.sc-shell {
|
| 7222 |
-
width:
|
| 7223 |
-
max-width:
|
| 7224 |
-
margin: 0;
|
| 7225 |
-
padding:
|
| 7226 |
}
|
| 7227 |
|
| 7228 |
/* ---- Hero ---- */
|
| 7229 |
.sc-hero {
|
| 7230 |
-
padding:
|
| 7231 |
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
| 7232 |
-
margin: 0
|
| 7233 |
}
|
| 7234 |
|
| 7235 |
.sc-hero-row {
|
|
@@ -7238,21 +7574,41 @@ body.rounds-drawer-open {
|
|
| 7238 |
align-items: flex-start;
|
| 7239 |
}
|
| 7240 |
|
| 7241 |
-
.sc-hero-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7242 |
margin-left: auto;
|
| 7243 |
-
flex-shrink: 0;
|
| 7244 |
-
align-self: center;
|
| 7245 |
}
|
| 7246 |
|
| 7247 |
.sc-ring-wrap {
|
| 7248 |
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7249 |
}
|
| 7250 |
|
| 7251 |
.sc-ring {
|
| 7252 |
-
width:
|
| 7253 |
-
height:
|
| 7254 |
-
min-width:
|
| 7255 |
-
min-height:
|
| 7256 |
border-radius: 50%;
|
| 7257 |
border: 3px solid #f5c842;
|
| 7258 |
background: rgba(245, 200, 66, 0.05);
|
|
@@ -7261,22 +7617,28 @@ body.rounds-drawer-open {
|
|
| 7261 |
align-items: center;
|
| 7262 |
justify-content: center;
|
| 7263 |
box-shadow: none;
|
|
|
|
| 7264 |
}
|
| 7265 |
|
| 7266 |
.sc-ring .score-orb-value {
|
| 7267 |
-
font-size:
|
| 7268 |
font-weight: 800;
|
| 7269 |
line-height: 1;
|
| 7270 |
color: #f5c842;
|
| 7271 |
}
|
| 7272 |
|
| 7273 |
-
.sc-ring
|
| 7274 |
-
|
| 7275 |
-
|
|
|
|
|
|
|
|
|
|
| 7276 |
font-weight: 800;
|
| 7277 |
-
letter-spacing: 0.
|
|
|
|
| 7278 |
text-transform: uppercase;
|
| 7279 |
-
color: rgba(245, 200, 66, 0.
|
|
|
|
| 7280 |
}
|
| 7281 |
|
| 7282 |
.sc-hero-meta {
|
|
@@ -7307,25 +7669,105 @@ body.rounds-drawer-open {
|
|
| 7307 |
display: flex;
|
| 7308 |
flex-wrap: wrap;
|
| 7309 |
gap: 8px;
|
| 7310 |
-
margin-top: 12px;
|
| 7311 |
}
|
| 7312 |
|
| 7313 |
-
.
|
| 7314 |
-
|
| 7315 |
-
|
| 7316 |
-
font-
|
|
|
|
| 7317 |
letter-spacing: 0.04em;
|
| 7318 |
text-transform: uppercase;
|
| 7319 |
-
border-radius:
|
| 7320 |
-
|
| 7321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7322 |
color: #5ed3f4;
|
| 7323 |
-
|
| 7324 |
-
|
| 7325 |
-
|
| 7326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7327 |
white-space: nowrap;
|
| 7328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7329 |
}
|
| 7330 |
|
| 7331 |
.sc-btn-conversation:hover {
|
|
@@ -7378,15 +7820,19 @@ body.rounds-drawer-open {
|
|
| 7378 |
/* ---- Body grid ---- */
|
| 7379 |
.sc-body-grid {
|
| 7380 |
display: grid;
|
| 7381 |
-
grid-template-columns: minmax(
|
| 7382 |
-
min-height: 400px;
|
| 7383 |
width: 100%;
|
|
|
|
|
|
|
| 7384 |
}
|
| 7385 |
|
| 7386 |
.sc-left-col {
|
| 7387 |
-
padding:
|
| 7388 |
border-right: 1px solid rgba(255, 255, 255, 0.06);
|
| 7389 |
min-width: 0;
|
|
|
|
|
|
|
|
|
|
| 7390 |
}
|
| 7391 |
|
| 7392 |
.sc-section-label {
|
|
@@ -7458,8 +7904,8 @@ body.rounds-drawer-open {
|
|
| 7458 |
.sc-dim-list .dimension-row-scorecard {
|
| 7459 |
display: flex;
|
| 7460 |
flex-direction: column;
|
| 7461 |
-
gap:
|
| 7462 |
-
padding:
|
| 7463 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 7464 |
border-radius: 12px;
|
| 7465 |
background: rgba(255, 255, 255, 0.03);
|
|
@@ -7636,7 +8082,6 @@ body.rounds-drawer-open {
|
|
| 7636 |
min-width: 0;
|
| 7637 |
display: flex;
|
| 7638 |
flex-direction: column;
|
| 7639 |
-
min-height: 100%;
|
| 7640 |
}
|
| 7641 |
|
| 7642 |
.sc-tabs {
|
|
@@ -7671,7 +8116,7 @@ body.rounds-drawer-open {
|
|
| 7671 |
|
| 7672 |
.sc-tab-panel {
|
| 7673 |
display: none;
|
| 7674 |
-
padding:
|
| 7675 |
overflow-y: auto;
|
| 7676 |
max-height: none;
|
| 7677 |
}
|
|
@@ -7722,6 +8167,37 @@ body.rounds-drawer-open {
|
|
| 7722 |
|
| 7723 |
.sc-voice-inline {
|
| 7724 |
margin-top: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7725 |
}
|
| 7726 |
|
| 7727 |
.sc-answers-stack {
|
|
@@ -7857,24 +8333,82 @@ blockquote.sc-answer-text {
|
|
| 7857 |
color: rgba(255, 255, 255, 0.65);
|
| 7858 |
}
|
| 7859 |
|
| 7860 |
-
/* ---- Verdict
|
| 7861 |
-
.
|
| 7862 |
-
|
| 7863 |
-
|
| 7864 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7865 |
}
|
| 7866 |
|
| 7867 |
-
.
|
| 7868 |
-
|
| 7869 |
-
margin-left: 0;
|
| 7870 |
-
margin-right: 0;
|
| 7871 |
-
padding: 20px 22px 22px;
|
| 7872 |
-
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
| 7873 |
-
background: rgba(0, 0, 0, 0.15);
|
| 7874 |
}
|
| 7875 |
|
| 7876 |
-
.
|
| 7877 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7878 |
}
|
| 7879 |
|
| 7880 |
.sc-verdict-card {
|
|
@@ -8029,39 +8563,183 @@ blockquote.sc-answer-text {
|
|
| 8029 |
-webkit-line-clamp: unset;
|
| 8030 |
}
|
| 8031 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8032 |
@media (max-width: 960px) {
|
| 8033 |
.sc-shell {
|
| 8034 |
-
padding:
|
| 8035 |
}
|
| 8036 |
|
| 8037 |
.sc-hero-row {
|
| 8038 |
flex-wrap: wrap;
|
| 8039 |
}
|
| 8040 |
|
| 8041 |
-
.sc-hero-
|
| 8042 |
-
|
| 8043 |
-
|
| 8044 |
-
align-self: stretch;
|
| 8045 |
}
|
| 8046 |
|
| 8047 |
-
.sc-
|
|
|
|
| 8048 |
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8049 |
text-align: center;
|
| 8050 |
}
|
| 8051 |
|
| 8052 |
-
.sc-
|
| 8053 |
grid-template-columns: 1fr;
|
| 8054 |
}
|
| 8055 |
|
| 8056 |
-
.
|
| 8057 |
-
|
| 8058 |
-
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
| 8059 |
}
|
|
|
|
| 8060 |
|
| 8061 |
-
|
| 8062 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8063 |
}
|
| 8064 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8065 |
}
|
| 8066 |
|
| 8067 |
@media (max-width: 720px) {
|
|
|
|
| 26 |
box-sizing: border-box;
|
| 27 |
}
|
| 28 |
|
| 29 |
+
html {
|
| 30 |
+
width: 100%;
|
| 31 |
+
max-width: 100%;
|
| 32 |
+
overflow-x: hidden;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
body {
|
| 36 |
margin: 0;
|
| 37 |
min-height: 100vh;
|
| 38 |
+
width: 100%;
|
| 39 |
+
max-width: 100%;
|
| 40 |
+
overflow-x: hidden;
|
| 41 |
font-family: "Segoe UI", system-ui, sans-serif;
|
| 42 |
color: var(--text);
|
| 43 |
background: #09090f;
|
|
|
|
| 56 |
|
| 57 |
.app {
|
| 58 |
position: relative;
|
| 59 |
+
width: min(1280px, calc(100vw - 32px));
|
| 60 |
+
max-width: 100%;
|
| 61 |
margin: 0 auto;
|
| 62 |
+
padding: clamp(1rem, 2vw, 1.5rem) clamp(0.75rem, 2vw, 1rem) clamp(1.25rem, 3vw, 2rem);
|
| 63 |
}
|
| 64 |
|
| 65 |
.app:has(#screen-battle.active),
|
|
|
|
| 133 |
.arena-landing {
|
| 134 |
position: relative;
|
| 135 |
width: 100vw;
|
| 136 |
+
min-height: auto;
|
| 137 |
+
padding: clamp(0.5rem, 1.5vh, 1rem) 0;
|
| 138 |
margin-left: calc(50% - 50vw);
|
| 139 |
margin-right: calc(50% - 50vw);
|
| 140 |
overflow: hidden;
|
|
|
|
| 747 |
position: relative;
|
| 748 |
z-index: 2;
|
| 749 |
text-align: center;
|
| 750 |
+
padding: clamp(1rem, 3vw, 1.75rem) clamp(0.85rem, 2.5vw, 1.25rem) clamp(1rem, 2.5vw, 1.5rem);
|
| 751 |
max-width: 820px;
|
| 752 |
width: 100%;
|
| 753 |
}
|
|
|
|
| 777 |
}
|
| 778 |
|
| 779 |
.arena-title {
|
| 780 |
+
margin: 0 0 clamp(0.6rem, 2vw, 1rem);
|
| 781 |
+
font-size: clamp(2rem, 6vw, 3.5rem);
|
| 782 |
font-weight: 800;
|
| 783 |
letter-spacing: 0.03em;
|
| 784 |
line-height: 1.02;
|
|
|
|
| 791 |
|
| 792 |
/* Pass 2 — hook lines */
|
| 793 |
.arena-hook {
|
| 794 |
+
min-height: clamp(3rem, 8vw, 4rem);
|
| 795 |
+
margin-bottom: clamp(0.5rem, 1.5vw, 0.75rem);
|
| 796 |
}
|
| 797 |
|
| 798 |
.arena-hook-line {
|
|
|
|
| 826 |
|
| 827 |
.arena-support {
|
| 828 |
max-width: 520px;
|
| 829 |
+
margin: 0 auto clamp(0.75rem, 2vw, 1rem);
|
| 830 |
font-size: clamp(0.88rem, 2vw, 0.95rem);
|
| 831 |
line-height: 1.5;
|
| 832 |
color: var(--text-muted);
|
|
|
|
| 903 |
flex-wrap: wrap;
|
| 904 |
gap: 0.4rem;
|
| 905 |
justify-content: center;
|
| 906 |
+
margin-top: clamp(0.65rem, 2vw, 1rem);
|
| 907 |
+
padding-bottom: 0.35rem;
|
| 908 |
opacity: 0;
|
| 909 |
transform: translateY(8px);
|
| 910 |
transition: opacity 0.5s ease 0.12s, transform 0.5s ease 0.12s;
|
|
|
|
| 934 |
}
|
| 935 |
|
| 936 |
.arena-landing-footer {
|
| 937 |
+
margin-top: clamp(0.85rem, 2vw, 1.25rem);
|
| 938 |
+
padding-top: clamp(0.65rem, 1.5vw, 0.9rem);
|
| 939 |
+
padding-bottom: 0.35rem;
|
| 940 |
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 941 |
opacity: 0;
|
| 942 |
transition: opacity 0.5s ease 0.2s;
|
|
|
|
| 1222 |
|
| 1223 |
.persona-grid {
|
| 1224 |
display: grid;
|
| 1225 |
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
| 1226 |
+
gap: 0.6rem;
|
| 1227 |
+
margin-top: 0.65rem;
|
| 1228 |
}
|
| 1229 |
|
| 1230 |
.persona-card {
|
| 1231 |
text-align: left;
|
| 1232 |
+
padding: 0.75rem 0.85rem;
|
| 1233 |
border-radius: 14px;
|
| 1234 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 1235 |
background: rgba(0, 0, 0, 0.22);
|
|
|
|
| 1256 |
}
|
| 1257 |
|
| 1258 |
.difficulty-selector {
|
| 1259 |
+
margin-top: 1rem;
|
| 1260 |
}
|
| 1261 |
|
| 1262 |
.difficulty-selector h3 {
|
|
|
|
| 1269 |
|
| 1270 |
.difficulty-grid {
|
| 1271 |
display: grid;
|
| 1272 |
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
| 1273 |
+
gap: 0.55rem;
|
| 1274 |
}
|
| 1275 |
|
| 1276 |
.difficulty-card {
|
| 1277 |
text-align: left;
|
| 1278 |
+
padding: 0.65rem 0.8rem;
|
| 1279 |
border-radius: 12px;
|
| 1280 |
background: rgba(255, 255, 255, 0.04);
|
| 1281 |
border: 1px solid var(--border);
|
|
|
|
| 1648 |
position: relative;
|
| 1649 |
z-index: 1;
|
| 1650 |
width: min(1180px, 100%);
|
| 1651 |
+
height: min(80vh, 720px);
|
| 1652 |
+
max-height: min(80vh, calc(100dvh - 24px));
|
| 1653 |
display: flex;
|
| 1654 |
flex-direction: column;
|
| 1655 |
border: 1px solid rgba(78, 205, 196, 0.2);
|
|
|
|
| 2845 |
.battle-arena.arena-shell {
|
| 2846 |
position: relative;
|
| 2847 |
z-index: 1;
|
| 2848 |
+
width: min(1360px, calc(100vw - 32px));
|
| 2849 |
+
max-width: 1360px;
|
| 2850 |
min-width: 0;
|
| 2851 |
margin: 0 auto;
|
| 2852 |
+
min-height: 0;
|
| 2853 |
display: grid;
|
| 2854 |
grid-template-columns: 280px minmax(0, 1fr);
|
| 2855 |
+
gap: 16px;
|
| 2856 |
align-items: stretch;
|
| 2857 |
}
|
| 2858 |
|
|
|
|
| 2861 |
margin: 0 auto;
|
| 2862 |
}
|
| 2863 |
|
| 2864 |
+
.app:has(#screen-scorecard.active),
|
| 2865 |
+
.app.app-scorecard-fullwidth {
|
| 2866 |
+
max-width: none !important;
|
| 2867 |
+
width: 100% !important;
|
| 2868 |
+
padding: clamp(0.5rem, 1.2vw, 0.85rem) !important;
|
| 2869 |
+
}
|
| 2870 |
+
|
| 2871 |
+
#screen-setup.active .briefing-shell-wide {
|
| 2872 |
+
width: min(1180px, 92vw);
|
| 2873 |
+
max-width: 100%;
|
| 2874 |
+
margin: 0 auto;
|
| 2875 |
+
padding-bottom: 0.5rem;
|
| 2876 |
+
}
|
| 2877 |
+
|
| 2878 |
.briefing-shell-wide {
|
| 2879 |
+
max-width: min(1180px, 92vw);
|
| 2880 |
+
width: 100%;
|
| 2881 |
+
margin: 0 auto;
|
| 2882 |
}
|
| 2883 |
|
| 2884 |
.briefing-mode-tabs {
|
| 2885 |
display: flex;
|
| 2886 |
+
gap: 0.45rem;
|
| 2887 |
+
margin-bottom: 0.75rem;
|
| 2888 |
flex-wrap: wrap;
|
| 2889 |
}
|
| 2890 |
|
| 2891 |
.briefing-mode-tab {
|
| 2892 |
flex: 1;
|
| 2893 |
min-width: 140px;
|
| 2894 |
+
padding: 0.65rem 1rem;
|
| 2895 |
border-radius: 12px;
|
| 2896 |
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 2897 |
background: rgba(0, 0, 0, 0.25);
|
|
|
|
| 2916 |
}
|
| 2917 |
|
| 2918 |
.briefing-grid-wide {
|
| 2919 |
+
grid-template-columns: minmax(0, 1.12fr) minmax(280px, 0.88fr);
|
| 2920 |
+
gap: 0.85rem;
|
| 2921 |
align-items: stretch;
|
| 2922 |
}
|
| 2923 |
|
| 2924 |
.briefing-left-col {
|
| 2925 |
+
display: grid;
|
| 2926 |
+
grid-template-columns: minmax(0, 1fr);
|
| 2927 |
+
align-items: stretch;
|
| 2928 |
min-width: 0;
|
| 2929 |
overflow: visible;
|
| 2930 |
}
|
| 2931 |
|
| 2932 |
+
.briefing-left-col > .panel {
|
| 2933 |
+
min-height: 0;
|
| 2934 |
+
}
|
| 2935 |
+
|
| 2936 |
+
.briefing-left-col.mode-advanced .quick-pitch-panel,
|
| 2937 |
+
.briefing-left-col.mode-advanced .brief-preview-panel {
|
| 2938 |
+
display: none !important;
|
| 2939 |
+
}
|
| 2940 |
+
|
| 2941 |
+
.briefing-left-col.mode-structured .quick-pitch-panel,
|
| 2942 |
+
.briefing-left-col:not(.mode-structured) .brief-preview-panel {
|
| 2943 |
+
display: none !important;
|
| 2944 |
+
}
|
| 2945 |
+
|
| 2946 |
+
[hidden] {
|
| 2947 |
+
display: none !important;
|
| 2948 |
+
}
|
| 2949 |
+
|
| 2950 |
.quick-pitch-panel {
|
| 2951 |
+
padding: clamp(0.75rem, 1.8vw, 0.95rem) clamp(0.85rem, 1.8vw, 1rem);
|
| 2952 |
}
|
| 2953 |
|
| 2954 |
.quick-pitch-label {
|
|
|
|
| 2962 |
|
| 2963 |
.quick-pitch-textarea {
|
| 2964 |
width: 100%;
|
| 2965 |
+
min-height: clamp(96px, 14vh, 128px);
|
| 2966 |
+
max-height: clamp(120px, 18vh, 150px);
|
| 2967 |
+
padding: 0.7rem 0.85rem;
|
| 2968 |
+
border-radius: 12px;
|
| 2969 |
border: 1px solid rgba(255, 255, 255, 0.12);
|
| 2970 |
background: rgba(0, 0, 0, 0.35);
|
| 2971 |
color: var(--text);
|
| 2972 |
+
font-size: 0.92rem;
|
| 2973 |
+
line-height: 1.45;
|
| 2974 |
+
resize: none;
|
| 2975 |
box-shadow: inset 0 2px 12px rgba(0, 0, 0, 0.25);
|
| 2976 |
}
|
| 2977 |
|
|
|
|
| 2990 |
.quick-pitch-actions {
|
| 2991 |
display: flex;
|
| 2992 |
flex-direction: column;
|
| 2993 |
+
gap: 0.55rem;
|
| 2994 |
+
margin-top: 0.75rem;
|
| 2995 |
}
|
| 2996 |
|
| 2997 |
.quick-pitch-actions .btn-wide {
|
|
|
|
| 3011 |
}
|
| 3012 |
|
| 3013 |
.quick-pitch-hint {
|
| 3014 |
+
margin: 0.55rem 0 0;
|
| 3015 |
+
font-size: 0.8rem;
|
| 3016 |
color: rgba(244, 211, 94, 0.85);
|
| 3017 |
}
|
| 3018 |
|
| 3019 |
+
.quick-pitch-success {
|
| 3020 |
+
margin: 0.65rem 0 0;
|
| 3021 |
+
padding: 0.5rem 0.7rem;
|
| 3022 |
+
border-radius: 10px;
|
| 3023 |
+
border: 1px solid rgba(244, 211, 94, 0.35);
|
| 3024 |
+
background: rgba(244, 211, 94, 0.08);
|
| 3025 |
+
color: var(--gold);
|
| 3026 |
+
font-size: 0.82rem;
|
| 3027 |
+
line-height: 1.4;
|
| 3028 |
+
}
|
| 3029 |
+
|
| 3030 |
+
.quick-pitch-reedit {
|
| 3031 |
+
margin-top: 0.35rem;
|
| 3032 |
+
align-self: flex-start;
|
| 3033 |
+
padding: 0.35rem 0.65rem;
|
| 3034 |
+
font-size: 0.78rem;
|
| 3035 |
+
}
|
| 3036 |
+
|
| 3037 |
.brief-preview-panel {
|
| 3038 |
+
padding: clamp(0.7rem, 1.6vw, 0.85rem) clamp(0.8rem, 1.8vw, 0.95rem);
|
| 3039 |
overflow: visible;
|
| 3040 |
position: relative;
|
| 3041 |
+
display: flex;
|
| 3042 |
+
flex-direction: column;
|
| 3043 |
+
height: 100%;
|
| 3044 |
+
min-height: 100%;
|
| 3045 |
}
|
| 3046 |
|
| 3047 |
.brief-preview-panel.is-editing-active::before {
|
|
|
|
| 3055 |
}
|
| 3056 |
|
| 3057 |
.brief-preview-header {
|
| 3058 |
+
margin-bottom: 0.5rem;
|
| 3059 |
}
|
| 3060 |
|
| 3061 |
.brief-preview-title-row {
|
|
|
|
| 3071 |
}
|
| 3072 |
|
| 3073 |
.brief-preview-helper {
|
| 3074 |
+
margin: 0.25rem 0 0;
|
| 3075 |
color: var(--muted);
|
| 3076 |
+
font-size: 0.78rem;
|
| 3077 |
}
|
| 3078 |
|
| 3079 |
.brief-preview-hint {
|
| 3080 |
+
margin: 0.35rem 0 0;
|
| 3081 |
+
font-size: 0.78rem;
|
| 3082 |
color: rgba(244, 211, 94, 0.85);
|
| 3083 |
}
|
| 3084 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3085 |
.brief-preview-grid {
|
| 3086 |
display: grid;
|
| 3087 |
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 3088 |
+
gap: 0.45rem;
|
| 3089 |
+
flex: 1 1 auto;
|
| 3090 |
}
|
| 3091 |
|
| 3092 |
.brief-read-card {
|
| 3093 |
+
padding: 0.45rem 0.55rem;
|
| 3094 |
+
border-radius: 10px;
|
| 3095 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 3096 |
background: rgba(0, 0, 0, 0.22);
|
| 3097 |
transition: border-color 0.2s, background 0.2s, box-shadow 0.2s, transform 0.2s;
|
|
|
|
| 3251 |
.brief-preview-actions {
|
| 3252 |
display: flex;
|
| 3253 |
flex-direction: column;
|
| 3254 |
+
gap: 0.45rem;
|
| 3255 |
+
margin-top: auto;
|
| 3256 |
+
padding-top: 0.55rem;
|
| 3257 |
}
|
| 3258 |
|
| 3259 |
.brief-preview-actions .btn-wide {
|
|
|
|
| 3261 |
}
|
| 3262 |
|
| 3263 |
.advanced-briefing-panel {
|
| 3264 |
+
padding: clamp(0.7rem, 1.6vw, 0.85rem) clamp(0.8rem, 1.8vw, 0.95rem);
|
| 3265 |
+
display: flex;
|
| 3266 |
+
flex-direction: column;
|
| 3267 |
+
height: 100%;
|
| 3268 |
+
min-height: 100%;
|
| 3269 |
}
|
| 3270 |
|
| 3271 |
+
.advanced-briefing-form {
|
| 3272 |
+
display: grid;
|
| 3273 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 3274 |
+
gap: 0.45rem;
|
| 3275 |
+
margin-top: 0;
|
| 3276 |
+
flex: 1 1 auto;
|
| 3277 |
+
align-content: start;
|
| 3278 |
+
}
|
| 3279 |
+
|
| 3280 |
+
.advanced-briefing-form .adv-field-wide {
|
| 3281 |
+
grid-column: 1 / -1;
|
| 3282 |
+
}
|
| 3283 |
+
|
| 3284 |
+
.advanced-briefing-form label {
|
| 3285 |
+
display: grid;
|
| 3286 |
+
gap: 0.25rem;
|
| 3287 |
+
margin: 0;
|
| 3288 |
+
font-size: 0.68rem;
|
| 3289 |
+
font-weight: 700;
|
| 3290 |
+
letter-spacing: 0.1em;
|
| 3291 |
+
text-transform: uppercase;
|
| 3292 |
+
color: var(--accent-cyan);
|
| 3293 |
+
}
|
| 3294 |
+
|
| 3295 |
+
.advanced-briefing-form input,
|
| 3296 |
+
.advanced-briefing-form textarea {
|
| 3297 |
+
padding: 0.45rem 0.55rem;
|
| 3298 |
+
font-size: 0.82rem;
|
| 3299 |
+
line-height: 1.35;
|
| 3300 |
+
border-radius: 8px;
|
| 3301 |
+
}
|
| 3302 |
+
|
| 3303 |
+
.advanced-briefing-form textarea {
|
| 3304 |
+
min-height: 52px;
|
| 3305 |
+
max-height: 68px;
|
| 3306 |
+
resize: none;
|
| 3307 |
+
overflow-y: auto;
|
| 3308 |
+
}
|
| 3309 |
+
|
| 3310 |
+
.briefing-left-col.mode-advanced .advanced-briefing-panel {
|
| 3311 |
+
min-height: 100%;
|
| 3312 |
+
}
|
| 3313 |
+
|
| 3314 |
+
.briefing-opponent-panel .briefing-section-title,
|
| 3315 |
+
.briefing-opponent-panel .difficulty-selector h3 {
|
| 3316 |
+
margin: 0 0 0.4rem;
|
| 3317 |
+
font-size: 0.82rem;
|
| 3318 |
+
letter-spacing: 0.08em;
|
| 3319 |
+
}
|
| 3320 |
+
|
| 3321 |
+
.briefing-opponent-panel .persona-grid {
|
| 3322 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 3323 |
+
gap: 0.45rem;
|
| 3324 |
+
margin-top: 0.35rem;
|
| 3325 |
+
}
|
| 3326 |
+
|
| 3327 |
+
.briefing-opponent-panel .difficulty-grid {
|
| 3328 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 3329 |
+
gap: 0.45rem;
|
| 3330 |
+
}
|
| 3331 |
+
|
| 3332 |
+
.briefing-opponent-panel .persona-card {
|
| 3333 |
+
padding: 0.5rem 0.55rem;
|
| 3334 |
+
border-radius: 10px;
|
| 3335 |
+
}
|
| 3336 |
+
|
| 3337 |
+
.briefing-opponent-panel .persona-card .persona-icon {
|
| 3338 |
+
font-size: 1rem;
|
| 3339 |
+
line-height: 1;
|
| 3340 |
+
}
|
| 3341 |
+
|
| 3342 |
+
.briefing-opponent-panel .persona-card h3 {
|
| 3343 |
+
margin: 0.25rem 0 0.15rem;
|
| 3344 |
+
font-size: 0.8rem;
|
| 3345 |
+
}
|
| 3346 |
+
|
| 3347 |
+
.briefing-opponent-panel .persona-card p {
|
| 3348 |
+
font-size: 0.68rem;
|
| 3349 |
+
line-height: 1.25;
|
| 3350 |
+
}
|
| 3351 |
+
|
| 3352 |
+
.briefing-opponent-panel .difficulty-selector {
|
| 3353 |
+
margin-top: 0.65rem;
|
| 3354 |
+
}
|
| 3355 |
+
|
| 3356 |
+
.briefing-opponent-panel .difficulty-card {
|
| 3357 |
+
padding: 0.5rem 0.55rem;
|
| 3358 |
+
border-radius: 10px;
|
| 3359 |
+
}
|
| 3360 |
+
|
| 3361 |
+
.briefing-opponent-panel .difficulty-card .difficulty-icon {
|
| 3362 |
+
font-size: 1rem;
|
| 3363 |
+
margin-bottom: 0.15rem;
|
| 3364 |
+
}
|
| 3365 |
+
|
| 3366 |
+
.briefing-opponent-panel .difficulty-card h4 {
|
| 3367 |
+
margin: 0 0 0.1rem;
|
| 3368 |
+
font-size: 0.78rem;
|
| 3369 |
+
}
|
| 3370 |
+
|
| 3371 |
+
.briefing-opponent-panel .difficulty-card p {
|
| 3372 |
+
font-size: 0.66rem;
|
| 3373 |
+
line-height: 1.2;
|
| 3374 |
}
|
| 3375 |
|
| 3376 |
.briefing-opponent-note {
|
|
|
|
| 3386 |
flex-direction: column;
|
| 3387 |
align-self: stretch;
|
| 3388 |
min-height: 100%;
|
| 3389 |
+
height: 100%;
|
| 3390 |
gap: 0;
|
| 3391 |
+
padding: clamp(0.7rem, 1.6vw, 0.85rem) clamp(0.8rem, 1.8vw, 0.95rem);
|
| 3392 |
}
|
| 3393 |
|
| 3394 |
.briefing-opponent-footer {
|
| 3395 |
margin-top: auto;
|
| 3396 |
+
padding-top: 1rem;
|
| 3397 |
display: flex;
|
| 3398 |
flex-direction: column;
|
| 3399 |
+
gap: 0.55rem;
|
| 3400 |
}
|
| 3401 |
|
| 3402 |
.briefing-opponent-panel .btn-arena-start {
|
|
|
|
| 3412 |
justify-content: space-between;
|
| 3413 |
align-items: flex-start;
|
| 3414 |
gap: 1rem;
|
| 3415 |
+
margin-bottom: 0.75rem;
|
| 3416 |
}
|
| 3417 |
|
| 3418 |
.briefing-title {
|
|
|
|
| 4847 |
|
| 4848 |
.voice-delivery-summary {
|
| 4849 |
display: grid;
|
| 4850 |
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 4851 |
+
gap: 0.5rem;
|
| 4852 |
}
|
| 4853 |
|
| 4854 |
.voice-wave-decor {
|
|
|
|
| 5910 |
.outcome-strong_win { background: rgba(74, 222, 128, 0.18); color: #86efac; }
|
| 5911 |
.outcome-weak_concession { background: rgba(248, 113, 113, 0.18); color: #fca5a5; }
|
| 5912 |
|
| 5913 |
+
/* Voice mode — Phase 7 + entry path chooser */
|
| 5914 |
+
.screen-start-path {
|
| 5915 |
+
width: 100%;
|
|
|
|
|
|
|
|
|
|
| 5916 |
}
|
| 5917 |
|
| 5918 |
+
.start-path-shell {
|
| 5919 |
+
width: min(920px, 92vw);
|
| 5920 |
+
max-width: 100%;
|
| 5921 |
+
margin: 0 auto;
|
| 5922 |
+
padding: clamp(0.35rem, 1.5vw, 0.75rem) 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5923 |
}
|
| 5924 |
|
| 5925 |
+
.start-path-header {
|
| 5926 |
+
display: flex;
|
| 5927 |
+
justify-content: space-between;
|
| 5928 |
+
align-items: flex-start;
|
| 5929 |
+
gap: 1rem;
|
| 5930 |
+
margin-bottom: clamp(0.85rem, 2vw, 1.15rem);
|
| 5931 |
}
|
| 5932 |
|
| 5933 |
+
.start-path-title {
|
| 5934 |
margin: 0.35rem 0 0.25rem;
|
| 5935 |
+
font-size: clamp(1.45rem, 3vw, 1.85rem);
|
| 5936 |
}
|
| 5937 |
|
| 5938 |
+
.start-path-sub {
|
| 5939 |
margin: 0;
|
| 5940 |
color: var(--muted);
|
| 5941 |
+
font-size: 0.88rem;
|
| 5942 |
+
line-height: 1.45;
|
| 5943 |
+
}
|
| 5944 |
+
|
| 5945 |
+
.start-path-grid {
|
| 5946 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 5947 |
+
gap: clamp(0.75rem, 2vw, 1rem);
|
| 5948 |
+
margin: 0;
|
| 5949 |
+
}
|
| 5950 |
+
|
| 5951 |
+
.start-path-card {
|
| 5952 |
+
padding: clamp(1rem, 2.5vw, 1.25rem) clamp(0.95rem, 2vw, 1.15rem);
|
| 5953 |
+
border-radius: 16px;
|
| 5954 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 5955 |
+
background: linear-gradient(165deg, rgba(18, 18, 24, 0.95), rgba(8, 8, 12, 0.92));
|
| 5956 |
+
text-align: left;
|
| 5957 |
+
transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
| 5958 |
+
}
|
| 5959 |
+
|
| 5960 |
+
.start-path-card-text:hover,
|
| 5961 |
+
.start-path-card-text:focus-visible {
|
| 5962 |
+
border-color: rgba(244, 211, 94, 0.55);
|
| 5963 |
+
box-shadow: 0 0 32px rgba(244, 211, 94, 0.14);
|
| 5964 |
+
transform: translateY(-2px);
|
| 5965 |
+
}
|
| 5966 |
+
|
| 5967 |
+
.start-path-card-voice:hover,
|
| 5968 |
+
.start-path-card-voice:focus-visible {
|
| 5969 |
+
border-color: rgba(78, 205, 196, 0.55);
|
| 5970 |
+
box-shadow: 0 0 32px rgba(78, 205, 196, 0.14);
|
| 5971 |
+
transform: translateY(-2px);
|
| 5972 |
+
}
|
| 5973 |
+
|
| 5974 |
+
.start-path-badge {
|
| 5975 |
+
display: inline-block;
|
| 5976 |
+
margin-bottom: 0.55rem;
|
| 5977 |
+
font-size: 0.62rem;
|
| 5978 |
+
font-weight: 800;
|
| 5979 |
+
letter-spacing: 0.16em;
|
| 5980 |
+
text-transform: uppercase;
|
| 5981 |
+
color: var(--gold);
|
| 5982 |
+
}
|
| 5983 |
+
|
| 5984 |
+
.start-path-badge-voice {
|
| 5985 |
+
color: #7efff0;
|
| 5986 |
+
}
|
| 5987 |
+
|
| 5988 |
+
.start-path-card h3 {
|
| 5989 |
+
margin: 0.45rem 0 0.15rem;
|
| 5990 |
+
font-size: clamp(1.1rem, 2.2vw, 1.3rem);
|
| 5991 |
+
}
|
| 5992 |
+
|
| 5993 |
+
.start-path-tagline {
|
| 5994 |
+
margin: 0 0 0.45rem;
|
| 5995 |
+
font-size: 0.68rem;
|
| 5996 |
+
font-weight: 700;
|
| 5997 |
+
letter-spacing: 0.12em;
|
| 5998 |
+
text-transform: uppercase;
|
| 5999 |
+
color: var(--gold);
|
| 6000 |
+
}
|
| 6001 |
+
|
| 6002 |
+
.start-path-card-voice .start-path-tagline {
|
| 6003 |
+
color: #7efff0;
|
| 6004 |
+
}
|
| 6005 |
+
|
| 6006 |
+
.start-path-desc {
|
| 6007 |
+
margin: 0;
|
| 6008 |
+
font-size: 0.84rem;
|
| 6009 |
+
line-height: 1.45;
|
| 6010 |
+
color: var(--muted);
|
| 6011 |
+
}
|
| 6012 |
+
|
| 6013 |
+
@media (max-width: 640px) {
|
| 6014 |
+
.start-path-grid {
|
| 6015 |
+
grid-template-columns: 1fr;
|
| 6016 |
+
}
|
| 6017 |
+
}
|
| 6018 |
+
|
| 6019 |
+
@media (max-height: 760px) {
|
| 6020 |
+
.start-path-header {
|
| 6021 |
+
margin-bottom: 0.65rem;
|
| 6022 |
+
}
|
| 6023 |
+
|
| 6024 |
+
.start-path-card {
|
| 6025 |
+
padding: 0.85rem 0.9rem;
|
| 6026 |
+
}
|
| 6027 |
+
|
| 6028 |
+
.start-path-card h3 {
|
| 6029 |
+
font-size: 1.05rem;
|
| 6030 |
+
}
|
| 6031 |
+
}
|
| 6032 |
+
|
| 6033 |
+
.start-method-grid {
|
| 6034 |
+
display: grid;
|
| 6035 |
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 6036 |
+
gap: 0.85rem;
|
| 6037 |
+
margin: 1rem 0 1.25rem;
|
| 6038 |
+
}
|
| 6039 |
+
|
| 6040 |
+
.start-method-card {
|
| 6041 |
+
text-align: left;
|
| 6042 |
+
padding: 1.1rem;
|
| 6043 |
+
border-radius: 12px;
|
| 6044 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 6045 |
+
background: rgba(0, 0, 0, 0.2);
|
| 6046 |
+
color: inherit;
|
| 6047 |
+
cursor: pointer;
|
| 6048 |
+
transition: border-color 0.2s, box-shadow 0.2s;
|
| 6049 |
+
}
|
| 6050 |
+
|
| 6051 |
+
.start-method-card.selected {
|
| 6052 |
+
border-color: rgba(125, 211, 252, 0.45);
|
| 6053 |
+
box-shadow: 0 0 20px rgba(125, 211, 252, 0.12);
|
| 6054 |
+
}
|
| 6055 |
+
|
| 6056 |
+
.start-method-card h3 {
|
| 6057 |
+
margin: 0.35rem 0 0.25rem;
|
| 6058 |
+
font-size: 1rem;
|
| 6059 |
+
}
|
| 6060 |
+
|
| 6061 |
+
.start-method-card p {
|
| 6062 |
+
margin: 0;
|
| 6063 |
+
color: var(--muted);
|
| 6064 |
+
font-size: 0.85rem;
|
| 6065 |
+
line-height: 1.4;
|
| 6066 |
+
}
|
| 6067 |
|
| 6068 |
.start-method-icon {
|
| 6069 |
font-size: 1.4rem;
|
|
|
|
| 6262 |
margin-top: 0.5rem;
|
| 6263 |
}
|
| 6264 |
|
| 6265 |
+
.voice-delivery-wrap {
|
| 6266 |
+
width: 100%;
|
| 6267 |
+
}
|
| 6268 |
+
|
| 6269 |
+
.voice-delivery-layout {
|
| 6270 |
+
display: flex;
|
| 6271 |
+
flex-direction: column;
|
| 6272 |
+
gap: 0.55rem;
|
| 6273 |
+
}
|
| 6274 |
+
|
| 6275 |
+
.voice-delivery-metrics {
|
| 6276 |
display: grid;
|
| 6277 |
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 6278 |
+
gap: 0.5rem;
|
| 6279 |
}
|
| 6280 |
|
| 6281 |
+
.voice-delivery-box,
|
| 6282 |
+
.voice-delivery-insight {
|
| 6283 |
+
padding: 0.55rem 0.65rem;
|
| 6284 |
+
border-radius: 10px;
|
| 6285 |
+
border: 1px solid rgba(78, 205, 196, 0.22);
|
| 6286 |
+
background: rgba(0, 0, 0, 0.28);
|
| 6287 |
}
|
| 6288 |
|
| 6289 |
+
.voice-delivery-box {
|
| 6290 |
+
display: flex;
|
| 6291 |
+
flex-direction: column;
|
| 6292 |
+
justify-content: space-between;
|
| 6293 |
+
gap: 0.35rem;
|
| 6294 |
+
min-height: 58px;
|
| 6295 |
+
}
|
| 6296 |
+
|
| 6297 |
+
.voice-delivery-box-label {
|
| 6298 |
display: block;
|
| 6299 |
+
font-size: 0.62rem;
|
| 6300 |
+
font-weight: 700;
|
| 6301 |
+
letter-spacing: 0.1em;
|
| 6302 |
text-transform: uppercase;
|
| 6303 |
+
color: rgba(125, 211, 252, 0.85);
|
| 6304 |
}
|
| 6305 |
|
| 6306 |
+
.voice-delivery-box-value {
|
| 6307 |
display: block;
|
| 6308 |
+
font-size: 0.88rem;
|
| 6309 |
+
font-weight: 700;
|
| 6310 |
+
line-height: 1.3;
|
| 6311 |
+
color: var(--text);
|
| 6312 |
+
word-break: break-word;
|
| 6313 |
+
}
|
| 6314 |
+
|
| 6315 |
+
.voice-delivery-insight {
|
| 6316 |
+
display: grid;
|
| 6317 |
+
grid-template-columns: minmax(72px, 100px) minmax(0, 1fr);
|
| 6318 |
+
gap: 0.65rem;
|
| 6319 |
+
align-items: start;
|
| 6320 |
+
}
|
| 6321 |
+
|
| 6322 |
+
.voice-delivery-insight-text {
|
| 6323 |
+
margin: 0;
|
| 6324 |
+
font-size: 0.84rem;
|
| 6325 |
+
line-height: 1.45;
|
| 6326 |
+
color: rgba(255, 255, 255, 0.9);
|
| 6327 |
+
}
|
| 6328 |
+
|
| 6329 |
+
.voice-delivery-overall {
|
| 6330 |
+
margin: 0;
|
| 6331 |
+
padding: 0.55rem 0.65rem;
|
| 6332 |
+
border-radius: 10px;
|
| 6333 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 6334 |
+
background: rgba(255, 255, 255, 0.03);
|
| 6335 |
+
font-size: 0.84rem;
|
| 6336 |
+
line-height: 1.45;
|
| 6337 |
+
color: rgba(255, 255, 255, 0.82);
|
| 6338 |
}
|
| 6339 |
|
| 6340 |
.voice-delivery-notes {
|
| 6341 |
+
margin: 0.25rem 0 0;
|
| 6342 |
+
padding: 0.55rem 0.65rem 0.55rem 1.1rem;
|
| 6343 |
+
border-radius: 10px;
|
| 6344 |
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
| 6345 |
+
background: rgba(0, 0, 0, 0.18);
|
| 6346 |
+
font-size: 0.82rem;
|
| 6347 |
color: var(--muted);
|
| 6348 |
}
|
| 6349 |
|
| 6350 |
+
@media (max-width: 900px) {
|
| 6351 |
+
.voice-delivery-metrics {
|
| 6352 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 6353 |
+
}
|
| 6354 |
+
}
|
| 6355 |
+
|
| 6356 |
+
@media (max-width: 520px) {
|
| 6357 |
+
.voice-delivery-metrics {
|
| 6358 |
+
grid-template-columns: 1fr 1fr;
|
| 6359 |
+
}
|
| 6360 |
+
|
| 6361 |
+
.voice-delivery-insight {
|
| 6362 |
+
grid-template-columns: 1fr;
|
| 6363 |
+
gap: 0.35rem;
|
| 6364 |
+
}
|
| 6365 |
+
}
|
| 6366 |
+
|
| 6367 |
.voice-overall-feedback {
|
| 6368 |
grid-column: 1 / -1;
|
| 6369 |
margin: 0.5rem 0 0;
|
|
|
|
| 7555 |
================================ */
|
| 7556 |
|
| 7557 |
.sc-shell {
|
| 7558 |
+
width: min(1320px, 90vw);
|
| 7559 |
+
max-width: 100%;
|
| 7560 |
+
margin: 0 auto;
|
| 7561 |
+
padding: clamp(10px, 1.8vw, 16px) clamp(12px, 2vw, 20px);
|
| 7562 |
}
|
| 7563 |
|
| 7564 |
/* ---- Hero ---- */
|
| 7565 |
.sc-hero {
|
| 7566 |
+
padding: clamp(12px, 2vw, 16px) clamp(14px, 2vw, 20px);
|
| 7567 |
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
| 7568 |
+
margin: 0;
|
| 7569 |
}
|
| 7570 |
|
| 7571 |
.sc-hero-row {
|
|
|
|
| 7574 |
align-items: flex-start;
|
| 7575 |
}
|
| 7576 |
|
| 7577 |
+
.sc-hero-actions-row {
|
| 7578 |
+
display: flex;
|
| 7579 |
+
flex-wrap: wrap;
|
| 7580 |
+
align-items: center;
|
| 7581 |
+
justify-content: space-between;
|
| 7582 |
+
gap: 10px;
|
| 7583 |
+
margin-top: 12px;
|
| 7584 |
+
}
|
| 7585 |
+
|
| 7586 |
+
.sc-hero-actions-primary,
|
| 7587 |
+
.sc-hero-actions-secondary {
|
| 7588 |
+
display: flex;
|
| 7589 |
+
flex-wrap: wrap;
|
| 7590 |
+
gap: 8px;
|
| 7591 |
+
align-items: center;
|
| 7592 |
+
}
|
| 7593 |
+
|
| 7594 |
+
.sc-hero-actions-secondary {
|
| 7595 |
margin-left: auto;
|
|
|
|
|
|
|
| 7596 |
}
|
| 7597 |
|
| 7598 |
.sc-ring-wrap {
|
| 7599 |
flex-shrink: 0;
|
| 7600 |
+
display: flex;
|
| 7601 |
+
flex-direction: column;
|
| 7602 |
+
align-items: center;
|
| 7603 |
+
gap: 5px;
|
| 7604 |
+
min-width: 88px;
|
| 7605 |
}
|
| 7606 |
|
| 7607 |
.sc-ring {
|
| 7608 |
+
width: 84px;
|
| 7609 |
+
height: 84px;
|
| 7610 |
+
min-width: 84px;
|
| 7611 |
+
min-height: 84px;
|
| 7612 |
border-radius: 50%;
|
| 7613 |
border: 3px solid #f5c842;
|
| 7614 |
background: rgba(245, 200, 66, 0.05);
|
|
|
|
| 7617 |
align-items: center;
|
| 7618 |
justify-content: center;
|
| 7619 |
box-shadow: none;
|
| 7620 |
+
overflow: visible;
|
| 7621 |
}
|
| 7622 |
|
| 7623 |
.sc-ring .score-orb-value {
|
| 7624 |
+
font-size: 30px;
|
| 7625 |
font-weight: 800;
|
| 7626 |
line-height: 1;
|
| 7627 |
color: #f5c842;
|
| 7628 |
}
|
| 7629 |
|
| 7630 |
+
.sc-ring-caption {
|
| 7631 |
+
display: block;
|
| 7632 |
+
max-width: 92px;
|
| 7633 |
+
margin: 0;
|
| 7634 |
+
text-align: center;
|
| 7635 |
+
font-size: 8px;
|
| 7636 |
font-weight: 800;
|
| 7637 |
+
letter-spacing: 0.08em;
|
| 7638 |
+
line-height: 1.25;
|
| 7639 |
text-transform: uppercase;
|
| 7640 |
+
color: rgba(245, 200, 66, 0.9);
|
| 7641 |
+
word-break: break-word;
|
| 7642 |
}
|
| 7643 |
|
| 7644 |
.sc-hero-meta {
|
|
|
|
| 7669 |
display: flex;
|
| 7670 |
flex-wrap: wrap;
|
| 7671 |
gap: 8px;
|
|
|
|
| 7672 |
}
|
| 7673 |
|
| 7674 |
+
.score-action-btn {
|
| 7675 |
+
min-height: 42px;
|
| 7676 |
+
padding: 10px 16px;
|
| 7677 |
+
font-size: 12px;
|
| 7678 |
+
font-weight: 700;
|
| 7679 |
letter-spacing: 0.04em;
|
| 7680 |
text-transform: uppercase;
|
| 7681 |
+
border-radius: 10px;
|
| 7682 |
+
white-space: nowrap;
|
| 7683 |
+
line-height: 1.2;
|
| 7684 |
+
display: inline-flex;
|
| 7685 |
+
align-items: center;
|
| 7686 |
+
justify-content: center;
|
| 7687 |
+
}
|
| 7688 |
+
|
| 7689 |
+
.score-action-btn-secondary {
|
| 7690 |
+
border: 2px solid rgba(94, 211, 244, 0.45);
|
| 7691 |
+
background: rgba(94, 211, 244, 0.08);
|
| 7692 |
color: #5ed3f4;
|
| 7693 |
+
}
|
| 7694 |
+
|
| 7695 |
+
@media (min-width: 900px) {
|
| 7696 |
+
.briefing-opponent-panel .persona-grid,
|
| 7697 |
+
.briefing-opponent-panel .difficulty-grid {
|
| 7698 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 7699 |
+
}
|
| 7700 |
+
}
|
| 7701 |
+
|
| 7702 |
+
@media (max-width: 900px) {
|
| 7703 |
+
.advanced-briefing-form {
|
| 7704 |
+
grid-template-columns: 1fr;
|
| 7705 |
+
}
|
| 7706 |
+
|
| 7707 |
+
.advanced-briefing-form .adv-field-wide {
|
| 7708 |
+
grid-column: auto;
|
| 7709 |
+
}
|
| 7710 |
+
|
| 7711 |
+
.briefing-opponent-panel .persona-grid,
|
| 7712 |
+
.briefing-opponent-panel .difficulty-grid {
|
| 7713 |
+
grid-template-columns: 1fr;
|
| 7714 |
+
}
|
| 7715 |
+
}
|
| 7716 |
+
|
| 7717 |
+
.score-action-btn-verdict {
|
| 7718 |
+
border: 2px solid rgba(0, 230, 200, 0.45);
|
| 7719 |
+
background: rgba(0, 230, 200, 0.08);
|
| 7720 |
+
color: #00e6c8;
|
| 7721 |
+
}
|
| 7722 |
+
|
| 7723 |
+
.score-action-btn-verdict:hover {
|
| 7724 |
+
border-color: #00e6c8;
|
| 7725 |
+
background: rgba(0, 230, 200, 0.16);
|
| 7726 |
+
}
|
| 7727 |
+
|
| 7728 |
+
.sc-btn-gold,
|
| 7729 |
+
.sc-btn-secondary {
|
| 7730 |
+
min-height: 42px;
|
| 7731 |
+
padding: 10px 16px;
|
| 7732 |
+
font-size: 12px;
|
| 7733 |
+
}
|
| 7734 |
+
|
| 7735 |
+
.sc-btn-verdict,
|
| 7736 |
+
.sc-btn-conversation {
|
| 7737 |
+
min-height: 42px;
|
| 7738 |
+
padding: 10px 16px;
|
| 7739 |
+
font-size: 12px;
|
| 7740 |
+
font-weight: 700;
|
| 7741 |
+
letter-spacing: 0.04em;
|
| 7742 |
+
text-transform: uppercase;
|
| 7743 |
+
border-radius: 10px;
|
| 7744 |
white-space: nowrap;
|
| 7745 |
+
}
|
| 7746 |
+
|
| 7747 |
+
.sc-btn-verdict {
|
| 7748 |
+
border: 2px solid rgba(0, 230, 200, 0.45);
|
| 7749 |
+
background: rgba(0, 230, 200, 0.08);
|
| 7750 |
+
color: #00e6c8;
|
| 7751 |
+
}
|
| 7752 |
+
|
| 7753 |
+
.sc-btn-conversation {
|
| 7754 |
+
border: 2px solid rgba(94, 211, 244, 0.45);
|
| 7755 |
+
background: rgba(94, 211, 244, 0.08);
|
| 7756 |
+
color: #5ed3f4;
|
| 7757 |
+
box-shadow: none;
|
| 7758 |
+
}
|
| 7759 |
+
|
| 7760 |
+
.retry-projection-note {
|
| 7761 |
+
margin: 0.65rem 0 0;
|
| 7762 |
+
font-size: 12px;
|
| 7763 |
+
font-weight: 600;
|
| 7764 |
+
color: rgba(255, 255, 255, 0.55);
|
| 7765 |
+
text-align: center;
|
| 7766 |
+
}
|
| 7767 |
+
|
| 7768 |
+
.retry-drill-panel {
|
| 7769 |
+
max-height: min(82vh, 760px);
|
| 7770 |
+
overflow-y: auto;
|
| 7771 |
}
|
| 7772 |
|
| 7773 |
.sc-btn-conversation:hover {
|
|
|
|
| 7820 |
/* ---- Body grid ---- */
|
| 7821 |
.sc-body-grid {
|
| 7822 |
display: grid;
|
| 7823 |
+
grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
|
|
|
|
| 7824 |
width: 100%;
|
| 7825 |
+
align-items: start;
|
| 7826 |
+
gap: 0;
|
| 7827 |
}
|
| 7828 |
|
| 7829 |
.sc-left-col {
|
| 7830 |
+
padding: clamp(10px, 1.8vw, 14px) clamp(12px, 2vw, 16px);
|
| 7831 |
border-right: 1px solid rgba(255, 255, 255, 0.06);
|
| 7832 |
min-width: 0;
|
| 7833 |
+
max-height: min(72vh, 680px);
|
| 7834 |
+
overflow-y: auto;
|
| 7835 |
+
overscroll-behavior: contain;
|
| 7836 |
}
|
| 7837 |
|
| 7838 |
.sc-section-label {
|
|
|
|
| 7904 |
.sc-dim-list .dimension-row-scorecard {
|
| 7905 |
display: flex;
|
| 7906 |
flex-direction: column;
|
| 7907 |
+
gap: 8px;
|
| 7908 |
+
padding: 10px 12px;
|
| 7909 |
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 7910 |
border-radius: 12px;
|
| 7911 |
background: rgba(255, 255, 255, 0.03);
|
|
|
|
| 8082 |
min-width: 0;
|
| 8083 |
display: flex;
|
| 8084 |
flex-direction: column;
|
|
|
|
| 8085 |
}
|
| 8086 |
|
| 8087 |
.sc-tabs {
|
|
|
|
| 8116 |
|
| 8117 |
.sc-tab-panel {
|
| 8118 |
display: none;
|
| 8119 |
+
padding: clamp(12px, 2vw, 16px) clamp(14px, 2vw, 18px);
|
| 8120 |
overflow-y: auto;
|
| 8121 |
max-height: none;
|
| 8122 |
}
|
|
|
|
| 8167 |
|
| 8168 |
.sc-voice-inline {
|
| 8169 |
margin-top: 4px;
|
| 8170 |
+
width: 100%;
|
| 8171 |
+
}
|
| 8172 |
+
|
| 8173 |
+
.sc-voice-inline .voice-delivery-wrap,
|
| 8174 |
+
.sc-voice-inline .voice-delivery-layout {
|
| 8175 |
+
width: 100%;
|
| 8176 |
+
}
|
| 8177 |
+
|
| 8178 |
+
.sc-voice-inline .voice-delivery-metrics {
|
| 8179 |
+
display: flex;
|
| 8180 |
+
flex-direction: row;
|
| 8181 |
+
flex-wrap: nowrap;
|
| 8182 |
+
gap: 0.45rem;
|
| 8183 |
+
width: 100%;
|
| 8184 |
+
}
|
| 8185 |
+
|
| 8186 |
+
.sc-voice-inline .voice-delivery-box {
|
| 8187 |
+
flex: 1 1 0;
|
| 8188 |
+
min-width: 0;
|
| 8189 |
+
}
|
| 8190 |
+
|
| 8191 |
+
@media (max-width: 720px) {
|
| 8192 |
+
.sc-voice-inline .voice-delivery-metrics {
|
| 8193 |
+
display: grid;
|
| 8194 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 8195 |
+
flex-wrap: wrap;
|
| 8196 |
+
}
|
| 8197 |
+
|
| 8198 |
+
.sc-voice-inline .voice-delivery-box {
|
| 8199 |
+
flex: unset;
|
| 8200 |
+
}
|
| 8201 |
}
|
| 8202 |
|
| 8203 |
.sc-answers-stack {
|
|
|
|
| 8333 |
color: rgba(255, 255, 255, 0.65);
|
| 8334 |
}
|
| 8335 |
|
| 8336 |
+
/* ---- Judge Verdict Modal ---- */
|
| 8337 |
+
.verdict-overlay {
|
| 8338 |
+
position: fixed;
|
| 8339 |
+
inset: 0;
|
| 8340 |
+
background: rgba(0, 0, 0, 0.72);
|
| 8341 |
+
z-index: 55;
|
| 8342 |
+
overflow-y: auto;
|
| 8343 |
+
display: flex;
|
| 8344 |
+
align-items: center;
|
| 8345 |
+
justify-content: center;
|
| 8346 |
+
padding: 1rem;
|
| 8347 |
}
|
| 8348 |
|
| 8349 |
+
.verdict-overlay[hidden] {
|
| 8350 |
+
display: none;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8351 |
}
|
| 8352 |
|
| 8353 |
+
.verdict-modal-panel {
|
| 8354 |
+
width: 100%;
|
| 8355 |
+
max-width: 640px;
|
| 8356 |
+
padding: clamp(0.9rem, 2vw, 1.15rem);
|
| 8357 |
+
margin: auto;
|
| 8358 |
+
max-height: min(80vh, 720px);
|
| 8359 |
+
overflow-y: auto;
|
| 8360 |
+
}
|
| 8361 |
+
|
| 8362 |
+
.verdict-modal-header {
|
| 8363 |
+
display: flex;
|
| 8364 |
+
justify-content: space-between;
|
| 8365 |
+
align-items: flex-start;
|
| 8366 |
+
gap: 1rem;
|
| 8367 |
+
margin-bottom: 0.75rem;
|
| 8368 |
+
}
|
| 8369 |
+
|
| 8370 |
+
.verdict-modal-title {
|
| 8371 |
+
margin: 0;
|
| 8372 |
+
font-size: 1.2rem;
|
| 8373 |
+
}
|
| 8374 |
+
|
| 8375 |
+
.sc-verdict-modal-card {
|
| 8376 |
+
padding: 14px 16px;
|
| 8377 |
+
border-radius: 10px;
|
| 8378 |
+
background: rgba(255, 255, 255, 0.03);
|
| 8379 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 8380 |
+
border-top: 2px solid rgba(0, 230, 200, 0.35);
|
| 8381 |
+
overflow: visible;
|
| 8382 |
+
}
|
| 8383 |
+
|
| 8384 |
+
.verdict-deal-locked-msg {
|
| 8385 |
+
margin: 10px 0 0;
|
| 8386 |
+
padding: 10px 12px;
|
| 8387 |
+
border-radius: 8px;
|
| 8388 |
+
font-size: 13px;
|
| 8389 |
+
font-weight: 600;
|
| 8390 |
+
color: rgba(255, 255, 255, 0.65);
|
| 8391 |
+
background: rgba(255, 80, 80, 0.08);
|
| 8392 |
+
border: 1px solid rgba(255, 80, 80, 0.2);
|
| 8393 |
+
}
|
| 8394 |
+
|
| 8395 |
+
.verdict-modal-footer {
|
| 8396 |
+
margin-top: 1rem;
|
| 8397 |
+
display: flex;
|
| 8398 |
+
flex-direction: column;
|
| 8399 |
+
gap: 0.75rem;
|
| 8400 |
+
}
|
| 8401 |
+
|
| 8402 |
+
.verdict-modal-actions {
|
| 8403 |
+
display: flex;
|
| 8404 |
+
flex-wrap: wrap;
|
| 8405 |
+
gap: 0.5rem;
|
| 8406 |
+
}
|
| 8407 |
+
|
| 8408 |
+
.verdict-modal-footer-secondary {
|
| 8409 |
+
display: flex;
|
| 8410 |
+
flex-wrap: wrap;
|
| 8411 |
+
gap: 0.5rem;
|
| 8412 |
}
|
| 8413 |
|
| 8414 |
.sc-verdict-card {
|
|
|
|
| 8563 |
-webkit-line-clamp: unset;
|
| 8564 |
}
|
| 8565 |
|
| 8566 |
+
.sc-prep-empty .sc-prep-retry-box {
|
| 8567 |
+
margin-top: 0.75rem;
|
| 8568 |
+
}
|
| 8569 |
+
|
| 8570 |
+
.sc-answers-empty {
|
| 8571 |
+
padding: 1rem;
|
| 8572 |
+
border-radius: 10px;
|
| 8573 |
+
background: rgba(255, 255, 255, 0.03);
|
| 8574 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 8575 |
+
}
|
| 8576 |
+
|
| 8577 |
+
.sc-empty-title {
|
| 8578 |
+
margin: 0 0 6px;
|
| 8579 |
+
font-size: 15px;
|
| 8580 |
+
font-weight: 700;
|
| 8581 |
+
color: rgba(255, 255, 255, 0.9);
|
| 8582 |
+
}
|
| 8583 |
+
|
| 8584 |
+
.sc-empty-sub {
|
| 8585 |
+
margin: 0 0 12px;
|
| 8586 |
+
font-size: 13px;
|
| 8587 |
+
color: rgba(255, 255, 255, 0.6);
|
| 8588 |
+
line-height: 1.5;
|
| 8589 |
+
}
|
| 8590 |
+
|
| 8591 |
+
/* ---- HF Spaces / responsive polish ---- */
|
| 8592 |
+
@media (max-width: 1100px) {
|
| 8593 |
+
.briefing-grid,
|
| 8594 |
+
.briefing-grid-wide {
|
| 8595 |
+
grid-template-columns: 1fr;
|
| 8596 |
+
}
|
| 8597 |
+
|
| 8598 |
+
.sc-body-grid {
|
| 8599 |
+
grid-template-columns: 1fr;
|
| 8600 |
+
}
|
| 8601 |
+
|
| 8602 |
+
.sc-left-col {
|
| 8603 |
+
border-right: none;
|
| 8604 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
| 8605 |
+
}
|
| 8606 |
+
|
| 8607 |
+
.sc-hero-actions-secondary {
|
| 8608 |
+
margin-left: 0;
|
| 8609 |
+
width: 100%;
|
| 8610 |
+
}
|
| 8611 |
+
}
|
| 8612 |
+
|
| 8613 |
@media (max-width: 960px) {
|
| 8614 |
.sc-shell {
|
| 8615 |
+
padding: 14px 12px;
|
| 8616 |
}
|
| 8617 |
|
| 8618 |
.sc-hero-row {
|
| 8619 |
flex-wrap: wrap;
|
| 8620 |
}
|
| 8621 |
|
| 8622 |
+
.sc-hero-actions-row {
|
| 8623 |
+
flex-direction: column;
|
| 8624 |
+
align-items: stretch;
|
|
|
|
| 8625 |
}
|
| 8626 |
|
| 8627 |
+
.sc-hero-actions-primary,
|
| 8628 |
+
.sc-hero-actions-secondary {
|
| 8629 |
width: 100%;
|
| 8630 |
+
}
|
| 8631 |
+
|
| 8632 |
+
.sc-btn-conversation,
|
| 8633 |
+
.sc-btn-verdict {
|
| 8634 |
+
flex: 1;
|
| 8635 |
+
min-width: 140px;
|
| 8636 |
text-align: center;
|
| 8637 |
}
|
| 8638 |
|
| 8639 |
+
.sc-verdict-meta {
|
| 8640 |
grid-template-columns: 1fr;
|
| 8641 |
}
|
| 8642 |
|
| 8643 |
+
.verdict-modal-panel {
|
| 8644 |
+
max-width: 100%;
|
|
|
|
| 8645 |
}
|
| 8646 |
+
}
|
| 8647 |
|
| 8648 |
+
@media (max-width: 768px) {
|
| 8649 |
+
.arena-title {
|
| 8650 |
+
font-size: clamp(1.75rem, 8vw, 2.5rem);
|
| 8651 |
+
}
|
| 8652 |
+
|
| 8653 |
+
.founder-silhouette,
|
| 8654 |
+
.judge-silhouette {
|
| 8655 |
+
transform: scale(0.72);
|
| 8656 |
+
opacity: 0.55;
|
| 8657 |
+
}
|
| 8658 |
+
|
| 8659 |
+
.sc-tabs {
|
| 8660 |
+
overflow-x: auto;
|
| 8661 |
+
flex-wrap: nowrap;
|
| 8662 |
+
-webkit-overflow-scrolling: touch;
|
| 8663 |
+
}
|
| 8664 |
+
|
| 8665 |
+
.sc-tab {
|
| 8666 |
+
flex-shrink: 0;
|
| 8667 |
+
}
|
| 8668 |
+
|
| 8669 |
+
.sc-dim-list .dimension-name {
|
| 8670 |
+
font-size: 13px;
|
| 8671 |
+
}
|
| 8672 |
+
}
|
| 8673 |
+
|
| 8674 |
+
@media (max-height: 760px) {
|
| 8675 |
+
.arena-landing {
|
| 8676 |
+
min-height: auto;
|
| 8677 |
+
padding: 0.5rem 0;
|
| 8678 |
}
|
| 8679 |
|
| 8680 |
+
.arena-landing-content {
|
| 8681 |
+
padding-top: 0.75rem;
|
| 8682 |
+
padding-bottom: 0.75rem;
|
| 8683 |
+
}
|
| 8684 |
+
|
| 8685 |
+
.founder-silhouette,
|
| 8686 |
+
.judge-silhouette {
|
| 8687 |
+
transform: scale(0.68);
|
| 8688 |
+
opacity: 0.5;
|
| 8689 |
+
}
|
| 8690 |
+
|
| 8691 |
+
.briefing-header {
|
| 8692 |
+
margin-bottom: 0.45rem;
|
| 8693 |
+
}
|
| 8694 |
+
|
| 8695 |
+
.briefing-title {
|
| 8696 |
+
font-size: clamp(1.25rem, 2.5vw, 1.55rem);
|
| 8697 |
+
}
|
| 8698 |
+
|
| 8699 |
+
.briefing-subtitle {
|
| 8700 |
+
font-size: 0.82rem;
|
| 8701 |
+
}
|
| 8702 |
+
|
| 8703 |
+
.quick-pitch-textarea {
|
| 8704 |
+
min-height: 88px;
|
| 8705 |
+
max-height: 110px;
|
| 8706 |
+
}
|
| 8707 |
+
|
| 8708 |
+
.advanced-briefing-form textarea {
|
| 8709 |
+
min-height: 44px;
|
| 8710 |
+
max-height: 58px;
|
| 8711 |
+
}
|
| 8712 |
+
|
| 8713 |
+
.persona-grid,
|
| 8714 |
+
.difficulty-grid {
|
| 8715 |
+
gap: 0.45rem;
|
| 8716 |
+
}
|
| 8717 |
+
|
| 8718 |
+
.briefing-opponent-panel .persona-card,
|
| 8719 |
+
.briefing-opponent-panel .difficulty-card {
|
| 8720 |
+
padding: 0.45rem 0.5rem;
|
| 8721 |
+
}
|
| 8722 |
+
|
| 8723 |
+
.persona-card,
|
| 8724 |
+
.difficulty-card {
|
| 8725 |
+
padding: 0.55rem 0.65rem;
|
| 8726 |
+
}
|
| 8727 |
+
|
| 8728 |
+
.sc-hero {
|
| 8729 |
+
padding-top: 10px;
|
| 8730 |
+
padding-bottom: 10px;
|
| 8731 |
+
}
|
| 8732 |
+
|
| 8733 |
+
.sc-ring {
|
| 8734 |
+
width: 68px;
|
| 8735 |
+
height: 68px;
|
| 8736 |
+
min-width: 68px;
|
| 8737 |
+
min-height: 68px;
|
| 8738 |
+
}
|
| 8739 |
+
|
| 8740 |
+
.sc-ring .score-orb-value {
|
| 8741 |
+
font-size: 22px;
|
| 8742 |
+
}
|
| 8743 |
}
|
| 8744 |
|
| 8745 |
@media (max-width: 720px) {
|