layout improvements
This commit is contained in:
parent
25cdc64f99
commit
129a1b1a21
@ -29,7 +29,7 @@ const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
const hasKnowledgebase = tool.knowledgebase === true;
|
||||
|
||||
// Determine card styling based on hosting status (derived from projectUrl)
|
||||
const cardClass = hasValidProjectUrl ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
|
||||
const cardClass = hasValidProjectUrl ? 'card card-hosted tool-card' : (tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card');
|
||||
---
|
||||
|
||||
<div class={cardClass} onclick={`window.showToolDetails('${tool.name}')`} style="cursor: pointer;">
|
||||
@ -79,7 +79,7 @@ const cardClass = hasValidProjectUrl ? 'card card-hosted' : (tool.license !== 'P
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 1rem;">
|
||||
<div class="tool-tags-container" style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 1rem;">
|
||||
{tool.tags.map(tag => (
|
||||
<span class="tag">{tag}</span>
|
||||
))}
|
||||
|
@ -8,82 +8,59 @@ const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
||||
const data = load(yamlContent) as any;
|
||||
|
||||
const tools = data.tools;
|
||||
const domains = data.domains;
|
||||
const phases = data.phases.filter((p: any) => p.id !== 'collaboration-general'); // Exclude from matrix
|
||||
const phases = data.phases;
|
||||
const tools = data.tools;
|
||||
|
||||
// Filter collaboration tools (tools with "collaboration-general" phase)
|
||||
// Separate collaboration tools from domain-specific tools
|
||||
const collaborationTools = tools.filter((tool: any) =>
|
||||
tool.phases && tool.phases.includes('collaboration-general')
|
||||
);
|
||||
|
||||
// Filter regular tools for matrix (excluding collaboration-only tools from matrix display)
|
||||
const matrixTools = tools.filter((tool: any) => {
|
||||
if (!tool.phases || tool.phases.length === 0) return false;
|
||||
// Include if has non-collaboration phases and non-empty domains
|
||||
const hasRegularPhases = tool.phases.some((phase: string) => phase !== 'collaboration-general');
|
||||
const hasDomains = tool.domains && tool.domains.length > 0;
|
||||
return hasRegularPhases && hasDomains;
|
||||
});
|
||||
const domainTools = tools.filter((tool: any) =>
|
||||
!tool.phases || !tool.phases.includes('collaboration-general')
|
||||
);
|
||||
|
||||
// Also include collaboration tools in matrix if they have domains and regular phases
|
||||
const collaborationToolsInMatrix = collaborationTools.filter((tool: any) => {
|
||||
const hasRegularPhases = tool.phases && tool.phases.some((phase: string) => phase !== 'collaboration-general');
|
||||
const hasDomains = tool.domains && tool.domains.length > 0;
|
||||
return hasRegularPhases && hasDomains;
|
||||
// Create matrix structure for domain-specific tools only
|
||||
const matrix: Record<string, Record<string, any[]>> = {};
|
||||
domains.forEach((domain: any) => {
|
||||
matrix[domain.id] = {};
|
||||
phases.filter((phase: any) => phase.id !== 'collaboration-general').forEach((phase: any) => {
|
||||
matrix[domain.id][phase.id] = domainTools.filter((tool: any) =>
|
||||
tool.domains && tool.domains.includes(domain.id) && tool.phases && tool.phases.includes(phase.id)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Combine for matrix display
|
||||
const allMatrixTools = [...matrixTools, ...collaborationToolsInMatrix];
|
||||
---
|
||||
|
||||
<!-- Matrix View Container -->
|
||||
<section id="matrix-container" style="display: none; padding: 2rem 0;">
|
||||
<div style="text-align: center; margin-bottom: 2rem;">
|
||||
<h2 style="margin-bottom: 1rem;">Tool-Matrix nach NIST-Framework</h2>
|
||||
<p class="text-muted" style="max-width: 600px; margin: 0 auto;">
|
||||
Übersicht der Tools nach forensischen Domänen und Untersuchungsphasen.
|
||||
Bewegen Sie die Maus über Zeilen und Spalten für bessere Orientierung.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Collaboration Tools Section (above matrix) -->
|
||||
<div id="collaboration-section" style="margin-bottom: 2rem;">
|
||||
<h3 style="margin-bottom: 1rem; color: var(--color-accent); display: flex; align-items: center; gap: 0.5rem;">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||||
<circle cx="8.5" cy="7" r="4"/>
|
||||
<line x1="20" y1="8" x2="20" y2="14"/>
|
||||
<line x1="23" y1="11" x2="17" y2="11"/>
|
||||
</svg>
|
||||
Übergreifende Tools & Kollaboration
|
||||
</h3>
|
||||
<div class="collaboration-tools-compact">
|
||||
<div id="matrix-container" class="matrix-wrapper" style="display: none;">
|
||||
<!-- Collaboration Tools Section (compact horizontal layout for matrix view) -->
|
||||
<div id="collaboration-tools-section" style="margin-bottom: 1.5rem;">
|
||||
<h3 style="margin-bottom: 0.75rem; color: var(--color-text); font-size: 1.125rem;">Übergreifend & Kollaboration</h3>
|
||||
<div class="collaboration-tools-compact" id="collaboration-tools-container">
|
||||
{collaborationTools.map((tool: any) => {
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
return (
|
||||
<div class={`collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}`}
|
||||
<div class={`collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`}
|
||||
onclick={`window.showToolDetails('${tool.name}')`}>
|
||||
<div class="tool-compact-header">
|
||||
<h4 style="margin: 0; font-size: 1rem;">{tool.name}</h4>
|
||||
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">{tool.name}</h4>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
{hasValidProjectUrl && <span class="badge-mini badge-primary">Self-Hosted</span>}
|
||||
{tool.license !== 'Proprietary' && <span class="badge-mini badge-success">OSS</span>}
|
||||
{tool.knowledgebase === true && <span class="badge-mini badge-error">Infos 📖</span>}
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 0.75rem; line-height: 1.4;">
|
||||
{tool.description.length > 120 ? `${tool.description.substring(0, 120)}...` : tool.description}
|
||||
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
||||
{tool.description}
|
||||
</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
{tool.tags && tool.tags.slice(0, 3).map((tag: string) => (
|
||||
<span class="tag" style="font-size: 0.625rem;">{tag}</span>
|
||||
))}
|
||||
{tool.tags && tool.tags.length > 3 && (
|
||||
<span class="tag" style="font-size: 0.625rem; font-style: italic;">+{tool.tags.length - 3}</span>
|
||||
)}
|
||||
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
||||
<span>{tool.platforms.join(', ')}</span>
|
||||
<span>•</span>
|
||||
<span>{tool.skillLevel}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -91,253 +68,372 @@ const allMatrixTools = [...matrixTools, ...collaborationToolsInMatrix];
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Matrix Table -->
|
||||
<div class="matrix-wrapper">
|
||||
<table class="matrix-table" id="matrix-table">
|
||||
<!-- DFIR Tools Matrix -->
|
||||
<div id="dfir-matrix-section">
|
||||
<h2 style="margin-bottom: 1rem; color: var(--color-text);">DFIR Tools Matrix</h2>
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="background-color: var(--color-bg-secondary);">Domäne / Phase</th>
|
||||
{phases.map((phase: any) => (
|
||||
<th data-phase={phase.id} style="text-align: center; min-width: 150px;">
|
||||
{phase.name}
|
||||
</th>
|
||||
<th style="width: 200px;">Domain / Phase</th>
|
||||
{phases.filter((phase: any) => phase.id !== 'collaboration-general').map((phase: any) => (
|
||||
<th data-phase={phase.id}>{phase.name}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{domains.map((domain: any) => (
|
||||
<tr data-domain={domain.id}>
|
||||
<th data-domain={domain.id} style="background-color: var(--color-bg-secondary); font-weight: 600;">
|
||||
{domain.name}
|
||||
</th>
|
||||
{phases.map((phase: any) => {
|
||||
// Find tools for this domain-phase combination
|
||||
const cellTools = allMatrixTools.filter((tool: any) => {
|
||||
const hasPhase = tool.phases && tool.phases.includes(phase.id);
|
||||
const hasDomain = tool.domains && tool.domains.includes(domain.id);
|
||||
return hasPhase && hasDomain;
|
||||
});
|
||||
|
||||
return (
|
||||
<td data-domain={domain.id} data-phase={phase.id} class="matrix-cell">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
{cellTools.map((tool: any) => {
|
||||
<th>{domain.name}</th>
|
||||
{phases.filter((phase: any) => phase.id !== 'collaboration-general').map((phase: any) => (
|
||||
<td class="matrix-cell" data-domain={domain.id} data-phase={phase.id}>
|
||||
{matrix[domain.id][phase.id].map((tool: any) => {
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
return (
|
||||
<span
|
||||
class={`tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : (tool.license !== 'Proprietary' ? 'tool-chip-oss' : '')}`}
|
||||
class={`tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`}
|
||||
data-tool-name={tool.name}
|
||||
onclick={`window.showToolDetails('${tool.name}')`}
|
||||
title={tool.description}
|
||||
title={`${tool.name}${tool.knowledgebase === true ? ' (KB verfügbar)' : ''}`}
|
||||
>
|
||||
{tool.name}
|
||||
{tool.knowledgebase === true && <span style="margin-left: 0.25rem; font-size: 0.6875rem;">📖</span>}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Matrix Statistics -->
|
||||
<div style="margin-top: 2rem; text-align: center;">
|
||||
<div style="display: inline-flex; gap: 2rem; background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem;">
|
||||
<div>
|
||||
<strong style="color: var(--color-primary);" id="matrix-total-tools">{allMatrixTools.length}</strong>
|
||||
<span class="text-muted" style="font-size: 0.875rem;">Tools in Matrix</span>
|
||||
<!-- Tool Details Modal -->
|
||||
<div class="modal-overlay" id="modal-overlay" onclick="window.hideToolDetails()"></div>
|
||||
<div class="tool-details" id="tool-details">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
||||
<h2 id="tool-name" style="margin: 0;">Tool Name</h2>
|
||||
<button class="btn-icon" onclick="window.hideToolDetails()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<strong style="color: var(--color-accent);" id="collaboration-total-tools">{collaborationTools.length}</strong>
|
||||
<span class="text-muted" style="font-size: 0.875rem;">Collaboration Tools</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong style="color: var(--color-text);" id="matrix-visible-tools">{allMatrixTools.length + collaborationTools.length}</strong>
|
||||
<span class="text-muted" style="font-size: 0.875rem;">Gesamt sichtbar</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script define:vars={{ allMatrixTools, collaborationTools, domains, phases }}>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const matrixTable = document.getElementById('matrix-table');
|
||||
const collaborationSection = document.getElementById('collaboration-section');
|
||||
<p id="tool-description" class="text-muted"></p>
|
||||
|
||||
if (!matrixTable || !collaborationSection) return;
|
||||
<div id="tool-badges" style="display: flex; gap: 0.5rem; margin-bottom: 1rem;"></div>
|
||||
|
||||
// Store original tools data
|
||||
let currentMatrixTools = [...allMatrixTools];
|
||||
let currentCollaborationTools = [...collaborationTools];
|
||||
<div id="tool-metadata" style="margin-bottom: 1rem;"></div>
|
||||
|
||||
// Matrix hover effects
|
||||
let hoveredRow = null;
|
||||
let hoveredColumn = null;
|
||||
<div id="tool-tags" style="margin-bottom: 1rem;"></div>
|
||||
|
||||
function updateMatrixHighlight() {
|
||||
// Clear all highlights
|
||||
matrixTable.querySelectorAll('tr').forEach(row => row.classList.remove('highlight-row'));
|
||||
matrixTable.querySelectorAll('th, td').forEach(cell => cell.classList.remove('highlight-column'));
|
||||
<!-- Updated button container for dual buttons -->
|
||||
<div id="tool-links" style="display: flex; gap: 0.5rem; flex-direction: column;"></div>
|
||||
</div>
|
||||
|
||||
// Apply current highlights
|
||||
if (hoveredRow !== null) {
|
||||
const rows = matrixTable.querySelectorAll('tr');
|
||||
if (rows[hoveredRow]) {
|
||||
rows[hoveredRow].classList.add('highlight-row');
|
||||
}
|
||||
<script define:vars={{ toolsData: tools, collaborationTools, domainTools }}>
|
||||
// Helper function to get selected phase from active button
|
||||
function getSelectedPhase() {
|
||||
const activePhaseButton = document.querySelector('.phase-button.active');
|
||||
return activePhaseButton ? activePhaseButton.getAttribute('data-phase') : '';
|
||||
}
|
||||
|
||||
if (hoveredColumn !== null) {
|
||||
const cells = matrixTable.querySelectorAll(`th:nth-child(${hoveredColumn + 1}), td:nth-child(${hoveredColumn + 1})`);
|
||||
cells.forEach(cell => cell.classList.add('highlight-column'));
|
||||
}
|
||||
// Helper function to get selected domain from dropdown
|
||||
function getSelectedDomain() {
|
||||
const domainSelect = document.getElementById('domain-select');
|
||||
return domainSelect ? domainSelect.value : '';
|
||||
}
|
||||
|
||||
// Add hover listeners to matrix table
|
||||
matrixTable.addEventListener('mouseover', (e) => {
|
||||
const target = e.target.closest('tr');
|
||||
if (target) {
|
||||
const rows = Array.from(matrixTable.querySelectorAll('tr'));
|
||||
hoveredRow = rows.indexOf(target);
|
||||
updateMatrixHighlight();
|
||||
}
|
||||
// Update matrix highlighting based on current filters
|
||||
function updateMatrixHighlighting() {
|
||||
const matrixTable = document.querySelector('.matrix-table');
|
||||
if (!matrixTable) return;
|
||||
|
||||
// Clear existing highlights
|
||||
matrixTable.querySelectorAll('.highlight-row, .highlight-column').forEach(el => {
|
||||
el.classList.remove('highlight-row', 'highlight-column');
|
||||
});
|
||||
|
||||
matrixTable.addEventListener('mouseout', (e) => {
|
||||
if (!e.relatedTarget || !matrixTable.contains(e.relatedTarget)) {
|
||||
hoveredRow = null;
|
||||
updateMatrixHighlight();
|
||||
}
|
||||
});
|
||||
const selectedDomain = getSelectedDomain();
|
||||
const selectedPhase = getSelectedPhase();
|
||||
|
||||
// Add hover listeners to header cells
|
||||
matrixTable.addEventListener('mouseover', (e) => {
|
||||
if (e.target.matches('th[data-phase]')) {
|
||||
const headers = Array.from(matrixTable.querySelectorAll('thead th'));
|
||||
hoveredColumn = headers.indexOf(e.target);
|
||||
updateMatrixHighlight();
|
||||
// Highlight selected domain row
|
||||
if (selectedDomain) {
|
||||
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
|
||||
if (domainRow) {
|
||||
domainRow.classList.add('highlight-row');
|
||||
}
|
||||
});
|
||||
|
||||
matrixTable.addEventListener('mouseout', (e) => {
|
||||
if (e.target.matches('th[data-phase]') && (!e.relatedTarget || !matrixTable.contains(e.relatedTarget))) {
|
||||
hoveredColumn = null;
|
||||
updateMatrixHighlight();
|
||||
}
|
||||
});
|
||||
|
||||
// Function to update matrix display based on filtered tools
|
||||
function updateMatrixDisplay(filteredTools) {
|
||||
// Separate collaboration tools from matrix tools
|
||||
const filteredCollaborationTools = filteredTools.filter(tool =>
|
||||
tool.phases && tool.phases.includes('collaboration-general')
|
||||
// Highlight selected phase column
|
||||
if (selectedPhase) {
|
||||
const phaseHeaders = matrixTable.querySelectorAll('thead th[data-phase]');
|
||||
const phaseIndex = Array.from(phaseHeaders).findIndex(th =>
|
||||
th.getAttribute('data-phase') === selectedPhase
|
||||
);
|
||||
|
||||
const filteredMatrixTools = filteredTools.filter(tool => {
|
||||
if (!tool.phases || tool.phases.length === 0) return false;
|
||||
const hasRegularPhases = tool.phases.some(phase => phase !== 'collaboration-general');
|
||||
const hasDomains = tool.domains && tool.domains.length > 0;
|
||||
return hasRegularPhases && hasDomains;
|
||||
if (phaseIndex >= 0) {
|
||||
const columnIndex = phaseIndex + 1; // +1 because first column is domain names
|
||||
matrixTable.querySelectorAll(`tr`).forEach(row => {
|
||||
const cell = row.children[columnIndex];
|
||||
if (cell) {
|
||||
cell.classList.add('highlight-column');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update collaboration tools section visibility
|
||||
if (filteredCollaborationTools.length === 0) {
|
||||
collaborationSection.style.display = 'none';
|
||||
} else {
|
||||
collaborationSection.style.display = 'block';
|
||||
|
||||
// Update collaboration tools display
|
||||
const collaborationContainer = collaborationSection.querySelector('.collaboration-tools-compact');
|
||||
if (collaborationContainer) {
|
||||
collaborationContainer.innerHTML = '';
|
||||
|
||||
filteredCollaborationTools.forEach(tool => {
|
||||
// Helper function to create compact collaboration tool cards for matrix view
|
||||
function createCollaborationToolCardCompact(tool) {
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
|
||||
const toolDiv = document.createElement('div');
|
||||
toolDiv.className = `collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}`;
|
||||
toolDiv.onclick = () => window.showToolDetails(tool.name);
|
||||
const cardDiv = document.createElement('div');
|
||||
const cardClass = `collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`;
|
||||
cardDiv.className = cardClass;
|
||||
cardDiv.onclick = () => window.showToolDetails(tool.name);
|
||||
|
||||
toolDiv.innerHTML = `
|
||||
cardDiv.innerHTML = `
|
||||
<div class="tool-compact-header">
|
||||
<h4 style="margin: 0; font-size: 1rem;">${tool.name}</h4>
|
||||
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">${tool.name}</h4>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
${hasValidProjectUrl ? '<span class="badge-mini badge-primary">Self-Hosted</span>' : ''}
|
||||
${tool.license !== 'Proprietary' ? '<span class="badge-mini badge-success">OSS</span>' : ''}
|
||||
${tool.knowledgebase === true ? '<span class="badge-mini badge-error">Infos 📖</span>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 0.75rem; line-height: 1.4;">
|
||||
${tool.description.length > 120 ? `${tool.description.substring(0, 120)}...` : tool.description}
|
||||
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
||||
${tool.description}
|
||||
</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
${tool.tags ? tool.tags.slice(0, 3).map(tag => `<span class="tag" style="font-size: 0.625rem;">${tag}</span>`).join('') : ''}
|
||||
${tool.tags && tool.tags.length > 3 ? `<span class="tag" style="font-size: 0.625rem; font-style: italic;">+${tool.tags.length - 3}</span>` : ''}
|
||||
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
||||
<span>${(tool.platforms || []).join(', ')}</span>
|
||||
<span>•</span>
|
||||
<span>${tool.skillLevel}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
collaborationContainer.appendChild(toolDiv);
|
||||
});
|
||||
}
|
||||
return cardDiv;
|
||||
}
|
||||
|
||||
// Update matrix cells
|
||||
domains.forEach(domain => {
|
||||
phases.forEach(phase => {
|
||||
const cell = matrixTable.querySelector(`td[data-domain="${domain.id}"][data-phase="${phase.id}"]`);
|
||||
if (!cell) return;
|
||||
// Tool details functions
|
||||
window.showToolDetails = function(toolName) {
|
||||
const tool = toolsData.find(t => t.name === toolName);
|
||||
if (!tool) return;
|
||||
|
||||
const cellTools = filteredMatrixTools.filter(tool => {
|
||||
const hasPhase = tool.phases && tool.phases.includes(phase.id);
|
||||
const hasDomain = tool.domains && tool.domains.includes(domain.id);
|
||||
return hasPhase && hasDomain;
|
||||
});
|
||||
// Update modal content
|
||||
document.getElementById('tool-name').textContent = tool.name;
|
||||
document.getElementById('tool-description').textContent = tool.description;
|
||||
|
||||
const cellContent = cell.querySelector('div') || cell;
|
||||
cellContent.innerHTML = '';
|
||||
|
||||
cellTools.forEach(tool => {
|
||||
// Badges
|
||||
const badgesContainer = document.getElementById('tool-badges');
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
|
||||
const chip = document.createElement('span');
|
||||
chip.className = `tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : (tool.license !== 'Proprietary' ? 'tool-chip-oss' : '')}`;
|
||||
chip.onclick = () => window.showToolDetails(tool.name);
|
||||
chip.title = tool.description;
|
||||
chip.textContent = tool.name;
|
||||
|
||||
cellContent.appendChild(chip);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Update statistics
|
||||
const matrixTotalEl = document.getElementById('matrix-total-tools');
|
||||
const collaborationTotalEl = document.getElementById('collaboration-total-tools');
|
||||
const visibleTotalEl = document.getElementById('matrix-visible-tools');
|
||||
|
||||
if (matrixTotalEl) matrixTotalEl.textContent = filteredMatrixTools.length;
|
||||
if (collaborationTotalEl) collaborationTotalEl.textContent = filteredCollaborationTools.length;
|
||||
if (visibleTotalEl) visibleTotalEl.textContent = (filteredMatrixTools.length + filteredCollaborationTools.length);
|
||||
badgesContainer.innerHTML = '';
|
||||
if (hasValidProjectUrl) {
|
||||
badgesContainer.innerHTML += '<span class="badge badge-primary">Self-Hosted</span>';
|
||||
}
|
||||
if (tool.license !== 'Proprietary') {
|
||||
badgesContainer.innerHTML += '<span class="badge badge-success">Open Source</span>';
|
||||
}
|
||||
if (tool.knowledgebase === true) {
|
||||
badgesContainer.innerHTML += '<span class="badge badge-error">Infos 📖</span>';
|
||||
}
|
||||
|
||||
// Listen for filter events
|
||||
window.addEventListener('toolsFiltered', (event) => {
|
||||
const filteredTools = event.detail;
|
||||
updateMatrixDisplay(filteredTools);
|
||||
// Metadata - safe array handling
|
||||
const metadataContainer = document.getElementById('tool-metadata');
|
||||
const domains = tool.domains || [];
|
||||
const phases = tool.phases || [];
|
||||
const domainsText = domains.length > 0 ? domains.join(', ') : 'Domain-agnostic';
|
||||
const phasesText = phases.join(', ');
|
||||
metadataContainer.innerHTML = `
|
||||
<div style="display: grid; gap: 0.5rem;">
|
||||
<div><strong>Betriebssystem:</strong> ${(tool.platforms || []).join(', ')}</div>
|
||||
<div><strong>Skill Level:</strong> ${tool.skillLevel}</div>
|
||||
<div><strong>Lizenzmodell:</strong> ${tool.license}</div>
|
||||
<div><strong>Deployment:</strong> ${tool.accessType}</div>
|
||||
<div><strong>Einsatzgebiete:</strong> ${domainsText}</div>
|
||||
<div><strong>Ermittlungsphasen:</strong> ${phasesText}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Tags - safe array handling
|
||||
const tagsContainer = document.getElementById('tool-tags');
|
||||
const tags = tool.tags || [];
|
||||
tagsContainer.innerHTML = `
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
||||
${tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Links - Updated to handle dual buttons for hosted tools AND knowledgebase links
|
||||
const linksContainer = document.getElementById('tool-links');
|
||||
|
||||
let linksHTML = '';
|
||||
|
||||
// Main action buttons
|
||||
if (hasValidProjectUrl) {
|
||||
// Two buttons for tools we're hosting
|
||||
linksHTML += `
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
|
||||
Software-Homepage
|
||||
</a>
|
||||
<a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
|
||||
Zugreifen
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
// Single button for tools we're not hosting
|
||||
linksHTML += `
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Software-Homepage
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
// Add knowledgebase link if available
|
||||
if (tool.knowledgebase === true) {
|
||||
const kbId = tool.name.toLowerCase().replace(/\s+/g, '-');
|
||||
linksHTML += `
|
||||
<a href="/knowledgebase#kb-${kbId}" class="btn btn-secondary" style="width: 100%; margin-top: 0.5rem;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
<polyline points="10 9 9 9 8 9"/>
|
||||
</svg>
|
||||
Knowledgebase anzeigen
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
linksContainer.innerHTML = linksHTML;
|
||||
|
||||
// Show modal
|
||||
document.getElementById('modal-overlay').classList.add('active');
|
||||
document.getElementById('tool-details').classList.add('active');
|
||||
};
|
||||
|
||||
window.hideToolDetails = function() {
|
||||
document.getElementById('modal-overlay').classList.remove('active');
|
||||
document.getElementById('tool-details').classList.remove('active');
|
||||
};
|
||||
|
||||
// Listen for view changes to trigger highlighting
|
||||
window.addEventListener('viewChanged', (event) => {
|
||||
const view = event.detail;
|
||||
if (view === 'matrix') {
|
||||
// Delay highlighting to ensure matrix is visible
|
||||
setTimeout(updateMatrixHighlighting, 100);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial display
|
||||
updateMatrixDisplay([...allMatrixTools, ...collaborationTools]);
|
||||
});
|
||||
// Listen for filter changes to update highlighting
|
||||
window.addEventListener('toolsFiltered', (event) => {
|
||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||
if (currentView === 'matrix') {
|
||||
setTimeout(updateMatrixHighlighting, 50);
|
||||
}
|
||||
});
|
||||
|
||||
// Update matrix on filter change
|
||||
window.addEventListener('toolsFiltered', (event) => {
|
||||
const filtered = event.detail;
|
||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||
|
||||
if (currentView === 'matrix') {
|
||||
// Get selected phase from active button instead of dropdown
|
||||
const selectedPhase = getSelectedPhase();
|
||||
|
||||
// Handle collaboration tools section
|
||||
const collaborationSection = document.getElementById('collaboration-tools-section');
|
||||
const dfirMatrixSection = document.getElementById('dfir-matrix-section');
|
||||
const collaborationContainer = document.getElementById('collaboration-tools-container');
|
||||
|
||||
if (selectedPhase === 'collaboration-general') {
|
||||
// Show only collaboration tools, hide matrix
|
||||
collaborationSection.style.display = 'block';
|
||||
dfirMatrixSection.style.display = 'none';
|
||||
|
||||
// Filter collaboration tools
|
||||
const filteredCollaboration = filtered.filter(tool => (tool.phases || []).includes('collaboration-general'));
|
||||
collaborationContainer.innerHTML = '';
|
||||
|
||||
filteredCollaboration.forEach(tool => {
|
||||
const toolCard = createCollaborationToolCardCompact(tool);
|
||||
collaborationContainer.appendChild(toolCard);
|
||||
});
|
||||
} else {
|
||||
// Show matrix, handle collaboration tools visibility
|
||||
dfirMatrixSection.style.display = 'block';
|
||||
|
||||
if (selectedPhase === '' || selectedPhase === null) {
|
||||
// Show collaboration tools when no specific phase is selected
|
||||
collaborationSection.style.display = 'block';
|
||||
|
||||
// Show all collaboration tools that pass general filters
|
||||
const filteredCollaboration = filtered.filter(tool => (tool.phases || []).includes('collaboration-general'));
|
||||
collaborationContainer.innerHTML = '';
|
||||
|
||||
filteredCollaboration.forEach(tool => {
|
||||
const toolCard = createCollaborationToolCardCompact(tool);
|
||||
collaborationContainer.appendChild(toolCard);
|
||||
});
|
||||
} else {
|
||||
// Hide collaboration tools when specific DFIR phase is selected
|
||||
collaborationSection.style.display = 'none';
|
||||
}
|
||||
|
||||
// Clear and update matrix cells with DFIR tools only
|
||||
document.querySelectorAll('.matrix-cell').forEach(cell => {
|
||||
cell.innerHTML = '';
|
||||
});
|
||||
|
||||
// Re-populate with filtered DFIR tools - with safe array handling
|
||||
const filteredDfirTools = filtered.filter(tool => !(tool.phases || []).includes('collaboration-general'));
|
||||
filteredDfirTools.forEach(tool => {
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
|
||||
const domains = tool.domains || [];
|
||||
const phases = tool.phases || [];
|
||||
|
||||
domains.forEach(domain => {
|
||||
phases.forEach(phase => {
|
||||
if (phase !== 'collaboration-general') {
|
||||
const cell = document.querySelector(`[data-domain="${domain}"][data-phase="${phase}"]`);
|
||||
if (cell) {
|
||||
const chip = document.createElement('span');
|
||||
chip.className = `tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`;
|
||||
chip.setAttribute('title', `${tool.name}${tool.knowledgebase === true ? ' (KB verfügbar)' : ''}`);
|
||||
chip.innerHTML = `${tool.name}${tool.knowledgebase === true ? '<span style="margin-left: 0.25rem; font-size: 0.6875rem;">📖</span>' : ''}`;
|
||||
chip.onclick = () => window.showToolDetails(tool.name);
|
||||
cell.appendChild(chip);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Update highlighting after matrix content is updated
|
||||
setTimeout(updateMatrixHighlighting, 50);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -78,7 +78,7 @@ const tools = data.tools;
|
||||
|
||||
<!-- Tools Grid -->
|
||||
<section id="tools-grid" style="padding-bottom: 2rem;">
|
||||
<div class="grid grid-cols-3 gap-4" id="tools-container">
|
||||
<div class="grid-auto-fit" id="tools-container">
|
||||
{tools.map((tool: any) => (
|
||||
<ToolCard tool={tool} />
|
||||
))}
|
||||
@ -352,16 +352,16 @@ function createToolCard(tool) {
|
||||
cardDiv.style.cursor = 'pointer';
|
||||
cardDiv.onclick = () => (window as any).showToolDetails(tool.name);
|
||||
|
||||
// Create button HTML based on hosting status
|
||||
// Create responsive button HTML
|
||||
let buttonHTML;
|
||||
if (hasValidProjectUrl) {
|
||||
// Two buttons for tools we're hosting
|
||||
// Two buttons for tools we're hosting - responsive layout
|
||||
buttonHTML = `
|
||||
<div style="display: flex; gap: 0.5rem;" onclick="event.stopPropagation();">
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
|
||||
<div class="button-container" style="display: flex; gap: 0.5rem;" onclick="event.stopPropagation();">
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1; text-align: center;">
|
||||
Software-Homepage
|
||||
</a>
|
||||
<a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
|
||||
<a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1; text-align: center;">
|
||||
Zugreifen
|
||||
</a>
|
||||
</div>
|
||||
@ -369,28 +369,28 @@ function createToolCard(tool) {
|
||||
} else {
|
||||
// Single button for tools we're not hosting
|
||||
buttonHTML = `
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;" onclick="event.stopPropagation();">
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%; text-align: center;" onclick="event.stopPropagation();">
|
||||
Software-Homepage
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
cardDiv.innerHTML = `
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.75rem;">
|
||||
<h3 style="margin: 0;">${tool.name}</h3>
|
||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 0.75rem; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<h3 style="margin: 0; flex: 1; min-width: 0;">${tool.name}</h3>
|
||||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; flex-shrink: 0;">
|
||||
${hasValidProjectUrl ? '<span class="badge badge-primary">Self-Hosted</span>' : ''}
|
||||
${tool.license !== 'Proprietary' ? '<span class="badge badge-success">Open Source</span>' : ''}
|
||||
${hasKnowledgebase ? '<span class="badge badge-error">Infos 📖</span>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-muted" style="font-size: 0.875rem; margin-bottom: 1rem;">
|
||||
<p class="text-muted" style="font-size: 0.875rem; margin-bottom: 1rem; line-height: 1.5;">
|
||||
${tool.description}
|
||||
</p>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem;">
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem;">
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<line x1="9" y1="9" x2="15" y2="9"></line>
|
||||
@ -401,7 +401,7 @@ function createToolCard(tool) {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem;">
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
@ -411,7 +411,7 @@ function createToolCard(tool) {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem;">
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||
<polyline points="14 2 14 8 20 8"></polyline>
|
||||
|
@ -136,6 +136,8 @@ nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
overflow-x: hidden; /* Prevent horizontal overflow */
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
@ -143,12 +145,18 @@ nav {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 0;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.nav-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0; /* Prevent brand from shrinking */
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
@ -159,15 +167,20 @@ nav {
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5rem;
|
||||
gap: 2rem; /* Reduced from 5rem */
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-wrap: wrap; /* Allow wrapping on very small screens */
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--color-text);
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
font-size: 1.2rem;
|
||||
font-size: 1rem; /* Reduced from 1.2rem */
|
||||
white-space: nowrap; /* Prevent text wrapping */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
@ -296,11 +309,20 @@ input[type="checkbox"] {
|
||||
box-shadow: 0 0 0 1px var(--color-hosted);
|
||||
}
|
||||
|
||||
/* Adjust gradient for different card types */
|
||||
.card-hosted .tool-tags-container::after {
|
||||
background: linear-gradient(to bottom, transparent 0%, var(--color-hosted-bg) 80%);
|
||||
}
|
||||
|
||||
.card-oss {
|
||||
background-color: var(--color-oss-bg);
|
||||
border-color: var(--color-oss);
|
||||
}
|
||||
|
||||
.card-oss .tool-tags-container::after {
|
||||
background: linear-gradient(to bottom, transparent 0%, var(--color-oss-bg) 80%);
|
||||
}
|
||||
|
||||
/* Grid and Layout */
|
||||
.grid {
|
||||
display: grid;
|
||||
@ -323,6 +345,19 @@ input[type="checkbox"] {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
/* NEW: Auto-responsive grid classes */
|
||||
.grid-auto-fit {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.grid-auto-fit-small {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Tool Matrix */
|
||||
.matrix-wrapper {
|
||||
overflow-x: auto;
|
||||
@ -421,6 +456,37 @@ input[type="checkbox"] {
|
||||
box-shadow: inset 0 0 0 1px rgb(59 130 246 / 40%);
|
||||
}
|
||||
|
||||
/* Tool Card Description Truncation - 4 lines max */
|
||||
.tool-card .text-muted {
|
||||
display: -webkit-box !important;
|
||||
-webkit-line-clamp: 4;
|
||||
line-clamp: 4;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden !important;
|
||||
line-height: 1.4;
|
||||
max-height: calc(1.4em * 4);
|
||||
}
|
||||
|
||||
/* Tool Card Tags Truncation - 3 lines max (uses height approach since we need flex) */
|
||||
.tool-card .tool-tags-container {
|
||||
max-height: calc(1.5em * 3 + 0.5rem) !important; /* 3 lines + gap space */
|
||||
overflow: hidden !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Add subtle fade for truncated tags */
|
||||
.tool-card .tool-tags-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1.2em;
|
||||
background: linear-gradient(to bottom, transparent 0%, var(--color-bg) 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.tool-chip {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
@ -1007,23 +1073,43 @@ footer {
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (width: 768px) {
|
||||
@media (width <= 1200px) {
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 900px) {
|
||||
.grid-cols-3,
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.grid-cols-2 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
@ -1143,13 +1229,21 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
@media (width: 600px) {
|
||||
.phase-tools {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width: 640px) {
|
||||
|
||||
@media (width <= 640px) {
|
||||
.grid-cols-2,
|
||||
.grid-cols-3,
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.grid,
|
||||
.grid-auto-fit,
|
||||
.grid-auto-fit-small {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.phase-buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
@ -1167,9 +1261,35 @@ footer {
|
||||
.tag-cloud.expanded {
|
||||
max-height: 800px;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
gap: 0.75rem;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 0.8125rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width: 480px) {
|
||||
@media (width <= 600px) {
|
||||
.phase-tools {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
.phase-buttons {
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
@ -1199,6 +1319,31 @@ footer {
|
||||
.phase-tools {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.375rem;
|
||||
}
|
||||
|
||||
.nav-brand h1 {
|
||||
font-size: 1.25rem; /* Smaller brand text on very small screens */
|
||||
}
|
||||
|
||||
.grid,
|
||||
.grid-auto-fit,
|
||||
.grid-auto-fit-small {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* AI Interface Container */
|
||||
|
Loading…
x
Reference in New Issue
Block a user