diff --git a/.gitignore b/.gitignore
index 0b32899..bd0d6a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
 .codex/
 .claude/
 .devloop/
-.specs/
 .DS_Store
 coverage/
diff --git a/AGENTS.md b/AGENTS.md
index 4bc5302..c7610dc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -8,7 +8,7 @@ This is a Bash CLI project. The active runtime is the root `devloop` executable.
 
 - `bash scripts/devloop_test.sh`: run the shell test suite.
 - `./scripts/install.sh`: link `devloop` into `~/.local/bin` or `DEVLOOP_BIN_DIR`.
-- `./devloop --plain .specs/change.md`: example local CLI invocation from a target git worktree.
+- `./devloop --plain .devloop/specs/change.md`: example local CLI invocation from a target git worktree.
 - `./scripts/release.sh patch --dry-run`: validate the release path without changing files.
 
 ## Coding Style & Naming Conventions
diff --git a/README.md b/README.md
index 8f4d376..6f0da7b 100644
--- a/README.md
+++ b/README.md
@@ -28,8 +28,8 @@ devloop doctor
 ```sh
 devloop
 devloop spec "add retry behavior to the chat sender"
-devloop .specs/change.md
-devloop --create-pr .specs/change.md
+devloop .devloop/specs/change.md
+devloop --create-pr .devloop/specs/change.md
 devloop continue
 devloop status
 devloop clean
@@ -50,7 +50,7 @@ devloop spec --agent claude notes.md
 
 Strict mode is on by default. Specs need `## Acceptance criteria`, and reviews must pass both the spec gate and engineering quality gate.
 
-Devloop stores shared settings in `~/.devloop/config`. The default spec directory is `~/Projects/specs/`; the picker also searches the current repo's `.specs/` directory.
+Devloop stores shared settings in `~/.devloop/config`. The default spec directory is the current repo's `.devloop/specs/`. Set a custom `spec_dir` (global or per-repo) to point elsewhere, for example `~/Projects/specs`; the picker searches both the custom directory and the repo's `.devloop/specs/`.
 
 ## PR mode
 
diff --git a/devloop b/devloop
index ffa9103..716a2da 100755
--- a/devloop
+++ b/devloop
@@ -6,7 +6,7 @@ CODEX_REASONING_ARGS=(-c 'model_reasoning_effort="xhigh"')
 CLAUDE_MODEL_ARGS=(--model claude-opus-4-8)
 CLAUDE_EFFORT_ARGS=(--effort max)
 DEFAULT_TIMEOUT_MINUTES=30
-DEFAULT_SPEC_DIR_SUFFIX="Projects/specs"
+DEFAULT_SPEC_DIR=".devloop/specs"
 
 SCRIPT_PATH="${BASH_SOURCE[0]}"
 while [ -L "$SCRIPT_PATH" ]; do
@@ -178,12 +178,12 @@ Common commands:
   devloop continue
   devloop menu
   devloop spec "add retry behavior to the chat sender"
-  devloop .specs/change.md
-  devloop --tui .specs/change.md
-  devloop --plain .specs/change.md
-  devloop --report-format markdown .specs/change.md 3
-  devloop --coder claude --reviewer codex .specs/change.md
-  devloop --create-pr .specs/change.md
+  devloop .devloop/specs/change.md
+  devloop --tui .devloop/specs/change.md
+  devloop --plain .devloop/specs/change.md
+  devloop --report-format markdown .devloop/specs/change.md 3
+  devloop --coder claude --reviewer codex .devloop/specs/change.md
+  devloop --create-pr .devloop/specs/change.md
 
 Options:
   --tui                         force terminal UI output
@@ -216,8 +216,8 @@ welcome_tui() {
   printf '  %-42s %s\n' "devloop continue" "resume a tracked run"
   printf '  %-42s %s\n' "devloop menu" "open the guided UI"
   printf '  %-42s %s\n' 'devloop spec "add retry behavior"' "launch a spec agent"
-  printf '  %-42s %s\n' "devloop .specs/change.md" "run a spec"
-  printf '  %-42s %s\n' "devloop --create-pr .specs/change.md" "open and maintain a draft PR during the loop"
+  printf '  %-42s %s\n' "devloop .devloop/specs/change.md" "run a spec"
+  printf '  %-42s %s\n' "devloop --create-pr .devloop/specs/change.md" "open and maintain a draft PR during the loop"
   printf '\n'
   gum style --foreground "$UI_ACCENT_COLOR" --bold "Options"
   printf '  %-30s %s\n' "--tui" "force terminal UI output"
@@ -328,32 +328,22 @@ config_file_value() {
 }
 
 devloop_default_spec_dir() {
-  if [ -n "${HOME:-}" ]; then
-    printf '%s/%s\n' "${HOME%/}" "$DEFAULT_SPEC_DIR_SUFFIX"
+  local root
+  if root="$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null)"; then
+    printf '%s/%s\n' "${root%/}" "$DEFAULT_SPEC_DIR"
   else
-    printf '%s\n' ".specs"
+    printf '%s\n' "$DEFAULT_SPEC_DIR"
   fi
 }
 
 ensure_global_config() {
-  local file value spec_dir
+  local file value
   file="$(devloop_global_config_file 2>/dev/null)" || return 0
   mkdir -p "$(dirname "$file")" || return 1
   if [ ! -f "$file" ]; then
-    spec_dir="$(devloop_default_spec_dir)"
-    mkdir -p "$spec_dir" || return 1
-    {
-      printf 'spec_dir=%s\n' "$spec_dir"
-      printf 'timeout_minutes=%s\n' "$DEFAULT_TIMEOUT_MINUTES"
-    } > "$file"
+    printf 'timeout_minutes=%s\n' "$DEFAULT_TIMEOUT_MINUTES" > "$file"
     return 0
   fi
-  value="$(config_file_value spec_dir "$file" || true)"
-  if [ -z "$value" ]; then
-    spec_dir="$(devloop_default_spec_dir)"
-    mkdir -p "$spec_dir" || return 1
-    printf 'spec_dir=%s\n' "$spec_dir" >> "$file"
-  fi
   value="$(config_file_value timeout_minutes "$file" || true)"
   if [ -z "$value" ]; then
     printf 'timeout_minutes=%s\n' "$DEFAULT_TIMEOUT_MINUTES" >> "$file"
@@ -498,7 +488,7 @@ spec_search_dirs() {
   local configured dir seen
   configured="$(devloop_spec_dir)"
   seen=""
-  for dir in "$configured" ".specs"; do
+  for dir in "$configured" "$(devloop_default_spec_dir)"; do
     [ -n "$dir" ] || continue
     case "$seen" in
       *"|$dir|"*) continue ;;
@@ -1204,13 +1194,12 @@ interactive_settings() {
     if [ -n "$custom_spec_dir" ]; then
       ui_print_key_values \
         "configured" "$custom_spec_dir" \
-        "fallback" ".specs" \
+        "fallback" "$default_spec_dir" \
         "timeout" "$timeout_display"
       choices=("Remove spec path" "Set timeout" "Back")
     else
       ui_print_key_values \
         "default" "$default_spec_dir" \
-        "fallback" ".specs" \
         "timeout" "$timeout_display"
       choices=("Add spec path" "Set timeout" "Back")
     fi
diff --git a/scripts/devloop_test.sh b/scripts/devloop_test.sh
index 88bb9bd..e38c6ac 100755
--- a/scripts/devloop_test.sh
+++ b/scripts/devloop_test.sh
@@ -323,30 +323,29 @@ equals "$(clean_candidate_status "$clean_status_track" "$clean_status_repo" "$wo
 
 config_repo="$work/config-repo"
 config_home="$work/config-home"
-config_default_specs="$config_home/Projects/specs"
-mkdir -p "$config_repo/.specs" "$config_repo/.devloop/specs" "$config_home"
-config_repo_real="$(cd "$config_repo" && pwd)"
+git init -q "$config_repo"
+config_repo_real="$(cd "$config_repo" && pwd -P)"
+config_default_specs="$config_repo_real/.devloop/specs"
+mkdir -p "$config_repo/.devloop/specs" "$config_home"
 equals "$(HOME="$config_home" devloop_config_file)" "$config_home/.devloop/config" "default config file"
-printf '%s\n' "# Default" > "$config_repo/.specs/default.md"
 printf '%s\n' "# Devloop" > "$config_repo/.devloop/specs/devloop.md"
 config_specs="$(cd "$config_repo" && HOME="$config_home" list_spec_files)"
 [[ -f "$config_home/.devloop/config" ]] || fail "global config was not created"
-contains "$(cat "$config_home/.devloop/config")" "spec_dir=$config_default_specs" "global config default spec dir"
+if grep -q "spec_dir=" "$config_home/.devloop/config"; then fail "global config seeded a default spec dir"; fi
 contains "$(cat "$config_home/.devloop/config")" "timeout_minutes=30" "global config default timeout"
-[[ -d "$config_default_specs" ]] || fail "global default spec dir was not created"
-contains "$config_specs" ".specs/default.md" "default spec search"
-if printf '%s\n' "$config_specs" | grep -Fq ".devloop/specs/devloop.md"; then fail "default spec search included .devloop/specs"; fi
-equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$config_default_specs" "default global spec dir"
-equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs, .specs" "spec search label"
-if (cd "$config_repo" && HOME="$config_home" configured_spec_dir) >/dev/null 2>&1; then fail "default global spec dir reported as custom"; fi
+contains "$config_specs" "$config_default_specs/devloop.md" "default spec search uses repo .devloop/specs"
+equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$config_default_specs" "default spec dir is repo .devloop/specs"
+equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs" "spec search label is single default dir"
+if (cd "$config_repo" && HOME="$config_home" configured_spec_dir) >/dev/null 2>&1; then fail "default spec dir reported as custom"; fi
 equals "$(cd "$config_repo" && HOME="$config_home" write_config_spec_dir local "custom-specs")" "custom-specs" "write local config spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "custom-specs" "configured spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir)" "custom-specs" "custom spec dir"
 equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir_scope)" "local" "custom spec dir scope"
 [[ -d "$config_repo/custom-specs" ]] || fail "configured spec dir was not created"
+printf '%s\n' "# Custom" > "$config_repo/custom-specs/custom.md"
 configured_specs="$(cd "$config_repo" && HOME="$config_home" list_spec_files)"
-contains "$configured_specs" ".specs/default.md" "configured spec search includes default dir"
-if printf '%s\n' "$configured_specs" | grep -Fq ".devloop/specs/devloop.md"; then fail "configured spec search included .devloop/specs"; fi
+contains "$configured_specs" "custom-specs/custom.md" "configured spec search includes custom dir"
+contains "$configured_specs" "$config_default_specs/devloop.md" "configured spec search still includes default"
 if (cd "$config_repo" && HOME="$config_home" write_config_spec_dir "../bad") >/dev/null 2>&1; then fail "write_config_spec_dir accepted path traversal"; fi
 
 absolute_specs="$work/shared-specs"
@@ -357,13 +356,13 @@ equals "$(cd "$config_repo" && HOME="$config_home" configured_spec_dir)" "$absol
 printf '%s\n' "# Shared" > "$absolute_specs/shared.md"
 absolute_configured_specs="$(cd "$config_repo" && HOME="$config_home" list_spec_files)"
 contains "$absolute_configured_specs" "$absolute_specs/shared.md" "configured spec search includes absolute dir"
-contains "$absolute_configured_specs" ".specs/default.md" "absolute spec search includes default dir"
+contains "$absolute_configured_specs" "$config_default_specs/devloop.md" "absolute spec search still includes default"
 equals "$(spec_dir_status "$absolute_specs")" "exists" "spec dir status exists"
 equals "$(spec_dir_status "$work/missing-specs")" "missing" "spec dir status missing"
 (cd "$config_repo" && HOME="$config_home" remove_config_spec_dir local)
 if (cd "$config_repo" && HOME="$config_home" configured_spec_dir) >/dev/null 2>&1; then fail "custom spec dir was not removed"; fi
 equals "$(cd "$config_repo" && HOME="$config_home" devloop_spec_dir)" "$config_default_specs" "removed custom spec dir falls back"
-equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs, .specs" "removed custom spec search label"
+equals "$(cd "$config_repo" && HOME="$config_home" spec_search_label)" "$config_default_specs" "removed custom spec search label"
 
 global_repo="$work/global-repo"
 global_home="$work/global-home"
@@ -405,7 +404,7 @@ raw_tilde_repo="$work/raw-tilde-repo"
 raw_tilde_home="$work/raw-tilde-home"
 mkdir -p "$raw_tilde_repo/.devloop" "$raw_tilde_home"
 printf '%s\n' "spec_dir=~/raw-specs" > "$raw_tilde_repo/.devloop/config"
-equals "$(cd "$raw_tilde_repo" && HOME="$raw_tilde_home" devloop_spec_dir)" "$raw_tilde_home/Projects/specs" "raw tilde config falls back"
+equals "$(cd "$raw_tilde_repo" && HOME="$raw_tilde_home" devloop_spec_dir)" ".devloop/specs" "raw tilde config falls back to default"
 
 equals "$(normalize_timeout_minutes 1)" "1" "timeout lower bound"
 equals "$(normalize_timeout_minutes 30)" "30" "timeout normalize"
@@ -488,8 +487,8 @@ if ! ( cd "$empty_spec_repo" && UI_BACK=false; interactive_open_report >/dev/nul
 USE_TUI="$old_use_tui"
 
 cancel_spec_repo="$work/cancel-spec-repo"
-mkdir -p "$cancel_spec_repo/.specs"
-printf '%s\n' "# Cancel" > "$cancel_spec_repo/.specs/cancel.md"
+mkdir -p "$cancel_spec_repo/.devloop/specs"
+printf '%s\n' "# Cancel" > "$cancel_spec_repo/.devloop/specs/cancel.md"
 if ! ( cd "$cancel_spec_repo" && ui_pick_from_file() { return 130; }; UI_BACK=false; interactive_run_spec >/dev/null 2>&1; [ "$UI_BACK" = true ] ); then fail "interactive_run_spec escape navigation"; fi
 
 picker_file="$work/picker.txt"
@@ -671,6 +670,8 @@ contains "$remote_install_output" "$remote_default_bin is not on PATH" "remote i
 contains "$remote_install_output" "export PATH=\"$remote_default_bin:\$PATH\"" "remote install PATH guidance"
 contains "$remote_install_output" "[ok] gum:" "remote install UI check"
 contains "$remote_install_output" "[ok] codex:" "remote install agent check"
+contains "$remote_install_output" "devloop $remote_version installed" "remote install banner version"
+contains "$remote_install_output" "try: devloop" "remote install banner try line"
 [[ -f "$remote_home/.agents/skills/devloop-spec/SKILL.md" ]] || fail "remote installer did not install Codex spec skill"
 [[ -f "$remote_home/.agents/skills/devloop-review/.devloop-checksum" ]] || fail "remote installer did not write Codex skill checksum"
 [[ -f "$remote_home/.claude/skills/devloop-spec/SKILL.md" ]] || fail "remote installer did not install Claude spec skill"
@@ -746,7 +747,7 @@ ok "installer"
 printf '%s\n' "user edit" >> "$install_home/.agents/skills/devloop-review/SKILL.md"
 DEVLOOP_BIN_DIR="$bin_dir" HOME="$install_home" PATH="$install_path" "$SCRIPTS_DIR/install.sh" >/tmp/devloop-install-skip.out 2>&1
 contains "$(cat /tmp/devloop-install-skip.out)" "skipping modified skill" "installer modified skill guard"
-contains "$(cat /tmp/devloop-install-skip.out)" "try: devloop doctor" "installer guidance after skill skip"
+contains "$(cat /tmp/devloop-install-skip.out)" "try: devloop" "installer guidance after skill skip"
 contains "$(cat "$install_home/.agents/skills/devloop-review/SKILL.md")" "user edit" "installer modified skill preserved"
 DEVLOOP_FORCE=1 DEVLOOP_BIN_DIR="$bin_dir" HOME="$install_home" PATH="$install_path" "$SCRIPTS_DIR/install.sh" >/tmp/devloop-install-force.out
 if grep -q "user edit" "$install_home/.agents/skills/devloop-review/SKILL.md"; then fail "installer force did not restore skill"; fi
diff --git a/scripts/install.remote.sh b/scripts/install.remote.sh
index 7bb3a2e..1cdb195 100755
--- a/scripts/install.remote.sh
+++ b/scripts/install.remote.sh
@@ -11,6 +11,31 @@ YES=false
 NO_SKILLS=false
 DRY_RUN=false
 
+if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
+  C_ACCENT=$'\033[38;5;141m'
+  C_DIM=$'\033[38;5;244m'
+  C_BOLD=$'\033[1m'
+  C_RESET=$'\033[0m'
+else
+  C_ACCENT=""
+  C_DIM=""
+  C_BOLD=""
+  C_RESET=""
+fi
+
+print_banner() {
+  local version="$1"
+  printf '\n%s' "$C_ACCENT"
+  cat <<'EOF'
+░█▀▄░█▀▀░█░█░█░░░█▀█░█▀█░█▀█
+░█░█░█▀▀░▀▄▀░█░░░█░█░█░█░█▀▀
+░▀▀░░▀▀▀░░▀░░▀▀▀░▀▀▀░▀▀▀░▀░░
+EOF
+  printf '%s\n' "$C_RESET"
+  printf '  %sdevloop %s installed%s\n' "$C_DIM" "$version" "$C_RESET"
+  printf '  %stry:%s %s%sdevloop%s\n\n' "$C_DIM" "$C_RESET" "$C_BOLD" "$C_ACCENT" "$C_RESET"
+}
+
 usage() {
   cat <<'EOF'
 usage: install.remote.sh [options]
@@ -352,8 +377,7 @@ main() {
   check_ui_tools "$ui_missing"
   check_agent_tools "$agent_missing"
   print_path_guidance
-  info ""
-  info "try: devloop doctor"
+  print_banner "$version"
 }
 
 main "$@"
diff --git a/scripts/install.sh b/scripts/install.sh
index 85bf18d..07d2165 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -21,6 +21,31 @@ SOURCE="$ROOT/devloop"
 SKILL_STATUS=0
 TOOL_STATUS=0
 
+if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
+  C_ACCENT=$'\033[38;5;141m'
+  C_DIM=$'\033[38;5;244m'
+  C_BOLD=$'\033[1m'
+  C_RESET=$'\033[0m'
+else
+  C_ACCENT=""
+  C_DIM=""
+  C_BOLD=""
+  C_RESET=""
+fi
+
+print_banner() {
+  local version="$1"
+  printf '\n%s' "$C_ACCENT"
+  cat <<'EOF'
+░█▀▄░█▀▀░█░█░█░░░█▀█░█▀█░█▀█
+░█░█░█▀▀░▀▄▀░█░░░█░█░█░█░█▀▀
+░▀▀░░▀▀▀░░▀░░▀▀▀░▀▀▀░▀▀▀░▀░░
+EOF
+  printf '%s\n' "$C_RESET"
+  printf '  %sdevloop %s installed%s\n' "$C_DIM" "$version" "$C_RESET"
+  printf '  %stry:%s %s%sdevloop%s\n\n' "$C_DIM" "$C_RESET" "$C_BOLD" "$C_ACCENT" "$C_RESET"
+}
+
 install_required_ui_tools() {
   local missing=()
   local tool
@@ -76,8 +101,7 @@ case ":${PATH:-}:" in
     ;;
 esac
 
-echo
-echo "try: devloop doctor"
+print_banner "$(cat "$ROOT/VERSION" 2>/dev/null)"
 if [ "$TOOL_STATUS" -ne 0 ] || [ "$SKILL_STATUS" -ne 0 ]; then
   exit 1
 fi
diff --git a/skills/devloop-spec/SKILL.md b/skills/devloop-spec/SKILL.md
index 1012ca8..d23ee5e 100644
--- a/skills/devloop-spec/SKILL.md
+++ b/skills/devloop-spec/SKILL.md
@@ -124,4 +124,4 @@ Keep every standard section present, remove leftover placeholders, and list the
 
 ## Output
 
-When a caller provides an output path, write the spec there. Otherwise, write only the markdown spec to stdout or save it under the caller's requested default spec directory, usually `.specs/YYYY-MM-DD-<slug>.md`. Do not wrap the spec in a code fence unless the caller explicitly asks for a fenced snippet.
+When a caller provides an output path, write the spec there. Otherwise, write only the markdown spec to stdout or save it under the caller's requested default spec directory, usually `.devloop/specs/YYYY-MM-DD-<slug>.md`. Do not wrap the spec in a code fence unless the caller explicitly asks for a fenced snippet.
