generated from hjess/PythonTemplateProject
Compare commits
1 Commits
0416201742
...
https_shit
| Author | SHA1 | Date | |
|---|---|---|---|
| fa359f095d |
@@ -1,61 +0,0 @@
|
||||
job "lifefaq-blue" {
|
||||
region = "global"
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
update {
|
||||
stagger = "60s"
|
||||
max_parallel = 1
|
||||
progress_deadline = "6m"
|
||||
}
|
||||
|
||||
group "lifefaq-blue-group" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "port-app" {
|
||||
to = 9210 # Internal application port
|
||||
}
|
||||
}
|
||||
|
||||
# Register the service with Consul
|
||||
service {
|
||||
provider = "consul"
|
||||
name = "lifefaq-blue"
|
||||
port = "port-app"
|
||||
|
||||
# Traefik-specific tags for routing
|
||||
tags = [
|
||||
"PORT=${NOMAD_PORT_port-app}"
|
||||
]
|
||||
|
||||
# Define a health check using TCP
|
||||
check {
|
||||
name = "tcp_check"
|
||||
type = "tcp"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
task "lifefaq-blue-task" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "registry.i80.dk/gitea/lifefaq:latest"
|
||||
ports = ["port-app"]
|
||||
}
|
||||
|
||||
env {
|
||||
APP_ENV = "production"
|
||||
PORT = "${NOMAD_PORT_port-app}"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 250
|
||||
memory = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,5 +3,4 @@
|
||||
.ídea
|
||||
.idea/*
|
||||
.gitea/**/build*/
|
||||
data/**/index.html
|
||||
|
||||
|
||||
@@ -17,4 +17,4 @@ COPY . .
|
||||
EXPOSE 9210
|
||||
|
||||
# Command to run the FastAPI application
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "9210", "--workers", "1"]
|
||||
CMD ["uvicorn", "app:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "9210", "--workers", "1"]
|
||||
Binary file not shown.
BIN
__pycache__/markdown_render.cpython-312.pyc
Normal file
BIN
__pycache__/markdown_render.cpython-312.pyc
Normal file
Binary file not shown.
147
app.py
147
app.py
@@ -1,5 +1,144 @@
|
||||
import uvicorn
|
||||
from app.main import app
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
import json
|
||||
import os
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
from contextlib import asynccontextmanager
|
||||
from markdown_render import render_markdown_with_jinja
|
||||
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("app.main:app", host="0.0.0.0", port=9210, reload=False)
|
||||
|
||||
|
||||
|
||||
|
||||
# Context manager for app lifespan
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
print("App startup: Processing Markdown files...")
|
||||
process_markdown_files("./data", "./data") # Process all Markdown files
|
||||
print("Markdown processing complete!")
|
||||
yield # Allow the app to start
|
||||
print("App shutdown: Cleanup complete.")
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
app.mount("/data", StaticFiles(directory="data"), name="data")
|
||||
|
||||
|
||||
# Mount static files
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
# Templates directory
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
# Load JSON data
|
||||
with open("mock_data.json") as file:
|
||||
data = json.load(file)
|
||||
|
||||
@app.get("/test", response_class=HTMLResponse)
|
||||
async def home_test():
|
||||
# Load the Markdown content from a file
|
||||
with open("templates/example.md", "r") as f:
|
||||
markdown_content = f.read()
|
||||
|
||||
# Render Markdown first, then inject Jinja2 components
|
||||
rendered_html = render_markdown_with_jinja(markdown_content)
|
||||
|
||||
# Wrap in a base HTML layout
|
||||
html_template = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Markdown + Jinja2</title>
|
||||
<style>
|
||||
.img-left-overlay img {{ width: 300px; }}
|
||||
.box {{ border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; }}
|
||||
.note {{ background-color: #e7f3fe; border-left: 4px solid #2196F3; padding: 10px; }}
|
||||
.warning {{ background-color: #fff3cd; border-left: 4px solid #ffeb3b; padding: 10px; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
{rendered_html}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_template)
|
||||
|
||||
def process_markdown_files(input_dir: str, output_dir: str):
|
||||
"""
|
||||
Recursively process all Markdown files in the input directory,
|
||||
render them to HTML, and save them in the output directory.
|
||||
"""
|
||||
for root, _, files in os.walk(input_dir):
|
||||
for file in files:
|
||||
if file.endswith(".md"):
|
||||
input_file_path = os.path.join(root, file)
|
||||
|
||||
# Determine output file path (convert .md to .html)
|
||||
relative_path = os.path.relpath(input_file_path, input_dir)
|
||||
output_file_path = os.path.join(output_dir, os.path.splitext(relative_path)[0] + ".html")
|
||||
|
||||
# Ensure the output directory exists
|
||||
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
|
||||
|
||||
# Read Markdown content
|
||||
with open(input_file_path, "r", encoding="utf-8") as md_file:
|
||||
markdown_content = md_file.read()
|
||||
|
||||
# Render Markdown with Jinja2
|
||||
print(f"Processing: {input_file_path} -> {output_file_path}")
|
||||
rendered_html = render_markdown_with_jinja(markdown_content)
|
||||
|
||||
# Write the rendered HTML to the output file
|
||||
with open(output_file_path, "w", encoding="utf-8") as html_file:
|
||||
html_file.write(rendered_html)
|
||||
|
||||
|
||||
# Index route
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def get_index(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
"index.html",
|
||||
{"request": request, "data": data, "page_title": "Forside", "author": "Henrik"}
|
||||
)
|
||||
|
||||
@app.get("/sitemap", response_class=HTMLResponse)
|
||||
async def sitemap():
|
||||
"""Simple home page listing available HTML files."""
|
||||
links = []
|
||||
for root, _, files in os.walk("./data"):
|
||||
for file in files:
|
||||
if file.endswith(".html"):
|
||||
relative_path = os.path.relpath(os.path.join(root, file), "./data")
|
||||
link = f"<a href='/data/{relative_path}'>{relative_path}</a>"
|
||||
links.append(link)
|
||||
links_html = "<br>".join(links)
|
||||
return HTMLResponse(content=f"<h1>Available Pages</h1>{links_html}")
|
||||
|
||||
# Category route
|
||||
@app.get("/category/{category_name}", response_class=HTMLResponse)
|
||||
async def get_category(request: Request, category_name: str):
|
||||
# Find den korrekte kategori
|
||||
category = next((cat for cat in data["categories"] if cat["path"] == category_name), None)
|
||||
if category:
|
||||
category_file = f"data/{category_name}/index.html"
|
||||
if os.path.exists(category_file):
|
||||
with open(category_file) as file:
|
||||
category_content = file.read()
|
||||
return templates.TemplateResponse(
|
||||
"category.html",
|
||||
{
|
||||
"request": request,
|
||||
"data": data,
|
||||
"page_title": category["name"],
|
||||
"author": category["author"],
|
||||
"content": category_content
|
||||
},
|
||||
)
|
||||
return HTMLResponse("Kategori ikke fundet", status_code=404)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,117 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import JSONResponse
|
||||
import time
|
||||
|
||||
class CategoryController:
|
||||
def __init__(self,data_file="generated_data.json"):
|
||||
"""Initialize the controller."""
|
||||
self.router = APIRouter()
|
||||
self.templates = Jinja2Templates(directory="templates")
|
||||
self.data = self._load_data( data_file )
|
||||
self._add_routes()
|
||||
|
||||
def _add_routes(self):
|
||||
"""Add routes to the router."""
|
||||
self.router.add_api_route("/", self.get_index, methods=["GET"], response_class=HTMLResponse)
|
||||
self.router.add_api_route(
|
||||
"/category/{category_name}",
|
||||
self.get_category,
|
||||
methods=["GET"],
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
self.router.add_api_route(
|
||||
"/categories", self.list_categories, methods = ["GET"], response_class = JSONResponse
|
||||
)
|
||||
|
||||
def _load_data(self, data_file):
|
||||
"""Load JSON data from a file."""
|
||||
with open(data_file, "r", encoding="utf-8") as file:
|
||||
return json.load(file)
|
||||
|
||||
async def get_index(self, request: Request):
|
||||
"""
|
||||
Handle requests for the index (home) page.
|
||||
|
||||
This function is executed every time the root route (index page) is accessed.
|
||||
It renders the 'index.html' template and populates it with dynamic data, such as:
|
||||
- 'page_title': A static title for the home page ("Forside").
|
||||
- 'author': The author's name ("Henrik").
|
||||
- 'data': General data accessible to the template.
|
||||
|
||||
Args:
|
||||
request (Request): The HTTP request object.
|
||||
|
||||
Returns:
|
||||
TemplateResponse: A rendered HTML page for the index (home) route.
|
||||
"""
|
||||
|
||||
unix_time_now = int( time.time() )
|
||||
with open(f"data/_frontpage/index.html", "r") as fp:
|
||||
content = fp.read()
|
||||
|
||||
return self.templates.TemplateResponse(
|
||||
"category.html",
|
||||
{
|
||||
"request": request,
|
||||
"data": self.data,
|
||||
"page_title": "Frontpage",
|
||||
"author": "Henrik Jess",
|
||||
"content": content,
|
||||
"timestamp": unix_time_now
|
||||
})
|
||||
|
||||
|
||||
async def get_category(self, request: Request, category_name: str):
|
||||
"""
|
||||
Handle requests for specific category pages.
|
||||
|
||||
This function is executed every time a category route is accessed.
|
||||
It dynamically retrieves and serves content for the requested category.
|
||||
- Searches for the requested category in 'self.data["categories"]' based on the provided category name.
|
||||
- Reads the 'index.html' file located under 'data/{category_name}/' if it exists.
|
||||
- Returns the rendered 'category.html' template with the following dynamic data:
|
||||
- 'page_title': The name of the category.
|
||||
- 'author': The author of the category.
|
||||
- 'content': The content of the 'index.html' file.
|
||||
- 'timestamp': The current Unix time when the request is processed.
|
||||
- Returns a 404 HTML response if the category is not found or the file does not exist.
|
||||
|
||||
Args:
|
||||
request (Request): The HTTP request object.
|
||||
category_name (str): The name of the category being accessed.
|
||||
|
||||
Returns:
|
||||
TemplateResponse: A rendered HTML page with dynamic category content.
|
||||
HTMLResponse: A 404 response if the category does not exist.
|
||||
"""
|
||||
category = next((cat for cat in self.data["categories"] if cat["path"] == category_name), None)
|
||||
unix_time_now = int( time.time() )
|
||||
if category:
|
||||
category_file = f"data/{category_name}/index.html"
|
||||
if os.path.exists(category_file):
|
||||
with open(category_file) as file:
|
||||
category_content = file.read()
|
||||
return self.templates.TemplateResponse(
|
||||
"category.html",
|
||||
{
|
||||
"request": request,
|
||||
"data": self.data,
|
||||
"page_title": category["name"],
|
||||
"author": category["author"],
|
||||
"content": category_content,
|
||||
"timestamp": unix_time_now
|
||||
},
|
||||
)
|
||||
return HTMLResponse("Kategori ikke fundet", status_code=404)
|
||||
|
||||
async def list_categories(self, request: Request):
|
||||
"""Return a list of all categories with their name and path."""
|
||||
categories = [
|
||||
{ "name": category["name"], "path": category["path"] }
|
||||
for category in self.data.get( "categories", [] )
|
||||
]
|
||||
return JSONResponse( content = categories )
|
||||
@@ -1,64 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse, Response
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
class DynamicController:
|
||||
def __init__(self, data_dir: str):
|
||||
"""Initialize the dynamic controller."""
|
||||
self.router = APIRouter()
|
||||
self.templates = Jinja2Templates(directory="templates")
|
||||
self.data_dir = data_dir
|
||||
self.data = self._load_mock_data()
|
||||
self._add_dynamic_routes()
|
||||
|
||||
def _load_mock_data(self):
|
||||
"""Load mock data from a JSON file."""
|
||||
with open("generated_data.json") as file:
|
||||
return json.load(file)
|
||||
|
||||
def _add_dynamic_routes(self):
|
||||
"""Scan data directory and create dynamic routes."""
|
||||
for root, dirs, files in os.walk(self.data_dir):
|
||||
for directory in dirs:
|
||||
route_path = f"/{directory}" # Create route based on directory name
|
||||
directory_path = os.path.join(root, directory)
|
||||
|
||||
# Register route dynamically
|
||||
self.router.add_api_route(
|
||||
route_path,
|
||||
self._serve_dynamic_template(directory, directory_path),
|
||||
methods=["GET"],
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
|
||||
def _serve_dynamic_template(self, route_name: str, directory_path: str):
|
||||
"""Closure to serve templates for each route."""
|
||||
|
||||
async def route_handler(request: Request):
|
||||
# Look for index.html or render fallback content
|
||||
index_html = os.path.join(directory_path, "index.html")
|
||||
if os.path.exists(index_html):
|
||||
with open(index_html, "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
# Find the author for this route from preloaded data
|
||||
for category in self.data.get("categories", []):
|
||||
if category["path"] == route_name:
|
||||
author_name = category["author"]
|
||||
break
|
||||
# Pass required data to the template
|
||||
return self.templates.TemplateResponse(
|
||||
"category.html",
|
||||
{
|
||||
"request": request,
|
||||
"page_title": route_name.capitalize(),
|
||||
"content": content,
|
||||
"author": author_name,
|
||||
"data": self.data, # Pass additional data if needed
|
||||
},
|
||||
)
|
||||
|
||||
# Fallback: Return a 404 if no content is found
|
||||
return Response(f"No content found for {route_name}", status_code=404)
|
||||
return route_handler
|
||||
@@ -1,86 +0,0 @@
|
||||
import json
|
||||
import random
|
||||
|
||||
from fastapi import APIRouter, Request, FastAPI
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from app.controllers.category_controller import CategoryController
|
||||
|
||||
class RouteToWeb:
|
||||
def __init__(self, app: FastAPI):
|
||||
"""Initialize the controller."""
|
||||
self.router = APIRouter()
|
||||
self.templates = Jinja2Templates(directory="templates")
|
||||
self.app = app
|
||||
self.category_controller = CategoryController()
|
||||
self._add_routes()
|
||||
self._add_global_middleware()
|
||||
|
||||
def _add_routes(self):
|
||||
"""Add routes to the router."""
|
||||
@self.router.get("/route-list", tags=["system"])
|
||||
async def route_list(request: Request):
|
||||
"""Render route list with categories."""
|
||||
routes = [
|
||||
{"path": route.path, "name": route.name or "Unnamed"}
|
||||
for route in self.app.routes
|
||||
]
|
||||
categories = request.state.categories
|
||||
return self.templates.TemplateResponse(
|
||||
"route_list.html",
|
||||
{"request": request, "routes": routes, "categories": categories, "page_title": "Route og Kategori Liste"},
|
||||
)
|
||||
|
||||
|
||||
|
||||
def _add_global_middleware(self):
|
||||
"""Middleware to add categories and next category globally to all requests."""
|
||||
|
||||
@self.app.middleware( "http" )
|
||||
async def add_categories_to_request(request: Request, call_next):
|
||||
def generate_dynamic_description(category_name: str) -> str:
|
||||
"""Generate a dynamic and engaging link text for a category."""
|
||||
templates = [
|
||||
"Dyk ned i kategorien {category} og bliv inspireret!",
|
||||
"Opdag alt, hvad du behøver at vide i kategorien {category}.",
|
||||
"Udforsk {category}-kategorien og find noget nyt og spændende.",
|
||||
"Lad dig fordybe i kategorien {category} – der er meget at se!",
|
||||
"Find din næste læseoplevelse i {category}-kategorien.",
|
||||
"Gå på opdagelse i kategorien {category} og bliv klogere.",
|
||||
"Der venter spændende indhold i {category}-kategorien – klik her!",
|
||||
"Vil du vide mere? Hele kategorien {category} er kun ét klik væk.",
|
||||
"Læs videre i kategorien {category} og få ny inspiration.",
|
||||
"Fordyb dig i {category}-kategorien og opdag nyt indhold.",
|
||||
"Spring ind i {category}-kategorien og gå på opdagelse!",
|
||||
"Find masser af viden og gode læseoplevelser i {category}-kategorien.",
|
||||
"Udforsk hele kategorien {category} og bliv beriget med ny viden.",
|
||||
"Der er mere at læse i {category}-kategorien – gå ikke glip af det!",
|
||||
"Tag et dybere kig i kategorien {category} og bliv inspireret!"
|
||||
]
|
||||
template = random.choice( templates )
|
||||
return template.format( category = category_name.lower() )
|
||||
"""Inject categories and next category into request.state globally."""
|
||||
# Hent kategorier direkte fra CategoryController
|
||||
categories_response = await self.category_controller.list_categories( request )
|
||||
categories_data = categories_response.body.decode()
|
||||
categories = json.loads( categories_data )
|
||||
|
||||
# Tilføj kategorier til request.state
|
||||
request.state.categories = categories
|
||||
|
||||
# Find den aktuelle og næste kategori
|
||||
current_path = request.url.path.split("/")[-1]
|
||||
next_category = None
|
||||
for index, category in enumerate( categories ):
|
||||
if category["path"] == current_path:
|
||||
# Find næste kategori (cirkulær, hvis det er den sidste)
|
||||
next_index = (index + 1) % len( categories )
|
||||
next_category = categories[next_index]
|
||||
next_category["description"] = generate_dynamic_description( next_category["path"] )
|
||||
|
||||
break
|
||||
|
||||
# Tilføj næste kategori til request.state
|
||||
request.state.next_category = next_category
|
||||
|
||||
response = await call_next( request )
|
||||
return response
|
||||
72
app/main.py
72
app/main.py
@@ -1,72 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from app.controllers.route_to_web import RouteToWeb
|
||||
from app.services.markdown_processor import MarkdownProcessor
|
||||
from app.services.metadata_processor import MetadataProcessor
|
||||
from app.controllers.category_controller import CategoryController
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from app.services.image_service import ImageService
|
||||
|
||||
|
||||
class Application:
|
||||
def __init__(self):
|
||||
"""Initialize the FastAPI app and configure it."""
|
||||
self.app = FastAPI(lifespan=self._lifespan_event)
|
||||
self._set_image_sizes()
|
||||
self._setup_static_files()
|
||||
self._include_routers()
|
||||
self._include_middelware()
|
||||
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def _lifespan_event(self, app: FastAPI):
|
||||
"""Lifespan event for startup and shutdown logic."""
|
||||
print("App startup: Processing Markdown files...")
|
||||
# Generate dynamic JSON data
|
||||
metadata_processor = MetadataProcessor(input_dir="./data", output_file="generated_data.json",app=self.app)
|
||||
metadata_processor.generate_json()
|
||||
print("Generated dynamic data file.")
|
||||
# Process Markdown files into HTML
|
||||
processor = MarkdownProcessor(input_dir="./data", templates_dir="./templates",app=self.app)
|
||||
processor.run()
|
||||
yield
|
||||
print("App shutdown: Cleanup complete.")
|
||||
|
||||
def _setup_static_files(self):
|
||||
"""Mount static file directories."""
|
||||
self.app.mount("/data", StaticFiles(directory="data"), name="data")
|
||||
self.app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
self.app.mount( "/images", StaticFiles( directory = "static/images" ), name = "images" )
|
||||
|
||||
def _include_routers(self):
|
||||
"""Include all route controllers."""
|
||||
category_controller = CategoryController()
|
||||
image_service = ImageService(self.app)
|
||||
route_to_web = RouteToWeb(self.app)
|
||||
|
||||
self.app.include_router( category_controller.router )
|
||||
self.app.include_router(route_to_web.router)
|
||||
self.app.include_router( image_service.router )
|
||||
|
||||
|
||||
def _include_middelware(self):
|
||||
self.app.add_middleware( GZipMiddleware, minimum_size = 500 )
|
||||
|
||||
def _set_image_sizes(self):
|
||||
self.app.state.IMAGE_SIZES = {
|
||||
'thumbnails': {'width': 150, 'height': 150},
|
||||
'large': {'width': 800, 'height': 600},
|
||||
'small': {'width': 300, 'height': 200},
|
||||
'original': {'width': None, 'height': None}, # Original størrelse
|
||||
}
|
||||
|
||||
def get_app(self):
|
||||
"""Return the FastAPI app instance."""
|
||||
return self.app
|
||||
|
||||
|
||||
application = Application()
|
||||
app = application.get_app()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,167 +0,0 @@
|
||||
import os
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi import APIRouter, Request, FastAPI
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class FileHandler:
|
||||
|
||||
def __init__(self, category=None, image_type=None, filename=None):
|
||||
self.filename = filename
|
||||
self.category = category
|
||||
|
||||
self.image_type = image_type
|
||||
|
||||
@property
|
||||
def src_file(self) -> str:
|
||||
src_path = "data/{category}/images/{filename}"
|
||||
return src_path.format( category = self.category, filename = self.filename )
|
||||
|
||||
@property
|
||||
def dest_file(self) -> str:
|
||||
base_url = "/images/{category}/{filename}"
|
||||
return base_url.format( category = self.category, filename = self.filename )
|
||||
|
||||
@property
|
||||
def dest_filename(self) -> str:
|
||||
base_url = "static/images/{category}/{image_type}/{filename}"
|
||||
return base_url.format( category = self.category, image_type = self.image_type, filename = self.filename )
|
||||
|
||||
@property
|
||||
def dest_path(self) -> str:
|
||||
base_url = "static/images/{category}/{image_type}"
|
||||
return base_url.format( category = self.category, image_type = self.image_type )
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
f"FileHandler(\n"
|
||||
f" filename='{self.filename}',\n"
|
||||
f" category='{self.category}',\n"
|
||||
f" image_type='{self.image_type}',\n"
|
||||
f" src_file='{self.src_file}',\n"
|
||||
f" dest_file='{self.dest_file}',\n"
|
||||
f" dest_filename='{self.dest_filename}'\n"
|
||||
f")"
|
||||
)
|
||||
|
||||
def get_category(self, file_path):
|
||||
# List all categories in the data directory
|
||||
categories = [
|
||||
name for name in os.listdir( "data/" )
|
||||
if os.path.isdir( os.path.join( "data/", name ) )
|
||||
]
|
||||
|
||||
# Search for the category in the file path
|
||||
for category in categories:
|
||||
if f"/{category}/" in file_path or f"\\{category}\\" in file_path:
|
||||
return category
|
||||
|
||||
# Return None if no category matches
|
||||
return None
|
||||
|
||||
|
||||
class ImageService:
|
||||
|
||||
def __init__(self,app: FastAPI):
|
||||
self.router = APIRouter()
|
||||
self.app = app
|
||||
self.IMAGE_SIZES = self.app.state.IMAGE_SIZES
|
||||
#self._ensure_directories_exist()
|
||||
self._add_routes()
|
||||
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Provides a string representation of the class instance.
|
||||
"""
|
||||
base_paths_str = "\n".join(
|
||||
[f"{key}: {value}" for key, value in self.base_paths.items()]
|
||||
)
|
||||
image_sizes_str = "\n".join(
|
||||
[
|
||||
f"{key}: width={value['width']}, height={value['height']}"
|
||||
for key, value in self.image_sizes.items()
|
||||
]
|
||||
)
|
||||
return f"<Class:ImageService Base Paths:{base_paths_str} Image Sizes:\n{image_sizes_str}"
|
||||
|
||||
def get_image_size(self, image_type: str) -> dict:
|
||||
"""
|
||||
Retrieve the width and height for a given image type from the app state.
|
||||
|
||||
Args:
|
||||
request (Request): FastAPI request object.
|
||||
image_type (str): The type of the image (e.g., 'thumbnails').
|
||||
|
||||
Returns:
|
||||
dict: A dictionary with 'width' and 'height'.
|
||||
"""
|
||||
|
||||
image_sizes = self.app.state.IMAGE_SIZES
|
||||
if image_type not in image_sizes:
|
||||
raise ValueError( f"Invalid image type: {image_type}. Must be one of {list( image_sizes.keys() )}" )
|
||||
|
||||
return image_sizes[image_type]
|
||||
|
||||
def _add_routes(self):
|
||||
self.router.add_api_route(
|
||||
"/image/{category}/{type}/{filename}",
|
||||
self.get_image,
|
||||
methods=["GET"],
|
||||
response_class=FileResponse,
|
||||
)
|
||||
|
||||
async def get_image(self, category: str, type: str, filename: str):
|
||||
"""
|
||||
Retrieve an image file from the specified category and type.
|
||||
"""
|
||||
file_path = self._resolve_path(category, type, filename)
|
||||
return FileResponse(file_path)
|
||||
|
||||
def validate_image(self, file_path:FileHandler=None, width:int=None, height:int=None, overwrite = True ) -> bool:
|
||||
if not os.path.exists( file_path.dest_filename ):
|
||||
with Image.open( file_path.src_file ) as img:
|
||||
print(file_path.src_file)
|
||||
self._resize_image( img, file_path, width, height )
|
||||
return True
|
||||
|
||||
with Image.open( file_path.dest_filename ) as img:
|
||||
if img.width != width or img.height != height:
|
||||
if overwrite:
|
||||
self._resize_image( img, file_path, width, height )
|
||||
return False
|
||||
return True
|
||||
|
||||
def _resize_image(self, img: Image.Image, file_path: FileHandler, width: int, height: int):
|
||||
resized_img = img.resize( (width, height), Image.Resampling.LANCZOS )
|
||||
os.makedirs(file_path.dest_path,exist_ok = True)
|
||||
resized_img.save( file_path.dest_filename )
|
||||
|
||||
|
||||
async def get_image(self, category: str, type: str, filename: str):
|
||||
file_path = self._resolve_path( category, type, filename )
|
||||
return FileResponse( file_path )
|
||||
|
||||
def image_tag(self, category: str, image_type: str, filename: str, alt: str = "", width: int = None,
|
||||
height: int = None) -> str:
|
||||
"""
|
||||
Generate an HTML <img> tag with default sizes if dimensions are not provided.
|
||||
"""
|
||||
# Use default sizes if none are provided
|
||||
default_size = self.get_image_size( image_type)
|
||||
width = width or default_size.get( "width" )
|
||||
height = height or default_size.get( "height" )
|
||||
file_path = FileHandler(category = category,image_type = image_type,filename = filename)
|
||||
self.validate_image( file_path, width = width,height=height, overwrite = True )
|
||||
|
||||
tag = f'<img src="/{file_path.dest_filename}" alt="{alt}"'
|
||||
if width:
|
||||
tag += f' width="{width}"'
|
||||
if height:
|
||||
tag += f' height="{height}"'
|
||||
tag += ">"
|
||||
|
||||
return tag
|
||||
@@ -1,99 +0,0 @@
|
||||
import os
|
||||
from bs4 import BeautifulSoup
|
||||
from fastapi import FastAPI
|
||||
from app.services.markdown_render import MarkdownRenderer
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
|
||||
class MarkdownProcessor:
|
||||
"""
|
||||
A class to process Markdown files, extract metadata, and generate a single
|
||||
'index.html' per category directory using a custom rendering engine.
|
||||
"""
|
||||
|
||||
def __init__(self, input_dir: str, templates_dir: str,app:FastAPI=None):
|
||||
"""
|
||||
Initialize the MarkdownProcessor.
|
||||
|
||||
Args:
|
||||
input_dir (str): Root directory containing category subdirectories.
|
||||
templates_dir (str): Directory containing Jinja2 templates.
|
||||
"""
|
||||
self.input_dir = input_dir
|
||||
self.env = Environment(loader=FileSystemLoader(templates_dir))
|
||||
self.app = app
|
||||
|
||||
|
||||
def _process_markdown_files_in_directory(self, directory_path: str) -> list:
|
||||
"""
|
||||
Process all Markdown files in a directory using Markdown and Jinja2 custom tags.
|
||||
|
||||
Args:
|
||||
directory_path (str): Path to the category directory.
|
||||
|
||||
Returns:
|
||||
list: A list of processed sections containing metadata and rendered content.
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
sections = []
|
||||
|
||||
|
||||
|
||||
for file in sorted(os.listdir(directory_path)):
|
||||
if file.endswith(".md"):
|
||||
file_path = os.path.join(directory_path, file)
|
||||
with open(file_path, "r", encoding="utf-8") as md_file:
|
||||
markdown_content = md_file.read()
|
||||
markdown_render = MarkdownRenderer(file_path=file_path,app=self.app)
|
||||
# Process Markdown and Jinja2
|
||||
|
||||
rendered_content, metadata = markdown_render.render_markdown_with_jinja ( markdown_content )
|
||||
|
||||
# Append the section to the list
|
||||
sections.append({
|
||||
"name": metadata.get("title", "Untitled"),
|
||||
"content": rendered_content,
|
||||
"summary": metadata.get("summary", ""),
|
||||
"author": metadata.get("author", "Unknown"),
|
||||
})
|
||||
|
||||
return sections
|
||||
|
||||
def _generate_index_html(self, directory_path: str, sections: list, output_file: str):
|
||||
"""
|
||||
Generate the index.html file for a category using the combined sections.
|
||||
|
||||
Args:
|
||||
directory_path (str): Path to the category directory.
|
||||
sections (list): List of processed Markdown content and metadata.
|
||||
output_file (str): Path to save the generated index.html.
|
||||
"""
|
||||
# Render the template with the combined sections
|
||||
template = self.env.get_template("combined_template.html")
|
||||
rendered_html = template.render(
|
||||
title=os.path.basename(directory_path).capitalize(),
|
||||
sections=sections
|
||||
)
|
||||
|
||||
# Write the rendered HTML to index.html
|
||||
os.makedirs(directory_path, exist_ok=True)
|
||||
with open(output_file, "w", encoding="utf-8") as output:
|
||||
soup = BeautifulSoup( rendered_html, 'html.parser' )
|
||||
cleaned_html = soup.prettify(formatter="html5")
|
||||
output.write(cleaned_html)
|
||||
print(f"Generated: {output_file}")
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the Markdown processing workflow: one 'index.html' per category.
|
||||
"""
|
||||
for root, dirs, _ in os.walk(self.input_dir):
|
||||
for directory in dirs:
|
||||
category_path = os.path.join(root, directory)
|
||||
output_file = os.path.join(category_path, "index.html")
|
||||
|
||||
# Process all Markdown files in the current category directory
|
||||
sections = self._process_markdown_files_in_directory(category_path)
|
||||
if sections:
|
||||
self._generate_index_html(category_path, sections, output_file)
|
||||
@@ -1,171 +0,0 @@
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import markdown
|
||||
from fastapi import FastAPI
|
||||
from jinja2 import Environment, DictLoader
|
||||
from markupsafe import Markup
|
||||
from .image_service import ImageService, FileHandler
|
||||
|
||||
|
||||
class MarkdownRenderer:
|
||||
def __init__(self, file_path: str = None, app: FastAPI=None):
|
||||
"""
|
||||
Initialize the MarkdownRenderer with a Jinja2 environment and custom functions.
|
||||
"""
|
||||
self.app = app
|
||||
self.image_service = ImageService(self.app)
|
||||
self.jinja_env = self._create_jinja_environment()
|
||||
self.file_path = file_path
|
||||
|
||||
|
||||
|
||||
def _create_jinja_environment(self) -> Environment:
|
||||
"""
|
||||
Create and configure the Jinja2 environment with custom functions.
|
||||
|
||||
Returns:
|
||||
Environment: A configured Jinja2 environment.
|
||||
"""
|
||||
env = Environment(loader=DictLoader({"base_template": "{{ content | safe }}"}))
|
||||
|
||||
env.globals.update({
|
||||
"img_left_overlay": self.img_left_overlay,
|
||||
"box": self.box,
|
||||
"note": self.note,
|
||||
"warning": self.warning,
|
||||
"link_to": self.link_to,
|
||||
"slider": self.slider,
|
||||
"image": self.get_image, # Add image handler function
|
||||
})
|
||||
return env
|
||||
|
||||
def img_left_overlay(self, src: str) -> str:
|
||||
"""Render an image with overlay."""
|
||||
return f'''
|
||||
<div class="img-left-overlay">
|
||||
<img src="{src}" alt="Overlay Image" loading="lazy">
|
||||
<div class="overlay-text">Overlay Text</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def box(self, title: str, content: str) -> str:
|
||||
"""Render a box component."""
|
||||
return f'''
|
||||
<div class="box">
|
||||
<strong>{title}</strong>
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def note(self, content: str) -> str:
|
||||
"""Render a note component."""
|
||||
return f'''
|
||||
<div class="note">
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def link_to(self, title: str, url: str) -> str:
|
||||
"""Render a link component."""
|
||||
return f'''
|
||||
<a href="{url}" target="_blank" rel="noopener noreferrer">{title}</a>
|
||||
'''
|
||||
|
||||
def warning(self, content: str) -> str:
|
||||
"""Render a warning component."""
|
||||
return f'''
|
||||
<div class="warning">
|
||||
⚠️ <p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def slider(self, options: dict, images: list) -> str:
|
||||
"""Render a slider component."""
|
||||
import uuid
|
||||
modal_id = uuid.uuid4().hex.upper()[0:6]
|
||||
|
||||
|
||||
html_content = []
|
||||
html_content.append('<div class="button-stack">')
|
||||
for i, val in enumerate(images):
|
||||
self.image_service = ImageService( self.app )
|
||||
modal_id_current = f"{modal_id}_{i}"
|
||||
modal_id_next = f"{modal_id}_{i + 1}" if i + 1 < len(images) else f"{modal_id}_0"
|
||||
|
||||
category = FileHandler().get_category(self.file_path)
|
||||
|
||||
thumbnal_img = self.image_service.image_tag(category = category, image_type = "thumbnails",filename = val,alt="A better description later on")
|
||||
modal_img = self.image_service.image_tag(category = category, image_type = "large",filename = val,alt="A better description later on")
|
||||
html_content.append(f"""
|
||||
<button onclick="openModal('modal{modal_id_current}')" class="stacked-button">
|
||||
{thumbnal_img}
|
||||
</button>
|
||||
<div class="modal" id="modal{modal_id_current}">
|
||||
<div class="modal-content">
|
||||
<h2>Modal {i}</h2>
|
||||
{modal_img}
|
||||
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modal{modal_id_current}')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal{modal_id_current}', 'modal{modal_id_next}')">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
""")
|
||||
html_content.append('</div>')
|
||||
return '\n'.join(html_content)
|
||||
|
||||
def _get_category(self):
|
||||
if isinstance(self.file_path, str):
|
||||
this_path = Path(self.file_path)
|
||||
return this_path.parent.name
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_image(self, image_type: str, filename: str, alt: str = "", width: int = None, height: int = None) -> Markup:
|
||||
"""
|
||||
Generate a dynamic HTML <img> tag for an image using ImageService's image_tag method.
|
||||
"""
|
||||
valid_types = ['thumbnails', 'large', 'small', 'original']
|
||||
if image_type not in valid_types:
|
||||
sys.tracebacklimit = 0
|
||||
raise ValueError( f"Invalid image type: {image_type}. Must be one of {valid_types}" )
|
||||
|
||||
tag = self.image_service.image_tag(
|
||||
category=self._get_category(),
|
||||
image_type=image_type,
|
||||
filename=filename,
|
||||
alt=alt,
|
||||
width=width,
|
||||
height=height
|
||||
)
|
||||
return Markup(tag)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def render_markdown_with_jinja(self, markdown_content: str):
|
||||
"""
|
||||
Convert Markdown to HTML and apply Jinja2 rendering for custom tags.
|
||||
|
||||
Args:
|
||||
markdown_content (str): Raw Markdown content.
|
||||
|
||||
Returns:
|
||||
tuple: Rendered HTML content and metadata as a dictionary.
|
||||
"""
|
||||
|
||||
md = markdown.Markdown(extensions=["extra", "nl2br", "meta"])
|
||||
intermediate_html = md.convert(markdown_content)
|
||||
metadata = {key: " ".join(value) for key, value in md.Meta.items()} if md.Meta else {}
|
||||
|
||||
# Step 3: Pass the resulting HTML with Jinja2 custom tags through Jinja2
|
||||
template = self.jinja_env.get_template("base_template")
|
||||
final_html = template.render(content=intermediate_html)
|
||||
|
||||
# Step 4: Re-render final_html in Jinja2 for embedded tags like {{ image(...) }}
|
||||
final_output = self.jinja_env.from_string(final_html).render()
|
||||
|
||||
return final_output, metadata
|
||||
@@ -1,105 +0,0 @@
|
||||
import os
|
||||
import markdown
|
||||
import json
|
||||
from typing import List, Dict
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class MetadataProcessor:
|
||||
"""
|
||||
A class to scan Markdown files, extract front matter metadata,
|
||||
and generate a structured JSON file.
|
||||
"""
|
||||
|
||||
def __init__(self, input_dir: str, output_file: str,app:FastAPI=None):
|
||||
"""
|
||||
Initialize the MetadataProcessor.
|
||||
|
||||
Args:
|
||||
input_dir (str): Directory containing Markdown files.
|
||||
output_file (str): Path to save the generated JSON file.
|
||||
"""
|
||||
self.input_dir = input_dir
|
||||
self.output_file = output_file
|
||||
self.data = {"categories": [], "favorites": []}
|
||||
|
||||
def _extract_metadata(self, file_path: str) -> Dict:
|
||||
"""
|
||||
Extract front matter metadata using the 'markdown' package.
|
||||
|
||||
Args:
|
||||
file_path (str): Path to the Markdown file.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the extracted metadata.
|
||||
"""
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
markdown_content = file.read()
|
||||
|
||||
# Initialize Markdown with meta extension
|
||||
md = markdown.Markdown(extensions=["extra", "nl2br", "meta"])
|
||||
md.convert(markdown_content)
|
||||
|
||||
# Metadata is stored in md.Meta as a dictionary of lists
|
||||
meta = {key: " ".join(value) for key, value in md.Meta.items()} if md.Meta else {}
|
||||
return meta
|
||||
|
||||
def _process_directory(self):
|
||||
"""
|
||||
Recursively scan the input directory for Markdown files
|
||||
and extract metadata to build the JSON structure.
|
||||
"""
|
||||
for root, _, files in os.walk(self.input_dir):
|
||||
for file in files:
|
||||
if file.endswith(".md"):
|
||||
file_path = os.path.join(root, file)
|
||||
metadata = self._extract_metadata(file_path)
|
||||
|
||||
if metadata:
|
||||
# Add to 'categories'
|
||||
self.data["categories"].append({
|
||||
"name": metadata.get("name", "Unknown"),
|
||||
"path": os.path.relpath(root, self.input_dir).replace(os.sep, "/"),
|
||||
"author": metadata.get("author", "Unknown")
|
||||
})
|
||||
|
||||
# Add to 'favorites' if 'favorite' is true
|
||||
if metadata.get("favorite") and metadata["favorite"].lower() == "true":
|
||||
self.data["favorites"].append({
|
||||
"name": metadata.get("name", "Unknown"),
|
||||
"image": metadata.get("image", "images/default.jpg"),
|
||||
"description": metadata.get("summary", "No description provided"),
|
||||
"path": os.path.relpath(root, self.input_dir).replace(os.sep, "/"),
|
||||
})
|
||||
|
||||
def generate_json(self):
|
||||
"""
|
||||
Generate the JSON structure, deduplicate and sort categories by 'path',
|
||||
then save it to the output file.
|
||||
"""
|
||||
self._process_directory() # Extract all markdown data into self.data
|
||||
|
||||
# Ensure 'categories' exists and is a list
|
||||
if "categories" not in self.data:
|
||||
self.data["categories"] = []
|
||||
|
||||
# Deduplicate 'categories' using 'path' as the unique key
|
||||
unique_categories = { }
|
||||
for category in self.data["categories"]:
|
||||
if isinstance( category, dict ): # Ensure valid category structure
|
||||
path = category.get( "path", "unknown" ) # Use 'path' as the unique key
|
||||
if path not in unique_categories:
|
||||
unique_categories[path] = category
|
||||
|
||||
# Replace the 'categories' list with a sorted version by 'path'
|
||||
self.data["categories"] = sorted(
|
||||
unique_categories.values(),
|
||||
key = lambda x: x.get( "path", "unknown" )
|
||||
)
|
||||
|
||||
# Save the updated JSON to file
|
||||
with open( self.output_file, "w", encoding = "utf-8" ) as json_file:
|
||||
json.dump( self.data, json_file, indent = 4, ensure_ascii = False )
|
||||
|
||||
print( f"Generated JSON saved to {self.output_file}" )
|
||||
43
convert_markdown_to_html.py
Normal file
43
convert_markdown_to_html.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import os
|
||||
from markdown_render import render_markdown_with_jinja
|
||||
|
||||
def process_markdown_files(input_dir: str, output_dir: str):
|
||||
"""
|
||||
Recursively process all Markdown files in the input directory,
|
||||
render them to HTML, and save them to the output directory.
|
||||
"""
|
||||
for root, _, files in os.walk(input_dir):
|
||||
for file in files:
|
||||
if file.endswith(".md"):
|
||||
input_file_path = os.path.join(root, file)
|
||||
|
||||
# Determine output file path (convert .md to .html)
|
||||
relative_path = os.path.relpath(input_file_path, input_dir)
|
||||
output_file_path = os.path.join(output_dir, os.path.splitext(relative_path)[0] + ".html")
|
||||
|
||||
# Ensure the output directory exists
|
||||
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
|
||||
|
||||
# Read Markdown content
|
||||
with open(input_file_path, "r", encoding="utf-8") as md_file:
|
||||
markdown_content = md_file.read()
|
||||
|
||||
# Render Markdown with Jinja2
|
||||
print(f"Processing: {input_file_path} -> {output_file_path}")
|
||||
rendered_html = render_markdown_with_jinja(markdown_content)
|
||||
|
||||
# Write the rendered HTML to the output file
|
||||
with open(output_file_path, "w", encoding="utf-8") as html_file:
|
||||
html_file.write(rendered_html)
|
||||
|
||||
print("Markdown processing complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Input directory containing Markdown files
|
||||
input_directory = "./data"
|
||||
|
||||
# Output directory where HTML files will be stored
|
||||
output_directory = "./data"
|
||||
|
||||
# Start the processing
|
||||
process_markdown_files(input_directory, output_directory)
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: Arbejde i Portugal
|
||||
description: Mine noter og fund om at arbejde i Portugal som EU-borger
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 22:16:13 CET 2024
|
||||
summary: Praktisk info fra min research om arbejde i Portugal
|
||||
favorite: false
|
||||
image: images/pic09.jpg
|
||||
category: Job
|
||||
tags: [Portugal, Arbejde, EU-borgere, NIF-nummer, Socialsikring]
|
||||
---
|
||||
|
||||
# Kan jeg som udlænding arbejde i Portugal?
|
||||
|
||||
Jeg er ikke selv flyttet til Portugal endnu, men jeg har brugt en del tid på at undersøge, hvad der kræves for at arbejde der som EU-borger. Her er mine noter, baseret på det, jeg har fundet på nettet, YouTube og forskellige guides.
|
||||
|
||||
Som dansker kan man heldigvis arbejde i Portugal uden at skulle søge om en arbejdstilladelse. Det skyldes EU's regler om fri bevægelighed, så på det punkt er det ret ligetil.
|
||||
|
||||
Men der er nogle praktiske ting, du skal have styr på, før du kan komme i gang:
|
||||
- **NIF-nummer**: Det er et skattemæssigt identifikationsnummer, som du skal bruge til stort set alt i Portugal – arbejde, bankkonto og bolig.
|
||||
- **Socialsikringssystemet**: Du skal registrere dig i det portugisiske socialsikringssystem for at få adgang til sundhed og sociale ydelser.
|
||||
|
||||
Selvom processen virker enkel på papiret, går det igen i mange kilder, at det kan tage lidt tid, især hvis du ikke taler portugisisk. Flere anbefaler at få hjælp fra nogen, der har prøvet det før, eller bruge lokale rådgivere, hvis det bliver for bøvlet.
|
||||
|
||||
Jeg deler bare det, jeg har fundet indtil videre, så hvis du også overvejer at arbejde i Portugal, håber jeg, det kan give dig en god start.
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: Populære områder i Portugal
|
||||
description: En guide til attraktive områder for tilflyttere i Portugal
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:00:00 CET 2024
|
||||
summary: Populære regioner som Lissabon, Porto og Algarve
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Bolig
|
||||
tags: [Portugal, Lissabon, Porto, Algarve, Coimbra, Viseu, Tilflyttere]
|
||||
---
|
||||
|
||||
# Hvilke områder er populære for tilflyttere?
|
||||
|
||||
Når man overvejer at flytte til Portugal, er der nogle områder, som skiller sig ud som særligt attraktive for tilflyttere.
|
||||
|
||||
### Populære områder:
|
||||
- **{{ link_to(title="Lissabon", url="https://www.google.com/maps?q=Lissabon") }}**: Portugals hovedstad med god infrastruktur, et levende kulturliv og mange jobmuligheder.
|
||||
- **{{ link_to(title="Porto", url="https://www.google.com/maps?q=Porto") }}**: Kendt for sin charme, historie og et voksende expat-fællesskab.
|
||||
- **{{ link_to(title="Algarve", url="https://www.google.com/maps?q=Algarve") }}**: Ideelt for dem, der søger sol, strande og en afslappet livsstil.
|
||||
|
||||
### Alternativer til storbyerne:
|
||||
Mindre kendte områder kan være attraktive for dem, der ønsker lavere boligpriser og en roligere livsstil:
|
||||
- **{{ link_to(title="Coimbra", url="https://www.google.com/maps?q=Coimbra") }}**: En universitetsby med historie og lavere leveomkostninger.
|
||||
- **{{ link_to(title="Viseu", url="https://www.google.com/maps?q=Viseu") }}**: Kendt for sin kvalitet af liv, smukke landskab og mere overkommelige boligpriser.
|
||||
|
||||
Uanset hvor i Portugal du vælger at bosætte dig, er der noget for enhver smag – fra travle byer med international atmosfære til mindre byer med ro og autentisk portugisisk kultur.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
name: Mit kommende område i Portugal
|
||||
description: Planer om at flytte til Porto
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:10:00 CET 2024
|
||||
summary: Kort om hvorfor Porto er mit foretrukne område i Portugal
|
||||
favorite: false
|
||||
image: images/pic06.jpg
|
||||
category: Bolig
|
||||
tags: [Portugal, Porto, Flytning, Planlægning]
|
||||
---
|
||||
|
||||
# Hvilket område i Portugal planlægger jeg at flytte til?
|
||||
|
||||
Primært Porto
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Boligsøgning i Portugal
|
||||
description: Effektive platforme og tips til boligsøgning i Portugal
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:15:00 CET 2024
|
||||
summary: Brug Idealista og tips til at finde bolig i Portugal
|
||||
favorite: false
|
||||
image: images/pic08.jpg
|
||||
category: Bolig
|
||||
tags: [Portugal, Bolig, Idealista, Boligsøgning, Flytning]
|
||||
---
|
||||
|
||||
# Hvor finder man bolig?
|
||||
|
||||
En af de bedste platforme, jeg har fundet til boligsøgning i Portugal, er **{{ link_to(title="idealista.pt", url="https://www.idealista.pt/") }}**. Den minder lidt om DBA og er både overskuelig og nem at bruge. Det er hurtigt at oprette en profil, og tjenesten er tilmed gratis.
|
||||
|
||||
### Udfordringer ved boligsøgning
|
||||
Hvis du ikke kender Portugal særlig godt, kan det være svært at beslutte, hvilket område der passer bedst til dine behov. Her er nogle tips:
|
||||
- Brug kortfunktionen på Idealista til at få overblik over priser i forskellige områder.
|
||||
- Overvej at besøge de mest interessante områder først, før du træffer din beslutning.
|
||||
- Tjek lokale Facebook-grupper og fora for tips fra andre tilflyttere.
|
||||
|
||||
Portugal har meget at byde på, og det rigtige område afhænger af, om du søger storbyliv, strande eller roligere omgivelser.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: Bolig i Portugal
|
||||
description: Købs- og lejepriser samt regionale forskelle i Portugal
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 22:45:00 CET 2024
|
||||
summary: Boligpriser og ekstra omkostninger i Portugal
|
||||
favorite: false
|
||||
image: images/pic09.jpg
|
||||
category: Bolig
|
||||
tags: [Portugal, Bolig, Leje, Køb, Lissabon, Ejendomsskatter]
|
||||
---
|
||||
|
||||
# Er det dyrt at købe eller leje bolig i Portugal?
|
||||
|
||||
Boligpriser i Portugal varierer meget afhængigt af regionen. I de større byer som **Lissabon** og **Porto** er priserne generelt høje, mens mindre byer som **Coimbra** og **Guarda** tilbyder mere overkommelige muligheder.
|
||||
|
||||
---
|
||||
|
||||
## Regionale forskelle
|
||||
- **Lissabon og Porto**: Som de mest populære områder for både tilflyttere og turister har disse byer nogle af de højeste boligpriser i landet.
|
||||
- **Mindre byer**: Byer som Coimbra, Viseu eller Guarda giver langt billigere alternativer og tilbyder en roligere livsstil.
|
||||
|
||||
---
|
||||
|
||||
## Ekstra omkostninger ved boligkøb
|
||||
Ud over selve boligprisen skal man være opmærksom på flere ekstra udgifter:
|
||||
- **Ejendomsskat (IMI)**: En årlig skat på mellem **0,3% og 0,45%** af boligens værdi.
|
||||
- **IMT-afgift**: En engangsafgift ved køb af bolig, som afhænger af købsprisen.
|
||||
- **Advokat- og notaromkostninger**: Ved boligkøb er det normalt at bruge advokat og notar for at sikre korrekt papirarbejde.
|
||||
|
||||
---
|
||||
|
||||
## Leje vs. køb
|
||||
Hvis du ikke er klar til at købe, er **leje** en god mulighed. Lejepriser varierer også meget:
|
||||
- I Lissabon og Porto er månedlig husleje typisk højere, men stadig billigere end i de fleste danske storbyer.
|
||||
- I mindre byer kan du finde boliger til markant lavere priser, især hvis du er fleksibel med beliggenheden.
|
||||
|
||||
---
|
||||
|
||||
## Konklusion
|
||||
Boligmarkedet i Portugal byder på både muligheder og udfordringer. Mens de populære områder har højere priser, er der stadig gode alternativer i mindre byer. Når man medregner de lavere leveomkostninger i Portugal sammenlignet med Danmark, er der potentiale for at få mere værdi for pengene.
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
name: Budget - Indkøb
|
||||
description: En kort sammenligning af priserne
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:25:00 CET 2024
|
||||
summary: Fødevarer er markant billigere i Portugal med få undtagelser som bær og specialvarer.
|
||||
favorite: true
|
||||
image: images/budget2.jpg
|
||||
category: Økonomi
|
||||
tags: [Portugal, Budget, Økonomi]
|
||||
---
|
||||
|
||||
|
||||
Jeg har taget udgangspunkt i et indkøb fra Mambeno / Rema1000 indkøb for en uge, og forsøgt og sammenligne det. Her er den fulde kvittering med korrekt **omregning til EUR**, de portugisiske priser og den **procentvise forskel** mellem Danmark og Portugal. Nogen af priserne er konsekvent højere, så er dt fordi der er købt store pakker, eksempelvis Laks.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
{{ box(title="Disclaimer",content="Priserne er baseret på her-og-nu priser fra REMA 1000 i Danmark. Enkelte produkter kan have været på tilbud, hvilket kan påvirke sammenligningen.") }}
|
||||
## **Samlet Oversigt**
|
||||
|
||||
**Valutakurs**: 1 EUR = 7,44 DKK
|
||||
|
||||
| **Nr.** | **Varebeskrivelse** | **Pris i Danmark (EUR)** | **Pris i Portugal (EUR)** | **Forskel (EUR)** | **% Forskel** |
|
||||
|---------|--------------------------------|--------------------------|---------------------------|------------------:|--------------:|
|
||||
| 1 | FLUTES | 0,87 | 0,50 | 0,37 | -42,5% |
|
||||
| 2 | TANDPASTA COLGATE | 3,49 | 2,00 | 1,49 | -42,7% |
|
||||
| 3 | SOLSIKKERUGBRØD DET GODE | 3,43 | 2,00 | 1,43 | -41,7% |
|
||||
| 4 | TOILETPAPIR SOFT 3-LAGS | 2,75 | 1,80 | 0,95 | -34,5% |
|
||||
| 5 | JORDBÆR (1 LTR) | 1,14 | 1,50 | -0,36 | +31,6% |
|
||||
| 6 | SOLBÆR (1 LTR) | 1,14 | 1,50 | -0,36 | +31,6% |
|
||||
| 7 | HINDBÆR/APPELSIN (1 LTR) | 1,14 | 1,40 | -0,26 | +22,8% |
|
||||
| 8 | MINIMÆLK 0,4% | 1,61 | 0,90 | 0,71 | -44,1% |
|
||||
| 9 | RISOTTO M/SVAMPE | 2,01 | 1,70 | 0,31 | -15,4% |
|
||||
| 10 | KÆRGÅRDEN SMØRBAR LET | 3,49 | 2,00 | 1,49 | -42,7% |
|
||||
| 11 | SDJ. SPEGEPØLSE 3-STJERNET | 2,68 | 2,00 | 0,68 | -25,4% |
|
||||
| 12 | OKSE SPEGEPØLSE 3-STJERNET | 2,68 | 2,00 | 0,68 | -25,4% |
|
||||
| 13 | KARTOFFELSPEGEPØLSE 3-STJERNET | 2,68 | 2,00 | 0,68 | -25,4% |
|
||||
| 14 | KØDPØLSE 3-STJERNET | 2,41 | 2,00 | 0,41 | -17,1% |
|
||||
| 15 | TORTILLAS HVEDE | 1,41 | 1,20 | 0,21 | -14,9% |
|
||||
| 16 | GUACAMOLE DIP | 1,51 | 1,20 | 0,31 | -20,5% |
|
||||
| 17 | SALSASAUCE HOT | 1,31 | 1,00 | 0,31 | -23,7% |
|
||||
| 18 | SMØR ARLA, ØKOLOGISK | 3,62 | 2,50 | 1,12 | -31,0% |
|
||||
| 19 | SKYR NATUREL | 2,41 | 2,00 | 0,41 | -17,1% |
|
||||
| 20 | CHEDDAR OST | 2,01 | 1,80 | 0,21 | -10,4% |
|
||||
| 21 | FRAICHE 5% | 2,68 | 2,20 | 0,48 | -17,9% |
|
||||
| 22 | KRYDDEROST BUKO | 3,62 | 2,50 | 1,12 | -31,0% |
|
||||
| 23 | FRILANDS BRUNCHÆG | 2,01 | 1,80 | 0,21 | -10,4% |
|
||||
| 24 | TORTILLA CHIPS SALT | 1,04 | 1,00 | 0,04 | -3,8% |
|
||||
| 25 | GROV CAFESANDWICH | 2,95 | 2,20 | 0,75 | -25,4% |
|
||||
| 26 | FULDKORNSTORTILLAS | 1,65 | 1,40 | 0,25 | -15,2% |
|
||||
| 27 | KYLLINGESCHNITZLER | 2,68 | 2,30 | 0,38 | -14,2% |
|
||||
| 28 | RATATOUILLE | 1,88 | 1,50 | 0,38 | -20,2% |
|
||||
| 29 | FINE ÆRTER | 1,61 | 1,20 | 0,41 | -25,5% |
|
||||
| 30 | FAJITA BLANDING | 1,88 | 1,40 | 0,48 | -25,5% |
|
||||
| 31 | SOLSIKKEKERNER | 1,55 | 1,20 | 0,35 | -22,6% |
|
||||
| 32 | KOGEPOSE RIS | 1,01 | 0,90 | 0,11 | -10,9% |
|
||||
| 33 | RASP | 0,93 | 0,80 | 0,13 | -14,0% |
|
||||
| 34 | HASSELNØDDEKERNER | 1,61 | 1,20 | 0,41 | -25,5% |
|
||||
| 35 | PENNE RIGATE | 1,14 | 1,00 | 0,14 | -12,3% |
|
||||
| 36 | ROGN | 2,14 | 1,80 | 0,34 | -15,9% |
|
||||
| 37 | TOMAT PASTA | 0,67 | 0,50 | 0,17 | -25,4% |
|
||||
| 38 | SØD FRANSK SENNEP | 1,28 | 1,00 | 0,28 | -21,9% |
|
||||
| 39 | SOJASAUCE | 1,34 | 1,10 | 0,24 | -17,9% |
|
||||
| 40 | MAJS | 1,01 | 0,80 | 0,21 | -20,8% |
|
||||
| 41 | HVIDE BØNNER I TOMAT | 0,93 | 0,80 | 0,13 | -14,0% |
|
||||
| 42 | HAKKEDE TOMATER | 1,01 | 0,80 | 0,21 | -20,8% |
|
||||
| 43 | GRØNTSAGSBOUILLON | 0,60 | 0,50 | 0,10 | -16,7% |
|
||||
| 44 | KRYDDERURTEDRESSING | 1,68 | 1,20 | 0,48 | -28,6% |
|
||||
| 45 | HONNING | 3,62 | 2,50 | 1,12 | -31,0% |
|
||||
| 46 | SØNDERJYSK SPEGEPØLSE | 1,34 | 1,20 | 0,14 | -10,4% |
|
||||
| 47 | LAKSEFILET | 10,75 | 7,00 | 3,75 | -34,9% |
|
||||
| 48 | KYLLINGEBRYSTFILET | 8,06 | 5,50 | 2,56 | -31,8% |
|
||||
| 49 | TOMATER I BAKKE | 1,61 | 1,20 | 0,41 | -25,5% |
|
||||
| 50 | TØRRET TIMIAN | 0,67 | 0,50 | 0,17 | -25,4% |
|
||||
| 51 | SPÆD KÅLSALAT | 3,36 | 2,50 | 0,86 | -25,6% |
|
||||
| 52 | PERSILLE | 1,21 | 1,00 | 0,21 | -17,4% |
|
||||
| 53 | ØKOLOGISKE CITRONER | 0,81 | 0,70 | 0,11 | -13,6% |
|
||||
| 54 | RØD PEBER | 1,07 | 0,90 | 0,17 | -15,9% |
|
||||
| 55 | AGURK | 0,81 | 0,70 | 0,11 | -13,6% |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **Opsummering af Prissammenligning mellem Danmark og Portugal**
|
||||
|
||||
| **Parameter** | **Værdi** |
|
||||
|--------------------------------------|--------------------------------|
|
||||
| Antal varer sammenlignet | 55 |
|
||||
| Gennemsnitlig pris i Danmark (EUR) | 2,42 |
|
||||
| Gennemsnitlig pris i Portugal (EUR) | 1,84 |
|
||||
| Gennemsnitlig besparelse | **24,0% lavere i Portugal** |
|
||||
| Antal varer billigere i Portugal | 47 |
|
||||
| Antal varer dyrere i Portugal | 8 |
|
||||
|
||||
1. **Generel besparelse**
|
||||
Priserne i Portugal er **i gennemsnit 24,0% lavere** end i Danmark på tværs af de 55 varer. Dette er særligt tydeligt for basisvarer som smør, mælk og kolonialvarer.
|
||||
|
||||
2. **Flest varer er billigere i Portugal**
|
||||
Af de 55 varer er **47 billigere** i Portugal, hvilket tydeligt afspejler de lavere leveomkostninger og en mere konkurrencedygtig fødevaresektor.
|
||||
|
||||
3. **Eksempler på store besparelser**
|
||||
- **Kærgården Smørbar Let**: -42,7% billigere
|
||||
- **Smør Arla Økologisk**: -31,0% billigere
|
||||
- **Mælk (Minimælk)**: -44,1% billigere
|
||||
- **Laksefilet**: -34,9% billigere
|
||||
Disse basisvarer oplever en markant prisreduktion i Portugal, hvilket giver væsentlige besparelser i dagligdagen.
|
||||
|
||||
4. **Få varer dyrere i Portugal**
|
||||
Der er **8 varer**, hvor priserne er højere i Portugal. Eksempler inkluderer:
|
||||
- **Jordbær/Solbær**: +31,6% dyrere
|
||||
- **Hindbær/Appelsin**: +22,8% dyrere
|
||||
Disse prisforskelle kan tilskrives sæsonvariationer og højere importomkostninger. Det kan også begrundes i det kan være danske Tilbuds vare.
|
||||
|
||||
5. **Frisk frugt og grønt**
|
||||
Generelt er grøntsager som **tomater, agurk og røde peberfrugter** betydeligt billigere i Portugal. Dog er enkelte varer som **bær** dyrere, sandsynligvis på grund af sæsonafhængighed og øgede importomkostninger.
|
||||
|
||||
---
|
||||
|
||||
Prissammenligningen viser, at dagligvarer generelt er **betydeligt billigere i Portugal**, især når det gælder smør, mælk og kolonialvarer, som typisk har besparelser på op til **50%**. Dette gør Portugal økonomisk attraktivt for husholdninger. Dog skal man være opmærksom på, at enkelte varer som **bær** og specialvarer kan være dyrere, hvilket er en vigtig faktor i planlægningen af leveomkostninger.
|
||||
|
||||
---
|
||||
|
||||
En anden (mere proff) side kommer til næsten de samme konklusioner, man kan måske godt se jeg har brugt Rema1000s tilbuds priser - Men de er relativt tæt på hinanden
|
||||
|
||||
|
||||
- **Leveomkostninger i Portugal er 38,5% lavere end i Danmark (eksklusive husleje).**
|
||||
- **Leveomkostninger inklusive husleje i Portugal er 32,4% lavere end i Danmark.**
|
||||
- **Huslejepriser i Portugal er 13,1% lavere end i Danmark.**
|
||||
- **Restaurantpriser i Portugal er 53,0% lavere end i Danmark.**
|
||||
- **Dagligvarepriser i Portugal er 33,4% lavere end i Danmark.**
|
||||
- **Den lokale købekraft i Portugal er 54,3% lavere end i Danmark.**
|
||||
|
||||
{{ link_to(title="Leveomkostningssammenligning mellem Danmark og Portugal - Numbeo", url="https://www.numbeo.com/cost-of-living/compare_countries_result.jsp?country1=Denmark&country2=Portugal") }} - De siger 33.4%
|
||||
{{ link_to(title="Leveomkostninger mellem Danmark og Portugal - Livingcost", url="https://livingcost.org/cost/denmark/portugal") }} - De siger 28.3% som rammer også meget godt.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: Supermarkedssammenligning
|
||||
description: En praktisk guide til at sammenligne supermarkeder i Danmark og Portugal.
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:55:00 CET 2024
|
||||
summary: Find de bedste steder at handle i Portugal, sammenlignet med danske supermarkeder.
|
||||
favorite: false
|
||||
image: images/supermarked.jpg
|
||||
category: Økonomi
|
||||
tags: [Portugal, Indkøb, Supermarkeder, Økonomi]
|
||||
---
|
||||
|
||||
### **Sammenligning af supermarkeder: Danmark vs Portugal**
|
||||
|
||||
| **Danmark** | **Portugal** | **Bemærkninger** |
|
||||
|--------------------------|-------------------------|------------------------------------------------------|
|
||||
| **REMA 1000** | **Pingo Doce** | Budgetvenligt supermarked med mange dagligvarer. |
|
||||
| **Føtex** / **Bilka** | **Continente** | Stort supermarked med bredt udvalg og gode tilbud. |
|
||||
| **Netto** | **Lidl** | Lavpris supermarked. Lidl findes i begge lande. |
|
||||
| **SuperBrugsen** | **Auchan** (tidl. Jumbo)| Mid-range supermarked med fokus på kvalitet. |
|
||||
| **Irma** | **El Corte Inglés** | Premium supermarked med internationale varer. |
|
||||
| **Meny** | **Intermarché** | God kvalitet og et stort udvalg af varer. |
|
||||
| **Aldi** | **Aldi** | Ensartet kæde i begge lande, kendt for lave priser. |
|
||||
| **Kvickly** | **Minipreço** | Prisvenligt med mindre butikker i lokalområder. |
|
||||
|
||||
---
|
||||
|
||||
### **Anbefalede steder at handle i Portugal**
|
||||
|
||||
1. {{ link_to(title="**Pingo Doce**", url="https://www.pingodoce.pt") }} – Kendt for lave priser og gode tilbud, især på dagligvarer som brød, mælk og grøntsager.
|
||||
2. {{ link_to(title="**Continente**", url="https://www.continente.pt") }} – Portugals svar på Bilka/Føtex med alt fra mad til husholdningsartikler. Ofte har de gode rabatprogrammer.
|
||||
3. {{ link_to(title="**Lidl**", url="https://www.lidl.pt") }} – Samme kendte koncept som i Danmark. Billige basisvarer og et mindre udvalg.
|
||||
4. {{ link_to(title="**Auchan**", url="https://www.auchan.pt") }} (tidligere Jumbo) – Større supermarkeder med et bredt udvalg af varer, inklusiv internationale produkter.
|
||||
5. {{ link_to(title="**Intermarché**", url="https://www.intermarche.pt") }} – Fokus på frisk frugt og grønt samt lokale portugisiske produkter.
|
||||
6. {{ link_to(title="**Minipreço**", url="https://www.minipreco.pt") }} – Lavprisalternativ med mindre butikker, som er nemme at finde i byområder.
|
||||
7. {{ link_to(title="**El Corte Inglés**", url="https://www.elcorteingles.pt") }} – Perfekt til luksusvarer og specialprodukter, men til lidt højere priser.
|
||||
|
||||
---
|
||||
|
||||
### **Tips til at spare penge i Portugal**
|
||||
- **Frugt og grønt** er ofte billigst på lokale markeder, som fx **"Mercado Municipal"**.
|
||||
- Brug **loyalitetskort** i supermarkeder som Pingo Doce og Continente for at få rabatter.
|
||||
- Køb kød og fisk på **lokale slagtere** og fiskemarkeder for bedre priser og kvalitet.
|
||||
- Planlæg ugentlige indkøb i større butikker som **Continente** for at udnytte kampagner og tilbud.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: El- og vandregninger i Portugal
|
||||
description: Hvordan el- og vandregninger påvirker leveomkostningerne
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:20:00 CET 2024
|
||||
summary: Elregninger er høje, mens vand og gebyrer er moderate
|
||||
favorite: false
|
||||
image: images/pic08.jpg
|
||||
category: Leveomkostninger
|
||||
tags: [Portugal, Leveomkostninger, Elregninger, Vandregninger, Gebyrer]
|
||||
---
|
||||
|
||||
# Hvordan påvirker el- og vandregninger leveomkostningerne?
|
||||
|
||||
- **Elregninger**: Elektricitet i Portugal kan være relativt dyrt sammenlignet med andre europæiske lande. Det er en omkostning, der især mærkes i vintermånederne, hvor opvarmning kan trække prisen op.
|
||||
- **Vand og affaldsgebyrer**: Disse regninger er typisk moderate og overkommelige, hvilket hjælper med at holde de samlede udgifter nede.
|
||||
|
||||
Elektricitetspriserne i Portugal er blandt de højeste i Europa. Ifølge Eurostat var den gennemsnitlige pris for husholdninger i første halvdel af 2024 €0,2426 per kWh, en stigning på 5,52% fra det foregående halvår. Denne stigning skyldes blandt andet landets afhængighed af importerede energikilder, som udgør omkring 65% af det samlede energiforbrug.
|
||||
|
||||
|
||||
I modsætning hertil er omkostningerne til vand og affaldshåndtering i Portugal mere moderate. For en lejlighed på 85 m² ligger de månedlige udgifter til el, vand, varme, køling og affaldsbortskaffelse mellem €54 og €150, med et gennemsnit omkring midten af dette interval. Disse omkostninger er relativt lave sammenlignet med andre europæiske lande.
|
||||
|
||||
For at reducere de høje eludgifter, især i vintermånederne, kan det være fordelagtigt at investere i energieffektive løsninger og apparater. Desuden kan bevidsthed om energiforbrug og valg af tidspunkter med lavere elpriser bidrage til at minimere omkostningerne.
|
||||
|
||||
Sammenfattende er det vigtigt at tage højde for de relativt høje elpriser i Portugal, mens vand- og affaldsgebyrer forbliver moderate. Ved at implementere energieffektive tiltag kan man opnå en mere balanceret og overkommelig samlet leveomkostning.
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
name: El- og vandregninger i Portugal: Sådan påvirker de leveomkostningerne
|
||||
description: Undersøg hvordan elregninger og vandgebyrer i Portugal påvirker leveomkostningerne. Få indsigt i boligudgifter, dagligvarer og sammenligning med Danmark.
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:20:00 CET 2024
|
||||
summary: Leveomkostningerne i Portugal er lave, især på bolig og dagligvarer. Få indsigt i, hvordan du kan leve godt og billigt under sydens sol med et gennemtænkt budget.
|
||||
image: images/pic08.jpg
|
||||
category: Økonomi
|
||||
tags: ["Portugal", "Leveomkostninger", "Elregninger", "Vandregninger", "Gebyrer"]
|
||||
---
|
||||
|
||||
|
||||
Her er det reviderede **husholdningsbudget i Portugal** uden kontorfællesskab og flyrejser, så du kan sammenligne med leveomkostninger i Danmark.
|
||||
|
||||
---
|
||||
|
||||
## **Månedligt Budget i Portugal**
|
||||
|
||||
{{ box(title="Disclaimer",content="Det skal siges jeg har ikke de faktiske tal endnu, så det er 100% på tommelfinger vudering") }}
|
||||
|
||||
|
||||
| **Kategori** | **Udgift pr. måned (EUR)** | **Bemærkninger** |
|
||||
|---------------------------------|----------------------------|---------------------------------------|
|
||||
| **Boligleje (100 m²)** | 1.000 - 1.200 | Afhænger af område (fx Lissabon dyrere) |
|
||||
| **El og varme** | 120 - 150 | Højt i vintermånederne |
|
||||
| **Vand og affaldsgebyrer** | 30 - 50 | Moderate priser |
|
||||
| **Internet og mobil** | 40 - 60 | Kombipakke til hjem og telefoner |
|
||||
| **Mad og dagligvarer** | 500 - 600 | Baseret på ugentlig kvittering (~125 EUR) |
|
||||
| **Transport (offentlig/benzin)** | 50 - 100 | Offentlig transport og små ture |
|
||||
| **Forsikringer** | 50 - 75 | Sundhed, indbo, bil osv. |
|
||||
| **Sundhed** | 50 - 100 | Egenbetaling for tandlæge og medicin |
|
||||
| **Fritid og aktiviteter** | 100 - 150 | Restauranter, biograf, hobbyer |
|
||||
| **Erika's skoleudgifter** | 50 - 100 | Materialer og småudgifter |
|
||||
|**Samlet månedlig udgift** | **1.990 - 2.585 EUR** | **Cirka 14.800 - 19.200 DKK** |
|
||||
|
||||
### **Kort analyse af budgetoversigten**
|
||||
|
||||
1. **Boligleje**: Boligleje udgør den største udgiftspost med **1.000 - 1.200 EUR** om måneden, afhængigt af området. Lissabon og større byer er typisk dyrere, mens mindre byer tilbyder mere overkommelige priser.
|
||||
2. **Energiomkostninger (El og varme)**: El og varme koster mellem **120 - 150 EUR**, og dette kan stige i vintermånederne, især hvis opvarmning ikke er energieffektiv. Dette er en vigtig post at planlægge for.
|
||||
3. **Vand og affaldsgebyrer**: Disse udgifter er relativt lave på **30 - 50 EUR**, hvilket hjælper med at holde de samlede faste omkostninger nede.
|
||||
4. **Dagligvarer og mad**: Madbudgettet på **500 - 600 EUR** om måneden er realistisk for to personer og er baseret på en gennemsnitlig ugentlig udgift på **125 EUR**. Dette afspejler rimelige priser på dagligvarer i Portugal.
|
||||
5. **Transport**: Transportomkostninger er beskedne på **50 - 100 EUR**, hvilket inkluderer offentlig transport og mindre biludgifter. Det lave niveau skyldes de lave priser på månedskort og brændstof i Portugal sammenlignet med Nordeuropa.
|
||||
6. **Sundhed og forsikringer**: Sundhedsudgifter og forsikringer varierer mellem **50 - 100 EUR** hver. Dette inkluderer egenbetaling til tandlæge, medicin og basale forsikringer som sundhed og indbo.
|
||||
7. **Fritid og aktiviteter**: Fritidsbudgettet på **100 - 150 EUR** dækker restauranter, biograf og hobbyer. Portugal tilbyder generelt billigere oplevelser, hvilket giver mere økonomisk frihed til fritidsaktiviteter.
|
||||
8. **Skoleudgifter**: Erika's skoleudgifter er moderate med **50 - 100 EUR**, typisk til materialer og mindre udgifter. - Dog med tanke på folkeskole med int. sproglinje (ikke privat)
|
||||
|
||||
---
|
||||
|
||||
### **Samlet vurdering**
|
||||
Det samlede budget på **1.990 - 2.585 EUR** om måneden (cirka **14.800 - 19.200 DKK**) viser, at det er muligt at leve komfortabelt i Portugal til en lavere pris end i Danmark.
|
||||
- **Boligleje** er den største omkostning, men stadig overkommelig sammenlignet med danske storbypriser.
|
||||
- **Mad og dagligvarer** udgør en betydelig, men rimelig del af budgettet.
|
||||
- **Faste udgifter** som energi og vand er relativt lave, mens sundhed og fritidsaktiviteter giver økonomisk fleksibilitet.
|
||||
|
||||
Dette budget giver plads til en stabil og behagelig hverdag i Portugal uden store kompromiser på livskvalitet.
|
||||
---
|
||||
|
||||
## **Sammenligning med Danmark**
|
||||
|
||||
|
||||
| **Kategori** | **Portugal (EUR)** | **Danmark (EUR)** | **Besparelse (%)** |
|
||||
|-----------------------------------|----------------------------|-------------------------|-------------------------|
|
||||
| **Boligleje (100 m²)** | 1.000 - 1.200 | 1.500 - 2.000 | -33% til -50% |
|
||||
| **El og varme** | 120 - 150 | 200 - 250 | -25% til -40% |
|
||||
| **Vand og affaldsgebyrer** | 30 - 50 | 60 - 100 | -50% til -70% |
|
||||
| **Internet og mobil** | 40 - 60 | 50 - 70 | -20% til -30% |
|
||||
| **Mad og dagligvarer** | 500 - 600 | 800 - 1.000 | -25% til -40% |
|
||||
| **Transport** | 50 - 100 | 150 - 200 | -50% til -66% |
|
||||
| **Forsikringer** | 50 - 75 | 100 - 150 | -50% |
|
||||
| **Sundhed** | 50 - 100 | 100 - 150 | -33% til -50% |
|
||||
| **Fritid og aktiviteter** | 100 - 150 | 200 - 300 | -50% |
|
||||
| **Samlet månedlig udgift** | **1.990 - 2.585** | **3.360 - 4.520** | **-30% til -45%** |
|
||||
|
||||
|
||||
---
|
||||
|
||||
### **Analyse**
|
||||
|
||||
1. **Boligudgifter**
|
||||
Bolig er markant billigere i Portugal med en besparelse på **33-50%** sammenlignet med Danmark. Lejepriserne i mindre byer er endnu lavere.
|
||||
|
||||
2. **Dagligvarer**
|
||||
Indkøb af mad og andre dagligvarer koster i gennemsnit **25-40% mindre** i Portugal, som også reflekteres i din kvittering.
|
||||
|
||||
3. **El og varme**
|
||||
Selvom elpriserne er høje i Portugal, er de stadig **25-40% lavere** end i Danmark, hvor opvarmningsomkostninger i vintermånederne er væsentligt højere.
|
||||
|
||||
4. **Transport og sundhed**
|
||||
Offentlig transport og sundhedsydelser er også væsentligt billigere i Portugal med besparelser på op til **50-66%**.
|
||||
|
||||
5. **Samlede omkostninger**
|
||||
Samlet set er leveomkostningerne i Portugal **30-45% lavere** end i Danmark. Dette giver et stort økonomisk råderum og mulighed for en bedre livskvalitet.
|
||||
|
||||
At bo i Portugal kan give betydelige besparelser på husleje, dagligvarer og generelle leveomkostninger. Selvom elpriserne kan være en udfordring, er det samlede billede væsentligt billigere end i Danmark, især med energieffektive løsninger.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Permanent ophold i Portugal
|
||||
description: Krav og praktiske trin for EU-borgere til permanent ophold
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:35:00 CET 2024
|
||||
summary: Registrering, NIF-nummer og socialsikring i Portugal
|
||||
favorite: false
|
||||
image: images/pic07.jpg
|
||||
category: Flytning
|
||||
tags: [Portugal, Permanent ophold, EU-borger, NIF-nummer, Socialsikring, Flytning]
|
||||
---
|
||||
|
||||
# Hvad kræves for at bo permanent i Portugal som dansk statsborger?
|
||||
|
||||
Som EU-borger er det relativt nemt at få permanent ophold i Portugal. Her er, hvad du skal have styr på:
|
||||
|
||||
1. **Registrering af adresse**: Du skal registrere dig som bosiddende i Portugal inden for de første 90 dage.
|
||||
2. **NIF-nummer**: Det portugisiske skattemæssige identifikationsnummer er nødvendigt for stort set alle økonomiske aktiviteter.
|
||||
3. **Socialsikringssystemet**: For at få adgang til sundhedssystemet og sociale ydelser skal du registrere dig i det portugisiske socialsikringssystem.
|
||||
4. **Arbejdsforhold**: En ansættelseskontrakt eller bevis på selvstændig virksomhed er ofte nødvendigt for at dokumentere din økonomiske stabilitet.
|
||||
|
||||
Det er en proces, der virker overskuelig på papiret, men fra det jeg har læst og hørt, kan der være lidt bureaukrati involveret. Hvis du planlægger at flytte, er det en god idé at sætte tid af til at få styr på papirarbejdet, så alt glider så nemt som muligt.
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Åbning af bankkonto i Portugal
|
||||
description: En guide til krav og fleksible løsninger for bankkonti
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:25:00 CET 2024
|
||||
summary: Krav for bankkonti og fleksible løsninger som Wise
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Bank og Økonomi
|
||||
tags: [Portugal, Bankkonto, NIF-nummer, Wise, Økonomi]
|
||||
---
|
||||
|
||||
# Hvordan åbner man en bankkonto i Portugal?
|
||||
|
||||
For at åbne en bankkonto i Portugal er der nogle få krav, du skal have styr på:
|
||||
- **NIF-nummer**: Dit skattemæssige identifikationsnummer, som er nødvendigt for stort set alle økonomiske aktiviteter i Portugal.
|
||||
- **Gyldig adresse**: Du skal kunne dokumentere din bopæl, enten i Portugal eller et andet land.
|
||||
|
||||
### Fleksible løsninger
|
||||
Hvis du gerne vil undgå høje gebyrer og samtidig have adgang til lokale eurokonti, anbefaler mange **Wise** (tidligere TransferWise). Wise er en god løsning til:
|
||||
- Internationale overførsler med lave gebyrer.
|
||||
- At få en lokal konto i euro, som fungerer godt i hverdagen.
|
||||
|
||||
Hvis du planlægger at blive i Portugal i længere tid, kan det være praktisk at kombinere en lokal bankkonto med en online bank som Wise for at få det bedste fra begge verdener.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Leveomkostninger på lavere indkomst i Portugal
|
||||
description: Hvordan lave udgifter gør livet muligt på en lavere indkomst
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:30:00 CET 2024
|
||||
summary: Lav bolig og billige varer gør livet overkommeligt
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Leveomkostninger
|
||||
tags: [Portugal, Leveomkostninger, Lav indkomst, Bolig, Mad, Transport]
|
||||
---
|
||||
|
||||
# Kan man leve godt på en lavere indkomst i Portugal?
|
||||
|
||||
Ja, alt tyder på, at man kan leve godt i Portugal, selv på en lavere indkomst. Det handler især om lave boligomkostninger og de generelt overkommelige priser på mad og transport.
|
||||
|
||||
### Lavere leveomkostninger
|
||||
- **Bolig**: Uden for de store byer som Lissabon og Porto kan huslejen være markant lavere.
|
||||
- **Mad og dagligvarer**: Lokale produkter som frugt, grøntsager og fisk er både friske og billige.
|
||||
- **Transport**: Offentlig transport er væsentligt billigere end i Danmark, og den er samtidig effektiv.
|
||||
|
||||
Hvis du kan acceptere et mere simpelt liv væk fra de mest populære områder, er Portugal et af de steder, hvor pengene rækker længere. Det gør landet til et attraktivt valg for dem, der ønsker at prioritere livskvalitet fremfor høje indkomster.
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: Flytte permanent til Portugal
|
||||
description: Praktiske trin for EU-borgere, der vil flytte til Portugal
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:40:00 CET 2024
|
||||
summary: Registrering, NIF-nummer og praktiske krav for at flytte
|
||||
favorite: false
|
||||
image: images/pic04.jpg
|
||||
category: Flytning
|
||||
tags: [Portugal, Flytning, NIF-nummer, EU-borger, Bosiddende, Bankkonto, Arbejde]
|
||||
---
|
||||
|
||||
# Hvad kræves for at flytte permanent til Portugal?
|
||||
|
||||
Som EU-borger er det relativt ukompliceret at flytte permanent til Portugal, men der er nogle formelle trin, du skal gennemføre:
|
||||
|
||||
1. **Registrering som bosiddende**: Inden for 90 dage efter ankomst skal du registrere dig som bosiddende i Portugal.
|
||||
2. **NIF-nummer (skattenummer)**: Dette er nødvendigt for stort set alt – at oprette en bankkonto, købe ejendom eller få arbejde.
|
||||
3. **Dokumentation for økonomisk aktivitet**: En ansættelseskontrakt, bevis på selvstændig virksomhed eller pensionsindkomst kan være nødvendigt for at bekræfte din økonomiske stabilitet.
|
||||
|
||||
### Praktiske råd
|
||||
- Sørg for at få styr på papirarbejdet hurtigt efter ankomst, da det kan spare dig for unødvendige problemer.
|
||||
- Overvej at få hjælp fra lokale rådgivere, hvis du støder på udfordringer med sprog eller bureaukrati.
|
||||
|
||||
Portugal byder på en forholdsvis smidig proces for EU-borgere, hvilket gør det til et oplagt valg for dem, der ønsker at starte et nyt liv i et andet land.
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: Fritidsaktiviteter i Portugal
|
||||
description: Aktiviteter og oplevelser i Portugals varierede landskab
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:50:00 CET 2024
|
||||
summary: Surfing, vandreture og strande – oplev Portugal
|
||||
favorite: false
|
||||
image: images/pic07.jpg
|
||||
category: Fritid
|
||||
tags: [Portugal, Fritid, Surfing, Vandreture, Strande, Aktiviteter, Vandsport]
|
||||
---
|
||||
|
||||
# Hvilke fritidsaktiviteter kan jeg nyde i Portugal?
|
||||
|
||||
Portugal er et fantastisk land for både aktive og afslappende fritidsaktiviteter. Landets varierede landskab betyder, at der er noget for enhver smag.
|
||||
|
||||
### Aktive oplevelser
|
||||
- **Surfing og vandsport**: Portugal er kendt for sine fremragende surfspots, især ved kystområder som **Nazaré**, **Ericeira** og **Peniche**.
|
||||
- **Vandreture**: De smukke bjergområder som **Serra da Estrela** byder på udfordrende og naturskønne ruter.
|
||||
- **Cykling**: Mange vælger at udforske landskabet på cykel, hvad enten det er langs kysten eller i det bakkede terræn.
|
||||
|
||||
### Afslappende aktiviteter
|
||||
- **Strandliv**: Portugals kyststrækning har nogle af Europas bedste strande, perfekte til afslapning og solbadning.
|
||||
- **Vinoplevelser**: Besøg vingårde i **Douro-dalen** og smag på lokalproduceret vin.
|
||||
- **Kultur og historie**: Tag på opdagelse i charmerende byer som **Sintra**, **Óbidos** og **Évora**.
|
||||
|
||||
Uanset om du er til eventyr i naturen eller bare vil slappe af med udsigt over Atlanterhavet, har Portugal masser at byde på.
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
name: Expat-fællesskaber i Portugal
|
||||
description: En guide til expat-netværk og ressourcer i Portugal
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:55:00 CET 2024
|
||||
summary: Netværk og fællesskaber for expats i Lissabon, Porto og Algarve
|
||||
favorite: false
|
||||
image: images/pic07.jpg
|
||||
category: Fællesskab
|
||||
tags: [Portugal, Expat, Lissabon, Porto, Algarve, Netværk, Ressourcer]
|
||||
---
|
||||
|
||||
# Findes der expat-fællesskaber i Portugal?
|
||||
|
||||
Ja, der findes mange expat-fællesskaber i Portugal, særligt i områder som **Lissabon**, **Porto** og **Algarve**. De giver social støtte, netværksmuligheder og praktisk hjælp til at tilpasse sig livet i et nyt land.
|
||||
|
||||
Uanset om du leder efter sociale arrangementer, professionelle kontakter eller praktiske råd, findes der mange gode ressourcer.
|
||||
|
||||
## Populære expat-fællesskaber og platforme
|
||||
|
||||
### {{ link_to(title="Internations Portugal", url="https://www.internations.org/portugal-expats") }}
|
||||
Internations er en af de største internationale platforme for expats og tilbyder:
|
||||
- Sociale arrangementer.
|
||||
- Professionelle netværk i byer som **Lissabon** og **Porto**.
|
||||
|
||||
### {{ link_to(title="Meetup - Expat Groups in Portugal", url="https://www.meetup.com/topics/expat/pt/") }}
|
||||
En global platform, hvor expats kan finde lokale grupper i Portugal. Eksempler inkluderer:
|
||||
- {{ link_to(title="Lisbon Digital Nomads Meetup", url="https://www.meetup.com/Lisbon-Digital-Nomads/") }}: Sociale og arbejdsrelaterede arrangementer i **Lissabon**.
|
||||
- {{ link_to(title="Porto Expats Meetup", url="https://www.meetup.com/Porto-Expats/") }}: Netværksgrupper for expats i **Porto**.
|
||||
- {{ link_to(title="Algarve Social & Community Meetup", url="https://www.meetup.com/topics/algarve/") }}: Sociale arrangementer i **Algarve**.
|
||||
|
||||
### Facebook-grupper
|
||||
Facebook er et godt sted at finde expat-grupper. Nogle populære grupper er:
|
||||
- {{ link_to(title="Expats in Portugal", url="https://www.facebook.com/groups/expatsinportugal") }}
|
||||
- {{ link_to(title="Expats in Lisbon", url="https://www.facebook.com/groups/expatslisbon") }}
|
||||
- {{ link_to(title="Expats in Algarve", url="https://www.facebook.com/groups/expatsinalgarve") }}
|
||||
- {{ link_to(title="Danskere i Portugal", url="https://www.facebook.com/groups/danskereportugal") }} – Her må jeg fremhæve, hvor venlige og hjælpsomme folk er!
|
||||
|
||||
### {{ link_to(title="Expat.com Portugal", url="https://www.expat.com/en/destination/europe/portugal/") }}
|
||||
Expat.com tilbyder:
|
||||
- {{ link_to(title="Praktiske guider og fora", url="https://www.expat.com/en/guide/europe/portugal/") }}.
|
||||
- Netværksmuligheder med andre udlændinge i Portugal.
|
||||
|
||||
### Lokale netværksgrupper
|
||||
Mange mindre fællesskaber blomstrer også op lokalt:
|
||||
- {{ link_to(title="Lisbon Digital Nomads", url="https://www.facebook.com/groups/lisbondigitalnomads") }}: En gruppe for digitale nomader og remote arbejdere.
|
||||
- {{ link_to(title="Porto Startup Scene", url="https://www.facebook.com/groups/portostartupscene") }}: Et netværk for iværksættere og tech-interesserede i **Porto**.
|
||||
|
||||
---
|
||||
|
||||
Portugal har noget for alle, og hvis du søger et fællesskab, vil du med sikkerhed finde det, der passer til dine behov.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Fordele og ulemper ved at flytte til Portugal
|
||||
description: De største fordele og ulemper ved at flytte til Portugal
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:10:00 CET 2024
|
||||
summary: Lavere leveomkostninger, mildt klima og bureaukratiske udfordringer
|
||||
favorite: false
|
||||
image: images/pic01.jpg
|
||||
category: Flytning
|
||||
tags: [Portugal, Fordele, Ulemper, Leveomkostninger, Klima, Bureaukrati, Sprog]
|
||||
---
|
||||
|
||||
# Hvad er fordelene og ulemperne ved at flytte til Portugal?
|
||||
|
||||
Jeg har samlet mine tanker og observationer om de største fordele og ulemper ved at flytte til Portugal, baseret på den research jeg har lavet.
|
||||
|
||||
### Fordele
|
||||
- **Lavere leveomkostninger**: Sammenlignet med Danmark er det billigere at bo, spise og leve i Portugal. Det gælder især uden for storbyerne.
|
||||
- **Mildt klima**: Med mange solskinsdage og milde vintre er vejret i Portugal et stort plus, især hvis man er træt af den danske kulde.
|
||||
- **Frisk start og livsstil**: Portugal tilbyder en mere afslappet livsstil, som jeg tror, mange kunne have glæde af.
|
||||
|
||||
### Ulemper
|
||||
- **Bureaukrati**: Der er mange historier om, at bureaukratiske processer kan være langsomme og forvirrende, især hvis man ikke taler portugisisk.
|
||||
- **Sproglige barrierer**: Selvom mange portugisere taler engelsk i de større byer, kan det være en udfordring at klare sig i hverdagen uden at kunne sproget.
|
||||
|
||||
Portugal virker som et land med mange muligheder, men det kræver forberedelse og tålmodighed at få det hele til at fungere.
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
name: Økonomiske konsekvenser ved at flytte til Portugal
|
||||
description: Fordele og ulemper ved økonomi og skatteforhold i Portugal
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:20:00 CET 2024
|
||||
summary: Lavere omkostninger og ændringer i skatteforhold ved flytning
|
||||
favorite: false
|
||||
image: images/pic01.jpg
|
||||
category: Økonomi
|
||||
tags: [Portugal, Økonomi, Skat, Leveomkostninger, Boligskatter, Danske ydelser]
|
||||
---
|
||||
|
||||
# Hvilke økonomiske konsekvenser er der for mig ved at flytte til Portugal?
|
||||
|
||||
Jeg har gjort mig nogle tanker og undersøgelser om, hvad det økonomisk betyder at flytte til Portugal. Her er, hvad jeg er kommet frem til:
|
||||
|
||||
### Fordele
|
||||
- **Lavere leveomkostninger**: De månedlige udgifter til bolig, mad og transport er typisk lavere end i Danmark, især hvis man vælger at bo uden for de største byer.
|
||||
- **Skatteforhold**: Afhængigt af ens situation kan skatten være lavere i Portugal, men det kræver en vurdering af de portugisiske regler sammenlignet med de danske.
|
||||
|
||||
### Ulemper
|
||||
- **Danske ydelser**: Hvis man fjerner sin danske adresse og bliver fuldt skattepligtig i Portugal, risikerer man at miste adgang til visse danske ydelser.
|
||||
- **Boligskatter**: Selvom man kan spare danske boligskatter ved at fjerne sin adresse, skal man være opmærksom på de portugisiske skatter som **IMI** (ejendomsskat) og **IMT** (købsskat).
|
||||
|
||||
### Overvejelser
|
||||
Hvis man planlægger at flytte, er det vigtigt at:
|
||||
1. Undersøge de nøjagtige skatteforhold og indberetningskrav i Portugal.
|
||||
2. Tage højde for, hvad det betyder at opgive sin danske bopæl, både økonomisk og administrativt.
|
||||
|
||||
Alt i alt ser det ud til, at Portugal kan give nogle økonomiske fordele, men det kræver planlægning og forståelse for, hvordan det påvirker skatten og andre økonomiske forhold.
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
name: Uddannelsesmuligheder i Portugal for børn
|
||||
description: Muligheder for gymnasier og internationale skoler i Portugal
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:15:00 CET 2024
|
||||
summary: Internationale skoler og portugisiske gymnasier for børn
|
||||
favorite: false
|
||||
image: images/pic02.jpg
|
||||
category: Uddannelse
|
||||
tags: [Portugal, Uddannelse, Gymnasier, Internationale skoler, STX, Børn]
|
||||
---
|
||||
|
||||
# Hvad er uddannelsesmulighederne i Portugal for mine børn?
|
||||
|
||||
Jeg har brugt tid på at undersøge, hvilke uddannelsesmuligheder der findes i Portugal, særligt fordi min datter på 16 år overvejer at tage en STX (studentereksamen) i Danmark. Her er, hvad jeg har fundet:
|
||||
|
||||
### Portugisiske gymnasier
|
||||
Portugals gymnasiale uddannelser minder på mange måder om det danske system, men både pensum og undervisningssproget er naturligvis anderledes. Hvis man ikke taler portugisisk flydende, kan det være en udfordring, især i begyndelsen.
|
||||
|
||||
### Internationale skoler
|
||||
Et oplagt alternativ er internationale skoler, der tilbyder undervisning på engelsk og ofte følger internationale curriculum som **International Baccalaureate (IB)**.
|
||||
Nogle fordele ved internationale skoler inkluderer:
|
||||
- Mulighed for at fortsætte på et globalt anerkendt pensum.
|
||||
- God støtte til elever, der er nye i landet.
|
||||
- Mange skoler har en stor blanding af elever fra forskellige nationaliteter.
|
||||
|
||||
### Overvejelser
|
||||
Når man vælger mellem portugisiske gymnasier og internationale skoler, er det værd at tænke over:
|
||||
- Sprogbarrierer og tilpasning til nyt pensum.
|
||||
- Økonomien – internationale skoler kan være dyrere.
|
||||
- Hvilken uddannelsesretning der passer bedst til fremtidige planer.
|
||||
|
||||
Der er mange gode muligheder, men det kræver lidt research at finde det, der passer bedst til ens børn og familiens behov.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Livsstilen i Portugal
|
||||
description: Livsstil i Portugal med fokus på klima, kultur og dagligdag
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:10:00 CET 2024
|
||||
summary: Et mildt klima, god mad og en afslappet atmosfære kendetegner Portugal
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Kultur
|
||||
tags: [Portugal, Livsstil, Klima, Mad, Vin, Udendørsaktiviteter]
|
||||
---
|
||||
|
||||
# Hvordan er livsstilen i Portugal?
|
||||
|
||||
Livsstilen i Portugal er præget af et mildt klima, en afslappet atmosfære og overkommelige leveomkostninger – især sammenlignet med Danmark.
|
||||
|
||||
### Klima og natur
|
||||
Portugal byder på over 300 solskinsdage om året, hvilket giver rig mulighed for udendørsaktiviteter som:
|
||||
- Vandreture i bjergområder som **Serra da Estrela**.
|
||||
- Afslapning på de mange smukke strande langs **Algarvekysten**.
|
||||
- Cykelture i det varierede landskab.
|
||||
|
||||
### Mad og vin
|
||||
Mad og vin er en central del af den portugisiske kultur:
|
||||
- Frisk fisk og skaldyr er en delikatesse langs kysten.
|
||||
- Lokale vine, især fra **Douro-dalen**, er kendt verden over.
|
||||
- Måltider nydes i et afslappet tempo, ofte sammen med familie og venner.
|
||||
|
||||
### Leveomkostninger og dagligdag
|
||||
Portugal tilbyder en mere overkommelig hverdag, hvor pengene rækker længere, især uden for storbyerne. Samtidig er atmosfæren rolig og indbydende, hvilket gør det lettere at finde en god balance mellem arbejde og fritid.
|
||||
|
||||
Portugal er et land, hvor kultur, natur og en høj livskvalitet går hånd i hånd – en ideel destination for dem, der søger en enklere og mere nydelsesfuld tilværelse.
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: Priser på mad og dagligvarer i Portugal
|
||||
description: Sammenligning af madpriser og dagligvarer mellem Portugal og Danmark
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:15:00 CET 2024
|
||||
summary: Lokale produkter som grøntsager og fisk er billigere i Portugal
|
||||
favorite: false
|
||||
image: images/pic10.jpg
|
||||
category: Leveomkostninger
|
||||
tags: [Portugal, Mad, Dagligvarer, Priser, Lokale produkter, Importvarer]
|
||||
---
|
||||
|
||||
# Hvordan er priserne på mad og dagligvarer i Portugal?
|
||||
|
||||
Priserne på mad og dagligvarer i Portugal er generelt lavere end i Danmark, især når det kommer til lokale produkter.
|
||||
|
||||
### Billigere lokale produkter
|
||||
Portugal har et stort udbud af friske, lokalt producerede fødevarer, som er både billige og af høj kvalitet:
|
||||
- **Grøntsager og frugt**: Friske råvarer som tomater, appelsiner og kartofler koster betydeligt mindre.
|
||||
- **Fisk og skaldyr**: Som et kystland har Portugal et stort udvalg af friskfanget fisk til lave priser.
|
||||
- **Vin og olivenolie**: Portugisiske vine og lokal olivenolie er meget overkommelige sammenlignet med danske priser.
|
||||
|
||||
### Dyrere importvarer
|
||||
Visse importvarer og specialprodukter, som man måske er vant til i Danmark, kan være dyrere:
|
||||
- Internationale mærkevarer.
|
||||
- Specielle ingredienser, som ikke produceres lokalt.
|
||||
|
||||
Hvis man holder sig til lokale produkter, er madbudgettet i Portugal markant lavere end i Danmark. Det er nemt at spise godt, sundt og billigt i Portugal, især hvis man benytter sig af lokale markeder og små butikker frem for supermarkeder med mange importerede varer.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Leveomkostninger i Portugal vs Danmark
|
||||
description: Sammenligning af bolig, dagligvarer og transport mellem Portugal og Danmark
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:00:00 CET 2024
|
||||
summary: Bolig, dagligvarer og transport er billigere i Portugal
|
||||
favorite: false
|
||||
image: images/pic09.jpg
|
||||
category: Leveomkostninger
|
||||
tags: [Portugal, Danmark, Leveomkostninger, Bolig, Dagligvarer, Transport]
|
||||
---
|
||||
|
||||
# Er leveomkostningerne i Portugal lavere end i Danmark?
|
||||
|
||||
Alt peger på, at leveomkostningerne i Portugal er lavere sammenlignet med Danmark.
|
||||
|
||||
### Hvad er billigere i Portugal?
|
||||
- **Bolig**: Udgifter til husleje og køb af bolig er væsentligt lavere, især uden for storbyer som **Lissabon** og **Porto**.
|
||||
- **Dagligvarer**: Lokale fødevarer som grøntsager, fisk og vin er billigere og ofte af høj kvalitet.
|
||||
- **Transport**: Offentlig transport som busser, metro og tog koster en brøkdel af, hvad det gør i Danmark.
|
||||
|
||||
### Hvad kan være dyrere?
|
||||
- **Elregninger**: Elektricitet i Portugal kan være relativt dyrt, især i vinterperioden.
|
||||
- **Importvarer**: Visse produkter, der importeres fra udlandet, kan være dyrere sammenlignet med Danmark.
|
||||
|
||||
Portugal tilbyder generelt en lavere leveomkostning, hvilket betyder, at man kan få mere for pengene – især hvis man vælger at bosætte sig uden for de dyreste områder. Det gør landet til et oplagt valg, hvis man ønsker at kombinere økonomisk overskud med en høj livskvalitet.
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: Omkostninger ved at bo i Portugal vs Danmark
|
||||
description: Sammenligning af boligudgifter, mad og transport mellem de to lande
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:05:00 CET 2024
|
||||
summary: Lavere boligudgifter, mad og transport i Portugal
|
||||
favorite: false
|
||||
image: images/pic06.jpg
|
||||
category: Leveomkostninger
|
||||
tags: [Portugal, Danmark, Bolig, Mad, Transport, Omkostninger]
|
||||
---
|
||||
|
||||
# Hvad koster det at bo i Portugal sammenlignet med Danmark?
|
||||
|
||||
Portugal har generelt lavere leveomkostninger end Danmark, men der er forskelle afhængigt af, hvor man bosætter sig, og hvilken livsstil man vælger.
|
||||
|
||||
### Lavere boligudgifter
|
||||
- Uden for storbyer som **Lissabon** og **Porto** er boligudgifterne markant lavere.
|
||||
- Selvom boligpriserne i storbyerne stiger, er de stadig mere overkommelige sammenlignet med danske forhold.
|
||||
|
||||
### Mad og dagligvarer
|
||||
- Lokale fødevarer som grøntsager, fisk og vin er billigere end i Danmark.
|
||||
- Importvarer og specialprodukter kan dog være dyrere, afhængigt af region og butik.
|
||||
|
||||
### Transport
|
||||
- Offentlig transport som metro, tog og busser er langt billigere og meget udbredt i Portugal.
|
||||
- Brændstofpriserne er ofte på niveau med eller lidt højere end i Danmark.
|
||||
|
||||
Hvis du er åben for at bo uden for de dyreste områder og tilpasse din livsstil, er Portugal et økonomisk fordelagtigt sted at bo. Leveomkostningerne er lavere, og du får typisk mere for pengene, især når det gælder bolig og dagligvarer.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Pendling mellem Danmark og Portugal
|
||||
description: Overvejelser om rejseudgifter, arbejdstid og praktiske udfordringer ved pendling
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:25:00 CET 2024
|
||||
summary: Rejseudgifter og tidsplanlægning ved pendling mellem Danmark og Portugal
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Pendling
|
||||
tags: [Portugal, Danmark, Pendling, Rejseudgifter, Arbejdstid, Fritid, Økonomi]
|
||||
---
|
||||
|
||||
# Hvordan håndterer jeg pendling mellem Danmark og Portugal?
|
||||
|
||||
Mit mål er at arbejde i Danmark hver anden uge og bruge resten af tiden i Portugal. Det giver en unik mulighed for at kombinere arbejde med en ny livsstil, men det kræver også en del planlægning.
|
||||
|
||||
### Praktiske aspekter ved pendling
|
||||
- **Rejseudgifter**: Flybilletter og lokal transport mellem hjem og arbejde kan hurtigt løbe op. Jeg planlægger at undersøge lavprisselskaber og tilbud for at holde omkostningerne nede.
|
||||
- **Tidsplanlægning**: Det kræver nøje planlægning at balancere arbejdstid i Danmark med fritid og dagligdag i Portugal. Jeg arbejder på at skabe en fast rytme for at minimere stress.
|
||||
- **Økonomiske konsekvenser**: Udgifter til rejser og ophold i Danmark skal vejes op mod de lavere leveomkostninger i Portugal.
|
||||
|
||||
### Overvejelser
|
||||
Pendling mellem to lande er ikke kun praktisk krævende, men også en livsstilsændring. For mig handler det om at finde en balance, hvor rejseplaner ikke går ud over livskvaliteten, og hvor økonomien stadig hænger sammen. Det er vigtigt at have realistiske forventninger til, hvad pendling kræver, både fysisk og økonomisk.
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
name: Nattelivet i Portugal
|
||||
description: En guide til Portugals varierede og festlige natteliv
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:25:00 CET 2024
|
||||
summary: Oplev barer, klubber og musikscener i Lissabon og Porto
|
||||
favorite: false
|
||||
image: images/pic04.jpg
|
||||
category: Kultur
|
||||
tags: [Portugal, Natteliv, Lissabon, Porto, Barer, Klub, Musik]
|
||||
---
|
||||
|
||||
# Hvordan er nattelivet i Portugal?
|
||||
|
||||
Nattelivet i Portugal er både levende og mangfoldigt med noget for enhver smag. Særligt i byer som **Lissabon** og **Porto** er der rig mulighed for at opleve en festlig og international atmosfære.
|
||||
|
||||
### Barer og klubber
|
||||
- **Lissabon**: Bairro Alto og Cais do Sodré er to af de mest populære områder. Bairro Alto er kendt for sine små, hyggelige barer, mens Cais do Sodré byder på større klubber som **Lux Fragil**, en af Portugals mest kendte natklubber.
|
||||
- **Porto**: Området omkring Galerias de Paris er centrum for nattelivet i Porto, hvor barerne ligger tæt og festen fortsætter til langt ud på natten.
|
||||
|
||||
### Musik og live-scener
|
||||
Portugal har en stærk musikscene med alt fra live-fado til moderne musik:
|
||||
- **Fado i Lissabon**: Oplev den traditionelle fado-musik i en af byens intime fado-restauranter.
|
||||
- **Koncerter**: Store koncertsteder i både Lissabon og Porto tiltrækker internationale kunstnere året rundt.
|
||||
|
||||
### Strandbarer og afslappede vibes
|
||||
Langs kysten, især i Algarve, finder man strandbarer, hvor man kan nyde drinks med fødderne i sandet og en smuk solnedgang.
|
||||
|
||||
### Konklusion
|
||||
Portugals natteliv har alt fra rolige barer og livemusik til energiske natklubber. Uanset om du vil danse natten væk i en storby eller nyde en cocktail med udsigt over havet, har Portugal noget at byde på.
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
name: Portugals unikke kultur
|
||||
description: Portugals særlige kulturarv med fokus på musik, mad og historie
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:20:00 CET 2024
|
||||
summary: Portugals fado-musik, historie og madkultur gør landet unikt
|
||||
favorite: false
|
||||
image: img/pic04.jpg
|
||||
category: Kultur
|
||||
tags: [Portugal, Kultur, Fado, Musik, Historie, Mad, Monumenter]
|
||||
---
|
||||
|
||||
# Hvad gør Portugal kulturelt unikt?
|
||||
|
||||
## Oplev Kulturlivet i Lissabon: Biografer, Teater, Mad og Historie
|
||||
|
||||
Lissabon er en by, der har det hele – fra intime biografer og smukke teatre til gastronomiske oplevelser og storslåede historiske monumenter. Hvis du er vant til kulturlivet i København, vil du opdage både genkendelige tilbud og unikke oplevelser, som gør Lissabon til noget ganske særligt.
|
||||
|
||||
### En aften i biografen – fra nostalgiske sale til moderne filmoplevelser
|
||||
|
||||
Ligesom i København, hvor du finder både små biografer som Grand Teatret og større kæder som Nordisk Film Biografer, tilbyder Lissabon noget for enhver filmelsker.
|
||||
|
||||
### En aften i biografen – fra nostalgiske sale til moderne filmoplevelser
|
||||
|
||||
Ligesom i København, hvor du finder både små biografer som Grand Teatret og større kæder som Nordisk Film Biografer, tilbyder Lissabon noget for enhver filmelsker.
|
||||
|
||||
- **Cinemateca Portuguesa**: Hvis du elsker en biograf med sjæl, er Cinemateca stedet. Det minder lidt om Cinemateket i København, men her ligger fokus ofte på portugisiske og internationale klassikere. Biografen viser film fra hele verden og arrangerer ofte retrospektiver. [Se programmet her](https://www.cinemateca.pt/).
|
||||
- **Cinema São Jorge**: Centralt placeret på Avenida da Liberdade og kendt for at være vært for filmfestivaler. Her får du følelsen af både kulturhus og biograf på én gang. Det kan minde om Park Bio i København med et bredt og kvalitetspræget udvalg af film. [Læs mere her](https://www.cinemasaojorge.pt/).
|
||||
- **UCI El Corte Inglés**: Den moderne biografoplevelse med de nyeste blockbusters. Sammenligneligt med Palads i København, men med en lidt mere luksuriøs oplevelse i selve komplekset.
|
||||
|
||||
|
||||
|
||||
{{ slider(options={"width": 500, "height": 500}, images=["158170.jpg", "Fachada_SaoJorge.jpg"]) }}
|
||||
|
||||
Biografbesøg i Lissabon er en afslappet måde at tilbringe tid med familien. Jeg nyder selv at tage afsted med Erika for at dele både film og hygge.
|
||||
|
||||
### Teatrets verden: Fra klassikere til moderne eksperimenter
|
||||
|
||||
Hvor København har Det Kongelige Teater og mindre eksperimentelle scener som Husets Teater, finder du i Lissabon et lignende spektrum med tilbud, der rummer både det storslåede og det intime.
|
||||
• Teatro Nacional D. Maria II: Byens mest ikoniske teater ligger smukt placeret på Rossio-pladsen. Bygningen er storslået – på linje med Det Kongelige Teater i København – og her opføres både portugisiske klassikere og internationale opsætninger. Læs mere her.
|
||||
• Teatro São Luiz: Centralt og intimt, perfekt til en alsidig oplevelse. Her præsenteres teater, musik og dans for både voksne og familier. Det minder om Skuespilhuset med sit smukke interiør og blandede program. Læs mere her.
|
||||
• Teatro Aberto: Kendt for at præsentere moderne og nyskabende forestillinger. Hvis du elsker små eksperimentelle teatre som Teater Grob, vil Teatro Aberto være et hit. Se mere her.
|
||||
|
||||
Teatrene i Lissabon gør det muligt at opleve portugisisk kultur og kreativitet på tæt hold – ofte til meget overkommelige priser sammenlignet med København.
|
||||
|
||||
### Smagen af Portugal: Unikke gastronomiske oplevelser
|
||||
|
||||
Maden er uden tvivl noget af det, der adskiller Lissabon fra København. Her handler det om en kombination af friske råvarer, historiske traditioner og autentiske smagsoplevelser.
|
||||
• Bacalhau: Den portugisiske nationalret – tørret og saltet torsk – serveres på utallige måder. I København er vi vant til stegt fisk og smørrebrød, men bacalhau er en unik oplevelse, der skal prøves.
|
||||
• Pastéis de Nata: Lissabons berømte vaniljetærter kan ikke sammenlignes med noget i Danmark. Besøg Pastéis de Belém i Belém-kvarteret, hvor de originale opskrifter stadig holdes hemmelige. Læs mere her.
|
||||
• Cervejaria Ramiro: Denne klassiske skaldyrsrestaurant er et paradis for fiskelskere. Tænk Fiskebaren i Kødbyen i København, men med endnu mere autentisk og rustik atmosfære. Læs mere her.
|
||||
|
||||
### Historiske højdepunkter: Lissabon versus København
|
||||
|
||||
Lissabon er fyldt med historie, og mens København byder på smukke steder som Rosenborg Slot og Nyhavn, har Lissabon sine egne historiske perler.
|
||||
• Jerónimos-klosteret og Torre de Belém: To unikke UNESCO-steder, der vidner om Portugals storhedstid under opdagelsesrejserne. Intet i København kan helt sammenlignes med disse monumenter. Læs mere her.
|
||||
• Castelo de São Jorge: Dette middelalderlige slot på toppen af byen giver en betagende udsigt. Det er lidt som at besøge Kronborg – men med en helt anden sydlandsk atmosfære.
|
||||
• Alfama-kvarteret: Oplev smalle brostensgader og historisk charme på en måde, som minder lidt om Christianshavn, men med mere farve og liv.
|
||||
|
||||
### Musikken: Den uundgåelige fado og moderne vibes
|
||||
|
||||
Musik spiller en central rolle i portugisisk kultur, og fado er noget, du ikke finder magen til i København. Fado er en melankolsk musikgenre, der ofte fremføres i intime restauranter og små scener i Alfama. Prøv steder som Clube de Fado eller O Faia.
|
||||
|
||||
For mere moderne musik kan du besøge Coliseu dos Recreios og Altice Arena, hvor både lokale og internationale kunstnere optræder året rundt.
|
||||
|
||||
### Livet i Lissabon: Kultur, sjæl og afslapning
|
||||
|
||||
Lissabon føles på mange måder som København – med fokus på kultur, kvalitet og hygge – men her tilføjes en sydlandsk lethed og varme. Uanset om du tager i biografen, nyder en teaterforestilling eller smager på den lokale mad, vil du opleve, at kulturen er lige så rig og mangfoldig som byen selv.
|
||||
|
||||
Samtidig er mange oplevelser billigere end i København, og her er en atmosfære, der er svær at finde nordpå: Lissabon har en unik blanding af tradition og modernitet, som gør den til en by, der bliver ved med at fascinere.
|
||||
|
||||
|
||||
### Lidt billeder
|
||||
|
||||
|
||||
{{ slider(options={"width": 500, "height": 500}, images=["158170.jpg", "Fachada_SaoJorge.jpg"]) }}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 34 MiB |
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: Livskvalitet i Portugal
|
||||
description: Høj livskvalitet i Portugal med sol, strande og en god balance mellem arbejde og fritid
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:35:00 CET 2024
|
||||
summary: Sol, strande og en afslappet livsstil giver høj livskvalitet i Portugal
|
||||
favorite: false
|
||||
image: images/pic05.jpg
|
||||
category: Livskvalitet
|
||||
tags: [Portugal, Livskvalitet, Solskinsdage, Strande, Balance, Arbejde, Fritid]
|
||||
---
|
||||
|
||||
# Hvorfor er livskvaliteten høj i Portugal?
|
||||
|
||||
Portugal tilbyder en livskvalitet, der kan være svær at matche andre steder i Europa. Det handler ikke kun om vejret, men også om den afslappede livsstil og mulighederne for at nyde livet.
|
||||
|
||||
### Over 300 solskinsdage om året
|
||||
Med et mildt klima og mere end **300 solskinsdage** om året er vejret i Portugal en stor faktor for livskvaliteten. De lange solrige perioder skaber gode rammer for udendørsaktiviteter hele året.
|
||||
|
||||
### Smukke strande og natur
|
||||
Portugal byder på nogle af de smukkeste strande i Europa, især langs **Algarvekysten** og **Costa Vicentina**. For naturelskere er der også:
|
||||
- Vandreture i bjergene.
|
||||
- Udforskning af nationalparker.
|
||||
- Cykelruter gennem det varierede landskab.
|
||||
|
||||
### Balance mellem arbejde og fritid
|
||||
Den portugisiske kultur lægger vægt på at nyde livet, og tempoet er generelt mere afslappet end i Danmark. Folk prioriterer tid med familie og venner, og det er lettere at finde en god balance mellem arbejde og fritid.
|
||||
|
||||
### Lavere leveomkostninger
|
||||
Den høje livskvalitet forstærkes af, at pengene rækker længere. Mad, bolig og transport er billigere, hvilket giver mulighed for at leve komfortabelt uden at gå på kompromis med livsstilen.
|
||||
|
||||
Portugal kombinerer sol, smuk natur og en rolig livsstil med lavere leveomkostninger. Det skaber et miljø, hvor det er lettere at nyde livet og finde en sund balance mellem arbejde og fritid.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Portugiserne som mennesker
|
||||
description: Portugisernes venlighed og gæstfrihed gør det let at føle sig velkommen
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:30:00 CET 2024
|
||||
summary: Venlige og imødekommende mennesker gør Portugal til et varmt sted at bo
|
||||
favorite: false
|
||||
image: images/pic02.jpg
|
||||
category: Kultur
|
||||
tags: [Portugal, Portugisere, Gæstfrihed, Venlighed, Tilflyttere, Kultur]
|
||||
---
|
||||
|
||||
# Hvordan er portugiserne som mennesker?
|
||||
|
||||
En af de ting, der har gjort størst indtryk på mig under min research om Portugal, er portugisernes venlighed og gæstfrihed.
|
||||
|
||||
### Venlige og imødekommende
|
||||
Portugiserne er kendt for deres rolige og varme personlighed. De møder både tilflyttere og turister med åbne arme, hvilket gør det langt lettere at føle sig velkommen i et nyt land.
|
||||
|
||||
### Hjælpsomme og nysgerrige
|
||||
Jeg har bemærket, at portugiserne ofte er hjælpsomme og nysgerrige over for nye mennesker. Selvom ikke alle taler engelsk flydende, vil de gerne gøre en indsats for at hjælpe og forstå.
|
||||
|
||||
### Traditioner og fællesskab
|
||||
Den portugisiske kultur lægger vægt på fællesskab og familie. Det kan ses i de lokale byfester, markeder og den afslappede måde, folk samles på caféer og restauranter. Det skaber en følelse af nærhed og samhørighed.
|
||||
|
||||
Hvis du overvejer at flytte til Portugal, kan du se frem til at møde mennesker, der er venlige, åbne og hjælpsomme. Portugisernes gæstfrihed gør det nemmere at skabe relationer og føle sig hjemme, selv som nytilflytter.
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
name: Skattesystemet i Portugal
|
||||
description: En guide til Portugals skattesystem, NHR-programmets afskaffelse og dobbeltbeskatning
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:40:00 CET 2024
|
||||
summary: Information om Portugals skat, NHR-ordningens afskaffelse og lokale skatter
|
||||
favorite: false
|
||||
image: images/pic07.jpg
|
||||
category: SKAT
|
||||
tags: [Portugal, SKAT, NHR, Dobbeltbeskatning, IMI, IMT, Indkomstskat]
|
||||
---
|
||||
|
||||
# En lille smule om SKAT
|
||||
|
||||
## Portugal og skattesystemet for tilflyttere
|
||||
|
||||
Portugal har længe været attraktivt for tilflyttere, særligt på grund af det tidligere **Non-Habitual Resident (NHR)**-skatteregime. Programmet blev introduceret i **2009** for at tiltrække højtkvalificerede arbejdstagere, pensionister og investorer med skattefordele i op til 10 år.
|
||||
|
||||
### NHR-programmet er afskaffet
|
||||
I oktober 2024 blev NHR-programmet officielt afskaffet for nye ansøgere. For eksisterende deltagere fortsætter ordningen frem til det oprindelige udløb.
|
||||
|
||||
### Hvordan fungerede NHR?
|
||||
NHR-ordningen tilbød:
|
||||
- En **fast indkomstskat på 20%** for visse højtkvalificerede erhverv som IT-specialister, læger og ingeniører.
|
||||
- Skattefritagelse på visse udenlandske indkomster afhængigt af dobbeltbeskatningsaftaler mellem Portugal og bopælslandet.
|
||||
|
||||
Regeringens beslutning om at afskaffe programmet kom som følge af bekymringer om økonomisk ulighed.
|
||||
|
||||
---
|
||||
|
||||
## Det portugisiske skattesystem
|
||||
|
||||
Portugal anvender et **progressivt skattesystem**:
|
||||
- **14,5%** for årlige indkomster op til 7.479 EUR.
|
||||
- Skatten stiger gradvist til en **toprate på 48%** for indkomster over 78.834 EUR.
|
||||
|
||||
Derudover skal man være opmærksom på lokale skatter:
|
||||
- **IMI (ejendomsskat)**: Varierer mellem 0,3% og 0,45% af boligens værdi årligt.
|
||||
- **IMT (købsskat)**: En engangsafgift, der betales ved køb af fast ejendom.
|
||||
|
||||
---
|
||||
|
||||
## Dobbeltbeskatning og skattemæssig bopæl
|
||||
|
||||
Danmark og Portugal har en **dobbeltbeskatningsaftale**, der sikrer, at man ikke betaler skat to gange. For at undgå dobbeltbeskatning kræves det:
|
||||
1. At du dokumenterer din primære bopæl i Portugal (f.eks. ved at afmelde dig CPR-registeret i Danmark).
|
||||
2. At du opgør, hvor du har din "tætteste økonomiske og personlige tilknytning".
|
||||
|
||||
Når du flytter permanent og ikke bevarer en dansk adresse, bliver du som udgangspunkt kun skattepligtig i Portugal. Dog gælder dansk kildeindkomst stadig, såsom udlejning af ejendomme eller udbytter.
|
||||
|
||||
---
|
||||
|
||||
## Fordele ved at flytte til Portugal
|
||||
|
||||
Udover skatteforholdene tilbyder Portugal:
|
||||
- **Lavere leveomkostninger** sammenlignet med nordeuropæiske lande.
|
||||
- Et **mildt klima** og høj livskvalitet.
|
||||
- Et velfungerende sundheds- og uddannelsessystem, som tilflyttere også kan benytte.
|
||||
|
||||
Skattesystemet kræver, at man sætter sig grundigt ind i reglerne, men for mange er fordelene ved at bo i Portugal stadig til at få øje på.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: Internationale skoler i Porto
|
||||
description: En guide til internationale skoler i Porto med britisk, fransk og tysk curriculum
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:30:00 CET 2024
|
||||
summary: Oversigt over internationale skoler i Porto med fokus på curriculum og faciliteter
|
||||
favorite: false
|
||||
image: images/pic08.jpg
|
||||
category: Uddannelse
|
||||
tags: [Portugal, Porto, Skoler, International, British School, Lycée Français, Deutsche Schule]
|
||||
---
|
||||
|
||||
# Hvilke skoler ser fede ud i Porto?
|
||||
|
||||
I min research har jeg fundet flere gode internationale skoler i Porto. De tilbyder forskellige curriculums og sprogkombinationer, som gør dem attraktive for tilflyttere med børn.
|
||||
|
||||
---
|
||||
|
||||
### Oporto British School
|
||||
- **Adresse**: {{ link_to(title="Rua Cerca 326, 4150-201 Porto", url="http://www.obs.edu.pt") }}
|
||||
- **Beskrivelse**: Grundlagt i **1894**, hvilket gør den til den ældste britiske skole i Portugal. Skolen tilbyder undervisning fra **3 til 18 år** og følger det britiske curriculum. For ældre elever tilbydes også **International Baccalaureate (IB) Diploma Programme**.
|
||||
- **Fordele**: Godt ry, stærkt curriculum og en multikulturel elevbase.
|
||||
|
||||
---
|
||||
|
||||
### Lycée Français International de Porto
|
||||
- **Adresse**: {{ link_to(title="Rua Gil Eanes, 27, Porto, 4150-348 Porto", url="https://www.lfip.pt") }}
|
||||
- **Beskrivelse**: En fransk international skole, der tilbyder undervisning fra **børnehave** til **gymnasium**. Hovedsproget er fransk, men skolen tilbyder også undervisning i **engelsk** og **portugisisk**.
|
||||
- **Fordele**: En stærk fransk uddannelsestradition og fokus på flere sprog. Skolen er ideel for fransktalende familier og dem, der ønsker en flersproget uddannelse.
|
||||
|
||||
---
|
||||
|
||||
### Externato Deutsche Schule Zu Porto
|
||||
- **Adresse**: {{ link_to(title="Rua Guerra Junqueiro 162, 4150-386 Porto", url="http://www.dsporto.de") }}
|
||||
- **Beskrivelse**: En tysk skole, der tilbyder undervisning fra **børnehave** til **gymnasium**. Hovedsproget er tysk, men eleverne får også undervisning i **engelsk** og **portugisisk**.
|
||||
- **Fordele**: En høj standard inden for tysk uddannelse, som er kendt for sin struktur og kvalitet.
|
||||
|
||||
---
|
||||
|
||||
### Konklusion
|
||||
Porto har flere internationale skoler af høj kvalitet, som imødekommer behovene hos tilflyttere. Valget afhænger af curriculum, sprog og ens egne prioriteter. Uanset om det er britisk, fransk eller tysk uddannelse, findes der gode muligheder i Porto.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Klarer man sig på engelsk i Portugal?
|
||||
description: Hvordan engelskkundskaber i Portugal gør det nemt for tilflyttere at kommunikere
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:45:00 CET 2024
|
||||
summary: Mange portugisere taler engelsk, især i turistområder og blandt yngre generationer
|
||||
favorite: false
|
||||
image: images/pic06.jpg
|
||||
category: Sprog
|
||||
tags: [Portugal, Engelsk, Portugisisk, Sprog, Tilflyttere, Kommunikation]
|
||||
---
|
||||
|
||||
# Kan jeg klare mig på engelsk i Portugal?
|
||||
|
||||
Under min research er det blevet tydeligt, at man som tilflytter godt kan klare sig på engelsk i Portugal, især i bestemte situationer og områder.
|
||||
|
||||
### Engelskkundskaber i turistområder
|
||||
I storbyer som **Lissabon**, **Porto** og langs **Algarvekysten** taler mange portugisere engelsk, især i områder med mange turister. Restauranter, butikker og hoteller har ofte engelsktalende personale, hvilket gør hverdagen nemmere.
|
||||
|
||||
### Yngre generationer og professionelle
|
||||
Blandt yngre portugisere og i erhvervslivet er engelskkundskaber generelt gode. Mange har lært engelsk i skolen, og i internationale virksomheder er det ofte arbejdssproget.
|
||||
|
||||
### Begrænset engelsk uden for byerne
|
||||
Uden for de større byer og turistområder kan det dog være sværere at finde folk, der taler engelsk flydende. Her er det en klar fordel at kunne grundlæggende portugisisk til daglig kommunikation.
|
||||
|
||||
Selvom portugisisk er det officielle sprog, gør engelskkundskaberne i Portugal det muligt at klare sig i hverdagen som tilflytter. Dog er det en god idé at lære lidt portugisisk, hvis man planlægger at bosætte sig permanent, især hvis man ønsker at integrere sig i det lokale samfund.
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: Sundhedssystemet i Portugal
|
||||
description: En oversigt over Portugals offentlige og private sundhedsydelser for EU-borgere
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:50:00 CET 2024
|
||||
summary: Portugals sundhedssystem tilbyder både offentlige tjenester og private alternativer
|
||||
favorite: false
|
||||
image: images/pic10.jpg
|
||||
category: Sundhed
|
||||
tags: [Portugal, Sundhedssystem, EHIC, Privat forsikring, Offentlige sundhedsydelser, EU-borgere]
|
||||
---
|
||||
|
||||
# Hvad med sundhedssystemet?
|
||||
|
||||
En af de vigtigste overvejelser ved at flytte til Portugal er sundhedssystemet. Her tilbyder landet både offentlige og private sundhedsydelser, der dækker forskellige behov.
|
||||
|
||||
### Offentligt sundhedssystem
|
||||
Som EU-borger har du ret til at bruge det portugisiske sundhedssystem ved hjælp af dit **EU-sygesikringskort (EHIC)**. Det offentlige system er generelt pålideligt og omfatter:
|
||||
- Grundlæggende sundhedsydelser som lægebesøg, hospitalsbehandling og akutpleje.
|
||||
- Lave brugerbetalinger for medicin og behandlinger sammenlignet med Danmark.
|
||||
|
||||
**Ulemper**: Ventetiden kan dog være lang, især hvis du har brug for specialister eller ikke-akutte behandlinger.
|
||||
|
||||
### Privat sundhedssystem
|
||||
Mange tilflyttere vælger at tegne **privat sundhedsforsikring** for at få hurtigere adgang til behandling og specialister. Det private system er ofte mere fleksibelt og har kortere ventetider, men koster naturligvis ekstra.
|
||||
|
||||
**Fordele**:
|
||||
- Hurtigere adgang til specialister.
|
||||
- Moderne faciliteter og engelsktalende personale på private klinikker og hospitaler.
|
||||
|
||||
### Kombination af offentligt og privat
|
||||
Nogle vælger en kombination, hvor de benytter det offentlige system til dagligdags sundhedsbehov og tegner en privat forsikring til mere specialiserede ydelser.
|
||||
|
||||
Sundhedssystemet i Portugal er både tilgængeligt og pålideligt for EU-borgere. Det offentlige system fungerer godt til de fleste behov, men privat forsikring er en god løsning, hvis man ønsker kortere ventetider og mere fleksibilitet.
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
name: Bedste apps til expats i Portugal
|
||||
description: En oversigt over nyttige apps, der gør hverdagen lettere for expats i Portugal
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 00:55:00 CET 2024
|
||||
summary: De 9 mest anbefalede apps til transport, kommunikation og praktisk hjælp i Portugal
|
||||
favorite: false
|
||||
image: images/pic07.jpg
|
||||
category: Apps og Teknologi
|
||||
tags: [Portugal, Apps, Expat, Teknologi, Dagligdag, Transport, Kommunikation]
|
||||
---
|
||||
|
||||
# 9 bedste apps til expats i Portugal
|
||||
|
||||
Hvis du planlægger at bo i Portugal som expat, er der en række apps, der kan gøre din hverdag langt nemmere. Her er mine anbefalinger:
|
||||
|
||||
---
|
||||
|
||||
1. **Google Maps**
|
||||
Perfekt til navigation og offentlig transport. Google Maps giver realtidsinformation om ruter, trafik og transportmidler i byer som Lissabon, Porto og Algarve.
|
||||
|
||||
2. **Uber/Bolt**
|
||||
To af de mest populære apps til nem og billig transport. Både Uber og Bolt er udbredt i Portugal og giver et godt alternativ til taxaer.
|
||||
|
||||
3. **MB Way**
|
||||
En uundværlig app til mobile betalinger og hurtig håndtering af portugisiske bankkonti. MB Way bruges bredt i butikker og til pengeoverførsler.
|
||||
|
||||
4. **Idealista**
|
||||
En af de bedste apps og hjemmesider til **boligsøgning** i Portugal. Den er overskuelig og fungerer godt, både til køb og leje af bolig.
|
||||
|
||||
5. **Duolingo**
|
||||
Har du brug for at lære portugisisk? Duolingo er en effektiv og sjov måde at lære sproget hurtigt, hvilket er en stor hjælp i hverdagen.
|
||||
|
||||
6. **Meetup**
|
||||
Find lokale grupper og sociale begivenheder, der matcher dine interesser. Perfekt til at møde nye mennesker og netværke som expat.
|
||||
|
||||
7. **Internations**
|
||||
En global platform for expats, hvor du kan deltage i netværksarrangementer og møde ligesindede.
|
||||
|
||||
8. **Revolut/Wise**
|
||||
To fremragende apps til **international bankoverførsel** og håndtering af valuta. De gør det nemt at sende penge mellem lande og undgå høje gebyrer.
|
||||
|
||||
9. **WhatsApp**
|
||||
Meget udbredt i Portugal til både lokal og international kommunikation. De fleste portugisere og expats bruger WhatsApp til beskeder og opkald.
|
||||
|
||||
---
|
||||
|
||||
Disse apps dækker alt fra transport og økonomi til sprog og sociale aktiviteter. Hvis du som expat lærer at bruge dem, vil du hurtigt opleve, hvor meget lettere det bliver at navigere i din nye hverdag i Portugal.
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
name: Flyselskaber mellem Lissabon og København
|
||||
description: Direkte flyselskaber og rejsetid mellem Lissabon og København
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:05:00 CET 2024
|
||||
summary: Direkte fly mellem Lissabon og København med TAP, SAS, easyJet og Norwegian
|
||||
favorite: false
|
||||
image: images/pic11.jpg
|
||||
category: Transport
|
||||
tags: [Portugal, København, Lissabon, Flyselskaber, TAP, SAS, Norwegian, easyJet]
|
||||
---
|
||||
|
||||
# Flyselskaber mellem Lissabon og København
|
||||
|
||||
Hvis du planlægger at pendle eller rejse mellem **Lissabon (LIS)** og **København (CPH)**, er der flere gode muligheder for direkte flyforbindelser.
|
||||
|
||||
---
|
||||
|
||||
### Direkte flyselskaber
|
||||
Flere flyselskaber tilbyder direkte fly på ruten:
|
||||
|
||||
- **TAP Air Portugal**
|
||||
Portugals nationale flyselskab med regelmæssige afgange og ofte god service på ruten.
|
||||
|
||||
- **Scandinavian Airlines (SAS)**
|
||||
Et oplagt valg for skandinaviske rejsende med direkte forbindelser mellem Lissabon og København.
|
||||
|
||||
- **easyJet**
|
||||
Lavprisselskabet easyJet opererer direkte flyvninger, hvilket gør det til en økonomisk mulighed.
|
||||
|
||||
- **Norwegian Air International**
|
||||
Et andet budgetvenligt alternativ med direkte fly mellem de to byer.
|
||||
|
||||
---
|
||||
|
||||
### Rejseinformation
|
||||
- **Antal ugentlige flyvninger**: Ca. **106 flyvninger** om ugen mellem Lissabon og København.
|
||||
- **Flyvetid**: Den gennemsnitlige flyvetid er ca. **3 timer og 42 minutter**.
|
||||
|
||||
---
|
||||
|
||||
### Tips til booking
|
||||
For at finde de bedste priser og afgange anbefaler jeg at bruge populære søgemaskiner som:
|
||||
- **{{ link_to(title="Skyscanner", url="https://www.skyscanner.dk") }}**
|
||||
- **{{ link_to(title="Momondo", url="https://www.momondo.dk") }}**
|
||||
|
||||
Det kan være en god idé at sammenligne priser på tværs af platforme og holde øje med kampagner fra de nævnte flyselskaber.
|
||||
|
||||
✈️ **God rejse!**
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
name: Er transport billigere i Portugal?
|
||||
description: En sammenligning af transportomkostninger og nyttige rejseapps i Portugal
|
||||
author: Henrik Jess
|
||||
date: tor 12 dec 01:00:00 CET 2024
|
||||
summary: Offentlig transport er billigere i Portugal med flere apps til nem rejseplanlægning
|
||||
favorite: false
|
||||
image: images/pic08.jpg
|
||||
category: Transport
|
||||
tags: [Portugal, Transport, Apps, Offentlig transport, Tog, Bus, Rejseplanlægning]
|
||||
---
|
||||
|
||||
# Er transport billigere i Portugal?
|
||||
|
||||
Ja, offentlig transport i Portugal, såsom busser, metro og tog, er betydeligt billigere end i Danmark. For eksempel koster en månedlig billet i storbyer som Lissabon typisk langt mindre end en tilsvarende billet i København. **Benzinpriserne** er dog ofte på niveau med eller højere end i Danmark, hvilket kan påvirke udgifterne til biltransport.
|
||||
|
||||
---
|
||||
|
||||
## Apps til rejseplanlægning i Portugal
|
||||
For at gøre det nemt at navigere i Portugals offentlige transportsystem findes der flere gode apps. De fungerer som den portugisiske udgave af **Rejseplanen** og hjælper dig med at planlægge ture med bus, metro, tog og mere.
|
||||
|
||||
---
|
||||
|
||||
### {{ link_to(title="Moovit", url="https://moovitapp.com/portugal") }}
|
||||
- **Beskrivelse**: En alsidig transportapp, der fungerer i de fleste portugisiske byer og regioner, såsom Lissabon, Porto, Portimão og Funchal.
|
||||
- **Funktioner**: Planlæg ruter med flere transportmidler (gang, cykling, bus, sporvogn), og få realtidsopdateringer om afgangstider. Perfekt til at navigere som en lokal.
|
||||
|
||||
---
|
||||
|
||||
### {{ link_to(title="CP – Comboios de Portugal", url="https://www.cp.pt/passageiros/pt/apps") }}
|
||||
- **Beskrivelse**: Den officielle app fra Portugals statsbaner (**CP**) er ideel, hvis du ofte rejser med tog.
|
||||
- **Funktioner**:
|
||||
- Køb togbilletter direkte i appen.
|
||||
- Planlæg rejser og se opdaterede køreplaner.
|
||||
- Få rabatter og information om forsinkelser.
|
||||
|
||||
---
|
||||
|
||||
### {{ link_to(title="Carris", url="https://www.carris.pt/en/") }}
|
||||
- **Beskrivelse**: Din guide til **Lissabons** offentlige transportsystem, herunder busser og sporvogne.
|
||||
- **Funktioner**:
|
||||
- Planlæg dine ruter og køb billetter digitalt.
|
||||
- Modtag opdateringer om driftsstatus og afgangstider.
|
||||
- Tilbyder nem adgang til kundeservice.
|
||||
|
||||
---
|
||||
|
||||
Med billigere priser på offentlig transport og nyttige apps som Moovit, CP og Carris er det let og økonomisk at rejse rundt i Portugal. For pendlere og rejsende er disse apps uundværlige værktøjer til at planlægge ture og spare tid.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Fordele ved offentlig transport i Portugal
|
||||
description: Effektivitet og økonomi ved Portugals offentlige transport
|
||||
author: Henrik Jess
|
||||
date: ons 11 dec 23:45:00 CET 2024
|
||||
summary: Offentlig transport er billig, effektiv og landsdækkende
|
||||
favorite: false
|
||||
image: images/pic10.jpg
|
||||
category: Transport
|
||||
tags: [Portugal, Offentlig transport, Tog, Metro, Bus, Lissabon, Porto]
|
||||
---
|
||||
|
||||
# Hvilke fordele er der ved at bruge offentlig transport i Portugal?
|
||||
|
||||
Portugal har et veludviklet og økonomisk overkommeligt offentligt transportsystem, især i større byer som **Lissabon** og **Porto**.
|
||||
|
||||
### Fordele ved offentlig transport:
|
||||
- **Pris**: Offentlig transport i Portugal er langt billigere end i Danmark. Det gælder både togrejser og bytransport som metro og busser.
|
||||
- **Effektivitet**: Byernes metrosystemer er moderne og punktlige, mens tog forbinder store dele af landet effektivt.
|
||||
- **Dækning**: Togene forbinder de fleste regioner, og i byerne findes der gode alternativer som sporvogne, busser og metro.
|
||||
|
||||
Hvis du planlægger at bosætte dig i Portugal, er det værd at overveje offentlig transport som en praktisk og økonomisk løsning til daglig transport.
|
||||
@@ -1,18 +0,0 @@
|
||||
# Min Drøm om Portugal
|
||||
## En Frisk Start for Henrik og Erika
|
||||
|
||||
|
||||
{{ image('thumbnails', 'pic11.jpg', alt='Mit fantatiske billed') }}
|
||||
|
||||
---
|
||||
|
||||
Portugal er blevet symbolet på den nye begyndelse, jeg drømmer om – en mulighed for at skabe et liv med mere ro, plads og mening. Som Henrik, far til min 16-årige datter Erika, har tanken om at flytte fra Danmark til Portugal vokset sig stærkere de seneste år. Det er ikke bare et ønske om sol og varme, men om en hverdag, hvor livskvalitet betyder mere end stress og travlhed.
|
||||
|
||||
Drømmen er konkret: en hverdag med tid til både arbejde og familieliv, omgivet af Portugals smukke landskaber og venlige kultur. Med Lissabon som udgangspunkt forestiller jeg mig et liv, hvor jeg arbejder struktureret, pendler til Danmark i de lige uger og tilbringer de øvrige uger i vores nye hjem. Det handler ikke om at efterlade alt bag sig, men om at finde en bedre balance mellem mine forpligtelser i Danmark og en ny tilværelse, hvor solen skinner lidt længere.
|
||||
|
||||
For Erika er det også en chance for at opleve en ny kultur, møde nye mennesker og måske starte på en ungdomsuddannelse i Portugal. Vi ved begge, at det ikke bliver uden udfordringer – med praktiske overvejelser som bolig, skat, pendling og skolegang – men fordelene vejer tungere. Leveomkostningerne er lavere, menneskerne er imødekommende, og tempoet giver plads til at trække vejret.
|
||||
|
||||
Denne hjemmeside er en samling af mine tanker, forberedelser og erfaringer på vejen mod at gøre drømmen til virkelighed. Måske overvejer du selv en lignende rejse, eller måske er du bare nysgerrig på, hvordan det er at tage springet. Her deler jeg min rejse – fra de første spæde idéer til de praktiske skridt mod et nyt liv i Portugal. For mig og for Erika.
|
||||
|
||||
---
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
1
data/bolig/index.html
Normal file
1
data/bolig/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<P> Lidt om Bolig </P>
|
||||
7
data/bolig/lidt_omkring_boliger.html
Normal file
7
data/bolig/lidt_omkring_boliger.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<h1>BoligBolig Bolig Bolig - Hvor skal sengen placeres</h1>
|
||||
<p>Nu bliver det spænde!</p>
|
||||
<p>
|
||||
<div class="note">
|
||||
<p>Dette er stadig en test side</p>
|
||||
</div>
|
||||
</p>
|
||||
5
data/bolig/lidt_omkring_boliger.md
Normal file
5
data/bolig/lidt_omkring_boliger.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BoligBolig Bolig Bolig - Hvor skal sengen placeres
|
||||
|
||||
Nu bliver det spænde!
|
||||
|
||||
{{ note("Dette er stadig en test side") }}
|
||||
7
data/job/det_ved_jeg_om_job.html
Normal file
7
data/job/det_ved_jeg_om_job.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<h1>Lidt mere info om job</h1>
|
||||
<p>Der skal langt mere tekst her</p>
|
||||
<p>
|
||||
<div class="note">
|
||||
<p>Husk alpha side</p>
|
||||
</div>
|
||||
</p>
|
||||
6
data/job/det_ved_jeg_om_job.md
Normal file
6
data/job/det_ved_jeg_om_job.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Lidt mere info om job
|
||||
|
||||
Der skal langt mere tekst her
|
||||
|
||||
|
||||
{{ note("Husk alpha side") }}
|
||||
1
data/job/index.html
Normal file
1
data/job/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<P> Lidt om job </P>
|
||||
1
data/skat/index.html
Normal file
1
data/skat/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<P> Lidt om skat </P>
|
||||
1
data/skole/index.html
Normal file
1
data/skole/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<P> Lidt om skole </P>
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"name": "Arbejde i Portugal",
|
||||
"path": "Arbejde",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Bolig i Portugal",
|
||||
"path": "Bolig",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "El- og vandregninger i Portugal: Sådan påvirker de leveomkostningerne",
|
||||
"path": "Budget",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Permanent ophold i Portugal",
|
||||
"path": "Flytning",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Fritidsaktiviteter i Portugal",
|
||||
"path": "Fritid",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Expat-fællesskaber i Portugal",
|
||||
"path": "Fællesskab",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Uddannelsesmuligheder i Portugal for børn",
|
||||
"path": "General",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Omkostninger ved at bo i Portugal vs Danmark",
|
||||
"path": "Hverdag",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Pendling mellem Danmark og Portugal",
|
||||
"path": "Job",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Portugals unikke kultur",
|
||||
"path": "Kultur",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Livskvalitet i Portugal",
|
||||
"path": "Livskvalitet",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Skattesystemet i Portugal",
|
||||
"path": "Skat",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Internationale skoler i Porto",
|
||||
"path": "Skole",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Klarer man sig på engelsk i Portugal?",
|
||||
"path": "Sprog",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Sundhedssystemet i Portugal",
|
||||
"path": "Sundhed",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Bedste apps til expats i Portugal",
|
||||
"path": "Tech",
|
||||
"author": "Henrik Jess"
|
||||
},
|
||||
{
|
||||
"name": "Er transport billigere i Portugal?",
|
||||
"path": "Transport",
|
||||
"author": "Henrik Jess"
|
||||
}
|
||||
],
|
||||
"favorites": [
|
||||
{
|
||||
"name": "Budget - Indkøb",
|
||||
"image": "images/budget2.jpg",
|
||||
"description": "Fødevarer er markant billigere i Portugal med få undtagelser som bær og specialvarer.",
|
||||
"path": "Budget"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -1,63 +0,0 @@
|
||||
Creative Commons Attribution 3.0 Unported
|
||||
http://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
|
||||
1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
|
||||
3. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
|
||||
4. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
5. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
|
||||
6. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
|
||||
7. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
|
||||
8. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
|
||||
9. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
|
||||
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
|
||||
|
||||
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
|
||||
|
||||
1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
|
||||
2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
|
||||
3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
|
||||
4. to Distribute and Publicly Perform Adaptations.
|
||||
5.
|
||||
|
||||
For the avoidance of doubt:
|
||||
1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
|
||||
2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
|
||||
3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
|
||||
|
||||
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
|
||||
|
||||
1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
|
||||
2. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
|
||||
3. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. Termination
|
||||
|
||||
1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
|
||||
2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
|
||||
|
||||
8. Miscellaneous
|
||||
|
||||
1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
|
||||
2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
|
||||
3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
|
||||
5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
|
||||
6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
|
||||
@@ -1,46 +0,0 @@
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
|
||||
|
||||
It's done! Parallelism was an idea I mocked up a few months ago but, due to other
|
||||
committments, didn't get around to putting to code until recently. Different to
|
||||
my more general-purpose freebies, this one's specifically built for portfolio use
|
||||
with a very unique look/layout. Hope you enjoy it!
|
||||
|
||||
Demo images* are courtesy of the supremely talented photographer Felicia Simion.
|
||||
If you like photography or just enjoy being blown away by awesome stuff, check
|
||||
out her portfolio for more stunning images:
|
||||
|
||||
http://ineedchemicalx.deviantart.com/
|
||||
|
||||
(* = Not included! Only meant for use with my own on-site demo, so please do NOT download
|
||||
and/or use any of Felicia's work without her explicit permission!)
|
||||
|
||||
Feedback, bug reports, and comments are not only welcome, but strongly encouraged :)
|
||||
|
||||
AJ
|
||||
aj@lkn.io | @ajlkn
|
||||
|
||||
|
||||
Credits:
|
||||
|
||||
Demo Images:
|
||||
Felicia Simion (ineedchemicalx.deviantart.com)
|
||||
"Pasadena" (ineedchemicalx.deviantart.com/art/Pasadena-357650036)
|
||||
"Your really got me" (ineedchemicalx.deviantart.com/art/You-really-got-me-345249340)
|
||||
"Ad Infinitum" (ineedchemicalx.deviantart.com/art/Ad-infinitum-354203162)
|
||||
"Different." (ineedchemicalx.deviantart.com/art/Different-353708988)
|
||||
"Elysium" (ineedchemicalx.deviantart.com/art/Elysium-355393900)
|
||||
"Kingdom of the Wind" (ineedchemicalx.deviantart.com/art/Kingdom-of-the-Wind-348268044)
|
||||
"The Pursuit" (ineedchemicalx.deviantart.com/art/The-Pursuit-355003425)
|
||||
"Boundless" (ineedchemicalx.deviantart.com/art/Boundless-291831118)
|
||||
"The Spectators" (ineedchemicalx.deviantart.com/art/The-Spectators-342155982)
|
||||
|
||||
Icons:
|
||||
Font Awesome (fontawesome.io)
|
||||
|
||||
Other:
|
||||
jQuery (jquery.com)
|
||||
normalizeWheel (@miorel + @pieterv of Facebook)
|
||||
Responsive Tools (github.com/ajlkn/responsive-tools)
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,833 +0,0 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600");
|
||||
@import url("fontawesome-all.min.css");
|
||||
|
||||
/*
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object,
|
||||
iframe, h1, h2, h3, h4, h5, h6, p, blockquote,
|
||||
pre, a, abbr, acronym, address, big, cite,
|
||||
code, del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var, b,
|
||||
u, i, center, dl, dt, dd, ol, ul, li, fieldset,
|
||||
form, label, legend, table, caption, tbody,
|
||||
tfoot, thead, tr, th, td, article, aside,
|
||||
canvas, details, embed, figure, figcaption,
|
||||
footer, header, hgroup, menu, nav, output, ruby,
|
||||
section, summary, time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;}
|
||||
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after, q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
/* Basic */
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url("images/overlay.png"), -moz-linear-gradient(top, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0.65)), url("../../images/bg.jpg");
|
||||
background-image: url("images/overlay.png"), -webkit-linear-gradient(top, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0.65)), url("../../images/bg.jpg");
|
||||
background-image: url("images/overlay.png"), -ms-linear-gradient(top, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0.65)), url("../../images/bg.jpg");
|
||||
background-image: url("images/overlay.png"), linear-gradient(top, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0.65)), url("../../images/bg.jpg");
|
||||
background-position: top left, bottom left, auto;
|
||||
background-size: auto, 100% 100%, cover;
|
||||
background-attachment: fixed;
|
||||
background-repeat: repeat, no-repeat, auto;
|
||||
position: relative;
|
||||
background-color: #150C07;
|
||||
line-height: 1.75em;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
body.is-preload *, body.is-preload *:before, body.is-preload *:after {
|
||||
-moz-animation: none !important;
|
||||
-webkit-animation: none !important;
|
||||
-ms-animation: none !important;
|
||||
animation: none !important;
|
||||
-moz-transition: none !important;
|
||||
-webkit-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
|
||||
body {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
body {
|
||||
background-attachment: scroll;
|
||||
background-size: auto, 100% 100%, 250% auto;
|
||||
background-repeat: repeat, no-repeat, no-repeat;
|
||||
background-position: top left, bottom left, 50% 0%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body, input, select, textarea {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-weight: 400;
|
||||
padding: 0;
|
||||
font-size: 13pt;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1680px) {
|
||||
|
||||
body, input, select, textarea {
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
|
||||
body, input, select, textarea {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
|
||||
body, input, select, textarea {
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
text-decoration: none;
|
||||
-moz-transition: color 0.25s ease-in-out;
|
||||
-webkit-transition: color 0.25s ease-in-out;
|
||||
-ms-transition: color 0.25s ease-in-out;
|
||||
transition: color 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #d64760;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
|
||||
strong, b {
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* List */
|
||||
|
||||
ul.icons {
|
||||
cursor: default;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
ul.icons li {
|
||||
display: inline-block;
|
||||
font-size: 1.5em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
ul.icons li span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.icons li a {
|
||||
opacity: 0.35;
|
||||
color: #fff;
|
||||
-moz-transition: opacity 0.25s ease-in-out;
|
||||
-webkit-transition: opacity 0.25s ease-in-out;
|
||||
-ms-transition: opacity 0.25s ease-in-out;
|
||||
transition: opacity 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
ul.icons li a:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
ul.icons li:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
ul.icons {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon:before {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
line-height: 1;
|
||||
text-transform: none !important;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.icon:before {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.icon > .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon.solid:before {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.icon.brands:before {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
}
|
||||
|
||||
/* Wrapper */
|
||||
|
||||
#wrapper {
|
||||
min-height: 100vh;
|
||||
display: -moz-flex;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flex;
|
||||
display: flex;
|
||||
-moz-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-moz-justify-content: space-between;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
-moz-align-items: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-align-items: center;
|
||||
align-items: center;
|
||||
-moz-transition: -moz-filter 0.5s ease-in-out;
|
||||
-webkit-transition: -webkit-filter 0.5s ease-in-out;
|
||||
-ms-transition: -ms-filter 0.5s ease-in-out;
|
||||
transition: filter 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
#wrapper:before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
|
||||
body.is-poptrox-visible #wrapper {
|
||||
-moz-filter: blur(0.25em);
|
||||
-webkit-filter: blur(0.25em);
|
||||
-ms-filter: blur(0.25em);
|
||||
filter: blur(0.25em);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
#wrapper {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Scroll Zone */
|
||||
|
||||
.scrollZone {
|
||||
position: fixed;
|
||||
width: 6rem;
|
||||
height: 100vh;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -ms-grab;
|
||||
cursor: grab;
|
||||
z-index: 10001;
|
||||
}
|
||||
|
||||
.scrollZone.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.scrollZone.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
.scrollZone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Main */
|
||||
|
||||
#main {
|
||||
-moz-transition: opacity 1s ease-in-out;
|
||||
-webkit-transition: opacity 1s ease-in-out;
|
||||
-ms-transition: opacity 1s ease-in-out;
|
||||
transition: opacity 1s ease-in-out;
|
||||
-moz-transition-delay: 0.5s;
|
||||
-webkit-transition-delay: 0.5s;
|
||||
-ms-transition-delay: 0.5s;
|
||||
transition-delay: 0.5s;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: -moz-min-content;
|
||||
width: -webkit-min-content;
|
||||
width: -ms-min-content;
|
||||
width: min-content;
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#main .items {
|
||||
display: -moz-flex;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#main .items > :last-child {
|
||||
border-right: solid 10px #ffffff;
|
||||
}
|
||||
|
||||
#main .item {
|
||||
-moz-flex-grow: 0;
|
||||
-webkit-flex-grow: 0;
|
||||
-ms-flex-grow: 0;
|
||||
flex-grow: 0;
|
||||
-moz-flex-shrink: 0;
|
||||
-webkit-flex-shrink: 0;
|
||||
-ms-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
margin: 5px;
|
||||
height: 16em;
|
||||
box-shadow: 0 0 0 10px #ffffff;
|
||||
}
|
||||
|
||||
#main .item.span-1 {
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
#main .item.span-2 {
|
||||
width: 30em;
|
||||
}
|
||||
|
||||
#main .item.span-3 {
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
#main .item.intro {
|
||||
background-color: #d64760;
|
||||
background-image: url("images/overlay.png");
|
||||
display: -moz-flex;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flex;
|
||||
display: flex;
|
||||
-moz-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-moz-justify-content: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-justify-content: center;
|
||||
justify-content: center;
|
||||
padding: 1em 3em;
|
||||
}
|
||||
|
||||
#main .item.intro h1 {
|
||||
font-size: 3em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
#main .item.intro p {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.5em;
|
||||
margin: 0.5em 0 0 0;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
#main .item.thumb {
|
||||
display: block;
|
||||
position: relative;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#main .item.thumb h2 {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(18, 21, 29, 0.85);
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#main .item.thumb img {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-moz-object-fit: cover;
|
||||
-webkit-object-fit: cover;
|
||||
-ms-object-fit: cover;
|
||||
object-fit: cover;
|
||||
-moz-object-position: center;
|
||||
-webkit-object-position: center;
|
||||
-ms-object-position: center;
|
||||
object-position: center;
|
||||
-moz-transition: opacity 0.75s ease-in-out;
|
||||
-webkit-transition: opacity 0.75s ease-in-out;
|
||||
-ms-transition: opacity 0.75s ease-in-out;
|
||||
transition: opacity 0.75s ease-in-out;
|
||||
-moz-transition-delay: 1.25s;
|
||||
-webkit-transition-delay: 1.25s;
|
||||
-ms-transition-delay: 1.25s;
|
||||
transition-delay: 1.25s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-1 img {
|
||||
-moz-transition-delay: 1.375s;
|
||||
-webkit-transition-delay: 1.375s;
|
||||
-ms-transition-delay: 1.375s;
|
||||
transition-delay: 1.375s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-2 img {
|
||||
-moz-transition-delay: 1.5s;
|
||||
-webkit-transition-delay: 1.5s;
|
||||
-ms-transition-delay: 1.5s;
|
||||
transition-delay: 1.5s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-3 img {
|
||||
-moz-transition-delay: 1.625s;
|
||||
-webkit-transition-delay: 1.625s;
|
||||
-ms-transition-delay: 1.625s;
|
||||
transition-delay: 1.625s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-4 img {
|
||||
-moz-transition-delay: 1.75s;
|
||||
-webkit-transition-delay: 1.75s;
|
||||
-ms-transition-delay: 1.75s;
|
||||
transition-delay: 1.75s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-5 img {
|
||||
-moz-transition-delay: 1.875s;
|
||||
-webkit-transition-delay: 1.875s;
|
||||
-ms-transition-delay: 1.875s;
|
||||
transition-delay: 1.875s;
|
||||
}
|
||||
|
||||
#main .item.thumb.delay-6 img {
|
||||
-moz-transition-delay: 2s;
|
||||
-webkit-transition-delay: 2s;
|
||||
-ms-transition-delay: 2s;
|
||||
transition-delay: 2s;
|
||||
}
|
||||
|
||||
body.is-preload #main {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
body.is-preload #main .item.thumb img {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1680px) {
|
||||
|
||||
#main .item {
|
||||
height: 14em;
|
||||
}
|
||||
|
||||
#main .item.span-1 {
|
||||
width: 17em;
|
||||
}
|
||||
|
||||
#main .item.span-2 {
|
||||
width: 25.5em;
|
||||
}
|
||||
|
||||
#main .item.span-3 {
|
||||
width: 34em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
#main {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 2.5px;
|
||||
}
|
||||
|
||||
#main .items {
|
||||
-moz-flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-moz-justify-content: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#main .items > :last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
#main .item {
|
||||
-moz-flex-grow: 1;
|
||||
-webkit-flex-grow: 1;
|
||||
-ms-flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
-moz-flex-shrink: 1;
|
||||
-webkit-flex-shrink: 1;
|
||||
-ms-flex-shrink: 1;
|
||||
flex-shrink: 1;
|
||||
width: calc(50vw - 15px) !important;
|
||||
margin: 2.5px;
|
||||
box-shadow: 0 0 0 5px #ffffff;
|
||||
}
|
||||
|
||||
#main .item.intro {
|
||||
width: calc(100vw - 20px) !important;
|
||||
text-align: center;
|
||||
height: auto;
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
#main .item.intro h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main .item.intro p {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#main .item.thumb h2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
|
||||
#main .item {
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
#footer {
|
||||
width: 100%;
|
||||
padding: 1.5em;
|
||||
display: -moz-flex;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flex;
|
||||
display: flex;
|
||||
-moz-justify-content: space-between;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
z-index: 10002;
|
||||
}
|
||||
|
||||
#footer > section:first-child {
|
||||
text-align: left;
|
||||
max-width: 36em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
#footer > section:last-child {
|
||||
text-align: right;
|
||||
max-width: 36em;
|
||||
}
|
||||
|
||||
#footer h2 {
|
||||
font-size: 2.25em;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
#footer .copyright li {
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
padding-left: 1em;
|
||||
border-left: solid 1px rgba(255, 255, 255, 0.25);
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
#footer .copyright li:first-child {
|
||||
border-left: 0;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
|
||||
#footer {
|
||||
-moz-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#footer > section:first-child {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#footer > section:last-child {
|
||||
text-align: left;
|
||||
margin: 1.5em 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
#footer {
|
||||
text-align: center;
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
#footer > section:first-child {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#footer > section:last-child {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#footer .copyright li {
|
||||
display: block;
|
||||
margin: 0.75em 0 0 0;
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
#footer .copyright li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
|
||||
.poptrox-popup {
|
||||
background: #1a1f2c;
|
||||
background: rgba(18, 21, 29, 0.9);
|
||||
box-shadow: 0px 0px 0px 10px #fff, 0px 10px 60px 10px rgba(8, 11, 19, 0.55);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.poptrox-popup .loader {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -24px 0 0 -24px;
|
||||
background: url("images/loader.gif");
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.poptrox-popup .caption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #1a1f2c;
|
||||
background: rgba(18, 21, 29, 0.85);
|
||||
display: block;
|
||||
width: 100%;
|
||||
line-height: 75px;
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next, .poptrox-popup .nav-previous {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
-moz-transition: opacity 0.25s ease-in-out;
|
||||
-webkit-transition: opacity 0.25s ease-in-out;
|
||||
-ms-transition: opacity 0.25s ease-in-out;
|
||||
transition: opacity 0.25s ease-in-out;
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next:hover, .poptrox-popup .nav-previous:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next, .poptrox-popup .nav-previous {
|
||||
text-decoration: none;
|
||||
text-transform: none !important;
|
||||
width: 150px;
|
||||
height: 75px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
font-size: 3em;
|
||||
line-height: 75px;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next:before, .poptrox-popup .nav-previous:before {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
line-height: 1;
|
||||
text-transform: none !important;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next {
|
||||
right: 0;
|
||||
text-align: right;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next:before {
|
||||
content: '\f105';
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-previous {
|
||||
left: 0;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-previous:before {
|
||||
content: '\f104';
|
||||
}
|
||||
|
||||
@media screen and (max-width: 736px) {
|
||||
|
||||
.poptrox-popup {
|
||||
background: #0a0f1c;
|
||||
box-shadow: 0px 0px 30px 10px rgba(8, 11, 19, 0.85);
|
||||
border: solid 2.5px #fff;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
-ms-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.poptrox-popup .caption {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-next {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.poptrox-popup .nav-previous {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
|
||||
/* Main */
|
||||
|
||||
#main {
|
||||
opacity: 1 !important;
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
|
||||
#main .item.thumb img {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
/* breakpoints.js v1.0 | @ajlkn | MIT licensed */
|
||||
var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e<t.events.length;e++)n=t.events[e],t.active(n.query)?n.state||(n.state=!0,n.handler()):n.state&&(n.state=!1)}};return e._=t,e.on=function(e,n){t.on(e,n)},e.active=function(e){return t.active(e)},e}();!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.breakpoints=t()}(this,function(){return breakpoints});
|
||||
2
inspiration/html5up/assets/js/browser.min.js
vendored
2
inspiration/html5up/assets/js/browser.min.js
vendored
@@ -1,2 +0,0 @@
|
||||
/* browser.js v1.0.1 | @ajlkn | MIT licensed */
|
||||
var browser=function(){"use strict";var t={name:null,version:null,os:null,osVersion:null,touch:null,mobile:null,_canUse:null,canUse:function(e){t._canUse||(t._canUse=document.createElement("div"));var n=t._canUse.style,r=e.charAt(0).toUpperCase()+e.slice(1);return e in n||"Moz"+r in n||"Webkit"+r in n||"O"+r in n||"ms"+r in n},init:function(){for(var e=navigator.userAgent,n="other",r=0,i=[["firefox",/Firefox\/([0-9\.]+)/],["bb",/BlackBerry.+Version\/([0-9\.]+)/],["bb",/BB[0-9]+.+Version\/([0-9\.]+)/],["opera",/OPR\/([0-9\.]+)/],["opera",/Opera\/([0-9\.]+)/],["edge",/Edge\/([0-9\.]+)/],["safari",/Version\/([0-9\.]+).+Safari/],["chrome",/Chrome\/([0-9\.]+)/],["ie",/MSIE ([0-9]+)/],["ie",/Trident\/.+rv:([0-9]+)/]],o=0;o<i.length;o++)if(e.match(i[o][1])){n=i[o][0],r=parseFloat(RegExp.$1);break}for(t.name=n,t.version=r,n="other",i=[["ios",/([0-9_]+) like Mac OS X/,function(e){return e.replace("_",".").replace("_","")}],["ios",/CPU like Mac OS X/,function(e){return 0}],["wp",/Windows Phone ([0-9\.]+)/,null],["android",/Android ([0-9\.]+)/,null],["mac",/Macintosh.+Mac OS X ([0-9_]+)/,function(e){return e.replace("_",".").replace("_","")}],["windows",/Windows NT ([0-9\.]+)/,null],["bb",/BlackBerry.+Version\/([0-9\.]+)/,null],["bb",/BB[0-9]+.+Version\/([0-9\.]+)/,null],["linux",/Linux/,null],["bsd",/BSD/,null],["unix",/X11/,null]],o=r=0;o<i.length;o++)if(e.match(i[o][1])){n=i[o][0],r=parseFloat(i[o][2]?i[o][2](RegExp.$1):RegExp.$1);break}"mac"==n&&"ontouchstart"in window&&(1024==screen.width&&1366==screen.height||834==screen.width&&1112==screen.height||810==screen.width&&1080==screen.height||768==screen.width&&1024==screen.height)&&(n="ios"),t.os=n,t.osVersion=r,t.touch="wp"==t.os?0<navigator.msMaxTouchPoints:!!("ontouchstart"in window),t.mobile="wp"==t.os||"android"==t.os||"ios"==t.os||"bb"==t.os}};return t.init(),t}();!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.browser=n()}(this,function(){return browser});
|
||||
2
inspiration/html5up/assets/js/jquery.min.js
vendored
2
inspiration/html5up/assets/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,390 +0,0 @@
|
||||
/*
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var $window = $(window),
|
||||
$body = $('body'),
|
||||
$wrapper = $('#wrapper'),
|
||||
$main = $('#main'),
|
||||
settings = {
|
||||
|
||||
// Keyboard shortcuts.
|
||||
keyboardShortcuts: {
|
||||
|
||||
// If true, enables scrolling via keyboard shortcuts.
|
||||
enabled: true,
|
||||
|
||||
// Sets the distance to scroll when using the left/right arrow keys.
|
||||
distance: 50
|
||||
|
||||
},
|
||||
|
||||
// Scroll wheel.
|
||||
scrollWheel: {
|
||||
|
||||
// If true, enables scrolling via the scroll wheel.
|
||||
enabled: true,
|
||||
|
||||
// Sets the scroll wheel factor. (Ideally) a value between 0 and 1 (lower = slower scroll, higher = faster scroll).
|
||||
factor: 1
|
||||
|
||||
},
|
||||
|
||||
// Scroll zones.
|
||||
scrollZones: {
|
||||
|
||||
// If true, enables scrolling via scroll zones on the left/right edges of the scren.
|
||||
enabled: true,
|
||||
|
||||
// Sets the speed at which the page scrolls when a scroll zone is active (higher = faster scroll, lower = slower scroll).
|
||||
speed: 15
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Breakpoints.
|
||||
breakpoints({
|
||||
xlarge: [ '1281px', '1680px' ],
|
||||
large: [ '981px', '1280px' ],
|
||||
medium: [ '737px', '980px' ],
|
||||
small: [ '481px', '736px' ],
|
||||
xsmall: [ null, '480px' ],
|
||||
});
|
||||
|
||||
// Tweaks/fixes.
|
||||
|
||||
// Mobile: Revert to native scrolling.
|
||||
if (browser.mobile) {
|
||||
|
||||
// Disable all scroll-assist features.
|
||||
settings.keyboardShortcuts.enabled = false;
|
||||
settings.scrollWheel.enabled = false;
|
||||
settings.scrollZones.enabled = false;
|
||||
|
||||
// Re-enable overflow on main.
|
||||
$main.css('overflow-x', 'auto');
|
||||
|
||||
}
|
||||
|
||||
// IE: Fix min-height/flexbox.
|
||||
if (browser.name == 'ie')
|
||||
$wrapper.css('height', '100vh');
|
||||
|
||||
// iOS: Compensate for address bar.
|
||||
if (browser.os == 'ios')
|
||||
$wrapper.css('min-height', 'calc(100vh - 30px)');
|
||||
|
||||
// Play initial animations on page load.
|
||||
$window.on('load', function() {
|
||||
window.setTimeout(function() {
|
||||
$body.removeClass('is-preload');
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Items.
|
||||
|
||||
// Assign a random "delay" class to each thumbnail item.
|
||||
$('.item.thumb').each(function() {
|
||||
$(this).addClass('delay-' + Math.floor((Math.random() * 6) + 1));
|
||||
});
|
||||
|
||||
// IE: Fix thumbnail images.
|
||||
if (browser.name == 'ie')
|
||||
$('.item.thumb').each(function() {
|
||||
|
||||
var $this = $(this),
|
||||
$img = $this.find('img');
|
||||
|
||||
$this
|
||||
.css('background-image', 'url(' + $img.attr('src') + ')')
|
||||
.css('background-size', 'cover')
|
||||
.css('background-position', 'center');
|
||||
|
||||
$img
|
||||
.css('opacity', '0');
|
||||
|
||||
});
|
||||
|
||||
// Poptrox.
|
||||
$main.poptrox({
|
||||
onPopupOpen: function() { $body.addClass('is-poptrox-visible'); },
|
||||
onPopupClose: function() { $body.removeClass('is-poptrox-visible'); },
|
||||
overlayColor: '#1a1f2c',
|
||||
overlayOpacity: 0.75,
|
||||
popupCloserText: '',
|
||||
popupLoaderText: '',
|
||||
selector: '.item.thumb a.image',
|
||||
caption: function($a) {
|
||||
return $a.prev('h2').html();
|
||||
},
|
||||
usePopupDefaultStyling: false,
|
||||
usePopupCloser: false,
|
||||
usePopupCaption: true,
|
||||
usePopupNav: true,
|
||||
windowMargin: 50
|
||||
});
|
||||
|
||||
breakpoints.on('>small', function() {
|
||||
$main[0]._poptrox.windowMargin = 50;
|
||||
});
|
||||
|
||||
breakpoints.on('<=small', function() {
|
||||
$main[0]._poptrox.windowMargin = 0;
|
||||
});
|
||||
|
||||
// Keyboard shortcuts.
|
||||
if (settings.keyboardShortcuts.enabled)
|
||||
(function() {
|
||||
|
||||
$window
|
||||
|
||||
// Keypress event.
|
||||
.on('keydown', function(event) {
|
||||
|
||||
var scrolled = false;
|
||||
|
||||
if ($body.hasClass('is-poptrox-visible'))
|
||||
return;
|
||||
|
||||
switch (event.keyCode) {
|
||||
|
||||
// Left arrow.
|
||||
case 37:
|
||||
$main.scrollLeft($main.scrollLeft() - settings.keyboardShortcuts.distance);
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
// Right arrow.
|
||||
case 39:
|
||||
$main.scrollLeft($main.scrollLeft() + settings.keyboardShortcuts.distance);
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
// Page Up.
|
||||
case 33:
|
||||
$main.scrollLeft($main.scrollLeft() - $window.width() + 100);
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
// Page Down, Space.
|
||||
case 34:
|
||||
case 32:
|
||||
$main.scrollLeft($main.scrollLeft() + $window.width() - 100);
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
// Home.
|
||||
case 36:
|
||||
$main.scrollLeft(0);
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
// End.
|
||||
case 35:
|
||||
$main.scrollLeft($main.width());
|
||||
scrolled = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Scrolled?
|
||||
if (scrolled) {
|
||||
|
||||
// Prevent default.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Stop link scroll.
|
||||
$main.stop();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
// Scroll wheel.
|
||||
if (settings.scrollWheel.enabled)
|
||||
(function() {
|
||||
|
||||
// Based on code by @miorel + @pieterv of Facebook (thanks guys :)
|
||||
// github.com/facebook/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
|
||||
var normalizeWheel = function(event) {
|
||||
|
||||
var pixelStep = 10,
|
||||
lineHeight = 40,
|
||||
pageHeight = 800,
|
||||
sX = 0,
|
||||
sY = 0,
|
||||
pX = 0,
|
||||
pY = 0;
|
||||
|
||||
// Legacy.
|
||||
if ('detail' in event)
|
||||
sY = event.detail;
|
||||
else if ('wheelDelta' in event)
|
||||
sY = event.wheelDelta / -120;
|
||||
else if ('wheelDeltaY' in event)
|
||||
sY = event.wheelDeltaY / -120;
|
||||
|
||||
if ('wheelDeltaX' in event)
|
||||
sX = event.wheelDeltaX / -120;
|
||||
|
||||
// Side scrolling on FF with DOMMouseScroll.
|
||||
if ('axis' in event
|
||||
&& event.axis === event.HORIZONTAL_AXIS) {
|
||||
sX = sY;
|
||||
sY = 0;
|
||||
}
|
||||
|
||||
// Calculate.
|
||||
pX = sX * pixelStep;
|
||||
pY = sY * pixelStep;
|
||||
|
||||
if ('deltaY' in event)
|
||||
pY = event.deltaY;
|
||||
|
||||
if ('deltaX' in event)
|
||||
pX = event.deltaX;
|
||||
|
||||
if ((pX || pY)
|
||||
&& event.deltaMode) {
|
||||
|
||||
if (event.deltaMode == 1) {
|
||||
pX *= lineHeight;
|
||||
pY *= lineHeight;
|
||||
}
|
||||
else {
|
||||
pX *= pageHeight;
|
||||
pY *= pageHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fallback if spin cannot be determined.
|
||||
if (pX && !sX)
|
||||
sX = (pX < 1) ? -1 : 1;
|
||||
|
||||
if (pY && !sY)
|
||||
sY = (pY < 1) ? -1 : 1;
|
||||
|
||||
// Return.
|
||||
return {
|
||||
spinX: sX,
|
||||
spinY: sY,
|
||||
pixelX: pX,
|
||||
pixelY: pY
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// Wheel event.
|
||||
$body.on('wheel', function(event) {
|
||||
|
||||
// Disable on <=small.
|
||||
if (breakpoints.active('<=small'))
|
||||
return;
|
||||
|
||||
// Prevent default.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Stop link scroll.
|
||||
$main.stop();
|
||||
|
||||
// Calculate delta, direction.
|
||||
var n = normalizeWheel(event.originalEvent),
|
||||
x = (n.pixelX != 0 ? n.pixelX : n.pixelY),
|
||||
delta = Math.min(Math.abs(x), 150) * settings.scrollWheel.factor,
|
||||
direction = x > 0 ? 1 : -1;
|
||||
|
||||
// Scroll page.
|
||||
$main.scrollLeft($main.scrollLeft() + (delta * direction));
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
// Scroll zones.
|
||||
if (settings.scrollZones.enabled)
|
||||
(function() {
|
||||
|
||||
var $left = $('<div class="scrollZone left"></div>'),
|
||||
$right = $('<div class="scrollZone right"></div>'),
|
||||
$zones = $left.add($right),
|
||||
paused = false,
|
||||
intervalId = null,
|
||||
direction,
|
||||
activate = function(d) {
|
||||
|
||||
// Disable on <=small.
|
||||
if (breakpoints.active('<=small'))
|
||||
return;
|
||||
|
||||
// Paused? Bail.
|
||||
if (paused)
|
||||
return;
|
||||
|
||||
// Stop link scroll.
|
||||
$main.stop();
|
||||
|
||||
// Set direction.
|
||||
direction = d;
|
||||
|
||||
// Initialize interval.
|
||||
clearInterval(intervalId);
|
||||
|
||||
intervalId = setInterval(function() {
|
||||
$main.scrollLeft($main.scrollLeft() + (settings.scrollZones.speed * direction));
|
||||
}, 25);
|
||||
|
||||
},
|
||||
deactivate = function() {
|
||||
|
||||
// Unpause.
|
||||
paused = false;
|
||||
|
||||
// Clear interval.
|
||||
clearInterval(intervalId);
|
||||
|
||||
};
|
||||
|
||||
$zones
|
||||
.appendTo($wrapper)
|
||||
.on('mouseleave mousedown', function(event) {
|
||||
deactivate();
|
||||
});
|
||||
|
||||
$left
|
||||
.css('left', '0')
|
||||
.on('mouseenter', function(event) {
|
||||
activate(-1);
|
||||
});
|
||||
|
||||
$right
|
||||
.css('right', '0')
|
||||
.on('mouseenter', function(event) {
|
||||
activate(1);
|
||||
});
|
||||
|
||||
$body
|
||||
.on('---pauseScrollZone', function(event) {
|
||||
|
||||
// Pause.
|
||||
paused = true;
|
||||
|
||||
// Unpause after delay.
|
||||
setTimeout(function() {
|
||||
paused = false;
|
||||
}, 500);
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,587 +0,0 @@
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* Generate an indented list of links from a nav. Meant for use with panel().
|
||||
* @return {jQuery} jQuery object.
|
||||
*/
|
||||
$.fn.navList = function() {
|
||||
|
||||
var $this = $(this);
|
||||
$a = $this.find('a'),
|
||||
b = [];
|
||||
|
||||
$a.each(function() {
|
||||
|
||||
var $this = $(this),
|
||||
indent = Math.max(0, $this.parents('li').length - 1),
|
||||
href = $this.attr('href'),
|
||||
target = $this.attr('target');
|
||||
|
||||
b.push(
|
||||
'<a ' +
|
||||
'class="link depth-' + indent + '"' +
|
||||
( (typeof target !== 'undefined' && target != '') ? ' target="' + target + '"' : '') +
|
||||
( (typeof href !== 'undefined' && href != '') ? ' href="' + href + '"' : '') +
|
||||
'>' +
|
||||
'<span class="indent-' + indent + '"></span>' +
|
||||
$this.text() +
|
||||
'</a>'
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
return b.join('');
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Panel-ify an element.
|
||||
* @param {object} userConfig User config.
|
||||
* @return {jQuery} jQuery object.
|
||||
*/
|
||||
$.fn.panel = function(userConfig) {
|
||||
|
||||
// No elements?
|
||||
if (this.length == 0)
|
||||
return $this;
|
||||
|
||||
// Multiple elements?
|
||||
if (this.length > 1) {
|
||||
|
||||
for (var i=0; i < this.length; i++)
|
||||
$(this[i]).panel(userConfig);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
// Vars.
|
||||
var $this = $(this),
|
||||
$body = $('body'),
|
||||
$window = $(window),
|
||||
id = $this.attr('id'),
|
||||
config;
|
||||
|
||||
// Config.
|
||||
config = $.extend({
|
||||
|
||||
// Delay.
|
||||
delay: 0,
|
||||
|
||||
// Hide panel on link click.
|
||||
hideOnClick: false,
|
||||
|
||||
// Hide panel on escape keypress.
|
||||
hideOnEscape: false,
|
||||
|
||||
// Hide panel on swipe.
|
||||
hideOnSwipe: false,
|
||||
|
||||
// Reset scroll position on hide.
|
||||
resetScroll: false,
|
||||
|
||||
// Reset forms on hide.
|
||||
resetForms: false,
|
||||
|
||||
// Side of viewport the panel will appear.
|
||||
side: null,
|
||||
|
||||
// Target element for "class".
|
||||
target: $this,
|
||||
|
||||
// Class to toggle.
|
||||
visibleClass: 'visible'
|
||||
|
||||
}, userConfig);
|
||||
|
||||
// Expand "target" if it's not a jQuery object already.
|
||||
if (typeof config.target != 'jQuery')
|
||||
config.target = $(config.target);
|
||||
|
||||
// Panel.
|
||||
|
||||
// Methods.
|
||||
$this._hide = function(event) {
|
||||
|
||||
// Already hidden? Bail.
|
||||
if (!config.target.hasClass(config.visibleClass))
|
||||
return;
|
||||
|
||||
// If an event was provided, cancel it.
|
||||
if (event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
}
|
||||
|
||||
// Hide.
|
||||
config.target.removeClass(config.visibleClass);
|
||||
|
||||
// Post-hide stuff.
|
||||
window.setTimeout(function() {
|
||||
|
||||
// Reset scroll position.
|
||||
if (config.resetScroll)
|
||||
$this.scrollTop(0);
|
||||
|
||||
// Reset forms.
|
||||
if (config.resetForms)
|
||||
$this.find('form').each(function() {
|
||||
this.reset();
|
||||
});
|
||||
|
||||
}, config.delay);
|
||||
|
||||
};
|
||||
|
||||
// Vendor fixes.
|
||||
$this
|
||||
.css('-ms-overflow-style', '-ms-autohiding-scrollbar')
|
||||
.css('-webkit-overflow-scrolling', 'touch');
|
||||
|
||||
// Hide on click.
|
||||
if (config.hideOnClick) {
|
||||
|
||||
$this.find('a')
|
||||
.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
|
||||
|
||||
$this
|
||||
.on('click', 'a', function(event) {
|
||||
|
||||
var $a = $(this),
|
||||
href = $a.attr('href'),
|
||||
target = $a.attr('target');
|
||||
|
||||
if (!href || href == '#' || href == '' || href == '#' + id)
|
||||
return;
|
||||
|
||||
// Cancel original event.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Hide panel.
|
||||
$this._hide();
|
||||
|
||||
// Redirect to href.
|
||||
window.setTimeout(function() {
|
||||
|
||||
if (target == '_blank')
|
||||
window.open(href);
|
||||
else
|
||||
window.location.href = href;
|
||||
|
||||
}, config.delay + 10);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Event: Touch stuff.
|
||||
$this.on('touchstart', function(event) {
|
||||
|
||||
$this.touchPosX = event.originalEvent.touches[0].pageX;
|
||||
$this.touchPosY = event.originalEvent.touches[0].pageY;
|
||||
|
||||
})
|
||||
|
||||
$this.on('touchmove', function(event) {
|
||||
|
||||
if ($this.touchPosX === null
|
||||
|| $this.touchPosY === null)
|
||||
return;
|
||||
|
||||
var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
|
||||
diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
|
||||
th = $this.outerHeight(),
|
||||
ts = ($this.get(0).scrollHeight - $this.scrollTop());
|
||||
|
||||
// Hide on swipe?
|
||||
if (config.hideOnSwipe) {
|
||||
|
||||
var result = false,
|
||||
boundary = 20,
|
||||
delta = 50;
|
||||
|
||||
switch (config.side) {
|
||||
|
||||
case 'left':
|
||||
result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
|
||||
break;
|
||||
|
||||
case 'right':
|
||||
result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
|
||||
break;
|
||||
|
||||
case 'top':
|
||||
result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
|
||||
break;
|
||||
|
||||
case 'bottom':
|
||||
result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
||||
$this.touchPosX = null;
|
||||
$this.touchPosY = null;
|
||||
$this._hide();
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Prevent vertical scrolling past the top or bottom.
|
||||
if (($this.scrollTop() < 0 && diffY < 0)
|
||||
|| (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Event: Prevent certain events inside the panel from bubbling.
|
||||
$this.on('click touchend touchstart touchmove', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
// Event: Hide panel if a child anchor tag pointing to its ID is clicked.
|
||||
$this.on('click', 'a[href="#' + id + '"]', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
config.target.removeClass(config.visibleClass);
|
||||
|
||||
});
|
||||
|
||||
// Body.
|
||||
|
||||
// Event: Hide panel on body click/tap.
|
||||
$body.on('click touchend', function(event) {
|
||||
$this._hide(event);
|
||||
});
|
||||
|
||||
// Event: Toggle.
|
||||
$body.on('click', 'a[href="#' + id + '"]', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
config.target.toggleClass(config.visibleClass);
|
||||
|
||||
});
|
||||
|
||||
// Window.
|
||||
|
||||
// Event: Hide on ESC.
|
||||
if (config.hideOnEscape)
|
||||
$window.on('keydown', function(event) {
|
||||
|
||||
if (event.keyCode == 27)
|
||||
$this._hide(event);
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply "placeholder" attribute polyfill to one or more forms.
|
||||
* @return {jQuery} jQuery object.
|
||||
*/
|
||||
$.fn.placeholder = function() {
|
||||
|
||||
// Browser natively supports placeholders? Bail.
|
||||
if (typeof (document.createElement('input')).placeholder != 'undefined')
|
||||
return $(this);
|
||||
|
||||
// No elements?
|
||||
if (this.length == 0)
|
||||
return $this;
|
||||
|
||||
// Multiple elements?
|
||||
if (this.length > 1) {
|
||||
|
||||
for (var i=0; i < this.length; i++)
|
||||
$(this[i]).placeholder();
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
// Vars.
|
||||
var $this = $(this);
|
||||
|
||||
// Text, TextArea.
|
||||
$this.find('input[type=text],textarea')
|
||||
.each(function() {
|
||||
|
||||
var i = $(this);
|
||||
|
||||
if (i.val() == ''
|
||||
|| i.val() == i.attr('placeholder'))
|
||||
i
|
||||
.addClass('polyfill-placeholder')
|
||||
.val(i.attr('placeholder'));
|
||||
|
||||
})
|
||||
.on('blur', function() {
|
||||
|
||||
var i = $(this);
|
||||
|
||||
if (i.attr('name').match(/-polyfill-field$/))
|
||||
return;
|
||||
|
||||
if (i.val() == '')
|
||||
i
|
||||
.addClass('polyfill-placeholder')
|
||||
.val(i.attr('placeholder'));
|
||||
|
||||
})
|
||||
.on('focus', function() {
|
||||
|
||||
var i = $(this);
|
||||
|
||||
if (i.attr('name').match(/-polyfill-field$/))
|
||||
return;
|
||||
|
||||
if (i.val() == i.attr('placeholder'))
|
||||
i
|
||||
.removeClass('polyfill-placeholder')
|
||||
.val('');
|
||||
|
||||
});
|
||||
|
||||
// Password.
|
||||
$this.find('input[type=password]')
|
||||
.each(function() {
|
||||
|
||||
var i = $(this);
|
||||
var x = $(
|
||||
$('<div>')
|
||||
.append(i.clone())
|
||||
.remove()
|
||||
.html()
|
||||
.replace(/type="password"/i, 'type="text"')
|
||||
.replace(/type=password/i, 'type=text')
|
||||
);
|
||||
|
||||
if (i.attr('id') != '')
|
||||
x.attr('id', i.attr('id') + '-polyfill-field');
|
||||
|
||||
if (i.attr('name') != '')
|
||||
x.attr('name', i.attr('name') + '-polyfill-field');
|
||||
|
||||
x.addClass('polyfill-placeholder')
|
||||
.val(x.attr('placeholder')).insertAfter(i);
|
||||
|
||||
if (i.val() == '')
|
||||
i.hide();
|
||||
else
|
||||
x.hide();
|
||||
|
||||
i
|
||||
.on('blur', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
|
||||
|
||||
if (i.val() == '') {
|
||||
|
||||
i.hide();
|
||||
x.show();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
x
|
||||
.on('focus', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var i = x.parent().find('input[name=' + x.attr('name').replace('-polyfill-field', '') + ']');
|
||||
|
||||
x.hide();
|
||||
|
||||
i
|
||||
.show()
|
||||
.focus();
|
||||
|
||||
})
|
||||
.on('keypress', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
x.val('');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Events.
|
||||
$this
|
||||
.on('submit', function() {
|
||||
|
||||
$this.find('input[type=text],input[type=password],textarea')
|
||||
.each(function(event) {
|
||||
|
||||
var i = $(this);
|
||||
|
||||
if (i.attr('name').match(/-polyfill-field$/))
|
||||
i.attr('name', '');
|
||||
|
||||
if (i.val() == i.attr('placeholder')) {
|
||||
|
||||
i.removeClass('polyfill-placeholder');
|
||||
i.val('');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
.on('reset', function(event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
$this.find('select')
|
||||
.val($('option:first').val());
|
||||
|
||||
$this.find('input,textarea')
|
||||
.each(function() {
|
||||
|
||||
var i = $(this),
|
||||
x;
|
||||
|
||||
i.removeClass('polyfill-placeholder');
|
||||
|
||||
switch (this.type) {
|
||||
|
||||
case 'submit':
|
||||
case 'reset':
|
||||
break;
|
||||
|
||||
case 'password':
|
||||
i.val(i.attr('defaultValue'));
|
||||
|
||||
x = i.parent().find('input[name=' + i.attr('name') + '-polyfill-field]');
|
||||
|
||||
if (i.val() == '') {
|
||||
i.hide();
|
||||
x.show();
|
||||
}
|
||||
else {
|
||||
i.show();
|
||||
x.hide();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
i.attr('checked', i.attr('defaultValue'));
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
i.val(i.attr('defaultValue'));
|
||||
|
||||
if (i.val() == '') {
|
||||
i.addClass('polyfill-placeholder');
|
||||
i.val(i.attr('placeholder'));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
i.val(i.attr('defaultValue'));
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves elements to/from the first positions of their respective parents.
|
||||
* @param {jQuery} $elements Elements (or selector) to move.
|
||||
* @param {bool} condition If true, moves elements to the top. Otherwise, moves elements back to their original locations.
|
||||
*/
|
||||
$.prioritize = function($elements, condition) {
|
||||
|
||||
var key = '__prioritize';
|
||||
|
||||
// Expand $elements if it's not already a jQuery object.
|
||||
if (typeof $elements != 'jQuery')
|
||||
$elements = $($elements);
|
||||
|
||||
// Step through elements.
|
||||
$elements.each(function() {
|
||||
|
||||
var $e = $(this), $p,
|
||||
$parent = $e.parent();
|
||||
|
||||
// No parent? Bail.
|
||||
if ($parent.length == 0)
|
||||
return;
|
||||
|
||||
// Not moved? Move it.
|
||||
if (!$e.data(key)) {
|
||||
|
||||
// Condition is false? Bail.
|
||||
if (!condition)
|
||||
return;
|
||||
|
||||
// Get placeholder (which will serve as our point of reference for when this element needs to move back).
|
||||
$p = $e.prev();
|
||||
|
||||
// Couldn't find anything? Means this element's already at the top, so bail.
|
||||
if ($p.length == 0)
|
||||
return;
|
||||
|
||||
// Move element to top of parent.
|
||||
$e.prependTo($parent);
|
||||
|
||||
// Mark element as moved.
|
||||
$e.data(key, $p);
|
||||
|
||||
}
|
||||
|
||||
// Moved already?
|
||||
else {
|
||||
|
||||
// Condition is true? Bail.
|
||||
if (condition)
|
||||
return;
|
||||
|
||||
$p = $e.data(key);
|
||||
|
||||
// Move element back to its original location (using our placeholder).
|
||||
$e.insertAfter($p);
|
||||
|
||||
// Unmark element as moved.
|
||||
$e.removeData(key);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,223 +0,0 @@
|
||||
// breakpoints.scss v1.0 | @ajlkn | MIT licensed */
|
||||
|
||||
// Vars.
|
||||
|
||||
/// Breakpoints.
|
||||
/// @var {list}
|
||||
$breakpoints: () !global;
|
||||
|
||||
// Mixins.
|
||||
|
||||
/// Sets breakpoints.
|
||||
/// @param {map} $x Breakpoints.
|
||||
@mixin breakpoints($x: ()) {
|
||||
$breakpoints: $x !global;
|
||||
}
|
||||
|
||||
/// Wraps @content in a @media block targeting a specific orientation.
|
||||
/// @param {string} $orientation Orientation.
|
||||
@mixin orientation($orientation) {
|
||||
@media screen and (orientation: #{$orientation}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps @content in a @media block using a given query.
|
||||
/// @param {string} $query Query.
|
||||
@mixin breakpoint($query: null) {
|
||||
|
||||
$breakpoint: null;
|
||||
$op: null;
|
||||
$media: null;
|
||||
|
||||
// Determine operator, breakpoint.
|
||||
|
||||
// Greater than or equal.
|
||||
@if (str-slice($query, 0, 2) == '>=') {
|
||||
|
||||
$op: 'gte';
|
||||
$breakpoint: str-slice($query, 3);
|
||||
|
||||
}
|
||||
|
||||
// Less than or equal.
|
||||
@elseif (str-slice($query, 0, 2) == '<=') {
|
||||
|
||||
$op: 'lte';
|
||||
$breakpoint: str-slice($query, 3);
|
||||
|
||||
}
|
||||
|
||||
// Greater than.
|
||||
@elseif (str-slice($query, 0, 1) == '>') {
|
||||
|
||||
$op: 'gt';
|
||||
$breakpoint: str-slice($query, 2);
|
||||
|
||||
}
|
||||
|
||||
// Less than.
|
||||
@elseif (str-slice($query, 0, 1) == '<') {
|
||||
|
||||
$op: 'lt';
|
||||
$breakpoint: str-slice($query, 2);
|
||||
|
||||
}
|
||||
|
||||
// Not.
|
||||
@elseif (str-slice($query, 0, 1) == '!') {
|
||||
|
||||
$op: 'not';
|
||||
$breakpoint: str-slice($query, 2);
|
||||
|
||||
}
|
||||
|
||||
// Equal.
|
||||
@else {
|
||||
|
||||
$op: 'eq';
|
||||
$breakpoint: $query;
|
||||
|
||||
}
|
||||
|
||||
// Build media.
|
||||
@if ($breakpoint and map-has-key($breakpoints, $breakpoint)) {
|
||||
|
||||
$a: map-get($breakpoints, $breakpoint);
|
||||
|
||||
// Range.
|
||||
@if (type-of($a) == 'list') {
|
||||
|
||||
$x: nth($a, 1);
|
||||
$y: nth($a, 2);
|
||||
|
||||
// Max only.
|
||||
@if ($x == null) {
|
||||
|
||||
// Greater than or equal (>= 0 / anything)
|
||||
@if ($op == 'gte') {
|
||||
$media: 'screen';
|
||||
}
|
||||
|
||||
// Less than or equal (<= y)
|
||||
@elseif ($op == 'lte') {
|
||||
$media: 'screen and (max-width: ' + $y + ')';
|
||||
}
|
||||
|
||||
// Greater than (> y)
|
||||
@elseif ($op == 'gt') {
|
||||
$media: 'screen and (min-width: ' + ($y + 1) + ')';
|
||||
}
|
||||
|
||||
// Less than (< 0 / invalid)
|
||||
@elseif ($op == 'lt') {
|
||||
$media: 'screen and (max-width: -1px)';
|
||||
}
|
||||
|
||||
// Not (> y)
|
||||
@elseif ($op == 'not') {
|
||||
$media: 'screen and (min-width: ' + ($y + 1) + ')';
|
||||
}
|
||||
|
||||
// Equal (<= y)
|
||||
@else {
|
||||
$media: 'screen and (max-width: ' + $y + ')';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Min only.
|
||||
@else if ($y == null) {
|
||||
|
||||
// Greater than or equal (>= x)
|
||||
@if ($op == 'gte') {
|
||||
$media: 'screen and (min-width: ' + $x + ')';
|
||||
}
|
||||
|
||||
// Less than or equal (<= inf / anything)
|
||||
@elseif ($op == 'lte') {
|
||||
$media: 'screen';
|
||||
}
|
||||
|
||||
// Greater than (> inf / invalid)
|
||||
@elseif ($op == 'gt') {
|
||||
$media: 'screen and (max-width: -1px)';
|
||||
}
|
||||
|
||||
// Less than (< x)
|
||||
@elseif ($op == 'lt') {
|
||||
$media: 'screen and (max-width: ' + ($x - 1) + ')';
|
||||
}
|
||||
|
||||
// Not (< x)
|
||||
@elseif ($op == 'not') {
|
||||
$media: 'screen and (max-width: ' + ($x - 1) + ')';
|
||||
}
|
||||
|
||||
// Equal (>= x)
|
||||
@else {
|
||||
$media: 'screen and (min-width: ' + $x + ')';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Min and max.
|
||||
@else {
|
||||
|
||||
// Greater than or equal (>= x)
|
||||
@if ($op == 'gte') {
|
||||
$media: 'screen and (min-width: ' + $x + ')';
|
||||
}
|
||||
|
||||
// Less than or equal (<= y)
|
||||
@elseif ($op == 'lte') {
|
||||
$media: 'screen and (max-width: ' + $y + ')';
|
||||
}
|
||||
|
||||
// Greater than (> y)
|
||||
@elseif ($op == 'gt') {
|
||||
$media: 'screen and (min-width: ' + ($y + 1) + ')';
|
||||
}
|
||||
|
||||
// Less than (< x)
|
||||
@elseif ($op == 'lt') {
|
||||
$media: 'screen and (max-width: ' + ($x - 1) + ')';
|
||||
}
|
||||
|
||||
// Not (< x and > y)
|
||||
@elseif ($op == 'not') {
|
||||
$media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')';
|
||||
}
|
||||
|
||||
// Equal (>= x and <= y)
|
||||
@else {
|
||||
$media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// String.
|
||||
@else {
|
||||
|
||||
// Missing a media type? Prefix with "screen".
|
||||
@if (str-slice($a, 0, 1) == '(') {
|
||||
$media: 'screen and ' + $a;
|
||||
}
|
||||
|
||||
// Otherwise, use as-is.
|
||||
@else {
|
||||
$media: $a;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Output.
|
||||
@media #{$media} {
|
||||
@content;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/// Removes a specific item from a list.
|
||||
/// @author Hugo Giraudel
|
||||
/// @param {list} $list List.
|
||||
/// @param {integer} $index Index.
|
||||
/// @return {list} Updated list.
|
||||
@function remove-nth($list, $index) {
|
||||
|
||||
$result: null;
|
||||
|
||||
@if type-of($index) != number {
|
||||
@warn "$index: #{quote($index)} is not a number for `remove-nth`.";
|
||||
}
|
||||
@else if $index == 0 {
|
||||
@warn "List index 0 must be a non-zero integer for `remove-nth`.";
|
||||
}
|
||||
@else if abs($index) > length($list) {
|
||||
@warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`.";
|
||||
}
|
||||
@else {
|
||||
|
||||
$result: ();
|
||||
$index: if($index < 0, length($list) + $index + 1, $index);
|
||||
|
||||
@for $i from 1 through length($list) {
|
||||
|
||||
@if $i != $index {
|
||||
$result: append($result, nth($list, $i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@return $result;
|
||||
|
||||
}
|
||||
|
||||
/// Gets a value from a map.
|
||||
/// @author Hugo Giraudel
|
||||
/// @param {map} $map Map.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function val($map, $keys...) {
|
||||
|
||||
@if nth($keys, 1) == null {
|
||||
$keys: remove-nth($keys, 1);
|
||||
}
|
||||
|
||||
@each $key in $keys {
|
||||
$map: map-get($map, $key);
|
||||
}
|
||||
|
||||
@return $map;
|
||||
|
||||
}
|
||||
|
||||
/// Gets a duration value.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function _duration($keys...) {
|
||||
@return val($duration, $keys...);
|
||||
}
|
||||
|
||||
/// Gets a font value.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function _font($keys...) {
|
||||
@return val($font, $keys...);
|
||||
}
|
||||
|
||||
/// Gets a misc value.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function _misc($keys...) {
|
||||
@return val($misc, $keys...);
|
||||
}
|
||||
|
||||
/// Gets a palette value.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function _palette($keys...) {
|
||||
@return val($palette, $keys...);
|
||||
}
|
||||
|
||||
/// Gets a size value.
|
||||
/// @param {string} $keys Key(s).
|
||||
/// @return {string} Value.
|
||||
@function _size($keys...) {
|
||||
@return val($size, $keys...);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/// Makes an element's :before pseudoelement a FontAwesome icon.
|
||||
/// @param {string} $content Optional content value to use.
|
||||
/// @param {string} $category Optional category to use.
|
||||
/// @param {string} $where Optional pseudoelement to target (before or after).
|
||||
@mixin icon($content: false, $category: regular, $where: before) {
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
&:#{$where} {
|
||||
|
||||
@if $content {
|
||||
content: $content;
|
||||
}
|
||||
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
line-height: 1;
|
||||
text-transform: none !important;
|
||||
|
||||
@if ($category == brands) {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
}
|
||||
@elseif ($category == solid) {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 900;
|
||||
}
|
||||
@else {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Applies padding to an element, taking the current element-margin value into account.
|
||||
/// @param {mixed} $tb Top/bottom padding.
|
||||
/// @param {mixed} $lr Left/right padding.
|
||||
/// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left)
|
||||
/// @param {bool} $important If true, adds !important.
|
||||
@mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) {
|
||||
|
||||
@if $important {
|
||||
$important: '!important';
|
||||
}
|
||||
|
||||
$x: 0.1em;
|
||||
|
||||
@if unit(_size(element-margin)) == 'rem' {
|
||||
$x: 0.1rem;
|
||||
}
|
||||
|
||||
padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important};
|
||||
|
||||
}
|
||||
|
||||
/// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp).
|
||||
/// @param {string} $svg SVG data URL.
|
||||
/// @return {string} Encoded SVG data URL.
|
||||
@function svg-url($svg) {
|
||||
|
||||
$svg: str-replace($svg, '"', '\'');
|
||||
$svg: str-replace($svg, '%', '%25');
|
||||
$svg: str-replace($svg, '<', '%3C');
|
||||
$svg: str-replace($svg, '>', '%3E');
|
||||
$svg: str-replace($svg, '&', '%26');
|
||||
$svg: str-replace($svg, '#', '%23');
|
||||
$svg: str-replace($svg, '{', '%7B');
|
||||
$svg: str-replace($svg, '}', '%7D');
|
||||
$svg: str-replace($svg, ';', '%3B');
|
||||
|
||||
@return url("data:image/svg+xml;charset=utf8,#{$svg}");
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Misc.
|
||||
$misc: (
|
||||
z-index-base: 10000
|
||||
);
|
||||
|
||||
// Duration.
|
||||
$duration: (
|
||||
);
|
||||
|
||||
// Size.
|
||||
$size: (
|
||||
);
|
||||
|
||||
// Font.
|
||||
$font: (
|
||||
);
|
||||
|
||||
// Palette.
|
||||
$palette: (
|
||||
);
|
||||
@@ -1,376 +0,0 @@
|
||||
// vendor.scss v1.0 | @ajlkn | MIT licensed */
|
||||
|
||||
// Vars.
|
||||
|
||||
/// Vendor prefixes.
|
||||
/// @var {list}
|
||||
$vendor-prefixes: (
|
||||
'-moz-',
|
||||
'-webkit-',
|
||||
'-ms-',
|
||||
''
|
||||
);
|
||||
|
||||
/// Properties that should be vendorized.
|
||||
/// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org
|
||||
/// @var {list}
|
||||
$vendor-properties: (
|
||||
|
||||
// Animation.
|
||||
'animation',
|
||||
'animation-delay',
|
||||
'animation-direction',
|
||||
'animation-duration',
|
||||
'animation-fill-mode',
|
||||
'animation-iteration-count',
|
||||
'animation-name',
|
||||
'animation-play-state',
|
||||
'animation-timing-function',
|
||||
|
||||
// Appearance.
|
||||
'appearance',
|
||||
|
||||
// Backdrop filter.
|
||||
'backdrop-filter',
|
||||
|
||||
// Background image options.
|
||||
'background-clip',
|
||||
'background-origin',
|
||||
'background-size',
|
||||
|
||||
// Box sizing.
|
||||
'box-sizing',
|
||||
|
||||
// Clip path.
|
||||
'clip-path',
|
||||
|
||||
// Filter effects.
|
||||
'filter',
|
||||
|
||||
// Flexbox.
|
||||
'align-content',
|
||||
'align-items',
|
||||
'align-self',
|
||||
'flex',
|
||||
'flex-basis',
|
||||
'flex-direction',
|
||||
'flex-flow',
|
||||
'flex-grow',
|
||||
'flex-shrink',
|
||||
'flex-wrap',
|
||||
'justify-content',
|
||||
'order',
|
||||
|
||||
// Font feature.
|
||||
'font-feature-settings',
|
||||
'font-language-override',
|
||||
'font-variant-ligatures',
|
||||
|
||||
// Font kerning.
|
||||
'font-kerning',
|
||||
|
||||
// Fragmented borders and backgrounds.
|
||||
'box-decoration-break',
|
||||
|
||||
// Grid layout.
|
||||
'grid-column',
|
||||
'grid-column-align',
|
||||
'grid-column-end',
|
||||
'grid-column-start',
|
||||
'grid-row',
|
||||
'grid-row-align',
|
||||
'grid-row-end',
|
||||
'grid-row-start',
|
||||
'grid-template-columns',
|
||||
'grid-template-rows',
|
||||
|
||||
// Hyphens.
|
||||
'hyphens',
|
||||
'word-break',
|
||||
|
||||
// Masks.
|
||||
'mask',
|
||||
'mask-border',
|
||||
'mask-border-outset',
|
||||
'mask-border-repeat',
|
||||
'mask-border-slice',
|
||||
'mask-border-source',
|
||||
'mask-border-width',
|
||||
'mask-clip',
|
||||
'mask-composite',
|
||||
'mask-image',
|
||||
'mask-origin',
|
||||
'mask-position',
|
||||
'mask-repeat',
|
||||
'mask-size',
|
||||
|
||||
// Multicolumn.
|
||||
'break-after',
|
||||
'break-before',
|
||||
'break-inside',
|
||||
'column-count',
|
||||
'column-fill',
|
||||
'column-gap',
|
||||
'column-rule',
|
||||
'column-rule-color',
|
||||
'column-rule-style',
|
||||
'column-rule-width',
|
||||
'column-span',
|
||||
'column-width',
|
||||
'columns',
|
||||
|
||||
// Object fit.
|
||||
'object-fit',
|
||||
'object-position',
|
||||
|
||||
// Regions.
|
||||
'flow-from',
|
||||
'flow-into',
|
||||
'region-fragment',
|
||||
|
||||
// Scroll snap points.
|
||||
'scroll-snap-coordinate',
|
||||
'scroll-snap-destination',
|
||||
'scroll-snap-points-x',
|
||||
'scroll-snap-points-y',
|
||||
'scroll-snap-type',
|
||||
|
||||
// Shapes.
|
||||
'shape-image-threshold',
|
||||
'shape-margin',
|
||||
'shape-outside',
|
||||
|
||||
// Tab size.
|
||||
'tab-size',
|
||||
|
||||
// Text align last.
|
||||
'text-align-last',
|
||||
|
||||
// Text decoration.
|
||||
'text-decoration-color',
|
||||
'text-decoration-line',
|
||||
'text-decoration-skip',
|
||||
'text-decoration-style',
|
||||
|
||||
// Text emphasis.
|
||||
'text-emphasis',
|
||||
'text-emphasis-color',
|
||||
'text-emphasis-position',
|
||||
'text-emphasis-style',
|
||||
|
||||
// Text size adjust.
|
||||
'text-size-adjust',
|
||||
|
||||
// Text spacing.
|
||||
'text-spacing',
|
||||
|
||||
// Transform.
|
||||
'transform',
|
||||
'transform-origin',
|
||||
|
||||
// Transform 3D.
|
||||
'backface-visibility',
|
||||
'perspective',
|
||||
'perspective-origin',
|
||||
'transform-style',
|
||||
|
||||
// Transition.
|
||||
'transition',
|
||||
'transition-delay',
|
||||
'transition-duration',
|
||||
'transition-property',
|
||||
'transition-timing-function',
|
||||
|
||||
// Unicode bidi.
|
||||
'unicode-bidi',
|
||||
|
||||
// User select.
|
||||
'user-select',
|
||||
|
||||
// Writing mode.
|
||||
'writing-mode',
|
||||
|
||||
);
|
||||
|
||||
/// Values that should be vendorized.
|
||||
/// Data via caniuse.com, github.com/postcss/autoprefixer, and developer.mozilla.org
|
||||
/// @var {list}
|
||||
$vendor-values: (
|
||||
|
||||
// Cross fade.
|
||||
'cross-fade',
|
||||
|
||||
// Element function.
|
||||
'element',
|
||||
|
||||
// Filter function.
|
||||
'filter',
|
||||
|
||||
// Flexbox.
|
||||
'flex',
|
||||
'inline-flex',
|
||||
|
||||
// Grab cursors.
|
||||
'grab',
|
||||
'grabbing',
|
||||
|
||||
// Gradients.
|
||||
'linear-gradient',
|
||||
'repeating-linear-gradient',
|
||||
'radial-gradient',
|
||||
'repeating-radial-gradient',
|
||||
|
||||
// Grid layout.
|
||||
'grid',
|
||||
'inline-grid',
|
||||
|
||||
// Image set.
|
||||
'image-set',
|
||||
|
||||
// Intrinsic width.
|
||||
'max-content',
|
||||
'min-content',
|
||||
'fit-content',
|
||||
'fill',
|
||||
'fill-available',
|
||||
'stretch',
|
||||
|
||||
// Sticky position.
|
||||
'sticky',
|
||||
|
||||
// Transform.
|
||||
'transform',
|
||||
|
||||
// Zoom cursors.
|
||||
'zoom-in',
|
||||
'zoom-out',
|
||||
|
||||
);
|
||||
|
||||
// Functions.
|
||||
|
||||
/// Removes a specific item from a list.
|
||||
/// @author Hugo Giraudel
|
||||
/// @param {list} $list List.
|
||||
/// @param {integer} $index Index.
|
||||
/// @return {list} Updated list.
|
||||
@function remove-nth($list, $index) {
|
||||
|
||||
$result: null;
|
||||
|
||||
@if type-of($index) != number {
|
||||
@warn "$index: #{quote($index)} is not a number for `remove-nth`.";
|
||||
}
|
||||
@else if $index == 0 {
|
||||
@warn "List index 0 must be a non-zero integer for `remove-nth`.";
|
||||
}
|
||||
@else if abs($index) > length($list) {
|
||||
@warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`.";
|
||||
}
|
||||
@else {
|
||||
|
||||
$result: ();
|
||||
$index: if($index < 0, length($list) + $index + 1, $index);
|
||||
|
||||
@for $i from 1 through length($list) {
|
||||
|
||||
@if $i != $index {
|
||||
$result: append($result, nth($list, $i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@return $result;
|
||||
|
||||
}
|
||||
|
||||
/// Replaces a substring within another string.
|
||||
/// @author Hugo Giraudel
|
||||
/// @param {string} $string String.
|
||||
/// @param {string} $search Substring.
|
||||
/// @param {string} $replace Replacement.
|
||||
/// @return {string} Updated string.
|
||||
@function str-replace($string, $search, $replace: '') {
|
||||
|
||||
$index: str-index($string, $search);
|
||||
|
||||
@if $index {
|
||||
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
|
||||
}
|
||||
|
||||
@return $string;
|
||||
|
||||
}
|
||||
|
||||
/// Replaces a substring within each string in a list.
|
||||
/// @param {list} $strings List of strings.
|
||||
/// @param {string} $search Substring.
|
||||
/// @param {string} $replace Replacement.
|
||||
/// @return {list} Updated list of strings.
|
||||
@function str-replace-all($strings, $search, $replace: '') {
|
||||
|
||||
@each $string in $strings {
|
||||
$strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace));
|
||||
}
|
||||
|
||||
@return $strings;
|
||||
|
||||
}
|
||||
|
||||
// Mixins.
|
||||
|
||||
/// Wraps @content in vendorized keyframe blocks.
|
||||
/// @param {string} $name Name.
|
||||
@mixin keyframes($name) {
|
||||
|
||||
@-moz-keyframes #{$name} { @content; }
|
||||
@-webkit-keyframes #{$name} { @content; }
|
||||
@-ms-keyframes #{$name} { @content; }
|
||||
@keyframes #{$name} { @content; }
|
||||
|
||||
}
|
||||
|
||||
/// Vendorizes a declaration's property and/or value(s).
|
||||
/// @param {string} $property Property.
|
||||
/// @param {mixed} $value String/list of value(s).
|
||||
@mixin vendor($property, $value) {
|
||||
|
||||
// Determine if property should expand.
|
||||
$expandProperty: index($vendor-properties, $property);
|
||||
|
||||
// Determine if value should expand (and if so, add '-prefix-' placeholder).
|
||||
$expandValue: false;
|
||||
|
||||
@each $x in $value {
|
||||
@each $y in $vendor-values {
|
||||
@if $y == str-slice($x, 1, str-length($y)) {
|
||||
|
||||
$value: set-nth($value, index($value, $x), '-prefix-' + $x);
|
||||
$expandValue: true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand property?
|
||||
@if $expandProperty {
|
||||
@each $vendor in $vendor-prefixes {
|
||||
#{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)};
|
||||
}
|
||||
}
|
||||
|
||||
// Expand just the value?
|
||||
@elseif $expandValue {
|
||||
@each $vendor in $vendor-prefixes {
|
||||
#{$property}: #{str-replace-all($value, '-prefix-', $vendor)};
|
||||
}
|
||||
}
|
||||
|
||||
// Neither? Treat them as a normal declaration.
|
||||
@else {
|
||||
#{$property}: #{$value};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,681 +0,0 @@
|
||||
@import 'libs/vars';
|
||||
@import 'libs/functions';
|
||||
@import 'libs/mixins';
|
||||
@import 'libs/vendor';
|
||||
@import 'libs/breakpoints';
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600');
|
||||
@import url('fontawesome-all.min.css');
|
||||
|
||||
/*
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
|
||||
// Breakpoints.
|
||||
|
||||
@include breakpoints((
|
||||
xlarge: ( 1281px, 1680px ),
|
||||
large: ( 981px, 1280px ),
|
||||
medium: ( 737px, 980px ),
|
||||
small: ( 481px, 736px ),
|
||||
xsmall: ( null, 480px )
|
||||
));
|
||||
|
||||
// Reset.
|
||||
// Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain)
|
||||
|
||||
html, body, div, span, applet, object,
|
||||
iframe, h1, h2, h3, h4, h5, h6, p, blockquote,
|
||||
pre, a, abbr, acronym, address, big, cite,
|
||||
code, del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var, b,
|
||||
u, i, center, dl, dt, dd, ol, ul, li, fieldset,
|
||||
form, label, legend, table, caption, tbody,
|
||||
tfoot, thead, tr, th, td, article, aside,
|
||||
canvas, details, embed, figure, figcaption,
|
||||
footer, header, hgroup, menu, nav, output, ruby,
|
||||
section, summary, time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
/* Basic */
|
||||
|
||||
// Set box model to border-box.
|
||||
// Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
@include vendor('background-image', ('url("images/overlay.png")', 'linear-gradient(top, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0.65))', 'url("../../images/bg.jpg")'));
|
||||
background-position: top left, bottom left, auto;
|
||||
background-size: auto, 100% 100%, cover;
|
||||
background-attachment: fixed;
|
||||
background-repeat: repeat, no-repeat, auto;
|
||||
position: relative;
|
||||
background-color: #150C07;
|
||||
line-height: 1.75em;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
// Stops initial animations until page loads.
|
||||
&.is-preload {
|
||||
*, *:before, *:after {
|
||||
@include vendor('animation', 'none !important');
|
||||
@include vendor('transition', 'none !important');
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=large') {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
background-attachment: scroll;
|
||||
background-size: auto, 100% 100%, 250% auto;
|
||||
background-repeat: repeat, no-repeat, no-repeat;
|
||||
background-position: top left, bottom left, 50% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
body, input, select, textarea {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-weight: 400;
|
||||
padding: 0;
|
||||
font-size: 13pt;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
|
||||
@include breakpoint('<=xlarge') {
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
@include breakpoint('<=large') {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
@include breakpoint('<=medium') {
|
||||
font-size: 11pt;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
text-decoration: none;
|
||||
@include vendor('transition', 'color 0.25s ease-in-out');
|
||||
|
||||
&:hover {
|
||||
color: #d64760;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
|
||||
strong, b {
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* List */
|
||||
|
||||
ul {
|
||||
&.icons {
|
||||
cursor: default;
|
||||
margin: 0 0 0.5em 0;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
font-size: 1.5em;
|
||||
margin-left: 1em;
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
opacity: 0.35;
|
||||
color: #fff;
|
||||
@include vendor('transition', 'opacity 0.25s ease-in-out');
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
|
||||
.icon {
|
||||
@include icon;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
|
||||
&:before {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
> .label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.solid {
|
||||
&:before {
|
||||
font-weight: 900;
|
||||
}
|
||||
}
|
||||
|
||||
&.brands {
|
||||
&:before {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wrapper */
|
||||
|
||||
#wrapper {
|
||||
min-height: 100vh;
|
||||
@include vendor('display', 'flex');
|
||||
@include vendor('flex-direction', 'column');
|
||||
@include vendor('justify-content', 'space-between');
|
||||
@include vendor('align-items', 'center');
|
||||
@include vendor('transition', 'filter 0.5s ease-in-out');
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
|
||||
body.is-poptrox-visible & {
|
||||
@include vendor('filter', 'blur(0.25em)');
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scroll Zone */
|
||||
|
||||
.scrollZone {
|
||||
position: fixed;
|
||||
width: 6rem;
|
||||
height: 100vh;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -ms-grab;
|
||||
cursor: grab;
|
||||
z-index: _misc(z-index-base) + 1;
|
||||
|
||||
&.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main */
|
||||
|
||||
#main {
|
||||
$border: 10px;
|
||||
|
||||
@include vendor('transition', 'opacity 1s ease-in-out');
|
||||
@include vendor('transition-delay', '0.5s');
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: -moz-min-content;
|
||||
width: -webkit-min-content;
|
||||
width: -ms-min-content;
|
||||
width: min-content;
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: ($border * 0.5);
|
||||
|
||||
.items {
|
||||
@include vendor('display', 'flex');
|
||||
|
||||
> :last-child {
|
||||
border-right: solid $border #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
$width: 20em;
|
||||
|
||||
@include vendor('flex-grow', '0');
|
||||
@include vendor('flex-shrink', '0');
|
||||
margin: ($border * 0.5);
|
||||
height: 16em;
|
||||
box-shadow: 0 0 0 $border #ffffff;
|
||||
|
||||
&.span-1 {
|
||||
width: $width;
|
||||
}
|
||||
|
||||
&.span-2 {
|
||||
width: $width * 1.5;
|
||||
}
|
||||
|
||||
&.span-3 {
|
||||
width: $width * 2;
|
||||
}
|
||||
|
||||
&.intro {
|
||||
background-color: #d64760;
|
||||
background-image: url('images/overlay.png');
|
||||
@include vendor('display', 'flex');
|
||||
@include vendor('flex-direction', 'column');
|
||||
@include vendor('justify-content', 'center');
|
||||
padding: 1em 3em;
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.5em;
|
||||
margin: 0.5em 0 0 0;
|
||||
opacity: 0.65;
|
||||
}
|
||||
}
|
||||
|
||||
&.thumb {
|
||||
display: block;
|
||||
position: relative;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
cursor: default;
|
||||
|
||||
h2 {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(18, 21, 29, 0.85);
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include vendor('object-fit', 'cover');
|
||||
@include vendor('object-position', 'center');
|
||||
@include vendor('transition', 'opacity 0.75s ease-in-out');
|
||||
@include vendor('transition-delay', '1.25s');
|
||||
}
|
||||
|
||||
@for $i from 1 through 6 {
|
||||
&.delay-#{$i} {
|
||||
img {
|
||||
@include vendor('transition-delay', '#{1.25 + ($i * 0.125)}s');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.is-preload & {
|
||||
opacity: 0;
|
||||
|
||||
.item {
|
||||
&.thumb {
|
||||
img {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=xlarge') {
|
||||
.item {
|
||||
$width: 17em;
|
||||
|
||||
height: 14em;
|
||||
|
||||
&.span-1 {
|
||||
width: $width;
|
||||
}
|
||||
|
||||
&.span-2 {
|
||||
width: $width * 1.5;
|
||||
}
|
||||
|
||||
&.span-3 {
|
||||
width: $width * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
$border: 5px;
|
||||
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: ($border * 0.5);
|
||||
|
||||
.items {
|
||||
@include vendor('flex-wrap', 'wrap');
|
||||
@include vendor('justify-content', 'center');
|
||||
|
||||
> :last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
@include vendor('flex-grow', '1');
|
||||
@include vendor('flex-shrink', '1');
|
||||
width: calc(50vw - #{$border * 3}) !important;
|
||||
margin: ($border * 0.5);
|
||||
box-shadow: 0 0 0 $border #ffffff;
|
||||
|
||||
&.intro {
|
||||
width: calc(100vw - #{$border * 4}) !important;
|
||||
text-align: center;
|
||||
height: auto;
|
||||
padding: 3em;
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.thumb {
|
||||
h2 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=xsmall') {
|
||||
.item {
|
||||
//width: calc(100vw - #{$border * 4}) !important;
|
||||
height: 10em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
#footer {
|
||||
width: 100%;
|
||||
padding: 1.5em;
|
||||
@include vendor('display', 'flex');
|
||||
@include vendor('justify-content', 'space-between');
|
||||
position: relative;
|
||||
z-index: _misc(z-index-base) + 2;
|
||||
|
||||
> section {
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
max-width: 36em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
max-width: 36em;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2.25em;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
padding-left: 1em;
|
||||
border-left: solid 1px rgba(255, 255, 255, 0.25);
|
||||
line-height: 1em;
|
||||
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=medium') {
|
||||
@include vendor('flex-direction', 'column');
|
||||
|
||||
> section {
|
||||
&:first-child {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: left;
|
||||
margin: 1.5em 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
text-align: center;
|
||||
padding: 3em;
|
||||
|
||||
> section {
|
||||
&:first-child {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.copyright {
|
||||
li {
|
||||
display: block;
|
||||
margin: 0.75em 0 0 0;
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
|
||||
.poptrox-popup {
|
||||
background: #1a1f2c;
|
||||
background: rgba(18, 21, 29, 0.9);
|
||||
box-shadow: 0px 0px 0px 10px #fff, 0px 10px 60px 10px rgba(8, 11, 19, 0.55);
|
||||
cursor: default;
|
||||
|
||||
.loader {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -24px 0 0 -24px;
|
||||
background: url('images/loader.gif');
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.caption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #1a1f2c;
|
||||
background: rgba(18, 21, 29, 0.85);
|
||||
display: block;
|
||||
width: 100%;
|
||||
line-height: 75px;
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-next, .nav-previous {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
@include vendor('transition', 'opacity 0.25s ease-in-out');
|
||||
opacity: 0.35;
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-next, .nav-previous {
|
||||
@include icon(false, solid);
|
||||
|
||||
text-transform: none !important;
|
||||
width: 150px;
|
||||
height: 75px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
font-size: 3em;
|
||||
line-height: 75px;
|
||||
}
|
||||
|
||||
.nav-next {
|
||||
right: 0;
|
||||
text-align: right;
|
||||
padding-right: 30px;
|
||||
&:before {
|
||||
content: '\f105';
|
||||
}
|
||||
}
|
||||
|
||||
.nav-previous {
|
||||
left: 0;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
&:before {
|
||||
content: '\f104';
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint('<=small') {
|
||||
background: #0a0f1c;
|
||||
box-shadow: 0px 0px 30px 10px rgba(8, 11, 19, 0.85);
|
||||
border: solid 2.5px #fff;
|
||||
@include vendor('box-sizing', 'content-box');
|
||||
|
||||
.caption {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nav-next {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nav-previous {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
@import 'libs/vars';
|
||||
@import 'libs/functions';
|
||||
@import 'libs/mixins';
|
||||
@import 'libs/vendor';
|
||||
@import 'libs/breakpoints';
|
||||
|
||||
/*
|
||||
Parallelism by HTML5 UP
|
||||
html5up.net | @ajlkn
|
||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||
*/
|
||||
|
||||
/* Main */
|
||||
|
||||
#main {
|
||||
opacity: 1 !important;
|
||||
overflow-x: auto !important;
|
||||
|
||||
.item {
|
||||
&.thumb {
|
||||
img {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user