Hello from MCP server

List Files | Just Commands | Repo | Logs

← back |
<template>
  <BaseLayout title="Technician - Service Call">
    <!-- Toolbar at top of screen -->
    <Toolbar :force-step1="true" @help-clicked="openHelpModal" @agent-clicked="openAgentModal" />

    <div class="dashboard-container">
      <div class="content-section">
        <!-- Add More Jobs Button - shown when step5 is true and search is hidden -->
        <div v-if="sessionStore.appProgress.step5 && !showSearch" class="add-jobs-button-container">
          <ion-button expand="block" color="secondary" @click="showSearch = true">
            Add More Jobs
          </ion-button>
        </div>

        <!-- Search Component - shown by default or when button is clicked -->
        <SearchBar
          v-if="!sessionStore.appProgress.step5 || showSearch"
          :search-terms="appliedSearchTerms"
          :selected-tags="selectedTagObjects"
          :available-tags="availableTagObjects"
          :suggestions="searchSuggestions"
          @navigate="handleNavigate"
          @search-input="handleSearchInput"
        />

        <div v-if="displayJobs.length > 0" class="divider-compact"></div>

        <!-- Jobs Section Heading -->
        <h2 v-if="displayJobs.length > 0 && sessionStore.appProgress.step5" class="jobs-section-heading">Copy All Job Details To Continue</h2>

        <!-- Jobs Section -->
        <div class="jobs-section">
          <DashboardJob
            v-for="job in displayJobs"
            :key="job.id"
            :title="job.title"
            :hours="job.hours"
            :blink-fix-hours="job.blinkFixHours"
            :has-confirmed-offer="job.hasConfirmedOffer"
            :job-data="job.jobData"
            @delete="() => handleDeleteJob(job.id)"
            @details="() => handleJobDetails(job.id)"
            @fix-hours="() => handleFixHours(job.id)"
            @edit="() => handleEdit(job.id)"
            @copied="() => handleJobCopied(job.id)"
            @card-click="() => handleJobCardClick(job.id)"
          />
        </div>

        <!-- Show Menus and Invoice Buttons - shown when all jobs have completed check-hours -->
        <div v-if="allJobsHaveHours" class="show-menus-button-container">
          <ion-button expand="block" color="primary" size="large" @click="handleShowMenus">
            Show Menus
          </ion-button>
          <ion-button expand="block" color="success" size="large" @click="handleGoToInvoice">
            Go to Invoice
          </ion-button>
        </div>
      </div>
    </div>

    <!-- Help Modal -->
    <HelpModal :is-open="isHelpModalOpen" title="Service Call" @close="closeHelpModal">
      <template #documentation>
        <h2>Service Call Dashboard</h2>
        <p>This is your main screen for managing service calls. From here you can search for jobs, view active jobs, and track your progress.</p>

        <h3>Adding Jobs</h3>
        <ul>
          <li>Use the search bar to find jobs by name or tags</li>
          <li>Click on a job to start the confirmation process</li>
          <li>Multiple jobs can be added to a single service call</li>
        </ul>

        <h3>Job Cards</h3>
        <ul>
          <li>Each card shows the job name and estimated hours</li>
          <li>Click "Edit" to modify job details</li>
          <li>Click "Delete" to remove a job from the service call</li>
          <li>Once customer selects an option, use "Copy" to copy details</li>
        </ul>

        <h3>Workflow</h3>
        <p>The progress dots at the top show your position in the workflow: Find Job → Confirm Job → Check Hours → Add Jobs → Show Menus</p>
      </template>
    </HelpModal>

    <!-- Agent Modal -->
    <AgentModal :is-open="isAgentModalOpen" @close="closeAgentModal" />

    <!-- Bookmarks Modal -->
    <ion-modal :is-open="isBookmarksModalOpen" @didDismiss="closeBookmarksModal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Bookmarks</ion-title>
          <ion-buttons slot="end">
            <ion-button @click="closeBookmarksModal">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <div class="bookmarks-content">
          <div class="bookmarks-list">
            <div
              v-for="job in bookmarkedJobs"
              :key="job.id"
              @click="selectBookmarkedJob(job)"
              class="bookmark-item"
            >
              <ion-icon :icon="star" color="warning" class="bookmark-star"></ion-icon>
              <div class="bookmark-details">
                <h3 class="bookmark-title">{{ job.name }}</h3>
                <p class="bookmark-description">{{ job.description }}</p>
              </div>
            </div>
          </div>
        </div>
      </ion-content>
    </ion-modal>

    <!-- Delete Confirmation Alert -->
    <ion-alert
      :is-open="isDeleteAlertOpen"
      header="Delete Job"
      message="Are you sure you want to delete this job? This action cannot be undone."
      :buttons="[
        {
          text: 'Cancel',
          role: 'cancel',
          handler: cancelDelete
        },
        {
          text: 'Delete',
          role: 'destructive',
          handler: confirmDelete
        }
      ]"
      @didDismiss="cancelDelete"
    ></ion-alert>

    <!-- Job Details Modal -->
    <ion-modal :is-open="isJobDetailsModalOpen" @didDismiss="closeJobDetailsModal">
      <ion-header>
        <ion-toolbar>
          <ion-title>Job Details{{ sessionStore.editMode ? ' (Edit Mode)' : '' }}</ion-title>
          <ion-buttons slot="end">
            <ion-button @click="closeJobDetailsModal">Close</ion-button>
          </ion-buttons>
        </ion-toolbar>
      </ion-header>
      <ion-content class="ion-padding">
        <div v-if="selectedJobForDetails" class="job-details-content">
          <div class="detail-section">
            <h3 class="detail-label">Job</h3>
            <p class="detail-value">{{ selectedJobForDetails.problem?.name || selectedJobForDetails.title || 'Untitled Job' }}</p>
          </div>

          <div v-if="selectedJobForDetails.problem?.description" class="detail-section">
            <h3 class="detail-label">Description</h3>
            <p class="detail-value">{{ selectedJobForDetails.problem.description }}</p>
          </div>

          <div class="detail-section">
            <h3 class="detail-label">
              Steps
              <ion-icon v-if="sessionStore.editMode && !editingSteps" :icon="pencil" class="edit-icon" @click="startEditingSteps" />
            </h3>
            <!-- Edit mode: textarea for multiline editing -->
            <div v-if="editingSteps" class="steps-edit-container">
              <ion-textarea
                v-model="editStepsText"
                :rows="8"
                placeholder="Enter steps, one per line..."
                class="steps-textarea"
              />
              <div class="steps-edit-buttons">
                <ion-button size="small" fill="outline" @click="cancelEditingSteps">Cancel</ion-button>
                <ion-button size="small" @click="saveSteps">Save</ion-button>
              </div>
            </div>
            <!-- View mode: ordered list -->
            <ol v-else class="steps-list">
              <li v-for="(step, index) in getJobSteps(selectedJobForDetails)" :key="index" class="step-item">
                {{ step }}
              </li>
            </ol>
          </div>

          <div v-if="selectedJobForDetails.baseHours" class="detail-section">
            <h3 class="detail-label">Estimated Time</h3>
            <p class="detail-value">{{ ((selectedJobForDetails.baseHours || 0) + (selectedJobForDetails.extraTime || 0)).toFixed(1) }} hours</p>
          </div>

          <div v-if="selectedJobForDetails.selectedTierName" class="detail-section">
            <h3 class="detail-label">Selected Option</h3>
            <p class="detail-value">{{ selectedJobForDetails.selectedTierName }}</p>
          </div>

          <div v-if="selectedJobForDetails.selectedPrice" class="detail-section">
            <h3 class="detail-label">Price</h3>
            <p class="detail-value detail-price">${{ parseFloat(selectedJobForDetails.selectedPrice).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}</p>
          </div>

          <div v-if="selectedJobForDetails.notes && selectedJobForDetails.notes.length > 0" class="detail-section">
            <h3 class="detail-label">Notes</h3>
            <div class="detail-notes">
              <p v-for="(note, index) in selectedJobForDetails.notes" :key="index" class="detail-note">{{ note }}</p>
            </div>
          </div>
        </div>
      </ion-content>
    </ion-modal>
  </BaseLayout>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted } from "vue";
import BaseLayout from "@/components/BaseLayout.vue";
import Toolbar from "@/components/Toolbar.vue";
import SearchBar from "@/components/SearchBar.vue";
import DashboardJob from "@/components/DashboardJob.vue";
import HelpModal from "@/components/HelpModal.vue";
import AgentModal from "@/components/AgentModal.vue";
import type { ProblemsRecord, ProblemTagsRecord } from "@/pocketbase-types";
import { IonModal, IonHeader, IonToolbar, IonTitle, IonButtons, IonButton, IonContent, IonIcon, IonAlert, IonTextarea } from "@ionic/vue";
import { star, pencil } from "ionicons/icons";
import { useRouter, useRoute } from "vue-router";
import { useSessionStore } from "@/stores/session";
import { useJobsAndTags } from "@/composables/useJobsAndTags";

const router = useRouter();
const route = useRoute();
const sessionStore = useSessionStore();

// Use composable for jobs and tags logic
const {
  problems,
  problemTags,
  selectedTags,
  appliedSearchQuery,
  getProblemTagIds,
  availableTagObjects,
  selectedTagObjects,
  appliedSearchTerms
} = useJobsAndTags();

// Clear search state and ensure tags are loaded whenever we navigate to service call (including back button)
watch(() => route.path, async (newPath) => {
  if (newPath === '/service-call') {
    await sessionStore.clearSearch();
    // Ensure problems and tags are always loaded from database
    await sessionStore.loadProblemsAndTags();
  }
}, { immediate: true });

const isHelpModalOpen = ref(false);
const isAgentModalOpen = ref(false);
const isBookmarksModalOpen = ref(false);
const isDeleteAlertOpen = ref(false);
const jobToDelete = ref<string | null>(null);
const showSearch = ref(false);
const copiedJobIds = ref<Set<string>>(new Set());
const isJobDetailsModalOpen = ref(false);
const selectedJobForDetails = ref<any>(null);

// Edit mode state for job details modal
const editingSteps = ref(false);
const editStepsText = ref('');

const openHelpModal = () => {
  isHelpModalOpen.value = true;
};

const closeHelpModal = () => {
  isHelpModalOpen.value = false;
};

const openAgentModal = () => {
  isAgentModalOpen.value = true;
};

const closeAgentModal = () => {
  isAgentModalOpen.value = false;
};

const logActiveJobs = () => {
  console.log('Active Jobs:', sessionStore.jobs);
  console.log('Number of Jobs:', sessionStore.jobs.length);
  console.log('App Progress:', sessionStore.appProgress);
};

const openBookmarksModal = () => {
  isBookmarksModalOpen.value = true;
};

const closeBookmarksModal = () => {
  isBookmarksModalOpen.value = false;
};

const handleDeleteJob = (jobId: string) => {
  jobToDelete.value = jobId;
  isDeleteAlertOpen.value = true;
};

const confirmDelete = async () => {
  if (jobToDelete.value) {
    await sessionStore.deleteJob(jobToDelete.value);
    jobToDelete.value = null;
  }
  isDeleteAlertOpen.value = false;
};

const cancelDelete = () => {
  jobToDelete.value = null;
  isDeleteAlertOpen.value = false;
};

const handleJobDetails = (jobId: string) => {
  // Find the job in the session to get its problem ID
  const job = sessionStore.jobs.find(j => j.id === jobId);
  if (job && job.problem?.id) {
    router.push(`/view-job/${job.problem.id}`);
  }
};

const handleFixHours = (jobId: string) => {
  router.push(`/confirm-job/${jobId}`);
};

const handleEdit = (jobId: string) => {
  router.push(`/editor/job/${jobId}`);
};

const handleJobCardClick = (jobId: string) => {
  // Navigate to the job content screen
  router.push(`/job/${jobId}`);
};

const closeJobDetailsModal = () => {
  isJobDetailsModalOpen.value = false;
  selectedJobForDetails.value = null;
  editingSteps.value = false;
  editStepsText.value = '';
};

// Start editing steps - populate textarea with current steps
const startEditingSteps = () => {
  if (selectedJobForDetails.value) {
    const steps = getJobSteps(selectedJobForDetails.value);
    editStepsText.value = steps.join('\n');
    editingSteps.value = true;
  }
};

// Save edited steps - split lines into array and save to job
const saveSteps = async () => {
  if (selectedJobForDetails.value) {
    const newSteps = editStepsText.value
      .split('\n')
      .map(s => s.trim())
      .filter(s => s !== '');

    // Update the job's problem tech_handbook
    const job = sessionStore.jobs.find(j => j.id === selectedJobForDetails.value.id);
    if (job && job.problem) {
      (job.problem as any).tech_handbook = newSteps;
      await sessionStore.save();
    }

    editingSteps.value = false;
  }
};

// Cancel editing steps
const cancelEditingSteps = () => {
  editingSteps.value = false;
  editStepsText.value = '';
};

// Default tech handbook steps
const DEFAULT_STEPS = [
  "Do some work",
  "Do some more work",
  "And there are different steps.",
  "Clean up the job site.",
  "Tell the customer thank you."
];

// Get tech handbook steps from job's problem or use default
const getJobSteps = (job: any): string[] => {
  if (!job || !job.problem) return DEFAULT_STEPS;

  const techHandbook = (job.problem as any)?.tech_handbook || (job.problem as any)?.techHandbook;

  if (techHandbook) {
    // If tech handbook exists, parse it into steps
    if (typeof techHandbook === 'string') {
      return techHandbook.split('\n').filter((step: string) => step.trim() !== '');
    } else if (Array.isArray(techHandbook)) {
      return techHandbook;
    }
  }

  // Return default steps
  return DEFAULT_STEPS;
};

const handleJobCopied = async (jobId: string) => {
  // Mark this job as copied
  copiedJobIds.value.add(jobId);

  // Check if all jobs have been copied
  const allJobsCopied = sessionStore.jobs.every(job => copiedJobIds.value.has(job.id));

  if (allJobsCopied && sessionStore.jobs.length > 0) {
    // All jobs copied - review invoice step complete
    console.log('[ServiceCall] All jobs copied - review invoice complete');
  }
};

const handleShowMenus = async () => {
  if (sessionStore.jobs.length > 0) {
    // Set step 4 to true (Pass to customer)
    sessionStore.appProgress.step4 = true;
    await sessionStore.save();

    router.push("/to-customer");
  }
};

const handleGoToInvoice = () => {
  router.push("/invoice");
};

const goToSearch = () => {
  router.push("/find-job");
};

// Check if all jobs have completed check-hours (have baseHours set)
// extraTime can be 0 if no adjustments were made, so we only check baseHours
const allJobsHaveHours = computed(() => {
  if (sessionStore.jobs.length === 0) return false;
  return sessionStore.jobs.every(job => job.baseHours > 0);
});

// Search-related state
const searchInputQuery = ref("");

const searchSuggestions = computed(() => {
  if (searchInputQuery.value.trim().length < 2) return [];

  const query = searchInputQuery.value.toLowerCase();
  const suggestions: Array<{ id: string; name: string; type: 'tag' | 'job' }> = [];

  problemTags.value.forEach((tag) => {
    if (tag.name?.toLowerCase().includes(query)) {
      if (!selectedTags.value.includes(tag.id)) {
        suggestions.push({
          id: tag.id,
          name: tag.name || '',
          type: 'tag'
        });
      }
    }
  });

  problems.value.forEach((problem) => {
    if (problem.name?.toLowerCase().includes(query)) {
      suggestions.push({
        id: problem.id,
        name: problem.name || '',
        type: 'job'
      });
    }
  });

  return suggestions.slice(0, 10);
});

// Event handlers
const handleNavigate = (url: string) => {
  router.push(url);
};

const handleSearchInput = (query: string) => {
  searchInputQuery.value = query;
};

// Get 5 random jobs as bookmarks (mock data)
const bookmarkedJobs = computed(() => {
  const problems = sessionStore.problems;
  if (problems.length === 0) return [];

  // Create a copy and shuffle
  const shuffled = [...problems].sort(() => 0.5 - Math.random());

  // Return first 5
  return shuffled.slice(0, 5);
});

// Select a bookmarked job
const selectBookmarkedJob = (problem: ProblemsRecord) => {
  closeBookmarksModal();
  router.push(`/view-job/${problem.id}`);
};


// Jobs to display: mock jobs if no real jobs have ever been added, otherwise real jobs (or empty)
const displayJobs = computed(() => {
  // If real jobs have been added, never show mock data again (even if jobs array is empty)
  if (sessionStore.hasHadRealJobs) {
    // Show real jobs (or empty array if all deleted)
    return sessionStore.jobs.map(job => {
      const totalHours = (job.baseHours || 0) + (job.extraTime || 0);
      return {
        id: job.id,
        title: job.title || job.problem?.name || 'Untitled Job',
        hours: totalHours > 0
          ? `${totalHours.toFixed(1)} h`
          : '--- h',
        blinkFixHours: !job.baseHours || job.baseHours === 0,
        hasConfirmedOffer: !!(job.selectedTierName && job.selectedPrice),
        jobData: job,
      };
    });
  }

  // Return empty array if no jobs
  return [];
});

onMounted(async () => {
  await sessionStore.load();

  // Set test data for demo purposes
  sessionStore.serviceCallsCount = 2;
  sessionStore.menusShownCount = 4;
  sessionStore.higherTierChosenCount = 1;
  sessionStore.serviceCallsWithMenusShown = 2;
});
</script>

<style scoped>
.dashboard-container {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}

.header-section {
  margin-bottom: 32px;
}

.title-container {
  position: relative;
  margin-bottom: 24px;
}

.title-icons-left {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  gap: 8px;
  align-items: center;
}

.title-icons-left ion-icon {
  font-size: 48px;
}

.title-icons-right {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  gap: 8px;
  align-items: center;
}

.title-icons-right ion-icon {
  font-size: 48px;
}

.star-icon-wrapper {
  position: relative;
  display: inline-block;
}

.star-icon-shadow {
  position: absolute;
  top: 2px;
  left: -2px;
  font-size: 48px;
  z-index: 0;
}

.star-icon-front {
  position: relative;
  z-index: 1;
}

.clickable-icon {
  cursor: pointer;
  transition: transform 0.2s ease, color 0.2s ease;
}

.clickable-icon:hover {
  transform: scale(1.1);
  color: var(--ion-color-primary-shade);
}

.clickable-icon:active {
  transform: scale(0.95);
}

.page-title {
  margin: 0;
  color: var(--ion-text-color);
  font-size: 48px;
  font-weight: 700;
  text-align: center;
  font-variant: small-caps;
}

.content-section {
  padding: 0 20px 20px 20px;
}

.progress-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  max-width: 1000px;
  margin: 0 auto 24px auto;
}

.progress-column {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.progress-number {
  font-size: 43px;
  font-weight: 700;
  color: var(--ion-color-primary);
  font-variant: normal;
  font-family: "Courier New", Courier, monospace;
  letter-spacing: 4px;
  text-align: center;
}

.progress-label {
  font-size: 22px;
  font-weight: 600;
  color: var(--ion-color-medium);
  font-variant: small-caps;
  text-align: center;
}

.progress-wrapper {
  position: relative;
  width: 100%;
  height: 43px;
  border-radius: 12px;
  background: var(--ion-color-medium);
  overflow: hidden;
}

.progress-gradient {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    to right,
    var(--ion-color-secondary) 0%,
    var(--ion-color-primary) 100%
  );
  border-radius: 12px;
}

.progress-cover {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  background: var(--ion-color-medium);
  border-top-right-radius: 12px;
  border-bottom-right-radius: 12px;
  transition: width 0.3s ease;
}

/* Mobile adjustments for progress bars */
@media (max-width: 767px) {
  .progress-number {
    font-size: 34px;
  }

  .progress-label {
    font-size: 14px;
  }

  .progress-wrapper {
    height: 34px;
  }
}

.clocks-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  max-width: 1000px;
  margin: 0 auto;
}

.divider {
  width: 100%;
  height: 1px;
  background-color: #d3d3d3;
  margin: 40px 0;
  max-width: 1000px;
  margin-left: auto;
  margin-right: auto;
}

.divider-compact {
  width: 100%;
  height: 1px;
  background-color: #d3d3d3;
  margin: 16px 0;
  max-width: 1000px;
  margin-left: auto;
  margin-right: auto;
}

/* Dashboard Search Section */
.jobs-section-heading {
  max-width: 1000px;
  margin: 0 auto 16px auto;
  color: var(--ion-text-color);
  font-size: 24px;
  font-weight: 600;
  text-align: center;
  font-variant: small-caps;
}

.jobs-section {
  max-width: 1000px;
  margin: 0 auto 16px auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.add-jobs-button-container {
  max-width: 1000px;
  margin: 0 auto 16px auto;
}

.add-jobs-button-container ion-button {
  --padding-top: 16px;
  --padding-bottom: 16px;
  font-weight: 600;
  font-size: 18px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.show-menus-button-container {
  max-width: 1000px;
  margin: 16px auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.show-menus-button-container ion-button {
  --padding-top: 20px;
  --padding-bottom: 20px;
  font-weight: 700;
  font-size: 20px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.info-content {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ion-text-color);
}

.info-content h2 {
  margin-top: 24px;
  margin-bottom: 12px;
  font-size: 20px;
  font-weight: 700;
  color: var(--ion-color-primary);
}

.info-content h2:first-child {
  margin-top: 0;
}

.info-content p {
  margin-bottom: 16px;
  color: var(--ion-text-color);
}

.info-content p:last-child {
  margin-bottom: 0;
}

.debug-button {
  padding: 12px 24px;
  background-color: var(--ion-color-primary);
  color: #ffffff;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.2s ease;
  margin-bottom: 16px;
}

.debug-button:hover {
  background-color: var(--ion-color-primary-shade);
}

.debug-button:active {
  transform: scale(0.98);
}

/* Mobile adjustments */
@media (max-width: 767px) {
  .dashboard-container {
    padding: 16px;
  }

  .page-title {
    font-size: 36px;
  }

  .progress-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }

  .clocks-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
}

/* Job Details Modal Styles */
.job-details-content {
  max-width: 600px;
  margin: 0 auto;
}

.detail-section {
  margin-bottom: 24px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}

.detail-section:last-child {
  border-bottom: none;
}

.detail-label {
  margin: 0 0 8px 0;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--ion-color-medium);
  display: flex;
  align-items: center;
  gap: 8px;
}

.edit-icon {
  font-size: 14px;
  color: var(--ion-color-primary);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.edit-icon:hover {
  opacity: 1;
}

.detail-value {
  margin: 0;
  font-size: 18px;
  line-height: 1.5;
  color: var(--ion-text-color);
}

.detail-price {
  font-size: 32px;
  font-weight: 700;
  color: var(--ion-color-success);
}

.detail-notes {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.detail-note {
  margin: 0;
  padding: 12px;
  background-color: rgba(255, 255, 255, 0.1);
  border: 1px solid var(--ion-color-medium);
  border-radius: 8px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--ion-text-color);
}

.steps-list {
  margin: 0;
  padding-left: 24px;
  color: var(--ion-text-color);
}

.step-item {
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 8px;
  color: var(--ion-text-color);
}

.steps-edit-container {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.steps-textarea {
  --background: rgba(255, 255, 255, 0.1);
  --color: var(--ion-text-color);
  --padding-start: 12px;
  --padding-end: 12px;
  --padding-top: 12px;
  --padding-bottom: 12px;
  border: 1px solid var(--ion-color-medium);
  border-radius: 8px;
  font-size: 16px;
  line-height: 1.6;
}

.steps-edit-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* Bookmarks Modal Styles */
.bookmarks-content {
  font-size: 16px;
  line-height: 1.6;
  color: var(--ion-text-color);
}

.bookmarks-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.bookmark-item {
  display: flex;
  gap: 16px;
  padding: 16px;
  background-color: var(--ion-color-dark);
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 0.2s ease;
  border-left: 3px solid var(--ion-color-warning);
}

.bookmark-item:hover {
  background-color: rgba(118, 221, 84, 0.1);
}

.bookmark-star {
  font-size: 24px;
  flex-shrink: 0;
  margin-top: 4px;
}

.bookmark-details {
  flex: 1;
}

.bookmark-title {
  margin: 0 0 8px 0;
  font-size: 20px;
  font-weight: 600;
  color: var(--ion-text-color);
}

.bookmark-description {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ion-color-medium);
  max-width: 600px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
</style>