/* ── craftdesign.group fossil skin ── */ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap'); :root { --bg: #0f1117; --surface: #1a1d27; --surface2: #22263a; --border: #2a2d3e; --accent: #6c8ebf; --accent2: #4a7c9e; --text: #e2e8f0; --muted: #8892a4; --green: #4ade80; --red: #f87171; --yellow: #fbbf24; --radius: 8px; --font: 'Inter', system-ui, -apple-system, sans-serif; --mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; color-scheme: dark; } /* ── Reset & base ── */ *, *::before, *::after { box-sizing: border-box; } body { background: var(--bg); color: var(--text); font-family: var(--font); font-size: 14px; line-height: 1.6; margin: 0; padding: 0; min-height: 100vh; display: flex; flex-direction: column; } a { color: var(--accent); text-decoration: none; } a:hover { color: #8fb3d9; text-decoration: underline; } a:visited { color: #7ea5cc; } code, pre, tt, kbd { font-family: var(--mono); font-size: 0.875em; } pre { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; overflow-x: auto; color: var(--text); } blockquote { border-left: 3px solid var(--border); margin: 0.5rem 0; padding: 0.25rem 1rem; color: var(--muted); } hr { border: none; border-top: 1px solid var(--border); margin: 1.5rem 0; } /* ── Header ── */ header { display: flex; align-items: center; gap: 1rem; padding: 0.9rem 1.5rem; border-bottom: 1px solid var(--border); background: var(--bg); } .logo { flex-shrink: 0; } .logo-mark { width: 32px; height: 32px; background: linear-gradient(135deg, var(--accent), var(--accent2)); border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 0.95rem; font-weight: 700; color: #fff; font-family: var(--font); } .hub-link { text-decoration: none; } .hub-link:hover .logo-mark { opacity: 0.85; } .title { flex: 1; display: flex; align-items: baseline; gap: 0.6rem; min-width: 0; } .project-name { font-family: var(--mono); font-size: 0.9rem; font-weight: 500; color: var(--text); text-decoration: none; white-space: nowrap; } .project-name:hover { color: var(--accent); text-decoration: none; } span.page-title { font-size: 0.8rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Hide the duplicate h1 page-title that fossil inserts after the nav */ h1.page-title { display: none; } .status { flex-shrink: 0; font-size: 0.8rem; } .status a { color: var(--muted); } .status a:hover { color: var(--text); text-decoration: none; } /* ── Main nav ── */ nav.mainmenu { display: flex; align-items: stretch; gap: 0; padding: 0 1rem; border-bottom: 1px solid var(--border); background: var(--surface); z-index: 21; font-size: 0.82rem; font-weight: 500; min-height: 44px; overflow-x: auto; } nav.mainmenu a { color: var(--muted); text-decoration: none; padding: 0 0.75rem; border-bottom: 2px solid transparent; white-space: nowrap; transition: color 0.15s, border-color 0.15s; display: inline-flex; align-items: center; min-height: 44px; /* WCAG 2.5.5 minimum touch target */ } nav.mainmenu a:hover { color: var(--text); text-decoration: none; border-bottom-color: var(--border); } nav.mainmenu a.active { color: var(--accent); border-bottom-color: var(--accent); } /* Hamburger button — visually distinct, 44×44 touch target */ #hbbtn { color: var(--muted); font-size: 1.15rem; padding: 0 0.875rem; border-bottom: 2px solid transparent !important; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; margin-right: 0.25rem; border-right: 1px solid var(--border); } #hbbtn:hover { color: var(--text); } #hbbtn[aria-expanded="true"] { color: var(--accent); background: rgba(108, 142, 191, 0.08); } /* Hamburger dropdown — position:fixed so it overlays content regardless of DOM structure. --nav-bottom is set by a11y.js to the nav bar's bottom edge and updated on resize, so the dropdown always appears directly below the nav. */ nav#hbdrop { background: var(--surface); border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius) var(--radius); display: none; position: fixed; top: var(--nav-bottom, 88px); left: 0; right: 0; z-index: 200; box-shadow: 0 8px 24px rgba(0,0,0,0.5); } nav#hbdrop a { display: flex; align-items: center; padding: 0 1.5rem; min-height: 44px; /* WCAG 2.5.5 minimum touch target */ color: var(--muted); font-size: 0.875rem; border-bottom: 1px solid var(--border); } nav#hbdrop a:last-child { border-bottom: none; } nav#hbdrop a:hover { color: var(--text); background: var(--surface2); text-decoration: none; } /* ── Content area ── */ div.content { flex: 1; max-width: 960px; margin: 1rem auto 0; padding: 0 1.5rem 1.5rem; width: 100%; } div.content p { margin: 0.5rem 0; } div.content h1, div.content h2, div.content h3, div.content h4, div.content h5 { color: var(--text); font-weight: 600; margin: 1.25rem 0 0.5rem; letter-spacing: -0.01em; } /* Submenu bar */ div.submenu, div.sectionmenu { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.4rem 1rem; margin: 1rem auto 1rem; max-width: 960px; font-size: 0.8rem; display: flex; flex-wrap: wrap; gap: 0.25rem; align-items: center; } div.submenu a, div.sectionmenu a { color: var(--muted); padding: 0.2rem 0.5rem; border-radius: 4px; } div.submenu a:hover, div.sectionmenu a:hover { color: var(--text); background: var(--surface2); text-decoration: none; } div.submenu select, div.submenu input[type=text] { background: var(--surface2); border: 1px solid var(--border); border-radius: 4px; color: var(--text); font-size: 0.8rem; padding: 0.2rem 0.4rem; } /* ── Timeline ── */ table.timelineTable { width: 100%; border-spacing: 0 2px; } /* Fix Fossil's built-in card styles: Base class: padding:0.75em, border-radius:1em (big pill shape) — tighten both. [id] variant: background:#efefef (light grey) — make transparent for dark bg. */ .timelineModernCell, .timelineColumnarCell, .timelineDetailCell { padding: 0.4em 0.6em; border-radius: var(--radius); } .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { background-color: transparent; } /* Detail text has opacity:0.75 in Fossil's built-in CSS. On dark bg that drops effective contrast to ~2:1. Remove it and set a solid muted color. */ .timelineModernDetail { opacity: 1; color: var(--muted); } .timelineModernComment { color: var(--text); } tr.timelineCurrent td { background: rgba(108, 142, 191, 0.08); border-left: 2px solid var(--accent) !important; } tr.timelineSelected td { background: rgba(108, 142, 191, 0.12); border: 1px solid var(--accent); box-shadow: none; } .timelineSecondary td { background: rgba(74, 222, 128, 0.05); } td.timelineTime { color: var(--muted); font-family: var(--mono); font-size: 0.78rem; vertical-align: top; text-align: right; padding-right: 0.75rem; white-space: nowrap; } td.timelineGraph { width: 20px; vertical-align: top; } span.timelineLeaf, span.timelineHash { font-weight: 600; } span.timelineHash { font-family: var(--mono); font-size: 0.82rem; color: var(--muted); } .timelineDate { color: var(--muted); font-size: 0.75rem; font-family: var(--mono); padding: 0.75rem 0 0.25rem; } /* ── Tables ── */ table.sortable, table.filelistTable, table.branchlistTable, table.taglistTable, table.userlistTable { width: 100%; border-collapse: collapse; } table.sortable th, table.filelistTable th { text-align: left; padding: 0.5rem 0.75rem; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); border-bottom: 1px solid var(--border); cursor: pointer; } table.sortable td, table.filelistTable td { padding: 0.5rem 0.75rem; border-bottom: 1px solid rgba(42, 45, 62, 0.5); vertical-align: top; font-size: 0.85rem; } table.sortable tr:hover td { background: var(--surface); } /* ── File diff ── */ table.diff { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 0.8rem; } td.difflnc, td.difflna, td.difflnb { color: var(--muted); padding: 0 0.5rem; text-align: right; user-select: none; border-right: 1px solid var(--border); width: 1%; white-space: nowrap; } td.difftext { padding: 0 0.5rem; white-space: pre; } tr.diffadd td { background: rgba(74, 222, 128, 0.08); } tr.diffrm td { background: rgba(248, 113, 113, 0.08); } tr.diffchng td { background: rgba(251, 191, 36, 0.06); } span.diffadd { color: var(--green); } span.diffrm { color: var(--red); } span.diffchng { color: var(--yellow); } /* ── Forms & inputs ── */ input[type=text], input[type=password], input[type=email], input[type=search], textarea, select { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-family: var(--font); font-size: 0.875rem; padding: 0.45rem 0.75rem; outline: none; transition: border-color 0.15s; width: 100%; } input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, textarea:focus, select:focus { border-color: var(--accent); } input[type=submit], input[type=button], button { background: var(--accent); border: none; border-radius: var(--radius); color: #fff; cursor: pointer; font-family: var(--font); font-size: 0.875rem; font-weight: 500; padding: 0.45rem 1rem; transition: background 0.15s; } input[type=submit]:hover, button:hover { background: #5a7eb0; } textarea { font-family: var(--mono); min-height: 120px; resize: vertical; } /* ── Wiki ── */ div.wiki { line-height: 1.75; } div.wiki p { margin: 0.75rem 0; } div.wiki ul, div.wiki ol { margin: 0.5rem 0 0.5rem 1.5rem; } div.wiki li { margin: 0.2rem 0; } div.wiki table { border-collapse: collapse; margin: 1rem 0; } div.wiki th, div.wiki td { border: 1px solid var(--border); padding: 0.4rem 0.75rem; } div.wiki th { background: var(--surface); } /* ── Ticket ── */ table.report { width: 100%; border-collapse: collapse; } table.report th { background: var(--surface); border: 1px solid var(--border); padding: 0.4rem 0.75rem; font-size: 0.8rem; color: var(--muted); } table.report td { border: 1px solid var(--border); padding: 0.4rem 0.75rem; font-size: 0.85rem; } table.report tr:hover td { background: var(--surface); } /* ── Annotation / blame ── */ table.annotation { width: 100%; border-collapse: collapse; } td.annotateDate, td.annotateUser { color: var(--muted); font-family: var(--mono); font-size: 0.78rem; padding: 0 0.5rem; white-space: nowrap; border-right: 1px solid var(--border); } /* ── Misc ── */ div.sideboxTitle { color: var(--accent); font-weight: 600; } div.sidebox { background: var(--surface); border: 1px solid var(--border) !important; border-radius: var(--radius); margin: 0 0 1rem 1.5rem; padding: 0.75rem; } .disabled { color: var(--red); } .modpending { color: var(--yellow); } /* Branch/tag badges */ .branch-name, span.tagDsp { display: inline-block; background: rgba(108, 142, 191, 0.1); border: 1px solid rgba(108, 142, 191, 0.25); border-radius: 4px; color: var(--accent); font-family: var(--mono); font-size: 0.75rem; padding: 0.1rem 0.4rem; } /* Login page */ table.login_out { margin: 2rem auto; } td.login_out_label { color: var(--muted); padding: 0.4rem 0.75rem 0.4rem 0; font-size: 0.875rem; } td.login_out_value { padding: 0.3rem 0; } /* ── Footer ── */ footer { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.5rem; border-top: 1px solid var(--border); margin-top: 3rem; font-size: 0.75rem; color: var(--muted); font-family: var(--mono); } .footer-project { color: var(--muted); } .footer-meta { color: rgba(136, 146, 164, 0.6); } /* ════════════════════════════════════════════════════════ ACCESSIBILITY FIXES ════════════════════════════════════════════════════════ */ /* ── Skip navigation (WCAG 2.4.1) ── */ .skip-nav { position: absolute; top: -100%; left: 0; background: var(--accent); color: #fff; padding: 0.5rem 1.25rem; font-size: 0.875rem; font-weight: 500; z-index: 9999; border-radius: 0 0 var(--radius) 0; text-decoration: none; transition: top 0.1s; } .skip-nav:focus { top: 0; outline: 2px solid #fff; outline-offset: -4px; } /* ── Focus indicators (WCAG 2.4.7 / 2.4.11) ── Fossil has no :focus styles at all — keyboard users see nothing. */ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; } /* Nav links: inset ring looks cleaner inside the bar */ nav.mainmenu a:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; } /* Form elements already have a border so offset 0 works better */ input:focus-visible, button:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; } /* ── Fix visited-link contrast (#5a7aaa → #7ea5cc, ~4.6:1 on --surface) ── */ a:visited { color: #7ea5cc; } /* ── Branch/tag badge contrast fix ── Old: #6c8ebf on rgba(108,142,191,0.1)+#1a1d27 ≈ 3.4:1 — fails AA. New: #8fb3d9 on same tinted surface ≈ 5.2:1 — passes AA. */ .branch-name, span.tagDsp { color: #8fb3d9; border-color: rgba(143, 179, 217, 0.3); } /* ════════════════════════════════════════════════════════ TIMELINE GRAPH — DARK THEME OVERRIDES Fossil's built-in graph CSS hardcodes light-theme black/white colors. Every rule below overrides one of those hardcoded values. ════════════════════════════════════════════════════════ */ /* ── Node dots ── Default: background:#fff, border:1px solid #000 Branch nodes also receive inline style.backgroundColor = p.bg (e.g. #e8f0c7). !important beats inline styles, giving us uniform accent-colored dots. */ .tl-node { background: var(--accent) !important; border-color: var(--accent2) !important; } /* Leaf indicator (inner filled square): was #000, now white for contrast */ .tl-node.leaf:after { background: #fff !important; } /* Closed-leaf SVG icon color */ .tl-node.closed-leaf svg { color: var(--text) !important; } /* Selected node stays red — red is visible on dark and is intentional feedback */ /* ── Vertical connector lines ── Default: background:#000 — completely invisible on dark background. */ .tl-line { background: var(--muted) !important; } .tl-line.warp { background: var(--yellow) !important; } /* Cherry-pick and dotted lines use border rather than background */ .tl-line.cherrypick.h { border-top-color: var(--muted) !important; background: transparent !important; } .tl-line.cherrypick.v { border-left-color: var(--muted) !important; background: transparent !important; } .tl-line.dotted.v { border-left-color: var(--muted) !important; background: transparent !important; } /* ── Arrows ── All use border-color tricks; defaults are #000, invisible on dark. Branch-specific arrows get inline borderBottomColor set by JS when p.fg is non-empty — !important overrides those too for consistency. */ .tl-arrow.u { border-bottom-color: var(--muted) !important; } .tl-arrow.u.sm { border-bottom-color: var(--muted) !important; } .tl-arrow.merge.l { border-right-color: var(--muted) !important; } .tl-arrow.merge.r { border-left-color: var(--muted) !important; } .tl-arrow.cherrypick.l { border-right-color: var(--muted) !important; } .tl-arrow.cherrypick.r { border-left-color: var(--muted) !important; } /* Warp arrow: was dark maroon #600000, now yellow warning tone */ .tl-arrow.warp { border-left-color: var(--yellow) !important; } /* ── Branch row colors (dark-mode adaptation) ── Fossil JS sets style.backgroundColor = p.bg (light pastels, e.g. "#e8f0c7") on td#mc. CSS background-image layers sit on TOP of background-color, so we overlay a semi-transparent dark layer — the pastel bleeds through as a subtle hue that identifies the branch without wrecking text contrast. */ table.timelineTable td[style*="background"] { background-image: linear-gradient( rgba(15, 17, 23, 0.75), rgba(15, 17, 23, 0.75) ) !important; } /* Re-assert our intentional row highlights with !important since we're now in an !important cascade for timeline cells. */ tr.timelineCurrent td { background: rgba(108, 142, 191, 0.08) !important; border-left: 2px solid var(--accent) !important; } tr.timelineSelected td { background: rgba(108, 142, 191, 0.12) !important; border: 1px solid var(--accent) !important; box-shadow: none !important; } /* ── Tooltip ── Default: white background with black border — flashes bright on dark page. */ .tl-tooltip { background: var(--surface2) !important; color: var(--text) !important; border-color: var(--border) !important; box-shadow: 0 4px 16px rgba(0,0,0,0.5) !important; } /* ── Mobile / responsive ────────────────────────────────────────────────── */ /* On small screens: hide inline nav links, show only the hamburger button. The full link list is accessible via the hamburger dropdown. */ @media (max-width: 640px) { nav.mainmenu > a:not(#hbbtn) { display: none; } #hbbtn { border-right: none; margin-right: 0; } /* Tighten header for small screens */ header { padding: 0.75rem 1rem; gap: 0.75rem; } span.page-title { display: none; /* project name alone is enough on narrow viewports */ } /* Content: reduce side padding so text isn't cramped */ div.content { padding: 0 1rem 1rem; margin-top: 0.75rem; } /* Submenu: stack items on mobile */ div.submenu, div.sectionmenu { padding: 0.35rem 0.75rem; gap: 0.15rem; } /* Timeline: narrow the time column */ td.timelineTime { font-size: 0.72rem; padding-right: 0.5rem; } td.timelineGraph { width: 16px; } } /* Medium screens: keep inline links but reduce padding a bit */ @media (max-width: 900px) { div.content { padding: 0 1.25rem 1.25rem; } div.submenu, div.sectionmenu { margin: 0.75rem auto; } } /* ── Chat page ──────────────────────────────────────────────────────────── */ /* User-assigned message bubble colors: Fossil JS sets inline background-color (m.uclr) on .message-widget-content and .message-widget-tab using bright palette colors designed for white backgrounds. Same overlay technique as branch colors: dark semi-transparent background-image sits on top. */ body.chat .message-widget-content[style*="background"], body.chat .message-widget-tab[style*="background"] { background-image: linear-gradient( rgba(15, 17, 23, 0.72), rgba(15, 17, 23, 0.72) ) !important; } /* Input field: Fossil uses rgba(0,0,0,0.2) border — invisible on dark bg. */ body.chat .chat-input-field:not(:focus) { border-color: var(--border) !important; background: var(--surface) !important; color: var(--text) !important; } body.chat .chat-input-field:focus { border-color: var(--accent) !important; background: var(--surface) !important; color: var(--text) !important; } body.chat #chat-input-line-wrapper { border-color: var(--border) !important; background: var(--surface) !important; } /* Message tab (sender name / timestamp bar) */ body.chat .message-widget-tab { color: var(--muted) !important; } body.chat .message-widget-tab .xfrom { color: var(--text) !important; } /* Search match highlight — Fossil provides #ff4800 for dark mode which is fine, but ensure it stays readable */ body.chat.fossil-dark-style .message-widget .match { color: var(--bg) !important; } /* Message popup (timestamp/delete controls) */ body.chat .chat-message-popup { background: var(--surface2) !important; border-color: var(--border) !important; color: var(--text) !important; } /* ── Forum page ─────────────────────────────────────────────────────────── */ /* Forum post collapser border uses rgba(0,0,0,0.2) — near-invisible on dark */ .forum-post-collapser { border-color: var(--border) !important; background: var(--surface) !important; color: var(--muted) !important; } .forum-post-collapser:hover { background: var(--surface2) !important; color: var(--text) !important; } /* ── Diff view ──────────────────────────────────────────────────────────── */ /* Fossil built-in diff colors (#a0e4b2, #ffc0c0, #ffe8e8) are very light — */ td.diffln ins, td.difftxt ins { background-color: rgba(74,222,128,0.15) !important; } td.diffln del, td.difftxt del { background-color: rgba(248,113,113,0.15) !important; } td.difftxt del > del { background-color: rgba(248,113,113,0.3) !important; } td.difftxt ins > ins { background-color: rgba(74,222,128,0.3) !important; } /* ── Ticket / report pages ──────────────────────────────────────────────── */ /* Fossil hardcodes color:black and color:blue on these pages */ body.tkt td.tktDspValue { color: var(--text) !important; } body.tkt td.tktDspValue a { color: var(--accent) !important; } body.report table.report tr td { color: var(--text) !important; } body.report table.report a { color: var(--accent) !important; }