:root { --bg: #f4f8fa; --card: #ffffff; --text: #183747; --muted: #627987; --border: #d8e5eb; --primary: #c83676; --primary-dark: #a92a62; --secondary: #1e516b; --secondary-dark: #173d51; --danger: #b8223e; --danger-dark: #941a31; --danger-bg: #fdecef; --warning: #9a5b00; --warning-bg: #fff4d8; --success: #188654; --success-bg: #e7f5ee; } .hidden { display: none !important; } * { box-sizing: border-box; } body { margin: 0; font-family: Arial, sans-serif; background: linear-gradient(180deg, #eef6f9 0, var(--bg) 220px); color: var(--text); } header { background: var(--secondary); color: white; padding: 14px 32px; display: flex; align-items: center; justify-content: space-between; gap: 18px; border-bottom: 5px solid var(--primary); box-shadow: 0 10px 26px rgba(30, 81, 107, 0.16); } nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; } .brand { display: inline-flex; align-items: center; gap: 14px; color: white; text-decoration: none; font-weight: 800; font-size: 19px; } .brand:hover { text-decoration: none; } .brand img { display: block; width: 148px; height: auto; background: white; border-radius: 6px; padding: 6px 8px; } nav a, .nav-menu-button { color: white; text-decoration: none; font-weight: 600; padding: 10px 12px; border-radius: 8px; font-size: 14px; line-height: 1.2; } nav a:hover, .nav-menu-button:hover, .nav-menu:focus-within .nav-menu-button, .nav-menu:hover .nav-menu-button { background: rgba(255, 255, 255, 0.12); text-decoration: none; } .nav-menu { position: relative; padding-bottom: 10px; margin-bottom: -10px; } .nav-menu-button { background: transparent; color: white; border: 1px solid rgba(255, 255, 255, 0.20); min-height: 0; display: inline-flex; align-items: center; gap: 7px; } .nav-menu-button::after { content: "▾"; font-size: 10px; line-height: 1; opacity: 0.82; } .nav-menu-panel { display: none; position: absolute; top: 100%; left: 0; z-index: 20; min-width: 180px; background: var(--secondary-dark); border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 8px; padding: 6px; box-shadow: 0 12px 28px rgba(10, 31, 42, 0.28); } .nav-menu-panel::before { content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px; } .nav-menu:hover .nav-menu-panel, .nav-menu:focus-within .nav-menu-panel { display: block; } .nav-menu-panel a { display: block; margin: 0; padding: 10px 12px; white-space: nowrap; } .user-menu { display: flex; align-items: center; gap: 10px; color: white; font-size: 13px; font-weight: 700; } .user-menu .muted { color: rgba(255, 255, 255, 0.72); } .user-menu form { margin: 0; } .user-avatar { width: 32px; height: 32px; border-radius: 8px; object-fit: cover; background: #eef6f9; border: 1px solid var(--border); } .auth-card { max-width: 420px; margin: 48px auto 24px; } .auth-card input { width: 100%; } .alert { border-radius: 8px; padding: 10px 12px; font-weight: 700; } .alert-danger { background: var(--danger-bg); color: var(--danger); } main { padding: 32px; max-width: 1500px; margin: 0 auto; } .card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 22px; box-shadow: 0 8px 24px rgba(30, 81, 107, 0.07); margin-bottom: 24px; overflow-x: auto; } .card h2 { color: var(--secondary); } .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 24px; } .stat-card { background: var(--card); border: 1px solid var(--border); border-left: 5px solid var(--secondary); border-radius: 8px; padding: 18px; box-shadow: 0 8px 24px rgba(30, 81, 107, 0.07); } .stat-card span { display: block; color: var(--muted); font-size: 13px; font-weight: 700; text-transform: uppercase; } .stat-card strong { display: block; margin-top: 8px; font-size: 28px; color: var(--secondary); } .stat-success { border-left-color: var(--success); background: linear-gradient(90deg, var(--success-bg), var(--card) 46%); } .stat-success strong { color: var(--success); } .stat-warning { border-left-color: var(--warning); background: linear-gradient(90deg, var(--warning-bg), var(--card) 46%); } .stat-warning strong { color: var(--warning); } .stat-danger { border-left-color: var(--danger); background: linear-gradient(90deg, var(--danger-bg), var(--card) 46%); } .stat-danger strong { color: var(--danger); } .stat-total { border-left-color: var(--secondary); } table { border-collapse: collapse; width: 100%; min-width: 760px; margin-top: 12px; } th, td { padding: 12px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; } th { color: var(--muted); font-size: 13px; text-transform: uppercase; letter-spacing: .04em; background: #eef6f9; border-top: 1px solid var(--border); } a { color: var(--primary); text-decoration: none; font-weight: 600; } a:hover { text-decoration: underline; } button, .btn { border: 0; border-radius: 8px; padding: 8px 12px; min-height: 34px; background: var(--primary); color: white; font-weight: 700; cursor: pointer; display: inline-block; } button:hover, .btn:hover { background: var(--primary-dark); text-decoration: none; } .nav-menu-button:hover { background: rgba(255, 255, 255, 0.12); color: white; } .btn + .btn { margin-left: 8px; } .btn-secondary { background: var(--secondary); } .btn-secondary:hover { background: var(--secondary-dark); } .btn.disabled, .btn.disabled:hover { background: #b8c8d0; color: #f8fbfc; cursor: not-allowed; pointer-events: none; } .danger { background: var(--danger); } .danger:hover { background: var(--danger-dark); } .muted { color: var(--muted); font-size: 13px; } .pill { display: inline-block; border-radius: 999px; padding: 4px 10px; font-size: 12px; background: #eef6f9; color: var(--secondary); font-weight: 700; } .pill-success { background: var(--success-bg); color: var(--success); } .pill-warning { background: var(--warning-bg); color: var(--warning); } .pill-danger { background: var(--danger-bg); color: var(--danger); } .pill-muted { background: #eef6f9; color: var(--muted); } .health-dot { display: inline-block; width: 11px; height: 11px; border-radius: 999px; background: var(--muted); box-shadow: 0 0 0 4px rgba(98, 121, 135, 0.14); flex: 0 0 auto; } .health-dot-success { background: var(--success); box-shadow: 0 0 0 4px var(--success-bg); } .health-dot-warning { background: var(--warning); box-shadow: 0 0 0 4px var(--warning-bg); } .health-dot-danger { background: var(--danger); box-shadow: 0 0 0 4px var(--danger-bg); } .health-dot-muted { background: var(--muted); } /* Disabled IP access rules are dimmed so active/inactive rules are visually distinct. */ .ip-rule-row.rule-disabled { opacity: 0.55; } .ip-rule-row.rule-disabled input, .ip-rule-row.rule-disabled select { background: #f3f5f7; } .info-dot { position: relative; display: inline-flex; width: 18px; height: 18px; margin-left: 4px; align-items: center; justify-content: center; color: var(--muted); font-size: 13px; line-height: 1; text-transform: none; cursor: help; } .info-dot:hover, .info-dot:focus-visible { color: var(--secondary-dark); outline: none; } /* Custom tooltip bubble — the native title attribute is slow and easy to miss, so info hints render as an immediate, styled popover on hover/focus. */ .info-dot[data-tooltip]::after { content: attr(data-tooltip); position: absolute; top: calc(100% + 8px); left: 50%; transform: translateX(-50%); z-index: 20; width: max-content; max-width: 260px; padding: 8px 10px; border-radius: 8px; background: #1f2937; color: #fff; font-size: 12px; font-weight: 400; line-height: 1.4; text-align: left; white-space: normal; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22); opacity: 0; visibility: hidden; transition: opacity 0.12s ease; } .info-dot[data-tooltip]::before { content: ""; position: absolute; top: calc(100% + 2px); left: 50%; transform: translateX(-50%); z-index: 20; border: 6px solid transparent; border-bottom-color: #1f2937; opacity: 0; visibility: hidden; transition: opacity 0.12s ease; } .info-dot[data-tooltip]:hover::after, .info-dot[data-tooltip]:focus-visible::after, .info-dot[data-tooltip]:hover::before, .info-dot[data-tooltip]:focus-visible::before { opacity: 1; visibility: visible; } input, select, textarea { padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: white; } textarea { min-height: 92px; resize: vertical; } .metadata-form { display: grid; grid-template-columns: minmax(160px, 240px) minmax(280px, 1fr); gap: 12px 16px; align-items: center; max-width: 980px; } .metadata-form label { color: var(--secondary); font-weight: 800; } .metadata-form input, .metadata-form select, .metadata-form textarea { width: 100%; } .checkbox-label { display: inline-flex; align-items: center; gap: 8px; } .checkbox-label input { width: auto; } .form-actions { grid-column: 2; } button:disabled, input:disabled, select:disabled, textarea:disabled { cursor: wait; opacity: 0.72; } .form-loader { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-weight: 700; } .form-loader[hidden] { display: none; } .spinner { width: 16px; height: 16px; border: 2px solid var(--border); border-top-color: var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; } .async-status { grid-column: 2; min-height: 22px; color: var(--muted); font-weight: 700; } .async-status-error { color: var(--danger); } #new-app-result { margin-top: 18px; } @keyframes spin { to { transform: rotate(360deg); } } .detail-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 18px; } .add-variable-form { margin-top: 18px; } .add-variable-form h3 { grid-column: 1 / -1; margin: 0; color: var(--secondary); } .variable-form { display: grid; grid-template-columns: minmax(140px, 1fr) minmax(160px, 1fr) auto auto; gap: 8px; align-items: center; margin-bottom: 8px; } .env-usage-example { margin: 14px 0 18px; } .env-usage-example h3 { margin: 0 0 6px; color: var(--secondary); } .env-usage-example pre { margin: 8px 0 0; font-size: 13px; line-height: 1.45; } input[readonly] { width: 100%; font-family: monospace; background: #f8fbfc; color: var(--secondary-dark); } .cmd-row { display: grid; grid-template-columns: 1fr auto; gap: 8px; margin: 8px 0 14px; } .copy-row { display: grid; grid-template-columns: minmax(260px, 1fr) 82px; gap: 8px; align-items: start; margin: 0 0 10px; } .copy-row input { min-height: 34px; } .copy-button, .actions-cell button { width: 82px; text-align: center; } .actions-cell { width: 150px; white-space: nowrap; } .actions-cell form, .actions-cell p { margin: 0 0 8px; } .service-row td { vertical-align: middle; } .service-name-cell { min-width: 260px; } .service-title { display: flex; align-items: center; gap: 12px; } .resource-summary { display: grid; grid-template-columns: 1fr; gap: 5px; min-width: 170px; } .resource-summary span { color: var(--muted); font-size: 13px; } .resource-summary strong { color: var(--text); } .btn-compact { width: fit-content; min-height: 30px; padding: 6px 10px; font-size: 13px; } .resource-modal { width: min(520px, calc(100vw - 32px)); border: 1px solid var(--border); border-radius: 8px; padding: 20px; color: var(--text); box-shadow: 0 18px 48px rgba(10, 31, 42, 0.26); } .resource-modal::backdrop { background: rgba(10, 31, 42, 0.34); } .resource-modal form { display: grid; gap: 10px; margin: 0; } .resource-modal h3 { margin: 0; color: var(--secondary); } .resource-modal label { font-weight: 800; color: var(--secondary); } .resource-modal input { width: 100%; } .modal-header, .modal-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; } .modal-actions { justify-content: flex-end; margin-top: 8px; } .icon-button { width: 34px; min-height: 34px; padding: 0; border: 1px solid var(--border); background: white; color: var(--secondary); font-size: 22px; line-height: 1; } .icon-button:hover { background: #eef6f9; color: var(--secondary-dark); } .clone-details { min-width: 170px; } .clone-details summary { color: var(--text); } .service-actions { display: flex; align-items: center; gap: 8px; width: auto; min-width: 250px; } .service-actions .btn { margin: 0; } .action-menu { position: relative; } .action-menu summary { display: inline-flex; align-items: center; justify-content: center; width: 34px; min-height: 34px; border-radius: 8px; color: var(--secondary); font-weight: 900; list-style: none; cursor: pointer; } .action-menu summary::-webkit-details-marker { display: none; } .action-menu summary:hover, .action-menu[open] summary { background: #eef6f9; text-decoration: none; } .action-menu-panel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 12; width: 210px; padding: 6px; border: 1px solid var(--border); border-radius: 8px; background: white; box-shadow: 0 16px 34px rgba(10, 31, 42, 0.18); } .action-menu-panel form { margin: 0; } .menu-action { width: 100%; padding: 10px 12px; background: white; color: var(--text); text-align: left; font-weight: 700; } .menu-action:hover { background: #eef6f9; color: var(--secondary-dark); } .menu-action-danger { color: var(--danger); } .menu-action-danger:hover { background: var(--danger-bg); color: var(--danger-dark); } .action-icons-cell { width: 1%; white-space: nowrap; vertical-align: middle; } .action-icons { display: flex; align-items: center; justify-content: flex-end; gap: 8px; } .action-icons form { margin: 0; display: inline-flex; } .icon-action { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; min-height: 38px; padding: 0; border: 1px solid var(--border); border-radius: 8px; background: white; color: var(--secondary); font-size: 16px; line-height: 1; cursor: pointer; transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease; } .icon-action:hover, .icon-action:focus-visible { background: #eef6f9; color: var(--secondary-dark); border-color: var(--secondary); text-decoration: none; outline: none; } .icon-action-danger { color: var(--danger); } .icon-action-danger:hover, .icon-action-danger:focus-visible { background: var(--danger-bg); color: var(--danger-dark); border-color: var(--danger); } .running-row td { background: var(--warning-bg); } .backup-row td { vertical-align: middle; } details summary { cursor: pointer; font-weight: 700; } pre { background: var(--secondary-dark); color: #e5e7eb; padding: 18px; border-radius: 12px; white-space: pre-wrap; overflow: auto; } .log-viewer { max-height: 520px; min-height: 220px; white-space: pre-wrap; overflow: auto; font-family: Consolas, "Liberation Mono", Menlo, monospace; font-size: 13px; line-height: 1.45; } .log-stdout { background: #102a3a; color: #edf6f9; } .log-stderr { background: #3a111b; color: #ffe5ea; border: 1px solid #f3a8b7; } .log-system { background: #263846; color: #f2f7fa; border: 1px solid #8aa4b4; } #live-log-panel .log-viewer { min-height: 0; max-height: none; margin: 8px 0 12px; padding: 12px; } .failed-log { border-color: #f3a8b7; } .resource-help { font-size: 12px; color: var(--muted); line-height: 1.4; margin-top: 8px; } .inline-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } .filter-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } .pagination { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--muted); font-size: 13px; font-weight: 700; margin: 12px 0; } .pagination-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .page-indicator { color: var(--secondary); white-space: nowrap; } @media (max-width: 980px) { header { align-items: flex-start; flex-direction: column; padding: 14px 18px; } .brand img { width: 120px; } nav { width: 100%; } .nav-menu-panel { position: static; width: 100%; margin-top: 4px; box-shadow: none; } .user-menu { width: 100%; justify-content: flex-start; flex-wrap: wrap; } main { padding: 18px; } } @media (max-width: 640px) { .brand { align-items: flex-start; flex-direction: column; gap: 8px; font-size: 16px; } nav a, .nav-menu, .nav-menu-button { width: 100%; } .nav-menu-button, nav a { text-align: left; } .stats-grid, .grid { grid-template-columns: 1fr; } .card { padding: 16px; } .filter-form, .inline-form, .metadata-form, .variable-form { align-items: stretch; display: flex; flex-direction: column; } input, select, textarea, button, .btn { width: 100%; } .form-actions { grid-column: auto; } .btn + .btn { margin-left: 0; margin-top: 8px; } .cmd-row, .copy-row { grid-template-columns: 1fr; } .copy-button, .actions-cell button { width: 100%; } .service-title { align-items: flex-start; } .service-actions { align-items: stretch; flex-direction: column; min-width: 180px; } .service-actions .btn, .service-actions .action-menu, .action-menu summary { width: 100%; } .action-menu-panel { position: static; width: 100%; margin-top: 6px; } .modal-actions { align-items: stretch; flex-direction: column-reverse; } .pagination { align-items: flex-start; flex-direction: column; } .pagination-actions { width: 100%; } }