docs: record redesign review artifacts

This commit is contained in:
2026-09-28 20:29:02 +03:30
parent 6f7a30bc0d
commit 37189a4ac8
11 changed files with 380 additions and 4 deletions

View File

@@ -0,0 +1,38 @@
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="./assets/favicon.ico">
<title>مسیرهای انجمن — طرح A</title>
<style>
@font-face{font-family:Vazir;src:url('./assets/Vazirmatn-variable.woff2') format('woff2');font-display:swap}
:root{--paper:#f2f6f2;--ink:#103d3b;--green:#226d64;--mint:#c9e2d9;--white:#fff;--signal:#ad3c20;--line:#9cb9af}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--ink);font:400 16px/1.8 Vazir,Tahoma,sans-serif}
a{color:inherit}a:focus-visible{outline:3px solid var(--signal);outline-offset:4px}::selection{background:var(--mint);color:var(--ink)}
.wrap{width:min(100% - 48px,1200px);margin:auto}.mast{border-bottom:1px solid var(--line);background:var(--white)}
.mast .wrap{min-height:94px;display:flex;justify-content:space-between;align-items:center;gap:24px}
.brand{display:flex;align-items:center;gap:14px;text-decoration:none;min-width:0}.brand img{width:56px;height:56px;object-fit:contain}.brand strong{display:block;font-size:18px;line-height:1.45}.brand small{display:block;color:var(--green);font-size:12px}
nav{display:flex;align-items:center;gap:8px;flex-wrap:wrap}nav a{text-decoration:none;font-weight:650;padding:8px 13px;border-radius:4px}nav a:hover{background:var(--mint)}nav .active,nav .active:hover{color:var(--white);background:var(--ink)}
.hero{position:relative;overflow:hidden;border-bottom:1px solid var(--line)}.hero:before{content:"";position:absolute;right:max(24px,calc((100% - 1200px)/2));top:0;bottom:0;width:2px;background:var(--signal)}
.hero .wrap{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(300px,.75fr);gap:7%;align-items:center;min-height:610px;padding-block:80px}
.hero-copy{padding-right:36px;position:relative}.hero-copy:before{content:"";position:absolute;right:-9px;top:31px;width:20px;height:20px;border-radius:50%;background:var(--signal);border:4px solid var(--paper)}
h1{font-size:clamp(36px,5.2vw,76px);font-weight:850;line-height:1.25;letter-spacing:-.02em;margin:0 0 24px;max-width:12ch}h1 em{font-style:normal;color:var(--green)}
.lead{font-size:clamp(17px,1.6vw,21px);max-width:45ch;margin:0 0 30px}.actions{display:flex;gap:10px;flex-wrap:wrap}.button{display:inline-flex;align-items:center;justify-content:center;min-height:50px;padding:9px 24px;border:2px solid var(--ink);text-decoration:none;font-weight:700;border-radius:4px}.button.primary{background:var(--ink);color:var(--white)}.button:hover{background:var(--signal);border-color:var(--signal);color:var(--white)}
.identity-panel{background:var(--ink);color:var(--white);padding:34px 32px;min-height:400px;display:flex;flex-direction:column;justify-content:space-between;position:relative}.identity-panel:after{content:"";position:absolute;left:22px;top:22px;width:20px;height:20px;border:2px solid var(--mint);border-radius:50%}.identity-panel span{color:var(--mint);font-size:13px}.identity-panel p{font-weight:700;font-size:clamp(21px,2.1vw,28px);line-height:1.65;margin:16px 0}.identity-panel .faculty{border-top:1px solid #4c7770;padding-top:18px;font-size:15px}
.paths{padding-block:72px}.paths h2,.about h2{font-size:clamp(28px,3vw,42px);line-height:1.4;margin:0 0 26px}.paths .route{display:grid;grid-template-columns:80px 1fr auto;align-items:center;gap:22px;padding:22px 12px;border-top:1px solid var(--line);text-decoration:none}.paths .route:last-child{border-bottom:1px solid var(--line)}.route .mark{display:grid;place-items:center;width:48px;height:48px;background:var(--mint)}.route .mark:before{content:"";width:12px;height:12px;border:3px solid var(--green);border-radius:50%}.route strong{font-size:22px}.route small{display:block;color:#345c55;font-size:14px}.route .arrow{width:14px;height:14px;border-left:2px solid var(--signal);border-bottom:2px solid var(--signal);transform:rotate(45deg)}.route:hover{background:#e3eee8}
.about{background:var(--white);border-top:1px solid var(--line)}.about .wrap{display:grid;grid-template-columns:1fr 1fr;gap:8%;padding-block:92px}.about .intro{position:sticky;top:40px;align-self:start}.about .intro p{font-size:20px;max-width:34ch}.about .intro img{width:74px;height:74px;object-fit:contain;margin-top:24px}.about .details{border-right:2px solid var(--signal);padding-right:34px}.detail{padding:4px 0 24px;border-bottom:1px solid var(--line);margin-bottom:25px}.detail h3{font-size:20px;margin:0 0 7px}.detail p{margin:0;color:#345c55}.contact a{display:block;overflow-wrap:anywhere;text-decoration-thickness:1px;text-underline-offset:4px}
footer{background:var(--ink);color:var(--white);padding:36px 0}footer .wrap{display:flex;justify-content:space-between;gap:20px;align-items:center}footer p{margin:0}footer a{color:var(--mint)}
@media(max-width:760px){.wrap{width:min(100% - 32px,1200px)}.mast .wrap{min-height:76px}.brand img{width:42px;height:42px}.brand strong{font-size:13px}.brand small{font-size:11px}nav{display:none}.hero:before{right:16px}.hero .wrap{grid-template-columns:1fr;gap:34px;min-height:0;padding-block:54px}.hero-copy{padding-right:25px}h1{max-width:14ch}.identity-panel{min-height:260px}.paths{padding-block:50px}.paths .route{grid-template-columns:40px 1fr 18px;gap:12px;padding:17px 2px}.route .mark{width:38px;height:38px;font-size:19px}.route strong{font-size:17px}.route small{font-size:12px}.about .wrap{grid-template-columns:1fr;padding-block:55px;gap:26px}.about .intro{position:static}.about .details{padding-right:20px}footer .wrap{display:block}footer p+ p{margin-top:10px}}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
</style>
</head>
<body>
<header class="mast"><div class="wrap"><a class="brand" href="#top"><img src="./assets/favicon.ico" alt="نشان فعلی انجمن"><span><strong>انجمن علمی مهندسی کامپیوتر</strong><small>دانشکده‌ی فنی و مهندسی شرق گیلان</small></span></a><nav aria-label="ناوبری طرح"><a class="active" href="#top">خانه</a><a href="#about">درباره انجمن</a><a href="#paths">مسیرها</a></nav></div></header>
<main id="top">
<section class="hero" aria-labelledby="hero-title"><div class="wrap"><div class="hero-copy"><h1 id="hero-title">جایی برای <em>یادگیری و همکاری</em> دانشجویان کامپیوتر</h1><p class="lead">انجمن علمی مهندسی کامپیوتر دانشگاه گیلان در دانشکده‌ی فنی و مهندسی شرق گیلان فعالیت می‌کند. اینجا می‌توانید با فعالیت‌های انجمن آشنا شوید و راه مشارکت خود را پیدا کنید.</p><div class="actions"><a class="button primary" href="#about">آشنایی با انجمن</a><a class="button" href="#paths">دیدن مسیرها</a></div></div><div class="identity-panel"><span>هویت انجمن</span><p>انجمن علمی مهندسی کامپیوتر دانشگاه گیلان</p><div class="faculty">دانشکده‌ی فنی و مهندسی شرق گیلان</div></div></div></section>
<section id="paths" class="paths wrap" aria-labelledby="paths-title"><h2 id="paths-title">از کجا شروع کنم؟</h2><a class="route" href="#about"><span class="mark" aria-hidden="true"></span><span><strong>آشنایی با انجمن</strong><small>مأموریت و راه‌های ارتباطی</small></span><span class="arrow" aria-hidden="true"></span></a><a class="route" href="https://east-guilan-ce.ir/events"><span class="mark" aria-hidden="true"></span><span><strong>رویدادها</strong><small>برنامه‌ها و فرصت‌های یادگیری</small></span><span class="arrow" aria-hidden="true"></span></a><a class="route" href="https://east-guilan-ce.ir/blog"><span class="mark" aria-hidden="true"></span><span><strong>بلاگ</strong><small>نوشته‌ها و محتوای آموزشی</small></span><span class="arrow" aria-hidden="true"></span></a></section>
<section id="about" class="about" aria-labelledby="about-title"><div class="wrap"><div class="intro"><h2 id="about-title">درباره انجمن</h2><p>یادگیری پیوسته، اشتراک دانش و فرصت برابر برای مشارکت، محور فعالیت انجمن است.</p><img src="./assets/favicon.ico" alt="نشان فعلی انجمن"></div><div class="details"><div class="detail"><h3>مأموریت</h3><p>توانمندسازی دانشجویان با مهارت‌های موردنیاز صنعت، ایجاد شبکه‌های حرفه‌ای و فراهم‌کردن مسیرهای یادگیری پروژه‌محور.</p></div><div class="detail"><h3>فعالیت‌ها</h3><p>برگزاری رویدادها، کارگاه‌ها و نشست‌های تخصصی و انتشار محتوای آموزشی.</p></div><div class="detail contact"><h3>ارتباط با انجمن</h3><a href="mailto:eastguilanceassociation@gmail.com" dir="ltr">eastguilanceassociation@gmail.com</a><a href="https://t.me/guilancea" dir="ltr">t.me/guilancea</a><a href="https://instagram.com/guilance.ir" dir="ltr">instagram.com/guilance.ir</a></div></div></div></section>
</main><footer><div class="wrap"><p>انجمن علمی مهندسی کامپیوتر دانشگاه گیلان</p><p><a href="#top">بازگشت به آغاز</a></p></div></footer>
</body>
</html>

View File

@@ -0,0 +1,32 @@
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="./assets/favicon.ico">
<title>دفتر انجمن — طرح B</title>
<style>
@font-face{font-family:Vazir;src:url('./assets/Vazirmatn-variable.woff2') format('woff2');font-display:swap}
:root{--navy:#14243f;--blue:#42649a;--paper:#fff9ea;--cream:#f1e8d0;--gold:#f0be54;--white:#fff;--line:#b8b2a4}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--navy);font:400 16px/1.85 Vazir,Tahoma,sans-serif}a{color:inherit}a:focus-visible{outline:3px solid var(--gold);outline-offset:4px}::selection{background:var(--gold);color:var(--navy)}
.wrap{width:min(100% - 56px,1240px);margin:auto}.header{background:var(--navy);color:var(--white);border-bottom:1px solid #66758e}.header .wrap{display:flex;justify-content:space-between;align-items:center;gap:30px;min-height:92px}.brand{display:flex;align-items:center;gap:13px;text-decoration:none}.brand img{height:52px;width:52px;object-fit:contain}.brand span{font-size:14px;font-weight:750;max-width:250px;line-height:1.5}.header nav{display:flex;gap:26px}.header nav a{color:#ecf0f7;text-decoration:none;text-underline-offset:9px;font-size:14px}.header nav a:hover,.header nav a:focus-visible{text-decoration:underline}
.cover{background:var(--navy);color:var(--paper);overflow:hidden}.cover .wrap{padding-block:70px 90px;display:grid;grid-template-columns:1.3fr .7fr;gap:8%;min-height:620px;align-items:end}.headline{position:relative}h1{font-weight:900;font-size:clamp(48px,7.3vw,112px);letter-spacing:-.035em;line-height:1.16;margin:0;max-width:12ch}h1 span{display:block;color:var(--gold)}.headline:after{content:"";display:block;width:70%;height:1px;background:#8190a3;margin-top:36px}.cover-note{align-self:center;border-top:2px solid var(--gold);background:var(--paper);color:var(--navy);padding:28px 30px 32px}.cover-note p{font-size:19px;line-height:1.8;margin:0 0 24px}.cover-note a{display:inline-block;font-weight:750;text-underline-offset:5px}.cover-note strong{display:block;font-size:13px;color:#49628b;margin-bottom:18px}
.ribbon{background:var(--gold);padding:16px 0}.ribbon .wrap{display:flex;align-items:center;gap:32px;flex-wrap:wrap;font-weight:750}.ribbon span{margin-left:auto}.ribbon a{text-decoration:none;border-bottom:1px solid currentColor}.ribbon a:hover{text-decoration:underline}
.chapter{padding-block:88px 100px}.chapter-head{display:grid;grid-template-columns:1fr 1fr;gap:7%;padding-bottom:40px;border-bottom:2px solid var(--navy)}.chapter h2{font-size:clamp(36px,4.2vw,62px);line-height:1.25;margin:0}.chapter-head p{font-size:21px;line-height:1.8;margin:0}.ledger{display:grid;grid-template-columns:repeat(3,1fr);border-bottom:1px solid var(--line)}.ledger div{padding:30px 22px 32px;border-left:1px solid var(--line)}.ledger div:first-child{padding-right:0}.ledger div:last-child{border:0}.ledger h3{font-size:23px;margin:0 0 10px}.ledger p{margin:0;color:#384c69}
.about{background:var(--cream);padding-block:92px}.about .wrap{display:grid;grid-template-columns:.8fr 1.2fr;gap:9%}.about aside{border-top:2px solid var(--navy);padding-top:22px}.about aside img{width:86px;height:86px;object-fit:contain}.about h2{font-size:clamp(38px,5vw,70px);line-height:1.2;margin:15px 0 26px}.about .name{font-weight:760;font-size:20px;line-height:1.7}.about article{padding-top:34px}.about article p{font-size:20px;line-height:2;max-width:44ch;margin:0 0 32px}.about dl{border-top:1px solid var(--navy);margin:0}.about dl div{display:grid;grid-template-columns:130px 1fr;border-bottom:1px solid var(--line);gap:20px;padding:18px 0}.about dt{font-weight:800}.about dd{margin:0;overflow-wrap:anywhere}.about dd a{text-underline-offset:4px}
.end{background:var(--navy);color:var(--paper);padding:58px 0}.end .wrap{display:flex;justify-content:space-between;align-items:end;gap:30px}.end p{margin:0;font-weight:700;font-size:22px;max-width:30ch}.end a{color:var(--gold);white-space:nowrap}
@media(max-width:820px){.wrap{width:min(100% - 32px,1240px)}.header .wrap{min-height:75px}.brand img{width:42px;height:42px}.brand span{font-size:12px}.header nav{display:none}.cover .wrap{grid-template-columns:1fr;gap:52px;min-height:0;padding-block:52px 68px}.cover-note{max-width:550px}.ribbon .wrap{gap:12px}.ribbon span{width:100%}.chapter{padding-block:58px}.chapter-head{grid-template-columns:1fr;gap:17px}.chapter-head p{font-size:18px}.ledger{grid-template-columns:1fr}.ledger div,.ledger div:first-child{padding:23px 0;border-left:0;border-bottom:1px solid var(--line)}.ledger div:last-child{border:0}.about{padding-block:60px}.about .wrap{grid-template-columns:1fr;gap:20px}.about article{padding-top:0}.about article p{font-size:18px}.end .wrap{display:block}.end a{display:inline-block;margin-top:22px}}
@media(max-width:400px){h1{font-size:45px}.about dl div{grid-template-columns:1fr;gap:3px}.cover-note{padding:22px}}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
</style>
</head>
<body>
<header class="header"><div class="wrap"><a class="brand" href="#top"><img src="./assets/favicon.ico" alt="نشان فعلی انجمن"><span>انجمن علمی مهندسی کامپیوتر<br>دانشکده‌ی فنی و مهندسی شرق گیلان</span></a><nav aria-label="ناوبری طرح"><a href="#top">آغاز</a><a href="#chapter">فعالیت‌ها</a><a href="#about">درباره انجمن</a></nav></div></header>
<main id="top">
<section class="cover" aria-labelledby="cover-title"><div class="wrap"><div class="headline"><h1 id="cover-title">انجمن برای <span>یاد گرفتن با هم</span></h1></div><div class="cover-note"><strong>انجمن علمی مهندسی کامپیوتر دانشگاه گیلان</strong><p>در دانشکده‌ی فنی و مهندسی شرق گیلان، فعالیت انجمن فرصتی برای یادگیری، اشتراک دانش و مشارکت دانشجویان است.</p><a href="#about">انجمن را بشناسید</a></div></div></section>
<div class="ribbon"><div class="wrap"><span>برای آشنایی با فعالیت‌ها</span><a href="#chapter">برنامه‌های انجمن</a><a href="#about">مأموریت و ارتباط</a></div></div>
<section id="chapter" class="chapter wrap" aria-labelledby="chapter-title"><div class="chapter-head"><h2 id="chapter-title">فعالیت‌های انجمن</h2><p>از رویدادهای تخصصی تا نوشته‌های آموزشی، برنامه‌ها برای یادگیری و ارتباط میان دانشجویان شکل می‌گیرند.</p></div><div class="ledger"><div><h3>رویدادها</h3><p>کارگاه‌ها، سمینارها و نشست‌های علمی.</p></div><div><h3>محتوا</h3><p>مقالات آموزشی و یادداشت‌های تخصصی.</p></div><div><h3>همکاری</h3><p>مشارکت دانشجویان و اشتراک تجربه.</p></div></div></section>
<section id="about" class="about" aria-labelledby="about-title"><div class="wrap"><aside><img src="./assets/favicon.ico" alt="نشان فعلی انجمن"><h2 id="about-title">درباره انجمن</h2><p class="name">انجمن علمی مهندسی کامپیوتر دانشگاه گیلان (دانشکده‌ی فنی و مهندسی شرق گیلان)</p></aside><article><p>مأموریت انجمن، توانمندسازی دانشجویان با مهارت‌های موردنیاز صنعت، ایجاد شبکه‌های حرفه‌ای و فراهم‌کردن مسیرهای یادگیری پروژه‌محور است.</p><dl><div><dt>ایمیل</dt><dd><a dir="ltr" href="mailto:eastguilanceassociation@gmail.com">eastguilanceassociation@gmail.com</a></dd></div><div><dt>تلگرام</dt><dd><a dir="ltr" href="https://t.me/guilancea">t.me/guilancea</a></dd></div><div><dt>اینستاگرام</dt><dd><a dir="ltr" href="https://instagram.com/guilance.ir">instagram.com/guilance.ir</a></dd></div></dl></article></div></section>
</main><footer class="end"><div class="wrap"><p>یادگیری پیوسته و اشتراک دانش، از ارزش‌های انجمن است.</p><a href="#top">بازگشت به آغاز</a></div></footer>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="./assets/favicon.ico">
<title>پالایش طرح فعلی — خانه و درباره انجمن</title>
<style>
@font-face{font-family:Vazirmatn;src:url('./assets/Vazirmatn-variable.woff2') format('woff2');font-display:swap}
:root{--background:#fff;--ink:#0f172a;--muted:#475569;--line:#e2e8f0;--wash:#f4f8fc;--accent:#1e4f85;--focus:#2563eb}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--background);color:var(--ink);font:400 16px/1.8 Vazirmatn,Tahoma,sans-serif}a{color:inherit}a:focus-visible,summary:focus-visible{outline:3px solid var(--focus);outline-offset:4px}::selection{background:#dbeafe;color:var(--ink)}
.wrap{width:min(100% - 48px,1120px);margin-inline:auto}.site-head{position:sticky;top:0;z-index:10;background:#ffffffed;border-bottom:1px solid var(--line);backdrop-filter:blur(16px)}
.site-head .wrap{min-height:74px;display:flex;align-items:center;justify-content:space-between;gap:24px}.brand{display:flex;align-items:center;gap:10px;min-width:0;text-decoration:none}.brand img{width:39px;height:39px;object-fit:contain}.brand strong{display:block;font-size:15px;line-height:1.5}.brand small{display:block;color:var(--muted);font-size:11px;line-height:1.5}
.top-nav{display:flex;align-items:center;gap:5px}.top-nav a{padding:9px 15px;border-radius:999px;text-decoration:none;font-size:14px;font-weight:600;color:var(--muted)}.top-nav a:hover,.top-nav a.active{background:var(--wash);color:var(--ink)}
.hero{background:linear-gradient(180deg,#f3f8ff 0%,#fff 100%);text-align:center;padding:clamp(78px,9vw,126px) 0 100px}.hero h1{max-width:19ch;margin:0 auto 23px;font-size:clamp(36px,4.4vw,61px);line-height:1.33;font-weight:850;letter-spacing:-.025em}.hero .lead{max-width:61ch;margin:0 auto;color:var(--muted);font-size:clamp(17px,1.6vw,20px);line-height:1.95}.hero .actions{margin-top:34px}
.actions{display:flex;flex-wrap:wrap;justify-content:center;gap:12px}.button{display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:10px 24px;border-radius:10px;border:1px solid var(--ink);text-decoration:none;font-weight:700;line-height:1.5;transition:background .18s,color .18s,border-color .18s}.button.primary{background:var(--ink);color:white}.button.secondary{background:white;color:var(--ink);border-color:#cbd5e1}.button:hover{background:var(--accent);border-color:var(--accent);color:white}
.section{padding:86px 0}.section-head{display:flex;justify-content:space-between;align-items:end;gap:24px;margin-bottom:26px}.section h2{font-size:clamp(26px,3vw,35px);line-height:1.45;margin:0}.section-head p{max-width:47ch;margin:0;color:var(--muted)}
.offer-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.offer{padding:27px;border:1px solid var(--line);border-radius:16px;background:white}.offer h3{font-size:20px;margin:0 0 10px}.offer p{color:var(--muted);font-size:15px;line-height:1.9;margin:0 0 20px}.text-link{color:var(--accent);text-decoration:underline;text-underline-offset:5px;font-weight:700}.text-link:hover{color:var(--ink)}
.home-join{background:var(--wash);border-block:1px solid var(--line);text-align:center}.home-join p{color:var(--muted);max-width:50ch;margin:12px auto 24px}.about-hero{padding:86px 0 70px;background:linear-gradient(180deg,#f4f8fc,white)}.about-hero h1{font-size:clamp(35px,4.3vw,55px);line-height:1.35;margin:0 0 16px}.about-hero p{max-width:68ch;margin:0;color:var(--muted);font-size:18px;line-height:1.95}.about-full-name{margin-top:30px;padding-top:18px;border-top:1px solid var(--line);font-size:14px;color:var(--muted)}
.about-layout{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr);gap:clamp(32px,6vw,90px)}.about-layout h2{margin:0 0 16px}.about-layout p{max-width:66ch;color:var(--muted);margin:0 0 22px}.activity-list{list-style:none;padding:0;margin:0}.activity-list li{padding:15px 0;border-bottom:1px solid var(--line)}.activity-list strong{display:block;font-size:17px}.activity-list span{display:block;color:var(--muted);font-size:14px}.contact-block{border:1px solid var(--line);border-radius:16px;padding:25px;background:var(--wash);align-self:start}.contact-block h3{font-size:20px;margin:0 0 10px}.contact-block p{font-size:14px;margin:0 0 18px}.contact-block a{display:block;overflow-wrap:anywhere;margin:8px 0;color:var(--accent);text-underline-offset:4px}.contact-block a[dir=ltr]{text-align:right}
.faq{padding-top:0}.faq details{border-bottom:1px solid var(--line);padding:17px 0}.faq summary{cursor:pointer;font-weight:650}.faq details p{color:var(--muted);margin:12px 0 0;max-width:65ch}.site-foot{border-top:1px solid var(--line);padding:35px 0;color:var(--muted);font-size:14px}.site-foot .wrap{display:flex;justify-content:space-between;gap:16px}.mobile-nav{display:none}
@media(max-width:700px){.wrap{width:min(100% - 32px,1120px)}.site-head .wrap{min-height:68px}.brand img{width:33px;height:33px}.brand small{display:none}.top-nav{display:none}.hero{padding:82px 0 72px}.hero h1{font-size:clamp(35px,9vw,48px)}.hero .lead{font-size:17px}.actions{flex-direction:column}.button{width:100%}.section{padding:62px 0}.section-head{display:block}.section-head p{margin-top:8px}.offer-grid,.about-layout{grid-template-columns:1fr}.offer{padding:23px}.about-hero{padding:70px 0 52px}.about-hero p{font-size:17px}.contact-block{margin-top:10px}.faq{padding-top:0}.site-foot{padding-bottom:130px}.site-foot .wrap{display:block}.site-foot p{margin:0 0 8px}.mobile-nav{position:fixed;z-index:20;bottom:calc(12px + env(safe-area-inset-bottom));left:16px;right:16px;display:flex;justify-content:space-around;gap:4px;padding:8px;border-radius:20px;background:#fffffff0;border:1px solid var(--line);box-shadow:0 12px 32px #0f172a20;backdrop-filter:blur(14px)}.mobile-nav a{flex:1;text-align:center;text-decoration:none;padding:7px 2px;border-radius:14px;font-size:12px;font-weight:700}.mobile-nav a.active{background:var(--ink);color:white}}
#about,#home,#contact{scroll-margin-top:80px}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.button{transition:none}}
</style>
</head>
<body>
<header class="site-head"><div class="wrap"><a class="brand" href="#home"><img src="./assets/favicon.ico" alt=""><span><strong>انجمن علمی مهندسی کامپیوتر</strong><small>دانشکده‌ی فنی و مهندسی شرق گیلان</small></span></a><nav class="top-nav" aria-label="بخش‌های نمونه"><a class="active" href="#home">خانه</a><a href="#about">درباره انجمن</a><a href="https://east-guilan-ce.ir/events">رویدادها</a><a href="https://east-guilan-ce.ir/blog">بلاگ</a></nav></div></header>
<main>
<section class="hero" id="home" aria-labelledby="home-title"><div class="wrap"><h1 id="home-title">انجمن علمی مهندسی کامپیوتر</h1><p class="lead">انجمن دانشجویان کامپیوتر در دانشکده‌ی فنی و مهندسی شرق گیلان است؛ جایی برای یادگیری، برگزاری رویدادهای تخصصی و همکاری با یکدیگر.</p><div class="actions"><a class="button primary" href="#about">آشنایی با انجمن</a><a class="button secondary" href="https://east-guilan-ce.ir/events">مشاهده رویدادها</a></div></div></section>
<section class="section" aria-labelledby="offer-title"><div class="wrap"><div class="section-head"><h2 id="offer-title">در انجمن چه می‌گذرد؟</h2><p>سه راه ساده برای شناختن فعالیت‌های انجمن و پیدا کردن نقطه‌ی شروع.</p></div><div class="offer-grid"><article class="offer"><h3>رویدادهای تخصصی</h3><p>کارگاه‌ها، سمینارها و نشست‌هایی برای یادگیری مهارت‌های تازه.</p><a class="text-link" href="https://east-guilan-ce.ir/events">دیدن رویدادها</a></article><article class="offer"><h3>محتوای آموزشی</h3><p>مطالبی در حوزه‌های گوناگون علوم و مهندسی کامپیوتر.</p><a class="text-link" href="https://east-guilan-ce.ir/blog">خواندن بلاگ</a></article><article class="offer"><h3>همکاری دانشجویی</h3><p>فرصتی برای مشارکت در فعالیت‌ها و ارتباط با دانشجویان علاقه‌مند.</p><a class="text-link" href="#contact">راه‌های ارتباطی</a></article></div></div></section>
<section class="section home-join" aria-labelledby="join-title"><div class="wrap"><h2 id="join-title">دوست دارید همراه انجمن باشید؟</h2><p>برای آشنایی با فعالیت‌ها از همین‌جا شروع کنید یا برنامه‌های پیش رو را ببینید.</p><div class="actions"><a class="button primary" href="#about">درباره انجمن</a><a class="button secondary" href="https://east-guilan-ce.ir/events">رویدادها</a></div></div></section>
<section class="about-hero" id="about" aria-labelledby="about-title"><div class="wrap"><h1 id="about-title">درباره انجمن</h1><p>انجمن علمی مهندسی کامپیوتر در دانشکده‌ی فنی و مهندسی شرق گیلان با برگزاری رویدادها و تولید محتوای آموزشی، فضایی برای یادگیری و همکاری دانشجویان فراهم می‌کند.</p><div class="about-full-name">انجمن علمی مهندسی کامپیوتر دانشگاه گیلان (دانشکده‌ی فنی و مهندسی شرق گیلان)</div></div></section>
<section class="section" aria-labelledby="activity-title"><div class="wrap about-layout"><div><h2 id="activity-title">فعالیت‌های انجمن</h2><p>محور فعالیت‌ها، یادگیری، اشتراک دانش و ارتباط میان دانشجویان و متخصصان است.</p><ul class="activity-list"><li><strong>برگزاری رویدادها</strong><span>کارگاه‌ها، سمینارها و نشست‌های تخصصی</span></li><li><strong>انتشار محتوا</strong><span>مقاله‌ها و مطالب آموزشی در حوزه‌ی کامپیوتر</span></li><li><strong>مشارکت دانشجویی</strong><span>همکاری در برنامه‌ریزی و اجرای فعالیت‌های انجمن</span></li></ul></div><aside class="contact-block" id="contact"><h3>ارتباط با انجمن</h3><p>برای پیشنهاد برنامه، همکاری یا پرسش، از یکی از راه‌های زیر پیام بدهید.</p><a dir="ltr" href="mailto:eastguilanceassociation@gmail.com">eastguilanceassociation@gmail.com</a><a dir="ltr" href="https://t.me/guilancea">t.me/guilancea</a><a dir="ltr" href="https://instagram.com/guilance.ir">instagram.com/guilance.ir</a></aside></div></section>
<section class="section faq" aria-labelledby="faq-title"><div class="wrap"><h2 id="faq-title">پرسش‌های رایج</h2><details><summary>چطور در رویدادها شرکت کنم؟</summary><p>رویداد موردنظرتان را انتخاب کنید و شرایط و مراحل ثبت‌نام همان رویداد را بخوانید.</p></details><details><summary>چطور با انجمن همکاری کنم؟</summary><p>برای پیشنهاد همکاری از راه‌های ارتباطی بالا پیام بدهید.</p></details></div></section>
</main>
<footer class="site-foot"><div class="wrap"><p>انجمن علمی مهندسی کامپیوتر دانشگاه گیلان</p><a href="#home">بازگشت به آغاز</a></div></footer>
<nav class="mobile-nav" aria-label="ناوبری نمونه در موبایل"><a class="active" href="#home">خانه</a><a href="#about">درباره</a><a href="https://east-guilan-ce.ir/events">رویدادها</a><a href="https://east-guilan-ce.ir/blog">بلاگ</a></nav>
<script>
function syncPreviewNav(){
const current = location.hash === '#about' ? '#about' : '#home';
for(const link of document.querySelectorAll('.top-nav a[href^="#"],.mobile-nav a[href^="#"]')){
const active = link.getAttribute('href') === current;
link.classList.toggle('active',active);
if(active) link.setAttribute('aria-current','page'); else link.removeAttribute('aria-current');
}
}
addEventListener('hashchange',syncPreviewNav);
syncPreviewNav();
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@@ -0,0 +1,74 @@
"""Create disposable Bundle 0 browser fixtures in the isolated Django test database.
Run only with --settings=config.settings.test and BUNDLE0_TEST_PASSWORD set.
Never point TEST_DB_NAME at a shared or production database.
"""
import os
from datetime import timedelta
from django.conf import settings
from django.utils import timezone
from apps.blog.models import Post
from apps.events.models import Event
from apps.users.models import User
assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test"
assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3"
password = os.environ["BUNDLE0_TEST_PASSWORD"]
now = timezone.now()
def fixture_user(username, mobile, *, staff=False):
user, _ = User.objects.get_or_create(
username=username,
defaults={"mobile": mobile, "email": f"{username}@example.invalid"},
)
user.mobile = mobile
user.email = f"{username}@example.invalid"
user.is_email_verified = True
user.is_mobile_verified = True
user.is_staff = staff
user.is_superuser = staff
user.set_password(password)
user.save()
return user
student = fixture_user("bundle0_student", "09198000111")
staff = fixture_user("bundle0_staff", "09198000112", staff=True)
for slug, title, price, capacity in (
("bundle0-free", "Bundle 0 Free Event", 0, 20),
("bundle0-full", "Bundle 0 Full Event", 0, 0),
("bundle0-paid", "Bundle 0 Paid Event", 50000, 20),
):
Event.objects.update_or_create(
slug=slug,
defaults={
"title": title,
"description": "Local baseline event for browser review.",
"start_time": now + timedelta(days=7),
"end_time": now + timedelta(days=7, hours=2),
"registration_start_date": now - timedelta(days=1),
"registration_end_date": now + timedelta(days=6),
"status": Event.StatusChoices.PUBLISHED,
"price": price,
"capacity": capacity,
},
)
Post.objects.update_or_create(
slug="bundle0-article",
defaults={
"title": "Bundle 0 Article",
"content": "# Local baseline article\n\nA sample article for the browser review.",
"author": staff,
"status": Post.StatusChoices.PUBLISHED,
"published_at": now,
},
)
print("Created isolated student, staff, free/full/paid events, and article fixtures.")

View File

@@ -0,0 +1,95 @@
"""Replay signup, OTP login, and password reset in the isolated test database.
SMS and the short-lived OTP store are mocked. Run after the fixture seed with
--settings=config.settings.test and BUNDLE0_TEST_PASSWORD set.
"""
import json
import os
from unittest.mock import patch
from django.conf import settings
from django.test import Client, override_settings
from apps.users.models import User
assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test"
assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3"
password = os.environ["BUNDLE0_TEST_PASSWORD"]
mobile = "09198000113"
client = Client()
class TemporaryOtpStore:
def __init__(self):
self.values = {}
def setex(self, key, _seconds, value):
self.values[key] = str(value).encode("utf-8")
def get(self, key):
return self.values.get(key)
def delete(self, key):
self.values.pop(key, None)
store = TemporaryOtpStore()
def post(path, payload):
return client.post(path, data=json.dumps(payload), content_type="application/json")
with (
override_settings(ALLOWED_HOSTS=["testserver", "localhost"]),
patch("apps.users.services.auth._redis", return_value=store),
patch("apps.users.services.auth.send_critical_sms.delay"),
):
invalid = post("/api/auth/otp/send", {"mobile": "123", "mode": "register"})
assert invalid.status_code == 400
sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "register"})
assert sent.status_code == 200, sent.content
code = store.get(f"auth_otp:register:{mobile}").decode("utf-8")
wrong = "00000" if code != "00000" else "99999"
rejected = post("/api/auth/otp/verify-register", {"mobile": mobile, "code": wrong})
assert rejected.status_code == 400
verified = post("/api/auth/otp/verify-register", {"mobile": mobile, "code": code})
assert verified.status_code == 200, verified.content
registered = post(
"/api/auth/register",
{"username": "bundle0_signup", "mobile": mobile, "code": code, "password": password},
)
assert registered.status_code == 201, registered.content
assert User.objects.get(username="bundle0_signup").is_mobile_verified
duplicate = post("/api/auth/otp/send", {"mobile": mobile, "mode": "register"})
assert duplicate.status_code == 400
sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "login"})
assert sent.status_code == 200, sent.content
code = store.get(f"auth_otp:login:{mobile}").decode("utf-8")
login = post("/api/auth/login/otp", {"mobile": mobile, "code": code})
assert login.status_code == 200 and login.json()["access_token"]
sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "reset_password"})
assert sent.status_code == 200, sent.content
code = store.get(f"auth_otp:reset_password:{mobile}").decode("utf-8")
new_password = password + "New"
rejected = post(
"/api/auth/reset-password",
{"mobile": mobile, "code": wrong if wrong != code else "88888", "new_password": new_password},
)
assert rejected.status_code == 400
changed = post(
"/api/auth/reset-password",
{"mobile": mobile, "code": code, "new_password": new_password},
)
assert changed.status_code == 200, changed.content
old_login = post("/api/auth/login", {"identifier": mobile, "password": password})
new_login = post("/api/auth/login", {"identifier": mobile, "password": new_password})
assert old_login.status_code == 401 and new_login.status_code == 200
print("Auth replay: invalid OTP request and code rejected; signup, OTP login, and password reset succeeded; old password rejected.")

View File

@@ -0,0 +1,85 @@
"""Replay paid-event outcomes in the isolated Bundle 0 Django test database.
External gateway calls, notifications, and SMS are mocked. This script is baseline
evidence, not a production payment test or a browser gateway substitute.
"""
import json
import os
from unittest.mock import Mock, patch
from django.conf import settings
from django.test import Client, override_settings
from apps.events.models import Event, Registration
from apps.payments.models import Payment
from apps.users.models import User
from core.authentication import create_jwt_token
assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test"
assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3"
student = User.objects.get(username="bundle0_student")
event = Event.objects.get(slug="bundle0-paid")
client = Client()
headers = {"HTTP_AUTHORIZATION": f"Bearer {create_jwt_token(student)}"}
body = json.dumps({"event_id": event.pk, "description": "Local mocked payment replay"})
def create_payment():
return client.post(
"/api/payments/create",
data=body,
content_type="application/json",
**headers,
)
with (
override_settings(ALLOWED_HOSTS=["testserver", "localhost"]),
patch("apps.payments.api.views.notify_user"),
patch("apps.payments.api.views.send_critical_sms.delay"),
patch("apps.payments.api.views.requests.post") as gateway,
):
gateway.side_effect = RuntimeError("simulated gateway outage")
failed_request = create_payment()
assert failed_request.status_code == 502, failed_request.content
assert not Payment.objects.filter(user=student, event=event).exists()
gateway.side_effect = None
gateway.return_value = Mock()
gateway.return_value.json.return_value = {
"data": {"code": 100, "authority": "BUNDLE0-CANCEL"}
}
pending = create_payment()
assert pending.status_code == 200, pending.content
assert pending.json()["start_pay_url"]
canceled = client.get(
"/api/payments/callback",
{"Authority": "BUNDLE0-CANCEL", "Status": "NOK"},
)
assert canceled.status_code == 302
assert "status=failed" in canceled.url
assert Payment.objects.get(authority="BUNDLE0-CANCEL").status == Payment.OrderStatusChoices.CANCELED
gateway.return_value.json.return_value = {
"data": {"code": 100, "authority": "BUNDLE0-SUCCESS"}
}
pending = create_payment()
assert pending.status_code == 200, pending.content
gateway.return_value.json.return_value = {
"data": {"code": 100, "ref_id": "BUNDLE0-REF"}
}
paid = client.get(
"/api/payments/callback",
{"Authority": "BUNDLE0-SUCCESS", "Status": "OK"},
)
assert paid.status_code == 302
assert "status=success" in paid.url
assert Payment.objects.get(authority="BUNDLE0-SUCCESS").status == Payment.OrderStatusChoices.PAID
assert Registration.objects.get(user=student, event=event).status == Registration.StatusChoices.CONFIRMED
duplicate = create_payment()
assert duplicate.status_code == 400
print("Paid replay: gateway outage 502; cancellation redirect; mocked success and confirmed registration; duplicate blocked.")

View File

@@ -33,10 +33,10 @@ Checkpoint C was approved by the owner on 2026-09-27 after reviewing [the schema
## 2. Home/About and shared public shell (9–12 person-days)
- [ ] **T15 — Establish approved visual tokens and shell (M; deps: T05, T14).** Files: F global styles, fonts, navigation/footer components. Acceptance: approved typography/colors/layout, responsive header/footer, and CMS identity/navigation/contact fields match comps; RTL/keyboard/reduced-motion work. Verify: screenshot comparison at 320/390/768/1440px and keyboard pass.
- [ ] **T16 — Build CMS-driven Home editorial sections and domain previews (M; deps: T15).** Files: F Home view, section components, tests. Acceptance: hero/featured-section/CTA text comes from the current CMS record in the fixed owner-approved order; event and blog cards read existing domain APIs with truthful status, useful links, and sound empty/long layouts. Verify: an admin Save changes the public text on the next request; event/blog source changes update cards; visual review at all widths.
- [ ] **Checkpoint G (deps: T15–T16).** Home concept fidelity and real content signed off before About.
- [ ] **T17 — Build CMS-driven About sections (M; deps: T15).** Files: F About view, section components, tests. Acceptance: official identity, mission, committee/association facts, and contacts come from reviewed current CMS records; unsupported claims are absent. Verify: compare rendered content to approved starting values and confirm an admin Save changes the page.
- [x] **T15 — Establish approved visual tokens and shell (M; deps: T05, T14).** Files: F global styles, fonts, navigation/footer components. Acceptance: approved typography/colors/layout, responsive header/footer, and CMS identity/navigation/contact fields match comps; RTL/keyboard/reduced-motion work. Implementation reviewed and approved by the owner at Checkpoint G on 2026-09-28.
- [x] **T16 — Build CMS-driven Home editorial sections and domain previews (M; deps: T15).** Files: F Home view, section components, tests. Acceptance: hero/featured-section/CTA text comes from the current CMS record in the fixed owner-approved order; event and blog cards read existing domain APIs with truthful status, useful links, and sound empty/long layouts. Home was reviewed and approved by the owner at Checkpoint G on 2026-09-28; frontend lint and production build passed.
- [x] **Checkpoint G (deps: T15–T16).** Owner approved the Home design and content at target widths on 2026-09-28. T17 can proceed.
- [ ] **T17 — Build CMS-driven About sections (M; deps: T15).** Files: F About view, section components, tests. Acceptance: owner-approved About identity, purpose, member-year archive, selected event highlights, separate memories gallery, and general history timeline come from current CMS records; unsupported claims are absent. Implementation is ready in the frontend working tree; final content and admin-save review remain open. Verify: compare rendered content to approved starting values and confirm an admin Save changes the page.
- [ ] **T18 — Make page SEO/structured data content-driven (M; deps: T16–T17).** Files: F route metadata/SEO utilities/tests. Acceptance: canonical, title, description, OG image, and structured-data identity are consistent with current CMS records and server-rendered HTML; no stale hardcoded names. Verify: inspect HTML/JSON-LD and link previews on staging after an admin Save.
- [ ] **T19 — Finish Home/About interaction and accessibility pass (M; deps: T16–T18).** Files: F affected page/styles/tests. Acceptance: motion supports comprehension but does not block content, reduced-motion works, contrast/focus/read order and Persian line wraps pass. Verify: real browser desktop/mobile captures, keyboard, screen-reader spot check, and Impeccable critique against approved comp.
- [ ] **Checkpoint H (deps: T17–T19).** Owner approves Home/About on staging; authorized staff can Save a visible change without developer help.