/* --- Global Styles & Variables --- */
:root {
  --panel: #e9e4d5;
  --wallpaper: url("../assets/wallpapers/wallpaper1.webp");
  --file-icon-size: 60px;
  --file-icon-size-sm: 40px;
}

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

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
  position: relative;
  font-family: "Pixelify Sans", "Poppins", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, sans-serif;
  color: #000;
  background: #000 var(--wallpaper) center/cover no-repeat fixed;
  overflow: hidden;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

input,
textarea,
button,
select {
  font-family: "VT323", "monospace", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, sans-serif;
  font-optical-sizing: auto;
}

/* OS-wide selection. This used to be an unscoped `::selection` inside
   css/terminal.css, so one app was quietly setting it for every window; the
   colours are unchanged, only the ownership. Kept dark and flat rather than
   letting the browser's blue default through, which the design system rules
   out. css/matrix.css overrides it while that theme is on. */
::selection {
  background: #3e4451;
  color: #e0e0e0;
}

/* --- Boot Sequence --- */
#boot-screen {
  position: fixed;
  inset: 0;
  z-index: 99999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
  color: #fff;
  user-select: none;
  transition: opacity 0.42s ease, transform 0.42s ease, visibility 0s 0.42s;
}
#boot-screen.boot-hide {
  opacity: 0;
  transform: scale(0.995);
  visibility: hidden;
  pointer-events: none;
}
.boot-center {
  text-align: center;
  width: 90%;
  max-width: 420px;
  padding: 20px;
}
.boot-apple {
  width: 70px;
  margin-bottom: 5px;
}
.boot-progress-wrap {
  width: 70%;
  height: 8px;
  margin: 12px auto 8px;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.04);
  box-shadow: inset 0 -2px 0 rgba(255, 255, 255, 0.02);
  overflow: hidden;
}
.boot-progress {
  height: 100%;
  width: 0%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.98),
    rgba(220, 220, 220, 0.95)
  );
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.3);
  transition: width 160ms linear;
}
.boot-percent {
  margin-top: 6px;
  font-family: "Pixelify Sans", "Poppins", sans-serif;
  font-weight: 700;
  letter-spacing: 0.8px;
}
.boot-caption {
  margin-top: 8px;
  font-size: 12px;
  opacity: 0.85;
}
.boot-enter {
  display: inline-block;
  margin-top: 18px;
  padding: 10px 18px;
  border: 3px solid #000;
  border-radius: 6px;
  background: var(--panel);
  box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.9);
  font-family: "Pixelify Sans", "Poppins", sans-serif;
  font-weight: 800;
  letter-spacing: 1px;
  cursor: pointer;
  transition: transform 120ms ease;
}
.boot-enter:active {
  transform: translate(2px, 2px);
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.9);
}

/* --- Boot Notice ---
   Lives inside .boot-center between the logo and the progress bar: sound advice,
   and the one control that can put the browser away. It borrows nothing and adds
   no surface of its own — same black field and white text as the screen it sits
   on — because it is part of the boot screen rather than a dialog over it.

   Nothing here borrows body.has-fullscreen-window. That class means a maximized
   app window and hides the dock and the top bar, which browser fullscreen must
   not do. */
.boot-notice {
  max-width: 400px;
  margin: 16px auto 8px;
}
/* One class hides the offer, the line that explains it, and the Esc footnote,
   for a browser with no Fullscreen API to offer. */
#boot-notice.fullscreen-unavailable .boot-fullscreen-only {
  display: none;
}
.boot-notice-title {
  font-family: "Pixelify Sans", "Poppins", sans-serif;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 2px;
  text-transform: uppercase;
  opacity: 0.6;
}
.boot-notice-line {
  margin: 10px 0 0;
  font-family: "VT323", monospace;
  font-size: 20px;
  line-height: 1.25;
  /* Both lines wrap at some width, and an even break reads far better than one
     orphaned word. Ignored by browsers that do not know it, which only puts the
     wrap back where it would have been. */
  text-wrap: balance;
}
.boot-notice-note {
  margin: 12px 0 0;
  font-family: "VT323", monospace;
  font-size: 15px;
  opacity: 0.5;
}
.boot-notice-btn {
  margin-top: 16px;
  padding: 6px 18px;
  font-size: 19px;
  line-height: 1.1;
  color: #000;
  background: #f0f0f0;
  border: 3px solid #000;
  /* The house shadow. The hairline sitting a pixel behind it is what lets a
     black slab read as raised on a black screen, the same trick .boot-progress-wrap
     plays with its low-alpha white border. */
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9),
    5px 5px 0 1px rgba(255, 255, 255, 0.22);
  cursor: pointer;
}
.boot-notice-btn:hover {
  background: #fff;
}
.boot-notice-btn:active {
  transform: translate(2px, 2px);
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.9),
    3px 3px 0 1px rgba(255, 255, 255, 0.22);
}
.boot-notice-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

/* Three stacked elements on a phone, and the progress bar is the one that must
   not be pushed off the bottom. The Esc line goes because a viewport this size
   rarely has the key. This lives here rather than in css/responsive.css so the
   boot screen keeps all of its rules in one place. */
@media (max-width: 600px) {
  .boot-notice {
    margin-top: 12px;
  }
  .boot-notice-line {
    font-size: 18px;
  }
  .boot-notice-note {
    display: none;
  }
}

/* --- Loaders --- */
@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
.retro-loader {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'VT323', monospace;
  font-size: 24px;
  color: #000;
  height: 100%;
  width: 100%;
}
.retro-loader::after {
  content: '█';
  animation: blink 1s step-end infinite;
  margin-left: 4px;
  font-size: 20px;
}

/* --- General UI --- */
.grain {
  position: fixed;
  inset: 0;
  background-image: repeating-linear-gradient(
    rgba(0, 0, 0, 0.02) 0 1px,
    transparent 1px 4px
  );
  mix-blend-mode: multiply;
  pointer-events: none;
}
.desktop-icon img,
.finder-icon img,
#trash .finder-icon img,
.favourite-item img,
.dock-icon img {
  width: var(--file-icon-size);
  height: var(--file-icon-size);
  object-fit: contain;
  pointer-events: none;
}
/* Scenery label above a desktop icon column. Not draggable, not selectable,
   and deliberately not .desktop-icon so Finder never mirrors it as an app. */
.desktop-section-heading {
  position: absolute;
  z-index: 5;
  width: 100px;
  padding: 0 4px;
  text-align: center;
  pointer-events: none;
  user-select: none;
}
.desktop-section-heading span {
  display: block;
  font-family: "VT323", monospace;
  font-size: 18px;
  letter-spacing: 1px;
  color: #fff;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.85);
  line-height: 1.1;
}
.desktop-section-heading::after {
  content: "";
  display: block;
  height: 2px;
  margin: 5px 8px 0;
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.45);
}

.desktop-icon {
  position: absolute;
  width: 105px;
  padding: 8px;
  border-radius: 6px;
  text-align: center;
  user-select: none;
  cursor: grab;
  transition: background-color 0.2s;
}
.desktop-icon.selected {
  background: rgba(0, 0, 0, 0.78);
}
.desktop-icon.selected .android-folder-box {
  background: rgba(210, 210, 210, 0.55);
  border-color: rgba(255, 255, 255, 0.9);
}
.android-folder-box {
  width: 64px;
  height: 64px;
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1.5px solid rgba(255, 255, 255, 0.4);
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease, background 0.2s ease;
  overflow: hidden;
  margin: 0 auto;
}
.android-folder:hover .android-folder-box {
  background: rgba(255, 255, 255, 0.35);
  transform: scale(1.06);
}
.android-folder-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3px;
  padding: 6px;
}
.android-folder-grid img {
  width: 22px !important;
  height: 22px !important;
  object-fit: contain;
  border-radius: 4px;
  pointer-events: none;
}

/* --- Socials Expanded Popup --- */
.socials-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.92);
  z-index: 9000;
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1.5px solid rgba(255, 255, 255, 0.4);
  border-radius: 24px;
  padding: 20px 24px 24px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
  min-width: 280px;
  animation: popupOpen 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
@keyframes popupOpen {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.socials-popup-title {
  font-family: 'VT323', monospace;
  font-size: 22px;
  color: #fff;
  text-align: center;
  text-shadow: 1px 1px 3px rgba(0,0,0,0.6);
  margin-bottom: 16px;
  letter-spacing: 1px;
}
.socials-popup-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px;
}
.socials-popup-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  cursor: pointer;
}
.socials-popup-item img {
  width: 56px !important;
  height: 56px !important;
  object-fit: contain;
  transition: transform 0.15s ease;
}
.socials-popup-item:hover img {
  transform: scale(1.12);
}
.socials-popup-item span {
  font-family: 'VT323', monospace;
  font-size: 14px;
  color: #fff;
  text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
  text-align: center;
  max-width: 96px;
}

/* Dim overlay behind popup */
.socials-overlay {
  position: fixed;
  inset: 0;
  z-index: 8999;
  background: rgba(0, 0, 0, 0.35);
}

.desktop-icon.dragging {
  cursor: grabbing;
  z-index: 2000;
}
/* No hyphens, for the reason the matching rule in css/finder.css records: these
   labels are filenames as often as they are words, and a filename that grows a
   hyphen is a filename the player cannot match against the one they were told
   to look for. overflow-wrap still breaks a label too long for the column; it
   just does it silently. */
.desktop-icon span {
  display: block;
  font-size: 15px;
  color: white;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
  word-break: normal;
  overflow-wrap: anywhere;
  hyphens: manual;
  line-height: 1.2;
  padding: 2px 0;
  pointer-events: none;
}
.desktop-icon.selected span {
  color: #fff;
  text-shadow: none;
}
button {
  font-family: inherit;
  padding: 6px 8px;
  border: 1px solid #000;
  background: #eaeaea;
  cursor: pointer;
}

/* --- Top Bar & Menus --- */
.top-bar {
  display: flex;
  align-items: center;
  gap: 22px;
  padding: 8px 14px;
  background: rgba(255, 255, 255, 0.4);
}
.top-bar .left {
  display: flex;
  gap: 12px;
  align-items: center;
}
.top-bar .icon {
  padding: 4px 8px;
  border-radius: 3px;
  font-weight: 600;
  user-select: none;
  cursor: pointer;
}
.top-bar .icon.active {
  background: #000;
  color: #fff;
}
.logo {
  width: 24px;
  margin-top: 5px;
}
.top-bar .right-icons {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-left: auto;
}
.status-icon {
  display: flex;
  align-items: center;
  height: 25px;
  padding: 8px 8px;
  border-radius: 10px;
  cursor: pointer;
  transition: background-color 0.2s;
}
.status-icon:hover {
  background: rgba(255, 255, 255, 0.6);
}
.status-icon img {
  height: 25px !important;
  width: auto;
}
.battery {
  position: relative;
  width: 28px;
  height: 18px;
  padding: 2px;
  border: 2px solid #000;
  border-radius: 2px;
  background-color: rgba(255, 255, 255, 0.5);
  cursor: pointer;
  transition: background-color 0.2s;
}
.battery:hover {
  background-color: rgba(255, 255, 255, 0.8);
}
.battery::after {
  content: "";
  position: absolute;
  right: -5px;
  top: 3px;
  width: 3px;
  height: 8px;
  border-radius: 0 2px 2px 0;
  background-color: #000;
}
.battery-level {
  width: 80%;
  height: 100%;
  background-color: #32cd32;
  transition: width 0.5s ease;
}
.top-bar .clock {
  padding: 5px 12px;
  border-radius: 10px;
  font-family: "VT323", monospace, sans-serif;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: 0.5px;
  opacity: 0.85;
  cursor: pointer;
  transition: background-color 0.2s;
}
.top-bar .clock:hover {
  background: rgba(255, 255, 255, 0.6);
}
.menu {
  display: none;
  position: absolute;
  top: 46px;
  z-index: 10000;
  min-width: 160px;
  padding: 6px;
  border: 2px solid #000;
  background: rgba(255, 255, 255, 0.8);
}
.menu div {
  padding: 6px 8px;
  cursor: pointer;
}
.menu div:hover {
  background: #000;
  color: #fff;
}

/* --- Windows --- */
.window {
  position: absolute;
  display: none;
  overflow: hidden;
  width: 360px;
  min-width: 250px;
  border: 3px solid #000;
  background: var(--panel);
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.5);
}
/* The height is declared rather than left to the font, and .content below
   subtracts the same number, which is the whole of the fix for the pale strip
   that used to run along the bottom inside the border of every fixed-height
   window. .content asked for calc(100% - 45px) while the title bar actually
   measured 34.4px, so 10.6px of window background was left over — invisible
   against var(--panel), obvious under Finder's white and under the Internet
   window's black.

   A flex column on .window would be the tidier fix and is not available:
   js/system.js opens windows with an inline `display: block`, which no
   stylesheet can outrank. So the one unknown in the sum is removed instead by
   pinning it. 36px rather than 34.4 because both desktop themes add a border to
   this element — 2px under css/matrix.css, 1px under css/devil.css — and
   box-sizing: border-box puts those inside the number, so the sum stays exact
   in all three. It is also the figure #terminal has been subtracting on its own
   since it hit the same bug from the other side. */
.window .title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 36px;
  padding: 6px 8px;
  background: #333333;
  color: #fff;
  cursor: move;
}
.window .content {
  height: calc(100% - 36px);
  background: #fff;
  padding: 10px;
  overflow: auto;
}
.title .controls {
  display: flex;
  gap: 6px;
}
.ctrl {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 2px solid #000;
  border-radius: 50%;
  font-family: "VT323", monospace, sans-serif;
  font-size: 16px;
  font-weight: bold;
  line-height: 1;
  user-select: none;
  cursor: pointer;
}
.ctrl-close {
  background-color: #ff5f57;
  color: #000;
}
.ctrl-max {
  background-color: #febc2e;
  color: #000;
  font-family: Arial, sans-serif;
  font-weight: 900;
  font-size: 13px; /* Slightly smaller to match center fit with Arial */
}
.ctrl-min {
  background-color: #32cd32;
  color: #000;
}
.window.maximized {
  position: fixed !important;
  inset: 0 !important;
  border-radius: 0;
  box-shadow: none;
  z-index: 6001 !important;
}

body.has-fullscreen-window .dock,
body.has-fullscreen-window .top-bar {
  display: none !important;
}

/* --- Dock --- */
.dock {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5000;
  display: flex;
  gap: 5px;
  align-items: flex-end;
  padding: 10px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.4);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.dock-icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  cursor: pointer;
  transition: transform 140ms ease-out;
}
.dock-icon::before {
  content: "";
  position: absolute;
  z-index: 1;
  width: 72px;
  height: 72px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.6);
  transition: all 180ms cubic-bezier(0.2, 0.8, 0.2, 1.2);
}
.dock-icon img,
.dock-icon svg {
  position: relative;
  z-index: 2;
  transform-origin: center bottom;
  pointer-events: none;
  transition: all 180ms cubic-bezier(0.2, 0.8, 0.2, 1.2);
}
.dock-icon .label {
  position: absolute;
  bottom: 100%;
  z-index: 3;
  margin-bottom: 12px;
  padding: 4px 8px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  font-size: 11px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: all 140ms;
}

/* Notification Badge */
.dock-badge {
  position: absolute;
  top: 0px;
  right: 0px;
  z-index: 4;
  background-color: #ff3b30;
  color: #fff;
  font-family: 'VT323', monospace;
  font-size: 16px;
  font-weight: bold;
  min-width: 22px;
  height: 22px;
  border-radius: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.5);
  border: 1px solid #fff;
  pointer-events: none;
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1.2);
}

.dock-icon:hover {
  transform: translateY(-12px);
}
.dock-icon:hover::before {
  background: #000;
  transform: scale(1.1);
  box-shadow: 0 12px 0 rgba(0, 0, 0, 0.5);
}
.dock-icon:hover img,
.dock-icon:hover svg {
  transform: scale(1.4) translateY(-1px);
}
.dock-icon:hover .label {
  opacity: 1;
  transform: translateY(0);
  margin-bottom: 16px;
}
.dock-icon.active::before {
  background: #000;
  transform: scale(1.05);
}
.dock-icon.active img,
.dock-icon.active svg {
  transform: scale(1.05);
}

/* --- Default Window Positions --- */
#finder {
  left: 100px;
  top: 80px;
  width: 600px;
  height: 400px;
}
/* No height: the window is as tall as the one local player it now holds. The
   old fixed height was sized around the Spotify embed that used to share this
   window, and a pixel height that outlives its content is what left a dead
   band under the Trash panel. Kept as a bare #music rule rather than inline on
   the element so css/responsive.css can still override it, the same reason
   #messages keeps its size here. */
#music {
  left: 250px;
  top: 150px;
  width: 320px;
}
#calculator {
  left: 60px;
  top: 180px;
}
#terminal {
  left: 10px;
  top: 150px;
  width: 600px;
  height: 450px;
}
#messages {
  left: 150px;
  top: 56px;
}
/* Pushed clear of the left edge so the comment prompt has somewhere to sit.
   Without a rule here the widest window in the OS lands at its static position
   flush against x=0. The min() keeps its right edge on screen on narrower
   laptops; below the tablet breakpoint css/responsive.css takes over.
   Size lives here rather than inline on the element for the same reason
   #settings does: an inline style outranks every media query, so the 700px this
   used to carry left the window hanging 320px off the side of a phone with no
   stylesheet able to reach it. */
/* Community clears room for the guestbook prompt on the left and the creator
   dialogue on the right. Opening the creator panel widens the window in place. */
#community {
  left: max(20px, min(280px, 100vw - 820px));
  top: 70px;
  width: 760px;
  height: 520px;
}
#community.creator-open {
  width: min(1080px, calc(100vw - 40px));
  left: max(20px, min(40px, 100vw - 1100px));
}
/* Width lives here rather than inline on the element so css/responsive.css can
   still reach it. An inline style outranks every media query. */
#settings {
  left: 300px;
  top: 320px;
  width: 340px;
}
#trash {
  left: 240px;
  top: 140px;
  width: 450px;
  height: auto; /* whichever trash scene is loaded sets the height */
}
/* Sized to the photo page: at 520px a 1024-wide photograph lands at about half
   size, which is where the figure inserted into it is still readable without
   the window owning the desktop. The height is what keeps the carousel honest
   — it is fixed, so .content is fixed, so turning a page cannot resize the
   window whatever length the entry runs to. Capped against the viewport the
   way #messages is rather than pinned, so a short laptop never puts the bottom
   of the window under the dock. A bare id rule, so the overrides in
   css/responsive.css can still reach it.

   680px is measured rather than round: at a 900px viewport the desktop between
   the menu bar and the dock is 713px, and this leaves the window 12px clear of
   the dock. It is deliberately as tall as that allows, because window height is
   the only lever that shortens a page — the photographs are the app, and
   shrinking them to force a page to fit would defeat it. */
#journal {
  left: 360px;
  top: 80px;
  width: 520px;
  height: min(680px, calc(100vh - 170px));
}
#internet {
  left: 150px;
  top: 150px;
  width: 530px;
  height: 540px;
}
#readme.window {
  width: 380px;
  left: 150px;
  top: 100px;
}
#clockApp {
  width: 320px;
}
/* The dialogs createMessageWindow() opens. A class rather than an id because
   each one is created on the spot under a generated id, and it belongs to the
   window manager rather than to any single app. */
.message-window {
  left: 220px;
  top: 160px;
  width: 340px;
}

/* createConfirmWindow()'s half of that pair. It carries .message-window as
   well, so the geometry and both of css/responsive.css's breakpoints already
   cover it and only the choice itself is described here. */
.confirm-window .content {
  padding: 15px;
}
.confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 18px;
}
/* Neither button is dressed up. A destructive dialog has to make the refusal
   at least as easy to reach as the agreement, so the only emphasis in the row
   is wherever focus is — and focus opens on Cancel. */
.confirm-actions button {
  font-family: "VT323", monospace;
  font-size: 17px;
  padding: 6px 16px;
  border: 2px solid #000;
  background: #fff;
  cursor: pointer;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.9);
  transition: background 0.1s;
}
.confirm-actions button:hover {
  background: #e0e0e0;
}
.confirm-actions button:active {
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.9);
  transform: translate(2px, 2px);
}
.confirm-actions button:focus-visible {
  outline: 3px solid #000;
  outline-offset: 3px;
}

#login-screen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9998; /* Below boot screen (9999 typically) but above desktop */
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;

  /* THE MAGIC: Blurs whatever is behind this div (the wallpaper/desktop) */
  backdrop-filter: blur(15px) brightness(0.8);
  -webkit-backdrop-filter: blur(15px) brightness(0.8);
  background-color: rgba(0, 0, 0, 0.15); /* Slight tint */
  transition: opacity 0.5s ease;
}

.login-container {
  text-align: center;
  color: white;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif;
  animation: fadeIn 0.8s ease-out;
}

/* dash-avatar.webp is a square 320px portrait already cropped to the disc, so
   the framing lives in the file and there is no object-position here to drift
   out of tune. A full-body picture would need one: dash-base.webp under cover
   draws a 19px stick in the middle of a 100px circle. Replace the file with
   another square head-and-shoulders crop and this rule still holds. The white
   behind him is the picture's own background rather than transparency, so the
   disc reads as deliberately white. */
.login-avatar img {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
  margin-bottom: 15px;
  object-fit: cover;
}

.login-name {
  font-size: 24px;
  font-weight: 500;
  margin-bottom: 20px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

.login-input-wrapper {
  position: relative;
  display: inline-block;
}

#login-password {
  background: rgba(255, 255, 255, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 20px;
  padding: 8px 35px 8px 15px;
  color: white;
  font-size: 14px;
  outline: none;
  width: 180px;
  transition: all 0.2s;
  backdrop-filter: blur(5px);
}

#login-password::placeholder {
  color: rgba(255, 255, 255, 0.7);
}

#login-password:focus {
  background: rgba(255, 255, 255, 0.4);
  width: 190px;
}

#login-btn {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(255, 255, 255, 0.8);
  border: none;
  border-radius: 50%;
  width: 24px;
  height: 24px;
  cursor: pointer;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #333;
  opacity: 0; /* Hidden until typing starts (optional, or set to 1) */
  transition: opacity 0.2s;
}

#login-password:not(:placeholder-shown) + #login-btn {
  opacity: 1;
}

.login-admin-error {
  margin-top: 12px;
  max-width: 280px;
  padding: 6px 10px;
  border: 2px solid #ff3b30;
  background: #fff5f5;
  color: #b00000;
  font-family: "VT323", monospace;
  font-size: 16px;
  line-height: 1.2;
  text-align: center;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.85);
}

.login-admin-error[hidden] {
  display: none !important;
}

/* The way in, spoken rather than captioned. Same bubble as Morphy's
   #morphy-speech-bubble in css/trash.css, copied per stylesheet rather than
   shared, like the three prompt bubbles are.
   
   Unlike them it is placed by CSS alone. positionDockBubble() and
   positionBubbleLeftOf() in js/utils.js exist to chase anchors that move; this
   one is pinned to a field in a fixed, centred layout, so a static placement is
   both simpler and incapable of drifting. It borrows only their 14px gap, which
   is what lands the tail's tip on the anchor rather than short of it.
   
   Hidden until js/auth.js says otherwise: the hint is spent once the visitor
   has actually got in, and display rather than opacity means it cannot flash
   before that check runs. It is out of flow at both widths — see the note in
   index.html. */
.login-prompt {
  display: none;
  position: absolute;
  left: calc(100% + 14px);
  top: 50%;
  transform: translateY(-50%);
  /* Shrink-to-fit would collapse to min-content here, because a box pinned by
     `left` alone has no width left over to fit into. */
  width: max-content;
  max-width: 260px;
  padding: 8px 12px;
  border: 3px solid #000;
  background: #fffdf2;
  color: #000;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9);
  font-family: "VT323", monospace;
  font-size: 20px;
  line-height: 1.1;
  text-align: center;
  pointer-events: none;
}

.login-prompt.visible {
  display: block;
}

/* Pointing left at the middle of the field's right edge. The rotated square
   shows the two edges that meet at the corner the undrawn borders leave open,
   so bottom + left is the mirror of the top + right that sends the Guestbook
   bubble's tail the other way. */
.login-prompt::after {
  content: "";
  position: absolute;
  left: -13px;
  top: calc(50% - 9px);
  width: 18px;
  height: 18px;
  border-bottom: 3px solid #000;
  border-left: 3px solid #000;
  background: #fffdf2;
  transform: rotate(45deg);
}

/* Below ~576px the bubble runs off the right edge, because the login block is
   centred and half a viewport stops being enough for a 190px field plus a
   166px bubble. 600px is the nearest breakpoint css/responsive.css already
   uses, and it clears that by enough not to be a near miss.
   
   It drops under the field rather than hiding, which is what the Guestbook
   bubble does in the same bind: that one is encouragement and can be skipped,
   but this is the only thing telling a phone visitor what to type. */
@media (max-width: 600px) {
  .login-prompt {
    left: 50%;
    top: calc(100% + 14px);
    transform: translateX(-50%);
  }

  /* Same square, rotated into the other free corner: top + left points up. */
  .login-prompt::after {
    left: calc(50% - 9px);
    top: -13px;
    border-bottom: none;
    border-right: none;
    border-top: 3px solid #000;
    border-left: 3px solid #000;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* --- Settings App --- */
.settings-content {
  padding: 16px;
  max-width: 100%;
}
.settings-section {
  margin-bottom: 16px;
}
.settings-heading {
  margin: 0 0 4px 0;
  font-family: "Pixelify Sans", sans-serif;
  font-size: 16px;
  color: #000;
}
.settings-desc {
  font-family: "VT323", monospace;
  font-size: 14px;
  color: #555;
  margin-bottom: 8px;
  line-height: 1.2;
}
.settings-divider {
  height: 2px;
  background: var(--border-dark);
  margin: 16px 0;
  border-bottom: 1px solid var(--border-light);
}
.settings-btn-group {
  display: flex;
  gap: 8px;
}
.settings-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--panel-light);
  border: 2px outset var(--border-light);
  padding: 8px;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  flex: 1;
  color: #000;
  text-align: center;
}
.settings-btn:active {
  border-style: inset;
  background: #d4d4d4;
}
.settings-btn img {
  width: 24px;
  height: 24px;
  image-rendering: pixelated;
}
.custom-upload-btn {
  flex-direction: row;
  padding: 10px;
  font-family: "Pixelify Sans", sans-serif;
  font-size: 14px;
  cursor: pointer;
}
