<?php
/**
 * Source-preserving Gutenberg blocks.
 *
 * Frontend renderers in this file emit the class names and element hierarchy
 * consumed by assets/css/pantheon.css and assets/js/pantheon.js. Gutenberg
 * stores only structured attributes; it does not add generic layout wrappers.
 *
 * @package HaPantheon
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Return the common source-block definitions.
 *
 * @return array<string,array<string,mixed>>
 */
function hapantheon_source_block_definitions() {
	static $definitions = null;
	if ( null !== $definitions ) {
		return $definitions;
	}

	$definitions = array(
		'page-hero' => array(
			'title'       => __( 'Source · Page Hero', 'hapantheon' ),
			'icon'        => 'cover-image',
			'attributes'  => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'HAPANTHEON' ),
				'title'    => array( 'type' => 'string', 'default' => __( 'כותרת העמוד', 'hapantheon' ) ),
				'lede'     => array( 'type' => 'string', 'default' => '' ),
			),
		),
		'gate' => array(
			'title'      => __( 'Source · Living Archive Gate', 'hapantheon' ),
			'icon'       => 'columns',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'HAPANTHEON · הארכיון החי' ),
				'title'    => array( 'type' => 'string', 'default' => 'חמישה אולמות. רק מה שראוי להישאר.' ),
				'lede'     => array( 'type' => 'string', 'default' => 'מתכונים שנבדקו, סיפורים שנשארו, מסעות איטיים, מלאכה בידיים ומחשבה שאינה נכתבת בחיפזון.' ),
				'scrollLabel' => array( 'type' => 'string', 'default' => 'גלו את האולמות' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'stats' => array(
			'title'      => __( 'Source · Statistics Strip', 'hapantheon' ),
			'icon'       => 'chart-bar',
			'attributes' => array(
				'items' => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'rooms' => array(
			'title'      => __( 'Source · Pantheon Halls', 'hapantheon' ),
			'icon'       => 'building',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'חמש דרכי כניסה' ),
				'title'    => array( 'type' => 'string', 'default' => 'האולמות של הפנתאון' ),
				'lede'     => array( 'type' => 'string', 'default' => '' ),
				'hint'     => array( 'type' => 'string', 'default' => 'גררו כדי לעבור בין האולמות' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'top10' => array(
			'title'      => __( 'Source · Ranked Archive', 'hapantheon' ),
			'icon'       => 'list-view',
			'attributes' => array(
				'eyebrow'     => array( 'type' => 'string', 'default' => 'המגילות האחרונות' ),
				'title'        => array( 'type' => 'string', 'default' => 'חדש בארכיון' ),
				'postsPerTab'  => array( 'type' => 'number', 'default' => 5 ),
				'tabs'         => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'featured' => array(
			'title'      => __( 'Source · Featured Scroll', 'hapantheon' ),
			'icon'       => 'star-filled',
			'attributes' => array(
				'postId'      => array( 'type' => 'number', 'default' => 0 ),
				'postSlug'    => array( 'type' => 'string', 'default' => '' ),
				'eyebrow'     => array( 'type' => 'string', 'default' => 'מגילה נבחרת' ),
				'title'        => array( 'type' => 'string', 'default' => '' ),
				'lede'         => array( 'type' => 'string', 'default' => '' ),
				'imageUrl'     => array( 'type' => 'string', 'default' => '' ),
				'imageId'      => array( 'type' => 'number', 'default' => 0 ),
				'buttonLabel'  => array( 'type' => 'string', 'default' => 'לקריאת המגילה' ),
				'buttonUrl'    => array( 'type' => 'string', 'default' => '' ),
				'tag'          => array( 'type' => 'string', 'default' => 'מגילה נבחרת' ),
				'metaOne'      => array( 'type' => 'string', 'default' => '8 דקות קריאה' ),
				'metaTwo'      => array( 'type' => 'string', 'default' => 'סיפורים' ),
			),
		),
		'oracle-band' => array(
			'title'      => __( 'Source · Oracle Band', 'hapantheon' ),
			'icon'       => 'lightbulb',
			'attributes' => array(
				'quote'       => array( 'type' => 'string', 'default' => '“מי ששואל — כבר החל את המסע.”' ),
				'buttonLabel' => array( 'type' => 'string', 'default' => 'אל האורקל' ),
				'buttonUrl'   => array( 'type' => 'string', 'default' => '/oracle/' ),
			),
		),
		'newsletter-section' => array(
			'title'      => __( 'Source · Newsletter Section', 'hapantheon' ),
			'icon'       => 'email-alt',
			'attributes' => array(
				'eyebrow'    => array( 'type' => 'string', 'default' => 'המגילה החודשית' ),
				'title'       => array( 'type' => 'string', 'default' => 'עדכון אחד בחודש. בלי רעש.' ),
				'lede'        => array( 'type' => 'string', 'default' => 'מגילה חדשה, הערת אוצר אחת וקישור אחד ששווה לשמור.' ),
				'placeholder' => array( 'type' => 'string', 'default' => 'הדוא״ל שלכם' ),
				'buttonLabel' => array( 'type' => 'string', 'default' => 'הרשמה' ),
				'source'      => array( 'type' => 'string', 'default' => 'homepage' ),
			),
		),
		'manifesto' => array(
			'title'      => __( 'Source · Manifesto', 'hapantheon' ),
			'icon'       => 'editor-quote',
			'attributes' => array(
				'lead'       => array( 'type' => 'string', 'default' => '' ),
				'paragraphs' => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'pillars' => array(
			'title'      => __( 'Source · Principles', 'hapantheon' ),
			'icon'       => 'screenoptions',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'ארבעת העמודים' ),
				'title'    => array( 'type' => 'string', 'default' => 'העקרונות שהמקדש עומד עליהם' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'timeline' => array(
			'title'      => __( 'Source · Timeline', 'hapantheon' ),
			'icon'       => 'backup',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'הכרוניקה' ),
				'title'    => array( 'type' => 'string', 'default' => 'מסע הפנתאון' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'process' => array(
			'title'      => __( 'Source · Editorial Process', 'hapantheon' ),
			'icon'       => 'yes-alt',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'השיטה' ),
				'title'    => array( 'type' => 'string', 'default' => 'איך מגילה נכנסת לארכיון' ),
				'lede'     => array( 'type' => 'string', 'default' => '' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'keeper' => array(
			'title'      => __( 'Source · Archive Keeper', 'hapantheon' ),
			'icon'       => 'admin-users',
			'attributes' => array(
				'eyebrow'     => array( 'type' => 'string', 'default' => 'מאחורי הקלעים' ),
				'title'        => array( 'type' => 'string', 'default' => 'שומר הארכיון' ),
				'body'         => array( 'type' => 'string', 'default' => '' ),
				'imageUrl'     => array( 'type' => 'string', 'default' => '' ),
				'imageId'      => array( 'type' => 'number', 'default' => 0 ),
				'buttonLabel'  => array( 'type' => 'string', 'default' => 'כתבו לשומר הארכיון' ),
				'buttonUrl'    => array( 'type' => 'string', 'default' => '/contact/' ),
			),
		),
		'oracle-hero' => array(
			'title'      => __( 'Source · Oracle Hero', 'hapantheon' ),
			'icon'       => 'visibility',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'OMEGA · האולם הפנימי' ),
				'title'    => array( 'type' => 'string', 'default' => 'האורקל' ),
				'lede'     => array( 'type' => 'string', 'default' => 'שאלה אחת בכל חודש. תשובה אחת שלא נכתבה בחיפזון.' ),
				'quote'    => array( 'type' => 'string', 'default' => '“אל תבקש דרך קצרה. בקש רגליים שאינן מתעייפות.”' ),
			),
		),
		'oracle-cards' => array(
			'title'      => __( 'Source · Oracle Question Families', 'hapantheon' ),
			'icon'       => 'grid-view',
			'attributes' => array(
				'eyebrow' => array( 'type' => 'string', 'default' => 'ארבע משפחות של שאלות' ),
				'title'    => array( 'type' => 'string', 'default' => 'מה האורקל מחפש' ),
				'items'    => array( 'type' => 'array', 'default' => array() ),
			),
		),
		'oracle-ask' => array(
			'title'      => __( 'Source · Ask the Oracle', 'hapantheon' ),
			'icon'       => 'format-chat',
			'attributes' => array(
				'eyebrow'     => array( 'type' => 'string', 'default' => 'שאלה לאורקל' ),
				'title'        => array( 'type' => 'string', 'default' => 'יש לכם שאלה שמגיעה לה מסה?' ),
				'lede'         => array( 'type' => 'string', 'default' => '' ),
				'buttonLabel'  => array( 'type' => 'string', 'default' => 'לשליחת שאלה' ),
				'buttonUrl'    => array( 'type' => 'string', 'default' => '/contact/' ),
			),
		),
		'faq-list' => array(
			'title'      => __( 'Source · FAQ List', 'hapantheon' ),
			'icon'       => 'editor-help',
			'attributes' => array(
				'tabs'        => array( 'type' => 'array', 'default' => array() ),
				'items'       => array( 'type' => 'array', 'default' => array() ),
				'ctaTitle'    => array( 'type' => 'string', 'default' => 'לא מצאתם את התשובה?' ),
				'ctaBody'     => array( 'type' => 'string', 'default' => '' ),
				'buttonLabel' => array( 'type' => 'string', 'default' => 'לעמוד צור קשר' ),
				'buttonUrl'   => array( 'type' => 'string', 'default' => '/contact/' ),
			),
		),
		'contact-layout' => array(
			'title'      => __( 'Source · Contact Layout', 'hapantheon' ),
			'icon'       => 'email',
			'attributes' => array(
				'eyebrow'   => array( 'type' => 'string', 'default' => 'כתבו אלינו' ),
				'title'      => array( 'type' => 'string', 'default' => 'מכתב לשומר הארכיון' ),
				'lede'       => array( 'type' => 'string', 'default' => '' ),
				'email'      => array( 'type' => 'string', 'default' => '' ),
				'channels'   => array( 'type' => 'array', 'default' => array() ),
				'stats'      => array( 'type' => 'array', 'default' => array() ),
				'quote'      => array( 'type' => 'string', 'default' => '' ),
			),
		),
		'recipe' => array(
			'title'      => __( 'Source · Recipe Body', 'hapantheon' ),
			'icon'       => 'food',
			'attributes' => array(
				'ingredientsTitle' => array( 'type' => 'string', 'default' => 'המרכיבים' ),
				'ingredients'      => array( 'type' => 'array', 'default' => array() ),
				'ratioLabel'       => array( 'type' => 'string', 'default' => 'היחס' ),
				'ratioValue'       => array( 'type' => 'string', 'default' => '1 : 15' ),
				'ratioBody'        => array( 'type' => 'string', 'default' => '' ),
				'equipmentTitle'   => array( 'type' => 'string', 'default' => 'הציוד' ),
				'equipment'        => array( 'type' => 'array', 'default' => array() ),
				'steps'            => array( 'type' => 'array', 'default' => array() ),
				'tipsTitle'        => array( 'type' => 'string', 'default' => '' ),
				'tips'             => array( 'type' => 'array', 'default' => array() ),
				'mediaEyebrow'     => array( 'type' => 'string', 'default' => 'מאחורי הקלעים' ),
				'mediaTitle'       => array( 'type' => 'string', 'default' => '' ),
				'mediaLede'        => array( 'type' => 'string', 'default' => '' ),
				'gallery'          => array( 'type' => 'array', 'default' => array() ),
				'videoPosterUrl'   => array( 'type' => 'string', 'default' => '' ),
				'videoId'          => array( 'type' => 'string', 'default' => '' ),
				'videoLabel'       => array( 'type' => 'string', 'default' => 'צפו בתהליך' ),
				'videoDuration'    => array( 'type' => 'string', 'default' => '03:18' ),
			),
		),
	);

	return $definitions;
}

/**
 * Build block wrapper attributes without introducing an extra element.
 *
 * @param string $class_name Source root class.
 * @return string
 */
function hapantheon_source_wrapper_attributes( $class_name ) {
	if ( function_exists( 'get_block_wrapper_attributes' ) ) {
		return get_block_wrapper_attributes( array( 'class' => $class_name ) );
	}
	return 'class="' . esc_attr( $class_name ) . '"';
}

/**
 * Resolve an editable image to a local or media-library URL.
 *
 * @param array  $item         Item data.
 * @param string $default_file Bundled demo image filename.
 * @param string $size         WordPress image size.
 * @return string
 */
function hapantheon_source_image( $item, $default_file = 'placeholder.svg', $size = 'full' ) {
	$attachment_id = isset( $item['imageId'] ) ? absint( $item['imageId'] ) : 0;
	if ( $attachment_id && function_exists( 'wp_get_attachment_image_url' ) ) {
		$url = wp_get_attachment_image_url( $attachment_id, $size );
		if ( $url ) {
			return $url;
		}
	}
	if ( ! empty( $item['imageUrl'] ) ) {
		return esc_url_raw( $item['imageUrl'] );
	}
	return HAPANTHEON_URI . '/assets/images/demo/' . ltrim( $default_file, '/' );
}

/**
 * Resolve relative editor URLs against the WordPress home URL.
 *
 * @param string $url URL or path.
 * @return string
 */
function hapantheon_source_url( $url ) {
	$url = trim( (string) $url );
	if ( '' === $url ) {
		return home_url( '/' );
	}
	if ( 0 === strpos( $url, '/' ) && 0 !== strpos( $url, '//' ) ) {
		return home_url( $url );
	}
	return $url;
}

/**
 * Pediment SVG used by every source hall card.
 *
 * @return string
 */
function hapantheon_source_pediment_svg() {
	return '<svg viewBox="0 0 300 86" preserveAspectRatio="none" aria-hidden="true"><path class="roof" d="M8 68 150 4l142 64v12H8Z"/><path class="rake" d="M18 66 150 10l132 56"/><path class="tymp" d="M34 65 150 17l116 48Z"/><path class="laurel" d="M119 53c12-20 50-20 62 0M123 52c8 9 16 12 27 12s20-3 28-12"/><path class="akro" d="M150 4v-9m-8 7 8-7 8 7"/></svg>';
}

/**
 * Render a source-preserving block.
 *
 * @param array    $attributes Block attributes.
 * @param string   $content    Saved content; intentionally unused for dynamic blocks.
 * @param WP_Block $block      Block instance.
 * @return string
 */
function hapantheon_render_source_block( $attributes, $content, $block ) {
	$name = isset( $block->name ) ? str_replace( 'hapantheon/', '', $block->name ) : '';
	$attributes = is_array( $attributes ) ? $attributes : array();

	switch ( $name ) {
		case 'page-hero':
			return hapantheon_render_source_page_hero( $attributes );
		case 'gate':
			return hapantheon_render_source_gate( $attributes );
		case 'stats':
			return hapantheon_render_source_stats( $attributes );
		case 'rooms':
			return hapantheon_render_source_rooms( $attributes );
		case 'top10':
			return hapantheon_render_source_top10( $attributes );
		case 'featured':
			return hapantheon_render_source_featured( $attributes );
		case 'oracle-band':
			return hapantheon_render_source_oracle_band( $attributes );
		case 'newsletter-section':
			return hapantheon_render_source_newsletter_section( $attributes );
		case 'manifesto':
			return hapantheon_render_source_manifesto( $attributes );
		case 'pillars':
			return hapantheon_render_source_pillars( $attributes );
		case 'timeline':
			return hapantheon_render_source_timeline( $attributes );
		case 'process':
			return hapantheon_render_source_process( $attributes );
		case 'keeper':
			return hapantheon_render_source_keeper( $attributes );
		case 'oracle-hero':
			return hapantheon_render_source_oracle_hero( $attributes );
		case 'oracle-cards':
			return hapantheon_render_source_oracle_cards( $attributes );
		case 'oracle-ask':
			return hapantheon_render_source_oracle_ask( $attributes );
		case 'faq-list':
			return hapantheon_render_source_faq( $attributes );
		case 'contact-layout':
			return hapantheon_render_source_contact( $attributes );
		case 'recipe':
			return hapantheon_render_source_recipe( $attributes );
	}
	return '';
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_page_hero( $a ) {
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'page-hero' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
		<div class="wrap">
			<?php if ( ! empty( $a['eyebrow'] ) ) : ?><span class="eyebrow"><?php echo esc_html( $a['eyebrow'] ); ?></span><?php endif; ?>
			<h1><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h1>
			<?php if ( ! empty( $a['lede'] ) ) : ?><p><?php echo esc_html( $a['lede'] ); ?></p><?php endif; ?>
			<div class="meander" aria-hidden="true"></div>
		</div>
	</section>
	<?php
	return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_gate( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'gate' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
		<div class="gate-track">
			<?php foreach ( $items as $index => $item ) :
				$url   = hapantheon_source_url( isset( $item['url'] ) ? $item['url'] : '/' );
				$image = hapantheon_source_image( $item, isset( $item['fallback'] ) ? $item['fallback'] : 'temple.jpg', 'hapantheon-hero' );
				$open  = 0 === $index ? ' act' : '';
				?>
				<a class="slice<?php echo esc_attr( $open ); ?>" href="<?php echo esc_url( $url ); ?>" data-hall="<?php echo esc_attr( isset( $item['slug'] ) ? sanitize_key( $item['slug'] ) : '' ); ?>">
					<img decoding="async" loading="<?php echo 0 === $index ? 'eager' : 'lazy'; ?>" src="<?php echo esc_url( $image ); ?>" alt="<?php echo esc_attr( isset( $item['imageAlt'] ) ? $item['imageAlt'] : ( isset( $item['title'] ) ? $item['title'] : '' ) ); ?>">
					<span class="slice-tag"><span class="glyph"><?php echo esc_html( isset( $item['glyph'] ) ? $item['glyph'] : '' ); ?></span><b><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></b><i aria-hidden="true"></i></span>
					<span class="slice-info">
						<span class="glyph"><?php echo esc_html( isset( $item['glyph'] ) ? $item['glyph'] : '' ); ?></span>
						<h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3>
						<p><?php echo esc_html( isset( $item['description'] ) ? $item['description'] : '' ); ?></p>
						<span class="row"><span class="go"><?php echo esc_html( isset( $item['buttonLabel'] ) ? $item['buttonLabel'] : 'כניסה לאולם' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><span class="cnt"><span class="num"><?php echo esc_html( isset( $item['count'] ) ? $item['count'] : '' ); ?></span> <?php echo esc_html( isset( $item['countLabel'] ) ? $item['countLabel'] : 'מגילות' ); ?></span></span>
					</span>
				</a>
			<?php endforeach; ?>
		</div>
		<div class="plaque" aria-hidden="true"><div class="plaque-in"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h1><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h1><p class="sub"><?php echo esc_html( isset( $a['lede'] ) ? $a['lede'] : '' ); ?></p><div class="meander"></div></div></div>
		<div class="scroll-cue"><span><?php echo esc_html( isset( $a['scrollLabel'] ) ? $a['scrollLabel'] : '' ); ?></span><i aria-hidden="true"></i></div>
		<div class="gate-pag" aria-label="<?php esc_attr_e( 'בחירת אולם', 'hapantheon' ); ?>"></div>
		<div class="meander" aria-hidden="true"></div>
	</section>
	<?php
	return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_stats( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'stats' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap stats-in">
		<?php foreach ( $items as $item ) : ?>
			<div class="stat rv"><span class="num"<?php echo isset( $item['countTo'] ) && '' !== (string) $item['countTo'] ? ' data-count="' . esc_attr( $item['countTo'] ) . '"' : ''; ?>><?php echo esc_html( isset( $item['value'] ) ? $item['value'] : '' ); ?></span><small><?php echo esc_html( isset( $item['label'] ) ? $item['label'] : '' ); ?></small></div>
		<?php endforeach; ?>
	</div></section>
	<?php
	return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_rooms( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'rooms' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap">
		<header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><?php if ( ! empty( $a['lede'] ) ) : ?><p><?php echo esc_html( $a['lede'] ); ?></p><?php endif; ?></header>
		<div class="rooms-grid">
			<?php foreach ( $items as $item ) :
				$slug  = isset( $item['slug'] ) ? sanitize_key( $item['slug'] ) : '';
				$url   = hapantheon_source_url( isset( $item['url'] ) ? $item['url'] : ( $slug ? '/category/' . $slug . '/' : '/' ) );
				$count = isset( $item['count'] ) ? $item['count'] : '';
				if ( $slug && function_exists( 'get_term_by' ) ) {
					$term = get_term_by( 'slug', $slug, 'category' );
					if ( $term && ! is_wp_error( $term ) ) {
						$url = get_category_link( $term->term_id );
						$count = $term->count;
					}
				}
				$image = hapantheon_source_image( $item, isset( $item['fallback'] ) ? $item['fallback'] : 'temple.jpg', 'hapantheon-card' );
				?>
				<a class="temple rv<?php echo ! empty( $item['inner'] ) ? ' inner-hall' : ''; ?>" href="<?php echo esc_url( $url ); ?>">
					<span class="pediment"><?php echo hapantheon_source_pediment_svg(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
					<span class="temple-body"><span class="inlay"></span><span class="entab"></span><span class="cella"><span class="colm r" aria-hidden="true"></span><span class="colm l" aria-hidden="true"></span><span class="cella-img"><img decoding="async" loading="lazy" src="<?php echo esc_url( $image ); ?>" alt="<?php echo esc_attr( isset( $item['imageAlt'] ) ? $item['imageAlt'] : ( isset( $item['title'] ) ? $item['title'] : '' ) ); ?>"><span class="cella-glyph"><?php echo esc_html( isset( $item['glyph'] ) ? $item['glyph'] : '' ); ?></span></span><span class="cella-title"><h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3><span class="lat"><?php echo esc_html( isset( $item['latin'] ) ? $item['latin'] : '' ); ?></span><span class="cnt"><span class="num"><?php echo esc_html( $count ); ?></span> <?php echo esc_html( isset( $item['countLabel'] ) ? $item['countLabel'] : 'מגילות' ); ?></span></span></span><span class="stepx s1"></span><span class="stepx s2"></span><span class="stepx s3"></span></span>
				</a>
			<?php endforeach; ?>
		</div>
		<p class="rooms-hint"><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( isset( $a['hint'] ) ? $a['hint'] : '' ); ?></p>
	</div></section>
	<?php
	return ob_get_clean();
}

/**
 * Query posts for a ranked source panel.
 *
 * @param string $slug  Category slug.
 * @param int    $limit Maximum posts.
 * @return array<int,array<string,string>>
 */
function hapantheon_source_ranked_posts( $slug, $limit ) {
	$rows = array();
	if ( ! class_exists( 'WP_Query' ) ) {
		return $rows;
	}
	$args = array(
		'post_type'           => 'post',
		'post_status'         => 'publish',
		'posts_per_page'      => max( 1, min( 10, $limit ) ),
		'ignore_sticky_posts' => true,
		'no_found_rows'       => true,
	);
	if ( $slug && 'all' !== $slug ) {
		$args['category_name'] = $slug;
	}
	$query = new WP_Query( $args );
	foreach ( $query->posts as $post ) {
		$category = hapantheon_primary_category( $post->ID );
		$hall     = $category ? hapantheon_hall_data( $category->slug ) : hapantheon_hall_data( '' );
		$rows[]   = array(
			'title' => get_the_title( $post ),
			'url'   => get_permalink( $post ),
			'cat'   => $hall['glyph'] . ( $category ? ' · ' . $category->name : '' ),
		);
	}
	wp_reset_postdata();
	return $rows;
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_top10( $a ) {
	$tabs  = ! empty( $a['tabs'] ) && is_array( $a['tabs'] ) ? $a['tabs'] : array( array( 'slug' => 'all', 'label' => 'הכול' ) );
	$limit = isset( $a['postsPerTab'] ) ? absint( $a['postsPerTab'] ) : 5;
	$uid   = function_exists( 'wp_unique_id' ) ? wp_unique_id( 'hapantheon-top10-' ) : 'hapantheon-top10';
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'top10' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap">
		<div class="top10-head"><div><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2></div><div class="top10-tabs" role="tablist" aria-label="<?php esc_attr_e( 'סינון המגילות המדורגות', 'hapantheon' ); ?>">
			<?php foreach ( $tabs as $index => $tab ) : $panel_id = $uid . '-' . sanitize_key( isset( $tab['slug'] ) ? $tab['slug'] : (string) $index ); ?>
				<button class="t10tab" type="button" role="tab" data-panel="<?php echo esc_attr( $panel_id ); ?>" aria-controls="<?php echo esc_attr( $panel_id ); ?>" aria-selected="<?php echo 0 === $index ? 'true' : 'false'; ?>"><?php echo esc_html( isset( $tab['label'] ) ? $tab['label'] : '' ); ?></button>
			<?php endforeach; ?>
		</div></div>
		<?php foreach ( $tabs as $index => $tab ) :
			$slug     = sanitize_key( isset( $tab['slug'] ) ? $tab['slug'] : 'all' );
			$panel_id = $uid . '-' . $slug;
			$rows     = hapantheon_source_ranked_posts( $slug, $limit );
			if ( empty( $rows ) && ! empty( $tab['items'] ) && is_array( $tab['items'] ) ) {
				$rows = $tab['items'];
			}
			?>
			<div class="t10-panel<?php echo 0 === $index ? ' act' : ''; ?>" id="<?php echo esc_attr( $panel_id ); ?>" role="tabpanel">
				<?php foreach ( $rows as $rank => $row ) : ?>
					<a class="t10 rv" href="<?php echo esc_url( hapantheon_source_url( isset( $row['url'] ) ? $row['url'] : '/' ) ); ?>"><span class="t10-r"><span class="rank num"><?php echo esc_html( str_pad( (string) ( $rank + 1 ), 2, '0', STR_PAD_LEFT ) ); ?></span><span><span class="t10-cat"><?php echo esc_html( isset( $row['cat'] ) ? $row['cat'] : '' ); ?></span><h4><?php echo esc_html( isset( $row['title'] ) ? $row['title'] : '' ); ?></h4></span></span><span class="arw"><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span></a>
				<?php endforeach; ?>
			</div>
		<?php endforeach; ?>
	</div></section>
	<?php
	return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_featured( $a ) {
	$post_id = isset( $a['postId'] ) ? absint( $a['postId'] ) : 0;
	if ( ! $post_id && ! empty( $a['postSlug'] ) && function_exists( 'get_page_by_path' ) ) {
		$featured_post = get_page_by_path( sanitize_title( $a['postSlug'] ), OBJECT, 'post' );
		if ( $featured_post ) {
			$post_id = absint( $featured_post->ID );
		}
	}
	if ( ! $post_id && function_exists( 'get_posts' ) ) {
		$posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 1, 'ignore_sticky_posts' => false ) );
		if ( $posts ) {
			$post_id = $posts[0]->ID;
		}
	}
	$title = ! empty( $a['title'] ) ? $a['title'] : ( $post_id ? get_the_title( $post_id ) : '' );
	$lede  = ! empty( $a['lede'] ) ? $a['lede'] : ( $post_id ? get_the_excerpt( $post_id ) : '' );
	$url   = ! empty( $a['buttonUrl'] ) ? hapantheon_source_url( $a['buttonUrl'] ) : ( $post_id ? get_permalink( $post_id ) : home_url( '/' ) );
	$image = $post_id ? hapantheon_featured_image_url( $post_id, 'hapantheon-hero' ) : hapantheon_source_image( $a, 'reflection.jpg', 'hapantheon-hero' );
	ob_start();
	?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'featured' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap feat-grid">
		<figure class="mframe rv"><img decoding="async" loading="lazy" src="<?php echo esc_url( $image ); ?>" alt="<?php echo esc_attr( $title ); ?>"><?php if ( ! empty( $a['tag'] ) ) : ?><span class="tag"><?php echo esc_html( $a['tag'] ); ?></span><?php endif; ?></figure>
		<div class="rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( $title ); ?></h2><p class="lede"><?php echo esc_html( $lede ); ?></p><div class="meta"><span class="num"><?php echo esc_html( isset( $a['metaOne'] ) ? $a['metaOne'] : '' ); ?></span><span><?php echo esc_html( isset( $a['metaTwo'] ) ? $a['metaTwo'] : '' ); ?></span></div><a class="btn-gold" href="<?php echo esc_url( $url ); ?>"><?php echo esc_html( isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div>
	</div></section>
	<?php
	return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_oracle_band( $a ) {
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'oracle-band' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap"><span class="medal"><?php echo hapantheon_icon( 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><blockquote class="oracle-q"><?php echo esc_html( isset( $a['quote'] ) ? $a['quote'] : '' ); ?></blockquote><a class="btn-line" href="<?php echo esc_url( hapantheon_source_url( isset( $a['buttonUrl'] ) ? $a['buttonUrl'] : '/oracle/' ) ); ?>"><?php echo esc_html( isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_newsletter_section( $a ) {
	$form_attributes = array(
		'source'      => isset( $a['source'] ) ? $a['source'] : 'site',
		'style'       => 'teal',
		'placeholder' => isset( $a['placeholder'] ) ? $a['placeholder'] : '',
		'buttonLabel' => isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '',
	);
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'news' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap"><div class="news-card rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><p><?php echo esc_html( isset( $a['lede'] ) ? $a['lede'] : '' ); ?></p><?php echo hapantheon_newsletter_shortcode( $form_attributes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><p id="ns" aria-live="polite"></p></div></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_manifesto( $a ) {
	$paragraphs = ! empty( $a['paragraphs'] ) && is_array( $a['paragraphs'] ) ? $a['paragraphs'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'manifesto' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php if ( ! empty( $a['lead'] ) ) : ?><p class="big"><?php echo wp_kses_post( $a['lead'] ); ?></p><?php endif; ?><?php foreach ( $paragraphs as $paragraph ) : ?><p><?php echo esc_html( is_array( $paragraph ) && isset( $paragraph['text'] ) ? $paragraph['text'] : $paragraph ); ?></p><?php endforeach; ?></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_pillars( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2></header><div class="pillars"><?php foreach ( $items as $item ) : ?><article class="pillar rv"><span aria-hidden="true"><?php echo hapantheon_icon( isset( $item['icon'] ) ? sanitize_key( $item['icon'] ) : 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3><p><?php echo esc_html( isset( $item['body'] ) ? $item['body'] : '' ); ?></p><span class="p-mea meander" aria-hidden="true"></span></article><?php endforeach; ?></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_timeline( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2></header><div class="timeline"><?php foreach ( $items as $item ) : ?><article class="tl rv"><span class="tl-y num"><?php echo esc_html( isset( $item['year'] ) ? $item['year'] : '' ); ?></span><h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3><p><?php echo esc_html( isset( $item['body'] ) ? $item['body'] : '' ); ?></p></article><?php endforeach; ?></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_process( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><?php if ( ! empty( $a['lede'] ) ) : ?><p><?php echo esc_html( $a['lede'] ); ?></p><?php endif; ?></header><div class="process"><?php foreach ( $items as $index => $item ) : ?><article class="proc rv"><span class="p-n num"><?php echo esc_html( isset( $item['number'] ) ? $item['number'] : (string) ( $index + 1 ) ); ?></span><h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3><p><?php echo esc_html( isset( $item['body'] ) ? $item['body'] : '' ); ?></p><?php if ( ! empty( $item['note'] ) ) : ?><p class="fail"><?php echo hapantheon_icon( ! empty( $item['success'] ) ? 'star' : 'close' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( $item['note'] ); ?></p><?php endif; ?></article><?php endforeach; ?></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_keeper( $a ) {
	$image = hapantheon_source_image( $a, 'writing.jpg', 'hapantheon-card' );
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="keeper rv"><figure class="kframe"><img decoding="async" loading="lazy" src="<?php echo esc_url( $image ); ?>" alt="<?php echo esc_attr( isset( $a['title'] ) ? $a['title'] : '' ); ?>"></figure><div><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><p><?php echo esc_html( isset( $a['body'] ) ? $a['body'] : '' ); ?></p><a class="btn-gold" href="<?php echo esc_url( hapantheon_source_url( isset( $a['buttonUrl'] ) ? $a['buttonUrl'] : '/contact/' ) ); ?>"><?php echo esc_html( isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_oracle_hero( $a ) {
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'ohero' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="wrap"><span class="medal-lg"><?php echo hapantheon_icon( 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h1><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h1><p class="lede"><?php echo esc_html( isset( $a['lede'] ) ? $a['lede'] : '' ); ?></p><blockquote class="oracle-q"><?php echo esc_html( isset( $a['quote'] ) ? $a['quote'] : '' ); ?></blockquote></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_oracle_cards( $a ) {
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2></header><div class="o-cards"><?php foreach ( $items as $index => $item ) : ?><article class="o-card rv"><span class="oc-n num"><?php echo esc_html( isset( $item['number'] ) ? $item['number'] : str_pad( (string) ( $index + 1 ), 2, '0', STR_PAD_LEFT ) ); ?></span><?php echo hapantheon_icon( isset( $item['icon'] ) ? sanitize_key( $item['icon'] ) : 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><h3><?php echo esc_html( isset( $item['title'] ) ? $item['title'] : '' ); ?></h3><p><?php echo esc_html( isset( $item['body'] ) ? $item['body'] : '' ); ?></p></article><?php endforeach; ?></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_oracle_ask( $a ) {
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="o-ask rv"><div><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><p><?php echo esc_html( isset( $a['lede'] ) ? $a['lede'] : '' ); ?></p></div><a class="btn-teal" href="<?php echo esc_url( hapantheon_source_url( isset( $a['buttonUrl'] ) ? $a['buttonUrl'] : '/contact/' ) ); ?>"><?php echo esc_html( isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_faq( $a ) {
	$tabs  = ! empty( $a['tabs'] ) && is_array( $a['tabs'] ) ? $a['tabs'] : array();
	$items = ! empty( $a['items'] ) && is_array( $a['items'] ) ? $a['items'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php if ( $tabs ) : ?><nav class="tabs faq-tabs" aria-label="<?php esc_attr_e( 'סינון שאלות', 'hapantheon' ); ?>"><?php foreach ( $tabs as $index => $tab ) : ?><button class="tab" type="button" data-fcat="<?php echo esc_attr( isset( $tab['slug'] ) ? sanitize_key( $tab['slug'] ) : 'all' ); ?>" aria-selected="<?php echo 0 === $index ? 'true' : 'false'; ?>"><?php echo esc_html( isset( $tab['label'] ) ? $tab['label'] : '' ); ?></button><?php endforeach; ?></nav><?php endif; ?><div class="faq-wrap"><?php foreach ( $items as $index => $item ) : ?><details class="faq" data-fcat="<?php echo esc_attr( isset( $item['category'] ) ? sanitize_key( $item['category'] ) : 'general' ); ?>"<?php echo 0 === $index ? ' open' : ''; ?>><summary><?php echo esc_html( isset( $item['question'] ) ? $item['question'] : '' ); ?><span aria-hidden="true">+</span></summary><div class="faq-a"><?php echo wp_kses_post( isset( $item['answer'] ) ? wpautop( $item['answer'] ) : '' ); ?></div></details><?php endforeach; ?></div><div class="faq-more rv"><h3><?php echo esc_html( isset( $a['ctaTitle'] ) ? $a['ctaTitle'] : '' ); ?></h3><p><?php echo esc_html( isset( $a['ctaBody'] ) ? $a['ctaBody'] : '' ); ?></p><a class="btn-teal" href="<?php echo esc_url( hapantheon_source_url( isset( $a['buttonUrl'] ) ? $a['buttonUrl'] : '/contact/' ) ); ?>"><?php echo esc_html( isset( $a['buttonLabel'] ) ? $a['buttonLabel'] : '' ); ?><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_contact( $a ) {
	$channels = ! empty( $a['channels'] ) && is_array( $a['channels'] ) ? $a['channels'] : array();
	$stats    = ! empty( $a['stats'] ) && is_array( $a['stats'] ) ? $a['stats'] : array();
	$email    = ! empty( $a['email'] ) ? sanitize_email( $a['email'] ) : sanitize_email( get_theme_mod( 'hapantheon_contact_email', get_option( 'admin_email' ) ) );
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="contact-grid"><aside class="cside"><?php foreach ( $channels as $channel ) : ?><article class="chan rv"><span class="ci"><?php echo hapantheon_icon( isset( $channel['icon'] ) ? sanitize_key( $channel['icon'] ) : 'mail' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><div><h3><?php echo esc_html( isset( $channel['title'] ) ? $channel['title'] : '' ); ?></h3><p><?php echo esc_html( isset( $channel['body'] ) ? $channel['body'] : '' ); ?></p><?php if ( ! empty( $channel['value'] ) || ( isset( $channel['kind'] ) && 'email' === $channel['kind'] ) ) : $value = ! empty( $channel['value'] ) ? $channel['value'] : $email; ?><a class="cv" href="<?php echo esc_url( isset( $channel['url'] ) && $channel['url'] ? hapantheon_source_url( $channel['url'] ) : 'mailto:' . antispambot( $value ) ); ?>"><?php echo esc_html( $value ); ?></a><?php endif; ?></div></article><?php endforeach; ?><div class="cstats"><?php foreach ( $stats as $stat ) : ?><div><span class="num"><?php echo esc_html( isset( $stat['value'] ) ? $stat['value'] : '' ); ?></span><small><?php echo esc_html( isset( $stat['label'] ) ? $stat['label'] : '' ); ?></small></div><?php endforeach; ?></div><?php if ( ! empty( $a['quote'] ) ) : ?><blockquote class="cquote"><?php echo wp_kses_post( $a['quote'] ); ?></blockquote><?php endif; ?></aside><div><span class="eyebrow"><?php echo esc_html( isset( $a['eyebrow'] ) ? $a['eyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['title'] ) ? $a['title'] : '' ); ?></h2><?php if ( ! empty( $a['lede'] ) ) : ?><p><?php echo esc_html( $a['lede'] ); ?></p><?php endif; ?><?php echo hapantheon_contact_form_shortcode(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div></div></section>
	<?php return ob_get_clean();
}

/** @param array $a Attributes. @return string */
function hapantheon_render_source_recipe( $a ) {
	$ingredients = ! empty( $a['ingredients'] ) && is_array( $a['ingredients'] ) ? $a['ingredients'] : array();
	$equipment   = ! empty( $a['equipment'] ) && is_array( $a['equipment'] ) ? $a['equipment'] : array();
	$steps       = ! empty( $a['steps'] ) && is_array( $a['steps'] ) ? $a['steps'] : array();
	$tips        = ! empty( $a['tips'] ) && is_array( $a['tips'] ) ? $a['tips'] : array();
	$gallery     = ! empty( $a['gallery'] ) && is_array( $a['gallery'] ) ? $a['gallery'] : array();
	ob_start(); ?>
	<section <?php echo hapantheon_source_wrapper_attributes( 'wrap' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><div class="recipe"><aside class="panel rv"><h3><?php echo hapantheon_icon( 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( isset( $a['ingredientsTitle'] ) ? $a['ingredientsTitle'] : '' ); ?></h3><ul class="ings"><?php foreach ( $ingredients as $item ) : ?><li><span><?php echo esc_html( isset( $item['name'] ) ? $item['name'] : '' ); ?></span><span class="num"><?php echo esc_html( isset( $item['amount'] ) ? $item['amount'] : '' ); ?></span></li><?php endforeach; ?></ul><div class="ratio"><?php echo esc_html( isset( $a['ratioLabel'] ) ? $a['ratioLabel'] : '' ); ?><b class="num"><?php echo esc_html( isset( $a['ratioValue'] ) ? $a['ratioValue'] : '' ); ?></b><?php if ( ! empty( $a['ratioBody'] ) ) : ?><span><?php echo esc_html( $a['ratioBody'] ); ?></span><?php endif; ?></div><h3><?php echo hapantheon_icon( 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( isset( $a['equipmentTitle'] ) ? $a['equipmentTitle'] : '' ); ?></h3><ul class="equip"><?php foreach ( $equipment as $item ) : $text = is_array( $item ) && isset( $item['text'] ) ? $item['text'] : $item; ?><li><?php echo hapantheon_icon( 'yes-alt' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><span><?php echo esc_html( $text ); ?></span></li><?php endforeach; ?></ul></aside><div><ol class="steps"><?php foreach ( $steps as $step ) : ?><li class="rv"><h4><?php echo esc_html( isset( $step['title'] ) ? $step['title'] : '' ); ?></h4><p><?php echo esc_html( isset( $step['body'] ) ? $step['body'] : '' ); ?></p><?php if ( ! empty( $step['time'] ) ) : ?><span class="stime"><?php echo hapantheon_icon( 'clock' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( $step['time'] ); ?></span><?php endif; ?></li><?php endforeach; ?></ol><?php if ( $tips ) : ?><aside class="rtips rv"><h3><?php echo hapantheon_icon( 'star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( isset( $a['tipsTitle'] ) ? $a['tipsTitle'] : '' ); ?></h3><ul><?php foreach ( $tips as $tip ) : $text = is_array( $tip ) && isset( $tip['text'] ) ? $tip['text'] : $tip; ?><li><?php echo esc_html( $text ); ?></li><?php endforeach; ?></ul></aside><?php endif; ?></div></div><?php if ( $gallery || ! empty( $a['videoPosterUrl'] ) ) : ?><header class="sec-head rv"><span class="eyebrow"><?php echo esc_html( isset( $a['mediaEyebrow'] ) ? $a['mediaEyebrow'] : '' ); ?></span><h2><?php echo esc_html( isset( $a['mediaTitle'] ) ? $a['mediaTitle'] : '' ); ?></h2><p><?php echo esc_html( isset( $a['mediaLede'] ) ? $a['mediaLede'] : '' ); ?></p></header><?php endif; ?><?php if ( $gallery ) : ?><div class="rgallery"><?php foreach ( $gallery as $item ) : ?><figure class="gframe rv"><img decoding="async" loading="lazy" src="<?php echo esc_url( hapantheon_source_image( $item, isset( $item['fallback'] ) ? $item['fallback'] : 'coffee.jpg', 'hapantheon-card' ) ); ?>" alt="<?php echo esc_attr( isset( $item['caption'] ) ? $item['caption'] : '' ); ?>"><figcaption><?php echo esc_html( isset( $item['caption'] ) ? $item['caption'] : '' ); ?></figcaption></figure><?php endforeach; ?></div><?php endif; ?><?php if ( ! empty( $a['videoPosterUrl'] ) ) : ?><button class="vframe rv" type="button" onclick="playRecipeVideo(this)" data-yt="<?php echo esc_attr( isset( $a['videoId'] ) ? $a['videoId'] : '' ); ?>"><span class="vposter"><img decoding="async" loading="lazy" src="<?php echo esc_url( $a['videoPosterUrl'] ); ?>" alt="<?php echo esc_attr( isset( $a['videoLabel'] ) ? $a['videoLabel'] : '' ); ?>"><span class="vplay"><i><?php echo hapantheon_icon( 'arrow' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></i></span><span class="vmeta"><?php echo esc_html( isset( $a['videoLabel'] ) ? $a['videoLabel'] : '' ); ?><span class="num"><?php echo esc_html( isset( $a['videoDuration'] ) ? $a['videoDuration'] : '' ); ?></span></span></span></button><?php endif; ?></section>
	<?php return ob_get_clean();
}

/**
 * Register source-preserving block types and editor assets.
 */
function hapantheon_register_source_blocks() {
	wp_register_script(
		'hapantheon-source-blocks-editor',
		HAPANTHEON_URI . '/assets/js/source-blocks.js',
		array( 'wp-blocks', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render' ),
		HAPANTHEON_VERSION,
		true
	);
	wp_register_style(
		'hapantheon-source-blocks-editor',
		HAPANTHEON_URI . '/assets/css/editor-source-blocks.css',
		array( 'wp-edit-blocks' ),
		HAPANTHEON_VERSION
	);

	$client_definitions = array();
	foreach ( hapantheon_source_block_definitions() as $client_slug => $client_definition ) {
		$client_definitions[ $client_slug ] = array(
			'attributes' => $client_definition['attributes'],
		);
	}
	wp_localize_script(
		'hapantheon-source-blocks-editor',
		'hapantheonSourceBlocks',
		array( 'definitions' => $client_definitions )
	);

	foreach ( hapantheon_source_block_definitions() as $slug => $definition ) {
		register_block_type(
			'hapantheon/' . $slug,
			array(
				'api_version'     => 2,
				'title'           => $definition['title'],
				'category'        => 'design',
				'icon'            => $definition['icon'],
				'attributes'      => $definition['attributes'],
				'supports'        => array(
					'align'  => array( 'full' ),
					'anchor' => true,
					'html'   => false,
				),
				'editor_script'   => 'hapantheon-source-blocks-editor',
				'editor_style'    => 'hapantheon-source-blocks-editor',
				'render_callback' => 'hapantheon_render_source_block',
			)
		);
	}
}
add_action( 'init', 'hapantheon_register_source_blocks' );
