updated store assets
@@ -1,5 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,78 +1,128 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# gradlew start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh gradlew
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -81,96 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
if $JAVACMD --add-opens java.base/java.lang=ALL-UNNAMED -version ; then
|
||||
DEFAULT_JVM_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED $DEFAULT_JVM_OPTS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@@ -1,84 +1,82 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem gradlew startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
@rem Execute gradlew
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
TEMPLATE="$ROOT_DIR/store-assets/google-ads/google-ads-creatives.html"
|
||||
OUT_DIR="$ROOT_DIR/store-assets/google-ads"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
creatives=(
|
||||
"pqs-square-01-scanner 1200 1200"
|
||||
"pqs-square-02-results 1200 1200"
|
||||
"pqs-square-03-risk 1200 1200"
|
||||
"pqs-square-04-image-scan 1200 1200"
|
||||
"pqs-square-05-contact 1200 1200"
|
||||
"pqs-square-06-history 1200 1200"
|
||||
"pqs-square-07-batch 1200 1200"
|
||||
"pqs-square-08-settings 1200 1200"
|
||||
"pqs-horizontal-01-scanner-results 1200 628"
|
||||
"pqs-horizontal-02-gallery 1200 628"
|
||||
"pqs-horizontal-03-risk 1200 628"
|
||||
"pqs-horizontal-04-history 1200 628"
|
||||
"pqs-horizontal-05-wifi 1200 628"
|
||||
"pqs-horizontal-06-event 1200 628"
|
||||
"pqs-horizontal-07-tablet 1200 628"
|
||||
"pqs-horizontal-08-settings 1200 628"
|
||||
"pqs-vertical-01-scanner 1200 1500"
|
||||
"pqs-vertical-02-gallery 1200 1500"
|
||||
"pqs-vertical-03-event 1200 1500"
|
||||
"pqs-vertical-04-privacy 1200 1500"
|
||||
)
|
||||
|
||||
for creative in "${creatives[@]}"; do
|
||||
read -r id width height <<< "$creative"
|
||||
google-chrome \
|
||||
--headless \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--hide-scrollbars \
|
||||
--allow-file-access-from-files \
|
||||
--window-size="${width},${height}" \
|
||||
--screenshot="$OUT_DIR/${id}.png" \
|
||||
"file://$TEMPLATE?creative=$id" >/dev/null
|
||||
printf '%s %sx%s\n' "$id" "$width" "$height"
|
||||
done
|
||||
@@ -0,0 +1,862 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Private QR Scanner Google Ads Creatives</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--deep: #07111f;
|
||||
--navy: #0b1220;
|
||||
--teal-900: #123b3f;
|
||||
--teal-800: #155e63;
|
||||
--teal-700: #0f766e;
|
||||
--teal-300: #2dd4bf;
|
||||
--mint: #dff7f2;
|
||||
--bg: #f6fbfa;
|
||||
--surface: #ffffff;
|
||||
--soft-blue: #f2f7ff;
|
||||
--text: #0b1220;
|
||||
--muted: #607080;
|
||||
--line: #dce8e6;
|
||||
--amber: #ffc857;
|
||||
--coral: #ff6b5f;
|
||||
--sky: #55b9ff;
|
||||
--violet: #8b7cf6;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
#stage {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: var(--w);
|
||||
height: var(--h);
|
||||
isolation: isolate;
|
||||
background: var(--base);
|
||||
}
|
||||
|
||||
.band,
|
||||
.panel,
|
||||
.rail,
|
||||
.tile-field,
|
||||
.scan-beam,
|
||||
.code-stack,
|
||||
.ticket-strip,
|
||||
.wifi-mark,
|
||||
.shield,
|
||||
.chips,
|
||||
.history-steps,
|
||||
.grid-plate,
|
||||
.device,
|
||||
.tablet {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.band {
|
||||
inset: auto -8% -18% -8%;
|
||||
height: 44%;
|
||||
background: var(--band);
|
||||
transform: skewY(-7deg);
|
||||
transform-origin: center;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.band.top {
|
||||
inset: -20% -8% auto -8%;
|
||||
height: 42%;
|
||||
transform: skewY(-6deg);
|
||||
}
|
||||
|
||||
.rail {
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0 12%, rgba(255,255,255,.22) 12% 13%, transparent 13% 28%, rgba(255,255,255,.2) 28% 29%, transparent 29%),
|
||||
linear-gradient(135deg, rgba(45,212,191,.2), rgba(85,185,255,.08));
|
||||
border: 1px solid rgba(255,255,255,.16);
|
||||
transform: rotate(var(--r));
|
||||
border-radius: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 22px;
|
||||
background: color-mix(in srgb, var(--panel-bg), transparent 0%);
|
||||
border: 1px solid var(--panel-border);
|
||||
box-shadow: 0 28px 70px rgba(7, 17, 31, .16);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tile-field {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 7px;
|
||||
opacity: .7;
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.tile-field span {
|
||||
border-radius: 2px;
|
||||
background: var(--tile);
|
||||
opacity: var(--o, .75);
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
height: 8px;
|
||||
background: linear-gradient(90deg, transparent, var(--teal-300), transparent);
|
||||
box-shadow: 0 0 34px rgba(45, 212, 191, .95);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.code-stack {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.code-card {
|
||||
height: 74px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, .88);
|
||||
border: 1px solid rgba(96, 112, 128, .14);
|
||||
box-shadow: 0 18px 44px rgba(7, 17, 31, .12);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code-card::before,
|
||||
.code-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
right: 18px;
|
||||
height: 10px;
|
||||
border-radius: 99px;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.code-card::before {
|
||||
top: 18px;
|
||||
width: 54%;
|
||||
}
|
||||
|
||||
.code-card::after {
|
||||
top: 40px;
|
||||
width: 78%;
|
||||
background: color-mix(in srgb, var(--teal-300), white 70%);
|
||||
}
|
||||
|
||||
.ticket-strip {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ticket {
|
||||
width: 272px;
|
||||
height: 96px;
|
||||
border-radius: 16px;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(7,17,31,.12) 0 2px, transparent 2px 16px),
|
||||
var(--surface);
|
||||
border: 1px solid rgba(96,112,128,.18);
|
||||
box-shadow: 0 20px 48px rgba(7,17,31,.16);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ticket::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 20px auto 20px 24px;
|
||||
width: 54px;
|
||||
background:
|
||||
linear-gradient(var(--text) 0 0) 0 0 / 16px 16px,
|
||||
linear-gradient(var(--text) 0 0) 26px 0 / 16px 16px,
|
||||
linear-gradient(var(--text) 0 0) 0 26px / 16px 16px,
|
||||
linear-gradient(var(--teal-300) 0 0) 26px 26px / 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ticket::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 96px;
|
||||
top: 24px;
|
||||
width: 132px;
|
||||
height: 42px;
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(var(--line), var(--line)) 0 0 / 100% 10px,
|
||||
linear-gradient(color-mix(in srgb, var(--teal-300), white 70%), color-mix(in srgb, var(--teal-300), white 70%)) 0 28px / 70% 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.wifi-mark {
|
||||
width: var(--s);
|
||||
height: var(--s);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.wifi-mark::before,
|
||||
.wifi-mark::after,
|
||||
.wifi-dot {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border: solid var(--wifi-color);
|
||||
border-width: 10px 10px 0 0;
|
||||
border-radius: 0 100% 0 0;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 100% 100%;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.wifi-mark::before {
|
||||
inset: 0 0 auto auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.wifi-mark::after {
|
||||
inset: 28% 28% auto auto;
|
||||
width: 44%;
|
||||
height: 44%;
|
||||
}
|
||||
|
||||
.wifi-dot {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 18%;
|
||||
height: 18%;
|
||||
background: var(--wifi-color);
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.shield {
|
||||
width: var(--s);
|
||||
height: calc(var(--s) * 1.25);
|
||||
background: color-mix(in srgb, var(--shield-fill), white 10%);
|
||||
clip-path: polygon(50% 0, 100% 18%, 100% 55%, 50% 100%, 0 55%, 0 18%);
|
||||
opacity: .9;
|
||||
box-shadow: 0 28px 60px rgba(7,17,31,.18);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.shield::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
top: 30%;
|
||||
width: 40%;
|
||||
height: 26%;
|
||||
border: solid var(--navy);
|
||||
border-width: 0 0 14px 14px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.chips {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.chip {
|
||||
width: var(--chip-w);
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
background: var(--chip-bg);
|
||||
box-shadow: 0 14px 34px rgba(7,17,31,.12);
|
||||
border: 1px solid rgba(255,255,255,.48);
|
||||
}
|
||||
|
||||
.history-steps {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step {
|
||||
width: var(--step-w);
|
||||
height: 64px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255,255,255,.9);
|
||||
border: 1px solid rgba(96,112,128,.16);
|
||||
box-shadow: 0 18px 44px rgba(7,17,31,.12);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
top: 18px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(var(--navy) 0 0) 6px 6px / 8px 8px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 6px / 8px 8px,
|
||||
linear-gradient(var(--teal-300) 0 0) 6px 18px / 8px 8px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 18px / 8px 8px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.step::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 62px;
|
||||
top: 21px;
|
||||
width: 52%;
|
||||
height: 22px;
|
||||
border-radius: 99px;
|
||||
background: var(--line);
|
||||
box-shadow: 0 28px 0 -6px color-mix(in srgb, var(--teal-300), white 72%);
|
||||
}
|
||||
|
||||
.grid-plate {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 8px;
|
||||
padding: 18px;
|
||||
border-radius: 22px;
|
||||
background: rgba(255,255,255,.82);
|
||||
border: 1px solid rgba(96,112,128,.12);
|
||||
box-shadow: 0 26px 64px rgba(7,17,31,.14);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.grid-plate span {
|
||||
border-radius: 4px;
|
||||
background: var(--text);
|
||||
}
|
||||
|
||||
.grid-plate span:nth-child(5n),
|
||||
.grid-plate span:nth-child(8n) {
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.device {
|
||||
width: var(--dw);
|
||||
aspect-ratio: 1080 / 1920;
|
||||
padding: var(--bezel, 15px);
|
||||
border-radius: var(--radius, 46px);
|
||||
background:
|
||||
linear-gradient(150deg, rgba(255,255,255,.28), transparent 18%),
|
||||
linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 42px 90px rgba(7, 17, 31, .34),
|
||||
inset 0 0 0 1px rgba(255, 255, 255, .18);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.device::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 50%;
|
||||
width: 23%;
|
||||
height: 12px;
|
||||
border-radius: 99px;
|
||||
background: #020617;
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.device img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: calc(var(--radius, 46px) - 12px);
|
||||
background: #07111f;
|
||||
}
|
||||
|
||||
.tablet {
|
||||
width: var(--tw);
|
||||
aspect-ratio: 1440 / 2560;
|
||||
padding: 18px;
|
||||
border-radius: 34px;
|
||||
background: linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 42px 90px rgba(7, 17, 31, .3),
|
||||
inset 0 0 0 1px rgba(255,255,255,.16);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tablet img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 20px;
|
||||
background: #07111f;
|
||||
}
|
||||
|
||||
.soft-light {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(255,255,255,.32), transparent 26%),
|
||||
linear-gradient(0deg, rgba(255,255,255,.12), transparent 40%);
|
||||
mix-blend-mode: soft-light;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fallback-list {
|
||||
padding: 28px;
|
||||
font: 20px/1.4 system-ui, sans-serif;
|
||||
color: #111827;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main id="stage"></main>
|
||||
<script>
|
||||
const screenshot = (kind, n) => `../private-qr-scanner-${kind}-screenshot-${n}.png`;
|
||||
|
||||
const creatives = [
|
||||
{
|
||||
id: "pqs-square-01-scanner",
|
||||
size: [1200, 1200],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.12)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "tileField", x: 64, y: 700, w: 300, h: 300, r: -8, tile: "rgba(223,247,242,.72)" },
|
||||
{ type: "gridPlate", x: 756, y: 128, w: 264, h: 264 },
|
||||
{ type: "beam", x: 188, y: 548, w: 824, r: -2 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 392, y: 94, w: 410, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-02-results",
|
||||
size: [1200, 1200],
|
||||
theme: ["#f6fbfa", "#dff7f2", "#ffffff", "rgba(21,94,99,.16)", "#0f766e"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "codeStack", x: 104, y: 184, w: 374, count: 4 },
|
||||
{ type: "chips", x: 770, y: 210, chipW: 250, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 2), x: 392, y: 86, w: 414, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-03-risk",
|
||||
size: [1200, 1200],
|
||||
theme: ["#101722", "#2b2f42", "#ffffff", "rgba(255,200,87,.24)", "#ffc857"],
|
||||
elements: [
|
||||
{ type: "rail", x: -110, y: 210, w: 1440, h: 180, r: -18 },
|
||||
{ type: "shield", x: 764, y: 654, s: 220, fill: "#ffc857" },
|
||||
{ type: "tileField", x: 770, y: 112, w: 260, h: 260, r: 7, tile: "rgba(255,200,87,.78)" },
|
||||
{ type: "phone", img: screenshot("phone", 3), x: 384, y: 84, w: 430, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-04-image-scan",
|
||||
size: [1200, 1200],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "panel", x: 118, y: 712, w: 452, h: 264 },
|
||||
{ type: "gridPlate", x: 748, y: 178, w: 260, h: 260 },
|
||||
{ type: "beam", x: 184, y: 602, w: 820, r: 4 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 390, y: 88, w: 420, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-05-contact",
|
||||
size: [1200, 1200],
|
||||
theme: ["#fff7f3", "#ffe3dc", "#ffffff", "rgba(255,107,95,.18)", "#ff6b5f"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "chips", x: 136, y: 192, chipW: 288, colors: ["#ffffff", "#ffe3dc", "#ffffff", "#dff7f2"] },
|
||||
{ type: "codeStack", x: 744, y: 698, w: 330, count: 3 },
|
||||
{ type: "phone", img: screenshot("phone", 5), x: 390, y: 84, w: 418, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-06-history",
|
||||
size: [1200, 1200],
|
||||
theme: ["#f7f5ff", "#e9e4ff", "#ffffff", "rgba(139,124,246,.2)", "#8b7cf6"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "history", x: 104, y: 228, w: 330, count: 4 },
|
||||
{ type: "tileField", x: 790, y: 690, w: 250, h: 250, r: -4, tile: "rgba(139,124,246,.55)" },
|
||||
{ type: "phone", img: screenshot("phone", 6), x: 392, y: 82, w: 416, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-07-batch",
|
||||
size: [1200, 1200],
|
||||
theme: ["#07111f", "#0f766e", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "rail", x: -120, y: 802, w: 1450, h: 186, r: 16 },
|
||||
{ type: "ticketStrip", x: 104, y: 182, count: 3, r: -4 },
|
||||
{ type: "beam", x: 220, y: 576, w: 760, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 392, y: 86, w: 416, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-square-08-settings",
|
||||
size: [1200, 1200],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 142, y: 198, s: 210, fill: "#dff7f2" },
|
||||
{ type: "chips", x: 778, y: 692, chipW: 276, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 394, y: 86, w: 412, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-01-scanner-results",
|
||||
size: [1200, 628],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.13)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "tileField", x: 88, y: 104, w: 248, h: 248, r: -5, tile: "rgba(223,247,242,.68)" },
|
||||
{ type: "codeStack", x: 790, y: 124, w: 318, count: 3 },
|
||||
{ type: "beam", x: 108, y: 310, w: 984, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 382, y: 32, w: 256, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 2), x: 568, y: 74, w: 216, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-02-gallery",
|
||||
size: [1200, 628],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "rail", x: -120, y: 86, w: 1440, h: 154, r: -10 },
|
||||
{ type: "panel", x: 742, y: 120, w: 326, h: 286 },
|
||||
{ type: "gridPlate", x: 128, y: 158, w: 214, h: 214 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 446, y: 34, w: 254, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-03-risk",
|
||||
size: [1200, 628],
|
||||
theme: ["#101722", "#2b2f42", "#ffffff", "rgba(255,200,87,.22)", "#ffc857"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "shield", x: 824, y: 216, s: 176, fill: "#ffc857" },
|
||||
{ type: "tileField", x: 118, y: 148, w: 214, h: 214, r: 8, tile: "rgba(255,200,87,.7)" },
|
||||
{ type: "phone", img: screenshot("phone", 3), x: 466, y: 34, w: 248, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-04-history",
|
||||
size: [1200, 628],
|
||||
theme: ["#f7f5ff", "#e9e4ff", "#ffffff", "rgba(139,124,246,.2)", "#8b7cf6"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "history", x: 112, y: 140, w: 306, count: 3 },
|
||||
{ type: "chips", x: 788, y: 158, chipW: 300, colors: ["#ffffff", "#e9e4ff", "#dff7f2"] },
|
||||
{ type: "phone", img: screenshot("phone", 6), x: 474, y: 34, w: 246, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-05-wifi",
|
||||
size: [1200, 628],
|
||||
theme: ["#ffffff", "#dff7f2", "#ffffff", "rgba(15,118,110,.18)", "#0f766e"],
|
||||
elements: [
|
||||
{ type: "rail", x: -116, y: 390, w: 1432, h: 154, r: 9 },
|
||||
{ type: "wifi", x: 150, y: 164, s: 198, color: "#0f766e" },
|
||||
{ type: "gridPlate", x: 828, y: 172, w: 206, h: 206 },
|
||||
{ type: "phone", img: screenshot("phone", 5), x: 466, y: 34, w: 250, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-06-event",
|
||||
size: [1200, 628],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "ticketStrip", x: 102, y: 118, count: 3, r: -3 },
|
||||
{ type: "beam", x: 196, y: 306, w: 806, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 474, y: 34, w: 250, r: 0 },
|
||||
{ type: "ticketStrip", x: 842, y: 152, count: 2, r: 5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-07-tablet",
|
||||
size: [1200, 628],
|
||||
theme: ["#f6fbfa", "#dff7f2", "#ffffff", "rgba(21,94,99,.16)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "codeStack", x: 108, y: 144, w: 330, count: 3 },
|
||||
{ type: "tablet", img: screenshot("7in-tablet", 2), x: 492, y: 18, w: 258, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 712, y: 62, w: 212, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-horizontal-08-settings",
|
||||
size: [1200, 628],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#607080"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 154, y: 168, s: 176, fill: "#dff7f2" },
|
||||
{ type: "chips", x: 796, y: 156, chipW: 290, colors: ["#ffffff", "#dff7f2", "#f2f7ff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 474, y: 34, w: 246, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-01-scanner",
|
||||
size: [1200, 1500],
|
||||
theme: ["#07111f", "#123b3f", "#dff7f2", "rgba(255,255,255,.12)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "band", cls: "top" },
|
||||
{ type: "tileField", x: 126, y: 900, w: 280, h: 280, r: -6, tile: "rgba(223,247,242,.68)" },
|
||||
{ type: "gridPlate", x: 760, y: 180, w: 256, h: 256 },
|
||||
{ type: "beam", x: 160, y: 700, w: 880, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 1), x: 366, y: 152, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-02-gallery",
|
||||
size: [1200, 1500],
|
||||
theme: ["#eef7ff", "#d7efff", "#ffffff", "rgba(85,185,255,.2)", "#55b9ff"],
|
||||
elements: [
|
||||
{ type: "rail", x: -146, y: 248, w: 1510, h: 188, r: -14 },
|
||||
{ type: "panel", x: 128, y: 1004, w: 380, h: 248 },
|
||||
{ type: "gridPlate", x: 782, y: 240, w: 230, h: 230 },
|
||||
{ type: "phone", img: screenshot("phone", 4), x: 366, y: 154, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-03-event",
|
||||
size: [1200, 1500],
|
||||
theme: ["#07111f", "#0f766e", "#dff7f2", "rgba(45,212,191,.16)", "#2dd4bf"],
|
||||
elements: [
|
||||
{ type: "ticketStrip", x: 122, y: 178, count: 3, r: -4 },
|
||||
{ type: "ticketStrip", x: 800, y: 902, count: 3, r: 4 },
|
||||
{ type: "beam", x: 186, y: 722, w: 828, r: 0 },
|
||||
{ type: "phone", img: screenshot("phone", 7), x: 366, y: 152, w: 470, r: 0 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "pqs-vertical-04-privacy",
|
||||
size: [1200, 1500],
|
||||
theme: ["#ffffff", "#f2f7ff", "#ffffff", "rgba(96,112,128,.14)", "#155e63"],
|
||||
elements: [
|
||||
{ type: "band" },
|
||||
{ type: "shield", x: 122, y: 214, s: 230, fill: "#dff7f2" },
|
||||
{ type: "history", x: 782, y: 866, w: 292, count: 3 },
|
||||
{ type: "chips", x: 136, y: 1020, chipW: 300, colors: ["#dff7f2", "#f2f7ff", "#ffffff"] },
|
||||
{ type: "phone", img: screenshot("phone", 8), x: 366, y: 154, w: 470, r: 0 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function px(v) {
|
||||
return `${v}px`;
|
||||
}
|
||||
|
||||
function setBox(el, item) {
|
||||
if (item.x !== undefined) el.style.left = px(item.x);
|
||||
if (item.y !== undefined) el.style.top = px(item.y);
|
||||
if (item.w !== undefined) el.style.width = px(item.w);
|
||||
if (item.h !== undefined) el.style.height = px(item.h);
|
||||
if (item.r !== undefined) el.style.setProperty("--r", `${item.r}deg`);
|
||||
}
|
||||
|
||||
function tilePattern(count) {
|
||||
return Array.from({ length: count }, (_, index) => {
|
||||
const span = document.createElement("span");
|
||||
if ([1, 4, 7, 10, 13, 16, 20, 24, 31, 36, 38, 43, 48, 51, 57, 64, 67, 71, 78].includes(index)) {
|
||||
span.style.visibility = "hidden";
|
||||
}
|
||||
if (index % 11 === 0) span.style.setProperty("--o", ".38");
|
||||
return span;
|
||||
});
|
||||
}
|
||||
|
||||
function renderElement(item) {
|
||||
let el;
|
||||
|
||||
if (item.type === "band") {
|
||||
el = document.createElement("div");
|
||||
el.className = `band ${item.cls || ""}`;
|
||||
}
|
||||
|
||||
if (item.type === "rail") {
|
||||
el = document.createElement("div");
|
||||
el.className = "rail";
|
||||
setBox(el, item);
|
||||
}
|
||||
|
||||
if (item.type === "panel") {
|
||||
el = document.createElement("div");
|
||||
el.className = "panel";
|
||||
setBox(el, item);
|
||||
}
|
||||
|
||||
if (item.type === "tileField") {
|
||||
el = document.createElement("div");
|
||||
el.className = "tile-field";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--tile", item.tile);
|
||||
tilePattern(81).forEach((span) => el.appendChild(span));
|
||||
}
|
||||
|
||||
if (item.type === "gridPlate") {
|
||||
el = document.createElement("div");
|
||||
el.className = "grid-plate";
|
||||
setBox(el, item);
|
||||
tilePattern(36).forEach((span) => el.appendChild(span));
|
||||
}
|
||||
|
||||
if (item.type === "beam") {
|
||||
el = document.createElement("div");
|
||||
el.className = "scan-beam";
|
||||
setBox(el, { ...item, h: 8 });
|
||||
}
|
||||
|
||||
if (item.type === "codeStack") {
|
||||
el = document.createElement("div");
|
||||
el.className = "code-stack";
|
||||
setBox(el, item);
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "code-card";
|
||||
card.style.width = px(item.w - i * 18);
|
||||
card.style.marginLeft = px(i % 2 ? 22 : 0);
|
||||
el.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "ticketStrip") {
|
||||
el = document.createElement("div");
|
||||
el.className = "ticket-strip";
|
||||
setBox(el, item);
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const ticket = document.createElement("div");
|
||||
ticket.className = "ticket";
|
||||
ticket.style.marginLeft = px(i % 2 ? 28 : 0);
|
||||
el.appendChild(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "wifi") {
|
||||
el = document.createElement("div");
|
||||
el.className = "wifi-mark";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--s", px(item.s));
|
||||
el.style.setProperty("--wifi-color", item.color);
|
||||
const dot = document.createElement("div");
|
||||
dot.className = "wifi-dot";
|
||||
el.appendChild(dot);
|
||||
}
|
||||
|
||||
if (item.type === "shield") {
|
||||
el = document.createElement("div");
|
||||
el.className = "shield";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--s", px(item.s));
|
||||
el.style.setProperty("--shield-fill", item.fill);
|
||||
}
|
||||
|
||||
if (item.type === "chips") {
|
||||
el = document.createElement("div");
|
||||
el.className = "chips";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--chip-w", px(item.chipW));
|
||||
item.colors.forEach((color, index) => {
|
||||
const chip = document.createElement("div");
|
||||
chip.className = "chip";
|
||||
chip.style.setProperty("--chip-bg", color);
|
||||
chip.style.marginLeft = px(index % 2 ? 24 : 0);
|
||||
el.appendChild(chip);
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === "history") {
|
||||
el = document.createElement("div");
|
||||
el.className = "history-steps";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--step-w", px(item.w));
|
||||
for (let i = 0; i < item.count; i += 1) {
|
||||
const step = document.createElement("div");
|
||||
step.className = "step";
|
||||
step.style.marginLeft = px(i % 2 ? 26 : 0);
|
||||
el.appendChild(step);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "phone") {
|
||||
el = document.createElement("div");
|
||||
el.className = "device";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--dw", px(item.w));
|
||||
const img = document.createElement("img");
|
||||
img.src = item.img;
|
||||
img.alt = "";
|
||||
el.appendChild(img);
|
||||
}
|
||||
|
||||
if (item.type === "tablet") {
|
||||
el = document.createElement("div");
|
||||
el.className = "tablet";
|
||||
setBox(el, item);
|
||||
el.style.setProperty("--tw", px(item.w));
|
||||
const img = document.createElement("img");
|
||||
img.src = item.img;
|
||||
img.alt = "";
|
||||
el.appendChild(img);
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function renderCreative(creative) {
|
||||
const stage = document.getElementById("stage");
|
||||
const [w, h] = creative.size;
|
||||
document.documentElement.style.setProperty("--w", px(w));
|
||||
document.documentElement.style.setProperty("--h", px(h));
|
||||
stage.style.setProperty("--base", creative.theme[0]);
|
||||
stage.style.setProperty("--band", creative.theme[1]);
|
||||
stage.style.setProperty("--panel-bg", creative.theme[2]);
|
||||
stage.style.setProperty("--panel-border", creative.theme[3]);
|
||||
stage.style.setProperty("--tile", creative.theme[4]);
|
||||
document.title = creative.id;
|
||||
|
||||
creative.elements.forEach((item) => stage.appendChild(renderElement(item)));
|
||||
|
||||
const light = document.createElement("div");
|
||||
light.className = "soft-light";
|
||||
stage.appendChild(light);
|
||||
}
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
const requested = params.get("creative");
|
||||
const creative = creatives.find((item) => item.id === requested);
|
||||
|
||||
if (creative) {
|
||||
renderCreative(creative);
|
||||
window.__creativeReady = true;
|
||||
} else {
|
||||
const list = document.createElement("div");
|
||||
list.className = "fallback-list";
|
||||
list.innerHTML = `<strong>Available creatives</strong><br>${creatives.map((item) => item.id).join("<br>")}`;
|
||||
document.body.appendChild(list);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,945 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="ad.orientation" content="portrait,landscape">
|
||||
<title>Private QR Scanner</title>
|
||||
<link rel="preload" as="image" href="assets/app-icon.png">
|
||||
<link rel="preload" as="image" href="assets/phone-scanner.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-result.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-warning.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-image.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-history.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-batch.jpg">
|
||||
<link rel="preload" as="image" href="assets/phone-settings.jpg">
|
||||
<script type="text/javascript" src="https://tpc.googlesyndication.com/pagead/gadgets/html5/api/exitapi.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--deep: #07111f;
|
||||
--navy: #0b1220;
|
||||
--teal-900: #123b3f;
|
||||
--teal-800: #155e63;
|
||||
--teal-700: #0f766e;
|
||||
--teal-300: #2dd4bf;
|
||||
--mint: #dff7f2;
|
||||
--paper: #f6fbfa;
|
||||
--surface: #ffffff;
|
||||
--soft-blue: #f2f7ff;
|
||||
--line: #dce8e6;
|
||||
--muted: #607080;
|
||||
--amber: #ffc857;
|
||||
--coral: #ff6b5f;
|
||||
--sky: #55b9ff;
|
||||
--violet: #8b7cf6;
|
||||
--shadow: rgba(7, 17, 31, 0.28);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
color: var(--navy);
|
||||
background: var(--paper);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
letter-spacing: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
user-select: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
letter-spacing: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.ad {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(255,255,255,0.96), rgba(242,247,255,0.96) 40%, rgba(223,247,242,0.92)),
|
||||
var(--paper);
|
||||
}
|
||||
|
||||
.band {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
left: -16vw;
|
||||
right: -16vw;
|
||||
bottom: -13vh;
|
||||
height: 40vh;
|
||||
background: linear-gradient(135deg, var(--teal-800), var(--teal-900));
|
||||
transform: skewY(-8deg);
|
||||
}
|
||||
|
||||
.band-top {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
left: -18vw;
|
||||
right: -18vw;
|
||||
top: -16vh;
|
||||
height: 34vh;
|
||||
background: linear-gradient(135deg, rgba(85,185,255,0.25), rgba(45,212,191,0.26));
|
||||
transform: skewY(-7deg);
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.ticket-stack,
|
||||
.scan-beam,
|
||||
.shield,
|
||||
.wifi,
|
||||
.mini-list {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 5px;
|
||||
width: 132px;
|
||||
height: 132px;
|
||||
padding: 9px;
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,0.72);
|
||||
box-shadow: 0 18px 46px rgba(7,17,31,0.12);
|
||||
transform: rotate(-8deg);
|
||||
}
|
||||
|
||||
.qr-field span {
|
||||
border-radius: 2px;
|
||||
background: var(--navy);
|
||||
}
|
||||
|
||||
.qr-field span:nth-child(3n),
|
||||
.qr-field span:nth-child(11n) {
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.qr-field span:nth-child(5n),
|
||||
.qr-field span:nth-child(8n),
|
||||
.qr-field span:nth-child(13n) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
width: 168px;
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
|
||||
.ticket {
|
||||
height: 54px;
|
||||
border-radius: 12px;
|
||||
background: var(--surface);
|
||||
border: 1px solid rgba(96,112,128,0.16);
|
||||
box-shadow: 0 14px 34px rgba(7,17,31,0.12);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ticket:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 13px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background:
|
||||
linear-gradient(var(--navy) 0 0) 0 0 / 10px 10px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 0 / 10px 10px,
|
||||
linear-gradient(var(--teal-300) 0 0) 0 18px / 10px 10px,
|
||||
linear-gradient(var(--navy) 0 0) 18px 18px / 10px 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ticket:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 56px;
|
||||
top: 16px;
|
||||
width: 84px;
|
||||
height: 20px;
|
||||
border-radius: 999px;
|
||||
background: var(--line);
|
||||
box-shadow: 0 18px 0 -5px rgba(45,212,191,0.38);
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
width: 52vw;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, transparent, var(--teal-300), transparent);
|
||||
box-shadow: 0 0 30px rgba(45,212,191,0.9);
|
||||
opacity: 0.92;
|
||||
animation: beamPulse 1.7s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes beamPulse {
|
||||
0% { transform: translateY(-10px); opacity: 0.45; }
|
||||
50% { transform: translateY(10px); opacity: 1; }
|
||||
100% { transform: translateY(-10px); opacity: 0.45; }
|
||||
}
|
||||
|
||||
.shield {
|
||||
width: 80px;
|
||||
height: 100px;
|
||||
background: var(--amber);
|
||||
clip-path: polygon(50% 0, 100% 18%, 100% 55%, 50% 100%, 0 55%, 0 18%);
|
||||
box-shadow: 0 16px 34px rgba(7,17,31,0.16);
|
||||
}
|
||||
|
||||
.shield:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
top: 30%;
|
||||
width: 40%;
|
||||
height: 24%;
|
||||
border: solid var(--navy);
|
||||
border-width: 0 0 8px 8px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.wifi {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.wifi:before,
|
||||
.wifi:after,
|
||||
.wifi-dot {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: solid var(--teal-700);
|
||||
border-width: 8px 8px 0 0;
|
||||
border-radius: 0 100% 0 0;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
.wifi:before {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
}
|
||||
|
||||
.wifi:after {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.wifi-dot {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--teal-700);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
width: 166px;
|
||||
}
|
||||
|
||||
.mini-row {
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.84);
|
||||
box-shadow: 0 12px 28px rgba(7,17,31,0.1);
|
||||
border: 1px solid rgba(96,112,128,0.12);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mini-row:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 11px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 5px;
|
||||
background: var(--teal-300);
|
||||
}
|
||||
|
||||
.mini-row:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 42px;
|
||||
top: 13px;
|
||||
width: 92px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.brand,
|
||||
.copy,
|
||||
.phone-wrap,
|
||||
.panel {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 22px rgba(7,17,31,0.15);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.brand strong {
|
||||
display: block;
|
||||
color: var(--navy);
|
||||
font-size: 16px;
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
margin: 0;
|
||||
color: var(--navy);
|
||||
font-size: 32px;
|
||||
line-height: 1.02;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.copy p {
|
||||
margin: 12px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.34;
|
||||
max-width: 330px;
|
||||
}
|
||||
|
||||
.pills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
max-width: 330px;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255,255,255,0.74);
|
||||
color: var(--teal-900);
|
||||
border: 1px solid rgba(96,112,128,0.14);
|
||||
font-size: 12px;
|
||||
line-height: 1.15;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 10px 24px rgba(7,17,31,0.07);
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
border-radius: 32px;
|
||||
padding: 11px;
|
||||
background: linear-gradient(180deg, #111827, #030712);
|
||||
box-shadow:
|
||||
0 34px 72px rgba(7,17,31,0.34),
|
||||
inset 0 0 0 1px rgba(255,255,255,0.16);
|
||||
}
|
||||
|
||||
.phone-wrap:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 50%;
|
||||
width: 58px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: #020617;
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.phone-screen {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 23px;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 20px;
|
||||
padding: 16px;
|
||||
background: rgba(255,255,255,0.88);
|
||||
border: 1px solid rgba(96,112,128,0.14);
|
||||
box-shadow: 0 24px 54px rgba(7,17,31,0.16);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.panel-kicker {
|
||||
color: var(--teal-700);
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 8px 0 0;
|
||||
color: var(--navy);
|
||||
font-size: 20px;
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.panel p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.32;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
background: var(--soft-blue);
|
||||
color: var(--navy);
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
box-shadow: inset 0 0 0 1px rgba(96,112,128,0.16);
|
||||
}
|
||||
|
||||
.dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: #b7c7d8;
|
||||
}
|
||||
|
||||
.dot.active {
|
||||
width: 20px;
|
||||
background: var(--teal-700);
|
||||
}
|
||||
|
||||
.cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 46px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 18px;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, var(--teal-700), var(--teal-800));
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 18px 34px rgba(15,118,110,0.28);
|
||||
}
|
||||
|
||||
.glint {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(255,255,255,0.34), transparent 24%),
|
||||
linear-gradient(0deg, rgba(255,255,255,0.14), transparent 42%);
|
||||
mix-blend-mode: soft-light;
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
.brand {
|
||||
left: 26px;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 28px;
|
||||
right: 28px;
|
||||
top: 92px;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
max-width: 300px;
|
||||
font-size: 31px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: 50%;
|
||||
top: 276px;
|
||||
width: 226px;
|
||||
height: 402px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
left: 22px;
|
||||
right: 22px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
right: 16px;
|
||||
top: 188px;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
left: 20px;
|
||||
top: 386px;
|
||||
}
|
||||
|
||||
.shield {
|
||||
right: 44px;
|
||||
bottom: 214px;
|
||||
}
|
||||
|
||||
.wifi {
|
||||
left: 32px;
|
||||
bottom: 184px;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
right: 22px;
|
||||
top: 508px;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
left: 24vw;
|
||||
top: 496px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) {
|
||||
.brand {
|
||||
left: 34px;
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 36px;
|
||||
top: 100px;
|
||||
width: 32vw;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
max-width: 330px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 178px;
|
||||
height: 316px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
right: 26px;
|
||||
top: 54px;
|
||||
width: 30vw;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.qr-field {
|
||||
left: 38vw;
|
||||
top: 34px;
|
||||
width: 116px;
|
||||
height: 116px;
|
||||
}
|
||||
|
||||
.ticket-stack {
|
||||
right: 32px;
|
||||
bottom: 22px;
|
||||
}
|
||||
|
||||
.shield {
|
||||
right: 22vw;
|
||||
bottom: 40px;
|
||||
}
|
||||
|
||||
.wifi {
|
||||
left: 28vw;
|
||||
bottom: 24px;
|
||||
}
|
||||
|
||||
.mini-list {
|
||||
right: 25vw;
|
||||
top: 24px;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
left: 28vw;
|
||||
top: 50%;
|
||||
width: 45vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 340px) and (orientation: portrait) {
|
||||
.brand {
|
||||
top: 16px;
|
||||
left: 18px;
|
||||
}
|
||||
|
||||
.brand div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 20px;
|
||||
right: 158px;
|
||||
top: 74px;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.copy p,
|
||||
.pills {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
left: calc(100% - 86px);
|
||||
top: 96px;
|
||||
width: 132px;
|
||||
height: 235px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
bottom: 12px;
|
||||
padding: 13px;
|
||||
}
|
||||
|
||||
.panel p,
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
min-height: 40px;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.mini-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scan-beam {
|
||||
top: 232px;
|
||||
width: 58vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 360px) and (orientation: landscape) {
|
||||
.brand {
|
||||
left: 22px;
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
.brand div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brand img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.brand span,
|
||||
.pills,
|
||||
.copy p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.copy {
|
||||
left: 22px;
|
||||
top: 74px;
|
||||
width: 34vw;
|
||||
}
|
||||
|
||||
.copy h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.phone-wrap {
|
||||
width: 140px;
|
||||
height: 249px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
right: 18px;
|
||||
top: 18px;
|
||||
width: 30vw;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.panel p,
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.panel p {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
min-height: 40px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.qr-field,
|
||||
.ticket-stack,
|
||||
.mini-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ad" id="ad">
|
||||
<div class="band-top"></div>
|
||||
<div class="band"></div>
|
||||
|
||||
<div class="qr-field" aria-hidden="true">
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
<span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
||||
</div>
|
||||
|
||||
<div class="ticket-stack" aria-hidden="true">
|
||||
<div class="ticket"></div>
|
||||
<div class="ticket"></div>
|
||||
<div class="ticket"></div>
|
||||
</div>
|
||||
|
||||
<div class="mini-list" aria-hidden="true">
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-row"></div>
|
||||
<div class="mini-row"></div>
|
||||
</div>
|
||||
|
||||
<div class="shield" aria-hidden="true"></div>
|
||||
<div class="wifi" aria-hidden="true"><div class="wifi-dot"></div></div>
|
||||
<div class="scan-beam" aria-hidden="true"></div>
|
||||
|
||||
<div class="brand">
|
||||
<img src="assets/app-icon.png" alt="">
|
||||
<div>
|
||||
<strong>Private QR Scanner</strong>
|
||||
<span>No ads. No tracking.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="copy" aria-label="Private QR Scanner campaign">
|
||||
<h1>Scan QR codes privately</h1>
|
||||
<p>Read codes on device, inspect links, and keep optional history local.</p>
|
||||
<div class="pills">
|
||||
<span class="pill">QR and barcodes</span>
|
||||
<span class="pill">Image scan</span>
|
||||
<span class="pill">Local warnings</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="phone-wrap" aria-hidden="true">
|
||||
<img class="phone-screen" id="phoneScreen" src="assets/phone-scanner.jpg" alt="">
|
||||
</div>
|
||||
|
||||
<section class="panel" aria-label="Feature preview">
|
||||
<div class="panel-kicker" id="stepNumber">Step 1 of 7</div>
|
||||
<h2 id="stepTitle">Live camera scanning</h2>
|
||||
<p id="stepCopy">Aim at a QR code or barcode and review clear results before taking action.</p>
|
||||
<div class="controls">
|
||||
<button class="control-btn" id="prevButton" type="button" aria-label="Previous feature">‹</button>
|
||||
<div class="dots" id="dots" aria-hidden="true"></div>
|
||||
<button class="control-btn" id="nextButton" type="button" aria-label="Next feature">›</button>
|
||||
</div>
|
||||
<button class="cta" id="cta" type="button">Try the app</button>
|
||||
</section>
|
||||
|
||||
<div class="glint"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var slides = [
|
||||
{
|
||||
image: "assets/phone-scanner.jpg",
|
||||
title: "Live camera scanning",
|
||||
copy: "Aim at a QR code or barcode and review clear results before taking action."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-result.jpg",
|
||||
title: "Useful result actions",
|
||||
copy: "Copy, share, open links, add contacts, or use the action that matches the scan."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-warning.jpg",
|
||||
title: "Local link warnings",
|
||||
copy: "Pause before opening unusual links with on-device URL risk checks."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-image.jpg",
|
||||
title: "Scan from an image",
|
||||
copy: "Choose a saved picture and detect QR codes or barcodes inside it."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-history.jpg",
|
||||
title: "Optional local history",
|
||||
copy: "Keep scan records only when you want them, stored locally on your device."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-batch.jpg",
|
||||
title: "Batch scanning",
|
||||
copy: "Capture multiple codes in one flow for events, tickets, or fast workflows."
|
||||
},
|
||||
{
|
||||
image: "assets/phone-settings.jpg",
|
||||
title: "Simple controls",
|
||||
copy: "Choose history, warnings, feedback, and app behavior in one clear settings view."
|
||||
}
|
||||
];
|
||||
|
||||
var index = 0;
|
||||
var phoneScreen = document.getElementById("phoneScreen");
|
||||
var stepNumber = document.getElementById("stepNumber");
|
||||
var stepTitle = document.getElementById("stepTitle");
|
||||
var stepCopy = document.getElementById("stepCopy");
|
||||
var dots = document.getElementById("dots");
|
||||
var prevButton = document.getElementById("prevButton");
|
||||
var nextButton = document.getElementById("nextButton");
|
||||
var cta = document.getElementById("cta");
|
||||
|
||||
function makeDots() {
|
||||
var fragment = document.createDocumentFragment();
|
||||
var i;
|
||||
for (i = 0; i < slides.length; i += 1) {
|
||||
var dot = document.createElement("span");
|
||||
dot.className = "dot";
|
||||
fragment.appendChild(dot);
|
||||
}
|
||||
dots.appendChild(fragment);
|
||||
}
|
||||
|
||||
function renderSlide() {
|
||||
var slide = slides[index];
|
||||
var dotNodes = dots.getElementsByClassName("dot");
|
||||
var i;
|
||||
phoneScreen.src = slide.image;
|
||||
stepNumber.innerHTML = "Step " + (index + 1) + " of " + slides.length;
|
||||
stepTitle.innerHTML = slide.title;
|
||||
stepCopy.innerHTML = slide.copy;
|
||||
for (i = 0; i < dotNodes.length; i += 1) {
|
||||
dotNodes[i].className = i === index ? "dot active" : "dot";
|
||||
}
|
||||
}
|
||||
|
||||
function move(delta) {
|
||||
index = (index + delta + slides.length) % slides.length;
|
||||
renderSlide();
|
||||
}
|
||||
|
||||
function exitAd() {
|
||||
if (window.ExitApi && typeof window.ExitApi.exit === "function") {
|
||||
window.ExitApi.exit();
|
||||
}
|
||||
}
|
||||
|
||||
makeDots();
|
||||
renderSlide();
|
||||
prevButton.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
move(-1);
|
||||
});
|
||||
nextButton.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
move(1);
|
||||
});
|
||||
cta.addEventListener("click", function(event) {
|
||||
event.stopPropagation();
|
||||
exitAd();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 170 KiB |