게시판 글보기 템플릿 (view.html)

게시판 글보기 템플릿 (view.html)

기본 글보기 구조

표준 글보기 레이아웃

<include target="_header.html" />

<!-- 글 정보 및 도구 -->
<div class="document-header">
    <div class="document-info">
        <!-- 카테고리 -->
        <span class="category" cond="$oDocument->get('category_srl')">
            <a href="{getUrl('category', $oDocument->get('category_srl'))}">
                {$category_list[$oDocument->get('category_srl')]->title}
            </a>
        </span>

        <!-- 제목 -->
        <h1 class="document-title">
            {$oDocument->getTitle()}

            <!-- 상태 아이콘 -->
            <span class="status-icons">
                <span class="secret-icon" cond="$oDocument->get('status') == 'SECRET'">
                    <i class="icon-lock" title="비밀글"></i>
                </span>
                <span class="attach-icon" cond="$oDocument->getAttachedFileCount() > 0">
                    <i class="icon-attachment" title="첨부파일"></i>
                </span>
            </span>
        </h1>

        <!-- 글 메타 정보 -->
        <div class="document-meta">
            <div class="meta-left">
                <!-- 작성자 -->
                <span class="author">
                    <!--@if($oDocument->getProfileImage())-->
                        <img src="{$oDocument->getProfileImage()}" alt="" class="author-avatar" />
                    <!--@endif-->
                    <span class="author-name">{$oDocument->getNickName()}</span>

                    <!-- 회원 레벨 표시 -->
                    <span class="author-level" cond="$oDocument->getMemberSrl() > 0">
                        Lv.{$oDocument->get('member_level')}
                    </span>
                </span>

                <!-- 작성일 -->
                <span class="reg-date">
                    <i class="icon-calendar"></i>
                    {zdate($oDocument->getRegdate(), 'Y년 m월 d일 H:i')}
                </span>

                <!-- 수정일 (수정된 경우) -->
                <span class="update-date" cond="$oDocument->getUpdate() != $oDocument->getRegdate()">
                    <i class="icon-edit"></i>
                    수정: {zdate($oDocument->getUpdate(), 'Y.m.d H:i')}
                </span>
            </div>

            <div class="meta-right">
                <!-- 조회수 -->
                <span class="read-count">
                    <i class="icon-eye"></i>
                    조회 {number_format($oDocument->getReadedCount())}
                </span>

                <!-- 추천수 -->
                <span class="vote-count" cond="$module_info->use_vote == 'Y'">
                    <i class="icon-thumbs-up"></i>
                    추천 {$oDocument->getVotedCount()}
                </span>

                <!-- 댓글수 -->
                <span class="comment-count" cond="$oDocument->getCommentCount() > 0">
                    <i class="icon-comment"></i>
                    댓글 {$oDocument->getCommentCount()}
                </span>
            </div>
        </div>
    </div>

    <!-- 글 관리 도구 -->
    <div class="document-tools">
        <!-- 작성자나 관리자용 도구 -->
        <div class="author-tools" cond="$oDocument->isGranted() || $grant->manager">
            <a href="{getUrl('act', 'dispBoardModify', 'document_srl', $oDocument->document_srl)}" 
               class="btn btn-sm btn-outline-primary">
                <i class="icon-edit"></i> 수정
            </a>

            <a href="{getUrl('act', 'dispBoardDelete', 'document_srl', $oDocument->document_srl)}" 
               class="btn btn-sm btn-outline-danger">
                <i class="icon-trash"></i> 삭제
            </a>
        </div>

        <!-- 일반 사용자용 도구 -->
        <div class="user-tools">
            <!-- 목록 버튼 -->
            <a href="{getUrl('document_srl', '')}" class="btn btn-sm btn-outline-secondary">
                <i class="icon-list"></i> 목록
            </a>

            <!-- 답글 버튼 -->
            <a href="{getUrl('act', 'dispBoardWrite', 'document_srl', $oDocument->document_srl)}" 
               class="btn btn-sm btn-outline-info" cond="$grant->write">
                <i class="icon-reply"></i> 답글
            </a>

            <!-- 스크랩 버튼 -->
            <button type="button" class="btn btn-sm btn-outline-warning" 
                    onclick="doScrap({$oDocument->document_srl})" cond="$logged_info">
                <i class="icon-bookmark"></i> 스크랩
            </button>

            <!-- 신고 버튼 -->
            <button type="button" class="btn btn-sm btn-outline-danger" 
                    onclick="doBlame({$oDocument->document_srl})" cond="$logged_info">
                <i class="icon-flag"></i> 신고
            </button>
        </div>
    </div>
</div>

<!-- 첨부파일 목록 -->
<div class="attached-files" cond="$oDocument->getAttachedFileCount() > 0">
    <h3 class="files-title">
        <i class="icon-attachment"></i>
        첨부파일 ({$oDocument->getAttachedFileCount()}개)
    </h3>

    <ul class="file-list">
        <li loop="$oDocument->getAttachedFiles() => $file" class="file-item">
            <!-- 이미지 파일 -->
            <!--@if($file->isImage())-->
                <div class="image-file">
                    <a href="{$file->uploaded_filename}" target="_blank" class="image-link">
                        <img src="{$file->thumbnail_filename}" alt="{$file->source_filename}" />
                    </a>
                    <div class="image-info">
                        <span class="filename">{$file->source_filename}</span>
                        <span class="filesize">({$file->file_size_text})</span>
                    </div>
                </div>
            <!--@else-->
                <!-- 일반 파일 -->
                <div class="general-file">
                    <a href="{getUrl('act', 'procFileDownload', 'file_srl', $file->file_srl)}" 
                       class="file-download">
                        <i class="icon-download"></i>
                        <span class="filename">{$file->source_filename}</span>
                        <span class="filesize">({$file->file_size_text})</span>
                        <span class="download-count">다운로드 {number_format($file->download_count)}</span>
                    </a>
                </div>
            <!--@endif-->
        </li>
    </ul>
</div>

<!-- 글 내용 -->
<div class="document-content">
    <div class="content-body">
        {$oDocument->getContent(false)}
    </div>

    <!-- 추가 첨부 이미지 (본문에 없는 것들) -->
    {@
        $attached_images = array();
        foreach($oDocument->getAttachedFiles() as $file) {
            if($file->isImage() && !strpos($oDocument->getContent(), $file->uploaded_filename)) {
                $attached_images[] = $file;
            }
        }
    }

    <div class="additional-images" cond="count($attached_images) > 0">
        <h4>추가 이미지</h4>
        <div class="image-gallery">
            <div class="image-item" loop="$attached_images => $image">
                <a href="{$image->uploaded_filename}" data-lightbox="gallery">
                    <img src="{$image->thumbnail_filename}" alt="{$image->source_filename}" />
                </a>
            </div>
        </div>
    </div>
</div>

<!-- 추천/비추천 영역 -->
<div class="vote-section" cond="$module_info->use_vote == 'Y' && $logged_info">
    <div class="vote-buttons">
        <button type="button" class="btn-vote btn-vote-up" 
                onclick="doVote({$oDocument->document_srl}, 'up')">
            <i class="icon-thumbs-up"></i>
            <span>추천</span>
            <span class="vote-count">{$oDocument->getVotedCount()}</span>
        </button>

        <button type="button" class="btn-vote btn-vote-down" 
                onclick="doVote({$oDocument->document_srl}, 'down')">
            <i class="icon-thumbs-down"></i>
            <span>비추천</span>
            <span class="vote-count">{$oDocument->get('blamed_count')}</span>
        </button>
    </div>

    <!-- 투표 결과 표시 -->
    <div class="vote-result" cond="$oDocument->getVotedCount() > 0 || $oDocument->get('blamed_count') > 0">
        <div class="vote-bar">
            {@
                $total_votes = $oDocument->getVotedCount() + $oDocument->get('blamed_count');
                $up_percent = $total_votes > 0 ? ($oDocument->getVotedCount() / $total_votes) * 100 : 0;
                $down_percent = $total_votes > 0 ? ($oDocument->get('blamed_count') / $total_votes) * 100 : 0;
            }
            <div class="vote-bar-up" style="width: {$up_percent}%"></div>
            <div class="vote-bar-down" style="width: {$down_percent}%"></div>
        </div>
        <div class="vote-stats">
            <span class="up-votes">추천 {$oDocument->getVotedCount()}</span>
            <span class="down-votes">비추천 {$oDocument->get('blamed_count')}</span>
        </div>
    </div>
</div>

<!-- 태그 -->
<div class="document-tags" cond="$oDocument->getTags()">
    <h4 class="tags-title">태그</h4>
    <ul class="tag-list">
        <li loop="$oDocument->getTags() => $tag" class="tag-item">
            <a href="{getUrl('search_target', 'tag', 'search_keyword', $tag)}" class="tag-link">
                #{$tag}
            </a>
        </li>
    </ul>
</div>

<!-- 이전/다음 글 -->
<div class="document-navigation">
    <!-- 이전 글 -->
    <div class="nav-item nav-prev" cond="$prev_document">
        <div class="nav-label">
            <i class="icon-chevron-up"></i>
            이전 글
        </div>
        <div class="nav-content">
            <a href="{getUrl('document_srl', $prev_document->document_srl)}" class="nav-title">
                {cut_str($prev_document->getTitle(), 50)}
            </a>
            <div class="nav-meta">
                <span class="nav-author">{$prev_document->getNickName()}</span>
                <span class="nav-date">{zdate($prev_document->getRegdate(), 'Y.m.d')}</span>
            </div>
        </div>
    </div>

    <!-- 다음 글 -->
    <div class="nav-item nav-next" cond="$next_document">
        <div class="nav-label">
            <i class="icon-chevron-down"></i>
            다음 글
        </div>
        <div class="nav-content">
            <a href="{getUrl('document_srl', $next_document->document_srl)}" class="nav-title">
                {cut_str($next_document->getTitle(), 50)}
            </a>
            <div class="nav-meta">
                <span class="nav-author">{$next_document->getNickName()}</span>
                <span class="nav-date">{zdate($next_document->getRegdate(), 'Y.m.d')}</span>
            </div>
        </div>
    </div>
</div>

<!-- 댓글 영역 -->
<div class="comment-section">
    <!-- 댓글 작성 폼 -->
    <include target="comment_form.html" />

    <!-- 댓글 목록 -->
    <include target="comment.html" />
</div>

<!-- 하단 도구 -->
<div class="document-footer">
    <div class="footer-buttons">
        <a href="{getUrl('document_srl', '')}" class="btn btn-secondary">목록</a>
        <a href="{getUrl('act', 'dispBoardWrite')}" class="btn btn-primary" cond="$grant->write">글쓰기</a>
    </div>
</div>

</div> <!-- board-container -->

이미지 갤러리형 글보기

이미지 중심 레이아웃

<div class="gallery-view">
    <!-- 메인 이미지 영역 -->
    <div class="main-image-area" cond="$oDocument->getAttachedFiles()">
        {@
            $image_files = array();
            foreach($oDocument->getAttachedFiles() as $file) {
                if($file->isImage()) {
                    $image_files[] = $file;
                }
            }
        }

        <div class="image-viewer" cond="count($image_files) > 0">
            <!-- 메인 이미지 -->
            <div class="main-image">
                <img src="{$image_files[0]->uploaded_filename}" 
                     alt="{$oDocument->getTitle()}" 
                     id="mainImage" />

                <!-- 이미지 네비게이션 -->
                <div class="image-nav" cond="count($image_files) > 1">
                    <button type="button" class="nav-btn prev-btn" onclick="prevImage()">
                        <i class="icon-chevron-left"></i>
                    </button>
                    <button type="button" class="nav-btn next-btn" onclick="nextImage()">
                        <i class="icon-chevron-right"></i>
                    </button>
                </div>

                <!-- 이미지 카운터 -->
                <div class="image-counter" cond="count($image_files) > 1">
                    <span id="currentImageIndex">1</span> / {count($image_files)}
                </div>
            </div>

            <!-- 썸네일 목록 -->
            <div class="thumbnail-list" cond="count($image_files) > 1">
                <div class="thumbnail-item" loop="$image_files => $index, $file">
                    <img src="{$file->thumbnail_filename}" 
                         alt="" 
                         onclick="showImage({$index})"
                         class="thumbnail active"|cond="$index == 0" />
                </div>
            </div>
        </div>
    </div>

    <!-- 글 정보 -->
    <div class="gallery-info">
        <!-- 제목과 메타 정보는 위와 동일 -->
        <h1 class="gallery-title">{$oDocument->getTitle()}</h1>

        <div class="gallery-meta">
            <span class="author">{$oDocument->getNickName()}</span>
            <span class="date">{zdate($oDocument->getRegdate(), 'Y.m.d H:i')}</span>
            <span class="views">조회 {$oDocument->getReadedCount()}</span>
        </div>

        <!-- 글 내용 -->
        <div class="gallery-content" cond="trim(strip_tags($oDocument->getContent()))">
            {$oDocument->getContent(false)}
        </div>
    </div>
</div>

<script>
// 이미지 갤러리 스크립트
var imageFiles = [
    <block loop="$image_files => $index, $file">
    '{$file->uploaded_filename}'<block cond="$index < count($image_files) - 1">,</block>
    </block>
];

var currentIndex = 0;

function showImage(index) {
    currentIndex = index;
    document.getElementById('mainImage').src = imageFiles[index];
    document.getElementById('currentImageIndex').textContent = index + 1;

    // 썸네일 활성화
    var thumbnails = document.querySelectorAll('.thumbnail');
    thumbnails.forEach(function(thumb, i) {
        thumb.classList.toggle('active', i === index);
    });
}

function prevImage() {
    var prevIndex = currentIndex > 0 ? currentIndex - 1 : imageFiles.length - 1;
    showImage(prevIndex);
}

function nextImage() {
    var nextIndex = currentIndex < imageFiles.length - 1 ? currentIndex + 1 : 0;
    showImage(nextIndex);
}

// 키보드 네비게이션
document.addEventListener('keydown', function(e) {
    if (e.key === 'ArrowLeft') prevImage();
    if (e.key === 'ArrowRight') nextImage();
});
</script>

모바일 최적화 글보기

반응형 레이아웃

{@ $is_mobile = Mobile::isFromMobilePhone() }

<div class="document-view {$is_mobile ? 'mobile' : 'desktop'}">
    <!-- 모바일용 헤더 -->
    <!--@if($is_mobile)-->
    <div class="mobile-header">
        <button type="button" class="back-btn" onclick="history.back()">
            <i class="icon-arrow-left"></i>
        </button>
        <span class="mobile-title">{cut_str($oDocument->getTitle(), 20)}</span>
        <button type="button" class="menu-btn" onclick="toggleMobileMenu()">
            <i class="icon-menu"></i>
        </button>
    </div>

    <!-- 모바일 메뉴 -->
    <div class="mobile-menu" id="mobileMenu">
        <div class="menu-item" cond="$oDocument->isGranted()">
            <a href="{getUrl('act', 'dispBoardModify', 'document_srl', $oDocument->document_srl)}">
                <i class="icon-edit"></i> 수정
            </a>
        </div>
        <div class="menu-item" cond="$oDocument->isGranted()">
            <a href="{getUrl('act', 'dispBoardDelete', 'document_srl', $oDocument->document_srl)}">
                <i class="icon-trash"></i> 삭제
            </a>
        </div>
        <div class="menu-item">
            <a href="{getUrl('document_srl', '')}">
                <i class="icon-list"></i> 목록
            </a>
        </div>
        <div class="menu-item" cond="$grant->write">
            <a href="{getUrl('act', 'dispBoardWrite', 'document_srl', $oDocument->document_srl)}">
                <i class="icon-reply"></i> 답글
            </a>
        </div>
    </div>
    <!--@endif-->

    <!-- 글 내용 (모바일/데스크탑 공통) -->
    <article class="document-article">
        <!-- 제목과 메타 정보 -->
        <header class="article-header">
            <h1 class="article-title">{$oDocument->getTitle()}</h1>

            <div class="article-meta">
                <div class="author-info">
                    <span class="author-name">{$oDocument->getNickName()}</span>
                    <span class="publish-date">{zdate($oDocument->getRegdate(), 'Y.m.d H:i')}</span>
                </div>

                <div class="article-stats">
                    <span class="views">
                        <i class="icon-eye"></i> {$oDocument->getReadedCount()}
                    </span>
                    <span class="comments" cond="$oDocument->getCommentCount() > 0">
                        <i class="icon-comment"></i> {$oDocument->getCommentCount()}
                    </span>
                </div>
            </div>
        </header>

        <!-- 글 본문 -->
        <div class="article-body">
            {$oDocument->getContent(false)}
        </div>

        <!-- 첨부파일 (모바일 최적화) -->
        <div class="article-attachments" cond="$oDocument->getAttachedFileCount() > 0">
            <h3>첨부파일</h3>
            <div class="attachment-list">
                <div class="attachment-item" loop="$oDocument->getAttachedFiles() => $file">
                    <!--@if($file->isImage())-->
                        <div class="image-attachment">
                            <img src="{$file->thumbnail_filename}" 
                                 alt="{$file->source_filename}"
                                 onclick="showFullImage('{$file->uploaded_filename}')" />
                        </div>
                    <!--@else-->
                        <div class="file-attachment">
                            <a href="{getUrl('act', 'procFileDownload', 'file_srl', $file->file_srl)}">
                                <i class="icon-download"></i>
                                <span class="filename">{cut_str($file->source_filename, 30)}</span>
                                <span class="filesize">({$file->file_size_text})</span>
                            </a>
                        </div>
                    <!--@endif-->
                </div>
            </div>
        </div>
    </article>
</div>

<!-- 전체화면 이미지 뷰어 (모바일용) -->
<div class="fullscreen-viewer" id="fullscreenViewer" onclick="closeFullImage()">
    <img src="" alt="" id="fullscreenImage" />
    <button type="button" class="close-btn" onclick="closeFullImage()">×</button>
</div>

<script>
function toggleMobileMenu() {
    var menu = document.getElementById('mobileMenu');
    menu.classList.toggle('active');
}

function showFullImage(src) {
    var viewer = document.getElementById('fullscreenViewer');
    var image = document.getElementById('fullscreenImage');
    image.src = src;
    viewer.style.display = 'flex';
}

function closeFullImage() {
    var viewer = document.getElementById('fullscreenViewer');
    viewer.style.display = 'none';
}

// 메뉴 외부 클릭 시 닫기
document.addEventListener('click', function(e) {
    var menu = document.getElementById('mobileMenu');
    if (!e.target.closest('.mobile-menu') && !e.target.closest('.menu-btn')) {
        menu.classList.remove('active');
    }
});
</script>

CSS 스타일링

글보기 기본 스타일

/* 글보기 기본 레이아웃 */
.document-header {
    border-bottom: 2px solid #eee;
    padding-bottom: 20px;
    margin-bottom: 30px;
}

.document-title {
    font-size: 24px;
    font-weight: 600;
    margin: 0 0 15px;
    line-height: 1.4;
    color: #333;
}

.document-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 15px;
    font-size: 14px;
    color: #666;
}

.author-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    margin-right: 8px;
    vertical-align: middle;
}

.author-level {
    background: #007bff;
    color: white;
    padding: 2px 6px;
    border-radius: 10px;
    font-size: 11px;
    margin-left: 5px;
}

.meta-right span {
    margin-left: 15px;
}

.meta-right i {
    margin-right: 3px;
    color: #999;
}

/* 글 도구 */
.document-tools {
    display: flex;
    justify-content: space-between;
    margin-top: 15px;
    gap: 10px;
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 8px 15px;
    border: 1px solid #ddd;
    border-radius: 4px;
    text-decoration: none;
    font-size: 13px;
    transition: all 0.2s;
}

.btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.btn-primary {
    background: #007bff;
    color: white;
    border-color: #007bff;
}

.btn-outline-primary {
    color: #007bff;
    border-color: #007bff;
}

.btn-outline-primary:hover {
    background: #007bff;
    color: white;
}

/* 첨부파일 */
.attached-files {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 20px;
    margin: 20px 0;
}

.files-title {
    font-size: 16px;
    margin: 0 0 15px;
    color: #495057;
}

.file-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.file-item {
    margin-bottom: 15px;
}

.image-file img {
    max-width: 200px;
    max-height: 150px;
    border-radius: 4px;
    margin-bottom: 5px;
}

.file-download {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px;
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    text-decoration: none;
    color: #333;
    transition: all 0.2s;
}

.file-download:hover {
    background: #f8f9fa;
    border-color: #007bff;
}

/* 글 내용 */
.document-content {
    margin: 30px 0;
    line-height: 1.8;
    font-size: 15px;
}

.content-body {
    min-height: 200px;
}

.content-body img {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin: 10px 0;
}

/* 추천 영역 */
.vote-section {
    text-align: center;
    padding: 30px 0;
    border-top: 1px solid #eee;
    border-bottom: 1px solid #eee;
    margin: 30px 0;
}

.vote-buttons {
    display: flex;
    justify-content: center;
    gap: 20px;
    margin-bottom: 20px;
}

.btn-vote {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    padding: 15px 20px;
    background: white;
    border: 2px solid #ddd;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    min-width: 80px;
}

.btn-vote-up:hover {
    border-color: #28a745;
    color: #28a745;
}

.btn-vote-down:hover {
    border-color: #dc3545;
    color: #dc3545;
}

.vote-bar {
    display: flex;
    height: 20px;
    background: #f8f9fa;
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 10px;
}

.vote-bar-up {
    background: #28a745;
}

.vote-bar-down {
    background: #dc3545;
}

/* 이전/다음 글 */
.document-navigation {
    border: 1px solid #eee;
    border-radius: 8px;
    overflow: hidden;
    margin: 30px 0;
}

.nav-item {
    display: flex;
    align-items: center;
    padding: 15px 20px;
    border-bottom: 1px solid #eee;
}

.nav-item:last-child {
    border-bottom: none;
}

.nav-label {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 80px;
    font-size: 13px;
    color: #666;
}

.nav-content {
    flex: 1;
    margin-left: 20px;
}

.nav-title {
    display: block;
    color: #333;
    text-decoration: none;
    font-weight: 500;
    margin-bottom: 5px;
}

.nav-title:hover {
    color: #007bff;
}

.nav-meta {
    font-size: 12px;
    color: #999;
}

.nav-meta span {
    margin-right: 10px;
}

/* 모바일 스타일 */
@media (max-width: 768px) {
    .document-title {
        font-size: 20px;
    }

    .document-meta {
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }

    .meta-right {
        display: flex;
        gap: 15px;
    }

    .document-tools {
        flex-direction: column;
    }

    .author-tools,
    .user-tools {
        display: flex;
        gap: 5px;
        flex-wrap: wrap;
    }

    .btn {
        font-size: 12px;
        padding: 6px 12px;
    }

    .vote-buttons {
        gap: 10px;
    }

    .btn-vote {
        min-width: 60px;
        padding: 10px 15px;
    }

    .nav-item {
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }

    .nav-content {
        margin-left: 0;
    }
}

/* 갤러리 뷰 스타일 */
.gallery-view {
    max-width: 800px;
    margin: 0 auto;
}

.image-viewer {
    margin-bottom: 30px;
}

.main-image {
    position: relative;
    text-align: center;
    margin-bottom: 15px;
}

.main-image img {
    max-width: 100%;
    max-height: 600px;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.image-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 100%;
    display: flex;
    justify-content: space-between;
    padding: 0 20px;
    pointer-events: none;
}

.nav-btn {
    background: rgba(0,0,0,0.5);
    color: white;
    border: none;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    cursor: pointer;
    pointer-events: auto;
    transition: all 0.3s;
}

.nav-btn:hover {
    background: rgba(0,0,0,0.7);
}

.image-counter {
    position: absolute;
    top: 10px;
    right: 10px;
    background: rgba(0,0,0,0.7);
    color: white;
    padding: 5px 10px;
    border-radius: 15px;
    font-size: 12px;
}

.thumbnail-list {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding: 10px 0;
}

.thumbnail-item {
    flex-shrink: 0;
}

.thumbnail {
    width: 80px;
    height: 60px;
    object-fit: cover;
    border-radius: 4px;
    cursor: pointer;
    opacity: 0.6;
    transition: all 0.3s;
}

.thumbnail.active,
.thumbnail:hover {
    opacity: 1;
    border: 2px solid #007bff;
}