Browse Our Most Popular Greeting Cards"; $schemaorgdescription = $metadescription; $nav = ""; $folder = ""; $doc = ""; $ogtitle = ""; $ogimage = ""; $ogdescription = ""; $h2 = ""; $displaySocialOnTop = false; $zazzle_url = ""; $prev_info = ""; $next_info = ""; $featured = false; $facebookUrl = ""; $pinterestUrl = ""; $isMe = false; $isSearch = false; $searchDisplay = ""; $searchTotal = 0; $searchAction = "/"; $searchClearUrl = "/"; // Suffix such as " in Birthday Cards" for the results heading. Phase 2 sets it // from the category in the URL; it is echoed through htmlspecialchars below. $searchScopeLabel = ""; $searchPlaceholder = "Search all cards & posters"; $searchHint = "Search every card and poster by title, category or description."; // Product pricing, shown on a listing's own product page. Greeting cards have // a regular price of $4.99 and are 20% off, which rounds to $3.99 -- the exact // amount checkout.php charges for a card. Posters are $4.99 with no discount, // and that is what checkout.php charges for a poster. The display and the // charge have to move together: if a price changes here, change the matching // $priceInCents in checkout.php in the same edit. $cardPriceRegular = "4.99"; $cardPriceSale = "3.99"; $cardPricePercentOff = "20"; $posterPrice = "4.99"; $productPriceHtml = ""; $productPriceCompactHtml = ""; $schemaPrice = ""; // Results pagination state. The search branch and the category branch each set // these; the grid at the bottom of the page only reads them, so a branch that // never paginates (the featured homepage) simply leaves the defaults. $pagerPage = 1; $pagerTotalPages = 1; $pagerPerPage = 40; $pagerBaseUrl = ""; $pagerDoc = ""; $pagerSearch = ""; /* --------------------------------------------------------------------------- Search helpers. Used by the router below AND by the AJAX "load more" branch further down, so the first page and every page after it always build the same WHERE clause. --------------------------------------------------------------------------- */ // Turn raw user input into a small list of normalised search terms. function csc_search_terms($raw) { if (!is_string($raw)) { return []; } $raw = trim($raw); if ($raw === '') { return []; } // Cap the length so a huge query string cannot build a huge LIKE chain. $raw = function_exists('mb_substr') ? mb_substr($raw, 0, 80, 'UTF-8') : substr($raw, 0, 80); // Stored text mixes spaces with - and _ (slugs such as birthday-cards), so // the query is normalised the same way csc_search_where() normalises columns. $raw = str_replace(['-', '_'], ' ', $raw); $raw = preg_replace('/\s+/u', ' ', $raw); if (!is_string($raw) || trim($raw) === '') { return []; } $terms = preg_split('/ /', trim($raw), -1, PREG_SPLIT_NO_EMPTY); return is_array($terms) ? array_slice($terms, 0, 5) : []; } // Escape LIKE wildcards, so searching for 100% matches the literal text. function csc_like_escape($term) { return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $term); } // Build the WHERE fragment for a set of search terms. // Returns ['sql' => '...', 'params' => [':s0' => '%term%', ...]]. function csc_search_where(array $terms) { $clauses = []; $params = []; foreach ($terms as $i => $term) { $key = ':s' . $i; $params[$key] = '%' . csc_like_escape($term) . '%'; // Titles, tags and categories separate words with spaces, but slugs use // dashes and some imported tags use underscores, hence the REPLACE()s. // The long descriptions are searched too, so a card can be found by a // detail that never made it into its title or tags. $clauses[] = "(REPLACE(title, '-', ' ') LIKE {$key}" . " OR REPLACE(REPLACE(tags, '_', ' '), '-', ' ') LIKE {$key}" . " OR REPLACE(category, '-', ' ') LIKE {$key}" . " OR REPLACE(subtitle, '-', ' ') LIKE {$key}" . " OR REPLACE(description, '-', ' ') LIKE {$key}" . " OR REPLACE(description_rich, '-', ' ') LIKE {$key}" . " OR REPLACE(og_meta_description, '-', ' ') LIKE {$key})"; } if (empty($clauses)) { return ['sql' => '1 = 0', 'params' => []]; } return ['sql' => implode(' AND ', $clauses), 'params' => $params]; } // Relevance tier: 0 when the first term appears in the title, 1 otherwise. // The ORDER BY needs its own parameter because the COUNT query cannot contain it. function csc_search_order(array $terms) { if (empty($terms)) { return ['sql' => 'id DESC', 'params' => []]; } return [ 'sql' => "CASE WHEN REPLACE(title, '-', ' ') LIKE :orderTitle THEN 0 ELSE 1 END, id DESC", 'params' => [':orderTitle' => '%' . csc_like_escape($terms[0]) . '%'], ]; } /* --------------------------------------------------------------------------- Results pagination. Drawn bottom-right on search results and category pages once there is more than one page of them. Every arrow is a real link to ?page=N, so a page can be opened in a new tab, bookmarked, shared or used with JavaScript switched off; the script at the foot of this file intercepts the clicks and swaps the grid in place instead of reloading. $baseUrl is the RAW page URL ("/cards/birthday-cards", "/?search=holiday") -- not an HTML-escaped one -- because the links are built from it too. --------------------------------------------------------------------------- */ function csc_pager_url($baseUrl, $page) { $separator = (strpos($baseUrl, '?') === false) ? '?' : '&'; return $baseUrl . $separator . 'page=' . (int)$page; } /* --------------------------------------------------------------------------- Builds the template image drawn on top of one of the mockup images in img/ (blank-card.jpg, template-card.jpg). $x,$y,$w,$h are pixels measured on the mockup's own $base x $base original -- 195,24 at 137x195 on the 500x500 blank card, say. They are turned into percentages here so one set of numbers is correct in the big screenshot and in the 84px thumbnail alike: the .csc-image-stack wrapper hugs the mockup image, so those percentages are measured against the image's own box at whatever size it is rendered. $view ties an overlay to the thumbnail that belongs with it; the click handler shows the overlay whose view is the selected one. An empty $src means the listing has no template image, i.e. no overlay at all. --------------------------------------------------------------------------- */ function csc_overlay_markup($src, $x, $y, $w, $h, $base, $view) { if ($src === "" || $base < 1) { return ""; } $pct = function ($value) use ($base) { // rtrim cannot eat the integer part: number_format always writes the // decimal point, and that is what stops it (100.0000 -> "100"). return rtrim(rtrim(number_format(100 * $value / $base, 4, '.', ''), '0'), '.') . '%'; }; return ""; } /* --------------------------------------------------------------------------- Price block for a product page. A card passes the regular price and the discount too: the sale price is then the largest thing in the row, the regular price is struck through beside it, and a gold sticker carries the percentage off. A poster passes the price alone, which draws just the one figure with no strike-through and no badge. Two sizes: the compact one sits above the buy button under the screenshot, the full one in the buy box beside the big BUY NOW. The aria-label spells out what the row shows, so a screen reader hears the same facts a sighted visitor sees. --------------------------------------------------------------------------- */ function csc_price_markup($price, $regular = '', $percent = '', $compact = false) { $onSale = ($regular !== '' && $percent !== ''); $classes = "csc-price" . ($onSale ? "" : " csc-price--single") . ($compact ? " csc-price--compact" : ""); $label = $onSale ? 'Sale price $' . $price . ', regular price $' . $regular . ', ' . $percent . ' percent off' : 'Price $' . $price; $html = "
" . "$" . $price . ""; if ($onSale) { $html .= "$" . $regular . "" . "" . $percent . "% off"; } return $html . "
"; } function csc_render_pager($page, $totalPages, $baseUrl, $doc = '', $search = '', $itemsPerPage = 40) { $page = (int)$page; $totalPages = (int)$totalPages; if ($totalPages < 2) { return; } if ($page < 1) { $page = 1; } if ($page > $totalPages) { $page = $totalPages; } $esc = function ($value) { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); }; // First / previous / next / last, each with the page it jumps to. The two // at the far end hold a valid href even when they are off (a no-JS click // then just reloads the page you are already on). $buttons = [ ['jump' => 'first', 'target' => 1, 'icon' => 'fa-angle-double-left', 'label' => 'First page', 'off' => ($page <= 1)], ['jump' => 'prev', 'target' => max(1, $page - 1), 'icon' => 'fa-angle-left', 'label' => 'Previous page', 'off' => ($page <= 1)], ['jump' => 'next', 'target' => min($totalPages, $page + 1), 'icon' => 'fa-angle-right', 'label' => 'Next page', 'off' => ($page >= $totalPages)], ['jump' => 'last', 'target' => $totalPages, 'icon' => 'fa-angle-double-right', 'label' => 'Last page', 'off' => ($page >= $totalPages)], ]; echo ""; } if(isset($_GET["search"]) && is_string($_GET["search"]) && trim($_GET["search"]) !== "") { // Whole-catalog search. $search stays RAW because it is bound into the // query; everything that reaches the page uses $searchDisplay, which is // HTML-escaped exactly once, here. $search = trim($_GET["search"]); $searchDisplay = htmlspecialchars($search, ENT_QUOTES, 'UTF-8'); $isSearch = true; // Which page of results. Anything silly is clamped once the total is known // below, so a stale ?page=99 link shows the last page rather than nothing. $pagerPerPage = 40; $pagerPage = isset($_GET['page']) ? (int)$_GET['page'] : 1; if ($pagerPage < 1) { $pagerPage = 1; } // The search page lives at the root ("/?search=..."), so its pager links // carry the query in the base URL; the term is rawurlencoded, never printed. $pagerBaseUrl = '/?search=' . rawurlencode($search); $pagerSearch = $search; $searchTerms = csc_search_terms($search); $searchWhere = csc_search_where($searchTerms); $searchOrder = csc_search_order($searchTerms); try { // Total matches, so the pager knows how many pages there are. $countStmt = $db->prepare("SELECT COUNT(*) AS total FROM listing WHERE " . $searchWhere['sql']); foreach ($searchWhere['params'] as $key => $value) { $countStmt->bindValue($key, $value, PDO::PARAM_STR); } $countStmt->execute(); $searchTotal = (int)$countStmt->fetch(PDO::FETCH_ASSOC)['total']; $pagerTotalPages = ($searchTotal > 0) ? (int)ceil($searchTotal / $pagerPerPage) : 0; if ($pagerTotalPages > 0 && $pagerPage > $pagerTotalPages) { $pagerPage = $pagerTotalPages; } $offset = ($pagerPage - 1) * $pagerPerPage; $query = " SELECT id, title, category, screenshot FROM listing WHERE " . $searchWhere['sql'] . " ORDER BY " . $searchOrder['sql'] . " LIMIT :offset, :limit "; $stmt = $db->prepare($query); foreach ($searchWhere['params'] as $key => $value) { $stmt->bindValue($key, $value, PDO::PARAM_STR); } foreach ($searchOrder['params'] as $key => $value) { $stmt->bindValue($key, $value, PDO::PARAM_STR); } $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->bindValue(':limit', $pagerPerPage, PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll() ?: []; } catch (PDOException $ex) { die("Failed to run query: " . $ex->getMessage()); } $displaySocialOnTop = true; // Head title and meta description for the results page. Truncate the RAW // query (so a cut can never split an HTML entity) and keep every literal " // out of these strings: they are echoed into double-quoted attributes and // into the JSON-LD block, where a stray quote truncates the tag or breaks // the JSON. $shortRaw = function_exists('mb_substr') ? mb_substr($search, 0, 60, 'UTF-8') : substr($search, 0, 60); $shortQuery = htmlspecialchars($shortRaw, ENT_QUOTES, 'UTF-8'); if ($shortQuery === '') { $shortQuery = 'your search'; } $title = 'Search results for ' . $shortQuery; $metadescription = $searchTotal . ' printable card' . ($searchTotal == 1 ? '' : 's') . ' and posters matching ' . $shortQuery . ' from Clever Studio Art.'; $schemaorgdescription = $metadescription; } else if(!empty($_GET["id"])) { // display the selected card $id = htmlspecialchars($_GET["id"]); $query = " SELECT id, title, subtitle, category, screenshot, template, REPLACE(tags,'_', ' ') as tags, etsy_listing, zazzle_url, description, og_image, og_meta_description, ( SELECT CONCAT(id, '|', title) FROM listing WHERE category = l.category AND id < :id ORDER BY id DESC LIMIT 1 ) AS prev_info, ( SELECT CONCAT(id, '|', title) FROM listing WHERE category = l.category AND id > :id ORDER BY id ASC LIMIT 1 ) AS next_info FROM listing l WHERE id = :id "; try { $stmt = $db->prepare($query); $stmt->bindValue(':id', $id, PDO::PARAM_STR); $stmt->execute(); } catch (PDOException $ex) { die("Failed to run query: " . $ex->getMessage()); } $row = $stmt->fetch(); if ($row == true) { $iddb = trim($row['id']); $title = trim($row['title']); $subtitle = trim($row['subtitle']); $titleLink = strtolower(str_replace(' ', '-', $row['title'])); $category = trim($row['category']); $catLink = str_replace(' ', '-', $row['category']); $tags = trim($row['tags']); $etsy_listing = trim($row['etsy_listing']); $description = trim($row['description']); $screenshot = rawurlencode(trim($row['screenshot'])); $ogimage = trim($row['og_image']); $ogdescription = trim($row['og_meta_description']); $metadescription = trim($row['og_meta_description']); $zazzle_url = trim($row['zazzle_url']); $prev_info = trim($row['prev_info']); $next_info = trim($row['next_info']); $screenshot = rawurlencode($screenshot); $catLink = rawurlencode($catLink); $catLink = str_replace('%2F', '/', $catLink);//make sure / is shown in link on page $titleLink = rawurlencode($titleLink); $ogimage = rawurlencode($ogimage); // The listing's own card face -- the file named in listing.template that // lives in template/. It gets drawn over the blank-card mockup, so an // empty column simply means no overlay is drawn. rawurlencode once: this // becomes a URL segment, it is never bound into a query. $templateFile = isset($row['template']) ? trim($row['template']) : ""; $templateOverlay = ($templateFile !== "") ? "https://www.cleverstudioart.com/template/" . rawurlencode($templateFile) : ""; $ogtitle = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); $ogdescription = htmlspecialchars($ogdescription, ENT_QUOTES, 'UTF-8'); } $pinterestUrl = "https://www.pinterest.com/pin/create/button/?url=https://www.cleverstudioart.com/" . $id . "/" . $catLink . "/" .$titleLink . "&media=https://www.cleverstudioart.com/screenshot/" . $screenshot . "&description=" . $ogtitle; $facebookUrl = "https://www.cleverstudioart.com/" .$id ."/" .$catLink ."/" .$titleLink; } else if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' && (isset($_GET['ajax']) || isset($_POST['ajax'])) && ($_GET['ajax'] === 'get_items' || $_POST['ajax'] === 'get_items')) { // Handle AJAX request for pagination header('Content-Type: application/json'); // Use request data from either GET or POST $requestData = array_merge($_GET, $_POST); try { $page = isset($requestData['page']) ? (int)$requestData['page'] : 1; $itemsPerPage = isset($requestData['itemsPerPage']) ? (int)$requestData['itemsPerPage'] : 40; $doc = isset($requestData['doc']) && is_string($requestData['doc']) ? $requestData['doc'] : ''; $searchRaw = isset($requestData['search']) && is_string($requestData['search']) ? $requestData['search'] : ''; if ($page < 1) $page = 1; if ($itemsPerPage < 1) $itemsPerPage = 40; $offset = ($page - 1) * $itemsPerPage; // Same WHERE builder the server-rendered page used, so every page the // pager asks for continues the exact same result set. The category is // only applied when the caller sent one, which is what keeps a search // inside its category. $whereParts = []; $whereParams = []; $searchTerms = csc_search_terms($searchRaw); if (!empty($searchTerms)) { $searchWhere = csc_search_where($searchTerms); $whereParts[] = $searchWhere['sql']; $whereParams += $searchWhere['params']; } if ($doc !== '') { $whereParts[] = "REPLACE(category, ' ', '-') = :doc"; $whereParams[':doc'] = $doc; } $whereSql = empty($whereParts) ? '1 = 0' : implode(' AND ', $whereParts); $searchOrder = csc_search_order($searchTerms); $query = " SELECT id, title, category, screenshot FROM listing WHERE " . $whereSql . " ORDER BY " . $searchOrder['sql'] . " LIMIT :offset, :limit "; $stmt = $db->prepare($query); foreach ($whereParams as $key => $value) { $stmt->bindValue($key, $value, PDO::PARAM_STR); } foreach ($searchOrder['params'] as $key => $value) { $stmt->bindValue($key, $value, PDO::PARAM_STR); } $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->bindValue(':limit', $itemsPerPage, PDO::PARAM_INT); $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; // Total count for the same filter, so pagination knows when to stop. $countStmt = $db->prepare("SELECT COUNT(*) as total FROM listing WHERE " . $whereSql); foreach ($whereParams as $key => $value) { $countStmt->bindValue($key, $value, PDO::PARAM_STR); } $countStmt->execute(); $totalItems = (int)$countStmt->fetch(PDO::FETCH_ASSOC)['total']; // Cast to int: ceil() returns a float, which json_encode would send as // 7.0 and the pager would have to parse back. $totalPages = ($totalItems > 0) ? (int)ceil($totalItems / $itemsPerPage) : 0; $hasMore = $page < $totalPages; echo json_encode([ 'success' => true, 'data' => $results, 'pagination' => [ 'page' => $page, 'itemsPerPage' => $itemsPerPage, 'totalItems' => $totalItems, 'totalPages' => $totalPages, 'hasMore' => $hasMore ] ]); exit(); } catch (Exception $e) { http_response_code(500); echo json_encode([ 'success' => false, 'error' => 'Database error: ' . $e->getMessage() ]); exit(); } } else if(!empty($_GET["folder"])) { // cards in a category $folder = htmlspecialchars($_GET["folder"]); $doc = htmlspecialchars($_GET["doc"]); if(!empty($_GET["sub"])) { $sub = htmlspecialchars($_GET["sub"]); $doc .= "/" . $sub; } // Raw copies of the same two segments, for the pager links and the AJAX // call: $folder/$doc are escaped for the page and bound into the query, but // a URL built from them would carry & where it needs &. $pagerBaseUrl = '/' . rawurlencode((string)($_GET['folder'] ?? '')) . '/' . rawurlencode((string)($_GET['doc'] ?? '')); $pagerDoc = (string)($_GET['doc'] ?? ''); if (!empty($_GET['sub']) && is_string($_GET['sub'])) { $pagerBaseUrl .= '/' . rawurlencode($_GET['sub']); $pagerDoc .= '/' . $_GET['sub']; } if ((strcasecmp($folder, "cards") == 0) || (strcasecmp($folder, "posters") == 0)) { $displaySocialOnTop = true; $pagerPerPage = 40; $pagerPage = isset($_GET['page']) ? (int)$_GET['page'] : 1; if ($pagerPage < 1) { $pagerPage = 1; } // First, get the total count $countStmt = $db->prepare("SELECT COUNT(*) as total FROM listing WHERE REPLACE(category, ' ', '-') = :doc"); $countStmt->bindValue(':doc', $doc, PDO::PARAM_STR); $countStmt->execute(); $totalItems = (int)$countStmt->fetch(PDO::FETCH_ASSOC)['total']; $pagerTotalPages = ($totalItems > 0) ? (int)ceil($totalItems / $pagerPerPage) : 0; if ($pagerTotalPages > 0 && $pagerPage > $pagerTotalPages) { $pagerPage = $pagerTotalPages; } $offset = ($pagerPage - 1) * $pagerPerPage; $query = " SELECT id, title, category, screenshot FROM listing WHERE REPLACE(category, ' ', '-') = :doc ORDER BY id DESC LIMIT :offset, :limit "; try { $stmt = $db->prepare($query); $stmt->bindValue(':doc', $doc, PDO::PARAM_STR); $stmt->bindValue(':offset', $offset, PDO::PARAM_INT); $stmt->bindValue(':limit', $pagerPerPage, PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch (PDOException $ex) { die("Failed to run query: " . $ex->getMessage()); } // only want this to run once if (!empty($rows)) { $firstRow = $rows[0]; $category = $firstRow['category']; //category will be like this: christmas-cards/funny-christmas-cards if (strrpos($category, '/') !== false) { $category = substr($category, strrpos($category, '/') + 1); } $catToGetDescription = $category; $category = str_replace('-', ' ', $category); //multiple titles per page so title of the page will be the category $title = ucwords($category); //grab the category description $query = " SELECT h2, description, descriptionlong FROM category_descriptions WHERE category = :catToGetDescription "; try { $stmt = $db->prepare($query); $stmt->bindValue(':catToGetDescription', $catToGetDescription, PDO::PARAM_STR); $stmt->execute(); } catch (PDOException $ex) { die("Failed to run query: " . $ex->getMessage()); } $catrows = $stmt->fetchAll() ?: []; $firstCatRow = $catrows[0]; $h2 = $firstCatRow['h2']; $metadescription = $firstCatRow['description']; $descriptionlong = $firstCatRow['descriptionlong']; } } } else { // get featured $displaySocialOnTop = true; $featured = true; $query = " SELECT id, title, category, screenshot FROM listing WHERE featured = 1 "; try { $stmt = $db->prepare($query); $stmt->execute(); } catch(PDOException $ex) { die("Failed to run query: " . $ex->getMessage()); } $rows = $stmt->fetchAll() ?: []; } // index page will already have Clever Studio Art so don't append $titletag = $title; if (stripos($titletag, 'Clever Studio Art') === false) { $titletag .= ' | Clever Studio Art'; } ?> <?php echo $titletag ?> \n\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\n\t"; } else if (strcasecmp($folder, "documents") == 0) { echo "\n\n\t"; } else { $result = $folder ? "$folder/$doc" : $doc; echo "\n\n\t"; // Search result pages are thin, duplicated content: keep them out of the // index but let crawlers follow the cards they link to. if ($isSearch) { echo "\n\n\t"; } echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\t"; echo "\n\n\t"; } ?>
Loading…

Thousands of Original Printable Cards and Posters

Ready for Instant Download

Age 40 Through 49 Birthday Cards". It is built HERE, above the search bar, because that is where it is printed now: on the same line, to the left of the search box. The listing branch that used to build it runs further down the file, after this markup has already gone out, so the one thing that has to be repeated is the display copy. $category is already in hand by now (the listing row is read at the top of the file), so this is the only place that builds a crumb -- do not add a second one below. The category slug is the listing's own, so a crumb can read "Home > Christmas Cards > Funny Adult Christmas Cards" when the category is stored with a slash in it. ------------------------------------------------------------------ */ $breadcrumbHtml = ""; if (!empty($iddb) && trim($category) !== "") { $crumbText = str_replace('-', ' ', $category); $crumbParent = ""; if (strrpos($crumbText, '/') !== false) { // ucwords here too: the old crumb printed the parent segment // straight from the slug, so a nested category read // "Home > christmas cards > Funny Adult Christmas Cards". $crumbParent = ucwords(substr($crumbText, 0, strrpos($crumbText, '/'))) . " > "; $crumbText = substr($crumbText, strrpos($crumbText, '/') + 1); } $crumbText = ucwords($crumbText); // The same substring test the listing branch, checkout.php and // webhook.php use to tell a poster from a card, so the crumb links // into the right half of the site. $crumbNav = (strpos(strtolower($category), 'poster') !== false) ? "posters" : "cards"; $breadcrumbHtml = "Home > " . $crumbParent . "" . $crumbText . ""; } ?>
5 printable cards you can instantly download, designed to fit the most popular envelope sizes (A7, A9, A2, A6). Each file is high resolution for a crisp, professional-quality print every time. No more guessing the right size!"; // A poster listing is decided by this one substring test, the // same one checkout.php and webhook.php use, so it is worked // out once here and reused further down to leave the poster // rail with the plain screenshot alone. $isPoster = (strpos(strtolower($category), 'poster') !== false); if ($isPoster) { $nav = "posters"; $item = "Poster"; $whatYouGet = "You get 5 high-quality JPEG files ready to download instantly, in these sizes (in inches): 5x7, 8x10, 8.5x11, 11x14, 16x20. Each file is high resolution, so your prints come out sharp, colorful, and beautiful at any size."; } // The visible price and the Offer schema price are both decided // here from the same $isPoster test, so a poster can never // advertise the card sale and a card can never advertise the // poster price. Cards draw the 20% offer; posters draw the // plain $4.99 with no strike-through and no badge. $schemaPrice = $isPoster ? $posterPrice : $cardPriceSale; if ($isPoster) { $productPriceHtml = csc_price_markup($posterPrice); $productPriceCompactHtml = csc_price_markup($posterPrice, '', '', true); } else { $productPriceHtml = csc_price_markup($cardPriceSale, $cardPriceRegular, $cardPricePercentOff); $productPriceCompactHtml = csc_price_markup($cardPriceSale, $cardPriceRegular, $cardPricePercentOff, true); } // The five files, listed under BUY NOW exactly the way // freebies.html lists its five downloads -- same rows, same // icon, same size-then-envelope wording -- so anyone who has // seen the freebies page recognises them at a glance. // The destination is the one thing that is NOT copied: these // are submit buttons for the buy form, not links to files. // Nothing here is downloadable before paying, and checkout.php // reads $_POST only, so a plain could not reach it at // all. Any one of the five buys the same thing, because the // purchase is the whole set -- which is why the heading says // so rather than implying a single-size download. // Sizes are the site's OWN names, not ISO A-series: A2 is // 4.25 x 5.5 and A9 is 5.5 x 8.5 (see AGENTS.md). They mirror // WH_SIZE_GUIDE in webhook.php and the five labels on the // freebies page, so keep all three in step. $buySizes = $isPoster ? array( array('5 × 7 in', ''), array('8 × 10 in', ''), array('8.5 × 11 in', ''), array('11 × 14 in', ''), array('16 × 20 in', '') ) : array( array('5 × 7 in', 'A7 envelope'), array('5½ × 8½ in', 'A9 envelope'), array('4¼ × 5½ in', 'A2 envelope'), array('5 × 5 in', 'square'), array('4½ × 6¼ in', 'A6 envelope') ); $buySizeIcon = $isPoster ? 'fa-file-image' : 'fa-file-pdf'; $buySizeItems = ""; foreach ($buySizes as $buySize) { $buySizeLabel = $buySize[0] . (($buySize[1] !== "") ? " (" . $buySize[1] . ")" : ""); $buySizeItems .= "
  • "; } $buySizesHtml = "
    " . "

    All 5 sizes are included:

    " . "
      " . $buySizeItems . "
    " . "

    Pick any size to check out — the download links for all five arrive by email.

    " . "
    "; // The breadcrumb is NOT built here any more: it is // printed up in the search bar's row, from // $breadcrumbHtml, which is built above that markup. $etsyButtonUrl = ""; $zazzleButtonUrl = ""; $editUrl = ""; // The listing's card face, drawn on top of the three mockups // in img/. All three mockups are 500x500 and every overlay // uses the same template file -- only the position differs: // blank-card.jpg the white card at the top of the shot // template-card.jpg the right-hand panel of the flat sheet // dimensions-card.jpg the five sizes, one card face each // onerror (inside the helper) hides an overlay rather than // printing a broken image icon if the template file is // missing. The click handler reveals whichever overlays belong // to the mockup being shown, so all five of the dimensions // ones share the one view name and come and go together. // Posters skip the lot (see $isPoster above): those mockups // are all folded-card shots -- a poster is a single flat // print, so there is no card face to lay on them and nothing // is built, which also keeps the template file off the page. $overlayBlankCard = ""; $overlayTemplateCard = ""; $overlayDimensionsCard = ""; if (!$isPoster) { $overlayBlankCard = csc_overlay_markup($templateOverlay, 195, 24, 137, 195, 500, 'blank'); $overlayTemplateCard = csc_overlay_markup($templateOverlay, 255, 221, 144, 207, 500, 'template'); $overlayDimensionsCard = csc_overlay_markup($templateOverlay, 142, 165, 69, 88, 500, 'dimensions') // 5" x 5" . csc_overlay_markup($templateOverlay, 307, 147, 80, 106, 500, 'dimensions') // 4 1/2" x 6 1/4" . csc_overlay_markup($templateOverlay, 104, 314, 74, 114, 500, 'dimensions') // 4 1/4" x 5 1/2" . csc_overlay_markup($templateOverlay, 214, 266, 98, 149, 500, 'dimensions') // 5 1/2" x 8 1/2" . csc_overlay_markup($templateOverlay, 346, 301, 94, 118, 500, 'dimensions'); // 5" x 7" } // The card face itself, built once so the three shop links // below cannot drift apart. $screenshotImg = "" . $overlayBlankCard . $overlayTemplateCard . $overlayDimensionsCard . ""; //default to Etsy shop $imgUrl = "
    " . "
    " . $screenshotImg . "
    "; if (!empty($etsy_listing)) { $etsyButtonUrl = "Get On Etsy"; $imgUrl = "" . "
    " . $screenshotImg . "
    "; } if (!empty($zazzle_url)) { $zazzleButtonUrl = " Get On Zazzle"; $imgUrl = "" . "
    " . $screenshotImg . "
    "; } // Thumbnails shown down the left of the big screenshot, // top to bottom. The FIRST entry is the image this page // loads, so it is the one marked active on arrival; the // rest are alternatives that replace it when clicked. // Adding another is a one-line append here — the markup, // the CSS and the click handler all pick it up with no // other change. // The three mockup shots that follow are card-only, so a poster // listing (tested once as $isPoster above) keeps the plain // screenshot on its own. $cardThumbs = array(); $cardThumbs[] = array( 'src' => "https://www.cleverstudioart.com/screenshot/" . $screenshot, 'alt' => htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . " thumbnail" ); if (!$isPoster) { $cardThumbs[] = array( 'src' => "https://www.cleverstudioart.com/img/blank-card.jpg", 'alt' => "Blank inside - room for your own personal message", // The card face sits on the blank mockup. The overlay is // drawn in proportion in the thumbnail and hidden on the // big image until this thumbnail is the one showing. 'overlay' => $overlayBlankCard, 'view' => 'blank' ); $cardThumbs[] = array( 'src' => "https://www.cleverstudioart.com/img/template-card.jpg", 'alt' => "Printable card sizes - one sheet folds to the card size", // Same template file again, on the flat sheet mockup. Its // own view name keeps the sets apart: showing this mockup // must not reveal the blank card's overlay. 'overlay' => $overlayTemplateCard, 'view' => 'template' ); $cardThumbs[] = array( 'src' => "https://www.cleverstudioart.com/img/dimensions-card.jpg", 'alt' => "The 5 printable card sizes included, each shown with its envelope", // Five overlays, one per card in the shot. They are a single // view because they are one picture: the handler turns on // every overlay whose view matches the thumbnail picked, so // the five arrive and leave together. 'overlay' => $overlayDimensionsCard, 'view' => 'dimensions' ); } $thumbRail = ""; $thumbCount = count($cardThumbs); foreach ($cardThumbs as $thumbIndex => $thumb) { $thumbActive = ($thumbIndex === 0) ? " is-active" : ""; $thumbView = (!empty($thumb['view'])) ? " data-view='" . $thumb['view'] . "'" : ""; $thumbOverlay = (!empty($thumb['overlay'])) ? $thumb['overlay'] : ""; $thumbRail .= ""; } // A rail holding one image has nothing to choose between, so it // is not drawn at all -- that is every poster listing, which // ends up with the screenshot alone and centred, exactly as it // was before the thumbnails existed. Keyed off the count rather // than $isPoster so it stays right if a poster ever gains a // second image. $thumbRailHtml = ""; if ($thumbCount > 1) { $thumbRailHtml = "
    " . $thumbRail . "
    "; } if ($isMe == true) { $editUrl = " Edit"; } $sanitized_description = nl2br(htmlspecialchars($description, ENT_QUOTES, 'UTF-8')); //previous and next links $prev_id = $next_id = $prev_title = $next_title = $prev_link = $next_link = ""; if (!empty($prev_info)) { list($prev_id, $prev_title) = explode('|', $prev_info, 2); } if (!empty($next_info)) { list($next_id, $next_title) = explode('|', $next_info, 2); } if (!empty($prev_id)) { $prev_title_link = strtolower(str_replace(' ', '-', $prev_title)); $prev_link = "" . ""; } if (!empty($next_id)) { $next_title_link = strtolower(str_replace(' ', '-', $next_title)); $next_link = "" . ""; } // The two neighbours are chevron buttons laid over the // left and right edges of the preview now, not text // links on a line under it -- so no " | " separator, // and no visible text at all: the label rides on // aria-label and the tooltip. // Built only when there IS a neighbour, so the first // and last card in a category draws one arrow, not a // dead one pointing nowhere. $cardNavHtml = ""; if ($prev_link !== "" || $next_link !== "") { $cardNavHtml = $prev_link . $next_link; } echo "

    {$title}

    {$subtitle}

    {$thumbRailHtml}
    {$imgUrl} {$cardNavHtml}
    {$productPriceCompactHtml}

    Includes all 5 print-ready files — print as many times as you like.

    {$title} Details

    {$sanitized_description}

    Get This {$item} Instantly

    {$productPriceHtml}

    {$whatYouGet}

    {$buySizesHtml}

    Also available on

    {$etsyButtonUrl} {$zazzleButtonUrl} {$editUrl}

    Frequently Asked Questions about this {$subcategoryDisplayWithoutSonEnd}

    Will the \"{$title}\" be mailed to me, or is it an instant download?

    Direct purchase using the \"Buy Now\" button above This is an instant download. When you purchase this \"{$title}\", you will receive 5 high resolution digital files ( by email that you can print yourself at home or through a professional print service. No physical {$item} will be shipped to you. This allows you to print it instantly and as many times as you like!

    "; if (!empty($etsy_listing)) { echo "

    The \"Get on Etsy\" button above This is an instant digital download. When you purchase this \"{$title}\", you’ll receive 5 high resolution digital files to print at home or through your favorite print service. No physical {$item} will be shipped. Print instantly and as many times as you like!

    "; } if (!empty($zazzle_url)) { echo "

    The \"Get on Zazzle\" button above This product is available as either an instant digital download or a professionally printed physical card you choose! When you select the download option, you’ll receive a high resolution digital file to print at home or through a print service. If you prefer a physical card, Zazzle will print and ship it directly to you.

    "; } echo "
    What are the size and format details of the printable {$subcategoryDisplayWithoutSonEnd} file?
    "; if (strtolower($item) === 'greeting card') { echo "

    Your download includes 5 high quality PDF cards that fit most common envelopes (in inches):

    • Card 1:  5 x 7 (Fits A7 Envelope)
    • Card 2:  5 1/2 x 8 1/2 (Fits A9 Envelope)
    • Card 3:  4 1/4 x 5 1/2 (Fits A2 Envelope)
    • Card 4:  5 x 5 (Fits 5 1/2 x 5 1/2 Envelope)
    • Card 5:  4 1/2 x 6 1/4 (Fits A6 Envelope)

    Each card is high resolution at 300 dpi and ready to print on high-quality lightweight cardstock, making it perfect for home printing.

    "; } if (strtolower($item) === 'poster') { echo "

    Your download includes 5 high quality JPEG files that contain:

    • 1 - 5\" x 7\" photo size
    • 1 - 8\" x 10\" poster size
    • 1 - 8.5\" x 11\" poster size
    • 1 - 11\" x 14\" poster size
    • 1 - 16\" x 20\" poster size

    Each poster is high resolution at 300 dpi and ready to print on high-quality lightweight cardstock, making it perfect for home printing or professional print shops.

    "; } echo "
    How do I print the {$item}?

    Choose the printing method that works best for you!

    1. Print from home using your personal printer
    2. Take or upload your file to a nearby print shop like Staples, Walgreens, or Walmart
    3. Print online using Snapfish, Shutterfly, or Mpix.com for convenience

    For the best printing results, choose high-quality lightweight cardstock.

    Who is the ideal recipient for this {$title}?

    This {$subcategoryDisplayWithoutSonEnd} is perfect for anyone you care about, whether it’s a friend, family member, partner, or coworker. It’s designed to share a heartfelt, thoughtful, or funny message with someone special for any occasion.

    Can I print this {$subcategoryDisplayWithoutSonEnd} on different types of paper?

    Absolutely! For the best quality, we recommend using a heavy cardstock (80 lb to 110 lb weight) in a bright white finish. You can use glossy, matte, or satin paper based on your personal preference. Printing on high-quality cardstock will give your instant download card a professional, finished look.

    "; } else if (strcasecmp($folder, "documents") == 0) { if (strcasecmp($doc, "privacy") == 0) { echo "

    Privacy Policy

    Last updated: October 16, 2026

    Acez Software LLC ('Acez Software'), the owner and operator of Clever Studio Art, is dedicated to respecting and protecting the privacy of users who engage with our services. This policy outlines our practices regarding the collection and use of information on CleverStudioArt.com ('Web Site').
    Please note that Acez Software may have distinct privacy policies for other products and services we provide. Additionally, some services or content accessible through this Web Site may be offered by third-party providers. The collection and use of your information in connection with these third-party services and content are subject to their respective privacy policies, terms of use, or other applicable terms and conditions.

    Information You Give Us
    We collect personal information that you choose to give us. Two things on this Web Site ask for it:

    • Our email list. When you sign up for free printables, newsletters or offers, we collect your email address and, if you type it in, your first name. Nothing else is required, and you can unsubscribe at any time.

    • Buying a printable. When you buy a card or poster, the checkout is handled by Stripe, our payment provider. Stripe collects your name, email address and payment details, and passes your name, email address and order details back to us so that we can send you the files you bought and keep a record of the sale. We never see or store your card number.

    Beyond those two things, this Web Site does not ask you for personal information, and we do not want you to send us any.

    How We Use Your Email Address
    If you join our email list, we use your email address to send you the free download you asked for, occasional newsletters and new designs, seasonal freebies, and offers related to Clever Studio Art. If you give us your first name, we use it only to greet you by name.
    Our legal basis for sending marketing email is your consent, given when you submit the signup form, together with our legitimate interest in telling people who have asked to hear from us about our work. You may withdraw that consent at any time by unsubscribing.

    Confirmed Opt-In
    We use a confirmed opt-in, also known as double opt-in. After you sign up we email you a confirmation link, and you are added to the mailing list only once you click it. This confirms that the address belongs to you and that the request came from you.

    Our Email Service Provider
    We do not run our own mailing system. Subscriber email addresses are stored and processed on our behalf by Kit (formerly ConvertKit), which serves the signup form embedded in this Web Site and sends our emails. Kit acts as our data processor: it is bound by its own privacy policy and data processing terms, and we do not permit it to use your address for its own marketing. When you submit the signup form, Kit also sets a cookie in your browser so that the site can recognise that you have already subscribed.

    Email Open and Click Tracking
    Like most mailing services, Kit records whether the emails we send are opened and which links inside them are clicked, so that we can see what people find useful and send less of what they do not. This is recorded against your email address. If you would rather not be counted, you can set your email program not to load images, which is what open tracking relies on.

    Unsubscribing and Opting Out
    Every email we send includes a one-click Unsubscribe link in its footer, and you may also reply to any email or write to us at the address at the end of this policy to be removed by hand. There is no charge, and you do not need to give a reason. Once you unsubscribe we stop sending you marketing email. We may keep your address on a short suppression list so that we can honour your request and do not contact you again by accident.

    How Long We Keep Your Information
    Your email address is kept for as long as you remain subscribed. If you unsubscribe, or ask us to delete it, we remove it from the mailing list and keep only the suppression record described above. Order records, such as the name, email address and item purchased, are kept as long as we need them for accounting, tax and customer service purposes. Server log information is kept only as long as our hosting provider retains it.

    Where Your Information Is Processed
    We are based in the United States, and our service providers, including Kit, store and process data in the United States. If you sign up from the European Union, the United Kingdom, Canada or elsewhere, your information will be transferred to and processed in the United States. Where the law requires it, our providers put appropriate safeguards in place for such transfers, such as the Standard Contractual Clauses or the EU-US Data Privacy Framework.

    We Do Not Sell Your Information
    We do not sell, rent or trade email addresses or any other personal information. We share it only with the service providers described in this policy who help us run the site, send our email and process orders, and only as far as is needed for those purposes.

    Log Files
    We adhere to standard practices for web hosting by utilizing log files to record visitor activity. These logs capture information such as: Internet Protocol (IP) addresses, Browser type, Internet Service Provider (ISP), Date and time of visits, Referring and exit pages, The number of clicks.
    This information is not used by us to personally identify individual users and is used solely for purposes such as analyzing trends, administering the site, tracking visitor movements, and gathering demographic data.

    Cookies, Local Storage and Tracking Technologies
    This Web Site does not set first-party tracking cookies of its own. Two things are stored in your browser, and both are described here:

    • The signup popup. It writes a small flag, and a timestamp if you dismiss it, into your browser's local storage, so that we do not ask you to subscribe again during the same visit or for the next 30 days. These stay in your browser and are never sent to us.

    • The signup form. Kit sets a cookie when you submit the form, as described above, so that the site knows you have already subscribed.

    Third-party services such as Google Analytics, Ahrefs, Google AdSense, Facebook and Pinterest may also set cookies through our site, as described below. You can manage or delete cookies at any time in your browser settings.

    Ahrefs Analytics
    We use Ahrefs to help us analyze website traffic, monitor backlinks, and understand user engagement on our Web Site. Ahrefs collects non-personally identifiable or aggregated data about visitors, such as pages visited, referring sites, and general behavior on the site. This information helps us improve our Web Site, optimize content, and make informed decisions about our online presence.

    Google Analytics
    We use Google Analytics to help us understand how visitors engage with our Web Site. Google Analytics collects information such as how often users visit this site, what pages they visit when they do so, and what other sites they used prior to coming to this site. This information is collected via cookies and is used to improve the Web Site and its offerings. Google's ability to use and share information collected by Google Analytics about your visits to this site is restricted by the Google Analytics Terms of Service and the Google Privacy Policy. You can prevent Google Analytics from recognizing you on return visits to this site by disabling cookies in your browser. Additionally, you may also consider using the Google Analytics opt-out browser add-on, which is available here. Google Analytics may collect anonymized or aggregated usage data and does not provide us with direct personal identification of individual users.

    Third-Party Advertising (Google AdSense)
    We use Google AdSense to serve advertisements on our Web Site. Google, as a third-party vendor, uses cookies to serve ads based on a user's prior visits to our site and other sites on the Internet. These cookies, known as the DoubleClick DART cookie, enable Google and its partners to serve ads to our users based on their visit to our sites and/or other sites on the Internet.

    You may opt out of the use of the DART cookie by visiting the Google Advertising Opt-out page: Ad Settings.

    For more information on how Google uses information from sites or apps that use its services, please visit: Advertising and How Google uses information from sites or apps that use our services.
    Google may also serve non-personalized advertisements based on contextual information rather than user browsing behavior.

    Other Third-Party Services on This Site
    Some of our pages load content or buttons from other companies, which may set their own cookies and will receive your IP address: Facebook (the page plugin and the share button in our footer), Pinterest (the Save button), and SociableKit (which displays reviews of our Etsy shop). We do not control what those companies collect through their own services, and their privacy policies apply. We encourage you to read them.

    Third-Party Privacy Policies
    This Privacy Policy does not apply to other advertisers or websites, and we are not responsible for their practices. We encourage you to read the privacy policy of each service you interact with through our site:
    Kit, our email service provider: kit.com/privacy
    Stripe, our checkout and payment provider: stripe.com/privacy
    Google, for Analytics and AdSense: policies.google.com/privacy
    Meta / Facebook: www.facebook.com/privacy/policy
    Pinterest: policy.pinterest.com/en/privacy-policy
    Ahrefs: ahrefs.com/legal/privacy-policy
    SociableKit, which shows our Etsy reviews: www.sociablekit.com/privacy-policy

    CCPA and CPRA Privacy Rights (California)
    Acez Software does not sell personal information, and does not share it for cross-context behavioral advertising, as those terms are defined under the California Consumer Privacy Act as amended by the California Privacy Rights Act.
    California consumers have the right to:
    • know the categories and specific pieces of personal information we have collected, and how we use and disclose it;
    • request that we delete personal information we have collected about you;
    • request that we correct inaccurate personal information;
    • opt out of the sale or sharing of personal information, which we do not do in any case;
    • not be discriminated against for exercising any of these rights.
    You may make a request yourself or through an authorized agent. We will respond within 45 days. If you would like to exercise any of these rights, please contact us using the details at the end of this policy.

    GDPR Data Protection Rights
    We would like to make sure you are fully aware of all of your data protection rights. Every user is entitled to the following:
    • The right to access – You have the right to request copies of your personal data. Requests are generally provided free of charge unless they are excessive or unfounded, in which case a reasonable fee may apply.
    • The right to rectification – You have the right to request that we correct any information you believe is inaccurate. You also have the right to request that we complete the information you believe is incomplete.
    • The right to erasure – You have the right to request that we erase your personal data, under certain conditions.
    • The right to restrict processing – You have the right to request that we restrict the processing of your personal data, under certain conditions.
    • The right to object to processing – You have the right to object to our processing of your personal data, under certain conditions.
    • The right to data portability – You have the right to request that we transfer the data that we have collected to another organization, or directly to you, under certain conditions.
    • The right to withdraw consent – Where we rely on your consent, such as for marketing email, you may withdraw it at any time by unsubscribing.
    • The right to lodge a complaint – You have the right to complain to your local data protection authority about how we handle your personal data.
    If you make a request, we have one month to respond to you. If you would like to exercise any of these rights, please contact us.

    Children
    This Web Site is intended for a general audience and is not directed toward children under the age of 13. We do not knowingly collect personal information from children.

    Policy Updates
    Acez Software reserves the right to modify this Privacy Policy at any time. Updates will be posted on this page, with the date at the top of the policy changed, to keep you informed about our data collection and usage practices.

    Contact Us
    If you have any questions or concerns about this Privacy Policy, or would like to exercise any of the rights described above, please contact us at:

    Mailing Address
    Acez Software LLC
    66 Park Ridge Drive
    East Greenbush, NY 12061
    United States

    "; } else if (strcasecmp($doc, "terms") == 0) { echo "

    Terms of Use

    Last updated: October 16, 2026

    Acez Software LLC ('Acez Software') provides this web site, CleverStudioArt.com, as a service to our customers. By accessing, browsing, and/or using this web site, you acknowledge that you have read, understood, and agree to be bound by the following terms of use and to comply with all applicable laws and regulations. If you do not agree to these terms, you should not review information or obtain goods or products from this site.

    1. General Terms and Acceptance

    The content, information, graphics, design, compilation, products, software, services, and all other matters related to this web site ('Materials') are protected by law, including, but not limited to, copyright laws and international treaties. Access to the Materials from territories where their contents are illegal is prohibited. Those who choose to access this web site from other locations do so on their own initiative and are responsible for compliance with applicable local laws.

    The invalidity or unenforceability of any provision of these Terms of Use will not affect the validity or enforceability of any other provision. Acez Software reserves the right to modify or to terminate your use of our site without notice if you fail to comply with any term of these Terms of Use as we may determine in our sole discretion. Upon termination, you must destroy all Materials and any copies obtained from this web site.

    Your right to use the web site is not transferable. Any download link or right given to you to obtain Materials is not transferable. These Terms of Use contain our entire understanding relating to the use of this web site and supersede any prior written or oral agreement or understandings between us with respect to this web site, and cannot be changed or terminated except in writing by an authorized representative of Acez Software.

    2. Modification of Terms and Site

    Acez Software reserves the right in its sole discretion to modify or delete any Material appearing on the web site or to terminate this web site without notice. Acez Software may in the future alter or add to these Terms of Use. Accordingly, you agree to check this part of our site from time to time to see if there has been a change, as we cannot guarantee that you will receive personal notice of modifications.

    3. Products and Delivery

    All items sold on this web site are digital downloads. No physical product is printed, packaged or shipped to you, and nothing will arrive by post. When you buy a card or poster, the five print-ready files are made available to you as download links, and the same links are emailed to the address you gave at checkout.
    Download links are provided for you, the buyer. They are personal and not transferable, and passing them on, publishing them or sharing them with anyone else is not permitted. Please save your files somewhere safe once you have downloaded them.
    The files are supplied at 300 dpi and sized for the print sizes described on the listing. How a card looks when printed depends on your printer, your paper and your settings, so we cannot guarantee the result of any particular printer or print service. For best results, use a good quality lightweight cardstock.

    4. Licence for Purchased and Free Downloads

    When you buy a file, or download one of our free printables, you are granted a licence to use the artwork for your own personal use. That means you may:

    • print the files as many times as you like, for yourself, your household, and cards you give away personally;
    • keep copies of the files for your own use;
    • print them at home, or take them to a print shop to be printed for you.

    The licence does not allow you to:

    • sell or resell the files, or anything printed from them, including print-on-demand products;
    • share, upload, publish or redistribute the files, whether or not you charge for them;
    • claim the artwork as your own, or register it as a trademark or copyright;
    • use the artwork in your own products, branding, advertising or client work.

    The artwork itself remains the property of Acez Software LLC. If you would like a licence for commercial use, please contact us and we will be glad to discuss it.

    5. Payment, Pricing and Taxes

    Payments are processed by Stripe, our payment provider. We do not receive or store your card details. Prices are shown in United States dollars (USD) and may change at any time; the price shown at the moment you place your order is the price that applies to that order. Where sales tax or similar taxes apply, they are added at checkout. Any duties or taxes charged in your own country are your responsibility.

    6. Refunds and Returns

    We want you to be happy with what you buy. If a purchase is not what you expected, write to us within 30 days of the purchase and we will refund it in full. You do not have to give a reason, and there is no charge for the refund.
    Because our products are digital downloads, there is nothing to send back. Refunds are made through Stripe to the card or account you paid with, and usually appear within a few business days. To request a refund, write to us at the address at the end of these Terms, quoting the email address you used at checkout.
    If a file is faulty, will not open, or is not the item you ordered, tell us and we will replace it or refund you.

    Buyers in the European Union and the United Kingdom: you would normally have 14 days to withdraw from a distance purchase. For digital content supplied immediately, that right ends once the download begins, and by placing your order and downloading the files you consent to immediate delivery and acknowledge that you lose the right of withdrawal. This does not affect your rights if a file is faulty, nor the 30-day refund described above.

    7. Intellectual Property and Usage

    The Materials contain copyrighted, trademarked, and/or service marked animations, photographs, images, videos, text, music and sounds. Nothing on this web site should be construed as granting, by implication, estoppel, or otherwise, any license or right to use any Materials, without the prior written permission of the copyright, trademark and/or service mark owner. Acez Software will enforce its intellectual property rights to the fullest extent of the law.

    The name of Acez Software, or the Acez Software logo may not be used in any way, including in advertising or publicity pertaining to distribution of Materials on this web site, without prior written permission. Acez Software prohibits use of the logo as an electronic link to any Acez Software site unless establishment of such a link is approved in advanced by Acez Software in writing.

    Screenshot Disclaimer: Art, posters, and card screenshots and icons on the web site may or may not be the actual size or true representation of the actual physical or digital art.

    ALL RIGHTS NOT EXPRESSLY GRANTED HEREIN ARE RESERVED.

    8. Disclaimers of Warranties and Limitation of Liability

    UNLESS OTHERWISE EXPLICITLY STATED, THE MATERIALS ARE FURNISHED 'AS IS,' 'AS AVAILABLE,' AND WITH ALL FAULTS. ACEZ SOFTWARE MAKES, AND YOU RECEIVE, NO WARRANTIES, EXPRESS, IMPLIED, STATUTORY, OR IN ANY COMMUNICATION WITH YOU REGARDING THE MATERIALS. ACEZ SOFTWARE SPECIFICALLY DISCLAIMS ANY OTHER WARRANTY INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE.

    ACEZ SOFTWARE DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE, VALIDITY, ACCURACY, OR RELIABILITY OF, OR THE RESULTS OF THE USE OF, OR OTHERWISE RESPECTING, THE MATERIALS ON THIS WEB SITE OR ANY WEB SITES LINKED TO THIS WEB SITE.

    IN NO EVENT WILL ACEZ SOFTWARE BE LIABLE FOR ANY INDIRECT DAMAGES, WHETHER ARISING FROM TORT OR CONTRACT, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA, LOST PROFITS, COST OF COVER, OR OTHER SPECIAL, INCIDENTAL, RELIANCE, CONSEQUENTIAL, OR INDIRECT DAMAGES ARISING OUT OF THE SERVICES PROVIDED, OR THE USE OR INABILITY TO USE ANY OF THE MATERIALS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY. THIS LIMITATION WILL APPLY EVEN IF ACEZ SOFTWARE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE AND EVEN IF ANY WARRANTY OR REMEDY FAILS OF ITS ESSENTIAL PURPOSE.

    THE TOTAL LIABILITY OF ACEZ SOFTWARE WILL BE LIMITED TO THE AMOUNT PAID BY YOU, IF ANY, FOR ACCESSING OR USING THIS WEB SITE. ANY CLAIMS RELATING TO MATERIALS DOWNLOADED PURSUANT TO A SEPARATE LICENSE ARE GOVERNED SOLELY BY THAT LICENSE. BECAUSE SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU.

    9. Indemnification

    You hereby agree to indemnify and hold Acez Software harmless for any and all costs, damages, expenses, liabilities, or obligations (including defense costs and attorneys' fees) that it may incur as a result of claims asserted against it by any third party that directly or indirectly arise or otherwise relate to your access to and use of the Materials, including without limitation your failure to comply with the terms of this Agreement or any License Agreement.

    10. Links to Third-Party Sites

    Acez Software may provide links to third-party sites. Acez Software does not control the linked sites and is not responsible for the contents of any linked site or any link contained in a linked site, or any changes or updates to such sites. Acez Software is providing these links to you only as a convenience, and the inclusion of any link does not imply endorsement by Acez Software of the site. If you decide to leave our web site and access these third-party sites, you do so at your own risk.

    11. Privacy

    How we handle personal information – what we collect, why we collect it, how long we keep it, and the rights you have over it – is set out in our Privacy Policy, which forms part of these Terms of Use.

    12. Eligibility

    This web site is intended for a general audience and is not directed to children under the age of 13. By buying from us, or by joining our email list, you confirm that you are old enough to enter into a contract, or that you have permission from a parent or guardian to do so.

    13. Copyright Complaints

    All artwork on this web site is original work created by Acez Software LLC. If you believe that anything on this web site infringes your copyright, please write to us at the address at the end of these Terms with details of the work concerned and of the material you believe infringes it. We will look into it promptly and remove the material if the complaint is well founded.

    14. Governing Law

    These Terms of Use are governed by the laws of the State of New York, United States, without regard to its conflict of law rules, and you agree that any dispute arising out of or relating to this web site, or to a purchase from it, will be brought in the state or federal courts located in New York.

    Contact Us
    If you have any questions or concerns about these Terms of Use, please contact us at:

    Acez Software LLC
    66 Park Ridge Drive
    East Greenbush, NY 12061
    United States

    "; } else { echo " "; } } else { // Output category title and description first $category = null; if (!empty($rows[0]['category'])) { $category = $rows[0]['category']; if (strrpos($category, '/') !== false) { $category = substr($category, strrpos($category, '/') + 1); } $category = str_replace('-', ' ', $category); $category = ucwords($category); } // Display featured items if this is the featured section if ($featured) { echo "

    Our Greeting Cards & Posters

    {$descriptionmainlong}

    "; echo "
    "; if (count($rows) > 0) { foreach ($rows as $item) { $title = htmlspecialchars($item['title']); $screenshot = htmlspecialchars($item['screenshot']); // Format the URL to match the site's structure $categorySlug = strtolower(str_replace(' ', '-', $item['category'])); $titleSlug = strtolower(preg_replace('/[^a-zA-Z0-9-]/', '-', $title)); $titleSlug = preg_replace('/-+/', '-', $titleSlug); // Replace multiple dashes with single dash $url = "/" . $item['id'] . "/" . $categorySlug . "/" . $titleSlug; // Build the full image URL with the /screenshot/ path $imageUrl = "https://www.cleverstudioart.com/screenshot/" . $screenshot; echo "
    "; echo "
    "; echo "
    "; echo "$title"; echo "
    "; echo "
    "; echo ""; echo "
    "; echo "
    "; echo "
    "; } } else { echo "

    No featured items found.

    "; } echo "
    "; // Close row } else { // Regular category display. The category heading is // skipped on a search page, which prints its own h1. if (!empty($folder) && !empty($category) && !$isSearch) { echo "
    "; echo "

    {$category}

    "; echo "

    {$h2}

    "; echo "

    {$descriptionlong}

    "; echo "
    "; } if ($isSearch) { $resultWord = ($searchTotal == 1) ? 'result' : 'results'; $scopeLabelHtml = htmlspecialchars($searchScopeLabel, ENT_QUOTES, 'UTF-8'); echo "
    "; echo "

    Search results for “{$searchDisplay}”

    "; if ($searchTotal > 0) { echo "

    {$searchTotal} {$resultWord}{$scopeLabelHtml}. Clear search

    "; } echo "
    "; } // Output the product container with initial items echo "
    "; // Add initial items if (!empty($rows)) { foreach ($rows as $item) { $title = htmlspecialchars($item['title']); $screenshot = htmlspecialchars($item['screenshot']); $categorySlug = strtolower(str_replace(' ', '-', $item['category'])); $titleSlug = strtolower(preg_replace('/[^a-zA-Z0-9-]/', '-', $title)); $titleSlug = preg_replace('/-+/', '-', $titleSlug); $url = "/" . $item['id'] . "/" . $categorySlug . "/" . $titleSlug; $imageUrl = "https://www.cleverstudioart.com/screenshot/" . $screenshot; echo "
    "; echo "
    "; echo "
    "; echo "$title"; echo "
    "; echo "
    "; echo ""; echo "
    "; echo "
    "; echo "
    \n"; } } echo "
    "; // Close product container // Nothing matched: say so and offer a way forward. if ($isSearch && $searchTotal < 1) { echo "
    "; echo "
    "; echo "

    Sorry, nothing matched “{$searchDisplay}”{$scopeLabelHtml}.

    "; echo "

    Try a shorter phrase, or browse Birthday Cards, Christmas Cards, Anniversary Cards or Valentine’s Day Cards.

    "; echo "

    Browse all cards & posters

    "; echo "
    "; echo "
    "; } // Results pagination, bottom right: real ?page=N links // that the script at the foot of the page turns into // an in-place swap, keeping the address bar in step. // Nothing is drawn when everything fits on one page. if ($pagerTotalPages > 1) { echo "
    "; echo "
    "; csc_render_pager($pagerPage, $pagerTotalPages, $pagerBaseUrl, $pagerDoc, $pagerSearch, $pagerPerPage); echo "
    "; echo "
    "; } } } ?>